Cannot find native module ‘ExpoKioskControl’

I’m trying to implement kiosk mode in my Expo app using the expo-kiosk-control package.

I installed the package with:

npm install expo-kiosk-control
However, when I try to run my app, I get the following error:

Error: Cannot find native module ‘ExpoKioskControl’
I am using Expo SDK 52.

I am running the app with npx expo start.

Is there something I’m missing?
How can I fix this and properly use expo-kiosk-control in my project?

Thank you for your help!

How to Preview and Silently Print PDF (Blocking Virtual Printers) in Electron for a Pawning Management System?

I’m working on converting an existing CodeIgniter-based web pawning management system into a desktop app using Electron. My requirements are:

  1. PDF Preview: When printing, the user should see a preview of the
    PDF, but should NOT be able to save or download it.
  2. Silent Printing: The pawn ticket (PDF) should be printed directly to
    the default physical printer, with no print dialog shown.
  3. Block Virtual Printers: Virtual printers (like Microsoft Print to
    PDF, XPS, OneNote, etc.) must be blocked—only real/physical printers
    should be selectable.

What I’ve tried:

  • I can print HTML content silently using Electron (my test print
    works).
  • The actual ticket data comes as a PDF generated by CodeIgniter (using
    TCPDF).
  • When I try to print the PDF silently, nothing is printed, and I see
    errors like Printer settings invalid … content size is empty.

I have code to filter out virtual printers, but the main issue is reliably printing the PDF silently and showing a preview without allowing save/download.
Questions:

  1. How can I show a PDF preview in Electron but prevent the user from
    saving/downloading the file?
  2. What’s the best way to print a PDF silently to a physical printer in
    Electron (or another desktop framework), especially when the PDF is
    generated by a web backend?
  3. How can I ensure only physical printers are used (block all virtual
    printers) in the print dialog or silent print?

Any code samples, libraries, or architectural suggestions are appreciated!
The backend is CodeIgniter, generating PDFs with TCPDF.
I’m open to using other frameworks if Electron can’t do this reliably.

Start of main.js

const { app, BrowserWindow, ipcMain, shell } = 
require('electron');
const path = require('path');
const fs = require('fs');
const os = require('os');
const { net } = require('electron');

const DEBUG_MODE = true; // Set to false in production

let mainWindow;

// Create the main application window
function createWindow() {
mainWindow = new BrowserWindow({
width: 1280,
height: 800,
webPreferences: {
  nodeIntegration: false,
  contextIsolation: true,
  sandbox: false,
  preload: path.join(__dirname, 'preload.js')
},
autoHideMenuBar: true
});

// Load your web application
mainWindow.loadURL('http://192.168.1.17/pawn/pawn_sew/');

// Open external links in default browser
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
if (!url.startsWith('http://192.168.1.17')) {
  shell.openExternal(url);
  return { action: 'deny' };
}
return { action: 'allow' };
});
}

// Main app initialization
app.whenReady().then(() => {
createWindow();

// FIXED: Using correct API to get printers in newer Electron 
versions
setTimeout(() => {
try {
  // In newer versions of Electron, getPrinters is on 
webContents.getPrintersAsync()
  // or mainWindow.webContents.print.getPrinterList() depending 
on version
  if (mainWindow.webContents.getPrinters) {
    const printers = mainWindow.webContents.getPrinters();
    logPrinters(printers);
  } else if (mainWindow.webContents.getPrintersAsync) {
    mainWindow.webContents.getPrintersAsync().then(logPrinters);
  } else if (mainWindow.webContents.print && 
mainWindow.webContents.print.getPrinterList) {
    mainWindow.webContents.print.getPrinterList().then(logPrinters);
  }
} catch (err) {
  console.error('Failed to get printers:', err);
}
}, 2000); // Wait for window to be ready

app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
  createWindow();
}
});
});

Codes related to printer selection

// Update the logPrinters function to show virtual printer status
function logPrinters(printers) {
  console.log('Available printers:');
  printers.forEach((printer, index) => {
    const isVirtual = isVirtualPrinter(printer.name);
    console.log(
      `${index + 1}. ${printer.name} (Default: ${printer.isDefault}, Virtual: ${isVirtual ? 'Yes' : 'No'})`
    );
  });
}

// Add this function to identify virtual printers
function isVirtualPrinter(printerName) {
  // Common virtual printer names (case-insensitive)
  const virtualPrinterPatterns = [
    /pdf/i,
    /microsoft/i,
    /adobe/i,
    /xps/i,
    /document writer/i,
    /onedrive/i,
    /fax/i,
    /virtual/i,
    /print to file/i,
    /cloud/i,
    /scan/i,
    /onenote/i,      // Added for OneNote
    /anydesk/i,      // Added for AnyDesk
    /remote/i,       // Common for remote printing solutions
    /universal/i,    // Often used in virtual print drivers
    /bullzip/i,      // Another common PDF printer
    /cutepdf/i,      // Common PDF printer
    /foxit/i,        // Foxit PDF printer
    /doro/i          // DoroEasy PDF
  ];
  
  return virtualPrinterPatterns.some(pattern => pattern.test(printerName));
}

// Add this helper function to determine if a printer is suitable for pawn tickets
function isSuitablePrinter(printerName) {
  const lowerName = printerName.toLowerCase();
  
  // Skip POS/thermal printers that aren't suitable for pawn tickets
  if (lowerName.includes('pos') || 
      lowerName.includes('thermal') || 
      lowerName.includes('receipt') || 
      lowerName.includes('dot matrix')) {
    console.log(`Skipping printer ${printerName} as it appears to be a thermal/POS printer`);
    return false;
  }
  
  // Include standard printers
  return true;
}

// Quit when all windows are closed (except on macOS)
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

Pdf print related code (I used Chatgpt)

async function printWithWindows(pdfPath, printerName) {
  return new Promise((resolve, reject) => {
    const { exec } = require('child_process');
    // Use the Windows 'print' command (works for some printers, not all)
    // Note: This is a basic fallback and may not support all PDF features
    const command = `PRINT /D:"${printerName}" "${pdfPath}"`;
    exec(command, (error, stdout, stderr) => {
      if (error) {
        console.error(`Direct print error: ${error.message}`);
        reject(error);
      } else {
        console.log('Direct Windows print completed successfully');
        resolve(true);
      }
    });
  });
}

// Update the silent-print-ticket handler
ipcMain.handle('silent-print-ticket', async (event, ticketUrl) => {
  console.log('Received print request for:', ticketUrl);
  
  const DEBUG_MODE = true; // Set to false in production
  let tempPdfPath;
  let printWindow;
  
  try {
    // 1. Download PDF
    tempPdfPath = await downloadPdf(ticketUrl);
    
    // Verify file exists before printing
    if (!fs.existsSync(tempPdfPath)) {
      throw new Error(`PDF file does not exist at ${tempPdfPath}`);
    }

    const fileStats = fs.statSync(tempPdfPath);
    console.log(`PDF file size: ${fileStats.size} bytes`);
    
    // 2. Create a window with proper PDF display capabilities
    printWindow = new BrowserWindow({
      width: 800,
      height: 1100,
      show: DEBUG_MODE,
      webPreferences: {
        plugins: true
      }
    });
    
    if (DEBUG_MODE) {
      printWindow.webContents.openDevTools();
    }
    
    // 3. Load the PDF directly
    const pdfUrl = `file://${tempPdfPath.replace(/\/g, '/')}`;
    console.log(`Loading PDF from: ${pdfUrl}`);
    await printWindow.loadURL(pdfUrl);
    
    // Add event listeners to capture PDF loading issues
    printWindow.webContents.on('did-fail-load', (event, errorCode, errorDescription) => {
      console.error(`Failed to load PDF: ${errorDescription} (${errorCode})`);
    });
    
    // 4. Give PDF time to render properly
    await new Promise(resolve => setTimeout(resolve, 2000));
    console.log('PDF loaded, preparing to print');
    
    // 5. Get available printers
    let printers = [];
    try {
      if (mainWindow.webContents.getPrinters) {
        printers = mainWindow.webContents.getPrinters();
      } else if (mainWindow.webContents.getPrintersAsync) {
        printers = await mainWindow.webContents.getPrintersAsync();
      } else if (mainWindow.webContents.print && mainWindow.webContents.print.getPrinterList) {
        printers = await mainWindow.webContents.print.getPrinterList();
      }
    } catch (err) {
      console.error('Failed to get printer list:', err);
      throw new Error('Could not find any printers. Please check your printer connection and try again.');
    }
    
    // 6. Filter out virtual printers and thermal printers
    const realPrinters = printers.filter(printer => 
      !isVirtualPrinter(printer.name) && isSuitablePrinter(printer.name)
    );
    console.log('Suitable printers for pawn tickets:', realPrinters.map(p => p.name));
    
    if (realPrinters.length === 0) {
      throw new Error('No suitable printers found for pawn tickets. Please connect a standard printer.');
    }
    
    // 7. Sort printers prioritizing default printers
    const sortedPrinters = [...realPrinters].sort((a, b) => {
      return a.isDefault ? -1 : (b.isDefault ? 1 : 0);
    });
    
    console.log('Prioritized printer order:', sortedPrinters.map(p => p.name));
    
    // 8. Try each printer until one succeeds - with standard Electron printing
    let lastError = null;
    for (const printer of sortedPrinters) {
      try {
        console.log(`Attempting to print to ${printer.name}...`);
        
        // Check network printer access
        if (printer.name.startsWith('\')) {
          const hasAccess = await checkPrinterAccess(printer.name);
          if (!hasAccess) {
            console.log(`No access to network printer: ${printer.name}, skipping...`);
            continue;
          }
        }
        
        // Try standard Electron print with A4 half portrait settings
        const printResult = await printWindow.webContents.print({
          silent: true,
          printBackground: true,
          deviceName: printer.name,
          pageSize: {
            width: 210000, // A4 width in microns
            height: 148500 // A4 height / 2 in microns
          },
          landscape: false,
          margins: {
            marginType: 'custom',
            top: 5000,
            bottom: 5000,
            left: 5000,
            right: 5000
          },
          dpi: {
            horizontal: 300,
            vertical: 300
          }
        });
        
        if (printResult) {
          console.log(`Successfully printed to ${printer.name}`);
          
          if (printWindow && !printWindow.isDestroyed()) {
            printWindow.close();
          }
          
          fs.unlink(tempPdfPath, () => {});
          
          return { 
            success: true, 
            printerName: printer.name,
            message: `Document successfully sent to printer "${printer.name}".`
          };
        } else {
          console.log(`Print to ${printer.name} returned false`);
          throw new Error('Printer returned unsuccessful status');
        }
      } catch (printerError) {
        console.error(`Failed to print to ${printer.name}:`, printerError.message);
        lastError = printerError;
        // Continue to next printer
      }
    }
    
    // 9. If Electron printing failed for all printers, try Windows direct printing
    console.log("All Electron print attempts failed. Trying Windows direct printing...");
    
    for (const printer of sortedPrinters) {
      try {
        console.log(`Attempting Windows direct print to ${printer.name}...`);
        await printWithWindows(tempPdfPath, printer.name);
        
        console.log(`Successfully printed to ${printer.name} using Windows direct printing`);
        
        if (printWindow && !printWindow.isDestroyed()) {
          printWindow.close();
        }
        
        return { 
          success: true, 
          printerName: printer.name,
          message: `Document successfully printed to "${printer.name}" using Windows direct printing.`
        };
      } catch (winError) {
        console.error(`Windows direct print to ${printer.name} failed:`, winError.message);
        // Continue to next printer
      }
    }
    
    // 10. Last resort: Show print dialog
    if (DEBUG_MODE) {
      console.log("All automatic print attempts failed. Trying with system dialog...");
      
      const dialogResult = await printWindow.webContents.print();
      
      if (dialogResult) {
        console.log("Print succeeded with system dialog");
        
        if (printWindow && !printWindow.isDestroyed()) {
          printWindow.close();
        }
        
        fs.unlink(tempPdfPath, () => {});
        
        return { 
          success: true, 
          message: "Document printed successfully using system dialog."
        };
      }
    }
    
    // If we get here, all methods failed
    throw new Error('All print methods failed. Please check your printer connections and try again.');
    
  } catch (error) {
    console.error('Print error:', error.message);
    if (printWindow && !printWindow.isDestroyed()) {
      printWindow.close();
    }
    if (tempPdfPath) {
      fs.unlink(tempPdfPath, () => {});
    }
    
    return { 
      success: false, 
      error: error.message,
      userFriendlyMessage: `Printing failed: ${error.message}. Please check your printer connections and try again.`,
      retryable: true
    };
  }
});

And this is the console outputs

Available printers:

  1. OneNote for Windows 10 (Default: false, Virtual: Yes)
  2. POS-80 (Default: false, Virtual: No)
  3. Microsoft XPS Document Writer (Default: false, Virtual: Yes)
  4. Microsoft Print to PDF (Default: false, Virtual: Yes)
  5. Fax (Default: false, Virtual: Yes)
  6. AnyDesk Printer (Default: false, Virtual: Yes)
  7. accounts-pcHP Ink Tank 310 series (Copy 1) (Default: true, Virtual: No) Received print request for:
    http://192.168.1.17/pawn/pawn_sew/swe_ticket.pdf PDF downloaded
    successfully (8038 bytes) PDF file size: 8038 bytes Loading PDF from:
    file://C:/Users/ADMINI~1/AppData/Local/Temp/ticket_1745819614508.pdf
    [10416:0428/112335.468:ERROR:CONSOLE(1)] “Request Autofill.enable
    failed. {“code”:-32601,”message”:”‘Autofill.enable’ wasn’t found”}”,
    source:
    devtools://devtools/bundled/core/protocol_client/protocol_client.js
    (1) [10416:0428/112335.468:ERROR:CONSOLE(1)] “Request
    Autofill.setAddresses failed.
    {“code”:-32601,”message”:”‘Autofill.setAddresses’ wasn’t found”}”,
    source:
    devtools://devtools/bundled/core/protocol_client/protocol_client.js
    (1) PDF loaded, preparing to print Skipping printer POS-80 as it
    appears to be a thermal/POS printer Suitable printers for pawn
    tickets: [ ‘\accounts-pcHP Ink Tank 310 series (Copy 1)’ ]
    Prioritized printer order: [ ‘\accounts-pcHP Ink Tank 310 series
    (Copy 1)’ ] Attempting to print to accounts-pcHP Ink Tank 310
    series (Copy 1)… Checking access to network path: accounts-pc
    Successfully accessed network path: accounts-pc Print to
    accounts-pcHP Ink Tank 310 series (Copy 1) returned false Failed to
    print to accounts-pcHP Ink Tank 310 series (Copy 1): Printer
    returned unsuccessful statused unsuccessful status All Electron print
    attempts failed. Trying Windows direct printing… …
    Attempting Windows direct print to accounts-pcHP Ink Tank 310
    series (Copy 1)…
    ows direct printing Direct Windows print completed successfully
    er: print_view_manager_base.cc:384 Printer settings invalid for
    accounts-pcH Successfully printed to accounts-pcHP Ink Tank 310
    series (Copy 1) using Windows direct printing
    [10416:0428/112337.075:ERROR:device_event_log_impl.cc(202)]
    [11:23:37.076] Printer: print_view_manager_base.cc:384 Printer
    settings invalid for accounts-pcHP Ink Tank 310 series (Copy 1)
    (destination type kLocal): content size is empty PS D:Softmaster
    ProjectsPawningpawn-electron-app>

Download blob PDF using Cypress

Here are my steps:

  1. Click a button
  2. A report is generated in new Tab as blob:https://myweburl/uuid and then downloaded.

I am using Cypress. After clicking the button, the PDF file opens in a new browser tab, and I can’t download the content automatically. Is there any way to force download the file instead of displaying the content in a new browser tab in Cypress?

I can’t download the content of the new window

Install phpspreadsheet on old version of Mac

I’m running PHP version 7.3.29 on Mac and need to install PhpSpreadsheet but get errors

phpoffice/phpspreadsheet 1.18.0 requires ext-zip * -> it is missing
from your system. Install or enable PHP’s zip extension.

and

phpoffice/phpspreadsheet[1.26.0, …, 1.29.10] require php ^7.4 ||
^8.0 -> your php version (7.3.29) does not satisfy that requirement.

Update : I’m using local running PHP version 8.1.23. I need the correct way to install PHP extensions in Local by Flywheel on macOS.

Cannot install phpspreadsheet and cannot download Excel spreadsheets in .xlsx) format. I have tried updating PHP and installing an older version pf php spreadsheet but nothing works.

So the core problem is that the PHP zip extension is missing from my system, which is preventing PhpSpreadsheet from being installed.

I cannot install the PHP zip extension on my machine

PHP form submit gives internal server error after migrating website to another server for some content only [duplicate]

I am getting internal server error after submitting form.. Already checked error_log but nothing is generating. This issue is occured when I moved website to another server. In old server, everything was working fine.

  • Current PHP Version 8.2.28

found the error log location at /home/username/logs/domainname.php.error.log — but it is empty. this is likely not the error log for my 500 error.

form.php

<form method="post" action="submitUpdatedSpeaker.php" name="Contact Form" id="contactform" enctype="multipart/form-data"> 
    <textarea name="content" id="content"></textarea>
    <input type="submit" value="Submit" /> 
</form> 

submitUpdatedSpeaker.php

<?php
ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
error_reporting(E_ALL);
echo "Test";
?>

I have many fields in form but for checking I just added this simple code and submitted form, but still this code is not working. submitUpdatedSpeaker.php file gives blank screen when I do view source it prints Internal Server Error 500.

Issue is only occured when I submitted following text in textarea

“Unknown is a professional football quarterback currently playing for the Test
in the He was selected as the first overall pick in
the YEAR Draft by the Location.

Name attended School Name High School in City, Country, where he excelled as as
quarterback, amassing Number passing yards and Number touchdowns over three varsity seasons.
He then played college football at the Univeristy of State, Country, from 2013 to 2015.
During his tenure with the Country Club, Name set numerous school records,
including career passing yards (Number) and touchdown passes (Number). In his junior year, he
set Name single-season records with Number passing yards and Number touchdown passes.
Following this standout season, Name declared for the Shortform Draft.”

Force PHP to NOT handle MariaDB / MySQL errors [duplicate]

I think about a year ago after an update to PHP it suddenly started handling database errors itself where as I have an else condition and function that is universal in my code. This is unacceptable as my database error handling function is much more graceful for users whereas PHP just kills the connection…because it’s not designed to handle database errors.

<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
set_error_handler('error_report');
register_shutdown_function('error_report');
?>

How do I force PHP to ignore database errors while still handling the maximum sensitivity for all other (relevant) errors?

How to manage different weight units at the product varition level in WooCommerce?

Referencing this particular question: How to manage different weight units at the product level in WooCommerce?

I have a question in regards to adding something similar to this coding.

How would I get it to apply to the individual variations on a product? Bonus if it gets the weight to appear according to selection on the invoice as well.

(https://i.sstatic.net/JpESdTF2.png)

— Product Example — I am offering two versions of the same product but one is Standard Edition (weighs 13oz) and the other is a Special Edition (weighs 1lb 6oz because of bonus material). I want to be able to display those weights in the different weights (lbs & oz).

I tried the suggestion in the referenced post but am unsure how to get it to appear under the individual variations.

Promise state is ‘fulfilled’ but showing 404 Not Found Error

I am sending user data to the backend using api. And i have problems with certain “skills”. Here in the image, in “skills” Array, when user selected’010308′, it shows 404 Not found error.
But I am quite confused about why only the certain skills are having error although ‘010301’ and ‘010308’ are almost the same – both are string without parameters. but ‘010301’ doesn’t have problems but ‘010308’ is having problem.
Moreover, the promise state become fulfilled but the message shows skill not found.
This is my code for sending data.

const updateResumeRequest = request({
url: /v1/user/resume/${rid}/update,
method: ‘post’,
headers: {
Authorization: Bearer ${token},
},
data: parseRequestData(data),
});
console.log(‘parserequestdata data’, parseRequestData(data))
console.log(‘updateresumerequest’, updateResumeRequest)

Console image

How to set an initial value to PlaceAutocompleteElement

I am creating an autocomplete address input element via google.maps.places.PlaceAutocompleteElement, to let users autocomplete addresses.

The thing is that users have previously saved their address in our database, so I need to set the PlaceAutocompleteElement input an initial address value, but it seems it is impossible.

I have read the documentation about the different options that can be passed, but found nothing to set an initial value to the input.

How can I set an initial value to the input element created with google.maps.places.PlaceAutocompleteElement??

Thank you so much!

How to close dialog in React?

I have a general DialogueWindow component:

function DialogueWindow({elements}) {

  return (

    <div className="backdrop">

      <dialog className="dialogue-window">

        {elements}

      </dialog>

    </div>
  )
}

export default DialogueWindow

It displays inside of it whatever elements it gets passed down to. Currently I’m passing on a FileUploader element, that has a cancel button for the user to close the dialog.

In App.js:

<DialogueWindow
     elements={
        <FileUploader/>
     }
/>

In FileUploader:

function FileUploader() {

  ...

  return (

    <div className="file-uploader">

        ...

        <div className="file-uploader-button-container">

            <button>
                Upload
            </button>

            <button>
                Cancel
            </button>

        </div>

    </div>
  )
}

export default FileUploader

I have seen that in standard javascript you call document.getElementById() to fetch the dialog element, and then call the close() method on it. But how does this work in React?

Next.js multi-zones assets is not working

I want to build nextjs app that gonna be served like a SaaS, so i have a main team that gonna develop this apps, and another division team that gonna join development with us,

My project structure gonna look like this

src

  • -/app
      • -/services1
      • -/services2
  • -/sub-app
      • -/services-sub-app

i’m looking to build it using Next.js Multi-zones feature, but i have some issues while building it.

I managed to connect the main app to the sub-app using next.config.ts rewrites on the main app like this.

const nextConfig: NextConfig = {
    // For Static Export
    output: "standalone",
    basePath: "/app",
    assetPrefix: "/app",
    images: {
        unoptimized: true,
    },
    async rewrites() {
        return [
            {
                source: `/ppsdm`,
                destination: `http://localhost:3001/ppsdm`,
            },
            {
                source: `/ppsdm/:path*`,
                destination: `http://localhost:3001/ppsdm/:path*`,
            },
            {
                source: `/ppsdm-static/_next/:path*`,
                destination: `http://localhost:3001/ppsdm-static/_next/:path*`,
            },
        ];
    },
};

and next.config.ts on the sub-app like this

const nextConfig: NextConfig = {
    /* config options here */
    basePath: "/ppsdm",
    assetPrefix: "/ppsdm-static",
};

the routing works just fine, but the sub-app assets and styles it not working, it got a lot of 404 not found on the console like on the image i attached.

enter image description here

when i try to manually open the link on the console, let’s say this one
http://localhost:3000/ppsdm-static/_next/static/chunks/%5Broot-of-the-server%5D__8ebb6d4b._.css
it returns 404 Not Found.

but when i added “app” in front of the ppsdm-static, it works just fine
http://localhost:3000/app/ppsdm-static/_next/static/chunks/%5Broot-of-the-server%5D__8ebb6d4b._.css

What could be wrong?

Why does the window move when the user makes a selection?

In our JavaScript Vue code we have a template that looks approximately like this:

<template>
  <NiceDialog
    ref="selectMenuBody"
    closable
    modal
    v-model:visible="visible"
    :header="title"
    style="min-width: 35rem; width: auto"
    @keydown.escape.exact.stop.prevent="doHide"
    @keydown.space.stop.prevent=""
    @hide="doHide"
    @dragend="dragEnd"
    @mousedown="dragging = true"
  >
    <div class="ml-1">Charset:</div>
    <select v-model="selected" style="width: calc(100% - 0.4rem); margin: 0.2rem; height: 1.75rem">
      <option v-for="o in listItems" :key="o">{{ o }}</option>
    </select>
    <table>
      <tbody>
        <tr v-for="m in 8" :key="m - 1">
          <td v-for="n in 16" :key="(m - 1) * 16 + n - 1">
            <button
              @click.stop="_clickButton(charIfPrintable(m, n))"
              style="width: 3.3rem; height: 2.2rem"
              onclick="this.blur();"
            >
              {{ charIfPrintable(m, n) }}
            </button>
          </td>
        </tr>
      </tbody>
    </table>
  </NiceDialog>
</template>

What bothers us is that when we move the window and then make a selection in the select box, the position of the window is modified. It’s not like it jumps back to a default position but the position is simply changed a bit. We would like to make the position static. How can this be done?

Twilio WhatsApp Template – Dynamic Media URL and Button URL Not Working

I’m working on Twilio’s WhatsApp Business API integration using an approved message template.

The template includes:

Dynamic body fields (like product title, color, price)

A Header (Image)

A Button (with URL)

I am sending the WhatsApp message using Twilio’s Content API, passing dynamic values like this:

await twilioClient.messages.create({
from: whatsapp:${twilioPhoneNumber},
to: whatsapp:+${formattedPhoneNumber},
contentSid: process.env.TWILIO_CONTENT_TEMPLATE_SID,
contentVariables: JSON.stringify({
‘1’: title,
‘2’: color,
‘3’: price,
‘4’: imageUrl, // Dynamic image URL
‘5’: productUrl // Dynamic button URL
})
});
Current Situation:

The Body text fields (title, color, price) are being dynamically replaced correctly.

But the Header Image and Button URL are still showing the static URLs that were submitted during template approval, even though I pass dynamic values in code.

What I Expected:

I expected Twilio and WhatsApp to replace the media (image) and button URL with the dynamic values sent through contentVariables.

What Actually Happened:

Only body text changes dynamically.

Header Image and Button URL remain static — same as the original template content.

Research Summary:
After deep research, it looks like WhatsApp Business API currently does NOT support dynamic media URLs (Header) or dynamic button URLs at runtime.
Both media and buttons must be fixed (static) at the time of template creation and approval.

Only text placeholders in the Body are supported as dynamic values during message sending.

Question:

Is there any known workaround to make media URL and button URL dynamic at runtime on Twilio?

Or is this 100% a WhatsApp Business API limitation?

Progress Update:

I created multiple templates, tested with valid HTTPS URLs.

I’m passing all required dynamic values correctly via contentVariables.

No more 63005 or validation errors now.

The body text is dynamic, but media and button links remain static.

Thanks in advance!

Magnific popup working only in bigger screen

It is working on my screen and some of the testers screen but some of the testers that has smaller screen size it is opening a new link instead of the pop up

HTML:

<a class="popup-video" href="videos/onequote 1.mp4">
  <img src="images/thumb1.png">
</a>

SCRIPT:

$(function() {
                $('.popup-video').magnificPopup({
                    disableOn: 1000,
                    type: 'iframe',
                    mainClass: 'mfp-fade',
                    removalDelay: 160,
                    preloader: false,
                    fixedContentPos: false
                });
            });