-/41

Basisopstelling voor een AJAX load event

HTML Code

html
<div class="pure-g">
    <div class="pure-u-1-4">
        <button id="ajax_trigger">Klik hier!</button>
        <span id="ajax_placeholder">Gladior</span>
    </div>
    <div class="pure-u-3-4">
        <div id="ajax_response"></div>
    </div>
</div>

PHP Code

php
/**
 * Nieuwe AJAX load request aanmaken
 */

// Stap 1: Registreer je script file, waarop je de AJAX actie wilt uitvoeren
function gladior_enqueue_ajax_script()
{

    // Enqueue the JavaScript file on the frontend.
    wp_enqueue_script(
        'gladior_ajax_script_handle', // Handle for the script.
        get_template_directory_uri() . '/scripts/the_ajax_script.js', // Path to the script file.
    );

    // Localize script to pass the AJAX URL to JavaScript
    wp_localize_script('gladior_ajax_script_handle', 'the_ajax_object', [
        'ajax_url' => admin_url('admin-ajax.php'),
        '_nonce_to_verify' => wp_create_nonce('_nonce_to_verify'),
    ]);
}
// Hook the function to the 'wp_enqueue_scripts' action.
add_action('wp_enqueue_scripts', 'gladior_enqueue_ajax_script');




function function_to_execute()
{
    $data = json_decode(file_get_contents('php://input'));
    wp_verify_nonce($data->_nonce_to_verify, '_nonce_to_verify') || wp_die("Invalid nonce " . $data->_nonce_to_verify);

    $content = $data->content ? $data->content : 'No content provided';
    wp_send_json_success(array(
        'output_content' => $content,
        'data' => $data,
    ));
}

add_action('wp_ajax_function_to_execute', 'function_to_execute');
add_action('wp_ajax_nopriv_function_to_execute', 'function_to_execute');

JAVASCRIPT Code

javascript
async function gladior_ajax_request_function(body) {
  body.base_url = window.location.href.replace(RegExp("page/[0-9]/"), "");
  try {
    const response = await fetch(
      `${the_ajax_object.ajax_url}?action=function_to_execute`,
      {
        method: "POST",
        body: JSON.stringify({
            _nonce_to_verify: the_ajax_object._nonce_to_verify,
            ...body
        }),
      }
    );
    const data = await response.json();
    return data.data;
  } catch (error) {
    return error;
  }
}

document.addEventListener("DOMContentLoaded", async function () {
  const placeholder = document.getElementById("ajax_placeholder");
  const outputContainer = document.getElementById("ajax_response");
  if (!outputContainer) return;

  // Functie om de content te updaten (beide keuzes nodig)
  // maak een async functie updateTheContent met een integer parameter
  async function updateTheContent(variable = placeholder?.innerHTML) {
    // Toon loading animatie
    outputContainer.innerHTML = "Loading...";

    const res = await gladior_ajax_request_function({
        _nonce_to_verify: the_ajax_object._nonce_to_verify,
        content: variable,
    });

    if (outputContainer && res.output_content) {
      outputContainer.innerHTML = res.output_content;
    }
  }

  // Als je al een initiële update wilt doen.
  await updateTheContent();


  // Voeg het updateTheContent toe aan alle navigatie items
  const trigger = document.getElementById("ajax_trigger");
    trigger.addEventListener("click", async () => {
        await updateTheContent();
    });
});
In je .function.php plak je de content van de PHP snippet.
Maak een .js file aan: /scripts/the_ajax_script.js en plak hierin de content van de JavaScript snippet.
Bekijk Snippet

Magento WordPress integratie – Widgets uitschakelen in WYSIWYG

Als je Wordpress erg traag wordt van alle Widgets die hij wil inladen vanuit

PHP Code

php
add_action('init', function () {
    // Loop through all callbacks for media_buttons
    global $wp_filter;
 
    if ( isset( $wp_filter['media_buttons'] ) ) {
        foreach ( $wp_filter['media_buttons']->callbacks as $priority => $callbacks ) {
            foreach ( $callbacks as $id => $callback ) {
 
                // Check if the callback belongs to M2I_Editor_Button
                if (
                    is_array($callback['function']) &&
                    is_object($callback['function'][0]) &&
                    get_class($callback['function'][0]) === 'M2I_Editor_Button'
                ) {
                    unset($wp_filter['media_buttons']->callbacks[$priority][$id]);
                }
 
            }
        }
    }
});
Voeg de snippet toe in je functions.php , om er voor te zorgen dat de Widgets niet wordt ingeladen. Zorgt namelijk voor enorme snelheidsverlies bij veel categories.
Bekijk Snippet

IP Adres blokkeren htaccess Magento 2

Plaats de volgende regels bovenin in je /pub/.htaccess bestand:

Order Allow,Deny
Allow from all
Deny from 34.141.10.24
Deny from 34.159.247.222
Bekijk Snippet

Van csv bestand via PHP post_types aanmaken en ACF velden vullen

PHP Code

php
if (get_current_user_id() === 1) : 
	/**
     * Import EQA Opleidingen from CSV into CPT 'eqa-opleiding'
     * Usage: put this in a one-off mu-plugin, a WP-CLI command, or run in a secure admin context.
     */

    if ( ! defined('ABSPATH') ) { exit; }

    // === CONFIG ===

    $fileloc = dirname(__DIR__, 1);
    $csvFile = $fileloc . '/pe-opleidingen.csv'; // adjust path
    $post_type = 'pe-opleiding';
    $taxonomy  = 'pe-type'; // ACF taxonomy attached to this post_type

    // Map logical field names to ACF field KEYS (recommended).
    // TODO: replace field_XXXXXXXX with your actual ACF field keys.
    $acf_keys = [
        'systeemid'         => 'systeemid', // number
        'opleidingskenmerk' => 'opleidingskenmerk', // text
        'link'              => 'link', // link
        'toegekend'         => 'pe_of_eqa_toegekend_op', // date
        'geldig'            => 'pe_of_eqa_geldig_tot', // date
        'opleidingsvorm'    => 'opleidingsvorm', // wysiwyg
        'opl_omschrijving'  => 'opl_omschrijving', // wysiwyg
        'peuren'            => 'pe_uren', // number
        'duurdagen'         => 'duur_dagen', // number
        'kosten'            => 'kosten', // number
        'startdata'         => 'startdata', // wysiwyg
        // taxonomy is handled via wp_set_object_terms()
    ];

    // === HELPERS ===
    /**
     * Try to detect delimiter based on header line.
     */
    function detect_delimiter($line) {
        $candidates = [",", ";", "\t", "|"];
        $counts = [];
        foreach ($candidates as $d) {
            $counts[$d] = substr_count($line, $d);
        }
        arsort($counts);
        return key($counts);
    }

    /**
     * Normalize header labels to canonical keys.
     */
    function normalize_header($label) {
        $label = trim(mb_strtolower($label));
        // allow small variations/spaces
        $label = preg_replace('/\s+/', '', $label);
        return $label;
    }

    /**
     * Parse a date string into ACF Date Picker format 'Ymd'.
     * Returns null if cannot parse or empty.
     */
    function to_acf_date($value) {
        $value = trim((string)$value);
        if ($value === '') return null;

        // Try common formats
        $formats = ['Y-m-d', 'd-m-Y', 'd/m/Y', 'Y/m/d', 'd.m.Y', 'Ymd'];
        foreach ($formats as $fmt) {
            $dt = DateTimeImmutable::createFromFormat($fmt, $value);
            if ($dt instanceof DateTimeImmutable) {
                return $dt->format('Ymd');
            }
        }

        // Last resort: strtotime
        $ts = strtotime($value);
        if ($ts !== false) {
            return date('Ymd', $ts);
        }

        return null;
    }

    /**
     * Read the first line safely (handling BOM).
     */
    function fopen_no_bom($path) {
        $h = fopen($path, 'r');
        if (! $h) return false;
        // Remove UTF-8 BOM if present
        $bom = fread($h, 3);
        if ($bom !== "\xEF\xBB\xBF") {
            // Not a BOM; rewind to start
            rewind($h);
        }
        return $h;
    }

    /**
     * Find existing post by unique meta 'systeemid'.
     */
    function find_post_by_systeemid($systeemid, $post_type) {
        $q = new WP_Query([
            'post_type'      => $post_type,
            'posts_per_page' => 1,
            'post_status'    => 'any',
            'meta_query'     => [
                [
                    'key'   => 'systeemid',
                    'value' => $systeemid,
                ],
            ],
            'fields'         => 'ids',
            'no_found_rows'  => true,
        ]);
        return $q->have_posts() ? (int)$q->posts[0] : 0;
    }

    // === RUN ===
    if (! file_exists($csvFile)) {
        wp_die('CSV not found at: ' . esc_html($csvFile));
    }

    $handle = fopen_no_bom($csvFile);
    if ($handle === false) {
        wp_die('Unable to open CSV.');
    }

    // Peek first line to detect delimiter
    $firstLine = fgets($handle);


    if ($firstLine === false) {
        fclose($handle);
        wp_die('CSV is empty.');
    }
    $delimiter = detect_delimiter($firstLine);

    // Build header from first line
    $headerRaw = str_getcsv($firstLine, $delimiter);
    $headerMap = [];
    foreach ($headerRaw as $i => $label) {
        $count++;
        $norm = normalize_header($label);
        $headerMap[$i] = $norm;
    }

    // Expected canonical keys (normalized):
    $required = ['systeemid','naam','opleidingskenmerk','link','toegekend','geldig','petype', 'opleidingsvorm', 'opl_omschrijving', 'peuren', 'duurdagen', 'kosten', 'startdata'];
    $missing  = array_diff($required, $headerMap);
    if ($missing) {
        // Try to be forgiving if your header used small differences like "systeemidnaam" earlier:
        // If you for some reason have combined headers, adjust here or ensure the CSV header matches the list above.
        // For now, enforce correctness:
        fclose($handle);
        wp_die('CSV header missing columns: ' . implode(', ', $missing));
    }

    $created = 0;
    $updated = 0;

    // Iterate rows
    while (($row = fgetcsv($handle, 0, $delimiter)) !== false) {
        if (count($row) === 1 && trim($row[0]) === '') {
            continue; // skip empty lines
        }

        $item = [];
        foreach ($row as $i => $value) {
            $key = $headerMap[$i] ?? 'col_'.$i;
            $item[$key] = is_string($value) ? trim($value) : $value;
        }

        // Basic validation
        if (($item['naam'] ?? '') === '') {
            continue; // skip rows without a title
        }

        $systeemid = $item['systeemid'] ?? '';
        $existing_id = $systeemid !== '' ? find_post_by_systeemid($systeemid, $post_type) : 0;

        $postarr = [
            'post_type'   => $post_type,
            'post_title'  => $item['naam'],
            'post_status' => 'publish',
        ];

        if ($existing_id) {
            $postarr['ID'] = $existing_id;
            $post_id = wp_update_post($postarr, true);
        } else {
            $post_id = wp_insert_post($postarr, true);
        }

        if (is_wp_error($post_id)) {
            error_log('Failed to save post for systeemid '.$systeemid.': '.$post_id->get_error_message());
            continue;
        }

        // === ACF fields ===
        // Number: systeemid


        // === ACF fields — TEXT ===
        if (isset($item['opleidingsvorm'])) {
            update_field($acf_keys['opleidingsvorm'], sanitize_text_field($item['opleidingsvorm']), $post_id);
        }
        if (isset($item['opleidingskenmerk'])) {
            update_field($acf_keys['opleidingskenmerk'], sanitize_text_field($item['opleidingskenmerk']), $post_id);
        }

        // === ACF fields — WYSIWYG ===
        foreach (['opl_omschrijving','startdata'] as $wysiwygKey) {
            if (array_key_exists($wysiwygKey, $item)) {
                $val = is_string($item[$wysiwygKey]) ? trim($item[$wysiwygKey]) : $item[$wysiwygKey];
                if ($val !== '' && $val !== null) {
                    update_field($acf_keys[$wysiwygKey], $val, $post_id);
                }
            }
        }

        // Numbers: peuren, duurdagen, kosten
        foreach (['systeemid','peuren','duurdagen','kosten'] as $numKey) {
            if (!empty($item[$numKey])) {
                update_field($acf_keys[$numKey], (float) $item[$numKey], $post_id);
            }
        }

        // Link: expects array(url, title, target)
        if (!empty($item['link'])) {
            $linkArr = [
                'url'    => esc_url_raw($item['link']),
                'title'  => $item['naam'],    // or a static label like "Bekijk"
                'target' => '_blank',         // adjust if you use same window
            ];
            update_field($acf_keys['link'], $linkArr, $post_id);
        }

        // Dates: to ACF date format 'Ymd'
        foreach (['toegekend','geldig','verlengd'] as $dateKey) {
            $val = isset($item[$dateKey]) ? to_acf_date($item[$dateKey]) : null;
            if ($val) {
                update_field($acf_keys[$dateKey], $val, $post_id);
            } else {
                // Optional: clear if empty
                // update_field($acf_keys[$dateKey], null, $post_id);
            }
        }

        // Taxonomy: niveau (can be multiple; split by comma if present)
        if (!empty($item['petype'])) {
            $termsRaw = array_map('trim', preg_split('/[,|;]+/', $item['petype']));
            $termsRaw = array_filter($termsRaw, fn($t) => $t !== '');
            if ($termsRaw) {
                // Ensure terms exist; create as needed
                $term_ids = [];
                foreach ($termsRaw as $termName) {
                    $existing = term_exists($termName, $taxonomy);
                    if ($existing === 0 || $existing === null) {
                        $created_term = wp_insert_term($termName, $taxonomy);
                        if (!is_wp_error($created_term)) {
                            $term_ids[] = (int)$created_term['term_id'];
                        }
                    } elseif (is_array($existing) && isset($existing['term_id'])) {
                        $term_ids[] = (int)$existing['term_id'];
                    }
                }
                if ($term_ids) {
                    wp_set_object_terms($post_id, $term_ids, $taxonomy, false);
                }
            }
        }

        if ($existing_id) { $updated++; } else { $created++; }
    }

    fclose($handle);

    printf(
        "Done. Created: %d, Updated: %d\n",
        $created,
        $updated
    );
endif;
Bekijk Snippet

Slider met alleen CSS

HTML Code

html
<div class="container py-4">
	<div class="your_class_name">
		<div class="g_card">1</div>
		<div class="g_card">2</div>
		<div class="g_card">3</div>
		<div class="g_card">4</div>		
		<div class="g_card">5</div>
		<div class="g_card">6</div>
		<div class="g_card">7</div>		
		<div class="g_card">8</div>
		<div class="g_card">9</div>
		<div class="g_card">10</div>
	</div>
</div>

CSS Code

css
@default_per_page: 3;

.g_slide {
	.g_slide();
}

.g_slide(@per_page: @default_per_page) {
	@per_page_percent: (100% / @per_page);
	@gap_adjustment: (1 - (1rem / @per_page));
	.d-flex;
	flex-wrap: nowrap; // Prevent wrapping

	width: 100%;
	gap: 1em;	
	
	overflow-x: auto;
	scroll-behavior: smooth;
	anchor-name: --slide-container;
	scroll-snap-type: x mandatory;
	scroll-marker-group: after;
	&::-webkit-scrollbar {
		.d-none;
	}
	&::scroll-marker-group {
		.d-flex;
		.justify-center;
		gap: 0.5em;
		margin-top: 0.5em;
	}
	
	&::scroll-button(right), &::scroll-button(left) {
		.btn;
		content: '>';
		position: fixed;
		position-anchor: --slide-container;
		position-area: right center;
		translate: -50%;
		&:disabled {
			opacity: 0.5;
			cursor: auto;
		}
	}
	&::scroll-button(left) {
		content: '<';
		position-area: left center;
		translate: 50%;
	}
	
	.g_card {
		scroll-snap-align: start;
		width: ~"calc(@{per_page_percent} - @{gap_adjustment})";
		flex-shrink: 0; 
		@media screen and (max-width: @screen_sm) {
			width: 100%;
			flex: 0 0 100%;
		}
		&::scroll-marker {
			content: '';
			height: 0.75em;
			width: 0.75em;
			background-color: @grey_clr;
			border-radius: 50%;
			&:target-current {
				background-color: @main_clr;
			}
		}
	}
}
.g_card {
	.bg-grey;
	.bradius;
	.center;
	min-height: 500px;
}


.your_class_name {
	.g_slide(3);
	@media screen and (max-width: @screen_lg) {
		.g_slide(2); // 2 slides per page on smaller screens
	}
}
Zorg dat je een HTML structuur hebt zoals in de Snippet:
<div class="your_class_name">
 <div class="g_card">1</div>
   ....
</div>

Nu kun je eenvoudig de responsiveness aangeven met: 
.g_slide(3);  Hierin in 3 natuurlijk de perPage.
Bekijk Snippet

CSS Classes uitbreiding Basisthema Cis

CSS Code

css
.setbradius(@radius: @default_border_radius) {
    border-radius: @radius;
    overflow: hidden;
}

.bradius {
    border-radius: 1em;
    overflow: hidden;
}

.h-100 {
    height: 100%;
}
.w-100 {
    width: 100%;
}

.center { 
    .d-flex;
    .align-center;
    .justify-center;
}

.grid-gap-base(@size: 5, @decrement: 0.5) when (@size >=0.5) {
  .grid-gap-base(@size - @decrement);
  // Convert decimal to dash format for class names
  @class-name: replace(~"@{size}", "\.", "-");
  .colgap-@{class-name} {
    column-gap: ~"@{size}rem";
  }
  .rowgap-@{class-name} {
    row-gap: ~"@{size}rem";
  }
}

.grid-gap-base();
Bekijk Snippet

Responsive grid-template-columns

CSS Code

css
grid-template-columns: repeat(auto-fit, minmax(225px, 1fr));
Bekijk Snippet

Bots uitsluiten van crawlen htaccess

Heb je veel last van verkeer van een crawl bot, zoals bijv.
    18-214-124-6.crawl.amazonbot.amazon
    44-208-193-63.crawl.amazonbot.amazon
    3-213-85-234.crawl.amazonbot.amazon
    23-21-175-228.crawl.amazonbot.amazon

Sluit deze bot dan uit via .htaccess
    # Blokkeer de Amazonbot
    RewriteCond %{HTTP_USER_AGENT} Amazonbot [NC]
    RewriteRule .* - [F,L]
Bekijk Snippet

Wishlist en product compare verwijderen

Bestand aanmaken:
/app/design/frontend/Gladior/GladiorTheme/Magento_Catalog/layout/default.xml


    
         
        
        
        
        
        

        
        

        
        
    
Bekijk Snippet

Downloadable products weghalen

Maak een bestand aan:
/app/design/frontend/Gladior/GladiorTheme/Magento_Customer/layout/customer_account.xml



    
        
        
            
        
    
Bekijk Snippet

Collapsible laten werken met werkelijke hoogte van in te klappen element

Door de scrollHeight op te halen van het element dat je wilt inklappen, wordt deze altijd zo groot als het element is.

JAVASCRIPT Code

javascript
	var coll = document.getElementsByClassName("collapsible");
	var i;
	for (i = 0; i < coll.length; i++) {
		coll[i].addEventListener("click", function() {
			var content = this.nextElementSibling;
			content.classList.toggle("inactive");
			if(content.classList.contains("inactive")){
				content.style.maxHeight = null;
			}else{
				content.style.maxHeight = content.scrollHeight + "px";
			}
		});
	}
Bekijk Snippet

Op basis van IP adres JS uitvoeren (Marker.io)

PHP Code

php
function get_user_ip() {   
    if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
        return $_SERVER['HTTP_CLIENT_IP'];
    } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
        if (strpos($ip, ',') !== false) {
            $ip = explode(',', $ip)[0];
        }
        return trim($ip);
    } else {
        return $_SERVER['REMOTE_ADDR'];
    }
}
$userIP = get_user_ip();

JAVASCRIPT Code

javascript
const allowedIPs = [
    '77.60.182.90',
    '77.60.182.91'
];

const userIP = '<?php echo $userIP; ?>';
// console.log('IP is: ' + userIP);
if (allowedIPs.includes(userIP)) {
    // laad iets in, bijv. Marker.io
}
Bekijk Snippet

jQuery terug inladen in WordPress thema

PHP Code

php
function reregister_jquery()
{
        wp_enqueue_script('jquery-core', includes_url('/js/jquery/jquery.min.js'), array(), null, false);
        wp_enqueue_script('jquery-migrate', includes_url('/js/jquery/jquery-migrate.min.js'), array('jquery-core'), null, false);
        wp_enqueue_script('jquery');
}
Bekijk Snippet

Magento Cronjobs / Indexers

Met de volgende commands kun je de diverse zaken omtrend Magento cronjobs analyseren.

> ps aux
Toont alle actieve processen op de server — dus alles wat op dat moment draait: PHP-scripts, cronjobs, system services (zoals Redis, MySQL), enzovoort.

> kill -id van crontaak-
Vastgelopen taken kun je killen middels:
 
> php bin/magento indexer:status
Geeft weer of de Magento-indexen up-to-date zijn, of dat er nog herindexatie nodig is.

> crontab -l
Toont welke cronjobs zijn ingesteld voor de gebruiker waarmee je bent ingelogd.
Bekijk Snippet

Site Health / Sitediagnose verbergen voor gebruikers zonder @gladior.com account

PHP Code

php
function hide_site_health_for_specific_users() {
    // Definieer de string die in het emailadres moet voorkomen
    $email_string = '@gladior.com'; // Pas dit aan naar de gewenste string
    
    // Controleer of de gebruiker is ingelogd
    if (!is_user_logged_in()) {
        return;
    }
    
    // Haal de huidige gebruiker op
    $current_user = wp_get_current_user();
    $user_email = $current_user->user_email;
    
    // Controleer of de string voorkomt in het emailadres
    if (strpos($user_email, $email_string) === false) {
        // Verwijder de Site Health menu items
        remove_submenu_page('tools.php', 'site-health.php');
        
        // Blokkeer directe toegang tot de pagina
        add_action('current_screen', 'block_site_health_access');
    }
}

/**
 * Blokkeert directe toegang tot de Site Health pagina
 */
function block_site_health_access() {
    $screen = get_current_screen();
    
    if ($screen && $screen->id === 'site-health') {
        // Redirect naar dashboard met foutmelding
        wp_redirect(admin_url('index.php?site-health-blocked=1'));
        exit;
    }
}

function site_health_blocked_notice() {
    if (isset($_GET['site-health-blocked'])) {
        echo '<div class="notice notice-error is-dismissible">
                <p><strong>Toegang geweigerd:</strong> U heeft geen toegang tot de Site Health pagina.</p>
              </div>';
    }
}

// Hook de functies aan de juiste WordPress acties
add_action('admin_menu', 'hide_site_health_for_specific_users', 999);
add_action('admin_notices', 'site_health_blocked_notice');

function hide_site_health_by_capability() {
    // Alleen administrators kunnen Site Health zien
    if (!current_user_can('manage_options')) {
        remove_submenu_page('tools.php', 'site-health.php');
        add_action('current_screen', 'block_site_health_access');
    }
}
add_action('admin_menu', 'hide_site_health_by_capability', 999);
Bekijk Snippet

Script inladen WordPress

PHP Code

php
// Javascript
if (!wp_script_is('script_segment_layout')) {
    wp_enqueue_script('module_script_slider', get_stylesheet_directory_uri() . '/scripts/slider-script.js');
}


// Stylesheet
if (!wp_script_is('slider_segment_layout')) {
    wp_enqueue_style('module_css_slider', get_stylesheet_directory_uri() . '/styles/assets/splide.css');
}
Bekijk Snippet

Timeout functie

JAVASCRIPT Code

javascript
function checkForElement() {
  const element = document.querySelector('#element_id .element_class');
  if (element) {
    console.log('found');
  } else {
    setTimeout(checkForElement, 100); // Controleer opnieuw na 100 milliseconden
  }
}

// Start de controle
checkForElement();
Bekijk Snippet

Magento verwijder links uit Klantaccount (Downloads en Nieuwsbriefabbonementen)

Voeg toe aan je thema: 
/webshop.nl/app/design/frontend/Gladior/GladiorTheme/Magento_Customer/layout/customer_account.xml

Met de inhoud als volgt:




    
        
                    
            
                
                    customer/account/logout
                    Log out
                
            
        
        
        

    

Bekijk Snippet

Magento PDF uploads mogelijk maken

Installeer deze module: https://github.com/experius/Magento-2-Module-Experius-WysiwygDownloads

composer require experius/module-wysiwygdownloads
  > indien problemen met de dependencies, gebruikt --ignore-platform-reqs
    composer require experius/module-wysiwygdownloads --ignore-platform-reqs
Bekijk Snippet

Magento User aanmaken via CLI

php bin/magento admin:user:create --admin-user=GladiorTemp --admin-password=MissionPossible2025 --admin-email=service+2025@gladior.com --admin-firstname=Mission --admin-lastname=Possible
Bekijk Snippet

Initials Helper / Initialen splitsen met punt

JAVASCRIPT Code

javascript
function initialsHelper(element) {
  console.log(element);
  const initials = element ? document.getElementById(element) : document.getElementById("voorletters");
  const char = [
    "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
    "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
    "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
    "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"
  ];
  
  // Bijhouden van de vorige waarde om backspace/delete te detecteren
  let previousValue = '';
  let isBackspacing = false;

  // Directe key event listeners voor backspace/delete detectie
  initials?.addEventListener("keydown", (e) => {
    // Detecteer backspace (8) of delete (46) toetsen
    if (e.keyCode === 8 || e.keyCode === 46) {
      isBackspacing = true;
    }
  });

  initials?.addEventListener("keyup", (e) => {
    // Reset de backspace vlag na keyup
    if (e.keyCode === 8 || e.keyCode === 46) {
      isBackspacing = false;
    }
  });

  // Helper functie om initialen juist te formatteren
  const formatInitials = () => {
    // Als gebruiker aan het backspacen is, niets doen
    if (isBackspacing) {
      return;
    }

    // Als de waarde korter is geworden, waarschijnlijk door backspace
    if (initials.value.length < previousValue.length) {
      previousValue = initials.value;
      return;
    }
    
    // Verwijder dubbele punten (..)
    let value = initials.value.replace(/\.{2,}/g, '.');
    
    // Verwijder spaties
    value = value.replace(/\s/g, '');
    
    // Zorg ervoor dat elke letter wordt gevolgd door één punt
    let formatted = '';
    for (let i = 0; i < value.length; i++) {
      if (char.includes(value[i])) {
        // Als het een letter is, voeg hoofdletter toe
        formatted += value[i].toUpperCase();
        
        // Voeg een punt toe als het volgende karakter niet al een punt is
        if (i + 1 >= value.length || value[i + 1] !== '.') {
          formatted += '.';
        }
      } else if (value[i] === '.' && formatted.length > 0 && formatted[formatted.length - 1] !== '.') {
        // Voeg punt toe als laatste karakter nog geen punt is
        formatted += '.';
      }
    }
    
    // Update de waarde alleen als het echt veranderd is
    if (formatted !== initials.value) {
      initials.value = formatted;
    }
    
    previousValue = initials.value;
  };

  // Input event handler
  const handleInput = () => {
    // Wacht een kort moment voor het formatteren
    setTimeout(formatInitials, 10);
  };

  // Gebruik input event voor normale invoer
  initials?.addEventListener("input", handleInput);
  
  // Voor oudere browsers of specifieke apparaten
  initials?.addEventListener("change", handleInput);
  
  // Bewaar initiële waarde
  if (initials) {
    previousValue = initials.value;
  }
}
Bekijk Snippet

Magento versie upgrade

Meer info hier Klik

Voer de volgende commando's uit om Magento te upgraden.

php bin/magento maintenance:enable

composer require magento/product-community-edition=2.3.3 --no-update
composer update
php bin/magento setup:upgrade
php bin/magento setup:di:compile

php bin/magento maintenance:disable
Bekijk Snippet

Magento fout enum na upgrade

Wanneer een upgrade niet werkt in een Magento X Wordpress omgeving die vertaald wordt door PolyLang krijg je de voglende melding:

"Cannot process definition to array for type enum / tinytext"

Ga naar de database tabel: 	wp_icl_translation_status
Zoek entry: 			review_status 

'enum' moet omgezet worden naar 'int'

=============

Cannot process definition to array for type tinytext

Ga naar database tabel: 	wp_comments (Indexen)
Selecteer de volgende Indexen:
comment_author, comment_date, comment_date_gmt

Verander de volgende waarden:	
comment_author 	  -> VARCHAR (lengte: 100)
comment_date 	    -> CURRENT_TIMESTAMP
comment_date_gmt  -> CURRENT_TIMESTAMP
Bekijk Snippet

Magento Productiemodus Cache legen

Staat een Magento in productie modus:

Modus checken:
php bin/magento deploy:mode:show


Na een wijziging geen ca:cl, maar:
rm -rf var/view_preprocessed/* rm -rf pub/static/frontend/* 
php bin/magento cache:clean
php bin/magento setup:static-content:deploy -t Gladior/Avinter -f -s

Om ook de admin te verversen:
php bin/magento setup:static-content:deploy en_US nl_NL
Bekijk Snippet

Magento App Cache Frontend Pool

Warning: Undefined array key "frontend" in /home/ishoprugnl/ishop.rug.nl/vendor/magento/framework/App/Cache/Frontend/Pool.php on line 90
 
oplossing in app/etc/env.php 
'cache' => [     
'frontend' => [
 
        ]
    ]
];
Bekijk Snippet

Magento API aanroepen via Postman

Eerst token verkrijgen via POST
https://webshop.stulz-benelux.com/rest/V1/integration/admin/token

In Header tab:
Content-Type : application/json

In Body tab:
selecteer 'raw'
{
    "username" : ""
    "password" : ""
}

Send.
Je krijgt nu een token.

======

Maak een nieuwe GET - request aan
In Authorization > Selecteer Bearer Token
Viel hier de zojuist verkregen token in. (zonder aanhalingstekens)

https://webshop.stulz-benelux.com/rest/default/V1/orders/1  of
https://webshop.stulz-benelux.com/rest/default/V1/customers/1
Bekijk Snippet

Magento attirbuut waarde gevuld, extra CSS class toevoegen

PHP Code

php
<?php foreach($_productCollection as $_product) : 
  $palletPrice = $_product->getData('pallet_prijs');
  $cssExtraClass = $palletPrice ? 'pallet-actie' : '';
?>

<li class="item <?= $cssExtraClass;?>">List-item</li>
Bekijk Snippet

Kruisje unicode

CSS Code

css
&:before {
   content: "\2715";
}
Bekijk Snippet

Javascript inladen in Magento module

JAVASCRIPT Code

javascript
<script type="text/x-magento-init">
    {
        "*": {
            "oci_post": {}
        }
    }
</script>
JavaScript inline inladen in module.
https://gitlab.indenty.nl/magento/OciPunchout/-/commit/b1a965af837ea9586e6fccb42b12f366f08ae91f

====================

In je .phtml bestand:

Hetgeen in de JS Snippet

====================

Dan in je require-config.js

var config = {
    map: {
        '*': {
            'oci_post': 'Gladior_OciPunchout/js/post'
        }
    }
};

====================

En dan in je module:

define([] , function ($) {
    return function () {
        document.getElementById("oci-submitbutton").style.display="none";
        document.getElementById("oci-checkout").submit()
    };
});
Bekijk Snippet

IP adres blokkeren op server (triplehosting)

Als server er uit vlieg, kun je kijken naar een IP adres met heel veel connecties.

0. Zet httpd uit onder > Service Monitor
1. Message System
2. Kijk naar een ticket met titel 'Warning: The system load average is 47.31'
3. Kijk naar een IP adres met veel connecties
4. Wil je hem blokkeren? 
5. Ga in Direct Admin naar > Extra Featues > ConfigServer Security & Firewall
6. Quick Deny > Vul hier het IP adres in met reden
7. Start nu pas de httpd weer op (anders werkt de block niet)
Bekijk Snippet

Icon-before (1cap hoogte)

CSS Code

css
.icon-before {
	height: 1cap;
	aspect-ratio: 1;
	background-repeat: no-repeat;
	background-position: center;
	.d-block;
	.transition();
	
	&.share {
    	.share(@escaped_secondary_clr);
		&:hover,
		&:focus {
		  .share(@escaped_main_clr);
		}
  	}
	&.facebook {
    .facebook(@escaped_secondary_clr);

    &:hover,
    &:focus {
      .facebook(@escaped_main_clr);
    }
  }
}
Bekijk Snippet

Hostfile aanpassen voor IP manipulatie

Open Kladblok als administrator.
In Windows 10 the hosts file is located at:

C:\Windows\System32\Drivers\etc\hosts
Bekijk Snippet

Google Tag Manager (GTM) inladen optimalisatie

JAVASCRIPT Code

javascript
<script>
function loadGTM() {
    // GTM code hier
}

const events = ['scroll', 'mousemove', 'click', 'touchstart'];

function interaction() {
    events.forEach(event => window.removeEventListener(event, interaction));
    clearTimeout(timeout);
    loadGTM();
}

const timeout = setTimeout(loadGTM, 5000);
events.forEach(event => window.addEventListener(event, interaction));
</script>
Bekijk Snippet

Styling van filters (checkbox)

CSS Code

css
label.checkbox {
    padding-left: 1em;
    &:before {
        position: absolute;
        transition: all 0.2s ease-in-out;
        content: '';
        height: 0.75em;
        width: 0.75em;
        left: 0;
        top: 50%;
        translate: 0 -50%;
        background-color: var(--grey);
        border: 1px solid var(--yellow);
        border-radius: 50%;
    }
}
input[type="checkbox"] {
    height: 0;
    width: 0;
    &:checked {
        &~label {
            &:before {
                background-color: var(--yellow);
                border: 1px solid var(--yellow);      
            }
        }
    }
}
Bekijk Snippet

Email validatie (AFAS) Regex

Email check regex
OUD: pattern="[^@\s]+@[^@\s]+\.[^@\s]{2,}


Hij checkt nu op minimaal 2 karakters achter de .

dus a@a.a is fout
maar a@a.aa is goed



=== UPDATE 12-9-2023 ===
pattern="[A-Za-z0-9!#$&.+-=_]+@[A-Za-z0-9.-]+\.[^@\s]{2,}"

Hier wordt extra gecheckt op dat het stuk voor het @-teken enkel de karakters !#$&.+-=_ bevat.
Een ~ wordt bijv. niet geaccepteerd door AFAS
Bekijk Snippet

ElasticSearch instellingen (locatie)

Elastic Search instellen:

Stores > Configuration > Catalog > Catalog > Catalog Search
Bekijk Snippet

Cookiebot styling met LESS CSS variabelen

CSS Code

css
@main_clr #748375;

@secondary_clr #EEF0EC;

@escaped_secondary_clr escape(~@{secondary_clr});

@root_font_family Avenir, Avenir, Sans-serif;

@grey_clr #525157;

@light_clr #FFF;

@escaped_light_clr escape(~@{light_clr});

 

.bg-properties(@size contain) {

  background-repeat no-repeat;

  background-position center;

  background-size @size;

}

 Voor Less compiler httpslesscss.orgless-preview 

 

 

 COOKIEBOT STYLING 
 
body {
  #CybotCookiebotDialogHeader {
    background-color @main_clr;
  }
  #CybotCookiebotDialogPoweredbyLink {
    width 250px;
        background-position center left;
    height 48px;
    .bg-properties();
    background-image url('wp-contentuploads202205WRANGU-logo-white_text-Use-on-dark-background-only.png');
  }
  #CybotCookiebotDialogPoweredbyImage {
    opacity 0;
  }
  #CybotCookiebotDialog {
    border-radius 1em;
    outline 1000vh solid fadeout(@main_clr, 80%);
    .CybotCookiebotScrollContainer {
      border unset;
    }
    .CookieCard {
      button {
        &after {
          content unset;
        }
      }
    }
  }
  #CybotCookiebotDialogPoweredbyCybot {
        path {
      fill white;
        }
  }
  #CybotCookiebotDialog,
  #CybotCookiebotDialogBodyUnderlay {
     {
      font-family @root_font_family;
    }
  }
  #CybotCookiebotDialogNav {
    .CybotCookiebotDialogNavItemLink {
      &hover {
        color @secondary_clr;
      }
      &.CybotCookiebotDialogActive {
        color @main_clr;
        border-color @main_clr;
      }
    }
  }
  #CybotCookiebotDialogHeader {
    border-color @grey_clr;
  }
  #CybotCookiebotDialogNav {
    border-color @grey_clr;
  }
  #CybotCookiebotDialogTabContent {
    .CybotCookiebotDialogBodyLevelButtonSlider {
      background-color @grey_clr;
    }
    input {
      &checked {
        &+.CybotCookiebotDialogBodyLevelButtonSlider {
          background-color @secondary_clr;
        }
      }
    }
  }
  #CybotCookiebotDialogFooter {
    #CybotCookiebotDialogBodyButtonAccept, #CybotCookiebotDialogBodyLevelButtonAccept,
    #CybotCookiebotDialogBodyLevelButtonLevelOptinAllowAll {
      .bg-secondary;
      border-color @secondary_clr;
      &after {
        background-image url(dataimagesvg+xml,%3Csvg xmlns='httpwww.w3.org2000svg' width='11.708' height='11.708' viewBox='0 0 11.708 11.708'%3E%3Cpath id='Path_3565' data-name='Path 3565' d='M5.854,0,4.79,1.064,8.819,5.094H0V6.614H8.819L4.79,10.644l1.064,1.064,5.854-5.854Z' fill='@{escaped_light_clr}'%3E%3Csvg%3E%0A);
      }
    }
     #CybotCookiebotDialogBodyLevelButtonCustomize,#CybotCookiebotDialogBodyLevelButtonLevelOptinAllowallSelection,
     .CybotCookiebotDialogBodyButton {
      border-color @light_clr;
      background-color @light_clr;
      color @secondary_clr;
      &after {
        background-image url(dataimagesvg+xml,%3Csvg xmlns='httpwww.w3.org2000svg' width='11.708' height='11.708' viewBox='0 0 11.708 11.708'%3E%3Cpath id='Path_3565' data-name='Path 3565' d='M5.854,0,4.79,1.064,8.819,5.094H0V6.614H8.819L4.79,10.644l1.064,1.064,5.854-5.854Z' fill='@{escaped_secondary_clr}'%3E%3Csvg%3E%0A);
      }
      .CybotCookiebotDialogArrow {
        display none;
      }
    }
    .CybotCookiebotDialogBodyButton {
      padding .75em 3em .75em 1.5em;
      border-radius 1em;
      border-width 1px;
      font-weight 700;
    }
    position relative;
    margin-top 3em;
  }
}
 
 END COOKIEBOT STYLING 
Bekijk Snippet

Cookiebot styling met CSS variabelen

CSS Code

css
<style>
  /* COOKIEBOT STYLING */
:root {
  --main_clr: #714B94;
  --secondary_clr: #93C01A;
  --grey_clr: #abb8c3;
  --light_clr: #fff;
  }
  
body {
    #CybotCookiebotDialogHeader {
        background-color: var(--main_clr);
    }

    #CybotCookiebotDialogPoweredbyLink {
        width: 250px;
        background-position: center left !important;
        height: 48px;
        background-repeat: no-repeat;
        background-size: contain;
        background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='168.601' height='49.74' viewBox='0 0 168.601 49.74'%3E%3Cpath d='M62.316,32.456c4.552,0,6.419.526,8.4,2.278C72.7,36.6,73.7,39.4,73.7,43.37V60.878H65.874V42.493c0-2.857-1.165-3.965-4.025-3.965H55.08V60.878H47.257V32.456Z' transform='translate(-31.736 -21.796)' fill='%23033b40'%3E%3C/path%3E%3Cpath d='M316.635,32.456h7.822V60.878h-15.35c-4.552,0-6.422-.526-8.4-2.278-1.983-1.867-2.976-4.667-2.976-8.636V32.456h7.819V50.722c0,2.569,1.4,4.085,3.738,4.085h7.352Z' transform='translate(-199.944 -21.796)' fill='%2393c01a'%3E%3C/path%3E%3Cpath d='M424.207,38.521h-13.89c-3.5,0-4.32.467-4.32,2.509a2.511,2.511,0,0,0,1.284,2.334c.526.351,1.4.467,3.794.467h4.611c6.243,0,9.805,2.92,9.805,8.114,0,3.387-1.811,6.77-4.376,8.054-1.169.642-3.151.874-6.6.874H398.354V54.8h13.89a18.85,18.85,0,0,0,4.2-.235,2.055,2.055,0,0,0,1.4-2.158,2.656,2.656,0,0,0-1.341-2.449c-.586-.295-1.4-.351-3.387-.351h-4.783c-3.622,0-5.313-.411-7-1.636a8.737,8.737,0,0,1-3.387-7.236,8.688,8.688,0,0,1,3.1-6.829c1.341-1.049,3.092-1.46,6.478-1.46h16.691Z' transform='translate(-267.246 -21.792)' fill='%2393c01a'%3E%3C/path%3E%3Cpath d='M147.838,78.8l7.107-7.107h17.765a10.658,10.658,0,0,0,0-21.316H165.6a3.553,3.553,0,0,1,0-7.107h7.107a17.764,17.764,0,0,1,0,35.529Z' transform='translate(-99.283 -29.059)' fill='%2393c01a'%3E%3C/path%3E%3Cpath d='M165.6,7.107a10.658,10.658,0,0,0,0,21.316h7.107a3.553,3.553,0,0,1,0,7.107H165.6A17.765,17.765,0,0,1,165.6,0h24.871l-7.107,7.107Z' transform='translate(-99.282 0)' fill='%23033b40'%3E%3C/path%3E%3Crect width='7.819' height='7.107' transform='translate(0 0.002)' fill='%23033b40'%3E%3C/rect%3E%3Crect width='7.819' height='28.422' transform='translate(0 10.66)' fill='%23033b40'%3E%3C/rect%3E%3Cpath d='M497.932,103.379c.432,0,.589-.109.589-.407s-.14-.4-.589-.4h-.821v.807Zm-.821,1.677h-.635v-2.98h1.442a1.762,1.762,0,0,1,.916.165l.375.681a.792.792,0,0,1-.579.8c.351.133.5.383.526.863a.865.865,0,0,0,.1.47h-.691l-.021-.056-.032-.158-.025-.224c-.056-.565-.291-.744-.958-.744h-.421Zm-1.821-1.491a2.48,2.48,0,1,0,2.52-2.52,2.508,2.508,0,0,0-2.52,2.52m5.541,0a3.062,3.062,0,1,1-3.011-3.039,3.044,3.044,0,0,1,3.011,3.039' transform='translate(-332.23 -67.51)' fill='%23033b40'%3E%3C/path%3E%3C/svg%3E");
    }

    #CybotCookiebotDialogPoweredbyImage {
        opacity: 0;
    }

    #CybotCookiebotDialog {
        border-radius: 1em;

        .CybotCookiebotScrollContainer {
            border: unset;
        }

        .CookieCard {
            button {
                &::after {
                    content: unset;
                }
            }
        }
    }

    #CybotCookiebotDialogPoweredbyCybot {
        path {
            fill: white;
        }
    }

    #CybotCookiebotDialog,
    #CybotCookiebotDialogBodyUnderlay {
        * {
            font-family: "archivo", "Open Sans", Helvetica, Arial, sans-serif;
        }
    }

    #CybotCookiebotDialogNav {
        .CybotCookiebotDialogNavItemLink {
            &:hover {
                color: var(--secondary_clr);
            }

            &.CybotCookiebotDialogActive {
                color: var(--main_clr);
                border-color: var(--main_clr);
            }
        }
    }

    #CybotCookiebotDialogHeader {
        border-color: var(--grey_clr);
    }

    #CybotCookiebotDialogNav {
        border-color: var(--grey_clr);
    }

    #CybotCookiebotDialogTabContent {
        .CybotCookiebotDialogBodyLevelButtonSlider {
            background-color: var(--grey_clr);
        }

        input {
            &:checked {
                &+.CybotCookiebotDialogBodyLevelButtonSlider {
                    background-color: var(--secondary_clr);
                }
            }
        }
    }

    #CybotCookiebotDialogFooter {

        #CybotCookiebotDialogBodyButtonAccept,
        #CybotCookiebotDialogBodyLevelButtonAccept,
        #CybotCookiebotDialogBodyLevelButtonLevelOptinAllowAll {
            background-color: var(--secondary_clr);
            border-color: var(--secondary_clr);
        }

        #CybotCookiebotDialogBodyLevelButtonCustomize,
        #CybotCookiebotDialogBodyLevelButtonLevelOptinAllowallSelection,
        .CybotCookiebotDialogBodyButton {
            border-color: var(--light_clr);
            background-color: var(--light_clr);
            color: var(--secondary_clr);

            .CybotCookiebotDialogArrow {
                display: none;
            }
        }

        .CybotCookiebotDialogBodyButton {
            padding: .75em 1.5em;
            border-radius: 1em;
            border-width: 1px;
            font-weight: 700;
        }

        position: relative;
        margin-top: 3em;
    }
}

/* END COOKIEBOT STYLING */
</style>
Bekijk Snippet

Composer forceren

Als een normale composer update niet werkt, kun je via een hogere PHP versie updaten.
Dit doe je zo:

which composer
- /usr/local/bin/composer

En dan:
php8.1 /usr/local/bin/composer update amasty/module-abandoned-cart-email-subscription-package
Bekijk Snippet

Block ophalen uit Magento

PHP Code

php
$submenu = $this->getLayout()->createBlock('Magento\Cms\Block\Block')->setBlockId('default-submenu')->toHtml();
Bekijk Snippet

Afbeelding inladen

Een snippet die laat zien hoe je een goede afbeelding plaatst die ook de mindere grote variant toont op kleine schermen.

HTML Code

html
<picture>
  <source media="(max-width: 768px)" srcset="<?= $afbeelding['sizes']['medium'];?>">
  <img src="<?= $afbeelding['url']; ?>" alt="<?= $afbeelding['alt'];?>" aria-label="" loading="lazy" width="300" height="150">
</picture>

CSS Code

css
.image {
  .cover-img;
}
Bekijk Snippet