From 5e330d5910cadc15db0db85c7eec83f0bd014ae1 Mon Sep 17 00:00:00 2001 From: bernardhanna Date: Wed, 8 Jul 2026 09:29:31 +0100 Subject: [PATCH] edit to online course page --- app/OnlineCoursesPage.php | 4 +- ...0_clear_online_courses_placeholder_cta.php | 27 ++ .../seeders/EditableStaticPagesSeeder.php | 4 +- docs/getting-help-public.md | 67 ++++ resources/views/online-courses.blade.php | 6 +- .../fix-blog-chevron-position-remove-arrow.sh | 91 +++++ scripts/fix-blog-footer-js-stray-brace.sh | 19 + scripts/fix-blog-footer-script-clean.sh | 190 ++++++++++ scripts/fix-blog-mobile-menu-clickability.sh | 174 +++++++++ scripts/fix-blog-mobile-menu-js-syntax.sh | 92 +++++ scripts/fix-blog-mobile-menu-laravel-match.sh | 330 ++++++++++++++++++ scripts/fix-blog-mobile-menu-snappy.sh | 178 ++++++++++ scripts/fix-blog-mobile-menu-v2.sh | 131 +++++++ scripts/patch-blog-menu-share-stories-icon.sh | 22 +- .../patch-blog-mobile-menu-parent-links.sh | 106 ++++++ 15 files changed, 1416 insertions(+), 25 deletions(-) create mode 100644 database/migrations/2026_07_08_090000_clear_online_courses_placeholder_cta.php create mode 100644 docs/getting-help-public.md create mode 100755 scripts/fix-blog-chevron-position-remove-arrow.sh create mode 100755 scripts/fix-blog-footer-js-stray-brace.sh create mode 100755 scripts/fix-blog-footer-script-clean.sh create mode 100755 scripts/fix-blog-mobile-menu-clickability.sh create mode 100755 scripts/fix-blog-mobile-menu-js-syntax.sh create mode 100755 scripts/fix-blog-mobile-menu-laravel-match.sh create mode 100755 scripts/fix-blog-mobile-menu-snappy.sh create mode 100755 scripts/fix-blog-mobile-menu-v2.sh create mode 100755 scripts/patch-blog-mobile-menu-parent-links.sh diff --git a/app/OnlineCoursesPage.php b/app/OnlineCoursesPage.php index e21f87f2a..aaffb2b27 100644 --- a/app/OnlineCoursesPage.php +++ b/app/OnlineCoursesPage.php @@ -30,8 +30,8 @@ public static function config(): self 'use_dynamic_content' => false, 'hero_title' => 'Online Courses', 'hero_text' => __('online-courses.online-courses-text'), - 'hero_cta_text' => 'Optional secondary CTA introduction to online courses', - 'hero_cta_link' => '/', + 'hero_cta_text' => null, + 'hero_cta_link' => null, 'intro_title' => 'EU Code Week Online Courses', 'intro_text_1' => __('online-courses.online-courses-sub-text1'), 'intro_text_2' => __('online-courses.online-courses-sub-text2'), diff --git a/database/migrations/2026_07_08_090000_clear_online_courses_placeholder_cta.php b/database/migrations/2026_07_08_090000_clear_online_courses_placeholder_cta.php new file mode 100644 index 000000000..97da1854d --- /dev/null +++ b/database/migrations/2026_07_08_090000_clear_online_courses_placeholder_cta.php @@ -0,0 +1,27 @@ +where('hero_cta_text', 'Optional secondary CTA introduction to online courses') + ->update([ + 'hero_cta_text' => null, + 'hero_cta_link' => null, + ]); + } + + public function down(): void + { + // Placeholder CTA intentionally removed; no restore. + } +}; diff --git a/database/seeders/EditableStaticPagesSeeder.php b/database/seeders/EditableStaticPagesSeeder.php index 3234d0fd3..2d898b1dc 100644 --- a/database/seeders/EditableStaticPagesSeeder.php +++ b/database/seeders/EditableStaticPagesSeeder.php @@ -32,8 +32,8 @@ private function seedOnlineCoursesPage(): void 'use_dynamic_content' => true, 'hero_title' => 'Online Courses', 'hero_text' => __('online-courses.online-courses-text'), - 'hero_cta_text' => 'Optional secondary CTA introduction to online courses', - 'hero_cta_link' => '/', + 'hero_cta_text' => null, + 'hero_cta_link' => null, 'intro_title' => 'EU Code Week Online Courses', 'intro_text_1' => __('online-courses.online-courses-sub-text1'), 'intro_text_2' => __('online-courses.online-courses-sub-text2'), diff --git a/docs/getting-help-public.md b/docs/getting-help-public.md new file mode 100644 index 000000000..04b6021a8 --- /dev/null +++ b/docs/getting-help-public.md @@ -0,0 +1,67 @@ +# Getting help with CodeWeek + +This page explains how to get help with your **CodeWeek** account, activities, or +certificates. It's for teachers, organisers, and participants. + +--- + +## How to ask for help + +The fastest way to reach the CodeWeek team is the official contact channel: + +- **Contact form / email:** use the contact option on the CodeWeek website at + [codeweek.eu](https://codeweek.eu) + +When you get in touch, please include: + +1. **The email address** you registered with on CodeWeek +2. **Your CodeWeek 4all code** (if your question is about an activity or certificate) +3. **A clear description** of the problem and what you'd like help with + +Including these details up front helps the team look into your request without +needing to email back and forth. + +--- + +## Common topics + +### Signing in / account access +If you can't sign in, double-check you're using the email address you originally +registered with. If you still have trouble, contact the team with that email address +and a short description of what happens when you try to log in. + +### Certificates +Participation and Certificate of Excellence availability depends on the activities +registered under your 4all code. You can find and download available certificates +after signing in: + +- [codeweek.eu/certificates](https://codeweek.eu/certificates) + +If you believe a certificate is missing, include your **registered email** and your +**4all code** when you contact the team so they can check your specific case. + +### Activities and events +For questions about adding, editing, or finding activities, contact the team with your +registered email and 4all code and describe what you're trying to do. + +--- + +## What to expect + +- The team reviews requests and replies by email. +- For account or data changes, the team verifies the details before making any change — + so please use the **same email address** associated with your CodeWeek account where + possible. +- Response times vary with demand, especially around the annual CodeWeek period. + +--- + +## Privacy + +Only share the information needed to resolve your request (typically your registered +email and 4all code). Please don't include passwords or other sensitive personal data +in your message. + +--- + +*For more about CodeWeek, visit [codeweek.eu](https://codeweek.eu).* diff --git a/resources/views/online-courses.blade.php b/resources/views/online-courses.blade.php index da3bf6b27..080d1d92c 100644 --- a/resources/views/online-courses.blade.php +++ b/resources/views/online-courses.blade.php @@ -38,12 +38,14 @@ @lang('online-courses.online-courses-text') @endif

+ @if($dynamic && $page->hero_cta_text && $page->hero_cta_link) - - {{ $dynamic && $page->hero_cta_text ? $page->hero_cta_text : 'Optional secondary CTA introduction to online courses' }} + + {{ $page->hero_cta_text }} + @endif
diff --git a/scripts/fix-blog-chevron-position-remove-arrow.sh b/scripts/fix-blog-chevron-position-remove-arrow.sh new file mode 100755 index 000000000..57b514a2e --- /dev/null +++ b/scripts/fix-blog-chevron-position-remove-arrow.sh @@ -0,0 +1,91 @@ +#!/usr/bin/env bash +# Chevron position (right: -.8rem) + remove arrow.png from Share your stories submenu. +set -euo pipefail + +THEME="${THEME:-/var/www/html/blog/wp-content/themes/eucodewe-1389}" +WP_ROOT="${WP_ROOT:-/var/www/html/blog}" +SHARE_TEXT="Share your stories" + +[[ -d "$THEME" ]] || { echo "Theme not found: $THEME"; exit 1; } +[[ -d "$WP_ROOT" ]] || { echo "WP_ROOT not found: $WP_ROOT"; exit 1; } + +cd "$THEME" +chmod u+w new.css 2>/dev/null || true + +python3 <<'PY' +from pathlib import Path +import re + +css = Path("new.css") +text = css.read_text() + +# Don't hide chevron on last top-level item when it has children (Blog) +text = text.replace( + ".header__menu nav > ul > li:last-child a:after {\n display: none;\n}", + ".header__menu nav > ul > li:last-child:not(.menu-item-has-children) a:after {\n display: none;\n}", +) + +chevron_sel = ( + "li.menu-item.menu-item-type-custom.menu-item-object-custom.menu-item-has-children a:after" +) +chevron_block = """li.menu-item.menu-item-type-custom.menu-item-object-custom.menu-item-has-children a:after { + content: ""; + width: 10px; + height: 10px; + background: url(https://codeweek.eu/blog/wp-content/uploads/2025/01/Vector-6.svg); + display: block; + background-size: contain; + background-repeat: no-repeat; + background-position: center; + transition: 0.3s; + right: -.8rem; +}""" + +pattern = re.compile( + r"li\.menu-item\.menu-item-type-custom\.menu-item-object-custom\.menu-item-has-children a:after\s*\{[^}]+\}", + re.S, +) +if pattern.search(text): + text = pattern.sub(chevron_block, text, count=1) + print("Updated chevron rule with right: -.8rem") +else: + print("WARNING: chevron rule not found — append manually if needed") + +# Remove submenu arrow layout overrides (no longer needed) +for pat in [ + r"\n/\* Blog dropdown: Share your stories.*?(?=\n/\* |\Z)", + r"\n/\* Blog nav: restore dropdown chevron.*?(?=\n/\* |\Z)", + r"\n/\* Blog nav: chevron on Blog.*?(?=\n/\* |\Z)", +]: + text = re.sub(pat, "\n", text, flags=re.S) + +css.write_text(text) +print("Patched new.css") +PY + +echo "--- chevron rule ---" +grep -n -A12 "menu-item-has-children a:after" new.css | head -15 + +cd "$WP_ROOT" +command -v wp >/dev/null 2>&1 || { echo "wp-cli required for menu arrow removal"; exit 1; } + +SHARE_ID="" +for menu in primary header main "Menu 1" 2; do + SHARE_ID=$(wp menu item list "$menu" --fields=db_id,title --format=csv --allow-root 2>/dev/null \ + | awk -F, -v t="$SHARE_TEXT" '$2 ~ t {print $1; exit}') || true + [[ -n "${SHARE_ID:-}" ]] && break +done +SHARE_ID="${SHARE_ID:-8142}" + +wp eval --allow-root " +\$id = (int) ${SHARE_ID}; +delete_post_meta(\$id, 'rt-wp-menu-custom-fields'); +delete_transient('rt-wp-menu-custom-fields-' . \$id); +echo \"Removed arrow custom HTML from menu item \$id\n\"; +" + +wp cache flush --allow-root 2>/dev/null | tail -1 || true +for svc in php8.3-fpm php8.2-fpm php8.1-fpm php8.0-fpm; do + systemctl is-active --quiet "$svc" 2>/dev/null && systemctl reload "$svc" && echo "Reloaded $svc" && break +done +echo "Done. Hard-refresh https://codeweek.eu/blog/" diff --git a/scripts/fix-blog-footer-js-stray-brace.sh b/scripts/fix-blog-footer-js-stray-brace.sh new file mode 100755 index 000000000..77749e391 --- /dev/null +++ b/scripts/fix-blog-footer-js-stray-brace.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +# Remove stray }); breaking blog footer JS. +set -euo pipefail +WP_ROOT="${WP_ROOT:-/var/www/html/blog}" +cd "$WP_ROOT" +wp eval --allow-root ' +$settings = get_option("auhfc_settings_sitewide"); +$footer = $settings["footer"] ?? ""; +$footer = str_replace( + "$(\".mobile-nav .menu-menu-1-container\").append(actionButtons);\n});\n\t});", + "$(\".mobile-nav .menu-menu-1-container\").append(actionButtons);\n});", + $footer +); +$settings["footer"] = $footer; +update_option("auhfc_settings_sitewide", $settings); +echo "Removed stray }); from footer script\n"; +' 2>/dev/null | grep -v '^Deprecated:\|^Notice:\|^Warning:\|^PHP Warning:' || true +wp cache flush --allow-root 2>/dev/null | tail -1 || true +echo Done. diff --git a/scripts/fix-blog-footer-script-clean.sh b/scripts/fix-blog-footer-script-clean.sh new file mode 100755 index 000000000..e01e37106 --- /dev/null +++ b/scripts/fix-blog-footer-script-clean.sh @@ -0,0 +1,190 @@ +#!/usr/bin/env bash +# Repair corrupted blog footer script and apply Laravel-style mobile menu JS. +set -euo pipefail + +WP_ROOT="${WP_ROOT:-/var/www/html/blog}" +CACHE_VER="${CACHE_VER:-$(date +%Y%m%d%H%M)}" + +cd "$WP_ROOT" +command -v wp >/dev/null 2>&1 || { echo "wp-cli required"; exit 1; } + +PATCH_PHP="$(mktemp)" +cat > "$PATCH_PHP" <<'PHP' + + $(window).scroll(function(){ + if ($(window).scrollTop() >= 330) { + $('.sticky-btn').addClass('fixed'); + } + else { + $('.sticky-btn').removeClass('fixed'); + } + }); + $(window).scroll(function(){ + if ($(window).scrollTop() >= 330) { + $('.header').addClass('header-fixed'); + } + else { + $('.header').removeClass('header-fixed'); + } + }); + $(document).ready(function () { + var actionButtons = ` +
+ Login / Sign up +
+ `; + $(".mobile-nav .menu-menu-1-container").append(actionButtons); + }); + +(function () { + var ARROW_SRC = "https://codeweek.eu/images/chevron-down-icon.svg"; + + function getMenuLabel(menuItem) { + var label = ""; + menuItem.childNodes.forEach(function (node) { + if (node.nodeType === Node.TEXT_NODE) { + label += node.textContent; + } + }); + return label.replace(/\s+/g, " ").trim(); + } + + function injectMobileParentLinks(menuItem) { + var href = menuItem.getAttribute("href") || ""; + var isVoidHref = !href || href === "#" || href.indexOf("javascript:") === 0; + if (isVoidHref) { + return; + } + + var parentItem = menuItem.parentElement; + var submenu = parentItem.querySelector(":scope > .sub-menu"); + if (!submenu || submenu.querySelector(".mobile-parent-link")) { + return; + } + + var li = document.createElement("li"); + li.className = "menu-item mobile-parent-link menu-item-type-custom menu-item-object-custom"; + var link = document.createElement("a"); + link.href = href; + link.textContent = getMenuLabel(menuItem) || menuItem.textContent.replace(/\s+/g, " ").trim(); + li.appendChild(link); + submenu.insertBefore(li, submenu.firstChild); + } + + function syncMenuSectionOpen() { + var menuToggle = document.getElementById("menuToggle"); + if (!menuToggle) { + return; + } + menuToggle.classList.toggle( + "menu-section-open", + !!menuToggle.querySelector(".menu-wrapper > .menu-item-has-children.active") + ); + } + + function toggleItem(parentItem) { + var willOpen = !parentItem.classList.contains("active"); + var siblings = parentItem.parentElement ? parentItem.parentElement.children : []; + + Array.prototype.forEach.call(siblings, function (sibling) { + if (sibling !== parentItem && sibling.classList && sibling.classList.contains("menu-item-has-children")) { + sibling.classList.remove("active"); + } + }); + + parentItem.classList.toggle("active", willOpen); + syncMenuSectionOpen(); + } + + function initBlogMobileMenu() { + var toggle = document.querySelector("#menuToggle input, #menuToggleCheckbox"); + if (toggle) { + toggle.addEventListener("change", function () { + document.body.style.overflow = this.checked ? "hidden" : ""; + if (!this.checked) { + document.querySelectorAll("#menuToggle .menu-item-has-children.active").forEach(function (el) { + el.classList.remove("active"); + }); + syncMenuSectionOpen(); + } + }); + } + + document.querySelectorAll("#menuToggle .menu-item-has-children > a").forEach(function (menuItem) { + if (!menuItem.querySelector(".arrow-icon")) { + var arrow = document.createElement("img"); + arrow.className = "arrow-icon"; + arrow.src = ARROW_SRC; + arrow.alt = ""; + arrow.setAttribute("aria-hidden", "true"); + menuItem.appendChild(arrow); + } + + injectMobileParentLinks(menuItem); + + var arrow = menuItem.querySelector(".arrow-icon"); + if (arrow && !arrow.dataset.bound) { + arrow.dataset.bound = "1"; + arrow.addEventListener("click", function (event) { + event.preventDefault(); + event.stopPropagation(); + toggleItem(menuItem.parentElement); + }); + } + + if (menuItem.dataset.bound) { + return; + } + menuItem.dataset.bound = "1"; + + menuItem.addEventListener("click", function (event) { + if (event.target.closest(".arrow-icon")) { + return; + } + + var href = menuItem.getAttribute("href") || ""; + var isVoidHref = !href || href === "#" || href.indexOf("javascript:") === 0; + + if (isVoidHref) { + event.preventDefault(); + toggleItem(menuItem.parentElement); + } + }); + }); + + syncMenuSectionOpen(); + } + + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", initBlogMobileMenu); + } else { + initBlogMobileMenu(); + } +})(); + +FOOTER; + +$settings['footer'] = $footer; +update_option('auhfc_settings_sitewide', $settings); +echo "Rebuilt clean footer script\n"; +PHP + +wp eval-file "$PATCH_PHP" --allow-root 2>/dev/null | grep -E '^(Rebuilt|Success)' || wp eval-file "$PATCH_PHP" --allow-root | tail -3 +rm -f "$PATCH_PHP" + +THEME="$WP_ROOT/wp-content/themes/eucodewe-1389" +for header in header.php header-v2.php; do + [[ -f "$THEME/$header" ]] || continue + sed -i "s|/new.css?v=[0-9]*|/new.css?v=$CACHE_VER|g; s|/new.css' ?>|/new.css?v=$CACHE_VER' ?>|g" "$THEME/$header" +done + +wp cache flush --allow-root 2>/dev/null | tail -1 || true +echo "Done. CSS v=$CACHE_VER" diff --git a/scripts/fix-blog-mobile-menu-clickability.sh b/scripts/fix-blog-mobile-menu-clickability.sh new file mode 100755 index 000000000..090766146 --- /dev/null +++ b/scripts/fix-blog-mobile-menu-clickability.sh @@ -0,0 +1,174 @@ +#!/usr/bin/env bash +# Fix blog mobile menu: hide overlapping promo, scope JS to .mobile-nav, repair footer script. +set -euo pipefail + +WP_ROOT="${WP_ROOT:-/var/www/html/blog}" +THEME="${THEME:-$WP_ROOT/wp-content/themes/eucodewe-1389}" + +[[ -d "$WP_ROOT" ]] || { echo "WP_ROOT not found"; exit 1; } +[[ -f "$THEME/new.css" ]] || { echo "Theme new.css not found"; exit 1; } +command -v wp >/dev/null 2>&1 || { echo "wp-cli required"; exit 1; } + +cd "$THEME" +chmod u+w new.css 2>/dev/null || true + +python3 <<'PY' +from pathlib import Path +import re + +css = Path("new.css") +text = css.read_text() +marker = "/* Mobile nav clickability fix */" +block = """ +/* Mobile nav clickability fix */ +@media (max-width: 1080px) { + .header__menu.menu-header { + display: none !important; + pointer-events: none !important; + } + + .mobile-nav li.box-featured, + .mobile-nav li.rt-custom-menu-field-item, + .mobile-nav #menu-item-5842, + .mobile-nav .rt-wp-menu-custom-fields-wrapper { + display: none !important; + pointer-events: none !important; + max-height: 0 !important; + overflow: hidden !important; + margin: 0 !important; + padding: 0 !important; + min-height: 0 !important; + } + + .mobile-nav .menu-item-has-children:not(.active) > .sub-menu { + max-height: 0 !important; + overflow: hidden !important; + visibility: hidden !important; + pointer-events: none !important; + padding-top: 0 !important; + } + + .mobile-nav .menu-item-has-children.active > .sub-menu { + max-height: none !important; + overflow: visible !important; + visibility: visible !important; + pointer-events: auto !important; + } + + .mobile-nav nav.menu-menu-1-container a { + display: block; + position: relative; + z-index: 5; + pointer-events: auto; + } +} +""" + +if marker in text: + text = re.sub(r"/\* Mobile nav clickability fix \*/[\s\S]*?(?=\n/\* |\Z)", block.strip() + "\n", text, count=1) +else: + text = text.rstrip() + "\n\n" + block.strip() + "\n" + +css.write_text(text) +print("Patched new.css") +PY + +cd "$WP_ROOT" + +PATCH_PHP="$(mktemp)" +cat > "$PATCH_PHP" <<'PHP' + a").forEach(function (menuItem) { + menuItem.addEventListener("click", function (event) { + var href = menuItem.getAttribute("href") || ""; + var isVoidHref = !href || href === "#" || href.indexOf("javascript:") === 0; + var rect = menuItem.getBoundingClientRect(); + var clickedChevron = event.clientX >= rect.right - 40; + + if (!isVoidHref && !clickedChevron) { + return; + } + + event.preventDefault(); + var parentItem = menuItem.parentElement; + var willOpen = !parentItem.classList.contains("active"); + + document.querySelectorAll(".mobile-nav nav.menu-menu-1-container .menu-item-has-children").forEach(function (otherItem) { + otherItem.classList.remove("active"); + }); + + if (willOpen) { + parentItem.classList.add("active"); + } + }); +}); +JS; + +$updated = false; +foreach ($settings as $key => $value) { + if (! is_string($value) || strpos($value, 'menuToggle') === false) { + continue; + } + + $value = preg_replace( + '/document\.querySelector\("#menuToggle input"\)\.addEventListener\("change"[\s\S]*?menu-item-has-children > a"\)[\s\S]*?\}\);\s*(?:\}\);\s*)?/m', + '', + $value + ); + $value = preg_replace( + '/document\.querySelectorAll\([\'"]\.?mobile-nav nav\.menu-menu-1-container \.menu-item-has-children > a[\'"]\)[\s\S]*?\}\);\s*(?:\}\);\s*)?/m', + '', + $value + ); + $value = preg_replace( + '/document\.querySelectorAll\([\'"]nav\.menu-menu-1-container \.menu-item-has-children > a[\'"]\)[\s\S]*?\}\);\s*(?:\}\);\s*)?/m', + '', + $value + ); + + $insertBefore = ''; + $pos = strrpos($value, $insertBefore); + if ($pos === false) { + fwrite(STDERR, "ERROR: no in {$key}\n"); + exit(1); + } + + $settings[$key] = substr($value, 0, $pos) . trim($mobileJs) . "\n" . substr($value, $pos); + $updated = true; + echo "Rebuilt scoped mobile menu JS in auhfc_settings_sitewide[{$key}]\n"; + break; +} + +if (! $updated) { + fwrite(STDERR, "ERROR: footer script not found\n"); + exit(1); +} + +update_option('auhfc_settings_sitewide', $settings); +PHP + +wp eval-file "$PATCH_PHP" --allow-root 2>/dev/null | grep -E '^(Patched|Rebuilt|Success)' || true +rm -f "$PATCH_PHP" + +wp cache flush --allow-root 2>/dev/null | tail -1 || true +for svc in php8.3-fpm php8.2-fpm php8.1-fpm php8.0-fpm; do + if systemctl is-active --quiet "$svc" 2>/dev/null; then + systemctl reload "$svc" + echo "Reloaded $svc" + break + fi +done + +echo "Done." diff --git a/scripts/fix-blog-mobile-menu-js-syntax.sh b/scripts/fix-blog-mobile-menu-js-syntax.sh new file mode 100755 index 000000000..3eced6862 --- /dev/null +++ b/scripts/fix-blog-mobile-menu-js-syntax.sh @@ -0,0 +1,92 @@ +#!/usr/bin/env bash +# Repair blog mobile menu footer JS (broken change listener + menu handler). +set -euo pipefail + +WP_ROOT="${WP_ROOT:-/var/www/html/blog}" +[[ -d "$WP_ROOT" ]] || { echo "WP_ROOT not found"; exit 1; } +command -v wp >/dev/null 2>&1 || { echo "wp-cli required"; exit 1; } + +cd "$WP_ROOT" + +PATCH_PHP="$(mktemp)" +cat > "$PATCH_PHP" <<'PHP' + a").forEach(function (menuItem) { + menuItem.addEventListener("click", function (event) { + var href = menuItem.getAttribute("href") || ""; + var isVoidHref = !href || href === "#" || href.indexOf("javascript:") === 0; + var rect = menuItem.getBoundingClientRect(); + var clickedChevron = event.clientX >= rect.right - 40; + + if (!isVoidHref && !clickedChevron) { + return; + } + + event.preventDefault(); + var parentItem = menuItem.parentElement; + parentItem.classList.toggle("active"); + + document.querySelectorAll("nav.menu-menu-1-container .menu-item-has-children").forEach(function (otherItem) { + if (otherItem !== parentItem) { + otherItem.classList.remove("active"); + } + }); + }); +}); +JS; + +$updated = false; +foreach ($settings as $key => $value) { + if (! is_string($value) || strpos($value, 'menuToggle') === false) { + continue; + } + + // Drop broken mobile-menu block from prior patches. + $value = preg_replace( + '/document\.querySelector\("#menuToggle input"\)\.addEventListener\("change"[\s\S]*?menu-item-has-children > a"\)[\s\S]*?\}\);\s*(?:\}\);\s*)?/m', + '', + $value + ); + $value = preg_replace( + '/document\.querySelectorAll\([\'"]nav\.menu-menu-1-container \.menu-item-has-children > a[\'"]\)[\s\S]*?\}\);\s*(?:\}\);\s*)?/m', + '', + $value + ); + + $insertBefore = ''; + $pos = strrpos($value, $insertBefore); + if ($pos === false) { + fwrite(STDERR, "ERROR: no in {$key}\n"); + exit(1); + } + + $settings[$key] = substr($value, 0, $pos) . trim($mobileBlock) . "\n" . substr($value, $pos); + $updated = true; + echo "Rebuilt mobile menu JS in auhfc_settings_sitewide[{$key}]\n"; + break; +} + +if (! $updated) { + fwrite(STDERR, "ERROR: footer script not found\n"); + exit(1); +} + +update_option('auhfc_settings_sitewide', $settings); +PHP + +wp eval-file "$PATCH_PHP" --allow-root 2>/dev/null | grep -E '^(Rebuilt|Menu|Done|Success)' || true +rm -f "$PATCH_PHP" +wp cache flush --allow-root 2>/dev/null | tail -1 || true +echo "Done." diff --git a/scripts/fix-blog-mobile-menu-laravel-match.sh b/scripts/fix-blog-mobile-menu-laravel-match.sh new file mode 100755 index 000000000..9c6247477 --- /dev/null +++ b/scripts/fix-blog-mobile-menu-laravel-match.sh @@ -0,0 +1,330 @@ +#!/usr/bin/env bash +# Match blog mobile menu to Laravel: inline chevrons, visible submenus, sibling hide. +set -euo pipefail + +WP_ROOT="${WP_ROOT:-/var/www/html/blog}" +THEME="${THEME:-$WP_ROOT/wp-content/themes/eucodewe-1389}" +CACHE_VER="${CACHE_VER:-$(date +%Y%m%d%H%M)}" + +[[ -d "$WP_ROOT" ]] || { echo "WP_ROOT not found"; exit 1; } +command -v wp >/dev/null 2>&1 || { echo "wp-cli required"; exit 1; } + +cd "$THEME" +chmod u+w new.css header.php header-v2.php 2>/dev/null || true + +python3 <<'PY' +from pathlib import Path +import re + +css = Path("new.css") +text = css.read_text() +marker = "/* Mobile nav clickability fix */" +block = """/* Mobile nav clickability fix */ +@media (max-width: 1080px) { + .header__menu.menu-header { + display: none !important; + pointer-events: none !important; + } + + /* Laravel-style parent row: label left, chevron right */ + #menuToggle .menu-item-has-children > a { + display: flex !important; + align-items: center !important; + justify-content: space-between !important; + gap: 8px !important; + width: 100% !important; + font-size: 16px !important; + font-weight: 600 !important; + color: #1C4DA1 !important; + padding: 12px 0 !important; + text-decoration: none !important; + } + + #menuToggle .menu-item-has-children > a::after { + display: none !important; + } + + #menuToggle .menu-item-has-children > a .arrow-icon { + width: 16px; + height: 16px; + flex-shrink: 0; + margin-left: auto; + transition: transform 0.3s ease; + } + + #menuToggle .menu-item-has-children.active > a .arrow-icon { + transform: scaleY(-1); + } + + /* Hide duplicate submenu header rows with WP custom arrow blocks */ + #menuToggle #menu-item-5845, + #menuToggle #menu-item-5847, + #menuToggle #menu-item-5848, + #menuToggle li#menu-item-5842, + #menuToggle li.box-featured, + #menuToggle li.rt-custom-menu-field-item, + #menuToggle .rt-wp-menu-custom-fields-wrapper { + display: none !important; + pointer-events: none !important; + height: 0 !important; + max-height: 0 !important; + overflow: hidden !important; + margin: 0 !important; + padding: 0 !important; + visibility: hidden !important; + } + + /* Laravel: when a section is open, hide other top-level items */ + #menuToggle:has(.menu-wrapper > .menu-item-has-children.active) .menu-wrapper > .menu-item-has-children:not(.active), + #menuToggle:has(.menu-wrapper > .menu-item-has-children.active) .menu-wrapper > .menu-item:not(.menu-item-has-children) { + display: none !important; + } + + /* Collapsed submenus */ + #menuToggle .menu-item-has-children:not(.active) > .sub-menu { + max-height: 0 !important; + overflow: hidden !important; + visibility: hidden !important; + pointer-events: none !important; + padding: 0 !important; + margin: 0 !important; + } + + /* Expanded direct submenu */ + #menuToggle .menu-item-has-children.active > .sub-menu { + max-height: none !important; + overflow: visible !important; + visibility: visible !important; + pointer-events: auto !important; + display: block !important; + padding: 0 0 24px !important; + } + + #menuToggle .menu-item-has-children.active > .sub-menu > li { + display: list-item !important; + visibility: visible !important; + pointer-events: auto !important; + margin-top: 16px !important; + } + + #menuToggle .menu-item-has-children.active > .sub-menu > li > a { + display: block !important; + font-size: 16px !important; + font-weight: 600 !important; + color: #1C4DA1 !important; + padding: 0 !important; + } + + /* Mega-menu: nested column sections */ + #menuToggle .mega-menu-nav.active > .sub-menu > li.col-menu { + display: list-item !important; + margin-top: 20px !important; + } + + #menuToggle .mega-menu-nav.active > .sub-menu > li.col-menu > a { + display: flex !important; + align-items: center !important; + justify-content: space-between !important; + font-size: 16px !important; + font-weight: 600 !important; + } + + #menuToggle .mega-menu-nav .col-menu:not(.active) > .sub-menu { + max-height: 0 !important; + overflow: hidden !important; + visibility: hidden !important; + pointer-events: none !important; + } + + #menuToggle .mega-menu-nav .col-menu.active > .sub-menu { + max-height: none !important; + overflow: visible !important; + visibility: visible !important; + pointer-events: auto !important; + padding-left: 0 !important; + margin-top: 8px !important; + } + + #menuToggle .mega-menu-nav .col-menu.active > .sub-menu > li { + display: list-item !important; + margin-top: 12px !important; + } + + #menuToggle .mega-menu-nav .col-menu.active > .sub-menu a { + display: block !important; + font-weight: 600 !important; + color: #1C4DA1 !important; + } +} +""" + +if marker in text: + text = re.sub(r"/\* Mobile nav clickability fix \*/[\s\S]*?(?=\n/\* |\Z)", block.strip() + "\n", text, count=1) +else: + text = text.rstrip() + "\n\n" + block.strip() + "\n" + +css.write_text(text) +print("Patched new.css") +PY + +for header in header.php header-v2.php; do + [[ -f "$header" ]] || continue + python3 - "$header" "$CACHE_VER" <<'PY' +import re, sys +path, ver = sys.argv[1], sys.argv[2] +text = open(path).read() +new = re.sub( + r"(get_stylesheet_directory_uri\(\)\s*\.\s*'/new\.css')(\?v=[^']*)?'", + rf"\1'?v={ver}'", + text, + count=1, +) +if new == text: + new = text.replace( + "get_stylesheet_directory_uri(). '/new.css' ?>", + f"get_stylesheet_directory_uri(). '/new.css?v={ver}' ?>", + ) +open(path, "w").write(text if new == text else new) +print(f"Cache-busted {path} -> ?v={ver}") +PY +done + +cd "$WP_ROOT" + +PATCH_PHP="$(mktemp)" +cat > "$PATCH_PHP" <<'PHP' + a").forEach(function (menuItem) { + if (!menuItem.querySelector(".arrow-icon")) { + var arrow = document.createElement("img"); + arrow.className = "arrow-icon"; + arrow.src = ARROW_SRC; + arrow.alt = ""; + arrow.setAttribute("aria-hidden", "true"); + menuItem.appendChild(arrow); + } + + menuItem.addEventListener("click", function (event) { + var href = menuItem.getAttribute("href") || ""; + var isVoidHref = !href || href === "#" || href.indexOf("javascript:") === 0; + var clickedArrow = event.target.closest(".arrow-icon"); + + if (clickedArrow || isVoidHref) { + event.preventDefault(); + toggleItem(menuItem.parentElement); + } + }); + }); + } + + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", initBlogMobileMenu); + } else { + initBlogMobileMenu(); + } +})(); +JS; + +$updated = false; +foreach ($settings as $key => $value) { + if (! is_string($value) || strpos($value, 'menuToggle') === false) { + continue; + } + + $value = preg_replace( + '/document\.querySelector\("#menuToggle input[\s\S]*?initBlogMobileMenu[\s\S]*?\}\)\(\);\s*/m', + '', + $value + ); + $value = preg_replace( + '/document\.querySelectorAll\([\'"]#menuToggle \.menu-item-has-children > a[\'"]\)[\s\S]*?\}\);\s*/m', + '', + $value + ); + $value = preg_replace( + '/document\.querySelectorAll\([\'"]\.mobile-nav nav\.menu-menu-1-container \.menu-item-has-children > a[\'"]\)[\s\S]*?\}\);\s*/m', + '', + $value + ); + $value = preg_replace( + '/document\.querySelector\("#menuToggle input, #menuToggleCheckbox"\)\.addEventListener\("change"[\s\S]*?\}\);\s*/m', + '', + $value + ); + + $insertBefore = ''; + $pos = strrpos($value, $insertBefore); + if ($pos === false) { + fwrite(STDERR, "ERROR: no in {$key}\n"); + exit(1); + } + + $settings[$key] = substr($value, 0, $pos) . trim($mobileJs) . "\n" . substr($value, $pos); + $updated = true; + echo "Rebuilt Laravel-style mobile menu JS in auhfc_settings_sitewide[{$key}]\n"; + break; +} + +if (! $updated) { + fwrite(STDERR, "ERROR: footer script not found\n"); + exit(1); +} + +update_option('auhfc_settings_sitewide', $settings); +PHP + +wp eval-file "$PATCH_PHP" --allow-root +rm -f "$PATCH_PHP" + +wp cache flush --allow-root 2>/dev/null | tail -1 || true + +for svc in php8.3-fpm php8.2-fpm php8.1-fpm php8.0-fpm; do + if systemctl is-active --quiet "$svc" 2>/dev/null; then + systemctl reload "$svc" + echo "Reloaded $svc" + break + fi +done + +echo "Done. new.css cache version: $CACHE_VER" diff --git a/scripts/fix-blog-mobile-menu-snappy.sh b/scripts/fix-blog-mobile-menu-snappy.sh new file mode 100755 index 000000000..90320848c --- /dev/null +++ b/scripts/fix-blog-mobile-menu-snappy.sh @@ -0,0 +1,178 @@ +#!/usr/bin/env bash +# Snappier blog mobile menu: instant toggles, no max-height animation, larger chevron tap targets. +set -euo pipefail + +WP_ROOT="${WP_ROOT:-/var/www/html/blog}" +THEME="${THEME:-$WP_ROOT/wp-content/themes/eucodewe-1389}" +CACHE_VER="${CACHE_VER:-$(date +%Y%m%d%H%M)}" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +[[ -d "$WP_ROOT" ]] || { echo "WP_ROOT not found"; exit 1; } + +cd "$THEME" +chmod u+w new.css 2>/dev/null || true + +python3 <<'PY' +from pathlib import Path +import re + +css = Path("new.css") +text = css.read_text() +marker = "/* Mobile nav clickability fix */" +block = """/* Mobile nav clickability fix */ +@media (max-width: 1080px) { + .header__menu.menu-header { + display: none !important; + pointer-events: none !important; + } + + #menuToggle, + #menuToggle a, + #menuToggle .sub-menu { + -webkit-tap-highlight-color: transparent; + touch-action: manipulation; + } + + /* Kill slow max-height animations from legacy menu CSS */ + #menuToggle .sub-menu, + #menuToggle .menu-menu-1-container ul .sub-menu { + transition: none !important; + } + + /* Laravel-style parent row: label left, chevron right */ + #menuToggle .menu-item-has-children > a { + display: flex !important; + align-items: center !important; + justify-content: space-between !important; + gap: 8px !important; + width: 100% !important; + font-size: 16px !important; + font-weight: 600 !important; + color: #1C4DA1 !important; + padding: 12px 0 !important; + text-decoration: none !important; + } + + #menuToggle .menu-item-has-children > a::after { + display: none !important; + } + + #menuToggle .menu-item-has-children > a .arrow-icon { + width: 16px; + height: 16px; + flex-shrink: 0; + margin-left: auto; + padding: 12px; + margin-right: -12px; + box-sizing: content-box; + cursor: pointer; + transition: none; + } + + #menuToggle .menu-item-has-children.active > a .arrow-icon { + transform: scaleY(-1); + } + + #menuToggle #menu-item-5845, + #menuToggle #menu-item-5847, + #menuToggle #menu-item-5848, + #menuToggle li#menu-item-5842, + #menuToggle li.box-featured, + #menuToggle li.rt-custom-menu-field-item, + #menuToggle .rt-wp-menu-custom-fields-wrapper { + display: none !important; + pointer-events: none !important; + height: 0 !important; + max-height: 0 !important; + overflow: hidden !important; + margin: 0 !important; + padding: 0 !important; + visibility: hidden !important; + } + + /* Faster than :has() — class toggled in JS */ + #menuToggle.menu-section-open .menu-wrapper > .menu-item-has-children:not(.active), + #menuToggle.menu-section-open .menu-wrapper > .menu-item:not(.menu-item-has-children) { + display: none !important; + } + + #menuToggle .menu-item-has-children:not(.active) > .sub-menu { + display: none !important; + pointer-events: none !important; + padding: 0 !important; + margin: 0 !important; + } + + #menuToggle .menu-item-has-children.active > .sub-menu { + display: block !important; + pointer-events: auto !important; + padding: 0 0 24px !important; + } + + #menuToggle .menu-item-has-children.active > .sub-menu > li { + display: list-item !important; + pointer-events: auto !important; + margin-top: 16px !important; + } + + #menuToggle .menu-item-has-children.active > .sub-menu > li > a { + display: block !important; + font-size: 16px !important; + font-weight: 600 !important; + color: #1C4DA1 !important; + padding: 0 !important; + } + + #menuToggle .mega-menu-nav.active > .sub-menu > li.col-menu { + display: list-item !important; + margin-top: 20px !important; + } + + #menuToggle .mega-menu-nav.active > .sub-menu > li.col-menu > a { + display: flex !important; + align-items: center !important; + justify-content: space-between !important; + font-size: 16px !important; + font-weight: 600 !important; + } + + #menuToggle .mega-menu-nav .col-menu:not(.active) > .sub-menu { + display: none !important; + pointer-events: none !important; + } + + #menuToggle .mega-menu-nav .col-menu.active > .sub-menu { + display: block !important; + pointer-events: auto !important; + padding-left: 0 !important; + margin-top: 8px !important; + } + + #menuToggle .mega-menu-nav .col-menu.active > .sub-menu > li { + display: list-item !important; + margin-top: 12px !important; + } + + #menuToggle .mega-menu-nav .col-menu.active > .sub-menu a { + display: block !important; + font-weight: 600 !important; + color: #1C4DA1 !important; + } +} +""" + +if marker in text: + text = re.sub(r"/\* Mobile nav clickability fix \*/[\s\S]*?(?=\n/\* |\Z)", block.strip() + "\n", text, count=1) +else: + text = text.rstrip() + "\n\n" + block.strip() + "\n" + +css.write_text(text) +print("Patched new.css for snappy toggles") +PY + +for header in header.php header-v2.php; do + [[ -f "$header" ]] || continue + sed -i "s|/new.css?v=[0-9]*|/new.css?v=$CACHE_VER|g; s|/new.css' ?>|/new.css?v=$CACHE_VER' ?>|g" "$header" +done + +bash "$SCRIPT_DIR/fix-blog-footer-script-clean.sh" diff --git a/scripts/fix-blog-mobile-menu-v2.sh b/scripts/fix-blog-mobile-menu-v2.sh new file mode 100755 index 000000000..85ef06057 --- /dev/null +++ b/scripts/fix-blog-mobile-menu-v2.sh @@ -0,0 +1,131 @@ +#!/usr/bin/env bash +# Fix blog mobile menu v2: nested mega-menu pointer-events, hide promo, cache-bust new.css. +set -euo pipefail + +WP_ROOT="${WP_ROOT:-/var/www/html/blog}" +THEME="${THEME:-$WP_ROOT/wp-content/themes/eucodewe-1389}" +CACHE_VER="${CACHE_VER:-$(date +%Y%m%d%H%M)}" + +[[ -d "$WP_ROOT" ]] || { echo "WP_ROOT not found"; exit 1; } +[[ -f "$THEME/new.css" ]] || { echo "Theme new.css not found"; exit 1; } +command -v wp >/dev/null 2>&1 || { echo "wp-cli required"; exit 1; } + +cd "$THEME" +chmod u+w new.css header.php header-v2.php 2>/dev/null || true + +python3 <<'PY' +from pathlib import Path +import re + +css = Path("new.css") +text = css.read_text() +marker = "/* Mobile nav clickability fix */" +block = """/* Mobile nav clickability fix */ +@media (max-width: 1080px) { + .header__menu.menu-header { + display: none !important; + pointer-events: none !important; + } + + /* Always hide desktop promo card in mobile drawer */ + #menuToggle li#menu-item-5842, + #menuToggle li.box-featured, + #menuToggle li.rt-custom-menu-field-item, + .mobile-nav li#menu-item-5842, + .mobile-nav li.box-featured, + .mobile-nav li.rt-custom-menu-field-item, + .mobile-nav .rt-wp-menu-custom-fields-wrapper { + display: none !important; + pointer-events: none !important; + height: 0 !important; + max-height: 0 !important; + overflow: hidden !important; + margin: 0 !important; + padding: 0 !important; + min-height: 0 !important; + opacity: 0 !important; + visibility: hidden !important; + } + + /* Collapse all submenu levels when parent is closed (mega-menu nests ul.sub-menu) */ + #menuToggle .menu-item-has-children:not(.active) > .sub-menu, + #menuToggle .menu-item-has-children:not(.active) .sub-menu .sub-menu { + max-height: 0 !important; + overflow: hidden !important; + visibility: hidden !important; + pointer-events: none !important; + padding-top: 0 !important; + margin: 0 !important; + } + + #menuToggle .menu-item-has-children:not(.active) .sub-menu *, + #menuToggle .menu-item-has-children:not(.active) .sub-menu .sub-menu * { + pointer-events: none !important; + } + + #menuToggle .menu-item-has-children.active > .sub-menu { + max-height: none !important; + overflow: visible !important; + visibility: visible !important; + pointer-events: auto !important; + } + + #menuToggle .menu-item-has-children.active .sub-menu a, + #menuToggle .menu-item-has-children.active .sub-menu li { + pointer-events: auto !important; + visibility: visible !important; + } + + #menuToggle nav.menu-menu-1-container a { + display: block; + position: relative; + z-index: 5; + pointer-events: auto; + } +} +""" + +if marker in text: + text = re.sub(r"/\* Mobile nav clickability fix \*/[\s\S]*?(?=\n/\* |\Z)", block.strip() + "\n", text, count=1) +else: + text = text.rstrip() + "\n\n" + block.strip() + "\n" + +css.write_text(text) +print("Patched new.css") +PY + +for header in header.php header-v2.php; do + if [[ -f "$header" ]]; then + python3 - "$header" "$CACHE_VER" <<'PY' +import re, sys +path, ver = sys.argv[1], sys.argv[2] +text = open(path).read() +new = re.sub( + r"(get_stylesheet_directory_uri\(\)\s*\.\s*'/new\.css')(\s*\?v=[^']*)?'", + rf"\1'?v={ver}'", + text, + count=1, +) +if new == text: + new = text.replace( + "get_stylesheet_directory_uri(). '/new.css' ?>", + f"get_stylesheet_directory_uri(). '/new.css?v={ver}' ?>", + ) +open(path, "w").write(new) +print(f"Cache-busted {path} -> ?v={ver}") +PY + fi +done + +cd "$WP_ROOT" +wp cache flush --allow-root 2>/dev/null | tail -1 || true + +for svc in php8.3-fpm php8.2-fpm php8.1-fpm php8.0-fpm; do + if systemctl is-active --quiet "$svc" 2>/dev/null; then + systemctl reload "$svc" + echo "Reloaded $svc" + break + fi +done + +echo "Done. new.css cache version: $CACHE_VER" diff --git a/scripts/patch-blog-menu-share-stories-icon.sh b/scripts/patch-blog-menu-share-stories-icon.sh index 70d1d75df..b43670f5b 100755 --- a/scripts/patch-blog-menu-share-stories-icon.sh +++ b/scripts/patch-blog-menu-share-stories-icon.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Fix "Share your stories" Blog submenu: arrow icon + Office form URL. +# Fix "Share your stories" Blog submenu: Office form URL (no arrow icon). # Run on server: WP_ROOT=/var/www/html/blog bash patch-blog-menu-share-stories-icon.sh set -euo pipefail @@ -7,7 +7,6 @@ set -euo pipefail WP_ROOT="${WP_ROOT:-/var/www/html/blog}" SHARE_TEXT="Share your stories" SHARE_URL="https://forms.office.com/Pages/ResponsePage.aspx?id=18F13DIal06vkB3AGRHqbCnyIKB_vXdLsUgagfjd7DRUN1dZTVYxSkJNQ1VWSlVZNlpBOFAyN0g4UC4u&embed=true" -ARROW_HTML='arrow right' [[ -d "$WP_ROOT" ]] || { echo "WP_ROOT not found: $WP_ROOT"; exit 1; } command -v wp >/dev/null 2>&1 || { echo "wp-cli required"; exit 1; } @@ -28,27 +27,12 @@ fi echo "Patching menu item ID $SHARE_ID" -# Copy arrow icon meta from an existing submenu item that already shows the arrow -REF_ID=$(wp menu item list 2 --fields=db_id,title --format=csv --allow-root 2>/dev/null \ - | awk -F, '$2 ~ /^Activities$/ {print $1; exit}') || true -REF_ID="${REF_ID:-5845}" - wp eval --allow-root " -\$ref = (int) ${REF_ID}; \$target = (int) ${SHARE_ID}; -\$meta = get_post_meta(\$ref, 'rt-wp-menu-custom-fields', true); -if (empty(\$meta) || ! is_array(\$meta)) { - \$meta = [ - 'selected-feature' => 'html', - 'html' => [ - 'custom-html' => '${ARROW_HTML}', - ], - ]; -} update_post_meta(\$target, '_menu_item_url', '${SHARE_URL}'); -update_post_meta(\$target, 'rt-wp-menu-custom-fields', \$meta); +delete_post_meta(\$target, 'rt-wp-menu-custom-fields'); delete_transient('rt-wp-menu-custom-fields-' . \$target); -echo \"Menu item \$target: URL + arrow icon meta applied (copied from \$ref)\n\"; +echo \"Menu item \$target: URL updated, arrow custom HTML removed\n\"; " wp cache flush --allow-root 2>/dev/null || true diff --git a/scripts/patch-blog-mobile-menu-parent-links.sh b/scripts/patch-blog-mobile-menu-parent-links.sh new file mode 100755 index 000000000..2804dce20 --- /dev/null +++ b/scripts/patch-blog-mobile-menu-parent-links.sh @@ -0,0 +1,106 @@ +#!/usr/bin/env bash +# Match main codeweek.eu mobile menu: parent labels navigate, chevron/void links expand submenu. +# Run on blog server: bash patch-blog-mobile-menu-parent-links.sh + +set -euo pipefail + +WP_ROOT="${WP_ROOT:-/var/www/html/blog}" +COMMUNITY_URL="${COMMUNITY_URL:-https://codeweek.eu/community}" +RESOURCES_URL="${RESOURCES_URL:-https://codeweek.eu/educational-resources}" +BLOG_URL="${BLOG_URL:-https://codeweek.eu/blog/}" + +[[ -d "$WP_ROOT" ]] || { echo "WP_ROOT not found: $WP_ROOT"; exit 1; } +command -v wp >/dev/null 2>&1 || { echo "wp-cli required"; exit 1; } + +cd "$WP_ROOT" + +PATCH_PHP="$(mktemp)" +cat > "$PATCH_PHP" <<'PHP' + a').forEach(function(menuItem) { + menuItem.addEventListener('click', function(event) { + var href = menuItem.getAttribute('href') || ''; + var isVoidHref = !href || href === '#' || href.indexOf('javascript:') === 0; + var rect = menuItem.getBoundingClientRect(); + var clickedChevron = event.clientX >= rect.right - 36; + + if (!isVoidHref && !clickedChevron) { + return; + } + + event.preventDefault(); + var parentItem = menuItem.parentElement; + parentItem.classList.toggle('active'); + + document.querySelectorAll('nav.menu-menu-1-container .menu-item-has-children').forEach(function(otherItem) { + if (otherItem !== parentItem) { + otherItem.classList.remove('active'); + } + }); + }); + }); +JS; + +$pattern = '/document\.querySelectorAll\(\x27nav\.menu-menu-1-container \.menu-item-has-children > a\x27\)[\s\S]*?(?:\}\);\s*){1,3}/'; + +$updated = false; +foreach ($settings as $key => $value) { + if (! is_string($value) || strpos($value, 'menu-item-has-children') === false) { + continue; + } + + $value = preg_replace($pattern, '', $value); + $value = preg_replace('/\s*\}\);\s*\}\);\s*(<\/script>)/', "\n$1", $value); + + $insertBefore = ''; + $pos = strrpos($value, $insertBefore); + if ($pos === false) { + continue; + } + + $settings[$key] = substr($value, 0, $pos) . "\n" . trim($newHandler) . "\n" . substr($value, $pos); + $updated = true; + echo "Patched mobile menu script in auhfc_settings_sitewide[{$key}]\n"; + break; +} + +if (! $updated) { + fwrite(STDERR, "ERROR: could not find mobile menu script in auhfc_settings_sitewide\n"); + exit(1); +} + +update_option('auhfc_settings_sitewide', $settings); + +$menuPatches = [ + 5637 => getenv('COMMUNITY_URL') ?: 'https://codeweek.eu/community', + 29 => getenv('RESOURCES_URL') ?: 'https://codeweek.eu/educational-resources', + 5643 => getenv('BLOG_URL') ?: 'https://codeweek.eu/blog/', +]; + +foreach ($menuPatches as $id => $url) { + update_post_meta((int) $id, '_menu_item_url', $url); + echo "Menu item {$id} -> {$url}\n"; +} +PHP + +COMMUNITY_URL="$COMMUNITY_URL" RESOURCES_URL="$RESOURCES_URL" BLOG_URL="$BLOG_URL" \ + wp eval-file "$PATCH_PHP" --allow-root +rm -f "$PATCH_PHP" + +wp cache flush --allow-root 2>/dev/null || true +for svc in php8.3-fpm php8.2-fpm php8.1-fpm php8.0-fpm; do + if systemctl is-active --quiet "$svc" 2>/dev/null; then + systemctl reload "$svc" + echo "Reloaded $svc" + break + fi +done + +echo "Done. Hard-refresh https://codeweek.eu/blog/ on mobile."