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

BIN
.DS_Store vendored Normal file

Binary file not shown.

0
.gitignore vendored Normal file
View File

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()?>

220
cart.php Normal file
View File

@@ -0,0 +1,220 @@
<?php
// Prevent direct access to file
defined(security_key) or exit;
// Remove product from cart, check for the URL param "remove", this is the product id, make sure it's a number and check if it's in the cart
if (isset($_GET['remove']) && is_numeric($_GET['remove']) && isset($_SESSION['cart']) && isset($_SESSION['cart'][$_GET['remove']])) {
// Remove the product from the shopping cart
array_splice($_SESSION['cart'], $_GET['remove'], 1);
header('Location: ' . url('index.php?page=cart'));
exit;
}
// Empty the cart
if (isset($_POST['emptycart']) && isset($_SESSION['cart'])) {
// Remove all products from the shopping cart
unset($_SESSION['cart']);
header('Location: ' . url('index.php?page=cart'));
exit;
}
// Update product quantities in cart if the user clicks the "Update" button on the shopping cart page
if ((isset($_POST['update']) || isset($_POST['checkout'])) && isset($_SESSION['cart'])) {
// Iterate the post data and update quantities for every product in cart
foreach ($_POST as $k => $v) {
if (strpos($k, 'quantity') !== false && is_numeric($v)) {
$id = str_replace('quantity-', '', $k);
// abs() function will prevent minus quantity and (int) will ensure the value is an integer (number)
$quantity = abs((int)$v);
// Always do checks and validation
if (is_numeric($id) && isset($_SESSION['cart'][$id]) && $quantity > 0) {
// Update new quantity
$_SESSION['cart'][$id]['quantity'] = $quantity;
}
}
}
// Send the user to the place order page if they click the Place Order button, also the cart should not be empty
if (isset($_POST['checkout']) && !empty($_SESSION['cart'])) {
header('Location: ' . url('index.php?page=checkout'));
exit;
}
header('Location: ' . url('index.php?page=cart'));
exit;
}
// Check if accessoiries are added
if (isset($_POST['accessoiries'])){
$options = '';
$quantity = 1;
$cart_product = &get_cart_product($_POST['add_product_id'], $options);
if ($cart_product) {
// Product exists in cart, update the quanity
$cart_product['quantity'] += $quantity;
} else {
// Product is not in cart, add it
$_SESSION['cart'][] = [
'id' => $_POST['add_product_id'],
'quantity' => $quantity,
'options' => $options,
'options_price' => $_POST['add_product_price'],
'options_weight' => $_POST['add_product_weight'],
'shipping_price' => 0.00
];
}
}
// Check if samples are added
if (isset($_POST['samples'])){
$options = $h2_cart_sample_product;
$quantity = 1;
$cart_product = &get_cart_product($_POST['add_product_id'], $options);
if ($cart_product) {
// Do no nothing
} else {
//remove existing product from CART
foreach ($_SESSION['cart'] as $num => $product){
if ($product['options'] == $h2_cart_sample_product && !empty(category_id_checkout_samples)){
array_splice($_SESSION['cart'], $num, 1);
}
}
//ADD Product to the chart
$_SESSION['cart'][] = [
'id' => $_POST['add_product_id'],
'quantity' => $quantity,
'options' => $options,
'options_price' => $_POST['add_product_price'],
'options_weight' => $_POST['add_product_weight'],
'shipping_price' => 0.00
];
}
}
// Check the session variable for products in cart
$products_in_cart = isset($_SESSION['cart']) ? $_SESSION['cart'] : [];
$subtotal = 0.00;
// If there are products in cart
if ($products_in_cart) {
// There are products in the cart so we need to select those products from the database
// Products in cart array to question mark string array, we need the SQL statement to include: IN (?,?,?,...etc)
$array_to_question_marks = implode(',', array_fill(0, count($products_in_cart), '?'));
// Prepare SQL statement
// $stmt = $pdo->prepare('SELECT p.id, pc.category_id, p.* FROM products p LEFT JOIN products_categories pc ON p.id = pc.product_id LEFT JOIN categories c ON c.id = pc.category_id WHERE p.id IN (' . $array_to_question_marks . ') GROUP BY p.id');
$stmt = $pdo->prepare('SELECT p.*, (SELECT m.full_path FROM products_media pm JOIN media m ON m.id = pm.media_id WHERE pm.product_id = p.id ORDER BY pm.position ASC LIMIT 1) AS img FROM products p WHERE p.id IN (' . $array_to_question_marks . ')');
// Leverage the array_column function to retrieve only the id's of the products
$stmt->execute(array_column($products_in_cart, 'id'));
// Fetch the products from the database and return the result as an Array
$products = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Iterate the products in cart and add the meta data (product name, desc, etc)
foreach ($products_in_cart as &$cart_product) {
foreach ($products as $product) {
if ($cart_product['id'] == $product['id']) {
$cart_product['meta'] = $product;
// Calculate the subtotal
$subtotal += (float)$cart_product['options_price'] * (int)$cart_product['quantity'];
}
}
}
}
?>
<?=template_header('Shopping Cart')?>
<div class="cart content-wrapper">
<h1><?=$h1_cart_name?></h1>
<h2 style="text-align: center;margin-top: -35px;">
<a href="<?=url(link_to_collection)?>" style="text-decoration: none;color: #555555;padding: 10px 10px;font-size: 10px;">
<?=$navigation_back_to_store?>
</a>
</h2>
<form id="cart-form" action="" method="post">
<table>
<thead>
<tr>
<td colspan="2"><?=$tr_product?></td>
<td class="rhide"></td>
<td class="rhide"><?=$tr_price?></td>
<td><?=$tr_quantity?></td>
<td><?=$tr_total?></td>
</tr>
</thead>
<tbody>
<?php if (empty($products_in_cart)): ?>
<tr>
<td colspan="6" style="text-align:center;"><?=$cart_message_empty?></td>
</tr>
<?php else: ?>
<?php foreach ($products_in_cart as $num => $product): ?>
<tr>
<td class="img">
<?php if (!empty($product['meta']['img']) && file_exists($product['meta']['img'])): ?>
<a href="<?=url('index.php?page=product&id=' . $product['id'])?>">
<img src="<?=base_url?><?=$product['meta']['img']?>" width="50" height="50" alt="<?=$product['meta']['name']?>">
</a>
<?php endif; ?>
</td>
<td>
<a href="<?=url('index.php?page=product&id=' . $product['id'])?>"><?=$product['meta']['name']?></a>
<br>
<a href="<?=url('index.php?page=cart&remove=' . $num)?>" class="remove">Remove</a>
</td>
<td class="options rhide">
<?=htmlspecialchars(str_replace(',', ', ', $product['options']), ENT_QUOTES)?>
<input type="hidden" name="options" value="<?=htmlspecialchars($product['options'], ENT_QUOTES)?>">
</td>
<td class="price rhide"><?=currency_code?><?=number_format($product['options_price'],2)?></td>
<?php if ($product['options'] == $h2_cart_sample_product && !empty(category_id_checkout_samples)) : ?>
<td class="quantity">
<input type="number" class="ajax-update" name="quantity-<?=$num?>" value="1" min="1" max="1" placeholder="Quantity" readonly>
</td>
<?php else: ?>
<td class="quantity">
<input type="number" class="ajax-update" name="quantity-<?=$num?>" value="<?=$product['quantity']?>" min="1" <?php if ($product['meta']['quantity'] != -1): ?>max="<?=$product['meta']['quantity']?>"<?php endif; ?> placeholder="Quantity" required>
</td>
<?php endif; ?>
<td class="price product-total"><?=currency_code?><?=number_format($product['options_price'] * $product['quantity'],2)?></td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</form>
<?php if (!empty($products_in_cart) && !empty(category_id_checkout_suggestions)): ?>
<?=getAccessoiries($pdo,category_id_checkout_suggestions)?>
<?php endif; ?>
<?php if (!empty($products_in_cart) && !empty(category_id_checkout_samples)): ?>
<?=getSamples($pdo,category_id_checkout_samples)?>
<?php endif; ?>
<div class="total">
<span class="text"><?=$total_subtotal?></span>
<span class="price"><?=currency_code?><?=number_format($subtotal,2)?></span>
<span class="note"><?=$total_note?></span>
</div>
<div class="buttons">
<input type="submit" form ="cart-form" value="<?=$btn_emptycart?>" name="emptycart" class="btn" title="Remove cart" style="background:none;">
<input type="submit" form ="cart-form" value="<?=$btn_update?>" name="update" class="btn" title="Refresh cart">
<input type="submit" form ="cart-form" value="<?=$btn_checkout?>" name="checkout" class="btn" style="background-color:green;">
</div>
<h4 style="text-align: right;margin-top: -35px;">
<a href="<?=url(link_to_collection)?>" style="text-decoration: none;color: #555555;padding: 10px 10px;font-size: 10px;">
<?=$navigation_back_to_store?>
</a>
</h4>
</div>
<script>
const buttonRight = document.getElementById("slideRight");
const buttonLeft = document.getElementById("slideLeft");
buttonRight.onclick = function() {
document.getElementById('add_samples_container').scrollLeft += 50;
};
buttonLeft.onclick = function() {
document.getElementById('add_samples_container').scrollLeft -= 50;
};
</script>
<?=template_footer()?>

774
checkout.php Normal file
View File

@@ -0,0 +1,774 @@
<?php
// Prevent direct access to file
defined(security_key) or exit;
// Default values for the input form elements
$account = [
'first_name' => '',
'last_name' => '',
'address_street' => '',
'address_city' => '',
'address_state' => '',
'address_zip' => '',
'address_country' => '',
'role' => 'Member',
'address_phone' => ''
];
// Error array, output errors on the form
$errors = [];
// Redirect the user if the shopping cart is empty
if (empty($_SESSION['cart'])) {
header('Location: ' . url('index.php?page=cart'));
exit;
}
// Check if user is logged in
if (isset($_SESSION['account_loggedin'])) {
$stmt = $pdo->prepare('SELECT * FROM accounts WHERE id = ?');
$stmt->execute([ $_SESSION['account_id'] ]);
// Fetch the account from the database and return the result as an Array
$account = $stmt->fetch(PDO::FETCH_ASSOC);
}
// Update discount code
if (isset($_POST['discount_code']) && !empty($_POST['discount_code'])) {
$_SESSION['discount'] = $_POST['discount_code'];
} else if (isset($_POST['discount_code']) && empty($_POST['discount_code']) && isset($_SESSION['discount'])) {
unset($_SESSION['discount']);
}
// Variables
$products_in_cart = isset($_SESSION['cart']) ? $_SESSION['cart'] : [];
$subtotal = 0.00;
$shippingtotal = 0.00;
$discounttotal = 0.00;
$taxtotal = 0.00;
$weighttotal = 0;
$selected_country = isset($_POST['address_country']) ? $_POST['address_country'] : $account['address_country'];
$selected_shipping_method = isset($_POST['shipping_method']) ? $_POST['shipping_method'] : null;
$selected_shipping_method_name = '';
$shipping_methods_available = [];
// If there are products in cart
if ($products_in_cart) {
// There are products in the cart so we need to select those products from the database
// Products in cart array to question mark string array, we need the SQL statement to include: IN (?,?,?,...etc)
$array_to_question_marks = implode(',', array_fill(0, count($products_in_cart), '?'));
$stmt = $pdo->prepare('SELECT p.*, (SELECT m.full_path FROM products_media pm JOIN media m ON m.id = pm.media_id WHERE pm.product_id = p.id ORDER BY pm.position ASC LIMIT 1) AS img, (SELECT GROUP_CONCAT(pc.category_id) FROM products_categories pc WHERE pc.product_id = p.id) AS categories FROM products p WHERE p.id IN (' . $array_to_question_marks . ')');
// We use the array_column to retrieve only the id's of the products
$stmt->execute(array_column($products_in_cart, 'id'));
// Fetch the products from the database and return the result as an Array
$products = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Retrieve the discount code
if (isset($_SESSION['discount'])) {
$stmt = $pdo->prepare('SELECT * FROM discounts WHERE discount_code = ?');
$stmt->execute([ $_SESSION['discount'] ]);
$discount = $stmt->fetch(PDO::FETCH_ASSOC);
}
// Get tax
$stmt = $pdo->prepare('SELECT * FROM taxes WHERE country = ?');
$stmt->execute([ isset($_POST['address_country']) ? $_POST['address_country'] : $account['address_country'] ]);
$tax = $stmt->fetch(PDO::FETCH_ASSOC);
$tax_rate = $tax ? $tax['rate'] : 0.00;
// Get the current date
$current_date = strtotime((new DateTime())->format('Y-m-d H:i:s'));
// Retrieve shipping methods
$stmt = $pdo->query('SELECT * FROM shipping');
$shipping_methods = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Iterate the products in cart and add the meta data (product name, desc, etc)
foreach ($products_in_cart as &$cart_product) {
foreach ($products as $product) {
if ($cart_product['id'] == $product['id']) {
// If product no longer in stock, prepare for removal
if ((int)$product['quantity'] === 0) {
$cart_product['remove'] = 1;
} else {
$cart_product['meta'] = $product;
// Prevent the cart quantity exceeding the product quantity
$cart_product['quantity'] = ($cart_product['quantity'] > $product['quantity'] && $product['quantity'] !== -1) ? $product['quantity'] : $cart_product['quantity'];
$product_weight = $cart_product['options_weight'];
$weighttotal += $product_weight;
// Calculate the subtotal
$product_price = (float)$cart_product['options_price'];
$subtotal += $product_price * (int)$cart_product['quantity'];
// Calculate the final price, which includes tax
$cart_product['final_price'] = $product_price; //+ (($tax_rate / 100) * $product_price);
//-------------------------------
//TAX ON TOP OFF OF PRICE
//-------------------------------
//$taxtotal += (($tax_rate / 100) * $product_price) * (int)$cart_product['quantity'];
//-------------------------------
//TAX INCLUDED IN PRICE
//-------------------------------
$taxtotal += ($product_price - ($product_price/ (1 + ($tax_rate / 100))))* (int)$cart_product['quantity'];
//-------------------------------
//-------------------------------
// Check which products are eligible for a discount
if (isset($discount) && $discount && $current_date >= strtotime($discount['start_date']) && $current_date <= strtotime($discount['end_date'])) {
// Check whether product list is empty or if product id is whitelisted
if (empty($discount['product_ids']) || in_array($product['id'], explode(',', $discount['product_ids']))) {
// Check whether category list is empty or if category id is whitelisted
if (empty($discount['category_ids']) || array_intersect(explode(',', $product['categories']), explode(',', $discount['category_ids']))) {
$cart_product['discounted'] = true;
}
}
}
}
}
}
}
// Remove products that are out of stock
for ($i = 0; $i < count($products_in_cart); $i++) {
if (isset($products_in_cart[$i]['remove'])) {
unset($_SESSION['cart'][$i]);
unset($products_in_cart[$i]);
}
}
$_SESSION['cart'] = array_values($_SESSION['cart']);
$products_in_cart = array_values($products_in_cart);
// Redirect the user if the shopping cart is empty
if (empty($products_in_cart)) {
header('Location: ' . url('index.php?page=cart'));
exit;
}
// Calculate the shipping
foreach ($products_in_cart as &$cart_product) {
foreach ($shipping_methods as $shipping_method) {
// Product weight
$product_weight = $cart_product['options_weight'] ? $cart_product['options_weight'] : $weighttotal;
// Determine the price
$product_price = $shipping_method['type'] == 'Single Product' ? (float)$cart_product['options_price'] : $subtotal;
// Check if no country required or if shipping method only available in specified countries
if (empty($shipping_method['countries']) || in_array($selected_country, explode(',', $shipping_method['countries']))) {
// Compare the price and weight to meet shipping method requirements
if ($shipping_method['id'] == $selected_shipping_method && $product_price >= $shipping_method['price_from'] && $product_price <= $shipping_method['price_to'] && $product_weight >= $shipping_method['weight_from'] && $product_weight <= $shipping_method['weight_to']) {
if ($shipping_method['type'] == 'Single Product') {
// Calculate single product price
$cart_product['shipping_price'] += (float)$shipping_method['price'] * (int)$cart_product['quantity'];
$shippingtotal += $cart_product['shipping_price'];
} else {
// Calculate entire order price
$cart_product['shipping_price'] = (float)$shipping_method['price'] / count($products_in_cart);
$shippingtotal = (float)$shipping_method['price'];
}
$shipping_methods_available[] = $shipping_method['id'];
} else if ($product_price >= $shipping_method['price_from'] && $product_price <= $shipping_method['price_to'] && $product_weight >= $shipping_method['weight_from'] && $product_weight <= $shipping_method['weight_to']) {
// No method selected, so store all methods available
$shipping_methods_available[] = $shipping_method['id'];
}
}
// Update selected shipping method name
if ($shipping_method['id'] == $selected_shipping_method) {
$selected_shipping_method_name = $shipping_method['name'];
}
}
}
// Number of discounted products
$num_discounted_products = count(array_column($products_in_cart, 'discounted'));
// Iterate the products and update the price for the discounted products
foreach ($products_in_cart as &$cart_product) {
if (isset($cart_product['discounted']) && $cart_product['discounted']) {
$price = &$cart_product['final_price'];
if ($discount['discount_type'] == 'Percentage') {
$d = (float)$price * ((float)$discount['discount_value'] / 100);
//$price -= $d;
$discounttotal += $d * (int)$cart_product['quantity'];
}
if ($discount['discount_type'] == 'Fixed') {
$d = (float)$discount['discount_value'] / $num_discounted_products;
//$price -= $d / (int)$cart_product['quantity'];
$discounttotal += $d;
}
}
}
//Override TAXTOTAAL IN CASE OF DISCOUNTS
//-------------------------------
//TAX ON TOP OFF OF PRICE
//-------------------------------
//$taxtotal = ($tax_rate / 100) * (($subtotal) - $discounttotal);
//-------------------------------
//TAX INCLUDED IN PRICE
//-------------------------------
$taxable_total = $subtotal - $discounttotal;
$taxtotal = $taxable_total - ($taxable_total / (1 + ($tax_rate / 100)));
//-------------------------------
//-------------------------------
}
// Make sure when the user submits the form all data was submitted and shopping cart is not empty
if (isset($_POST['method'], $_POST['first_name'], $_POST['last_name'], $_POST['address_street'], $_POST['address_city'], $_POST['address_state'], $_POST['address_zip'], $_POST['address_country'], $_POST['address_phone'], $_SESSION['cart']) && !isset($_POST['update'])) {
$account_id = null;
// If the user is already logged in
if (isset($_SESSION['account_loggedin'])) {
// Account logged-in, update the user's details
$stmt = $pdo->prepare('UPDATE accounts SET first_name = ?, last_name = ?, address_street = ?, address_city = ?, address_state = ?, address_zip = ?, address_country = ?, address_phone = ? WHERE id = ?');
$stmt->execute([ $_POST['first_name'], $_POST['last_name'], $_POST['address_street'], $_POST['address_city'], $_POST['address_state'], $_POST['address_zip'], $_POST['address_country'], $_POST['address_phone'], $_SESSION['account_id'] ]);
$account_id = $_SESSION['account_id'];
} else if (isset($_POST['email'], $_POST['password'], $_POST['cpassword']) && filter_var($_POST['email'], FILTER_VALIDATE_EMAIL) && !empty($_POST['password']) && !empty($_POST['cpassword'])) {
// User is not logged in, check if the account already exists with the email they submitted
$stmt = $pdo->prepare('SELECT id FROM accounts WHERE email = ?');
$stmt->execute([ $_POST['email'] ]);
if ($stmt->fetch(PDO::FETCH_ASSOC)) {
// Email exists, user should login instead...
$errors[] = $error_account_name;
}
if (strlen($_POST['password']) > 20 || strlen($_POST['password']) < 5) {
// Password must be between 5 and 20 characters long.
$errors[] = $error_account_password_rules;
}
if ($_POST['password'] != $_POST['cpassword']) {
// Password and confirm password fields do not match...
$errors[] = $error_account_password_match;
}
if (!$errors) {
// Hash the password
$password = password_hash($_POST['password'], PASSWORD_DEFAULT);
// Email doesnt exist, create new account
$stmt = $pdo->prepare('INSERT INTO accounts (email, password, first_name, last_name, address_street, address_city, address_state, address_zip, address_country, 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['address_phone'] ]);
$account_id = $pdo->lastInsertId();
$stmt = $pdo->prepare('SELECT * FROM accounts WHERE id = ?');
$stmt->execute([ $account_id ]);
// Fetch the account from the database and return the result as an Array
$account = $stmt->fetch(PDO::FETCH_ASSOC);
}
} else if (account_required) {
$errors[] = $error_account;
}
if (!$errors && $products_in_cart) {
$payment_amount = (($subtotal)-$discounttotal)+$shippingtotal;
// No errors, process the order
if (pay_on_delivery_enabled && $_POST['method'] == 'payondelivery') {
// Process Normal Checkout
// Generate unique transaction ID
$transaction_id = strtoupper(uniqid('SC') . substr(md5(mt_rand()), 0, 5));
// Insert transaction into database
$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, shipping_method, shipping_amount, discount_code, address_phone,tax_amount) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)');
$stmt->execute([
$transaction_id,
$payment_amount,
default_payment_status,
date('Y-m-d H:i:s'),
isset($account['email']) && !empty($account['email']) ? $account['email'] : $_POST['email'],
$_POST['first_name'],
$_POST['last_name'],
$_POST['address_street'],
$_POST['address_city'],
$_POST['address_state'],
$_POST['address_zip'],
$_POST['address_country'],
$account_id,
'PayOnDelivery',
$selected_shipping_method_name,
$shippingtotal,
isset($_SESSION['discount']) ? $_SESSION['discount'] : '',
$_POST['address_phone'],
$taxtotal
]);
// Get order ID
$order_id = $pdo->lastInsertId();
// Iterate products and deduct quantities
foreach ($products_in_cart as $product) {
// For every product in the shopping cart insert a new transaction into our database
$stmt = $pdo->prepare('INSERT INTO transactions_items (txn_id, item_id, item_price, item_quantity, item_options) VALUES (?,?,?,?,?)');
$stmt->execute([ $transaction_id, $product['id'], $product['final_price'], $product['quantity'], $product['options'] ]);
// Update product quantity in the products table
$stmt = $pdo->prepare('UPDATE products SET quantity = quantity - ? WHERE quantity > 0 AND id = ?');
$stmt->execute([ $product['quantity'], $product['id'] ]);
// Deduct option quantities
if ($product['options']) {
$options = explode(',', $product['options']);
foreach ($options as $opt) {
$option_name = explode('-', $opt)[0];
$option_value = explode('-', $opt)[1];
$stmt = $pdo->prepare('UPDATE products_options SET quantity = quantity - ? WHERE quantity > 0 AND title = ? AND (name = ? OR name = "")');
$stmt->execute([ $product['quantity'], $option_name, $option_value ]);
}
}
}
//Disable giftcard
if (isset($_SESSION['discount'])){
if (preg_match("/[#][0-9]/", $_SESSION['discount']) == 1){
useGiftCart($pdo, $_SESSION['discount']);
}
}
// Authenticate the user
if ($account_id != null) {
// Log the user in with the details provided
session_regenerate_id();
$_SESSION['account_loggedin'] = TRUE;
$_SESSION['account_id'] = $account_id;
$_SESSION['account_role'] = $account ? $account['role'] : 'Member';
}
// Send order details to the specified email address
send_order_details_email(
isset($account['email']) && !empty($account['email']) ? $account['email'] : $_POST['email'],
$products_in_cart,
$_POST['first_name'],
$_POST['last_name'],
$_POST['address_street'],
$_POST['address_city'],
$_POST['address_state'],
$_POST['address_zip'],
$_POST['address_country'],
$subtotal,
$discounttotal,
$shippingtotal,
$taxtotal,
$payment_amount,
$order_id
);
header('Location: ' . url('index.php?page=placeorder'));
exit;
}
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Mollie ++++++++++++++++++++++++++++++++++++++++++++++++++++
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
if (mollie_enabled && $_POST['method'] == 'mollie') {
// Process Normal Checkout
// Generate unique transaction ID
$transaction_id = strtoupper(uniqid('SC') . substr(md5(mt_rand()), 0, 5));
// Insert transaction into database
$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, shipping_method, shipping_amount, discount_code, address_phone, tax_amount) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)');
$stmt->execute([
$transaction_id,
$payment_amount,
default_payment_status,
date('Y-m-d H:i:s'),
isset($account['email']) && !empty($account['email']) ? $account['email'] : $_POST['email'],
$_POST['first_name'],
$_POST['last_name'],
$_POST['address_street'],
$_POST['address_city'],
$_POST['address_state'],
$_POST['address_zip'],
$_POST['address_country'],
$account_id,
'Debit/Credit',
$selected_shipping_method_name,
$shippingtotal,
isset($_SESSION['discount']) ? $_SESSION['discount'] : '',
$_POST['address_phone'],
$taxtotal
]);
// Get order ID
$order_id = $pdo->lastInsertId();
// Iterate products and deduct quantities
foreach ($products_in_cart as $product) {
// For every product in the shopping cart insert a new transaction into our database
$stmt = $pdo->prepare('INSERT INTO transactions_items (txn_id, item_id, item_price, item_quantity, item_options) VALUES (?,?,?,?,?)');
$stmt->execute([ $transaction_id, $product['id'], $product['final_price'], $product['quantity'], $product['options'] ]);
// Update product quantity in the products table
$stmt = $pdo->prepare('UPDATE products SET quantity = quantity - ? WHERE quantity > 0 AND id = ?');
$stmt->execute([ $product['quantity'], $product['id'] ]);
// Deduct option quantities
if ($product['options']) {
$options = explode(',', $product['options']);
foreach ($options as $opt) {
$option_name = explode('-', $opt)[0];
$option_value = explode('-', $opt)[1];
$stmt = $pdo->prepare('UPDATE products_options SET quantity = quantity - ? WHERE quantity > 0 AND title = ? AND (name = ? OR name = "")');
$stmt->execute([ $product['quantity'], $option_name, $option_value ]);
}
}
}
// Authenticate the user
if ($account_id != null) {
// Log the user in with the details provided
session_regenerate_id();
$_SESSION['account_loggedin'] = TRUE;
$_SESSION['account_id'] = $account_id;
$_SESSION['account_role'] = $account ? $account['role'] : 'Member';
}
try {
/*
* Initialize the Mollie API library with your API key.
*
* See: https://www.mollie.com/dashboard/developers/api-keys
*/
require "initialize.php";
/*
* Generate a unique order id for this example. It is important to include this unique attribute
* in the redirectUrl (below) so a proper return page can be shown to the customer.
*/
$orderId = $transaction_id;
$value = number_format(($subtotal-$discounttotal)+$shippingtotal,2,'.','');
/*
* Determine the url parts to these example files.
*/
$protocol = isset($_SERVER['HTTPS']) && strcasecmp('off', $_SERVER['HTTPS']) !== 0 ? "https" : "http";
$hostname = $_SERVER['HTTP_HOST'];
$path = dirname($_SERVER['REQUEST_URI'] ?? $_SERVER['PHP_SELF']);
/*
* Payment parameters:
* amount Amount in EUROs. This example creates a € 10,- payment.
* description Description of the payment.
* redirectUrl Redirect location. The customer will be redirected there after the payment.
* webhookUrl Webhook location, used to report when the payment changes state.
* metadata Custom metadata that is stored with the payment.
*/
if (rewrite_url){
$redirectURL = $protocol.'://'.$hostname.$path.'placeorder/'.$orderId;
}else{
$redirectURL = $protocol.'://'.$hostname.$path.'index.php?page=placeorder&order_id='.$orderId;
}
$payment = $mollie->payments->create([
"amount" => [
"currency" => "EUR",
"value" => "{$value}", // You must send the correct number of decimals, thus we enforce the use of strings
],
"description" => "Order #{$orderId}",
"redirectUrl" => "$redirectURL",
"webhookUrl" => "{$protocol}://{$hostname}{$path}webhook.php",
"metadata" => [
"order_id" => $orderId,
],
]);
/*
* Send the customer off to complete the payment.
* This request should always be a GET, thus we enforce 303 http response code
*/
// Send order details to the specified email address
send_order_details_email(
isset($account['email']) && !empty($account['email']) ? $account['email'] : $_POST['email'],
$products_in_cart,
$_POST['first_name'],
$_POST['last_name'],
$_POST['address_street'],
$_POST['address_city'],
$_POST['address_state'],
$_POST['address_zip'],
$_POST['address_country'],
$subtotal,
$discounttotal,
$shippingtotal,
$taxtotal,
$payment_amount,
$order_id
);
// Send customer to checkout
header("Location: " . $payment->getCheckoutUrl(), true, 303);
} catch (\Mollie\Api\Exceptions\ApiException $e) {
echo "API call failed: " . htmlspecialchars($e->getMessage());
}
exit;
}
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// PayPal Payment + +++++++++++++++++++++++++++++++++++++++++
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
if (paypal_enabled && $_POST['method'] == 'paypal') {
// Process Normal Checkout first then do PayPal related
// Generate unique transaction ID
$transaction_id = strtoupper(uniqid('SC') . substr(md5(mt_rand()), 0, 5));
// Insert transaction into database
$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, shipping_method, shipping_amount, discount_code, address_phone, tax_amount) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)');
$stmt->execute([
$transaction_id,
$payment_amount,
default_payment_status,
date('Y-m-d H:i:s'),
isset($account['email']) && !empty($account['email']) ? $account['email'] : $_POST['email'],
$_POST['first_name'],
$_POST['last_name'],
$_POST['address_street'],
$_POST['address_city'],
$_POST['address_state'],
$_POST['address_zip'],
$_POST['address_country'],
$account_id,
'paypal',
$selected_shipping_method_name,
$shippingtotal,
isset($_SESSION['discount']) ? $_SESSION['discount'] : '',
$_POST['address_phone'],
$taxtotal
]);
// Get order ID
$order_id = $pdo->lastInsertId();
// Iterate products and deduct quantities
foreach ($products_in_cart as $product) {
// For every product in the shopping cart insert a new transaction into our database
$stmt = $pdo->prepare('INSERT INTO transactions_items (txn_id, item_id, item_price, item_quantity, item_options) VALUES (?,?,?,?,?)');
$stmt->execute([ $transaction_id, $product['id'], $product['final_price'], $product['quantity'], $product['options'] ]);
// Update product quantity in the products table
$stmt = $pdo->prepare('UPDATE products SET quantity = quantity - ? WHERE quantity > 0 AND id = ?');
$stmt->execute([ $product['quantity'], $product['id'] ]);
// Deduct option quantities
if ($product['options']) {
$options = explode(',', $product['options']);
foreach ($options as $opt) {
$option_name = explode('-', $opt)[0];
$option_value = explode('-', $opt)[1];
$stmt = $pdo->prepare('UPDATE products_options SET quantity = quantity - ? WHERE quantity > 0 AND title = ? AND (name = ? OR name = "")');
$stmt->execute([ $product['quantity'], $option_name, $option_value ]);
}
}
}
if ($account_id != null) {
// Log the user in with the details provided
session_regenerate_id();
$_SESSION['account_loggedin'] = TRUE;
$_SESSION['account_id'] = $account_id;
$_SESSION['account_role'] = $account ? $account['role'] : 'Member';
}
//Process Payment
require_once __DIR__."/lib/paypal/paypal.php";
$base = PAYPAL_URL;
$id = PAYPAL_CLIENT_ID;
$secret = PAYPAL_CLIENT_SECRET;
//init input
$order = $transaction_id;
$price = $payment_amount;
$currency = "EUR";
//make payment
$paypal = new paypalCurl();
$paypal->init($id,$secret,$base);
$result = $paypal->makePaymentURL($order,$price,$currency);
if ($result->status === true) {
header("location:". $result->url);
die;
}
else { //raise error
echo $result->msg;
die;
}
}
}
// Preserve form details if the user encounters an error
$account = [
'first_name' => $_POST['first_name'],
'last_name' => $_POST['last_name'],
'address_street' => $_POST['address_street'],
'address_city' => $_POST['address_city'],
'address_state' => $_POST['address_state'],
'address_zip' => $_POST['address_zip'],
'address_country' => $_POST['address_country'],
'address_phone' => $_POST['address_phone']
];
}
$terms_link = url('index.php?page=termsandconditions');
?>
<?=template_header('Checkout')?>
<div class="checkout content-wrapper">
<h1><?=$h1_checkout?></h1>
<p class="error"><?=implode('<br>', $errors)?></p>
<?php if (!isset($_SESSION['account_loggedin'])): ?>
<p><?=$account_available?> <a href="<?=url('index.php?page=myaccount')?>"><?=$account_log_in?></a></p>
<?php endif; ?>
<form action="" method="post">
<div class="container">
<div class="shipping-details">
<div id="dropin-container"></div>
<h2><?=$payment_method?></h2>
<div class="payment-methods">
<?php if (mollie_enabled): ?>
<input id="mollie" type="radio" name="method" value="mollie" <?= ((mollie_default)? 'checked':'') ?>>
<label for="mollie">
<img src="./custom/assets/iDEAL.png" style="width: 50px;" alt="<?=$payment_method_1?>">
<img src="./custom/assets/bancontact.png" style="width: 50px;" alt="<?=$payment_method_1?>">
</label>
<?php endif; ?>
<?php if (paypal_enabled): ?>
<input id="paypal" type="radio" name="method" value="paypal" <?= ((paypal_default)? 'checked':'') ?>>
<label for="paypal"><img src="https://www.paypalobjects.com/webstatic/mktg/Logo/pp-logo-100px.png" alt="PayPal Logo"></label>
<?php endif; ?>
<?php if (pay_on_delivery_enabled): ?>
<input id="payondelivery" type="radio" name="method" value="payondelivery" <?= ((pay_on_delivery_default)? 'checked':'') ?> >
<label for="payondelivery"><?=$payment_method_2?></label>
<?php endif; ?>
</div>
<?php if (!isset($_SESSION['account_loggedin'])): ?>
<h2><?=$account_create_email?></h2>
<label for="email"></label>
<input type="email" name="email" id="email" placeholder="<?=$account_create_email?>" class="form-field" required>
<h2><?=$account_create?><?php if (!account_required): ?> <?=$account_create_optional?><?php endif; ?></h2>
<label for="password"><?=$account_create_password?></label>
<input type="password" name="password" id="password" placeholder="<?=$account_create_password?>" class="form-field" autocomplete="new-password">
<label for="cpassword"><?=$account_create_password_confirm?></label>
<input type="password" name="cpassword" id="cpassword" placeholder="<?=$account_create_password_confirm?>" class="form-field" autocomplete="new-password">
<?php endif; ?>
<h2><?=$h2_Shipping_details?></h2>
<div class="row1">
<label for="first_name"><?=$shipping_first_name?></label>
<input type="text" value="<?=htmlspecialchars($account['first_name'], ENT_QUOTES)?>" name="first_name" id="first_name" placeholder="<?=$shipping_first_name?>" class="form-field" required>
</div>
<div class="row2">
<label for="last_name"><?=$shipping_last_name?></label>
<input type="text" value="<?=htmlspecialchars($account['last_name'], ENT_QUOTES)?>" name="last_name" id="last_name" placeholder="<?=$shipping_last_name?>" class="form-field" required>
</div>
<label for="address_street"><?=$shipping_address?></label>
<input type="text" value="<?=htmlspecialchars($account['address_street'], ENT_QUOTES)?>" name="address_street" id="address_street" placeholder="<?=$shipping_address?>" class="form-field" required>
<label for="address_city"><?=$shipping_city?></label>
<input type="text" value="<?=htmlspecialchars($account['address_city'], ENT_QUOTES)?>" name="address_city" id="address_city" placeholder="<?=$shipping_city?>" class="form-field" required>
<div class="row1">
<label for="address_state"><?=$shipping_state?></label>
<input type="text" value="<?=htmlspecialchars($account['address_state'], ENT_QUOTES)?>" name="address_state" id="address_state" placeholder="<?=$shipping_state?>" class="form-field">
</div>
<div class="row2">
<label for="address_zip"><?=$shipping_zip?></label>
<input type="text" value="<?=htmlspecialchars($account['address_zip'], ENT_QUOTES)?>" name="address_zip" id="address_zip" placeholder="<?=$shipping_zip?>" class="form-field" required>
</div>
<label for="address_phone"><?=$shipping_phone?></label>
<input type="text" value="<?=htmlspecialchars($account['address_phone'], ENT_QUOTES)?>" name="address_phone" id="address_phone" placeholder="<?=$shipping_phone?>" class="form-field" required>
<label for="address_country"><?=$shipping_country?></label>
<select name="address_country" class="ajax-update form-field" required>
<?php foreach(get_countries() as $country): ?>
<option value="<?=$country?>"<?=$country==$account['address_country']?' selected':''?>><?=$country?></option>
<?php endforeach; ?>
</select>
</div>
<div class="cart-details">
<h2><?=$h2_shoppingcart?></h2>
<table>
<?php foreach($products_in_cart as $product): ?>
<tr>
<td><img src="<?=$product['meta']['img']?>" width="35" height="35" alt="<?=$product['meta']['name']?>"></td>
<td><?=$product['quantity']?> x <?=$product['meta']['name']?></td>
<td class="price"><?=currency_code?><?=number_format($product['options_price'] * $product['quantity'],2)?></td>
</tr>
<?php endforeach; ?>
</table>
<div class="discount-code">
<input type="text" class="ajax-update form-field" name="discount_code" placeholder="<?=$discount_label?>" value="<?=isset($_SESSION['discount']) ? $_SESSION['discount'] : ''?>">
<span class="result">
<?php if (isset($_SESSION['discount'], $discount) && !$discount): ?>
<?=$discount_error_1?>
<?php elseif (isset($_SESSION['discount'], $discount) && $current_date < strtotime($discount['start_date'])): ?>
<?=$discount_error_1?>
<?php elseif (isset($_SESSION['discount'], $discount) && $current_date > strtotime($discount['end_date'])): ?>
<?=$discount_error_2?>
<?php elseif (isset($_SESSION['discount'], $discount)): ?>
<?=$discount_message?>
<?php endif; ?>
</span>
</div>
<div class="shipping-methods-container">
<?php if ($shipping_methods_available): ?>
<div class="shipping-methods">
<h3><?=$h3_shipping_method?></h3>
<?php foreach($shipping_methods as $k => $method): ?>
<?php if (!in_array($method['id'], $shipping_methods_available)) continue; ?>
<div class="shipping-method">
<input type="radio" class="ajax-update" id="sm<?=$k?>" name="shipping_method" value="<?=$method['id']?>" required<?=$selected_shipping_method==$method['id']?' checked':''?>>
<label for="sm<?=$k?>"><?=$method['name']?> (<?=currency_code?><?=number_format($method['price'], 2)?><?=$method['type']=='Single Product'?' per item':''?>)</label>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
<div class="summary">
<div class="subtotal">
<span><?=$total_subtotal?></span>
<span><?=currency_code?><?=number_format($subtotal,2)?></span>
</div>
<div class="shipping">
<span><?=$total_shipping?></span>
<span><?=currency_code?><?=number_format($shippingtotal,2)?></span>
</div>
<?php if ($discounttotal > 0): ?>
<div class="discount">
<span><?=$total_discount?></span>
<span>-<?=currency_code?><?=number_format(round($discounttotal, 1),2)?></span>
</div>
<?php endif; ?>
<?php if ($tax): ?>
<div class="vat">
<span>VAT <span class="alt">(<?=$tax['rate']?>%)</span></span>
<span><?=currency_code?><?=number_format($taxtotal,2)?></span>
</div>
<?php endif; ?>
</div>
<div class="total">
<span><?=$total_total?> <span class="alt"><?=$total_total_note?></span></span><span><?=currency_code?><?=number_format(($subtotal)-round($discounttotal,1)+$shippingtotal,2)?></span>
</div>
<div class="summary">
<div class="subtotal">
<span>
<input type="checkbox" id="consent" name="consent_comms" value="1"><?=$order_consent_1?></a>
</span>
</div>
<div class="subtotal">
<span>
<input type="checkbox" id="consent" name="consent" value="1" required><?=$order_consent_2?> <a href="<?=$terms_link?>" target="_blank"><?=$order_consent_3?></a>
</span>
</div>
</div>
<div class="buttons">
<button type="submit" name="checkout" class="btn"><?=$btn_place_order?></button>
</div>
</div>
</div>
</form>
</div>
<?=template_footer()?>

BIN
custom/.DS_Store vendored Normal file

Binary file not shown.

BIN
custom/assets/.DS_Store vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 346 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

BIN
custom/assets/download-pdf.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
custom/assets/iDEAL.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 493 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 767 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 456 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 876 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 321.95 129.27"><defs><style>.cls-1{fill:#231f20;}</style></defs><g id="Layer_2-2"><path class="cls-1" d="m151.62,97.66c-.15,1.83-.4,3.71-1.96,4.94-2.63,1.97-6.25,2.13-9.46,2.16h-10.58c-.05,0-.09-.03-.09-.09v-15.39c0-.05.03-.09.09-.09h10.58c3.7.08,8.54.11,10.68,3.65.71,1.57.76,3.16.74,4.81h0Zm7.1-.68c0-9.93-6.18-14.89-18.52-14.89h-17.68c-.05,0-.09.03-.09.09v47c0,.05.03.09.09.09h6.93c.05,0,.09-.03.09-.09v-17.22c0-.05.03-.09.09-.09h10.58c1.42,0,2.75-.07,3.99-.2.04,0,.08.02.09.05l6.45,17.48s.04.06.08.06h6.42c.3,0,.5-.3.4-.57l-6.86-18.5s0-.09.04-.1c5.27-2.27,7.9-6.63,7.9-13.09h0Z"/><path class="cls-1" d="m299.91,122.07v-39.55c0-.24-.19-.43-.43-.43h-6.59c-.05,0-.09.03-.09.09v47c0,.05.03.09.09.09h28.97c.05,0,.09-.03.09-.09v-6.59c0-.24-.19-.43-.43-.43h-21.53c-.05,0-.09-.03-.09-.09h0Z"/><path class="cls-1" d="m0,82.52v46.66c0,.05.03.09.09.09h7.01c.05,0,.09-.03.09-.09v-31.14c0-.08.09-.11.15-.06l15.04,13.78s.09.03.12,0l14.97-13.78c.05-.05.15,0,.15.06l-.06,14.59v16.55c0,.05.03.09.09.09h7.01c.05,0,.09-.03.09-.09v-46.65c0-.38-.45-.57-.73-.32l-21.55,19.82s-.09.03-.12,0L.73,82.21c-.28-.25-.73-.06-.73.32h0Z"/><path class="cls-1" d="m196.07,113.7l-15.51-31.53c-.03-.05-.08-.08-.13-.08h-7.12c-.32,0-.52.33-.38.62l22.9,46.33c.15.32.61.32.76,0l22.9-46.33c.15-.28-.07-.62-.38-.62h-7.12c-.05,0-.1.03-.13.08l-15.51,31.53c-.05.1-.21.1-.26,0h0Z"/><path class="cls-1" d="m82.06,82.09c-13.01,0-23.56,10.56-23.56,23.59s10.55,23.59,23.56,23.59,23.56-10.56,23.56-23.59-10.55-23.59-23.56-23.59Zm0,40.15c-9.14,0-14.97-7.42-14.97-16.57s5.83-16.57,14.97-16.57,14.97,7.42,14.97,16.57-5.83,16.57-14.97,16.57Z"/><path class="cls-1" d="m227.65,129.26h7.25s.06-.02.08-.05l5.49-11.06s.04-.05.08-.05h20.41s.06.02.08.05l5.56,11.06s.04.05.08.05h7.25c.32,0,.53-.34.39-.62l-23.16-46.31c-.16-.32-.61-.32-.77,0l-23.09,46.31c-.15.29.07.62.39.62h0Zm16.71-18.97l6-11.95c.16-.32.61-.32.77,0l6,11.95c.15.29-.06.62-.39.62h-12c-.32,0-.53-.34-.39-.62h.02Z"/><path class="cls-1" d="m179.53,59.76h-8.18V18.49c0-5.6-4.38-10.15-9.77-10.15s-9.78,4.55-9.78,10.15v41.26h-8.18V18.49c0-10.2,8.06-18.49,17.96-18.49s17.96,8.3,17.96,18.49v41.26h0Zm-4.78-3.4h1.39V18.49c0-8.32-6.53-15.09-14.56-15.09s-14.56,6.77-14.56,15.09v37.86h1.39V18.49c0-7.47,5.91-13.55,13.17-13.55s13.16,6.08,13.16,13.55v37.86h0Z"/><path class="cls-1" d="m177.83,18.49v39.56h-4.78V18.49c0-6.54-5.13-11.85-11.47-11.85s-11.48,5.31-11.48,11.85v39.56h-4.78V18.49c0-9.28,7.28-16.79,16.26-16.79s16.26,7.52,16.26,16.79"/><path class="cls-1" d="m151.79,59.76h-8.18V18.49c0-5.6-4.38-10.15-9.77-10.15s-9.78,4.55-9.78,10.15v41.26h-8.18V18.49c0-10.2,8.06-18.49,17.96-18.49s17.96,8.3,17.96,18.49v41.26h0Zm-4.78-3.4h1.39V18.49c0-8.32-6.53-15.09-14.56-15.09s-14.56,6.77-14.56,15.09v37.86h1.39V18.49c0-7.47,5.91-13.55,13.17-13.55s13.16,6.08,13.16,13.55v37.86h0Z"/><path class="cls-1" d="m150.09,18.49v39.56h-4.78V18.49c0-6.54-5.13-11.85-11.47-11.85s-11.48,5.31-11.48,11.85v39.56h-4.78V18.49c0-9.28,7.28-16.79,16.26-16.79s16.26,7.52,16.26,16.79"/><path class="cls-1" d="m207.25,59.76h-8.18V18.49c0-5.6-4.38-10.15-9.77-10.15s-9.78,4.55-9.78,10.15v41.26h-8.18V18.49c0-10.2,8.06-18.49,17.96-18.49s17.96,8.3,17.96,18.49v41.26h0Zm-4.78-3.4h1.39V18.49c0-8.32-6.53-15.09-14.56-15.09s-14.56,6.77-14.56,15.09v37.86h1.39V18.49c0-7.47,5.91-13.55,13.17-13.55s13.16,6.08,13.16,13.55v37.86h0Z"/><path class="cls-1" d="m205.55,18.49v39.56h-4.78V18.49c0-6.54-5.13-11.85-11.47-11.85s-11.48,5.31-11.48,11.85v39.56h-4.78V18.49c0-9.28,7.28-16.79,16.26-16.79s16.26,7.52,16.26,16.79"/><path class="cls-1" d="m161.92,32.63l5.17,5.18c.2.2.2.51,0,.71l-5.17,5.18c-.2.2-.51.2-.71,0l-5.17-5.18c-.2-.2-.2-.51,0-.71l5.17-5.18c.2-.2.51-.2.71,0Z"/></g></svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 465 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 418 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 KiB

BIN
custom/assets/picture.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 359 KiB

BIN
custom/assets/picture2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 501 KiB

BIN
custom/assets/picture3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 KiB

BIN
custom/assets/picture4.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 368 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 24 KiB

1829
custom/css/custom.css Normal file

File diff suppressed because it is too large Load Diff

BIN
custom/css/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 B

1394
custom/css/style.css Normal file

File diff suppressed because it is too large Load Diff

974
custom/css/style.scss Normal file
View File

@@ -0,0 +1,974 @@
$font: -apple-system, BlinkMacSystemFont, "segoe ui", roboto, oxygen, ubuntu, cantarell, "fira sans", "droid sans", "helvetica neue", Arial, sans-serif;
$font-size: 16px;
$background-color: #FFFFFF;
$content-wrapper-width: 1050px;
$content-border-color: #EEEEEE;
$text-color: #555555;
$header-color: #394352;
$price-color: #999999;
$rrp-color: #BBBBBB;
$featured-header-font: Rockwell, "Courier Bold", Courier, Georgia, Times, "Times New Roman", sans-serif;
$featured-header-color: #FFFFFF;
$btn-color: #4b505c;
* {
box-sizing: border-box;
font-family: $font;
font-size: $font-size;
}
html {
height: 100%;
}
body {
position: relative;
min-height: 100%;
color: $text-color;
background-color: $background-color;
margin: 0;
padding-bottom: 100px; /* Same height as footer */
}
h1, h2, h3, h4, h5 {
color: $header-color;
}
.content-wrapper {
width: $content-wrapper-width;
margin: 0 auto;
&.error {
padding: 40px 0;
}
}
header {
position: relative;
border-bottom: 1px solid $content-border-color;
.content-wrapper {
display: flex;
}
h1, img {
display: flex;
flex-grow: 1;
flex-basis: 0;
font-size: 20px;
margin: 0;
padding: 24px 0;
}
nav {
display: flex;
flex-grow: 1;
flex-basis: 0;
justify-content: center;
align-items: center;
a {
white-space: nowrap;
text-decoration: none;
color: $text-color;
padding: 10px 10px;
margin: 0 10px;
&:hover {
border-bottom: 1px solid darken($background-color, 15);
}
}
}
.link-icons {
display: flex;
flex-grow: 1;
flex-basis: 0;
justify-content: flex-end;
align-items: center;
position: relative;
.search {
i {
font-size: 18px;
padding: 9px;
border-radius: 50%;
cursor: pointer;
&:hover {
background-color: darken($background-color, 3);
}
}
input {
display: none;
border: 0;
border-bottom: 1px solid $content-border-color;
padding: 10px 0;
max-width: 200px;
outline: none;
margin-right: 10px;
}
}
.responsive-toggle {
display: none;
}
a {
position: relative;
text-decoration: none;
color: #394352;
padding: 9px;
border-radius: 50%;
margin-left: 5px;
&:hover {
background-color: darken($background-color, 5);
}
i {
font-size: 18px;
}
span {
display: inline-flex;
justify-content: center;
align-items: center;
text-align: center;
background-color: lighten($header-color, 20);
background-color: #eea965;
border-radius: 50%;
color: #000;
font-size: 12px;
width: 16px;
height: 16px;
font-weight: 500;
position: absolute;
top: 0;
right: 0;
}
}
}
}
main {
.featured {
display: flex;
flex-direction: column;
background-repeat: no-repeat;
background-size: cover;
height: 500px;
align-items: center;
justify-content: center;
text-align: center;
h2 {
display: inline-block;
margin: 0;
width: $content-wrapper-width;
font-family: $featured-header-font;
font-size: 68px;
color: $featured-header-color;
padding-bottom: 10px;
}
p {
display: inline-block;
margin: 0;
width: $content-wrapper-width;
font-size: 24px;
color: $featured-header-color;
}
}
.recentlyadded {
h2 {
display: block;
font-weight: normal;
margin: 0;
padding: 40px 0;
font-size: 24px;
text-align: center;
width: 100%;
border-bottom: 1px solid $content-border-color;
}
}
.recentlyadded .products, .products .products-wrapper {
display: flex;
flex-wrap: wrap;
align-items: center;
padding: 40px 0 0 0;
.product {
display: block;
overflow: hidden;
text-decoration: none;
width: 25%;
padding-bottom: 60px;
img {
transform: scale(1.0);
transition: transform 1s;
}
.name {
display: block;
color: $text-color;
padding: 20px 0 2px 0;
}
.price {
display: block;
color: $price-color;
}
.rrp {
color: $rrp-color;
text-decoration: line-through;
}
&:hover {
img {
transform: scale(1.05);
transition: transform 1s;
}
.name {
text-decoration: underline;
}
}
}
}
> .product {
display: flex;
padding: 40px 0;
h1 {
font-size: 34px;
font-weight: normal;
margin: 0;
padding: 20px 0 10px 0;
}
.product-img-large {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 500px;
}
.product-small-imgs {
display: flex;
flex-flow: wrap;
.product-img-small {
display: flex;
align-items: center;
justify-content: center;
flex-basis: 31%;
border: 1px solid $content-border-color;
cursor: pointer;
margin: 20px 12px 0 0;
&:nth-child(3n) {
margin-right: 0;
}
&.selected {
border: 1px solid darken($content-border-color, 15);
}
}
}
.product-img-large img, .product-img-small img {
max-width: 100%;
max-height: 100%;
width: auto;
height: auto;
object-fit: contain;
}
.product-imgs {
flex: 1;
padding: 15px;
}
.product-wrapper {
padding-left: 25px;
flex: 1;
}
.prices {
display: flex;
}
.price {
display: block;
font-size: 22px;
color: $price-color;
}
.rrp {
color: $rrp-color;
text-decoration: line-through;
font-size: 22px;
padding-left: 10px;
}
form {
display: flex;
flex-flow: column;
margin: 25px 0 40px 0;
label {
padding-bottom: 10px;
}
select, input[type="number"], input[type="text"], input[type="datetime-local"] {
width: 400px;
padding: 8px 10px;
margin-bottom: 15px;
border: 1px solid darken($content-border-color, 10);
color: $text-color;
border-radius: 4px;
background-color: #fff;
}
.radio-checkbox {
display: flex;
flex-flow: wrap;
max-width: 400px;
input {
margin: 0 10px 10px 0;
}
label {
padding-right: 15px;
}
}
.btn {
margin-top: 10px;
width: 400px;
text-transform: uppercase;
}
}
}
> .products {
h1 {
display: block;
font-weight: normal;
margin: 0;
padding: 40px 0;
font-size: 24px;
text-align: center;
width: 100%;
}
.products-header {
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: 40px;
p {
margin: 0;
}
label {
padding-left: 20px;
}
select {
padding: 5px;
margin-left: 15px;
border: 1px solid darken($content-border-color, 10);
color: $text-color;
border-radius: 4px;
}
}
.buttons {
text-align: right;
padding-bottom: 40px;
a:first-child {
margin-right: 5px;
}
}
}
.cart, .myaccount {
h1 {
display: block;
font-weight: normal;
margin: 0;
padding: 40px 0;
font-size: 24px;
text-align: center;
width: 100%;
}
table {
width: 100%;
thead td {
padding: 30px 0;
border-bottom: 1px solid $content-border-color;
&:last-child {
text-align: right;
}
}
tbody td {
padding: 20px 0;
border-bottom: 1px solid $content-border-color;
&:last-child {
text-align: right;
}
}
.img {
width: 80px;
}
.remove {
color: #777777;
font-size: 12px;
padding-top: 3px;
&:hover {
text-decoration: underline;
}
}
.price, .options {
color: $price-color;
}
.options {
font-size: 14px;
max-width: 200px;
}
a {
text-decoration: none;
color: $text-color;
}
input[type="number"] {
width: 68px;
padding: 10px;
border: 1px solid darken($content-border-color, 10);
color: $text-color;
border-radius: 4px;
}
}
.total {
text-align: right;
padding: 30px 0 40px 0;
.text {
padding-right: 40px;
font-size: 18px;
}
.price {
font-size: 18px;
color: $price-color;
}
.note {
display: block;
padding-top: 15px;
}
}
.buttons {
text-align: right;
padding-bottom: 40px;
.btn {
margin: 0 0 10px 5px;
}
}
}
.placeorder {
h1 {
display: block;
font-weight: normal;
margin: 0;
padding: 40px 0;
font-size: 24px;
text-align: center;
width: 100%;
}
p {
text-align: center;
}
}
.checkout, .myaccount {
h1 {
display: block;
font-weight: normal;
margin: 0;
padding: 40px 0;
font-size: 24px;
text-align: center;
width: 100%;
}
.shipping-details {
width: 600px;
display: flex;
flex-flow: wrap;
padding-bottom: 40px;
h2 {
width: 100%;
font-weight: normal;
font-size: 20px;
padding: 30px 0 20px 0;
margin: 0 0 10px 0;
border-bottom: 1px solid $content-border-color;
&:first-child {
padding: 20px 0 20px 0;
}
}
label {
display: block;
padding: 15px 0 10px 0;
}
.row1, .row2 {
width: 50%;
display: inline-block;
}
.row1 {
padding-right: 10px;
}
.row2 {
padding-left: 10px;
}
}
}
.checkout {
.container {
display: flex;
align-items: flex-start;
}
.shipping-details {
margin-right: 25px;
width: 100%;
}
.payment-methods {
display: flex;
flex-flow: wrap;
width: 100%;
justify-content: space-between;
label {
text-decoration: none;
display: flex;
justify-content: center;
align-items: center;
border: 1px solid $content-border-color;
border-radius: 5px;
height: 60px;
width: 159px;
margin: 10px;
font-weight: 500;
color: lighten($header-color, 5);
padding: 0;
cursor: pointer;
.fa-stripe {
color: #6671E4;
}
&:nth-child(2), &:nth-child(8) {
margin-left: 0;
}
&:nth-child(3n) {
margin-right: 0;
}
&:hover {
border: 1px solid darken($content-border-color, 10);
}
}
input {
position: absolute;
top: -9999px;
left: -9999px;
visibility: hidden;
&:checked + label {
border:2px solid #7ed1a1;
}
}
}
.cart-details {
width: 90%;
box-shadow: 0px 0px 5px 0px rgba(0,0,0,0.15);
border-radius: 4px;
margin-left: 25px;
margin-bottom: 50px;
h2 {
margin: 0;
padding: 23px 15px;
font-weight: 500;
border-bottom: 1px solid $content-border-color;
}
table {
width: 100%;
padding: 20px;
.price {
text-align: right;
font-weight: 500;
}
td {
padding: 5px;
}
}
.discount-code {
padding: 0 23px 23px 23px;
.result {
display: block;
padding-top: 10px;
}
}
.shipping-methods {
border-top: 1px solid $content-border-color;
padding: 23px;
h3 {
margin: 0;
padding: 0 0 10px;
font-weight: 500;
}
.shipping-method {
padding-top: 10px;
margin: 0;
}
}
.total {
border-top: 1px solid $content-border-color;
display: flex;
justify-content: space-between;
padding: 23px;
font-size: 18px;
font-weight: 500;
}
.alt {
font-size: 14px;
color: mix($header-color, #fff, 45);
padding-left: 5px;
}
.summary {
border-top: 1px solid $content-border-color;
padding: 23px 0;
div {
display: flex;
justify-content: space-between;
padding: 10px 23px;
span {
font-size: 14px;
font-weight: 500;
}
}
.discount span {
color: #de0000;
}
}
.buttons {
margin: 0 23px 23px 23px;
.btn {
width: 100%;
}
}
}
}
.myaccount {
display: flex;
flex-flow: wrap;
.menu {
padding-right: 35px;
width: 300px;
a {
display: block;
text-decoration: none;
color: lighten($text-color, 20);
padding: 15px 0;
border-bottom: 1px solid lighten($content-border-color,3);
&:hover {
color: darken($text-color, 10);
}
}
}
.myorders, .mydownloads, .settings {
flex: 1;
padding-bottom: 50px;
}
.myorders {
.order {
box-shadow: 0px 0px 4px 0px rgba(0,0,0,0.15);
margin: 30px 0 30px 0;
.order-header {
display: flex;
justify-content: space-between;
border-bottom: 1px solid lighten($content-border-color,3);
> div {
display: flex;
padding: 15px;
div {
padding-right: 35px;
&:last-child {
padding-right: 0;
}
span {
display: block;
font-size: 14px;
}
}
}
}
.order-items {
padding: 15px;
table {
margin: 0;
padding: 0;
}
}
}
}
.settings {
form {
max-width: 400px;
.btn {
margin-top: 25px;
}
}
}
form {
width: 100%;
}
h2 {
width: 100%;
font-weight: normal;
font-size: 20px;
padding: 30px 0 20px 0;
margin: 0 0 10px 0;
border-bottom: 1px solid $content-border-color;
}
table {
padding-bottom: 40px;
tr:last-child td {
border-bottom: 0;
}
a {
display: inline-flex;
justify-content: center;
align-items: center;
height: 40px;
border: 1px solid $content-border-color;
border-radius: 4px;
padding: 0 10px;
font-size: 14px;
color: #de0000;
margin: 0 5px 5px 0;
&:hover {
color: darken(#de0000, 10);
}
i {
display: inline-flex;
align-items: center;
justify-content: center;
align-self: center;
padding-right: 10px;
margin-right: 10px;
height: 100%;
border-right: 1px solid $content-border-color;
}
}
.name {
word-break: break-all;
}
}
.login-register {
display: flex;
width: 100%;
justify-content: space-between;
margin-top: 40px;
h1 {
text-align: left;
padding-top: 15px;
}
.login {
width: 100%;
border-right: 1px solid lighten($content-border-color, 3);
padding-right: 45px;
}
.register {
width: 100%;
padding-left: 45px;
}
.btn {
margin-top: 25px;
}
}
}
p.error {
color: red;
}
}
.img-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,.7);
div {
position: absolute;
display: flex;
flex-flow: column;
width: 800px;
height: 800px;
max-width: 90%;
max-height: 90%;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: #FFFFFF;
padding: 15px;
a {
display: inline-flex;
align-self: flex-end;
text-decoration: none;
font-size: 34px;
line-height: 26px;
color: lighten($text-color, 40);
&:hover {
color: lighten($text-color, 30);
}
}
img {
width: auto;
height: auto;
max-width: 100%;
max-height: 100%;
object-fit: contain;
padding-top: 20px;
padding-bottom: 25px;
}
}
}
.btn {
text-decoration: none;
background: $btn-color;
border: 0;
color: #FFFFFF;
padding: 11px 16px;
font-size: 14px;
font-weight: 500;
border-radius: 4px;
cursor: pointer;
&:hover {
background: darken($btn-color, 3);
}
&:disabled {
background: #ddd;
}
}
.form-label {
display: block;
padding: 20px 0 10px 0;
}
.form-field {
width: 100%;
padding: 10px;
border: 1px solid darken($content-border-color, 10);
border-radius: 4px;
background-color: #fff;
}
footer {
position: absolute;
bottom: 0;
border-top: 1px solid $content-border-color;
padding: 20px 0;
width: 100%;
a {
text-decoration: none;
color: $text-color;
&:hover {
text-decoration: underline;
}
}
}
/* Responsive CSS below */
@media screen and (max-width: $content-wrapper-width) {
.rhide {
display: none;
}
.content-wrapper {
width: 100%;
padding: 0 10px;
}
header {
justify-content:space-between;
h1 {
font-size: 16px;
flex-basis: auto;
}
nav {
display: none;
position: absolute;
left: 0;
top: calc(100% + 1px);
width: 100%;
background-color: #FFFFFF;
a {
display: block;
padding: 10px 12px;
margin: 0;
border-bottom: 1px solid lighten($content-border-color, 3);
}
}
.link-icons {
display: inline-flex;
width: 100px;
.responsive-toggle {
display: block;
}
.search input {
max-width: 150px;
}
}
}
main {
.recentlyadded .products, .products .products-wrapper {
justify-content: center;
.product {
width: auto;
}
}
.featured {
height: 300px;
h2 {
font-size: 48px;
width: 100%;
padding: 0 10px;
}
p {
font-size: 22px;
width: 100%;
padding: 0 10px;
}
}
> .products {
.products-header {
flex-flow: column;
p {
padding-bottom: 10px;
}
form {
display: flex;
flex-flow: column;
label {
padding-top: 15px;
}
}
}
}
> .product {
padding: 0 10px;
flex-flow: column;
.product-imgs {
padding: 20px 10px 0 10px;
.product-img-large {
height: 300px;
}
.product-small-imgs {
.product-img-small {
height: 80px;
flex-basis: 30%;
}
}
}
form {
input[type="number"], input[type="text"], input[type="datetime-local"], input[type="submit"], select {
width: 100%;
}
}
.product-wrapper {
padding: 0;
}
}
.cart {
table {
input[type="number"] {
width: 40px;
}
}
}
.checkout, .myaccount {
.container {
flex-flow: column;
}
.shipping-details {
.payment-methods {
flex-flow: column;
label {
margin: 0 0 10px 0;
}
}
}
.cart-details {
margin: 0 0 40px 0;
width: 100%;
}
form {
width: 100%;
}
}
.myaccount {
.login-register {
flex-flow: column;
.login {
border-right: 0;
padding: 10px;
}
.register {
padding: 10px;
}
}
.menu {
width: 100%;
padding-right: 0;
}
}
}
}

525
custom/customfunctions.php Normal file
View File

@@ -0,0 +1,525 @@
<?php
//++++++++++++++++++++++++++++++++++++++++++++++++++++
// Custom functions
//++++++++++++++++++++++++++++++++++++++++++++++++++++
// Get country list
function get_countries() {
$default_country = country_default;
return [$default_country, "Afghanistan", "Albania", "Algeria", "American Samoa", "Andorra", "Angola", "Anguilla", "Antarctica", "Antigua and Barbuda", "Argentina", "Armenia", "Aruba", "Australia", "Austria", "Azerbaijan", "Bahamas", "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium", "Belize", "Benin", "Bermuda", "Bhutan", "Bolivia", "Bosnia and Herzegowina", "Botswana", "Bouvet Island", "Brazil", "British Indian Ocean Territory", "Brunei Darussalam", "Bulgaria", "Burkina Faso", "Burundi", "Cambodia", "Cameroon", "Canada", "Cape Verde", "Cayman Islands", "Central African Republic", "Chad", "Chile", "China", "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo", "Congo, the Democratic Republic of the", "Cook Islands", "Costa Rica", "Cote d'Ivoire", "Croatia (Hrvatska)", "Cuba", "Cyprus", "Czech Republic", "Denmark", "Djibouti", "Dominica", "Dominican Republic", "East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea", "Estonia", "Ethiopia", "Falkland Islands (Malvinas)", "Faroe Islands", "Fiji", "Finland", "France", "France Metropolitan", "French Guiana", "French Polynesia", "French Southern Territories", "Gabon", "Gambia", "Georgia", "Germany", "Ghana", "Gibraltar", "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guinea", "Guinea-Bissau", "Guyana", "Haiti", "Heard and Mc Donald Islands", "Holy See (Vatican City State)", "Honduras", "Hong Kong", "Hungary", "Iceland", "India", "Indonesia", "Iran (Islamic Republic of)", "Iraq", "Ireland", "Israel", "Italy", "Jamaica", "Japan", "Jordan", "Kazakhstan", "Kenya", "Kiribati", "Korea, Democratic People's Republic of", "Korea, Republic of", "Kuwait", "Kyrgyzstan", "Lao, People's Democratic Republic", "Latvia", "Lebanon", "Lesotho", "Liberia", "Libyan Arab Jamahiriya", "Liechtenstein", "Lithuania", "Luxembourg", "Macau", "Macedonia, The Former Yugoslav Republic of", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands", "Martinique", "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia, Federated States of", "Moldova, Republic of", "Monaco", "Mongolia", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia", "Nauru", "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand", "Nicaragua", "Niger", "Nigeria", "Niue", "Norfolk Island", "Northern Mariana Islands", "Norway", "Oman", "Pakistan", "Palau", "Panama", "Papua New Guinea", "Paraguay", "Peru", "Philippines", "Pitcairn", "Poland", "Portugal", "Puerto Rico", "Qatar", "Reunion", "Romania", "Russian Federation", "Rwanda", "Saint Kitts and Nevis", "Saint Lucia", "Saint Vincent and the Grenadines", "Samoa", "San Marino", "Sao Tome and Principe", "Saudi Arabia", "Senegal", "Seychelles", "Sierra Leone", "Singapore", "Slovakia (Slovak Republic)", "Slovenia", "Solomon Islands", "Somalia", "South Africa", "South Georgia and the South Sandwich Islands", "Spain", "Sri Lanka", "St. Helena", "St. Pierre and Miquelon", "Sudan", "Suriname", "Svalbard and Jan Mayen Islands", "Swaziland", "Sweden", "Switzerland", "Syrian Arab Republic", "Taiwan, Province of China", "Tajikistan", "Tanzania, United Republic of", "Thailand", "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey", "Turkmenistan", "Turks and Caicos Islands", "Tuvalu", "Uganda", "Ukraine", "United Arab Emirates", "United Kingdom", "United States", "United States Minor Outlying Islands", "Uruguay", "Uzbekistan", "Vanuatu", "Venezuela", "Vietnam", "Virgin Islands (British)", "Virgin Islands (U.S.)", "Wallis and Futuna Islands", "Western Sahara", "Yemen", "Yugoslavia", "Zambia", "Zimbabwe"];
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++
// PAGE HEADER / FOOTER and MENU
//++++++++++++++++++++++++++++++++++++++++++++++++++++
// Template Header default including MENU
function template_header($title, $head = '') {
include 'translations_'.strtoupper(language_code).'.php';
// Get the amount of items in the shopping cart, this will be displayed in the header.
$num_items_in_cart = isset($_SESSION['cart']) ? array_sum(array_column($_SESSION['cart'], 'quantity')) : 0;
$home_link = url('index.php');
$products_link = url(link_to_collection);
$about_link = url('index.php?page=about');
$myaccount_link = url('index.php?page=myaccount');
$cart_link = url('index.php?page=cart');
$admin_link = isset($_SESSION['account_loggedin'], $_SESSION['account_role']) && $_SESSION['account_role'] == 'Admin' ? '<a href="' . base_url . 'admin/index.php" target="_blank">Admin</a>' : '';
$logout_link = isset($_SESSION['account_loggedin']) ? '<a title="Logout" href="' . url('index.php?page=logout') . '"><i class="fas fa-sign-out-alt"></i></a>' : '';
$site_name = site_name;
$site_title = site_title;
$base_url = base_url;
$icon_image = icon_image;
//check for age_consent
if (age_verification_enabled){
if (isset($_POST['age_consent_allow'])){$_SESSION["age_consent"] = 1;}
$age_consent = (isset($_SESSION["age_consent"]) && $_SESSION["age_consent"] == 1) ? '' : consent();
} else {
$age_consent = '';
}
if ($title == 'Checkout' || $title == 'Shopping Cart') {$style = 'style="background-image: url(custom/assets/morval_checkout.jpg);"';} else {$style = '';}
//check for maintenanceMode
if (maintenanceMode){$maintenanceMode = maintenanceMode();} else {$maintenanceMode = '';}
$veliti_analytics = '';
if (veliti_analytics){
$veliti_analytics = '<script src="'.$base_url.'/lib/analytics/analytics.js"></script>';
}
// DO NOT INDENT THE BELOW CODE
echo <<<EOT
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,minimum-scale=1">
<title>$site_title - $title</title>
<link rel="icon" type="image/png" href="{$base_url}$icon_image">
<link href="{$base_url}style.css" rel="stylesheet" type="text/css">
<link href="{$base_url}custom.css" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v6.0.0/css/all.css">
$veliti_analytics
$head
</head>
<body $style>
<header>
<div class="content-wrapper">
<h1>
<a href="$home_link" style="color:inherit;text-decoration:inherit;">
<img src="{$base_url}$icon_image" alt="$site_name" style="width: 50px;height: 50px;padding:1px;border-radius: 10px;">
</a>
</h1>
<nav style="flex-grow: 1;flex-basis: 0px;justify-content: center;align-items: center;text-align:center;">
<a href="$home_link">$home_text</a>
<a href="$products_link">$products_text</a>
<a href="$about_link">$about_text</a>
$admin_link
</nav>
<div class="link-icons">
<div class="search">
<i class="fas fa-search" title="Search"></i>
<input type="text" placeholder="Search...">
</div>
<a href="$cart_link" title="Shopping Cart">
<i class="fas fa-shopping-cart"></i>
<span>$num_items_in_cart</span>
</a>
<a href="$myaccount_link" title="My Account">
<i class="fa fa-user"></i>
</a>
$logout_link
<a class="responsive-toggle" onclick="openMenu('header nav')" href="#">
<i class="fas fa-bars"></i>
</a>
</div>
</div>
</header>
<main>
$age_consent
$maintenanceMode
EOT;
}
// Template Header (related to MENU function)
function template_header_top($title, $head = '') {
include 'translations_'.strtoupper(language_code).'.php';
// Get the amount of items in the shopping cart, this will be displayed in the header.
$num_items_in_cart = isset($_SESSION['cart']) ? array_sum(array_column($_SESSION['cart'], 'quantity')) : 0;
$home_link = url('index.php');
$products_link = url(link_to_collection);
$about_link = url('index.php?page=about');
$myaccount_link = url('index.php?page=myaccount');
$cart_link = url('index.php?page=cart');
$admin_link = isset($_SESSION['account_loggedin'], $_SESSION['account_role']) && $_SESSION['account_role'] == 'Admin' ? '<a href="' . base_url . 'admin/index.php" target="_blank">Admin</a>' : '';
$logout_link = isset($_SESSION['account_loggedin']) ? '<a title="Logout" href="' . url('index.php?page=logout') . '"><i class="fas fa-sign-out-alt"></i></a>' : '';
$site_name = site_name;
$site_title = site_title;
$base_url = base_url;
$icon_image = icon_image;
//check for age_consent
if (age_verification_enabled){
$age_consent = (isset($_SESSION["age_consent"]) && $_SESSION["age_consent"] == 1) ? '' : consent();
} else {
$age_consent = '';
}
//check for banners
if (banner_enabled){
$banner = (isset($_SESSION["banner"]) && $_SESSION["banner"] == 1) ? '' : banner();
} else {
$banner = '';
}
//check for maintenanceMode
if (maintenanceMode){$maintenanceMode = maintenanceMode();} else {$maintenanceMode = '';}
$veliti_analytics = '';
if (veliti_analytics){
$veliti_analytics = '<script src="'.$base_url.'/lib/analytics/analytics.js"></script>';
}
// DO NOT INDENT THE BELOW CODE
echo <<<EOT
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,minimum-scale=1">
<title>$site_title - $title</title>
<link rel="icon" type="image/png" href="{$base_url}$icon_image">
<link href="{$base_url}style.css" rel="stylesheet" type="text/css">
<link href="{$base_url}custom.css" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v6.0.0/css/all.css">
$veliti_analytics
$head
</head>
<body>
<main>
$banner
$age_consent
$maintenanceMode
EOT;
}
// Template Menu
function template_menu(){
include 'translations_'.strtoupper(language_code).'.php';
// Get the amount of items in the shopping cart, this will be displayed in the header.
$num_items_in_cart = isset($_SESSION['cart']) ? array_sum(array_column($_SESSION['cart'], 'quantity')) : 0;
$home_link = url('index.php');
$products_link = url(link_to_collection);
$about_link = url('index.php?page=about');
$myaccount_link = url('index.php?page=myaccount');
$cart_link = url('index.php?page=cart');
$admin_link = isset($_SESSION['account_loggedin'], $_SESSION['account_role']) && $_SESSION['account_role'] == 'Admin' ? '<a href="' . base_url . 'admin/index.php" target="_blank">Admin</a>' : '';
$logout_link = isset($_SESSION['account_loggedin']) ? '<a title="Logout" href="' . url('index.php?page=logout') . '"><i class="fas fa-sign-out-alt"></i></a>' : '';
$site_name = site_name;
$site_title = site_title;
$base_url = base_url;
$icon_image = icon_image;
// DO NOT INDENT THE BELOW CODE
echo <<<EOT
<header_menu>
<div class="content-wrapper">
<h1>
<a href="$home_link" style="color:inherit;text-decoration:inherit;">
<img src="{$base_url}$icon_image" alt="$site_name" style="width: 50px;height: 50px;padding:1px;border-radius: 10px;">
</a>
</h1>
<nav style="flex-grow: 1;flex-basis: 0px;justify-content: center;align-items: center;text-align: center;">
<a href="$home_link">$home_text</a>
<a href="$products_link">$products_text</a>
<a href="$about_link">$about_text</a>
$admin_link
</nav>
<div class="link-icons">
<div class="search">
<i class="fas fa-search" title="Search"></i>
<input type="text" placeholder="Search...">
</div>
<a href="$cart_link" title="Shopping Cart">
<i class="fas fa-shopping-cart"></i>
<span>$num_items_in_cart</span>
</a>
<a href="$myaccount_link" title="My Account">
<i class="fa fa-user"></i>
</a>
$logout_link
<a class="responsive-toggle" onclick="openMenu('header_menu nav')" href="#">
<i class="fas fa-bars"></i>
</a>
</div>
</div>
</header_menu>
EOT;
}
// Template footer
function template_footer() {
include 'translations_'.strtoupper(language_code).'.php';
$base_url = base_url;
$rewrite_url = rewrite_url ? 'true' : 'false';
$year = date('Y');
$currency_code = currency_code;
$site_name = site_name;
$home_link = url('index.php');
$products_link = url('index.php?page=products');
$about_link = url('index.php?page=about');
$privacy_link = privacy_link;
$terms_link = termsandconditions_link;
$faq_link = url('index.php?page=faq');
$myaccount_link = url('index.php?page=myaccount');
$cart_link = url('index.php?page=cart');
$footer_city = footer_city;
$footer_country = footer_country;
$footer_phone = footer_phone;
$footer_email = footer_email;
$facebook_link = facebook_link;
$instagram_link = instagram_link;
// DO NOT INDENT THE BELOW CODE
echo <<<EOT
</main>
<footer>
<div class="container neutral-footer-section-1">
<div class="container neutral-footer-three-columns">
<div class="container description-logos-footer">
<h4 class="neutral-headline-footer">$social_punch_line</h4>
<div class="container social-footer-wrapper">
<a href="$facebook_link" target="_blank" class="social-image neutral-social-icon left-align"><img class="facebook-icon" src="{$base_url}socialicons/webicon-facebook.png" alt="Social Icon" width="100" height="100"></a>
<a href="$instagram_link" target="_blank" class="social-image neutral-social-icon"><img class="instagram-icon" src="{$base_url}socialicons/webicon-instagram.png" alt="Social Icon" width="100" height="100"></a>
</div>
</div>
<div class="container container-links-footer-wrapper">
<a class="link-text neutral-nav-link-footer" href="$home_link">$home_text</a>
<a class="link-text neutral-nav-link-footer" href="$products_link">$products_text</a>
<a class="link-text neutral-nav-link-footer" href="$about_link">$about_text</a>
<a class="link-text neutral-nav-link-footer" href="$faq_link">$faq_text</a>
<a class="link-text neutral-nav-link-footer" href="$privacy_link" target="blank">$privacy_text</a>
<a class="link-text neutral-nav-link-footer" href="$terms_link" target="blank">$terms_text</a>
</div>
<div class="container container-direction-footer">
<p class="paragraph direction-footer">$footer_city<br>$footer_country</p>
<div class="container container-telephone-footer"><span class="glyph icon-footer"><i class="coffeecup-icons-phone2"></i></span>
<a class="link-text nav-link-footer" href="#">$footer_phone</a>
</div>
<a class="link-text nav-link-footer" href="mailto:$footer_email?subject=Contact Us">$footer_email</a>
</div>
</div>
</div>
</footer>
<script>
const currency_code = "$currency_code", base_url = "$base_url", rewrite_url = $rewrite_url;
</script>
<script src="{$base_url}script.js"></script>
</body>
</html>
EOT;
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++
// Order email
//++++++++++++++++++++++++++++++++++++++++++++++++++++
//Template header order email
function template_order_email_header() {
include 'translations_'.strtoupper(language_code).'.php';
$home_link = url('index.php');
$myaccount_link = url('index.php?page=myaccount');
$site_name = site_name;
$base_url = 'https://'.$_SERVER['SERVER_NAME'].'/'; //base_url;
$featured_order_image = $base_url.featured_order_image;
$company_name = company_name;
$company_adres = company_adres;
$company_postal = company_postal;
$company_bank = company_bank;
$company_kvk = company_kvk;
$company_city = footer_city;
$company_country = footer_country;
$company_email = footer_email;
// DO NOT INDENT THE BELOW CODE
echo <<<EOT
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,minimum-scale=1">
<title>$site_name</title>
<style>
* {
box-sizing: border-box;
font-family: -apple-system, BlinkMacSystemFont, "segoe ui", roboto, oxygen, ubuntu, cantarell, "fira sans", "droid sans", "helvetica neue", Arial, sans-serif;
font-size: 12px;
}
.content-wrapper {
width:90%;
margin: auto;
padding-bottom: 40px;
}
header {
position: relative;
}
header img {
padding: 24px 0;
}
.container {
position: relative;
text-align: center;
color: white;
}
.emailfooter{
margin: 50px;
text-align: center;
border-top: 1px solid #EEEEEE;
}
.emailfooter a {
text-decoration: none;
color: #555555;
cursor: pointer;
font-family: inherit;
}
main .order-table {
margin: 0;
}
main .order-table .item-list-end {
border-bottom: 1px solid #0e0f10;
}
main .order-table .subtotal {
padding-top: 20px;
}
main .order-table .subtotal, main .order-table .shipping, main .order-table .tax , main .order-table .total {
text-align: right;
font-weight: 500;
font-size: 12px;
}
main .order-table .num {
text-align: right;
}
main .manage-order-table input, main .manage-order-table select {
border: 0;
padding: 5px 0;
height: 40px;
background: transparent;
width: 90%;
border-bottom: 1px solid #dedfe1;
}
main .manage-order-table .add-item {
display: inline-block;
text-decoration: none;
color: #676d72;
padding: 25px 0;
}
main .manage-order-table .add-item i {
padding-right: 5px;
}
main .manage-order-table .add-item:hover {
color: #4f5357;
}
main .manage-order-table .delete-item {
cursor: pointer;
color: #676d72;
}
main .manage-order-table .delete-item:hover {
color: #b44a4a;
}
.table table {
width: 100%;
border-collapse: collapse;
}
.table table thead td {
font-weight: 600;
font-size: 12px;
padding: 15px 0;
}
.table table thead td a {
font-weight: inherit;
font-size: inherit;
color: inherit;
text-decoration: none;
}
.table table thead td i {
padding-left: 5px;
}
.table table thead tr {
border-bottom: 1px solid #0e0f10;
}
.table table tbody tr:first-child td {
padding-top: 10px;
}
.table table tbody td {
padding: 5px 0;
}
.table table tbody .img {
padding: 1px 0;
}
.table table tbody .rrp {
color: #e26060;
}
.table table tbody .status {
padding: 4px 7px;
border-radius: 4px;
background-color: #81848a;
font-weight: 500;
font-size: 12px;
color: #fff;
}
.table table tbody .status.completed, .table table tbody .status.shipped {
background-color: #13b368;
}
.table table tbody .status.pending {
background-color: #eb8a0d;
}
.table table tbody .status.refunded, .table table tbody .status.failed, .table table tbody .status.cancelled, .table table tbody .status.reversed {
background-color: #bd4141;
}
</style>
</head>
<body>
<header>
<div class="content-wrapper">
<table style="border-collapse:collapse;width:100%;">
<tr>
<td width="75%">
<a href="$home_link" style="color:inherit;text-decoration:inherit;">
<img src="$featured_order_image" alt="$site_name" width="50%" />
</a>
</td>
<td width="25%">
$company_name<br>
$company_adres<br>
$company_postal $company_city<br>
$company_country<br>
<br>
$company_email <br>
<br>
Bank: $company_bank<br>
KvK: $company_kvk
</td>
</tr>
EOT;
}
// Template order footer
function template_order_email_footer() {
include 'translations_'.strtoupper(language_code).'.php';
$home_link = url('index.php');
$products_link = url(link_to_collection);
$about_link = url('index.php?page=about');
$privacy_link = url('index.php?page=privacy');
$terms_link = url('index.php?page=termsandconditions');
$myaccount_link = url('index.php?page=myaccount');
$cart_link = url('index.php?page=cart');
// DO NOT INDENT THE BELOW CODE
echo <<<EOT
<div class="emailfooter">
<a href="$products_link">$products_text</a> - <a href="$myaccount_link">$myaccount_text</a> - <a href="$privacy_link">$privacy_text</a> - <a href="$terms_link">$terms_text</a>
</div>
</main>
</body>
</html>
EOT;
}
?>

View File

@@ -0,0 +1,78 @@
<?php defined(security_key) or exit; ?>
<?=template_order_email_header()?>
<?php include 'translations_'.strtoupper(language_code).'.php';?>
</tr>
<tr><td><br></td></tr>
<tr>
<td>
<?=$address_name?><br>
<?=$address_street?><br>
<?=$address_zip?>, <?=$address_city?><br>
<?=$address_country?>
</td>
</tr>
<tr><td><br></td></tr>
<tr>
<td>
<h1><?=$order_email_message_1?></h1>
<p><?=$order_email_message_2?></p></td>
<td>
<p>Order: <?=$order_id?></p>
<p>Date: <?php echo date("Y-m-d");?></p></td>
</tr>
</table>
</div>
</header>
<main>
<div class="content-wrapper">
<div class="table order-table">
<table>
<thead>
<tr>
<td><?=$tr_product?></td>
<td>Opties</td>
<td><?=$tr_quantity?></td>
<td><?=$tr_price?></td>
<td style="text-align:right;"><?=$tr_total?></td>
</tr>
</thead>
<tbody>
<?php foreach($products as $product): ?>
<tr>
<td><?=$product['meta']['name']?></td>
<td><?=$product['options']?></td>
<td><?=$product['quantity']?></td>
<td><?=currency_code?><?=number_format($product['final_price'],2)?></td>
<td style="text-align:right;"><?=number_format($product['final_price'] * $product['quantity'],2)?></td>
</tr>
<?php endforeach; ?>
<tr>
<td colspan="5" class="item-list-end"></td>
</tr>
<tr>
<td colspan="4" class="subtotal"><?=$total_subtotal?></td>
<td class="num"><?=currency_code?><?=number_format($subtotal,2)?></td>
</tr>
<tr>
<td colspan="4" class="subtotal"><?=$total_discount?></td>
<td class="num"><?=currency_code?><?=number_format($discounttotal,2)?></td>
</tr>
<tr>
<td colspan="4" class="shipping"><?=$total_shipping?></td>
<td class="num"><?=currency_code?><?=number_format($shippingtotal,2)?></td>
</tr>
<tr>
<td colspan="4" class="tax"><?=$total_vat?></td>
<td class="num" style="border-bottom: 1px solid #0e0f10;"><?=currency_code?><?=number_format($taxtotal,2)?></td>
</tr>
<tr>
<td colspan="4" class="total"><?=$total_total?></td>
<td class="num"><?=currency_code?><?=number_format($total,2)?></td>
</tr>
</tbody>
</table>
</div>
</div>
<?=template_order_email_footer()?>

View File

@@ -0,0 +1,86 @@
<?php
//(defined(security_key) or defined('admin')) or exit; ?>
<?=template_order_email_header()?>
<?php include 'translations_'.strtoupper(language_code).'.php';?>
</tr>
<tr><td><br></td></tr>
<tr>
<td>
<?=$address_name?><br>
<?=$address_street?><br>
<?=$address_zip?>, <?=$address_city?><br>
<?=$address_country?>
</td>
</tr>
<tr><td><br></td></tr>
<tr>
<td><h2>Invoice: <?=$order_id?></h2></td>
<td><p>Date: <?php echo date("Y-m-d", strtotime($order_created))?></p></td>
</tr>
</table>
</div>
</header>
<main>
<div class="content-wrapper">
<div class="table order-table">
<table>
<thead>
<tr>
<td><?=$tr_product?></td>
<td>Options</td>
<td><?=$tr_quantity?></td>
<td><?=$tr_price?></td>
<td style="text-align:right;"><?=$tr_total?></td>
</tr>
</thead>
<tbody>
<?php
$subtotal = 0;
foreach($products as $product): ?>
<tr>
<td><?=$product['name']?></td>
<td><?=$product['item_options']?></td>
<td><?=$product['item_quantity']?></td>
<td><?=currency_code?> <?=number_format($product['item_price'],2)?></td>
<td style="text-align:right;"><?=currency_code?> <?=number_format($product['item_price'] * $product['item_quantity'],2)?></td>
</tr>
<?php $subtotal += $product['item_price']*$product['item_quantity'];?>
<?php endforeach; ?>
<tr>
<td colspan="5" class="item-list-end"></td>
</tr>
<tr>
<td colspan="4" class="subtotal"><?=$total_subtotal?></td>
<td class="num"><?=currency_code?> <?=number_format($subtotal,2)?></td>
</tr>
<tr>
<td colspan="4" class="subtotal"><?=$total_discount?></td>
<td class="num"><?=currency_code?> <?=number_format($total-($subtotal+$shippingtotal),2)?></td>
</tr>
<tr>
<td colspan="4" class="shipping"><?=$total_shipping?></td>
<td class="num"><?=currency_code?><?=number_format($shippingtotal,2)?></td>
</tr>
<tr>
<td colspan="4" class="tax"><?=$total_vat?></td>
<td class="num" style="border-bottom: 1px solid #0e0f10;"><?=currency_code?><?=number_format($taxtotal,2)?></td>
</tr>
<tr>
<td colspan="4" class="total"><?=$total_total?></td>
<td class="num"><?=currency_code?> <?=number_format($total,2)?></td>
</tr>
</tbody>
</table>
</div>
</div>
<?php if($order['payer_email'] == 'Paid'){
echo '
<div class="content-wrapper">
<p>Het totaalbedrag van deze factuur is betaald</p>
</div>';
}
?>

View File

@@ -0,0 +1,79 @@
<?php defined(security_key) or exit; ?>
<?=template_order_email_header()?>
<?php include 'translations_'.strtoupper(language_code).'.php';?>
</tr>
<tr><td><br></td></tr>
<tr>
<td>
<?=$address_name?><br>
<?=$address_street?><br>
<?=$address_zip?>, <?=$address_city?><br>
<?=$address_country?>
</td>
</tr>
<tr><td><br></td></tr>
<tr>
<td>
<h1><?=$order_email_message_1?></h1>
<p><?=$order_email_message_2?></p></td>
<td>
<p>Order: <?=$order_id?></p>
<p>Date: <?php echo date("Y-m-d");?></p></td>
</tr>
</table>
</div>
</header>
<main>
<div class="content-wrapper">
<div class="table order-table">
<table>
<thead>
<tr>
<td><?=$tr_product?></td>
<td>Options</td>
<td><?=$tr_quantity?></td>
<td><?=$tr_price?></td>
<td style="text-align:right;"><?=$tr_total?></td>
</tr>
</thead>
<tbody>
<?php foreach($products as $product): ?>
<tr>
<td><?=$product['meta']['name']?></td>
<td><?=$product['options']?></td>
<td><?=$product['quantity']?></td>
<td><?=currency_code?><?=number_format($product['final_price'],2)?></td>
<td style="text-align:right;"><?=number_format($product['final_price'] * $product['quantity'],2)?></td>
</tr>
<?php endforeach; ?>
<tr>
<td colspan="5" class="item-list-end"></td>
</tr>
<tr>
<td colspan="4" class="subtotal"><?=$total_subtotal?></td>
<td class="num"><?=currency_code?><?=number_format($subtotal,2)?></td>
</tr>
<tr>
<td colspan="4" class="subtotal"><?=$total_discount?></td>
<td class="num"><?=currency_code?><?=number_format($discounttotal,2)?></td>
</tr>
<tr>
<td colspan="4" class="shipping"><?=$total_shipping?></td>
<td class="num"><?=currency_code?><?=number_format($shippingtotal,2)?></td>
</tr>
<tr>
<td colspan="4" class="tax"><?=$total_vat?></td>
<td class="num" style="border-bottom: 1px solid #0e0f10;"><?=currency_code?><?=number_format($taxtotal,2)?></td>
</tr>
<tr>
<td colspan="4" class="total"><?=$total_total?></td>
<td class="num"><?=currency_code?><?=number_format($total,2)?></td>
</tr>
</tbody>
</table>
</div>
</div>
<?=template_order_email_footer()?>

67
custom/pages/about.php Normal file
View File

@@ -0,0 +1,67 @@
<?php
// Prevent direct access to file
defined(security_key) or exit;
?>
<?=template_header_top($about_text)?>
<div class="featured" style="background-image:url(<?=featured_about_image?>);background-position: center center;">
<?=template_menu()?>
<h2><?=$h2_about_1?></h2>
<h2><?=$h2_about_2?></h2>
<p></p>
</div>
<section class="container neutral-cover-section-1">
<div class="container neutral-two-columns">
<div class="container neutral-container-picture-wrap">
<div class="container neutral-column-text-right">
<h2 class="neutral-cover-heading"><?=$about_header_1?></h2>
<p class="paragraph neutral-paragraph-text">
<?=$about_1_p?>
</p>
</div>
</div>
<div class="responsive-picture neutral-picture">
<picture>
<img alt="Placeholder Picture" width="1024" height="684" src="<?=about_image_1?>">
</picture>
</div>
</div>
</section>
<section class="container neutral-cover-section-1">
<div class="container neutral-two-columns">
<div class="container neutral-container-picture-wrap">
<div class="responsive-picture neutral-picture">
<picture>
<img alt="Placeholder Picture" width="1024" height="832" src="<?=about_image_2?>">
</picture>
</div>
</div>
<div class="container neutral-column-text-right">
<h2 class="neutral-cover-heading"><?=$about_header_2?></h2>
<p class="paragraph neutral-paragraph-text">
<?=$about_2_p?>
</p>
</div>
</div>
</section>
<section class="container neutral-cover-section-1">
<div class="container neutral-two-columns">
<div class="container neutral-container-picture-wrap">
<div class="container neutral-column-text-right">
<h2 class="neutral-cover-heading"><?=$about_header_3?></h2>
<p class="paragraph neutral-paragraph-text">
<?=$about_3_p?>
</p>
<a class="link-text neutral-text-link-1" href="<?=url('index.php?page=about_morval')?>"><?=$about_morval_text?> &gt;</a>
</div>
</div>
<div class="responsive-picture neutral-picture">
<picture>
<img alt="Placeholder Picture" width="1024" height="684" src="<?=about_image_3?>">
</picture>
</div>
</div>
</section>
<?=template_footer()?>

View File

@@ -0,0 +1,67 @@
<?php
// Prevent direct access to file
defined(security_key) or exit;
?>
<?=template_header_top($about_morval_text)?>
<div class="featured" style="background-image:url(<?=featured_about_morval_image?>);background-position: center center;">
<?=template_menu()?>
<h2><?=$h2_about_morval_1?></h2>
<h2><?=$h2_about_morval_2?></h2>
<p></p>
</div>
<section class="container neutral-cover-section-1">
<div class="container neutral-two-columns">
<div class="container neutral-container-picture-wrap">
<div class="container neutral-column-text-right">
<h2 class="neutral-cover-heading"><?=$about_morval_header_1?></h2>
<p class="paragraph neutral-paragraph-text">
<?=$about_morval_1_p?>
</p>
</div>
</div>
<div class="responsive-picture neutral-picture">
<picture>
<img alt="Placeholder Picture" width="1024" height="684" src="<?=about_morval_image_1?>">
</picture>
</div>
</div>
</section>
<section class="container neutral-cover-section-1">
<div class="container neutral-two-columns">
<div class="container neutral-container-picture-wrap">
<div class="responsive-picture neutral-picture">
<picture>
<img alt="Placeholder Picture" width="1024" height="832" src="<?=about_morval_image_2?>">
</picture>
</div>
</div>
<div class="container neutral-column-text-right">
<h2 class="neutral-cover-heading"><?=$about_morval_header_2?></h2>
<p class="paragraph neutral-paragraph-text">
<?=$about_morval_2_p?>
</p>
</div>
</div>
</section>
<section class="container neutral-cover-section-1">
<div class="container neutral-two-columns">
<div class="container neutral-container-picture-wrap">
<div class="container neutral-column-text-right">
<h2 class="neutral-cover-heading"><?=$about_morval_header_3?></h2>
<p class="paragraph neutral-paragraph-text">
<?=$about_morval_3_p?>
</p>
<a class="link-text neutral-text-link-1" href="http://morval.free.fr/index.html" target="blank">source: history of Morval</a>
</div>
</div>
<div class="responsive-picture neutral-picture">
<picture>
<img alt="Placeholder Picture" width="1024" height="684" src="<?=about_morval_image_3?>">
</picture>
</div>
</div>
</section>
<?=template_footer()?>

59
custom/pages/faq.php Normal file
View File

@@ -0,0 +1,59 @@
<?php
// Prevent direct access to file
defined(security_key) or exit;
?>
<?=template_header($faq_text)?>
<div class="products content-wrapper">
<h1>FREQUENTLY ASKED QUESTIONS</h1>
<h2>How long will it take for my watch to arrive?</h2>
<p>
Your watch will be shipped within three business Days of placing your order. Most customers receive their watches 3-7 business days after their order. In case your watch isnt in stock, you will receive an e-mail with the expected date of delivery.
</p>
<h2>How much does shipping cost?</h2>
<p>
Shipping is free of charge!
</p>
<h2>Do you ship internationally?</h2>
<p>
We ship anywhere in the world.
</p>
<p>
For orders shipped outside of Europe, the customs office or other authorities in the destination country may apply import duties, custom fees, or other fees, duties, or taxes to the order. Customers are solely responsible for these charges.
</p>
<h2>What is your returns policy?</h2>
<p>
You may return any item for a refund or exchange, for any reason, within 14 days of receipt. Refunds or exchanges will only be issued for items returned in new, undamaged, re-sellable condition, with all original packaging and protective coverings. Return shipping and costs is the responsibility of the customer.
</p>
<h2>Is there a Warranty?</h2>
<p>
Morval watches are guaranteed to be free of defects and work well for at least two years. If you have any problems within two years of the date of purchase, we will repair or replace your watch for free. Damage caused by accidents, mishandling, tampering, etc., will void your warranty.
</p>
<p><a href="mailto:<?=footer_email?>?subject=Warranty">E-mail</a> us with any questions or warranty claims. </p>
<h2>How do I operate my watch?</h2>
<p>
For proper use please see our user guide.
</p>
<h2>How do I clean and maintain my watch?</h2>
<p>
You dont need to do much. Every few years, its a good idea to bring it for a tune up and lubrication check at your local watch repair shop. Clean with soap and water or a dry cloth as needed. Avoid getting leather straps wet.
</p>
<h2>Can I sell Morvalwatches at my retail store?</h2>
<p>Please connect with us</p>
<h2>Do you do special or custom orders?</h2>
<p>Let us know what you have in mind. </p>
<p>If you have other questions, please contact us by <a href="mailto:<?=footer_email?>?subject=Additional question">e-mail</a></p>
</div>
<?=template_footer()?>

22
custom/pages/privacy.php Normal file
View File

@@ -0,0 +1,22 @@
<?php
// Prevent direct access to file
defined(security_key) or exit;
if (isset($_GET['download'])){
generatedPDF($privacy_statement_items,$privacy_text);
}
?>
<?=template_header($privacy_text)?>
<div class="products content-wrapper">
<?php
$view = '<h1>'.$privacy_statement_header;
$view .= '<a style="text-decoration: none;color:#555555;" href="'.url("index.php?page=privacy&download=download").'"> <i class="fa-solid fa-download"></i></a>';
$view .= '</h1>';
$view .= $privacy_statement_items;
echo $view;
?>
<?=template_footer()?>

View File

@@ -0,0 +1,22 @@
<?php
// Prevent direct access to file
defined(security_key) or exit;
if (isset($_GET['download'])){
generatedPDF($termsandconditions_items,$terms_text);
}
?>
<?=template_header($terms_text)?>
<div class="products content-wrapper">
<?php
$view = '<h1>'.$termsandconditions_header;
$view .= '<a style="text-decoration: none;color:#555555;" href="'.url("index.php?page=termsandconditions&download=download").'" class="dowloadpdf"> <i class="fa-solid fa-download"></i></a>';
$view .= '</h1>';
$view .= $termsandconditions_items;
echo $view;
?>
<?=template_footer()?>

BIN
custom/settings/.DS_Store vendored Normal file

Binary file not shown.

182
custom/settings/config.php Normal file
View File

@@ -0,0 +1,182 @@
<?php
// Page security key
define('security_key','MorvalWatches');
// This will change the title on the website
define('site_name','MorvalWatches');
// This will change the title on browser TAB
define('site_title','MorvalWatches');
// Currency code, default is USD, you can view the list here: http://cactus.io/resources/toolbox/html-currency-symbol-codes
define('currency_code','&euro;');
//Default Countrycode during checkout
define('country_default','');
//Default language for translations -> refers to include file translations_XX.php
define('language_code','US');
// Default age verification
define('age_verification_enabled',false);
//Enable VeLiTi-analytics
define('veliti_analytics',false);
// Default logtraffic
define('log_usage',true);
/* Banners */
// Show offer at home page
define('show_offer_home_page',true);
define('show_offer_home_text','Free shipping on all of our watches');
// Show offer at products page
define('show_offer_product_page',true);
define('show_offer_product_text','Free shipping on all of our watches');
//Banner at site entry
define('banner_enabled',false);
define('banner_wow','Introduction offer');
define('banner_text','Visit our Kickstarter page for an introduction offer of 30%');
define('banner_link','https://www.kickstarter.com/projects/morvalwatches/morval-automatic-watches-a-brand-with-a-story?ref=discovery');
define('banner_btn_1','Continue@Kickstarter');
define('banner_btn_2','Stay@MorvalWatches');
/* Detailed settings */
// Homepage highlightedproducts
define('highlighted_product_range_1','Thomas I');
define('category_id_highlighted_products_1','6');
// Homepage highlightedproducts
define('highlighted_product_range_2','Thomas II');
define('category_id_highlighted_products_2','7');
//Homepage show options in carrousel
define('show_options_carrousel',false);
//Truncate productname on carrousel
define('product_truncate_text','Thomas-I');
define('product_truncate','1');
//Show small images from configuration
define('show_option_images',false);
// Default category_id for suggestions
define('category_id_checkout_suggestions','3');
// Default category_id for giftcard
define('giftcard_id','');
// Default category_id for samples
define('category_id_checkout_samples','');
// Default samples
define('sample_pricing_override',false);
// Default samples
define('sample_pricing_override_price','0');
// Shipment indicator
define('free_shipment_indicator',true);
// Shipment indicator price
define('free_shipment_price','250');
// Default payment status
define('default_payment_status','New');
// Account required for checkout?
define('account_required',false);
// Default product sort
define('default_product_sort','sort3');
// Enable automatice invoice forward to bookkeeping software
define('invoice_bookkeeping',false);
// Email of bookkeeping software
define('email_bookkeeping','');
/* Images */
// Featured image URL
define('featured_image','custom/assets/morval_header_2.jpg');
define('featured_store_image','custom/assets/morval_collection2.png');
// Featured image ABOUT US
define('featured_about_image','custom/assets/morval_about_morval.png');
// Featured image ABOUT MORVAL
define('featured_about_morval_image','custom/assets/morval_about_us.png');
// Featured image order notification
define('featured_order_image','custom/assets/Morval_Logo_invoice.jpg');
//Home image 1
define('home_product_image_1','uploads/PHOTO-2023-04-21-17-46-38.png');
//Home image 2
define('home_product_image_2','uploads/PHOTO-2023-04-21-17-46-47.png');
// ABOUT image 1
define('about_image_1','custom/assets/morval_werkplaats.jpg');
// ABOUT image 2
define('about_image_2','custom/assets/morval_header.jpg');
// ABOUT image 3
define('about_image_3','custom/assets/morval_monument.jpg');
// ABOUT MORVAL image 1
define('about_morval_image_1','custom/assets/morval_about_morval_monument_detail.png');
// ABOUT MORVAL image 2
define('about_morval_image_2','custom/assets/morval_about_morval_bordje.png');
// ABOUT MORVAL image 3
define('about_morval_image_3','custom/assets/morval_about_morval_monument_overzicht.png');
//Icon
define('icon_image','custom/assets/MORVALFavicon.svg');
//Banner
define('banner_background','custom/assets/morval_banner.jpg');
/* Links */
//Link to product catalog
define('link_to_collection','index.php?page=products&category=2');
define('link_to_external_product_site','');
define('privacy_link','/custom/assets/2024_Morval_Privacy_Statement.pdf');
define('termsandconditions_link','/custom/assets/2024_Morval_Terms_and_conditions.pdf');
define('withdrawal_link','/custom/assets/2024_Morval_Withdrawal_request_form.docx');
define('facebook_link','https://www.facebook.com/profile.php?id=61559120644993');
define('instagram_link','https://www.instagram.com/morvalwatches?igsh=azZ5NTN0M2lkYmdq');
/* Owner */
// City displayed in footer
define('company_name','');
define('company_adres','');
define('company_postal','');
define('company_bank','');
define('company_kvk','');
define('footer_city','');
// Country displayed in Footer
define('footer_country','');
// Phone number in Footer
define('footer_phone','');
// Phone number in Footer
define('footer_email','');
/* Email */
// The from email that will appear on the customer's order details email
define('mail_from','Name');
// Send mail to the customers, etc?
define('mail_enabled',false);
// Your email
define('email','');
// Receive email notifications?
define('email_notifications',false);
// Rewrite URL?
define('rewrite_url',true);
//Additional phpmailer-settings
define('email_host_name','');
define('email_reply_to','');
define('email_outgoing_pw','');
define('email_outgoing_port','587');
define('email_outgoing_security','ssl');
/* Database */
// Database hostname, don't change this unless your hostname is different
define('db_host','localhost');
// Database username
define('db_user','morvalwatches');//morvalwatches_prod
// Database password
define('db_pass','4~gv71bM6');
// Database name
define('db_name','advanced_shoppingcart'); //morvalwatches
/* Payment options */
//Pay on Delivery
define('pay_on_delivery_enabled',false);
define('pay_on_delivery_default',false);
// Mollie
define('mollie_enabled',false);
define('mollie_default',false);
define('mollie_api_key','');
// Accept payments with PayPal?
define('paypal_enabled',true);
define('paypal_default',true);
define('PAYPAL_URL','');
define('PAYPAL_WEBHOOK','');
define('PAYPAL_CLIENT_ID','');
define('PAYPAL_CLIENT_SECRET','');
/* Admin modes */
//debug
define('debug',false);
// Default maintenance_mode
define('maintenanceMode',false);
?>

View File

@@ -0,0 +1,221 @@
<?php
//include text files
include_once dirname(__FILE__).'/translations_termsandconditions.php';
include_once dirname(__FILE__).'/translations_privacy.php';
//Home.php
$h2_brand_name_1 = 'MorvalWatches';
$h2_brand_name_2 = 'a brand with a story';
$h2_content_top = 'Recente producten';
$h2_home_content_1 = '';
$h2_home_content_2 = '';
//Zie ook large TEXT
//Zie ook large TEXT
//Products.php
$h1_content_top = 'Onze collectie';
$product_count_1 = 'Product';
$product_count_2 = 'en';
$main_filter_category = 'Categorie';
$main_category = 'Alle';
$main_filter_sort = 'Sorteren';
$sort1 = 'A-Z';
$sort2 = 'Z-A';
$sort3 = 'Nieuwste';
$sort4 = 'Verschijningsdatum';
$sort5 = 'Prijs Hoog - Laag';
$sort6 = 'Prijs Laag - Hoog';
$free_delivery = 'gratis levering';
$non_free_delivery = 'gratis levering vanaf ';
//Product.php
$breadcrum_products = 'Winkel';
$product_quantity = 'Aantal';
$product_on_stock = 'Op Voorraad';
$out_of_stock_notify = 'Herinner mij';
$out_of_stock_notify_2 = 'Bezoek ons op @';
$out_of_stock = 'Niet op voorraad';
$add_to_basket = 'In Winkelwagen';
//Cart.php
$h1_cart_name = 'Winkelwagen';
$h2_cart_suggestions = 'Extra\'s';
$h2_cart_sample_product = '';
$tr_product = 'Product';
$tr_price = 'Prijs';
$tr_quantity = 'Aantal';
$tr_total = 'Totaal';
$total_subtotal = 'Subtotaal';
$total_note = '(verzendkosten worden berekend bij bestelling)';
$total_vat = 'Belasting';
$total_shipping = 'Verzending';
$total_shipping_note = '(incl. verzending)';
$total_discount = 'Korting';
$total_total = 'Totaal';
$total_total_note = '(incl. belastingen)';
$btn_emptycart = '🗑';
$btn_update = 'Verversen';
$btn_checkout = 'Bestellen';
$navigation_back_to_store = 'Verder winkelen';
$cart_message_empty = 'Er zitten geen producten in uw winkelwagen';
//Checkout.php
$error_account_name = 'Dit account bestaat al';
$error_account_password_rules = 'Wachtwoord moet minimaal 5 and maximaal 20 tekens lang zijn';
$error_account_password_match = 'Wachtwoorden zijn niet identiek';
$error_account = 'Account aanmaken';
$h1_checkout = 'Bestelling Afronden';
$account_available = 'Heeft u al een account?';
$account_log_in = 'Inloggen';
$account_create = 'Account aanmaken';
$account_create_optional = '(optioneel)';
$account_create_email = 'Email';
$account_create_password = 'Wachtwoord';
$account_create_password_confirm = 'Bevestig wachtwoord';
$h2_Shipping_details = 'Verzenden';
$h3_shipping_method = 'Verzendmethode';
$shipping_first_name = 'Voornaam';
$shipping_last_name = 'Achternaam';
$shipping_address = 'Adres';
$shipping_city = 'Stad';
$shipping_state = 'Regio/Provincie';
$shipping_zip = 'Postcode';
$shipping_country = 'Land';
$shipping_phone = 'Telefoon';
$payment_method = 'Betaalmethode';
$payment_method_1 = 'Debit/Credit';
$payment_method_2 = 'Achteraf betalen';
$h2_shoppingcart = 'Winkelwagen';
$discount_label = 'Kortingscode';
$discount_message = 'Kortingscode toegepast';
$discount_error_1 = 'Kortingscode niet correct';
$discount_error_2 = 'Kortingscode niet geldig';
$order_consent_1 = 'Ik wil graag email ontvangen over nieuws,producten en services van MorvalWatches.';
$order_consent_2 = 'Akkoord met';
$order_consent_3 = 'de algemene voorwaarden';
$btn_place_order = 'Bestellen en Betalen';
//Placeorder.php
$h1_order_succes_message = 'Uw order is ontvangen';
$order_succes_message = 'Bedankt voor uw order! We zullen u via email op de hoogte houden';
//Myaccount.php
$error_myaccount = 'Ingevoerde gegevens niet correct.';
$error_myaccount_exists = $error_account_name;
$h1_login = 'Inloggen';
$h1_register = 'Registeer';
$h1_myaccount = 'Mijn account';
$h2_menu = 'Menu';
$menu_orders = 'Orders';
$menu_downloads = 'Downloads';
$menu_settings = 'Instellingen';
$h2_myorders = 'Mijn orders';
$myorders_message = 'U heeft geen orders';
$myorders_order = 'Order';
$myorders_date = 'Datum';
$myorders_status = 'Status';
$myorders_shipping = 'Verzending';
$myorders_total = 'Totaal';
$h2_mydownloads = 'Mijn downloads';
$mydownloads_message = 'U heeft geen downloads';
$mydownloads_product = 'Product';
$h2_settings = 'Instellingen';
$settings_email = 'Email';
$settings_new_password = 'Nieuw wachtwoord';
$btn_settings_save = 'Opslaan';
//Functions.php
$age_consent_h4 = 'Even uw leeftijd controleren';
$age_consent_text = 'Om te kunnen bezoeken moet u 18 jaar of ouder zijn.';
$age_consent_btn_allow = 'Akkoord';
$age_consent_btn_deny = 'Niet akkoord';
$maintenanceMode_h4 = 'Onze webshop is in onderhoud';
$maintenanceMode_text = 'Onze webshop is in onderhoud, we zien u graag snel terug.';
$maintenanceMode_btn = 'OK';
$subject_order_notification = 'Morval - You have received a new order!';
$subject_new_order = 'Morval - Order Details';
$subject_out_of_stock = 'Morval - Out of Stock';
//Header_template (functions.php)
$home_text = 'Home';
$products_text = 'Winkel';
$about_text = 'Over Ons';
$myaccount_text = 'Account';
//Footer_template (functions.php)
$social_punch_line = 'MorvalWatches op social media';
$privacy_text = 'Privacy';
$terms_text = 'Voorwaarden';
$faq_text = 'Faq';
//order_details_template.php
$order_email_title = 'MorvalWatches';
$order_email_message_1 = 'Bedankt voor uw order';
$order_email_message_2 = 'Wij gaan aan de slag met uw order.';
$order_email_information = 'Uw gegevens';
//--------------------------------------------------------
// Custom TEXT area
//--------------------------------------------------------
//ABOUT.PHP
$h2_about_1 = 'Over Morval';
$h2_about_2 = '';
$about_header_1 = 'Over ons';
$about_1_p ='Morval Watches was founded in 2023 by Ralph van Wezel. Ralph is a hospital pharmacist and has a fascination for technology. In his work he strives to make medicines available that make a difference for the patient. Producing a medicine requires knowledge, precision, accuracy, technique, quality and craftsmanship. Herein lies the similarity with the manufacture of a high-quality automatic watch. Ralph has set itself the goal of developing a watch that can compete with the renowned brands, but is sold at an acceptable price.';
$about_header_2 = 'Onze horloges';
$about_2_p ='A Morval Watch is inspired by the vintage models and minimalistic design of Scandinavian watches. Due to variations in the color of the dial and straps, a Morval watch can be worn on any occasion, both as sport and dress watch.Morval watches meet the highest quality requirements and can compete with the well-known Swiss brands. The parts are supplied by renowned manufacturers from Europe and beyond. A Morval contains a Swiss-made caliber (STP) that is known for its reliable quality. The assemblies take place in Amsterdam by recognized watchmakers and each watch undergoes extensive quality control for functionality and aesthetics. The watch is manually adjusted and tested to minimize the deviation. Morval stands for an excellent price-quality ratio! When you purchase a Morval watch, you are assured of a timeless timepiece that will last for decades.A lot of attention has been paid to details, such as a brushed case of stainless steel 316 steel, superluminova on the hands, anti-reflective glass and infinitely adjustable leather strap. This translates into the luxurious appearance of the brand. With a Morval Watch you have a unique, robust, stylish and timeless timepiece that will last for generations!';
$about_header_3 = 'Over Morval';
$about_3_p =' Morval takes its name from the surname of one of Ralph\'s grandparents. The logo is inspired by the monument in the town of Morval in northern France, which was built from the remains of a church and the three bells from the tower.';
$about_morval_text = 'Lees meer over de historie van Morval';
//ABOUT MORVAL
$h2_about_morval_1 = 'The history of Morval';
$h2_about_morval_2 = '';
$about_morval_header_1 = '';
$about_morval_1_p ='Morval, a village of 96 inhabitants, is located between 4 communes of the Somme as a peninsula of Pas-de-Calais at the end of the canton of Bapaume.
Before 1914 it had 220 to 250 inhabitants, but it was completely destroyed during the Battle of Morval in the First World War (1914-18). The rebuilt church was the almost exact replica in style, allure and proportions (45 meters high, 40 meters long), as the previous one. The bell tower housed 3 beautiful bells, which is quite rare for a small village. It was inaugurated in October 1932.
Unfortunately, it was not a good reconstruction and in the aftermath of the Second World War it was necessary to repair the roof, close cracks around the bell tower, replace stained glass windows, etc. Soon, successive municipalities could no longer cope with the deterioration . A complete renovation would have required at least 1,500,000 francs, while the municipality\'s annual budget was only 120,000 francs.
In November 1973, during a funeral, part of the church collapsed during the sacrifice. The building was no longer safe and was closed for worship. In 1985, the mayor, deputies, councilors and administrators decided to destroy the church. An amount of 66,000 francs was reserved for this very sad operation, which was alleviated by a subsidy obtained from the general council.
';
$about_morval_header_2 = '';
$about_morval_2_p ='The entrepreneur, Mr Bonifatius d\'Equancourt (Somme), took advantage of a nice post-season and lowered the bells on September 21, 1987.
Mr Dromard, from the national CEFICEM center of Besançon (Doubs), had never had to carry out such work before. He first blew up the apse and transept with dynamite by drilling 600 holes. The preparations took longer than expected due to the hardness of the stones and concrete. At 7 p.m. on September 24, the 45-meter-high bell tower, so proud but so expensive to maintain, fell in a cloud of dust that had accumulated over 55 years.
Shrapnel splintered in all directions flew up to 100 meters. A brick was found in the kitchen of the school residence: it had gone through the open window. A nearby small house had broken windows despite closed shutters and the presence of an armored screen. The roof of the barn of the nearby farm was hit and in a small corner where photographers were standing, there were two minor injuries to the legs, not to mention a broken car window.
It was a bit like having the soul ripped out of our poor place that would never be like the others again.
';
$about_morval_header_3 = '';
$about_morval_3_p ='When the rubble was cleared, this large empty square remained like a wound in the hearts of the inhabitants. Three years later the municipality built a small campanile where the three bells that were lowered for demolition were hung. Sponsored by 1930s residents, they remain alive and still ring the bell to mark every happy or unfortunate event as we once did.
Thanks to them, Morval does not forget its missing bell tower.
';
?>

View File

@@ -0,0 +1,218 @@
<?php
//include text files
include_once dirname(__FILE__).'/translations_termsandconditions.php';
include_once dirname(__FILE__).'/translations_privacy.php';
//Home.php
$h2_brand_name_1 = 'MorvalWatches';
$h2_brand_name_2 = 'a brand with a story';
$h2_brand_visit = 'Visit our collection';
$h2_brand_wow = 'Morval brings together a unique combination of minimalistic design, Swiss quality and Dutch manufacturing. We give you a watch to wear in any occasion.';
$h2_content_top = '';
$h2_home_content_1 = '';
$h2_home_content_2 = '';
//Products.php
$h1_content_top = 'Our watch collection';
$product_count_1 = 'Product';
$product_count_2 = 's';
$main_filter_category = 'Category';
$main_category = 'All';
$main_filter_sort = 'Sort';
$sort1 = 'A-Z';
$sort2 = 'Z-A';
$sort3 = 'Latest';
$sort4 = 'Oldest';
$sort5 = 'Price High - Low';
$sort6 = 'Price Low - High';
$free_delivery = 'free delivery';
$non_free_delivery = 'free delivery from ';
//Product.php
$breadcrum_products = 'Collection';
$product_quantity = 'Quantity';
$product_on_stock = 'In Stock';
$out_of_stock_notify = 'Notify Me';
$out_of_stock_notify_2 = 'Visit us @';
$out_of_stock = 'Out of stock';
$add_to_basket = 'Add To Cart';
//Cart.php
$h1_cart_name = 'Shopping Cart';
$h2_cart_suggestions = 'Suggestions';
$h2_cart_sample_product = '';
$tr_product = 'Product';
$tr_price = 'Price';
$tr_quantity = 'Quantity';
$tr_total = 'Total';
$total_subtotal = 'Subtotal';
$total_note = '';
$total_vat = 'VAT';
$total_shipping = 'Shipping';
$total_shipping_note = '(shipping included)';
$total_discount = 'Discount';
$total_total = 'Total';
$total_total_note = 'VAT included ';
$btn_emptycart = '🗑';
$btn_update = 'Update';
$btn_checkout = 'Checkout';
$navigation_back_to_store = 'return to collection';
$cart_message_empty = 'You have no products added in your Shopping Cart';
//Checkout.php
$error_account_name = 'Account already exists.';
$error_account_password_rules = 'Password must be between 5 and 20 characters long.';
$error_account_password_match = 'Passwords do not match.';
$error_account = 'Account creation required.';
$h1_checkout = 'Checkout';
$account_available = 'Already have an account?';
$account_log_in = 'Log In';
$account_create = 'Create Account';
$account_create_optional = '(optional)';
$account_create_email = 'Email';
$account_create_password = 'Password';
$account_create_password_confirm = 'Confirm Password';
$h2_Shipping_details = 'Shipping Details';
$h3_shipping_method = 'Shipping Method';
$shipping_first_name = 'First Name';
$shipping_last_name = 'Last Name';
$shipping_address = 'Address';
$shipping_city = 'City';
$shipping_state = 'Region/State';
$shipping_zip = 'Zip';
$shipping_country = 'Country';
$shipping_phone = 'Phone';
$payment_method = 'Payment Method';
$payment_method_1 = 'Debit (NL/BE customers)';
$payment_method_2 = 'Pay on delivery';
$h2_shoppingcart = 'Shopping Cart';
$discount_label = 'Discount Code';
$discount_message = 'Discount code applied!';
$discount_error_1 = 'Incorrect discount code!';
$discount_error_2 = 'Discount code expired!';
$order_consent_1 = 'I would like to recieve email communication about MorvalWatches news, products and services';
$order_consent_2 = 'I agree with';
$order_consent_3 = 'Terms & Conditions';
$btn_place_order = 'Order';
//Placeorder.php
$h1_order_succes_message = 'Your Order Has Been Placed';
$order_succes_message = 'Thank you for ordering with us! We will contact you by email with your order details.';
//Myaccount.php
$error_myaccount = 'Incorrect Email/Password!';
$error_myaccount_exists = $error_account_name;
$h1_login = 'Login';
$h1_register = 'Register';
$h1_myaccount = 'My Account';
$h2_menu = 'Menu';
$menu_orders = 'Orders';
$menu_downloads = 'Downloads';
$menu_settings = 'Settings';
$h2_myorders = 'My orders';
$myorders_message = 'You have no orders';
$myorders_order = 'Order';
$myorders_date = 'Date';
$myorders_status = 'Status';
$myorders_shipping = 'Shipping';
$myorders_total = 'Total';
$h2_mydownloads = 'My downloads';
$mydownloads_message = 'You have no downloads';
$mydownloads_product = 'Product';
$h2_settings = 'Settings';
$settings_email = 'Email';
$settings_new_password = 'New Password';
$btn_settings_save = 'Save';
//Functions.php
$age_consent_h4 = 'Lets check your age';
$age_consent_text = 'To visit MorvalWatches you need to be 18 years or older.';
$age_consent_btn_allow = 'Agree';
$age_consent_btn_deny = 'disagree';
$maintenanceMode_h4 = 'Webshop is in maintenance';
$maintenanceMode_text = 'Our webshop is in maintenance. We like to see you back soon';
$maintenanceMode_btn = 'OK';
$subject_order_notification = 'MorvalWatches - You have received a new order!';
$subject_new_order = 'MorvalWatches - Order Details';
$subject_out_of_stock = 'MorvalWatches - Out of Stock';
//Header_template (functions.php)
$home_text = 'Home';
$products_text = 'Collection';
$about_text = 'About Us';
$myaccount_text = 'Account';
//Footer_template (functions.php)
$social_punch_line = 'Connect with Morval via our social media channels';
$privacy_text = 'Privacy';
$terms_text = 'Terms and Conditions';
$faq_text = 'Frequent asked questions';
//order_details_template.php
$order_email_title = 'MorvalWatches - Order';
$order_email_message_1 = 'Thank you for your order';
$order_email_message_2 = 'Your order has been received and is currently being processed. The details for your order are below.';
$order_email_information = 'Your Details';
//--------------------------------------------------------
// Custom TEXT area
//--------------------------------------------------------
//ABOUT.PHP
$h2_about_1 = 'About Morval';
$h2_about_2 = '';
$about_header_1 = 'About US';
$about_1_p ='Morval Watches was founded in 2023 by Ralph van Wezel. Ralph is a hospital pharmacist and has a fascination for technology. In his work he strives to make medicines available that make a difference for the patient. Producing a medicine requires knowledge, precision, accuracy, technique, quality and craftsmanship. Herein lies the similarity with the manufacture of a high-quality automatic watch. Ralph has set itself the goal of developing a watch that can compete with the renowned brands, but is sold at an acceptable price.';
$about_header_2 = 'About our watches';
$about_2_p ='A Morval Watch is inspired by the vintage models and minimalistic design of Scandinavian watches. Due to variations in the color of the dial and straps, a Morval watch can be worn on any occasion, both as sport and dress watch.Morval watches meet the highest quality requirements and can compete with the well-known Swiss brands. The parts are supplied by renowned manufacturers from Europe and beyond. A Morval contains a Swiss-made caliber (STP) that is known for its reliable quality. The assemblies take place in Amsterdam by recognized watchmakers and each watch undergoes extensive quality control for functionality and aesthetics. The watch is manually adjusted and tested to minimize the deviation. Morval stands for an excellent price-quality ratio! When you purchase a Morval watch, you are assured of a timeless timepiece that will last for decades.A lot of attention has been paid to details, such as a brushed case of stainless steel 316 steel, superluminova on the hands, anti-reflective glass and infinitely adjustable leather strap. This translates into the luxurious appearance of the brand. With a Morval Watch you have a unique, robust, stylish and timeless timepiece that will last for generations!';
$about_header_3 = 'About Morval';
$about_3_p =' Morval takes its name from the surname of one of Ralph\'s grandparents. The logo is inspired by the monument in the town of Morval in northern France, which was built from the remains of a church and the three bells from the tower.';
$about_morval_text = 'Read more about the history of Morval';
//ABOUT MORVAL
$h2_about_morval_1 = 'The history of Morval';
$h2_about_morval_2 = '';
$about_morval_header_1 = '';
$about_morval_1_p ='Morval, a village of 96 inhabitants, is located between 4 communes of the Somme as a peninsula of Pas-de-Calais at the end of the canton of Bapaume.
Before 1914 it had 220 to 250 inhabitants, but it was completely destroyed during the Battle of Morval in the First World War (1914-18). The rebuilt church was the almost exact replica in style, allure and proportions (45 meters high, 40 meters long), as the previous one. The bell tower housed 3 beautiful bells, which is quite rare for a small village. It was inaugurated in October 1932.
Unfortunately, it was not a good reconstruction and in the aftermath of the Second World War it was necessary to repair the roof, close cracks around the bell tower, replace stained glass windows, etc. Soon, successive municipalities could no longer cope with the deterioration . A complete renovation would have required at least 1,500,000 francs, while the municipality\'s annual budget was only 120,000 francs.
In November 1973, during a funeral, part of the church collapsed during the sacrifice. The building was no longer safe and was closed for worship. In 1985, the mayor, deputies, councilors and administrators decided to destroy the church. An amount of 66,000 francs was reserved for this very sad operation, which was alleviated by a subsidy obtained from the general council.
';
$about_morval_header_2 = '';
$about_morval_2_p ='The entrepreneur, Mr Bonifatius d\'Equancourt (Somme), took advantage of a nice post-season and lowered the bells on September 21, 1987.
Mr Dromard, from the national CEFICEM center of Besançon (Doubs), had never had to carry out such work before. He first blew up the apse and transept with dynamite by drilling 600 holes. The preparations took longer than expected due to the hardness of the stones and concrete. At 7 p.m. on September 24, the 45-meter-high bell tower, so proud but so expensive to maintain, fell in a cloud of dust that had accumulated over 55 years.
Shrapnel splintered in all directions flew up to 100 meters. A brick was found in the kitchen of the school residence: it had gone through the open window. A nearby small house had broken windows despite closed shutters and the presence of an armored screen. The roof of the barn of the nearby farm was hit and in a small corner where photographers were standing, there were two minor injuries to the legs, not to mention a broken car window.
It was a bit like having the soul ripped out of our poor place that would never be like the others again.
';
$about_morval_header_3 = '';
$about_morval_3_p ='When the rubble was cleared, this large empty square remained like a wound in the hearts of the inhabitants. Three years later the municipality built a small campanile where the three bells that were lowered for demolition were hung. Sponsored by 1930s residents, they remain alive and still ring the bell to mark every happy or unfortunate event as we once did.
Thanks to them, Morval does not forget its missing bell tower.
';
?>

View File

@@ -0,0 +1,55 @@
<?php
$privacy_statement_header ='Privacy statement';
$privacy_statement_items ='
<p>MorvalWatches, in the Netherlands, is responsible for the processing of personal data as shown in this privacy statement.</p>
<h2>Contact details:</h2>
<p>MorvalWatches can be reached via https://www.morvalwatches.com</p>
<p>Mr R. van Wezel is the Data Protection Officer of MorvalWatches. He can be reached via info@morvalwatches.com</p><p>
</p><h2>Personal data that we process</h2>
<p>MorvalWatches processes your personal data because you use our services and/or because you provide it to us yourself.</p>
<p>Below you will find an overview of the personal data we process:</p>
<ul class="terms" style="padding-left:40px;">
<li>First and last name</li>
<li>Address data</li>
<li>Phone number</li>
<li>E-mail address</li>
</ul>
<h2>Special and/or sensitive personal data that we process</h2>
<p>Our website and/or service does not intend to collect data about website visitors under the age of 16. Unless they have parental or guardian consent. However, we cannot check whether a visitor is older than 16. We therefore advise parents to be involved in the online activities of their children, in order to prevent data about children from being collected without parental consent. If you are convinced that we have collected personal information about a minor without this permission, please contact us at info@morvalwatches.com and we will delete this information.</p>
<h2>For what purpose and on what basis do we process personal data</h2>
<p>MorvalWatches processes your personal data for the following purposes:</p>
<ul class="terms" style="padding-left:40px;">
<li>Sending our newsletter and/or advertising brochure</li>
<li>To be able to call or e-mail you if this is necessary to carry out our services</li>
<li>To inform you about changes to our services and products</li>
<li>To offer you the possibility to create an account</li>
</ul>
<h2>How long we keep personal data</h2>
<p>MorvalWatches does not store your personal data longer than is strictly necessary to achieve the purposes for which your data is collected. We use a retention period of 3 years for personal data.</p>
<h2>Sharing personal data with third parties</h2>
<p>MorvalWatches only provides to third parties and only if this is necessary for the execution of our agreement with you or to comply with a legal obligation.</p>
<h2>Cookies, or similar techniques, that we use</h2>
<p>MorvalWatches does not use cookies or similar techniques.</p>
<h2>View, modify or delete data</h2>
<p>You have the right to view, correct or delete your personal data. In addition, you have the right to withdraw your consent to the data processing or to object to the processing of your personal data by MorvalWatches and you have the right to data portability. This means that you can submit a request to us to send the personal data we hold about you in a computer file to you or another organization mentioned by you.</p>
<p>You can send a request for inspection, correction, deletion, data transfer of your personal data or request for withdrawal of your consent or objection to the processing of your personal data to info@morvalwatches.com.</p>
<p>To ensure that the request for inspection has been made by you, we ask you to enclose a copy of your proof of identity with the request. Make your passport photo, MRZ (machine readable zone, the strip with numbers at the bottom of the passport), passport number and citizen service number (BSN) black in this copy. This is to protect your privacy. We will respond to your request as quickly as possible, but within four weeks.</p>
<p>MorvalWatches would also like to point out that you have the option of submitting a complaint to the national supervisory authority, the Dutch Data Protection Authority. This can be done via the following link: https://autoriteitpersoonsgegevens.nl/nl/contact-met-de-autoriteit-persoonsgegevens/tip-ons</p>
<h2>How we secure personal data</h2>
<p>MorvalWatches takes the protection of your data seriously and takes appropriate measures to prevent misuse, loss, unauthorized access, unwanted disclosure and unauthorized modification. If you have the impression that your data is not properly secured or there are indications of abuse, please contact our customer service or via info@morvalwatches.com</p>
</div>';
?>

View File

@@ -0,0 +1,182 @@
<?php
$termsandconditions_header = 'General terms and conditions';
$termsandconditions_items = '
<ul>
<li><b>Article 1 - Definitions</b><br>
In these conditions the following definitions apply:
<ul>
<li><b>Additional agreement</b>: an agreement whereby the consumer acquires products, digital content and/or services in connection with a distance contract and these goods, digital content and/or services are supplied by the entrepreneur or by a third party on the basis of an agreement between that third party and the entrepreneur;</li>
<li><b>Reflection period</b>: the period within which the consumer can exercise his right of withdrawal;</li>
<li><b>Consumer</b>: the natural person who does not act for purposes related to his trade, business, craft or profession;</li>
<li><b>Day</b>: calendar day;</li>
<li><b>Digital content</b>: data produced and delivered in digital form;</li>
<li><b>Permanence agreement</b>: an agreement that extends to the regular delivery of goods, services and/or digital content during a certain period;</li>
<li><b>Durable data carrier</b>: any tool - including e-mail - that enables the consumer or entrepreneur to store information that is personally addressed to him in a way that allows future consultation or use for a period that is appropriate to the purpose for which the information is intended and that allows unchanged reproduction of the stored information;</li>
<li><b>Right of withdrawal</b>: the consumer\'s option to cancel the distance contract within the cooling-off period;</li>
<li><b>Entrepreneur</b>: the natural or legal person who offers products, (access to) digital content and/or services remotely to consumers;</li>
<li><b>Distance agreement</b>: an agreement concluded between the entrepreneur and the consumer in the context of an organized system for distance selling of products, digital content and/or services, whereby up to and including the conclusion of the agreement involves exclusive or partial use of one or more techniques for distance communication;</li>
<li><b>Model withdrawal form</b>: the European model withdrawal form included in Appendix I of these conditions;</li>
<li><b>Technology for distance communication</b>: means that can be used to conclude an agreement, without the consumer and entrepreneur having to meet in the same room at the same time.</li>
</ul>
</li>
<li><b>Article 2 - Identity of the entrepreneur</b><br>
<ul>
<li>Morval Watches</li>
<li>address: Kasteellaan 2, Sint-Oedenrode, The Netherlands</li>
<li>Phone number: +31(0) 6 39725831</li>
<li>Email address: info@morvalwatches.com</li>
</ul>
</li>
<li><b>Article 3 - Applicability</b>
<ul>
<li>These general terms and conditions apply to every offer from the entrepreneur and to every distance contract concluded between the entrepreneur and the consumer.</li>
<li>Before the distance contract is concluded, the text of these general terms and conditions will be made available to the consumer. If this is not reasonably possible, before the distance contract is concluded, the entrepreneur will indicate how the general terms and conditions can be viewed at the entrepreneur\'s premises and that they will be sent free of charge as soon as possible at the request of the consumer.</li >
<li>If the distance contract is concluded electronically, notwithstanding the previous paragraph and before the distance contract is concluded, the text of these general terms and conditions can be made available to the consumer electronically in such a way that it can be read by the consumer can be easily stored on a durable data carrier. If this is not reasonably possible, before the distance contract is concluded, it will be indicated where the general terms and conditions can be viewed electronically and that they will be sent free of charge electronically or otherwise at the request of the consumer. </li>
<li>In the event that, in addition to these general terms and conditions, specific product or service conditions also apply, the second and third paragraphs apply mutatis mutandis and in the event of conflicting conditions, the consumer can always rely on the applicable provision that applies to him is most favorable.</li>
</ul>
</li>
<li><b>Article 4 - The offer</b>
<ul>
<li>If an offer has a limited period of validity or is made subject to conditions, this will be expressly stated in the offer.</li>
<li>The offer contains a complete and accurate description of the products, digital content and/or services offered. The description is sufficiently detailed to enable a good assessment of the offer by the consumer. If the entrepreneur uses images, these are a true representation of the products, services and/or digital content offered. Obvious mistakes or errors in the offer do not bind the entrepreneur.</li>
<li>Due to differences in display qualities, colors of the real product may slighly differ from the image on the website.</li>
<li>Each offer contains such information that it is clear to the consumer what the rights and obligations are associated with accepting the offer.</li>
</ul>
</li>
<li><b>Article 5 - The Agreement</b>
<ul>
<li>The agreement is concluded, subject to the provisions of paragraph 4, at the time of acceptance by the consumer of the offer and compliance with the conditions set.</li>
<li>If the consumer has accepted the offer electronically, the entrepreneur will immediately confirm receipt of the acceptance of the offer electronically. As long as receipt of this acceptance has not been confirmed by the entrepreneur, the consumer can terminate the agreement.</li>
<li>If the agreement is concluded electronically, the entrepreneur will take appropriate technical and organizational measures to secure the electronic transfer of data and ensure a secure web environment. If the consumer can pay electronically, the entrepreneur will take appropriate security measures.</li>
<li>The entrepreneur can, within legal frameworks, inform himself whether the consumer can meet his payment obligations, as well as all those facts and factors that are important for a responsible conclusion of the distance contract. If, based on this investigation, the entrepreneur has good reasons not to enter into the agreement, he is entitled to refuse an order or request with reasons or to attach special conditions to the execution.</li>
<li>The entrepreneur will send the following information to the consumer at the latest upon delivery of the product, service or digital content, in writing or in such a way that it can be stored by the consumer in an accessible manner on a durable data carrier:
<ul>
<li>the visiting address of the entrepreneur\'s branch where the consumer can go with complaints;</li>
<li>the conditions under which and the manner in which the consumer can exercise the right of withdrawal, or a clear statement regarding the exclusion of the right of withdrawal;</li>
<li>the information about warranties and existing after-sales service;</li>
<li>the price including all taxes of the product, service or digital content; where applicable, the costs of delivery; and the method of payment, delivery or execution of the distance contract;</li>
<li>the requirements for termination of the agreement if the agreement has a duration of more than one year or is of indefinite duration;</li>
<li>if the consumer has a right of withdrawal, the model withdrawal form.</li>
</ul>
</li>
<li>In the case of a duration transaction, the provision in the previous paragraph only applies to the first delivery.</li>
</ul>
</li>
<li><b>Article 6 - Right of withdrawal</b><br>
<ul>
<li>The consumer can terminate an agreement regarding the purchase of a product during a cooling-off period of at least 14 days without giving reasons. The entrepreneur may ask the consumer about the reason for withdrawal, but may not oblige him to state his reason(s).</li>
<li>The cooling-off period referred to in paragraph 1 starts on the day after the consumer, or a third party designated in advance by the consumer, who is not the carrier, has received the product, or:
<ul>
<li>if the consumer has ordered multiple products in the same order: the day on which the consumer, or a third party designated by him, received the last product. The entrepreneur may, provided he has clearly informed the consumer about this prior to the ordering process, refuse an order for multiple products with different delivery times.</li>
<li>if the delivery of a product consists of several shipments or parts: the day on which the consumer, or a third party designated by him, has received the last shipment or part;</li>
<li>in the case of agreements for regular delivery of products during a certain period: the day on which the consumer, or a third party designated by him, received the first product.</li>
</ul>
</li>
</ul>
</li>
<li><b>Article 7 - Obligations of the consumer during the cooling-off period</b>
<ul>
<li>During the cooling-off period, the consumer will handle the product and packaging with care. He will only unpack or use the product to the extent necessary to determine the nature, characteristics and operation of the product. The basic principle here is that the consumer may only handle and inspect the product as he would in a store.</li>
<li>The consumer is only liable for any reduction in value of the product that is the result of handling the product that goes beyond what is permitted in paragraph 1.</li>
<li>The consumer is not liable for depreciation of the product if the entrepreneur has not provided him with all legally required information about the right of withdrawal before or at the time of concluding the agreement.</li>
</ul>
</li>
<li><b>Article 8 - Exercise of the right of withdrawal by the consumer and costs thereof</b>
<ul>
<li>If the consumer makes use of his right of withdrawal, he must report this to the entrepreneur within the cooling-off period by means of the model withdrawal form or in another unambiguous manner. </li>
<li>As soon as possible, but within 14 days from the day following the notification referred to in paragraph 1, the consumer shall return the product or hand it over to (an authorized representative of) the entrepreneur. This is not necessary if the entrepreneur has offered to collect the product himself. The consumer has in any case observed the return period if he returns the product before the cooling-off period has expired.</li>
<li>The consumer returns the product with all supplied accessories, if reasonably possible in the original condition and packaging, and in accordance with the reasonable and clear instructions provided by the entrepreneur.</li>
<li>The risk and burden of proof for the correct and timely exercise of the right of withdrawal lies with the consumer.</li>
<li>The consumer bears the direct costs of returning the product. If the entrepreneur has not stated that the consumer must bear these costs or if the entrepreneur indicates that he will bear the costs himself, the consumer does not have to bear the costs for return.</li>
</ul>
</li>
<li><b>Article 9 - Obligations of the entrepreneur in the event of withdrawal</b>
<ul>
<li>If the entrepreneur enables the notification of withdrawal by the consumer electronically, he will immediately send a confirmation of receipt after receiving this notification.</li>
<li>The entrepreneur will reimburse all payments made by the consumer, including any delivery costs charged by the entrepreneur for the returned product, without delay but within 14 days following the day on which the consumer notifies him of the withdrawal. Unless the entrepreneur offers to collect the product himself, he may wait with reimbursement until he has received the product or until the consumer demonstrates that he has returned the product, whichever is the earlier.</li>
<li>The entrepreneur uses the same payment method that the consumer used for reimbursement, unless the consumer agrees to a different method. The refund is free of charge for the consumer.</li>
<li>If the consumer has opted for a more expensive method of delivery than the cheapest standard delivery, the entrepreneur does not have to reimburse the additional costs for the more expensive method.</li>
</ul>
</li>
<li><b>Article 10 - Exclusion of right of withdrawal</b><br>
The entrepreneur can exclude the following products and services from the right of withdrawal, but only if the entrepreneur has clearly stated this in the offer, at least in time before concluding the agreement:
<ul>
<li>Products or services whose price is subject to fluctuations in the financial market over which the entrepreneur has no influence and which may occur within the withdrawal period;</li>
<li>Agreements concluded during a public auction. A public auction is defined as a sales method in which products, digital content and/or services are offered by the entrepreneur to the consumer who is personally present or has the opportunity to be personally present at the auction, under the supervision of an auctioneer, and where the successful bidder is obliged to purchase the products, digital content and/or services;</li>
<li>Products manufactured to consumer specifications, which are not prefabricated and which are manufactured on the basis of an individual choice or decision of the consumer, or which are clearly intended for a specific person;</li>
<li>Sealed products that are not suitable for return for reasons of health protection or hygiene and of which the seal has been broken after delivery;</li>
</ul>
</li>
<li><b>Article 11 - The price</b>
<ul>
<li>During the period of validity stated in the offer, the prices of the products and/or services offered will not be increased, except for price changes as a result of changes in VAT rates.</li>
<li>Notwithstanding the previous paragraph, the entrepreneur may offer products or services with variable prices, the prices of which are subject to fluctuations in the financial market and over which the entrepreneur has no influence. This liability to fluctuations and the fact that any prices stated are target prices are stated in the offer.</li>
<li>Price increases within 3 months after the conclusion of the agreement are only permitted if they are the result of legal regulations or provisions.</li>
<li>Price increases from 3 months after the conclusion of the agreement are only permitted if the entrepreneur has stipulated this and:
<ul>
<li>these are the result of legal regulations or provisions; or</li>
<li>the consumer has the right to cancel the agreement with effect from the day on which the price increase takes effect.</li>
</ul>
</li>
<li>The prices stated in the offer of products or services include VAT.</li>
</ul>
</li>
<li><b>Article 12 - Compliance and additional warranty</b>
<ul>
<li>The enterpreneur guarantees a proper working of the product for a period of 2 years from the day the consumer has received the product.</li>
<li>The entrepreneur guarantees that the products and/or services comply with the agreement, the specifications stated in the offer, the reasonable requirements of reliability and/or usability and the existing requirements on the date of the conclusion of the agreement. </li>
<li>An additional guarantee provided by the entrepreneur, his supplier, manufacturer or importer never limits the legal rights and claims that the consumer can assert against the entrepreneur under the agreement if the entrepreneur has failed to fulfill his part of the contract. </li>
<li>Extra warranty means any obligation of the entrepreneur, his supplier, importer or producer in which he grants the consumer certain rights or claims that go beyond what he is legally obliged to do in the event that he has failed to fulfill his part of the agreement.</li>
</ul>
</li>
<li><b>Article 13 - Delivery and execution</b>
<ul>
<li>The entrepreneur will exercise the utmost care when receiving and executing orders for products and when assessing applications for the provision of services.</li>
<li>The place of delivery is the address that the consumer has communicated to the entrepreneur.</li>
<li>With due observance of what is stated in Article 4 of these general terms and conditions, the entrepreneur will execute accepted orders expeditiously, but no later than within 6 weeks, unless a different delivery period has been agreed. If delivery is delayed, or if an order cannot be fulfilled or can only be partially fulfilled, the consumer will be notified of this no later than 6 weeks after he has placed the order. In that case, the consumer has the right to terminate the agreement without costs and is entitled to any compensation.</li>
<li>After dissolution in accordance with the previous paragraph, the entrepreneur will immediately refund the amount paid by the consumer.</li>
<li>The risk of damage and/or loss of products rests with the entrepreneur until the moment of delivery to the consumer or a representative designated in advance and made known to the entrepreneur, unless expressly agreed otherwise.</li>
</ul>
</li>
<li><b>Article 14 - Duration transactions: duration, termination and extension</b>
<br>
Termination:
<ul>
<li>The consumer can terminate an agreement that has been entered into for an indefinite period and that extends to the regular delivery of products (including electricity) or services at any time, taking into account the agreed cancellation rules and a notice period of no more than one month. </li>
<br>
Extension:
<li>An agreement that has been entered into for a specific period and that extends to the regular delivery of products (including electricity) or services may not be tacitly extended or renewed for a specific period.</li>
<br>
Duration:
<li>If an agreement has a duration of more than one year, the consumer may terminate the agreement at any time after one year with a notice period of no more than one month, unless reasonableness and fairness oppose termination before the end of the agreed period.</li>
</ul>
</li>
<li><b>Article 15 - Payment</b>
<ul>
<li>Unless otherwise stipulated in the agreement or additional conditions, the amounts owed by the consumer must be paid within 14 days after the cooling-off period commences, or in the absence of a cooling-off period within 14 days after the conclusion of the contract. In the case of an agreement to provide a service, this period commences on the day after the consumer has received confirmation of the agreement.</li>
<li>The consumer has the obligation to immediately report inaccuracies in payment details provided or stated to the entrepreneur.</li>
<li>If the consumer does not fulfill his payment obligation(s) on time, after the entrepreneur has informed him of the late payment and the entrepreneur has granted the consumer a period of 14 days to still fulfill his payment obligations, statutory interest will be owed on the amount still owed and the entrepreneur is entitled to charge the extrajudicial collection costs incurred by him. These collection costs amount to a maximum of: 15% on outstanding amounts up to € 2,500; 10% on the next € 2,500 and 5% on the next € 5,000 with a minimum of € 40. The entrepreneur may deviate from the stated amounts and percentages for the benefit of the consumer.</li>
</ul>
</li>
<li><b>Article 16 - Complaints procedure</b>
<ul>
<li>The entrepreneur has a sufficiently publicized complaints procedure and handles the complaint in accordance with this complaints procedure.</li>
<li>Complaints about the execution of the agreement must be submitted fully and clearly described to the entrepreneur within a reasonable time after the consumer has discovered the defects.</li>
<li>Complaints submitted to the entrepreneur will be answered within a period of 14 days from the date of receipt. If a complaint requires a foreseeably longer processing time, the entrepreneur will respond within 14 days with an acknowledgment of receipt and an indication of when the consumer can expect a more detailed answer.</li>
<li>If the complaint cannot be resolved by mutual agreement within a reasonable period or within 3 months after submitting the complaint, a dispute arises that is subject to the dispute settlement procedure.</li>
</ul>
</li>
<li><b>Article 17 - Disputes</b>
<ul>
<li>Agreements between the entrepreneur and the consumer to which these general terms and conditions apply are exclusively governed by Dutch law.</li>
</ul>
</li>
<li><b>Article 18 - Additional or deviating provisions</b><br>
Additional or deviating provisions from these general terms and conditions may not be to the detriment of the consumer and must be recorded in writing or in such a way that they can be stored by the consumer in an accessible manner on a durable data carrier.
</li>
</ul>
</div>';
?>

33
download.php Normal file
View File

@@ -0,0 +1,33 @@
<?php
// Prevent direct access to file
defined(security_key) or exit;
// Disable the time limit (for large files)
set_time_limit(0);
// Customer must be logged-in and must provide the secret ID
if (isset($_GET['id'], $_SESSION['account_loggedin'])) {
// Get the product download URL and check if the ID matches
$stmt = $pdo->prepare('SELECT pd.* FROM products_downloads pd JOIN transactions t ON t.account_id = ? JOIN transactions_items ti ON t.txn_id = ti.txn_id AND ti.item_id = pd.product_id AND MD5(CONCAT(ti.txn_id, pd.id)) = ?');
$stmt->execute([ $_SESSION['account_id'], $_GET['id'] ]);
// Fetch the product from the database and return the result as an Array
$product_download = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$product_download) {
exit('Invalid ID!');
}
} else {
exit('Invalid ID!');
}
// Create the headers for the downloadable file
header('Pragma: public');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Cache-Control: public');
header('Content-Description: File Transfer');
header('Content-type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . basename($product_download['file_path']) . '"');
header('Content-Transfer-Encoding: binary');
header('Content-Length: ' . filesize($product_download['file_path']));
ob_end_flush();
// Download file
@readfile($product_download['file_path']);
exit;
?>

BIN
font/.DS_Store vendored Normal file

Binary file not shown.

27
font/GOODDOGP.TXT Normal file
View File

@@ -0,0 +1,27 @@
GoodDog Plain©1997 Fonthead Design
What is GoodDog Plain?
This is the latest font from Fonthead Design! It is a stripped down
version of my other font called GoodDog Cool. It is still whacky and fun,
looks great as text, and guaranteed to make your documents fun!
Also if you want the other two fonts in this set-- GoodDog Cool,
and GoodDog Bones (clip art!), be sure to visit my web site!
You can find them there for free download!
Is it shareware?
No! This is FREEWARE! Use it to your hearts content without any guilt.
Where is the Fonthead World Wide Web site?
http://www.fonthead.com
How can you contact me?
Email:
ethan@fonthead.com
URL:
http://www.fonthead.com
Distribution and Copyright Info:
This font is freeware, but is not in the public domain (meaning it is the sole property
of Fonthead Design). You have permission to redistribute this package as long as this
readme and the accompanying files are included. and... Use the font everywhere!

BIN
font/GOODDP__.TTF Normal file

Binary file not shown.

BIN
font/Gerb-Bold.ttf Normal file

Binary file not shown.

BIN
font/Gerb.ttf Normal file

Binary file not shown.

Binary file not shown.

784
functions.php Normal file
View File

@@ -0,0 +1,784 @@
<?php
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++
// PDF creator +++++++++++++++++++++++++++++++++++++++
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++
include dirname(__FILE__).'/custom/customfunctions.php';
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++
// PDF creator +++++++++++++++++++++++++++++++++++++++
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++
// include autoloader
require dirname(__FILE__).'/lib/dompdf/autoload.inc.php';
// reference the Dompdf namespace
use Dompdf\Dompdf;
// instantiate and use the dompdf class
use Dompdf\Options;
//+++++++++++++++++++++++++++++++++++++++++++
//dompdf libary +++++++++++++++++++++++++++
//+++++++++++++++++++++++++++++++++++++++++++
$options = new Options();
$options->set('isRemoteEnabled', true);
$dompdf = new Dompdf($options);
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++
// include PHP Mailer+++++++++++++++++++++++++++++++++++++
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
require dirname(__FILE__).'/lib/mail/PHPMailer.php';
require dirname(__FILE__).'/lib/mail/SMTP.php';
require dirname(__FILE__).'/lib/mail/Exception.php';
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Send Mail via PHPMailer++++++++++++++++++++++++++++++++
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++
function send_mail_by_PHPMailer($to, $subject, $message, $attachment, $attachment_name){
// SEND MAIL by PHP MAILER
$mail = new PHPMailer();
$mail->CharSet = 'UTF-8';
//$mail->isSMTP(); // Use SMTP protocol
$mail->Host = email_host_name; // Specify SMTP server
$mail->SMTPAuth = true; // Auth. SMTP
$mail->Username = email; // Mail who send by PHPMailer
$mail->Password = email_outgoing_pw; // your pass mail box
$mail->SMTPSecure = email_outgoing_security; // Accept SSL
$mail->Port = email_outgoing_port; // port of your out server
$mail->setFrom(email, mail_from); // Mail to send at
$mail->addAddress($to); // Add sender
$mail->addReplyTo(email_reply_to); // Adress to reply
$mail->isHTML(true); // use HTML message
$mail->Subject = $subject;
$mail->Body = $message;
if (!empty($attachment) || $attachment != ''){
$mail->AddStringAttachment($attachment, $attachment_name, 'base64', 'application/pdf');
}
// SEND
if( !$mail->send() ){
// render error if it is
$tab = array('error' => 'Mailer Error: '.$mail->ErrorInfo );
echo json_encode($tab);
exit;
}
else{
// return true if message is send
return true;
}
}
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Generated PDF ++++++++++++++++++++++++++++++++
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++
function generatedPDF($input,$filename){
$options = new Options();
$options->set('isRemoteEnabled', true);
$dompdf = new Dompdf($options);
$dompdf->loadHtml($input);
// (Optional) Setup the paper size and orientation
$dompdf->setPaper('A4', 'portrait');
// Render the HTML as PDF
$dompdf->render();
ob_end_clean();
$dompdf->stream($filename.'.pdf', array("Attachment" => false));
}
// Function that will connect to the MySQL database
function pdo_connect_mysql() {
try {
// Connect to the MySQL database using the PDO interface
$pdo = new PDO('mysql:host=' . db_host . ';dbname=' . db_name . ';charset=utf8', db_user, db_pass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $pdo;
} catch (PDOException $exception) {
// Could not connect to the MySQL database! If you encounter this error, ensure your db settings are correct in the config file!
exit('Failed to connect to database!');
}
}
// Function to retrieve a product from cart by the ID and options string
function &get_cart_product($id, $options) {
$p = null;
if (isset($_SESSION['cart'])) {
foreach ($_SESSION['cart'] as &$product) {
if ($product['id'] == $id && $product['options'] == $options) {
$p = &$product;
return $p;
}
}
}
return $p;
}
// Populate categories function
function populate_categories($categories, $selected = 0, $parent_id = 0, $n = 0) {
$html = '';
foreach ($categories as $category) {
if ($parent_id == $category['parent_id'] && $category['status'] == 1) {
$html .= '<option value="' . $category['id'] . '"' . ($selected == $category['id'] ? ' selected' : '') . '>' . str_repeat('--', $n) . ' ' . $category['name'] . '</option>';
$html .= populate_categories($categories, $selected, $category['id'], $n+1);
}
}
return $html;
}
// Send order details email function
function send_order_details_email($email, $products, $first_name, $last_name, $address_street, $address_city, $address_state, $address_zip, $address_country, $subtotal, $discounttotal,$shippingtotal,$taxtotal,$total, $order_id) {
include './custom/translations_'.strtoupper(language_code).'.php';
// Send payment notification to webmaster
$address_name = htmlspecialchars($first_name ?? '', ENT_QUOTES).' '.htmlspecialchars($last_name ?? '', ENT_QUOTES);
if (email_notifications) {
$subject = $subject_order_notification;
$headers = 'From: ' . mail_from . "\r\n" . 'Reply-To: ' . $email . "\r\n" . 'Return-Path: ' . mail_from . "\r\n" . 'X-Mailer: PHP/' . phpversion() . "\r\n" . 'MIME-Version: 1.0' . "\r\n" . 'Content-Type: text/html; charset=UTF-8' . "\r\n";
ob_start();
include './custom/order-notification-template.php';
$order_notification_template = ob_get_clean();
send_mail_by_PHPMailer(email, $subject, $order_notification_template, '', '');
}
if (!mail_enabled) {
return;
}
$subject = $subject_new_order;
$headers = 'From: ' . mail_from . "\r\n" . 'Reply-To: ' . mail_from . "\r\n" . 'Return-Path: ' . mail_from . "\r\n" . 'X-Mailer: PHP/' . phpversion() . "\r\n" . 'MIME-Version: 1.0' . "\r\n" . 'Content-Type: text/html; charset=UTF-8' . "\r\n";
ob_start();
include './custom/order-details-template.php';
$order_details_template = ob_get_clean();
send_mail_by_PHPMailer($email, $subject, $order_details_template, '', '');
}
//Send email to administrator for out of stock notification // only for registered users
function send_product_notification_email($email,$product_details){
include './custom/translations_'.strtoupper(language_code).'.php';
$subject = $subject_out_of_stock.' - '.$product_details;
$headers = 'From: ' . mail_from . "\r\n" . 'Reply-To: ' . $email . "\r\n" . 'Return-Path: ' . mail_from . "\r\n" . 'X-Mailer: PHP/' . phpversion() . "\r\n" . 'MIME-Version: 1.0' . "\r\n" . 'Content-Type: text/html; charset=UTF-8' . "\r\n";
$message = $product_details.' are out of stock. Please notify '.$email.' when available';
//mail(email, $subject, $message, $headers);
send_mail_by_PHPMailer(email, $subject, $message, '', '');
}
// Template admin header
function template_admin_header($title, $selected = 'orders', $selected_child = 'view') {
$base_url = 'https://'.$_SERVER['SERVER_NAME'].'/';
$site_name = site_name;
$icon_image = icon_image;
$admin_links = '
<a href="index.php?page=dashboard"' . ($selected == 'dashboard' ? ' class="selected"' : '') . '><i class="fas fa-tachometer-alt"></i>Dashboard</a>
<a href="index.php?page=orders"' . ($selected == 'orders' ? ' class="selected"' : '') . '><i class="fas fa-shopping-cart"></i>Orders</a>
<div class="sub">
<a href="index.php?page=orders"' . ($selected == 'orders' && $selected_child == 'view' ? ' class="selected"' : '') . '><span>&#9724;</span>View Orders</a>
<a href="index.php?page=order_manage"' . ($selected == 'orders' && $selected_child == 'manage' ? ' class="selected"' : '') . '><span>&#9724;</span>Create Order</a>
</div>
<a href="index.php?page=products"' . ($selected == 'products' ? ' class="selected"' : '') . '><i class="fas fa-box-open"></i>Products</a>
<div class="sub">
<a href="index.php?page=products"' . ($selected == 'products' && $selected_child == 'view' ? ' class="selected"' : '') . '><span>&#9724;</span>View Products</a>
<a href="index.php?page=product"' . ($selected == 'products' && $selected_child == 'manage' ? ' class="selected"' : '') . '><span>&#9724;</span>Create Product</a>
</div>
<a href="index.php?page=categories"' . ($selected == 'categories' ? ' class="selected"' : '') . '><i class="fas fa-list"></i>Categories</a>
<div class="sub">
<a href="index.php?page=categories"' . ($selected == 'categories' && $selected_child == 'view' ? ' class="selected"' : '') . '><span>&#9724;</span>View Categories</a>
<a href="index.php?page=category"' . ($selected == 'categories' && $selected_child == 'manage' ? ' class="selected"' : '') . '><span>&#9724;</span>Create Category</a>
</div>
<a href="index.php?page=accounts"' . ($selected == 'accounts' ? ' class="selected"' : '') . '><i class="fas fa-users"></i>Accounts</a>
<div class="sub">
<a href="index.php?page=accounts"' . ($selected == 'accounts' && $selected_child == 'view' ? ' class="selected"' : '') . '><span>&#9724;</span>View Accounts</a>
<a href="index.php?page=account"' . ($selected == 'accounts' && $selected_child == 'manage' ? ' class="selected"' : '') . '><span>&#9724;</span>Create Account</a>
</div>
<a href="index.php?page=shipping"' . ($selected == 'shipping' ? ' class="selected"' : '') . '><i class="fas fa-shipping-fast"></i>Shipping</a>
<div class="sub">
<a href="index.php?page=shipping"' . ($selected == 'shipping' && $selected_child == 'view' ? ' class="selected"' : '') . '><span>&#9724;</span>View Shipping Methods</a>
<a href="index.php?page=shipping_process"' . ($selected == 'shipping' && $selected_child == 'manage' ? ' class="selected"' : '') . '><span>&#9724;</span>Create Shipping Method</a>
</div>
<a href="index.php?page=discounts"' . ($selected == 'discounts' ? ' class="selected"' : '') . '><i class="fas fa-tag"></i>Giftcards / Discounts</a>
<div class="sub">
<a href="index.php?page=discounts"' . ($selected == 'discounts' && $selected_child == 'view' ? ' class="selected"' : '') . '><span>&#9724;</span>View Giftcard / Discounts</a>
<a href="index.php?page=discount"' . ($selected == 'discounts' && $selected_child == 'manage' ? ' class="selected"' : '') . '><span>&#9724;</span>Create Giftcard / Discount</a>
</div>
<a href="index.php?page=taxes"' . ($selected == 'taxes' ? ' class="selected"' : '') . '><i class="fa-solid fa-percent"></i>Taxes</a>
<div class="sub">
<a href="index.php?page=taxes"' . ($selected == 'taxes' && $selected_child == 'view' ? ' class="selected"' : '') . '><span>&#9724;</span>View Taxes</a>
<a href="index.php?page=tax"' . ($selected == 'taxes' && $selected_child == 'manage' ? ' class="selected"' : '') . '><span>&#9724;</span>Create Tax</a>
</div>
<a href="index.php?page=media"' . ($selected == 'media' ? ' class="selected"' : '') . '><i class="fas fa-images"></i>Media</a>
<a href="index.php?page=emailtemplates"' . ($selected == 'emailtemplates' ? ' class="selected"' : '') . '><i class="fas fa-envelope"></i>Email Templates</a>
<a href="index.php?page=settings"' . ($selected == 'settings' ? ' class="selected"' : '') . '><i class="fas fa-tools"></i>Settings</a>
<div class="sub">
<a href="index.php?page=settings"' . ($selected == 'settings' && $selected_child == '' ? ' class="selected"' : '') . '><span>&#9724;</span>Settings</a>
<a href="index.php?page=language"' . ($selected == 'language' && $selected_child == '' ? ' class="selected"' : '') . '><span>&#9724;</span>Language</a>
</div>
';
// DO NOT INDENT THE BELOW CODE
echo <<<EOT
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,minimum-scale=1">
<title>$title</title>
<link rel="icon" type="image/png" href="{$base_url}$icon_image">
<link href="admin.css" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v6.0.0/css/all.css">
</head>
<body class="admin">
<aside class="responsive-width-100 responsive-hidden">
<h1>$site_name</h1>
$admin_links
<div class="footer">
</div>
</aside>
<main class="responsive-width-100">
<header>
<a class="responsive-toggle" href="#">
<i class="fas fa-bars"></i>
</a>
<div class="space-between"></div>
<div class="dropdown right">
<i class="fas fa-user-circle"></i>
<div class="list">
<a href="index.php?page=account&id={$_SESSION['account_id']}">Edit Profile</a>
<a href="index.php?page=logout">Logout</a>
</div>
</div>
</header>
EOT;
}
// Template admin footer
function template_admin_footer($js_script = '') {
$js_script = $js_script ? '<script>' . $js_script . '</script>' : '';
// DO NOT INDENT THE BELOW CODE
echo <<<EOT
</main>
<script src="admin.js"></script>
{$js_script}
</body>
</html>
EOT;
}
// Determine URL function
function url($url) {
if (rewrite_url) {
$url = preg_replace('/\&(.*?)\=/', '/', str_replace(['index.php?page=', 'index.php'], '', $url));
}
return base_url . $url;
}
// Routeing function
function routes($urls) {
foreach ($urls as $url => $file_path) {
$url = '/' . ltrim($url, '/');
$prefix = dirname($_SERVER['PHP_SELF']);
$uri = $_SERVER['REQUEST_URI'];
if (substr($uri, 0, strlen($prefix)) == $prefix) {
$uri = substr($uri, strlen($prefix));
}
$uri = '/' . ltrim($uri, '/');
$path = explode('/', parse_url($uri)['path']);
$routes = explode('/', $url);
$values = [];
foreach ($path as $pk => $pv) {
if (isset($routes[$pk]) && preg_match('/{(.*?)}/', $routes[$pk])) {
$var = str_replace(['{','}'], '', $routes[$pk]);
$routes[$pk] = preg_replace('/{(.*?)}/', $pv, $routes[$pk]);
$values[$var] = $pv;
}
}
if ($routes === $path && rewrite_url) {
foreach ($values as $k => $v) {
$_GET[$k] = $v;
}
return file_exists($file_path) ? $file_path : 'home.php';
}
}
if (rewrite_url) {
header('Location: ' . url('index.php'));
exit;
}
return null;
}
// Format bytes to human-readable format
function format_bytes($bytes) {
$i = floor(log($bytes, 1024));
return round($bytes / pow(1024, $i), [0,0,2,2,3][$i]).['B','KB','MB','GB','TB'][$i];
}
function getAccessoiries($pdo, $categoryID){
include './custom/translations_'.strtoupper(language_code).'.php';
$stmt = $pdo->prepare('SELECT p.*, (SELECT m.full_path FROM products_media pm JOIN media m ON m.id = pm.media_id WHERE pm.product_id = p.id ORDER BY pm.position ASC LIMIT 1) AS img FROM products p JOIN products_categories pc ON pc.category_id = :category_id AND pc.product_id = p.id JOIN categories c ON c.id = pc.category_id WHERE p.status = 1');
$stmt->bindValue(':category_id', $categoryID, PDO::PARAM_INT);
$stmt->execute();
$additional_products = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo '<div class="content-wrapper">
<div class="add_products">
<h2>'.$h2_cart_suggestions.'</h2>
';
foreach ($additional_products as $additional_product){
if (!empty($additional_product['img']) && file_exists($additional_product['img'])){
$url_contents = 'index.php?page=product&id=';
$url_contents .= $additional_product['url_slug'] ? $additional_product['url_slug'] : $additional_product['id'];
$additional_product_url = url($url_contents);
echo'
<div class="add_product">
<a href="'.$additional_product_url.'" id="'.$additional_product['id'].'A" class="product">
<img src="'.base_url.$additional_product['img'].'" id="'.$additional_product['id'].'" width="15%" height="" alt="'.$additional_product['name'].'">
</a>
<form id="product-form" action="" method="post">
<input type="hidden" name="add_product_id" value="'.$additional_product['id'].'">
<input type="hidden" name="add_product_price" value="'.$additional_product['price'].'">
<input type="hidden" name="add_product_weight" value="'.$additional_product['weight'].'">
<input type="submit" name="accessoiries" value="+">
</form>
<a href="'.$additional_product_url.'" id="'.$additional_product['id'].'A" class="product">
<span class="add_name">'.$additional_product['name'].'</span>
<span class="add_price"> '.currency_code.'.'.number_format($additional_product['price'],2).'
';
if ($additional_product['rrp'] > 0){
echo '
<span class="add_rrp">'.currency_code.''.number_format($additional_product['rrp'],2).'</span>
';
}
echo'
</a>
</div>
';
}
}
echo '</div></div>';
}
function getSamples($pdo, $categoryID){
include './custom/translations_'.strtoupper(language_code).'.php';
$stmt = $pdo->prepare('SELECT p.*, (SELECT m.full_path FROM products_media pm JOIN media m ON m.id = pm.media_id WHERE pm.product_id = p.id ORDER BY pm.position ASC LIMIT 1) AS img FROM products p JOIN products_categories pc ON pc.category_id = :category_id AND pc.product_id = p.id JOIN categories c ON c.id = pc.category_id WHERE p.status = 1');
$stmt->bindValue(':category_id', $categoryID, PDO::PARAM_INT);
$stmt->execute();
$additional_products = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo '<div class="content-wrapper">
<h2 style="font-weight:normal;">'.$h2_cart_samples.'</h2>
<div class="add_sample_button"><button id="slideLeft" class="scrollButton" type="button"><</button></div>
<div id="add_samples_container" class="add_samples">
';
foreach ($additional_products as $additional_product){
if (!empty($additional_product['img']) && file_exists($additional_product['img'])){
//Pricing override for samples
if (sample_pricing_override){
$additional_product['price'] = sample_pricing_override_price; // Price override for free sample! Remove for standard price
}
$url_contents = 'index.php?page=product&id=';
$url_contents .= $additional_product['url_slug'] ? $additional_product['url_slug'] : $additional_product['id'];
$additional_product_url = url($url_contents);
echo'
<div class="add_sample">
<img src="'.base_url.$additional_product['img'].'" id="'.$additional_product['id'].'" width="50" height="50" alt="'.$additional_product['name'].'">
<form id="product-form" action="" method="post">
<input type="hidden" name="add_product_id" value="'.$additional_product['id'].'">
<input type="hidden" name="add_product_price" value="'.$additional_product['price'].'">
<input type="hidden" name="add_product_weight" value="'.$additional_product['weight'].'">
<input type="submit" name="samples" value="+">
</form>
<a href="'.$additional_product_url.'" id="'.$additional_product['id'].'A" class="product">
<span class="add_name">'.$additional_product['name'].'</span>';
if ($additional_product['price'] > 0){
echo '
<span class="add_price"> '.currency_code.'.'.number_format($additional_product['price'],2).'
';
}
if ($additional_product['rrp'] > 0){
echo '
<span class="add_rrp">'.currency_code.''.number_format($additional_product['rrp'],2).'</span>
';
}
echo'
</a>
</div>
';
}
}
echo '
</div>
<div class="add_sample_button"><button id="slideRight" class="scrollButton" type="button">></button></div>
</div>';
}
function createGiftCart($pdo, $orderID){
$giftcard_ID = giftcard_id;
//Check if Giftcard is ordered
$stmt = $pdo->prepare('SELECT t.payer_email as email, ti.id as id, ti.txn_id as txn, ti.item_price as item_price, ti.item_quantity as item_quantity FROM transactions t INNER JOIN transactions_items ti ON t.txn_id = ti.txn_id INNER JOIN products_categories p ON ti.item_id = p.product_id WHERE p.category_id = ? AND ti.txn_id = ?');
$stmt->execute([$giftcard_ID,$orderID]);
$giftcards = $stmt->fetchAll(PDO::FETCH_ASSOC);
if ($giftcards) {
foreach ($giftcards as $giftcard) {
//For each quantity
$x = 0;
while ($x < $giftcard['item_quantity']){
//Generate discount code = TXN/ID/X
$discount_code = $giftcard['txn'].'#'.$giftcard['id'].'#'.$x;
$value = $giftcard['item_price'];
// Get the current date
$start_date = date("Y-m-d H:i:s");
$end_date = date("Y-m-d H:i:s", strtotime("+5 years"));;
//Check if Giftcard already exists
$stmt = $pdo->prepare('SELECT * from discounts WHERE discount_code = ?');
$stmt->execute([$discount_code]);
$discount_exist = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (empty($discount_exist) || $discount_exist == '') {
//Insert Giftcard
//SQL Insert
$stmt = $pdo->prepare('INSERT INTO discounts (discount_code,discount_type,discount_value,start_date,end_date) VALUES (?,?,?,?,?)');
$stmt->execute([$discount_code, 'Fixed', $value, $start_date, $end_date]);
}
$x++;
}
}
}
}
function useGiftCart($pdo, $giftcard){
$discount_code = $giftcard;
// Get the current date
$end_date = date("Y-m-d H:i:s");
//Check if Giftcard already exists
$stmt = $pdo->prepare('SELECT * from discounts WHERE discount_code = ?');
$stmt->execute([$discount_code]);
$discount_exist = $stmt->fetchAll(PDO::FETCH_ASSOC);
//Dump parameters
//$stmt->debugDumpParams();
if (!empty($discount_exist) || $discount_exist != '') {
//Update Giftcard end data
$stmt = $pdo->prepare('UPDATE discounts SET end_date = ? WHERE discount_code = ?');
$stmt->execute([$end_date,$discount_code]);
}
}
function removeGiftCart($pdo, $orderID){
$discount_code = $orderID.'#%#%';
//Check if Giftcard already exists
$stmt = $pdo->prepare('SELECT * from discounts WHERE discount_code like ?');
$stmt->execute([$discount_code]);
$discount_exist = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (!empty($discount_exist) || $discount_exist != '') {
//Remove all Giftcards related to order
$stmt = $pdo->prepare('DELETE FROM discounts WHERE discount_code like ?');
$stmt->execute([$discount_code]);
}
}
function generateInvoice($pdo, $orderID){
// 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.txn_id = ?');
$stmt->execute([ $orderID ]);
$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, 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.txn_id = ?');
$stmt->execute([ $orderID]);
$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;
//$stmt->debugDumpParams();
//Variables
$customer_email = htmlspecialchars($order['payer_email'] ?? '', ENT_QUOTES);
$address_name = htmlspecialchars($order['first_name'] ?? '', ENT_QUOTES).' '.htmlspecialchars($order['last_name'] ?? '', ENT_QUOTES);
$address_street = htmlspecialchars($order['address_street'] ?? '', ENT_QUOTES);
$address_city = htmlspecialchars($order['address_city'] ?? '', ENT_QUOTES);
$address_state = htmlspecialchars($order['address_state'] ?? '', ENT_QUOTES);
$address_zip = htmlspecialchars($order['address_zip'] ?? '', ENT_QUOTES);
$address_country = htmlspecialchars($order['address_country'] ?? '', ENT_QUOTES);
$order_id = $order['id'];
$products = $order_items;
$shippingtotal = $order['shipping_amount'];
$total = $order['payment_amount'];
$taxtotal = $order['tax_amount'];
$order_created = $order['created'];
//Generate invoice
ob_start();
include dirname(__FILE__).'/custom/order-invoice-template.php';
$order_invoice_template = ob_get_clean();
return array($order_invoice_template,$customer_email,$order_id);
}
function freeShipment($price, $type){
include './custom/translations_'.strtoupper(language_code).'.php';
//Free delivery indicator
$delivery_status = ($price >= free_shipment_price) ? $free_delivery : $non_free_delivery.currency_code.free_shipment_price.',-';
$style = ($delivery_status == $free_delivery) ? 'style="color:green;font-weight: bold;"' : 'style="color:gray;font-weight: lighter;"';
echo '
<'.$type.' class="delivery">
<p '.$style.'> '.$delivery_status.'</p>
</'.$type.'>';
}
function consent()
{
include './custom/translations_'.strtoupper(language_code).'.php';
$age_consent = '
<div id="simple-cookie-consent">
<div class="cookie-consent-container">
<div class="cookie-consent-notice">
<h4>'.$age_consent_h4.'</h4>
<hr>
<p>'.$age_consent_text.'</p>
</div>
<div class="cookie-consent-selection">
<form action="'.htmlspecialchars($_SERVER["PHP_SELF"]).'" method="POST">
<input type="submit" name="age_consent_allow" value="'.$age_consent_btn_allow.'" class="cookie-consent-allow">
<input type="submit" name="age_consent_deny" value="'.$age_consent_btn_deny.'" class="cookie-consent-deny">
</form>
</div>
</div>
</div>
';
return $age_consent;
}
function banner()
{
include './custom/translations_'.strtoupper(language_code).'.php';
$banner = '
<div id="banner">
<div class="banner_container" style="background-image:url('.banner_background.');background-position: center center;">
<div class="banner_notice">
<h4>'.banner_wow.'</h4>
<hr>
<p>'.banner_text.'</p>
</div>
<div class="banner_selection">
<form action="'.htmlspecialchars($_SERVER["PHP_SELF"]).'" method="POST">
<input type="submit" name="banner_move" value="'.banner_btn_1.'" class="banner_allow">
<input type="submit" name="banner_stay" value="'.banner_btn_2.'" class="banner_deny">
</form>
</div>
</div>
</div>
';
return $banner;
}
function maintenanceMode()
{
include './custom/translations_'.strtoupper(language_code).'.php';
$maintenanceMode = '
<div id="simple-cookie-consent">
<div class="cookie-consent-container">
<div class="cookie-consent-notice">
<h4>'.$maintenanceMode_h4.'</h4>
<hr>
<p>'.$maintenanceMode_text.'</p>
</div>
<div class="cookie-consent-selection">
<form action="'.htmlspecialchars($_SERVER["PHP_SELF"]).'" method="POST">
<input type="submit" name="maintenanceMode" value="'.$maintenanceMode_btn.'" class="cookie-consent-allow">
</form>
</div>
</div>
</div>
';
return $maintenanceMode;
}
//++++++++++++++++++++++++++++++++++++++++
//HomePage Products
//++++++++++++++++++++++++++++++++++++++++
function getPictureID($pdo,$id,$config){
$stmt = $pdo->prepare('SELECT * FROM products_media where product_id = :product_id ORDER BY position ASC');
$stmt->bindValue(':product_id', $id, PDO::PARAM_INT);
$stmt->execute();
$product_media = $stmt->fetchAll(PDO::FETCH_ASSOC);
//Search for option_id
$option_profile = json_decode($config,true) ?? '';
if (!empty($option_profile) && $option_profile !=''){
foreach ($option_profile as $option){
if ($option['IMG_large_id'] == $product_media[0]['media_id']){
return $option['option_id'];
}
}
}
}
//++++++++++++++++++++++++++++++++++++++++
//HomePage Products
//++++++++++++++++++++++++++++++++++++++++
function highlightedProducts($pdo,$categoryID,$range){
include './custom/translations_'.strtoupper(language_code).'.php';
$stmt = $pdo->prepare('SELECT p.*, (SELECT m.full_path FROM products_media pm JOIN media m ON m.id = pm.media_id WHERE pm.product_id = p.id ORDER BY pm.position ASC LIMIT 1) AS img FROM products p JOIN products_categories pc ON pc.category_id = :category_id AND pc.product_id = p.id JOIN categories c ON c.id = pc.category_id WHERE p.status = 1');
$stmt->bindValue(':category_id', $categoryID, PDO::PARAM_INT);
$stmt->execute();
$products = $stmt->fetchAll(PDO::FETCH_ASSOC);
$view = '
<h2>'.$range.'<span class="limited">Limited edition</span></h2>
<div class="products">';
foreach($products as $product){
$view .= '
<div class="product">';
if (empty($product['product_config'])){
$view .= '<a href="'.url('index.php?page=product&id=' . ($product['url_slug'] ? ($product['url_slug'] ) : $product['id'])).'" id="'.$product['id'].'A" class="product">';
}
else{//ADD related optionID when configuration is found
$option_id = getPictureID($pdo,$product['id'],$product['product_config']);
$view .= '<a href="'.url('index.php?page=product&id=' . ($product['url_slug'] ? ($product['url_slug'].'/'.$option_id ) : $product['id'])).'" id="'.$product['id'].'A" class="product">';
}
if (!empty($product['img']) && file_exists($product['img'])){
if (empty($product['product_config'])){
$view .= '
<img src="'.$product['img'].'" width="" height="250" alt="'.$product['name'].'">';
$view .= '
</a>
<!-- Show small image below main image in case of not configured -->
<div class="" style="display:flex;justify-content: center">
<div>
<img class="img_config" src="'.base_url.$product['img'].'"/>
</div>
</div>';
} else {
$view .= '<img src="'.base_url.$product['img'].'" id="'.$product['id'].'" width="" height="250" alt="'.$product['name'].'">
</a>';
if (show_options_carrousel){
$view .= '<div class="" style="display:flex;justify-content: center">';
$option_profile = json_decode($product['product_config']);
foreach ($option_profile as $option){
//get all media
$stmt = $pdo->query('SELECT id, full_path FROM media');
$stmt->execute();
$media = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($media as $media_item){
if ($media_item['id'] == $option->IMG_small_id){
$IMG_small_id = $media_item['full_path'];
}
if ($media_item['id'] == $option->IMG_large_id){
$IMG_large_id = $media_item['full_path'];
}
}
$option_id = ($option->option_id != '') ? $option->option_id : '';
$view .= '
<div>
<img class="img_config" src="'.url($IMG_small_id).'" id="'.$option->IMG_small_id.'" onclick="update(\''.$product['id'].'\',\''.url($IMG_large_id).'\',\''.url('index.php?page=product&id=' . ($product['url_slug'] ? $product['url_slug'].'/'.$option_id : $product['id'].'/'.$option_id )).'\')" />
</div>
';
}
$view .= '</div>';
}
}
}
//Stock status
$stock_status = ($product['quantity'] != 0) ? $product_on_stock : $out_of_stock;
$style = ($stock_status == $product_on_stock) ? 'style="background-color: green;"' : 'style="background-color:gray;font-weight: lighter;"';
$view .=' <span class="stock">
<p '.$style.'> '.$stock_status.' </p>
</span>';
//Remove first characters from Productname
if (product_truncate_text != ''){
$productname = str_replace(product_truncate_text,'',$product['name']);
$productname = (product_truncate !=0)? substr($productname,product_truncate):$productname;
} else {
$productname = $product['name'];
}
//ADD related optionID when configuration is found
if (empty($product['product_config'])){
$option_id = '';
}else {
$option_id = '/'.getPictureID($pdo,$product['id'],$product['product_config']);
}
$view .= '
<a href="'.url('index.php?page=product&id=' . ($product['url_slug'] ? $product['url_slug'].$option_id : $product['id'])).'" id="'.$product['id'].'B" class="product">
<span class="productname">'.$productname.'</span>
<span class="productprice">'.currency_code.number_format($product['price'],2);
if ($product['rrp'] > 0) {
$view .= '<span class="productrrp">'.currency_code.number_format($product['rrp'],2).'</span>';
}
$view .= '
</span>
</a>
</div>';
}
$view .= '</div>';
return $view;
}
//---------------------------
//debuglog
//---------------------------
function debuglog($error){
$test = $error.PHP_EOL;
$filelocation = './log/log_'.date('m').'.txt';
error_log($test, 3, $filelocation);
}
?>

88
home.php Normal file
View File

@@ -0,0 +1,88 @@
<?php
// Prevent direct access to file
defined(security_key) or exit;
// Get the 4 most recent added products
$stmt = $pdo->prepare('SELECT p.*, (SELECT m.full_path FROM products_media pm JOIN media m ON m.id = pm.media_id WHERE pm.product_id = p.id ORDER BY pm.position ASC LIMIT 1) AS img FROM products p WHERE p.status = 1 ORDER BY p.date_added DESC LIMIT 4');
$stmt->execute();
$recently_added_products = $stmt->fetchAll(PDO::FETCH_ASSOC);
//LINK to products page:
$products_link = url(link_to_collection);
?>
<?=template_header_top($home_text)?>
<div class="featured" style="background-image:url(<?=featured_image?>);background-position: center center;">
<?=template_menu()?>
<h2><?=$h2_brand_name_1?></h2>
<p><?=$h2_brand_name_2?></p>
<a class="link-button action-btn" role="button" href="<?=$products_link?>" title="<?=$h2_brand_visit?>"><?=$h2_brand_visit?></a>
</div>
<div class="container highlight-section">
<h2 class="title-message" href="<?=$products_link?>">
<?=$h2_brand_wow?>
</h2>
<p class="paragraph neutral-paragraph-text" style="font-size: 30px;">
</p>
</div>
<?php
//SHOW OFFER
if(show_offer_home_page){
echo '
<div class="" style="text-align: center;">
<p class="p.paragraph.neutral-paragraph-text-1" style="font-family:\'gerb\';font-size: 15px;">'.show_offer_home_text.'</p>
</div>
';
}
?>
<section class="container neutral-cover-section-1" style="background-image:url(custom/assets/morval_checkout.jpg);background-size: cover;background-repeat: no-repeat;background-position: 50% 50%;">
<div class="recentlyadded">
<?php
$view = highlightedProducts($pdo,category_id_highlighted_products_1,highlighted_product_range_1);
echo $view;
?>
</div>
</section>
<section class="container neutral-cover-section-1">
<div class="recentlyadded">
<?php
$view = highlightedProducts($pdo,category_id_highlighted_products_2,highlighted_product_range_2);
echo $view;
?>
</div>
</section>
<section class="container neutral-features-section-4-1" style="background-image:url(custom/assets/picture2.jpg);background-size: cover;background-repeat: no-repeat;background-position: 50% 50%;">
<div class="container neutral-three-columns-1">
<div class="container neutral-feature-wrapper-1 neutral-left-align-items-1">
<div class="container container-feature-icon-wrapper-1"><span class="icon"></span>
<h4 class="neutral-feature-title-1 neutral-margin-left-1">Timeless</h4>
</div>
<p class="paragraph neutral-paragraph-text-1">
Morval Watches are unique, robust, stylish and timeless timepieces that will last for generations!
</p>
<a class="link-text neutral-text-link-1" href="<?=$products_link?>">shop now &gt;</a>
</div>
<div class="container neutral-feature-wrapper-1 neutral-left-align-items-1">
<div class="container container-feature-icon-wrapper-1"><span class="icon"></span>
<h4 class="neutral-feature-title-1 neutral-margin-left-1">Quality</h4>
</div>
<p class="paragraph neutral-paragraph-text-1">
Morval watches meet the highest quality requirements and can compete with the well-known Swiss brands. The parts are supplied by renowned manufacturers from Europe and beyond. A Morval contains a Swiss-made caliber (STP) that is known for its reliable quality.
</p>
<a class="link-text neutral-text-link-1" href="<?=$products_link?>">shop now &gt;</a>
</div>
<div class="container neutral-feature-wrapper-1 neutral-left-align-items-1">
<div class="container container-feature-icon-wrapper-1"><span class="icon"></span>
<h4 class="neutral-feature-title-1 neutral-margin-left-1">Price</h4>
</div>
<p class="paragraph neutral-paragraph-text-1">
Morval stands for an excellent price-quality ratio
</p>
<a class="link-text neutral-text-link-1" href="<?=$products_link?>">shop now &gt;</a>
</div>
</div>
</section>
</div>
<?=template_footer()?>

96
index.php Normal file
View File

@@ -0,0 +1,96 @@
<?php
define('MorvalWatches', true);
// 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, '/') . '/');
// If somehow the above URL fails to resolve the correct URL, you can simply comment out the below line and manually specifiy the URL to the system.
// define('base_url', 'http://yourdomain.com/shoppingcart/');
// Initialize a new session
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';
// Include translation file
include './custom/translations_'.strtoupper(language_code).'.php';
// Connect to MySQL database
$pdo = pdo_connect_mysql();
// Output error variable
$error = '';
//error reporting
if (debug){
ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
error_reporting(E_ALL);
}
//Age consent session
if (isset($_POST['age_consent_allow'])){$_SESSION["age_consent"] = 1;}
// Banner
if (isset($_POST['banner_stay'])){$_SESSION["banner"] = 1;}
if (isset($_POST['banner_move'])){
session_destroy();
header('location: '.banner_link.'');
die();
}
// Define all the routes for all pages
$url = routes([
'/' => 'home.php',
'/about' => 'about.php',
'/about_morval' => 'about_morval.php',
'/product/{id}' => 'product.php',
'/product/{id}/{option_id}' => 'product.php',
'/products' => 'products.php',
'/products/{category}' => 'products.php',
'/products/{category}/{sort}' => 'products.php',
'/products/{p}/{category}/{sort}' => 'products.php',
'/myaccount' => 'myaccount.php',
'/myaccount/{tab}' => 'myaccount.php',
'/download/{id}' => 'download.php',
'/cart' => 'cart.php',
'/cart/{remove}' => 'cart.php',
'/checkout' => 'checkout.php',
'/placeorder' => 'placeorder.php',
'/placeorder/{order_id}' => 'placeorder.php',
'/search/{query}' => 'search.php',
'/logout' => 'logout.php',
'/termsandconditions'=> 'termsandconditions.php',
'/termsandconditions/{download}'=> 'termsandconditions.php',
'/faq'=> 'faq.php',
'/privacy'=> 'privacy.php',
'/privacy/{download}'=> 'privacy.php',
'instructions-for-use' => 'faq.php'
]);
// Check if route exists
if ($url) {
include $url;
} else {
// 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'] : 'home';
// Include the requested page
include $page . '.php';
}
//debuglog
if (log_usage){
//GET REMOTE ADRESS
$user_1 = $_SERVER['REMOTE_ADDR'] ?? '<none>';
$user_2 = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? '<none>';
//GET PAGE DETAILS
$page = $_SERVER['REQUEST_URI'] ?? '<none>';
$date = date('Y-m-d H:i:s');
//CREATE LOG MESSAGE
$message = $date.';'.$page.';'.$user_1.';'.$user_2;
//UPDATE LOGFILE
debuglog($message);
}
?>

18
initialize.php Normal file
View File

@@ -0,0 +1,18 @@
<?php
/*
* Make sure to disable the display of errors in production code!
*/
ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
error_reporting(E_ALL);
// Include Mollie functions
require_once __DIR__ . "/lib/mollie/vendor/autoload.php";
/*
* Initialize the Mollie API library with your API key.
*
* See: https://www.mollie.com/dashboard/developers/api-keys
*/
$mollie = new \Mollie\Api\MollieApiClient();
$mollie->setApiKey(mollie_api_key);

Some files were not shown because too many files have changed in this diff Show More