Compare commits
1 Commits
master
...
18bf4e3ada
| Author | SHA1 | Date | |
|---|---|---|---|
| 18bf4e3ada |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +0,0 @@
|
||||
wp-pppr-salt.php
|
||||
167
admin.php
167
admin.php
@@ -1,167 +0,0 @@
|
||||
<?php
|
||||
|
||||
function init_postode_admin()
|
||||
{
|
||||
add_action("admin_menu", "local_postcodes_admin_menu", 99);
|
||||
add_action("admin_head", "local_postcodes_admin_css");
|
||||
add_action("admin_init", "local_postcodes_register_settings"); // Correct
|
||||
}
|
||||
|
||||
function local_postcodes_admin_menu()
|
||||
{
|
||||
add_submenu_page(
|
||||
"woocommerce", // Parent menu slug
|
||||
"Lokale Postcodes", // Page title
|
||||
"Lokale Postcodes", // Menu title
|
||||
"manage_options", // Capability
|
||||
"local-postcodes", // Menu slug
|
||||
"generate_admin_page", // Callback function
|
||||
"dashicons-buddicons-pm", // Optional icon URL or dashicon
|
||||
);
|
||||
}
|
||||
|
||||
function local_postcodes_register_settings()
|
||||
{
|
||||
register_setting(
|
||||
"my_plugin_options",
|
||||
"local_postcodes_values",
|
||||
"local_postcodes_input_validate",
|
||||
);
|
||||
}
|
||||
|
||||
function generate_admin_page()
|
||||
{
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1><span class="dashicons dashicons-admin-home"></span> Lokale Postcodereeksen</h1>
|
||||
<?php settings_errors("local_postcodes_error_messages"); ?>
|
||||
<div class="info">
|
||||
<p class="info">Vul hier de lokale postcodereeksen in.</p>
|
||||
<ul>
|
||||
<li>Het formaat voor een reeks is #### - #### (voorbeeld: 5000 - 5199).</li>
|
||||
<li>Plaats één reeks op één regel.</li>
|
||||
<li>Een reeks van 1 is geldig. (voorbeeld: 5000 - 5000).</li>
|
||||
<li>Om een reeks te verwijderen. Wis de reeks en sla vervolgens de wijzigen op.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<form id="postcodes_form" method="post" action="options.php">
|
||||
<?php
|
||||
settings_fields("my_plugin_options");
|
||||
do_settings_sections("my_plugin_options");
|
||||
?>
|
||||
<textarea id="local_postcodes_values" name="local_postcodes_values"><?php echo esc_textarea(
|
||||
get_option("local_postcodes_values"),
|
||||
); ?></textarea>
|
||||
<?php submit_button(); ?>
|
||||
</form>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
function local_postcodes_admin_css()
|
||||
{
|
||||
?>
|
||||
<style>
|
||||
h1 .dashicons{
|
||||
font-size: 25px;
|
||||
|
||||
}
|
||||
|
||||
.info {
|
||||
font-size: 1.1rem;
|
||||
max-width: 60ch;
|
||||
|
||||
ul {
|
||||
list-style: square inside;
|
||||
}
|
||||
}
|
||||
|
||||
#postcodes_form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: 60ch;
|
||||
align-items: flex-end;
|
||||
font-size: 1.1rem;
|
||||
|
||||
#local_postcodes_values{
|
||||
width: 100% !important;
|
||||
height: 50vh !important;
|
||||
font-size: 1.1rem;
|
||||
font-family: "Consolas", "Monaco", "Courier New", monospace;
|
||||
}
|
||||
|
||||
|
||||
.submit{
|
||||
margin-top: .2rem;
|
||||
}
|
||||
}
|
||||
</style>';
|
||||
<?php
|
||||
}
|
||||
|
||||
function local_postcodes_input_validate($input)
|
||||
{
|
||||
$valid_input = "";
|
||||
$lines = preg_split("/\R/", $input, -1, PREG_SPLIT_NO_EMPTY);
|
||||
$pattern = '/^\d{4}\s*\-\s*\d{4}$/';
|
||||
$pc_arr = [];
|
||||
|
||||
foreach ($lines as $line) {
|
||||
$line = trim($line);
|
||||
if (preg_match($pattern, $line)) {
|
||||
$parts = explode("-", $line);
|
||||
$first = (int) $parts[0];
|
||||
$second = (int) $parts[1];
|
||||
|
||||
if ($first <= $second) {
|
||||
$pc_arr[] = [$first, $second];
|
||||
} else {
|
||||
$pc_arr[] = [$second, $first];
|
||||
}
|
||||
} else {
|
||||
add_settings_error(
|
||||
"local_postcodes_error_messages",
|
||||
"invalid_postcode_" . md5($line),
|
||||
'❌️ Ongeldige regel: "' .
|
||||
esc_html($line) .
|
||||
'". Gebruik het formaat #### - ####.(bijvoorbeeld: 5000 - 5199)',
|
||||
"error",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
usort($pc_arr, function ($a, $b) {
|
||||
return $a[0] <=> $b[0];
|
||||
});
|
||||
|
||||
$old_pc_arr_len = count($pc_arr);
|
||||
$pc_arr = array_map(
|
||||
"unserialize",
|
||||
array_unique(array_map("serialize", $pc_arr)),
|
||||
);
|
||||
$new_pc_arr_len = count($pc_arr);
|
||||
|
||||
if ($new_pc_arr_len < $old_pc_arr_len) {
|
||||
add_settings_error(
|
||||
"local_postcodes_error_messages",
|
||||
"Duplicaten",
|
||||
// @formatter:off
|
||||
"⚠️ " .
|
||||
$old_pc_arr_len -
|
||||
$new_pc_arr_len .
|
||||
" " .
|
||||
($old_pc_arr_len - $new_pc_arr_len > 1
|
||||
? "duplicaten"
|
||||
: "duplicaat") .
|
||||
" verwijderd.",
|
||||
// @formatter:on
|
||||
"warning",
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($pc_arr as $range) {
|
||||
$valid_input .= $range[0] . " - " . $range[1] . "\n";
|
||||
}
|
||||
|
||||
return $valid_input;
|
||||
}
|
||||
@@ -1,127 +1,70 @@
|
||||
.postcode_modal {
|
||||
border-radius: 3px;
|
||||
border-radius: 20px;
|
||||
box-shadow(10px);
|
||||
border: none;
|
||||
max-width: 65ch;
|
||||
max-width: 80ch;
|
||||
box-shadow: 0 0 20pc black;
|
||||
padding: 2rem;
|
||||
padding-bottom: 1rem;
|
||||
opacity: 0;
|
||||
transition:
|
||||
opacity 0.3s ease,
|
||||
display 0.3s allow-discrete;
|
||||
transition: opacity 0.3s ease, display 0.3s allow-discrete;
|
||||
|
||||
&[open] {
|
||||
opacity: 1;
|
||||
transition-behavior: allow-discrete;
|
||||
transition:
|
||||
opacity 0.3s ease,
|
||||
display 0.3s allow-discrete;
|
||||
@starting-style {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 1rem;
|
||||
opacity: 1;
|
||||
transition-behavior: allow-discrete;
|
||||
transition: opacity 0.3s ease, display 0.3s allow-discrete;
|
||||
}
|
||||
|
||||
&:not([open]) {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
form {
|
||||
margin-top: 1rem;
|
||||
h2 {
|
||||
font-size: 1.5rem;
|
||||
margin: .6rem;
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
padding: 0.3rem;
|
||||
font-size: 1.2rem;
|
||||
caret-color: var(--wp--preset--color--vivid-red);
|
||||
background: hsl(0 1 80);
|
||||
border: none;
|
||||
text-transform: uppercase;
|
||||
|
||||
&:focus,
|
||||
&:valid,
|
||||
&:invalid,
|
||||
&:autofill,
|
||||
&:autofill:focus {
|
||||
background: hsl(0 1 80) !important;
|
||||
}
|
||||
padding: .3rem;
|
||||
font-size: 1.4rem;
|
||||
caret-color: hsl(344 98 40);
|
||||
}
|
||||
|
||||
#postcode_modal_submit {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr;
|
||||
place-items: center;
|
||||
font-size: 1.2rem;
|
||||
font-weight: 700;
|
||||
button {
|
||||
border: none;
|
||||
font-size: 1.4rem;
|
||||
color: hsl(233 100 100);
|
||||
padding: 0.4rem 1rem;
|
||||
background: var(--wp--preset--color--vivid-red);
|
||||
border: 1px var(--wp--preset--color--vivid-red) solid;
|
||||
transition: 0.2s all linear;
|
||||
padding: 0.2em 2.5em;
|
||||
&:hover {
|
||||
color: var(--wp--preset--color--vivid-red);
|
||||
padding: .4rem 1rem;
|
||||
border-radius: 5px;
|
||||
background: hsl(344 98 40);
|
||||
border: 1px hsl(344 98 40) solid;
|
||||
margin: 0 0 0 .3rem;
|
||||
transition: .2s all linear;
|
||||
|
||||
&:hover{
|
||||
color: hsl(344 98 40 );
|
||||
background: hsl(344 98 100);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&:hover > .btn_loader > svg > circle {
|
||||
fill: var(--wp--preset--color--vivid-red);
|
||||
}
|
||||
|
||||
.btn_loader,
|
||||
.btn_text {
|
||||
transition: 0.2s opacity linear;
|
||||
grid-row: 1 / -1;
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
}
|
||||
|
||||
.form_fields {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
|
||||
&>{
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
&::backdrop {
|
||||
backdrop-filter: blur(2px);
|
||||
background: hsl(0 1 65 / 0.6);
|
||||
backdrop-filter: blur(1px);
|
||||
background-color: hsl(40deg 100 0 /0.4);
|
||||
background: radial-gradient(circle,rgba(33, 33, 33, 0.68) 0%, rgba(15, 15, 15, 0.8) 23%, rgba(0, 0, 0, 0.87) 100%);
|
||||
}
|
||||
|
||||
.error_message_modal_postcode {
|
||||
min-height: 1lh;
|
||||
color: var(--wp--preset--color--vivid-red);
|
||||
} /* HTML: <div class="loader"></div> */
|
||||
}
|
||||
|
||||
/* HTML: <div class="loader"></div> */
|
||||
.btn_loader {
|
||||
opacity: 0;
|
||||
svg {
|
||||
height: 0.4rem;
|
||||
}
|
||||
circle {
|
||||
fill: white;
|
||||
transition: 0.2s fill linear;
|
||||
animation: loader-pulse 1.5s ease-in-out infinite;
|
||||
animation-delay: calc((var(--i, 0)) * 0.2s);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes loader-pulse {
|
||||
0%,
|
||||
100% {
|
||||
opacity: 0.3; /* Dim state */
|
||||
}
|
||||
50% {
|
||||
opacity: 1; /* Bright state */
|
||||
}
|
||||
color: hsl(344 98 40 );
|
||||
}/* HTML: <div class="loader"></div> */
|
||||
}
|
||||
|
||||
175
pppr_cookie.php
175
pppr_cookie.php
@@ -1,175 +0,0 @@
|
||||
<?php
|
||||
|
||||
function create_pppr_salt() {
|
||||
// 1. Check permissions first (Optional but recommended)
|
||||
if ( ! wp_is_writable( plugin_dir_path( __FILE__ ) ) ) {
|
||||
// Log error or handle failure
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. Define path INSIDE the plugin folder
|
||||
$file_path = plugin_dir_path( __FILE__ ) . 'wp-pppr-salt.php';
|
||||
|
||||
// 3. Get the salt
|
||||
$current_salt = wp_salt( 'auth' );
|
||||
|
||||
if ( empty( $current_salt ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 4. Prepare content
|
||||
$file_content = "<?php\n";
|
||||
$file_content .= "// Auto-generated salt constant\n";
|
||||
$file_content .= "// Exported salt from " . date( 'Y-m-d H:i:s' ) . "\n";
|
||||
$file_content .= "define( 'WP_PPPR_SALT', '{$current_salt}' );\n";
|
||||
|
||||
// 5. Write the file
|
||||
$bytes_written = file_put_contents( $file_path, $file_content, LOCK_EX );
|
||||
|
||||
// 6. Verify success
|
||||
if ( $bytes_written === false ) {
|
||||
// Handle error (e.g., log it)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function set_pppr_cookie($is_local, $street_name, $house_number, $postcode, $city) {
|
||||
$data_string = (int)$is_local . "|" . $street_name . "|" . $house_number . "|" . $postcode . "|" . $city;
|
||||
$signature = hash_hmac('sha256', $data_string, WP_PPPR_SALT);
|
||||
$cookie_value = $data_string . "|" . $signature;
|
||||
|
||||
$cookie_domain = $_SERVER['SERVER_NAME'];
|
||||
$is_secure = is_ssl();
|
||||
|
||||
setcookie("PPPR", $cookie_value, [
|
||||
'expires' => time() + 7200,
|
||||
'path' => '/',
|
||||
'domain' => $cookie_domain,
|
||||
'secure' => $is_secure,
|
||||
'httponly' => true, // Prevent JavaScript access
|
||||
'samesite' => 'Lax' // Protect against CSRF
|
||||
]);
|
||||
}
|
||||
|
||||
function unset_pppr_cookie( $path = '/') {
|
||||
unset($_COOKIE["PPPR"]);
|
||||
|
||||
$domain = $_SERVER['SERVER_NAME'];
|
||||
$is_secure = is_ssl();
|
||||
|
||||
// 3. Send the delete command with ALL matching parameters
|
||||
setcookie(
|
||||
"PPPR",
|
||||
'',
|
||||
time() - 3600,
|
||||
'/',
|
||||
$domain,
|
||||
$is_secure, // Critical: Must match the 'secure' flag used when setting
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
function update_expire_pppr_cookie() {
|
||||
if (!isset($_COOKIE['PPPR'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$cookie_value = $_COOKIE['PPPR'];
|
||||
$cookie_domain = $_SERVER['SERVER_NAME'];
|
||||
$is_secure = is_ssl();
|
||||
|
||||
setcookie("PPPR", $cookie_value, [
|
||||
'expires' => time() + 7200,
|
||||
'path' => '/',
|
||||
'domain' => $cookie_domain,
|
||||
'secure' => $is_secure,
|
||||
'httponly' => true, // Prevent JavaScript access
|
||||
'samesite' => 'Lax' // Protect against CSRF
|
||||
]);
|
||||
}
|
||||
|
||||
function verify_pppr_cookie_string() {
|
||||
if (!isset($_COOKIE['PPPR'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$cookie_value = $_COOKIE['PPPR'];
|
||||
|
||||
$last_delimiter = strrpos($cookie_value, '|');
|
||||
if ($last_delimiter === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$data_part = substr($cookie_value, 0, $last_delimiter);
|
||||
$hash_part = substr($cookie_value, $last_delimiter + 1);
|
||||
|
||||
// Check if salt is defined
|
||||
if (!defined('WP_PPPR_SALT')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$expected_hash = hash_hmac('sha256', $data_part, WP_PPPR_SALT);
|
||||
|
||||
return hash_equals($expected_hash, $hash_part);
|
||||
}
|
||||
|
||||
function get_PPPR_data() {
|
||||
// 1. Check if cookie exists
|
||||
if (!isset($_COOKIE['PPPR'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$cookie_value = $_COOKIE['PPPR'];
|
||||
|
||||
// 2. Split safely using the LAST delimiter (handles pipes in street names)
|
||||
$last_delimiter = strrpos($cookie_value, '|');
|
||||
if ($last_delimiter === false) {
|
||||
return false; // Malformed cookie
|
||||
}
|
||||
|
||||
$data_part = substr($cookie_value, 0, $last_delimiter);
|
||||
$provided_hash = substr($cookie_value, $last_delimiter + 1);
|
||||
|
||||
// 3. CRITICAL: Verify the signature before trusting ANY data
|
||||
// Ensure WP_PPPR_SALT is defined and matches the setter exactly
|
||||
if (!defined('WP_PPPR_SALT')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$expected_hash = hash_hmac('sha256', $data_part, WP_PPPR_SALT);
|
||||
|
||||
if (!hash_equals($expected_hash, $provided_hash)) {
|
||||
return false; // Tampered or invalid cookie
|
||||
}
|
||||
|
||||
// 4. Only now is it safe to explode the verified data
|
||||
$fields = explode('|', $data_part);
|
||||
|
||||
// Ensure we have enough fields
|
||||
if (count($fields) < 5) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return [
|
||||
'is_local' => (bool)$fields[0],
|
||||
'street' => $fields[1],
|
||||
'house_number' => $fields[2],
|
||||
'postcode' => $fields[3],
|
||||
'city' => $fields[4],
|
||||
];
|
||||
}
|
||||
|
||||
function pppr_is_local() {
|
||||
// Check if the cookie exists and is not empty
|
||||
if (isset($_COOKIE['PPPR']) && !empty($_COOKIE['PPPR'])) {
|
||||
// Ensure it's treated as a string before accessing the first character
|
||||
$cookie_value = (string) $_COOKIE['PPPR'];
|
||||
$first_char = $cookie_value[0] ?? '';
|
||||
return $first_char === '1';
|
||||
}
|
||||
|
||||
// Return false if cookie is missing or empty
|
||||
return false;
|
||||
}
|
||||
@@ -1,70 +1,77 @@
|
||||
<?php
|
||||
|
||||
session_start();
|
||||
require_once "session_dialog.php";
|
||||
/*
|
||||
* Plugin Name: Prijs per poscodeereeks.
|
||||
* Description: Producten worden gefiltered aan de hand van opgegegeven postcodereeksen.Gebruikers met een postcode die in een opgegegeven postcodereeks valt zal een lokale prijs zien. Waar elke gebruiker met een postcode die buiten eem opgegeven reeks valt zal de reguliere prijs te zien krijgen.
|
||||
* Plugin Name: prijzen per poscode range
|
||||
* Description: posctcodes in de 5000-5800 range krijgen een lokaal tarief aangeboden.
|
||||
* Author: Remo Zaros
|
||||
* Version: 0.9.090
|
||||
* Version: 0.9.1
|
||||
* Text Domeain: prijs-per-postcode
|
||||
*/
|
||||
|
||||
require_once plugin_dir_path(__FILE__) . "session_dialog.php";
|
||||
require_once plugin_dir_path(__FILE__) . "admin.php";
|
||||
require_once plugin_dir_path(__FILE__) . "pppr_cookie.php";
|
||||
|
||||
|
||||
if (!defined("ABSPATH")) {
|
||||
echo "big bag of potatoes";
|
||||
exit();
|
||||
}
|
||||
|
||||
/*
|
||||
* Create salt
|
||||
*/
|
||||
register_activation_hook( __FILE__, 'create_pppr_salt' );
|
||||
if ( ! defined( 'WP_PPPR_SALT' ) ) {
|
||||
$salt_file = __DIR__ . '/wp-pppr-salt.php';
|
||||
if ( file_exists( $salt_file ) ) {
|
||||
require_once $salt_file;
|
||||
}else {
|
||||
define("WP_PPPR_SALT", wp_salt( 'auth' ));
|
||||
}
|
||||
}
|
||||
class PrijsPerPostcode
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
add_action("init", [$this, "init"]);
|
||||
add_action("rest_api_init", "register_modal_api");
|
||||
}
|
||||
|
||||
public function init()
|
||||
{
|
||||
$uri = $_SERVER["REQUEST_URI"];
|
||||
init_postcode_handlers($uri);
|
||||
add_filter("woocommerce_sale_flash", "__return_null");
|
||||
|
||||
add_filter("woocommerce_sale_flash", "__return_null");
|
||||
add_filter('woocommerce_show_variation_price', '__return_true');
|
||||
//add_action("template_redirect", [$this, "redirect_if_missing_tag"]);
|
||||
add_action(
|
||||
"woocommerce_variation_options_pricing",
|
||||
[$this, "add_local_price_field"],
|
||||
10,
|
||||
3,
|
||||
);
|
||||
add_action(
|
||||
"woocommerce_save_product_variation",
|
||||
[$this, "save_local_price_field"],
|
||||
10,
|
||||
2,
|
||||
);
|
||||
add_action("woocommerce_before_calculate_totals", [
|
||||
$this,
|
||||
"use_local_price_if_local_postcode",
|
||||
]);
|
||||
|
||||
add_filter(
|
||||
"woocommerce_get_price_html",
|
||||
[$this, "display_local_price_on_product"],
|
||||
10,
|
||||
2,
|
||||
);
|
||||
add_action("template_redirect", [
|
||||
$this,
|
||||
"controleer_postcode_op_woocommerce_paginas",
|
||||
]);
|
||||
add_filter(
|
||||
"woocommerce_variation_is_visible",
|
||||
[$this, "filter_variation_by_local_price"],
|
||||
10,
|
||||
4,
|
||||
);
|
||||
add_filter(
|
||||
"gettext",
|
||||
[$this, "change_variation_regular_price_label"],
|
||||
99,
|
||||
3,
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Init the plugin
|
||||
*/
|
||||
add_action("init", function() {
|
||||
$uri = $_SERVER["REQUEST_URI"];
|
||||
init_postcode_handlers($uri);
|
||||
// set the admin page
|
||||
init_postode_admin();
|
||||
}, 1
|
||||
);
|
||||
|
||||
|
||||
/*
|
||||
* Set endpoint (postcode-modal/v1/submit) for the modal form.
|
||||
*/
|
||||
add_action("rest_api_init", function () {
|
||||
register_rest_route( 'postcode-modal/v1', '/submit', array(
|
||||
'methods' => 'POST',
|
||||
'callback' => 'handle_postcode_modal',
|
||||
'permission_callback' => '__return_true',
|
||||
) );
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
* Add local orice field to variation product.
|
||||
*/
|
||||
add_action("woocommerce_variation_options_pricing",
|
||||
function($loop, $variation_data, $variation)
|
||||
public function add_local_price_field($loop, $variation_data, $variation)
|
||||
{
|
||||
woocommerce_wp_text_input([
|
||||
"id" => "_local_price[" . $loop . "]",
|
||||
@@ -77,15 +84,9 @@ add_action("woocommerce_variation_options_pricing",
|
||||
"data_type" => "price",
|
||||
"wrapper_class" => "form-row form-row-first", // Left half
|
||||
]);
|
||||
}, 10, 3,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Save the local price on product save in the admin
|
||||
*/
|
||||
add_action("woocommerce_save_product_variation",
|
||||
function ($variation_id, $i)
|
||||
public function save_local_price_field($variation_id, $i)
|
||||
{
|
||||
$local_price = $_POST["_local_price"][$i];
|
||||
if (isset($local_price)) {
|
||||
@@ -95,36 +96,79 @@ add_action("woocommerce_save_product_variation",
|
||||
wc_clean($local_price),
|
||||
);
|
||||
}
|
||||
}, 10, 2,
|
||||
);
|
||||
}
|
||||
|
||||
public function use_local_price_if_local_postcode($cart)
|
||||
{
|
||||
if (is_admin() && !defined("DOING_AJAX")) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Replace "Reguliere prijs" with "Prijs overige regios"
|
||||
* in variation prodict admin
|
||||
*/
|
||||
add_filter( "gettext", function( $translated_text, $text, $domain,) {
|
||||
if (
|
||||
"woocommerce" === $domain &&
|
||||
is_admin() &&
|
||||
isset($_REQUEST["action"]) &&
|
||||
"woocommerce_load_variations" === $_REQUEST["action"]
|
||||
) {
|
||||
if ($translated_text === "Reguliere prijs (%s)") {
|
||||
$translated_text = "Prijs overige regios (%s)";
|
||||
foreach ($cart->get_cart() as $cart_item) {
|
||||
$product = $cart_item["data"];
|
||||
$variation_id = $product->is_type("variation")
|
||||
? $product->get_id()
|
||||
: 0;
|
||||
|
||||
if (
|
||||
$variation_id &&
|
||||
isset($_SESSION["postcode_is_local"]) &&
|
||||
$_SESSION["postcode_is_local"]
|
||||
) {
|
||||
$local_price = get_post_meta(
|
||||
$variation_id,
|
||||
"_local_price",
|
||||
true,
|
||||
);
|
||||
if ($local_price) {
|
||||
$product->set_price($local_price);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $translated_text;
|
||||
}, 99, 3,
|
||||
);
|
||||
}
|
||||
|
||||
public function controleer_postcode_op_woocommerce_paginas()
|
||||
{
|
||||
if (is_admin() || defined("DOING_AJAX")) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Filter out variations whwre the price is
|
||||
* either 0 or '' in the dropdown menu product page
|
||||
*/
|
||||
add_filter("woocommerce_variation_is_visible",
|
||||
function (
|
||||
if (
|
||||
(is_product() ||
|
||||
is_product_category() ||
|
||||
is_product_tag() ||
|
||||
is_cart() ||
|
||||
is_checkout() ||
|
||||
is_account_page()) &&
|
||||
!is_shop()
|
||||
) {
|
||||
if (!isset($_SESSION["postcode_is_local"])) {
|
||||
wp_redirect(home_url("/winkel/"));
|
||||
exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function display_local_price_on_product($price_html, $product)
|
||||
{
|
||||
if (
|
||||
$product->is_type("variation") &&
|
||||
isset($_SESSION["postcode_is_local"]) &&
|
||||
$_SESSION["postcode_is_local"] === true
|
||||
) {
|
||||
$local_price = get_post_meta(
|
||||
$product->get_id(),
|
||||
"_local_price",
|
||||
true,
|
||||
);
|
||||
if ($local_price !== "") {
|
||||
return wc_price($local_price);
|
||||
}
|
||||
}
|
||||
return $price_html;
|
||||
}
|
||||
|
||||
function filter_variation_by_local_price(
|
||||
$visible,
|
||||
$variation_id,
|
||||
$parent_id,
|
||||
@@ -139,7 +183,10 @@ add_filter("woocommerce_variation_is_visible",
|
||||
return false;
|
||||
}
|
||||
|
||||
$price = pppr_is_local()
|
||||
$is_local = isset($_SESSION["postcode_is_local"])
|
||||
? $_SESSION["postcode_is_local"]
|
||||
: false;
|
||||
$price = $is_local
|
||||
? $variation->get_meta("_local_price", true)
|
||||
: $variation->get_regular_price();
|
||||
|
||||
@@ -148,145 +195,25 @@ add_filter("woocommerce_variation_is_visible",
|
||||
}
|
||||
|
||||
return $visible;
|
||||
}, 10, 4,
|
||||
);
|
||||
|
||||
|
||||
/*
|
||||
* Dynamically recalculates the cart total by overriding the standard price
|
||||
* with a region-specific _local_price for variations when
|
||||
* the user's location matches the PPPR cookie criteria
|
||||
*/
|
||||
add_action("woocommerce_before_calculate_totals", function($cart)
|
||||
{
|
||||
if (is_admin() && !defined("DOING_AJAX")) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($cart->get_cart() as $cart_item)
|
||||
{
|
||||
$product = $cart_item["data"];
|
||||
$variation_id = $product->is_type("variation")
|
||||
? $product->get_id()
|
||||
: 0;
|
||||
|
||||
if ( $variation_id && isset($_COOKIE["PPPR"]) && pppr_is_local() )
|
||||
{
|
||||
$local_price = get_post_meta(
|
||||
$variation_id,
|
||||
"_local_price",
|
||||
true,
|
||||
);
|
||||
|
||||
if ($local_price)
|
||||
{
|
||||
$product->set_price($local_price);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
/*
|
||||
* Filter to display the regular or local price on teh product page.
|
||||
*/
|
||||
add_filter( "woocommerce_get_price_html",
|
||||
function($price_html, $product)
|
||||
{
|
||||
public function change_variation_regular_price_label(
|
||||
$translated_text,
|
||||
$text,
|
||||
$domain,
|
||||
) {
|
||||
if (
|
||||
$product->is_type("variation") &&
|
||||
isset($_COOKIE["PPPR"]) &&
|
||||
pppr_is_local()
|
||||
"woocommerce" === $domain &&
|
||||
is_admin() &&
|
||||
isset($_REQUEST["action"]) &&
|
||||
"woocommerce_load_variations" === $_REQUEST["action"]
|
||||
) {
|
||||
$local_price = get_post_meta(
|
||||
$product->get_id(),
|
||||
"_local_price",
|
||||
true,
|
||||
);
|
||||
if ($local_price !== "") {
|
||||
return wc_price($local_price);
|
||||
}
|
||||
}
|
||||
return $price_html;
|
||||
}, 10, 2,
|
||||
);
|
||||
|
||||
|
||||
/*
|
||||
* Enforces region-locking by clearing the cart and redirecting users to
|
||||
* the shop page if their location cookie is invalid or expired when
|
||||
* accessing key WooCommerce pages.
|
||||
*/
|
||||
add_action("template_redirect",
|
||||
function ()
|
||||
{
|
||||
if (is_admin() || defined("DOING_AJAX")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
(is_product() ||
|
||||
is_product_category() ||
|
||||
is_product_tag() ||
|
||||
is_cart() ||
|
||||
is_checkout() ||
|
||||
is_account_page()) &&
|
||||
!is_shop()
|
||||
) {
|
||||
if (!verify_pppr_cookie_string()) {
|
||||
if (!WC()->cart->is_empty()) {
|
||||
WC()->cart->empty_cart();
|
||||
}
|
||||
wp_safe_redirect(get_shop_url());
|
||||
exit();
|
||||
}else{
|
||||
update_expire_pppr_cookie();
|
||||
if ($translated_text === "Reguliere prijs (%s)") {
|
||||
$translated_text = "Prijs overige regios (%s)";
|
||||
}
|
||||
}
|
||||
return $translated_text;
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
/*
|
||||
* Chack or postcode is still valid when pressing the buy button
|
||||
*/
|
||||
add_action( 'woocommerce_after_checkout_validation', function( $data, $errors ) {
|
||||
// 1. Controleer of de cookie bestaat en geldig is
|
||||
if ( ! isset( $_COOKIE['PPPR'] ) || ! verify_pppr_cookie_string() ) {
|
||||
$errors->add(
|
||||
'pppr_cookie_invalid',
|
||||
__( '<strong>Fout:</strong> Sessie verlopen. Vernieuw de pagina en vul je postcode opnieuw in.', 'woocommerce' )
|
||||
);
|
||||
return; // Stop hier, geen zin om verder te checken
|
||||
}
|
||||
|
||||
$pppr_data = get_PPPR_data();
|
||||
|
||||
if ( ! $pppr_data || empty( $pppr_data['postcode'] ) ) {
|
||||
$errors->add(
|
||||
'pppr_data_missing',
|
||||
__( '<strong>Fout:</strong> Kon postcode niet laden.', 'woocommerce' )
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. Normalizeer postcodes (gebruik de juiste variabelen!)
|
||||
$cookie_pc = strtoupper( str_replace( ' ', '', $pppr_data['postcode'] ) );
|
||||
$shipping_pc = strtoupper( str_replace( ' ', '', $data['shipping_postcode'] ) );
|
||||
|
||||
// 4. Vergelijk
|
||||
if ( $cookie_pc !== $shipping_pc ) {
|
||||
wc_add_notice(
|
||||
__( '<strong>Fout:</strong> Afleverpostcode komt niet overeen met de eerder ingevulde postcode.', 'woocommerce' ),
|
||||
'error'
|
||||
);
|
||||
}
|
||||
}, 10, 2 );
|
||||
|
||||
|
||||
function get_shop_url () {
|
||||
return (function_exists('wcml_get_store_url'))
|
||||
? wcml_get_store_url() // WPML WooCommerce Multilingual
|
||||
: get_permalink(wc_get_page_id('shop')); // Fallback
|
||||
}
|
||||
|
||||
new PrijsPerPostcode();
|
||||
|
||||
5
read.md
5
read.md
@@ -5,3 +5,8 @@ Deze plugin laat verschillende producten zien in Woocommerce naar aanleiding van
|
||||
Een postcode in de 5000/5800 range laat producten zien met de tag "lokaal". Als een postcode buiten deze range ligt laat Woocommerce producten zien met de tag "algemeen". Een product moet de tag "lokaal" of "algemeen" hebben om in de winkel te verschijnen.
|
||||
|
||||
Bij het opgeven van de postcode en huisnummer wordt de straatnaam en woonplaats opgezocht. Deze informatie wordt ingevuld tijdens het checkout process.
|
||||
|
||||
- [ ] code warning
|
||||
- [ ] shop filter
|
||||
- [ ] range postcodes
|
||||
= [ ] pop up stylen
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
<?php
|
||||
|
||||
session_start();
|
||||
function init_postcode_handlers($uri)
|
||||
{
|
||||
if (strpos($uri, "/winkel/") !== false) {
|
||||
add_action("wp_enqueue_scripts", "modal_styles");
|
||||
add_action("wp_footer", "send_postcode_data");
|
||||
if (!verify_pppr_cookie_string()) {
|
||||
if (!is_admin()) {
|
||||
WC()->cart->empty_cart();
|
||||
if (strpos($uri, "/winkel/") !== false) {
|
||||
add_action("wp_enqueue_scripts", "modal_styles");
|
||||
add_action("wp_footer", "send_postcode_data");
|
||||
if (!has_postcode()) {
|
||||
if (!is_admin()) {
|
||||
WC()->cart->empty_cart();
|
||||
}
|
||||
add_action("wp_footer", "show_modal");
|
||||
render_dialog_html();
|
||||
@@ -21,166 +22,124 @@ function init_postcode_handlers($uri)
|
||||
|
||||
function modal_styles()
|
||||
{
|
||||
wp_enqueue_style(
|
||||
"prijs-per-postcode",
|
||||
plugins_url("/assets/postcode_modal.css", __FILE__),
|
||||
[], // Voeg een lege dependencies array toe (verplicht)
|
||||
filemtime(plugin_dir_path(__FILE__) . "assets/postcode_modal.css"), // Voeg versie toe
|
||||
wp_enqueue_style(
|
||||
"prijs-per-postcode",
|
||||
plugins_url("/assets/postcode_modal.css", __FILE__),
|
||||
);
|
||||
}
|
||||
|
||||
function show_modal()
|
||||
{
|
||||
echo <<<'HTML'
|
||||
<script id="postcode_modal_open">
|
||||
const postcodeModal = document.querySelector("#postcode_modal");
|
||||
postcodeModal.showModal();
|
||||
</script>
|
||||
HTML;
|
||||
?>
|
||||
<script id="postcode_modal">
|
||||
const postcodeModal = document.querySelector("#postcode_modal");
|
||||
postcodeModal.showModal();
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
|
||||
function send_postcode_data()
|
||||
{
|
||||
$URL = get_rest_url(null, "postcode-modal/v1/submit");
|
||||
$nonce = wp_create_nonce("wp_rest");
|
||||
|
||||
echo <<<HTML
|
||||
?>
|
||||
<script type="module">
|
||||
const postcodeModal = document.querySelector("#postcode_modal");
|
||||
const modalForm = document.querySelector("#postcode_modal_form");
|
||||
const sndBtn = document.querySelector("#postcode_modal_submit");
|
||||
const sndBtnLdr = sndBtn.querySelector(".btn_loader");
|
||||
const sndBtnTxt = sndBtn.querySelector(".btn_text");
|
||||
const errorMsg = document.querySelector("#error_message_modal_postcode");
|
||||
|
||||
// 1. Validatie bij blur (client-side checks)
|
||||
modalForm.querySelectorAll("input").forEach(input => {
|
||||
input.addEventListener("blur", ev => {
|
||||
errorMsg.innerHTML = "";
|
||||
if (!ev.target.validity.valid && ev.target.value !== "") {
|
||||
errorMsg.innerHTML = ev.target.title;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 2. Submit Handler
|
||||
modalForm.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
errorMsg.innerHTML = "";
|
||||
|
||||
// UI: Laadstatus
|
||||
sndBtn.disabled = true;
|
||||
sndBtnLdr.style.opacity = "1";
|
||||
sndBtnTxt.style.opacity = "0";
|
||||
|
||||
const formData = new FormData(e.target);
|
||||
const payload = Object.fromEntries(formData.entries());
|
||||
const data = Object.fromEntries(formData.entries());
|
||||
const json = JSON.stringify(data);
|
||||
|
||||
try {
|
||||
const resp = await fetch('{$URL}', {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
const resp = await fetch('<?php echo get_rest_url(
|
||||
null,
|
||||
"postcode-modal/v1/submit",
|
||||
); ?>', {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-WP-Nonce': '<?php echo wp_create_nonce("wp_rest"); ?>'
|
||||
},
|
||||
body: json
|
||||
});
|
||||
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-WP-Nonce': '{$nonce}'
|
||||
},
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
if(!resp.ok){
|
||||
throw new Error(`HTTP Error! status: ${resp.status}` );
|
||||
}
|
||||
const data = await resp.json();
|
||||
console.log("Data returnd", data);
|
||||
|
||||
const data = await resp.json();
|
||||
if (data.status === "error"){
|
||||
const err = data.message;
|
||||
let errmsg
|
||||
|
||||
sndBtn.disabled = false;
|
||||
sndBtnLdr.style.opacity = "0";
|
||||
sndBtnTxt.style.opacity = "1";
|
||||
switch (err) {
|
||||
case "Huisnummer not found":
|
||||
errmsg = "Adres niet gevonden.";
|
||||
break;
|
||||
case "Multiple addresses match this huisnummer; add huisletter and/or huisnummertoevoeging":
|
||||
errmsg = "Huisnummertovoeging mist.";
|
||||
break;
|
||||
default:
|
||||
errmsg = "Gegevens niet correct.";
|
||||
}
|
||||
document.querySelector("#error_message_modal_postcode").innerHTML = errmsg;
|
||||
}
|
||||
|
||||
if (data.status === "error") {
|
||||
const errorCode = data.code; // Komt nu correct door vanuit PHP
|
||||
const apiMessage = data.message;
|
||||
|
||||
let userErrorMessage = "Gegevens zijn niet correct.";
|
||||
|
||||
// Specifieke foutafhandeling
|
||||
if (errorCode === "HUISNUMMER_NOT_FOUND" || apiMessage.includes("not found")) {
|
||||
userErrorMessage = "Adres is niet gevonden.";
|
||||
}
|
||||
else if (errorCode === "HUISNUMMER_AMBIGUOUS") {
|
||||
const suggestions = data.suggestions || [];
|
||||
if (suggestions.length > 0) {
|
||||
userErrorMessage = `Meerdere opties: \${suggestions.join(", ")}. Voeg een toevoeging toe.`;
|
||||
} else {
|
||||
userErrorMessage = "Huisnummertoevoeging is verplicht.";
|
||||
}
|
||||
}
|
||||
else if (errorCode === "CURL_ERROR" || errorCode === "JSON_ERROR") {
|
||||
userErrorMessage = "Serverfout. Probeer het later opnieuw.";
|
||||
}
|
||||
|
||||
errorMsg.innerHTML = userErrorMessage;
|
||||
}
|
||||
|
||||
if (data.status === "success") {
|
||||
// Succes: Modal sluiten en eventueel reload of update
|
||||
postcodeModal.close();
|
||||
// Optioneel: location.reload(); of update de pagina content
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
sndBtn.disabled = false;
|
||||
sndBtnLdr.style.opacity = "0";
|
||||
sndBtnTxt.style.opacity = "1";
|
||||
errorMsg.innerHTML = "Er ging iets mis bij het verbinden.";
|
||||
if (data.status === "success"){
|
||||
postcodeModal.close();
|
||||
//location.reload();
|
||||
}
|
||||
}catch(err){
|
||||
console.error("Fetch Failed:", err);
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
HTML;
|
||||
<?php
|
||||
}
|
||||
|
||||
function render_dialog_html()
|
||||
{
|
||||
echo <<<'HTML'
|
||||
?>
|
||||
<dialog id="postcode_modal" class="postcode_modal" closedby="none">
|
||||
<h2>Vul je postcode en huisnummer in.</h2>
|
||||
<p>Onze prijzen zijn afhankelijk van de regio. Vul daarom de postcode en het huisnummer in om de exacte prijzen te bekijken.</p>
|
||||
<form id="postcode_modal_form" method="post" action="" novalidate>
|
||||
<form id="postcode_modal_form" method="post" action="" novalidation>
|
||||
<div class="form_fields">
|
||||
<!-- Dutch Postcode Field -->
|
||||
<input
|
||||
type="text"
|
||||
name="postcode"
|
||||
title="Voer een geldige postcode in (bijv. 1234AB of 1234 AB)."
|
||||
<div>
|
||||
<input type="text" name="postcode"
|
||||
title="Voer een geldige Nederlandse postcode in (bijv. 1234AB of 1234 AB)."
|
||||
pattern="[1-9][0-9]{3} ?(?!sa|sd|ss)[a-zA-Z]{2}"
|
||||
placeholder="5010 AA"
|
||||
size="8"
|
||||
placeholder= "1010 AA"
|
||||
size="10"
|
||||
required
|
||||
autocomplete="off"
|
||||
/>
|
||||
|
||||
<!-- House Number Field -->
|
||||
<input
|
||||
type="text"
|
||||
name="huisnummer"
|
||||
title="Voer een geldig huisnummer in (bijv. 1, 1A, 1-A)."
|
||||
pattern="\d+([\- ]?[a-zA-Z]+)?"
|
||||
placeholder="10"
|
||||
size="3"
|
||||
<input type="text" name="huisnummer"
|
||||
pattern="/\d+([-\\s]?[a-zA-Z]+)?/"
|
||||
title="Voer een geldig huisnummer in (bijv. 1, 1A, 1-A, 1a)."
|
||||
placeholder= "12A"
|
||||
size="5"
|
||||
required
|
||||
autocomplete="off"
|
||||
/>
|
||||
<button id="postcode_modal_submit" type="submit"><span class="btn_text">OK</span>
|
||||
<div class="btn_loader">
|
||||
<svg viewBox="0 0 128 30" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle style="--i:0" cx="14" cy="15" r="14" />
|
||||
<circle style="--i:1" cx="64" cy="15" r="14" />
|
||||
<circle style="--i:2" cx="114" cy="15" r="14" />
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<button id="postcode_modal_submit" type="submit">verzend</button>
|
||||
</div>
|
||||
</form>
|
||||
<div class="error_message_modal_postcode" id="error_message_modal_postcode" aria-live="polite"></div>
|
||||
<div class="loader_modal"></div>
|
||||
</dialog>
|
||||
HTML;
|
||||
<?php
|
||||
}
|
||||
|
||||
function has_postcode()
|
||||
{
|
||||
if (isset($_SESSION["postcode"])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function handle_postcode_modal($data)
|
||||
@@ -188,95 +147,73 @@ function handle_postcode_modal($data)
|
||||
$params = $data->get_params();
|
||||
$nonce = $data->get_header("X-WP-Nonce");
|
||||
|
||||
// 1. Security
|
||||
if (!wp_verify_nonce($nonce, "wp_rest")) {
|
||||
echo json_encode(["status" => "error", "message" => "nononce"]);
|
||||
exit();
|
||||
}
|
||||
|
||||
// 2. Basis Validatie
|
||||
if (!verify_postcode($params["postcode"])) {
|
||||
echo json_encode(["status" => "error", "message" => "postcode"]);
|
||||
exit();
|
||||
}
|
||||
if (!verify_huisnummer($params["huisnummer"])) {
|
||||
echo json_encode(["status" => "error", "message" => "huisnummer"]);
|
||||
exit();
|
||||
}
|
||||
|
||||
// 3. API Call (Ruwe data)
|
||||
$result = getStraatnaam($params["postcode"], $params["huisnummer"]);
|
||||
|
||||
// 4. Validatie & Logic
|
||||
$hasError = isset($result["error"]);
|
||||
$errorCode = null;
|
||||
$errorMessage = null;
|
||||
$candidates = [];
|
||||
$straatnaam = null;
|
||||
$woonplaats = null;
|
||||
|
||||
if ($hasError) {
|
||||
$errorCode = $result["error"]["code"] ?? "UNKNOWN_ERROR";
|
||||
$errorMessage = $result["error"]["message"] ?? "Onbekende fout";
|
||||
$candidates = $result["error"]["details"]["candidates"] ?? [];
|
||||
|
||||
// SPECIAL CASE: HUISNUMMER_AMBIGUOUS
|
||||
if ($errorCode === "HUISNUMMER_AMBIGUOUS" && !empty($candidates)) {
|
||||
$plainMatch = null;
|
||||
|
||||
// Zoek naar candidate ZONDER toevoeging
|
||||
foreach ($candidates as $candidate) {
|
||||
if (
|
||||
empty($candidate["huisletter"]) &&
|
||||
empty($candidate["huisnummertoevoeging"])
|
||||
) {
|
||||
$plainMatch = $candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Als we een 'platte' match hebben (bijv. 15), dan is het SUCCES
|
||||
if ($plainMatch) {
|
||||
$hasError = false; // Negeer de error
|
||||
$straatnaam = $plainMatch["straat"];
|
||||
$woonplaats = $plainMatch["woonplaats"];
|
||||
}
|
||||
if (wp_verify_nonce($nonce, "wp_rest")) {
|
||||
if (!verify_postcode($params["postcode"])) {
|
||||
$resp = [
|
||||
"status" => "error",
|
||||
"message" => "postcode",
|
||||
];
|
||||
echo json_encode($resp);
|
||||
exit();
|
||||
}
|
||||
|
||||
if (!verify_huisnummer($params["huisnummer"])) {
|
||||
$resp = [
|
||||
"status" => "error",
|
||||
"message" => "huisnummer",
|
||||
];
|
||||
echo json_encode($resp);
|
||||
exit();
|
||||
}
|
||||
|
||||
$result = getStraatnaam($params["postcode"], $params["huisnummer"]);
|
||||
if (isset($result["error"])) {
|
||||
$resp = [
|
||||
"status" => "error",
|
||||
"message" => $result["error"],
|
||||
"apirequest" => "openpostcode.nl",
|
||||
];
|
||||
echo json_encode($resp);
|
||||
exit();
|
||||
}
|
||||
|
||||
$_SESSION["postcode"] = $params["postcode"];
|
||||
$_SESSION["huisnummer"] = $params["huisnummer"];
|
||||
$_SESSION["straatnaam"] = $result["straatnaam"];
|
||||
$_SESSION["woonplaats"] = $result["woonplaats"];
|
||||
|
||||
$_SESSION["postcode_is_local"] = postcode_in_range(
|
||||
$params["postcode"],
|
||||
5000,
|
||||
5800,
|
||||
);
|
||||
|
||||
$resp = [
|
||||
"status" => "success",
|
||||
"message" => "all good",
|
||||
"straatnaam" => $result["straatnaam"],
|
||||
"lokaal_trarief" => postcode_in_range(
|
||||
$params["postcode"],
|
||||
5000,
|
||||
5800,
|
||||
),
|
||||
];
|
||||
} else {
|
||||
// Normaal succes (geen error in response)
|
||||
if (!empty($result["results"])) {
|
||||
$straatnaam = $result["results"][0]["straat"];
|
||||
$woonplaats = $result["results"][0]["woonplaats"];
|
||||
} else {
|
||||
// Edge case: geen error veld, maar ook geen results
|
||||
$hasError = true;
|
||||
$errorCode = "NO_RESULTS";
|
||||
$errorMessage = "Adres niet gevonden";
|
||||
}
|
||||
$resp = [
|
||||
"status" => "error",
|
||||
"message" => "nononce",
|
||||
];
|
||||
}
|
||||
echo json_encode($resp);
|
||||
exit();
|
||||
}
|
||||
|
||||
// 5. Response sturen
|
||||
if ($hasError) {
|
||||
return new WP_REST_Response([
|
||||
"status" => "error",
|
||||
"code" => $errorCode,
|
||||
"message" => $errorMessage,
|
||||
"suggestions" => $result["error"]["details"]["suggestions"] ?? [],
|
||||
"apirequest" => "openpostcode.nl",
|
||||
], 400);
|
||||
}
|
||||
set_pppr_cookie(
|
||||
postcode_in_range($params["postcode"]),
|
||||
trim($straatnaam),
|
||||
trim($params["huisnummer"]),
|
||||
trim($params["postcode"]),
|
||||
trim($woonplaats)
|
||||
);
|
||||
|
||||
return new WP_REST_Response([
|
||||
"status" => "success",
|
||||
"message" => "all good",
|
||||
], 200);
|
||||
function register_modal_api()
|
||||
{
|
||||
register_rest_route("postcode-modal/v1", "submit", [
|
||||
"methods" => "POST",
|
||||
"callback" => "handle_postcode_modal",
|
||||
]);
|
||||
}
|
||||
|
||||
function verify_postcode($postcode)
|
||||
@@ -310,45 +247,31 @@ function getStraatnaam($postcode, $huisnummer)
|
||||
curl_setopt($ch, CURLOPT_USERAGENT, "PHP/OpenPostcodeClient");
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
|
||||
if (curl_error($ch)) {
|
||||
curl_close($ch);
|
||||
return [
|
||||
"error" => ["code" => "CURL_ERROR", "message" => curl_error($ch)],
|
||||
];
|
||||
return ["error" => "cURL error: " . curl_error($ch)];
|
||||
}
|
||||
|
||||
curl_close($ch);
|
||||
|
||||
// Decodeer als associatieve array (true)
|
||||
$data = json_decode($response, true);
|
||||
|
||||
// Als JSON decode faalt
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
if (isset($data["error"])) {
|
||||
return [
|
||||
"error" => [
|
||||
"code" => "JSON_ERROR",
|
||||
"message" => "Invalid JSON response",
|
||||
],
|
||||
"error" => $data["error"]["message"],
|
||||
"code" => $data["error"]["code"],
|
||||
];
|
||||
}
|
||||
|
||||
// Give me back RUWE data (inclusive meta, error, results, etc.)
|
||||
return $data;
|
||||
return [
|
||||
"straatnaam" => $data["results"][0]["straat"],
|
||||
"woonplaats" => $data["results"][0]["woonplaats"],
|
||||
];
|
||||
}
|
||||
|
||||
function postcode_in_range($postcode)
|
||||
function postcode_in_range($postcode, $start, $end)
|
||||
{
|
||||
$vals = get_option("local_postcodes_values", "");
|
||||
$rows = preg_split("/\R/", $vals, -1, PREG_SPLIT_NO_EMPTY);
|
||||
$pc_arr = [];
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$row = trim($row);
|
||||
$postcode_range = explode("-", $row);
|
||||
$pc_arr[] = [(int) $postcode_range[0], (int) $postcode_range[1]];
|
||||
}
|
||||
$cleanPostcode = strtoupper(preg_replace("/\s+/", "", $postcode));
|
||||
|
||||
if (!preg_match('/^\d{4}[A-Z]{2}$/', $cleanPostcode)) {
|
||||
@@ -357,202 +280,120 @@ function postcode_in_range($postcode)
|
||||
|
||||
$numberPart = (int) substr($cleanPostcode, 0, 4);
|
||||
|
||||
foreach ($pc_arr as $pc_to_check) {
|
||||
if ($numberPart >= $pc_to_check[0] && $numberPart <= $pc_to_check[1]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return $numberPart >= $start && $numberPart <= $end;
|
||||
}
|
||||
|
||||
function modify_checkout_with_js() {
|
||||
if ( ! is_checkout() || ( is_wc_endpoint_url() && ! is_wc_endpoint_url( 'order-received' ) ) ) {
|
||||
function modify_checkout_with_js()
|
||||
{
|
||||
if (
|
||||
!is_checkout() ||
|
||||
(is_wc_endpoint_url() && !is_wc_endpoint_url("order-received"))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$data = get_PPPR_data();
|
||||
if ( empty( $data ) ) {
|
||||
return;
|
||||
}
|
||||
$woonplaats = $_SESSION["woonplaats"];
|
||||
$postcode = $formatted_postcode = preg_replace(
|
||||
"/(\d+)([A-Z]+)/",
|
||||
'$1 $2',
|
||||
strtoupper($_SESSION["postcode"]),
|
||||
);
|
||||
$address =
|
||||
$_SESSION["straatnaam"] . " " . strtoupper($_SESSION["huisnummer"]);
|
||||
// Output the JavaScript
|
||||
?>
|
||||
<script type="text/javascript" id="fill_address_fields">
|
||||
jQuery(document).ready(function($) {
|
||||
fillCheckoutFields();
|
||||
$(document.body).on('updated_checkout', fillCheckoutFields);
|
||||
});
|
||||
|
||||
$city = esc_js( $data['city'] );
|
||||
$postcode = esc_js( preg_replace( '/(\d+)([A-Z]+)/', '$1 $2', strtoupper( $data['postcode'])));
|
||||
$address = esc_js( $data['street'] . ' ' . strtoupper( $data['house_number']));
|
||||
function fillCheckoutFields() {
|
||||
if (typeof wp !== 'undefined' && wp.data && wp.data.dispatch) {
|
||||
const store = 'wc/store/cart';
|
||||
|
||||
// Enqueue script properly to handle dependencies and nonces
|
||||
wp_enqueue_script( 'jquery' );
|
||||
wp_localize_script( 'jquery', 'ajax_object', array(
|
||||
'ajax_url' => admin_url( 'admin-ajax.php' ),
|
||||
'nonce' => wp_create_nonce( 'unset_my_session_nonce' )
|
||||
) );
|
||||
wp.data.dispatch(store).setShippingAddress({
|
||||
first_name: '',
|
||||
last_name: '',
|
||||
address_1: '<?php echo esc_js($address); ?>',
|
||||
address_2: '',
|
||||
city: '<?php echo esc_js($woonplaats); ?>',
|
||||
state: '',
|
||||
postcode: '<?php echo esc_js($postcode); ?>',
|
||||
country: 'NL',
|
||||
phone: '',
|
||||
email: ''
|
||||
});
|
||||
|
||||
echo <<<HTML
|
||||
<script type="text/javascript" id="set_pppr_fields">
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
// Safari Fix: Handle back/forward cache (bfcache)
|
||||
window.addEventListener('pageshow', function(event) {
|
||||
if (event.persisted || (typeof wp !== 'undefined' && wp.data)) {
|
||||
initPPPRFields();
|
||||
}
|
||||
});
|
||||
//make fields READONLY and ppstcode reset.
|
||||
setTimeout(() => {
|
||||
// make prefilled fiields readonly.
|
||||
$('#shipping-postcode, #shipping-city, #shipping-address_1')
|
||||
.prop('readonly', true)
|
||||
.css('background', '#f9f9f9');
|
||||
|
||||
function initPPPRFields() {
|
||||
// Wait for React/WC Blocks to mount
|
||||
setTimeout(() => {
|
||||
const addressVal = '{$address}';
|
||||
const cityVal = '{$city}';
|
||||
const postcodeVal = '{$postcode}';
|
||||
// create postcode reset button
|
||||
const div = document.createElement("div");
|
||||
const script = document.createElement('script');
|
||||
div.setAttribute("class", "postcode-reset")
|
||||
div.innerHTML = `
|
||||
<a href="#" class="reset-postcode-show-comfirm" >Reset postcode.</a>
|
||||
<span class="bevestiging"> Weet je het zeker?
|
||||
<a href="#" class="accept">ja</a>/<a href="#" class="decline">nee</a>
|
||||
(Deze handeling leegt de winkelwagen.)
|
||||
</span> `;
|
||||
div.style.width = "100%";
|
||||
document.querySelector(".wc-block-components-address-form__city").after(div);
|
||||
}, 500);
|
||||
|
||||
// Map WC Blocks field selectors
|
||||
const fields = [
|
||||
{ id: 'shipping-address_1', val: addressVal },
|
||||
{ id: 'shipping-city', val: cityVal },
|
||||
{ id: 'shipping-postcode', val: postcodeVal }
|
||||
];
|
||||
|
||||
fields.forEach(field => {
|
||||
// 1. Try Redux Dispatch (Standard)
|
||||
if (typeof wp !== 'undefined' && wp.data && wp.data.dispatch) {
|
||||
try {
|
||||
wp.data.dispatch('wc/store/cart').setShippingAddress({ [field.id.replace('shipping-', '')]: field.val });
|
||||
} catch(e) { console.warn('Redux update skipped', e); }
|
||||
}
|
||||
|
||||
// 2. Force DOM Update (Safari Fallback)
|
||||
// Select both legacy and Blocks inputs
|
||||
const inputs = document.querySelectorAll('#' + field.id + ', input[name="' + field.id.replace('shipping-', '') + '"]');
|
||||
|
||||
inputs.forEach(input => {
|
||||
if (input.value !== field.val) {
|
||||
input.value = field.val;
|
||||
|
||||
// CRITICAL FOR SAFARI: Manually trigger events so React notices
|
||||
['input', 'change'].forEach(evtType => {
|
||||
input.dispatchEvent(new Event(evtType, { bubbles: true, cancelable: true }));
|
||||
});
|
||||
}
|
||||
|
||||
// 3. Apply Visual Lock
|
||||
input.setAttribute('data-pppr-locked', 'true');
|
||||
input.readOnly = true;
|
||||
});
|
||||
});
|
||||
|
||||
// Inject Reset Button once
|
||||
if (!document.querySelector('.postcode-reset')) {
|
||||
const cityContainer = document.querySelector('.wc-block-components-address-form__city');
|
||||
if (cityContainer) {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'postcode-reset';
|
||||
div.style.display = 'block';
|
||||
div.style.width = '100%';
|
||||
div.style.marginTop = '10px';
|
||||
|
||||
div.innerHTML = `
|
||||
<a href="#" class="reset-postcode-show-comfirm" style="font-size:12px; text-decoration:underline; display:block; margin-top:5px;">Reset postcode.</a>
|
||||
<span class="bevestiging" style="display:none; font-size:12px; margin-top:5px;">
|
||||
Deze handeling leegt ook de winkelwagen. Weet je het zeker?
|
||||
<a href="#" class="accept" style="color:red; font-weight:bold;"> JA </a>/
|
||||
<a href="#" class="decline"> nee </a>
|
||||
</span>`;
|
||||
cityContainer.parentNode.insertBefore(div, cityContainer.nextSibling);
|
||||
|
||||
// Event Listeners (using delegation for safety)
|
||||
div.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
if (e.target.classList.contains('reset-postcode-show-comfirm')) {
|
||||
e.target.style.display = 'none';
|
||||
div.querySelector('.bevestiging').style.display = 'block';
|
||||
} else if (e.target.classList.contains('decline')) {
|
||||
div.querySelector('.bevestiging').style.display = 'none';
|
||||
div.querySelector('.reset-postcode-show-comfirm').style.display = 'block';
|
||||
} else if (e.target.classList.contains('accept')) {
|
||||
fetch(ajax_object.ajax_url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: new URLSearchParams({
|
||||
action: 'unset_my_session',
|
||||
nonce: ajax_object.nonce
|
||||
})
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
if (res.success) location.reload();
|
||||
else alert('Failed to reset: ' + (res.data || 'Unknown error'));
|
||||
})
|
||||
.catch(err => console.error('AJAX error:', err));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Add CSS for locked state dynamically
|
||||
if (!document.getElementById('pppr-styles')) {
|
||||
const style = document.createElement('style');
|
||||
style.id = 'pppr-styles';
|
||||
style.textContent = `
|
||||
input[data-pppr-locked="true"] {
|
||||
background-color: #f9f9f9 !important;
|
||||
cursor: not-allowed !important;
|
||||
pointer-events: none !important;
|
||||
opacity: 0.7;
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
|
||||
}, 600); // Delay for WC Blocks hydration
|
||||
}
|
||||
|
||||
// Initialize on load
|
||||
initPPPRFields();
|
||||
|
||||
// Re-apply if checkout updates (e.g. shipping method change)
|
||||
document.body.addEventListener('updated_checkout', function() {
|
||||
setTimeout(initPPPRFields, 400);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
HTML;
|
||||
jQuery(document.body).trigger('update_checkout');
|
||||
} else {
|
||||
console.error('WooCommerce Blocks API is niet beschikbaar');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
|
||||
function load_assets_reset_postcode_on_checkout()
|
||||
{
|
||||
if (is_checkout() && !is_wc_endpoint_url()) {
|
||||
wp_enqueue_style(
|
||||
"reset-postcode-style",
|
||||
plugin_dir_url(__FILE__) . "assets/reset-postcode.css",
|
||||
[],
|
||||
"1.0.0",
|
||||
if (is_checkout() && !is_wc_endpoint_url()) {
|
||||
wp_enqueue_style(
|
||||
"reset-postcode-style",
|
||||
plugin_dir_url(__FILE__) . "assets/reset-postcode.css",
|
||||
[],
|
||||
"1.0.0",
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
"reset-postcode-script",
|
||||
plugin_dir_url(__FILE__) . "assets/reset-postcode.js",
|
||||
[],
|
||||
"1.0.0",
|
||||
true,
|
||||
wp_enqueue_script(
|
||||
"reset-postcode-script",
|
||||
plugin_dir_url(__FILE__) . "assets/reset-postcode.js",
|
||||
[],
|
||||
"1.0.0",
|
||||
true,
|
||||
);
|
||||
|
||||
// Pass PHP variables to JavaScript
|
||||
wp_localize_script("reset-postcode-script", "ajax_object", [
|
||||
"ajax_url" => admin_url("admin-ajax.php"),
|
||||
"nonce" => wp_create_nonce("reset_postcode_nonce"), // Creates a secure token
|
||||
// Pass PHP variables to JavaScript
|
||||
wp_localize_script("reset-postcode-script", "ajax_object", [
|
||||
"ajax_url" => admin_url("admin-ajax.php"),
|
||||
"nonce" => wp_create_nonce("reset_postcode_nonce"), // Creates a secure token
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function handle_unset_session_fetch()
|
||||
{
|
||||
// Verify the nonce for security
|
||||
if (!wp_verify_nonce($_POST["nonce"], "reset_postcode_nonce")) {
|
||||
wp_die("Security check failed.");
|
||||
// Verify the nonce for security
|
||||
if (!wp_verify_nonce($_POST["nonce"], "reset_postcode_nonce")) {
|
||||
wp_die("Security check failed.");
|
||||
}
|
||||
|
||||
// Unset the specific session variable
|
||||
if (isset($_SESSION["postcode"])) {
|
||||
$_SESSION = [];
|
||||
}
|
||||
unset_pppr_cookie();
|
||||
|
||||
// Send a JSON response
|
||||
wp_send_json_success();
|
||||
|
||||
Reference in New Issue
Block a user