Breaking a table row (tr) into two pages in mpdf or any other libraries

I am trying to break a table row (tr) into two pages if the content doesn’t fill the page,

Now it is breaking the whole into the next page which results in blank space in the first page above the footer

I am using the WriteHTML to generate pdf content

 foreach ($product_details as $prod_det) {
            $rate = format_currency($prod_det->qpd_rate);
            $amount = format_currency($prod_det->qpd_amount);
            $disc = number_format($prod_det->qpd_discount, 2);

            $pdf_data .= '
            <tr nobr="false">
                <td align="center" width="8%" style="padding: 2px; vertical-align: top;">' . $k . '</td>
                <td align="left" width="45%" style="padding: 2px;" vertical-align: top;>' . $prod_det->product_details . '</td>
                <td align="center" style="padding: 2px; vertical-align: top;" >' . $prod_det->qpd_quantity . '</td>
                <td align="center" style="padding: 2px; vertical-align: top;" >' . $prod_det->qpd_unit . '</td>
                <td align="right" style="padding: 2px; vertical-align: top;" >' . $rate . '</td>
                <td align="center" style="padding: 2px; color:red; vertical-align: top;" ><i>' . $disc . '</i></td>
                <td align="right" style="padding: 2px; vertical-align: top">' . $amount . '</td>
            </tr>
            ';
            $k++;
        }


 $mpdf = new MpdfMpdf([
            'margin_top' => 68,
            'margin_bottom' => 0,
            'margin_left' => 5,
            'margin_right' => 5,
            'defaultfooterline' => 0,
            'setAutoTopMargin'   => 'stretch',
            'setAutoBottomMargin'   => 'stretch',
            'keep_table_proportions' => false,
        ]);

 $mpdf->simpleTables = true;
        $mpdf->use_kwt = true;  // Keep-with-table
        $mpdf->packTableData = true;  // Better table handling
        $mpdf->shrink_tables_to_fit = 0;  // Don't shrink tables

        $mpdf->SetAutoPageBreak(true);

        $mpdf->SetTitle($title);


 $main_table = '<style>
                th, td { padding: 4px; font-size: 12px; }
                p { font-size: 12px; margin-bottom: 13px; } 
                table {
                page-break-inside: auto;
                }
                tr {
                    page-break-inside: auto;
                    page-break-after: auto;
                }
                td {
                    page-break-inside: auto;
                }
            </style>
            <table width="100%" style="page-break-inside:auto;border-collapse: collapse; margin-top: 10px;border-top:1px solid;line-height:18px;" autosize="1">
                <thead>
                    <tr>
                        <th align="center" width="8%" style="border-bottom:1px solid;">Item No</th>
                        <th align="center" width="45%" style="border-bottom:1px solid;">Description</th>
                        <th align="center" style="border-bottom:1px solid;">Qty</th>
                        <th align="center" style="border-bottom:1px solid;">Unit</th>
                        <th align="center" style="border-bottom:1px solid;">Rate</th>
                        <th align="center" style="border-bottom:1px solid;">Disc%</th>
                        <th align="center" style="border-bottom:1px solid;">Amount</th>
                    </tr>
                </thead>
                <tbody>
                ' . $pdf_data . '
                </tbody>
            </table>
            
            ';

$mpdf->WriteHTML($main_table);

//Other code

tried using page-break-inside: auto style inside table,tr,tbody, putting the row inside a whole tbody, in a whole table

See the blank space after the 9th row, the 10th is pushed to the next page, I want to take the space available and then push the remaining content to the next page

How to programmatically show out-of-stock products at the end of product listings in Shopware 6?

I am working on a Shopware 6 project and I want to change the product listing behavior on category pages (and search results).

Requirement:
Regardless of which sorting option a customer selects (price ascending, relevance, name, etc.), all out-of-stock products should always be shown at the end of the product listing.

What I’ve tried so far:

Subscribed to ProductListingCriteriaEvent and added an extra sorting:

$criteria->addSorting(new FieldSorting(‘stock’, FieldSorting::DESCENDING));

Custom password hasher cakephp 3.8

I’m throwing a message in a bottle for those who are fairly proficient with Cakephp 3.8. I admit I’m a little rusty after a few years of hiatus from development

I’ll explain my problem:
I have a basic module that allows user registration and login. So far, so good. My problem comes at the login stage, as I’m using a custom hash of the $password+$key type. The $key is automatically generated during registration and then stored in the users table with the other information.

I’d like to know if there is a way to retrieve this value without using manual checks in order to keep my code perfectly clean and functional.

Currently, I can enter any password, and $user returns true as long as the email address exists in the table.

src/Controller/Users/Usercontroller.php

public function register() {
     $userTable = TableRegistry::getTableLocator()->get('Users');
     $user = $this->Users->newEntity($this->request->getData(), ['validate' => 'UserStep1']);
     $user->type = $this->request->getSession()->read('account-type');
     $user->salt = sha1(md5($this->request->getSession()->read('salt')));
     $user->password = $hash->hash($this->request->getData('password'),$this->request->getSession()->read('salt'));

     if ($userTable->save($user)) {
     return true;
     }
}


public function login() {
        if($this->request->is('post')) {
            $user = $this->Auth->identify();
            if($user) {
                $this->Auth->setUser($user);
                $this->redirect($this->redirect($this->Auth->redirectUrl()));
                $this->Flash->response('Connecté', [
                    'key'   => 'response',
                    'params'    => [
                        'status'    => 'success'
                    ]
                ]);
            } else {
                $this->Flash->response('Identifiant ou mot de passe incorrect', [
                    'key'   => 'response',
                    'params'    => [
                        'status'    => 'error'
                    ]
                ]);
            }
        }
    }

src/Auth/CustomPasswordHasher.php

namespace Appauth;

use CakeAuthAbstractPasswordHasher;

class CustomPasswordHasher extends AbstractPasswordHasher {

    public function hash($password, $securestring = null) {
        $encode=  sha1(md5('stringmasked'). md5(sha1($password)).sha1(sha1(md5($securestring))));

        return $encode;
    }
    public function check($password, $salt)
    {
        $encode = sha1(md5('stringmasked').md5(sha1($password)).sha1(($salt)));

        return $encode;
    }
}


src/Controller/AppController.php

$this->loadComponent('Auth', [
            'authenticate'  => [
                'Form'  => [
                    'fields'    => ['username'  => 'email'],
                    'passwordHasher'    => ['className' => 'Custom']
                ]
            ]
        ]);
        $this->Auth->allow();

I admit that I’m a bit stuck on this problem, I don’t really know where the problem comes from

Thanks

PhpWord and n in line together with HTML code

Using PhpWord in my PHP project gives me issues.
I do have a variable containing CR/LF (n) and HTML code.
Something like:

$str='<div style="font-style: italic; text-align:center">Line1
line2
line3</div>';

The text is comming out of a MySql text field containing n as line separator.

When processing this text with PhpOfficePhpWordSharedHtml::addHtml, I am losing the CR/LF. All text is concatenated. When replacing n with html CRLF
$str=str_replace("n","<br>",$str); and then use addHtml, it gives me run-time errors:

Warning: DOMDocument::loadXML(): Opening and ending tag mismatch: br line 1 and div in Entity, line: 1 in /customers/7/4/3/ofiser.be/httpd.www/vieringen/vendor/phpoffice/phpword/src/PhpWord/Shared/Html.php on line 94

How can I process this text? Any help is appreciated.

how to fetch data from database with php (custom tables) and insert them into wordpress elementor element loop [closed]

I’m learning WordPress and migrating a site I built using plain HTML, CSS, jQuery, and PHP over to it. I’ve found that, using shortcodes, I can pull values from the database and display them on the frontend.

I’m using the free version of Elementor.

What I want to know is: can I fetch data directly from PHP and build the layout with iteration using Elementor, on top of that data? Obviously, I don’t know how many records I’ll be retrieving (it could be 5, 7, or 1,000 from the database). In other words, I’d like to pull a styled container and somehow inject each data item into it (each one inside a styled container), without having to worry about how many items are coming.

From what I’ve seen on ChatGPT, Google, etc., the typical suggestions are:

use the paid version of Elementor with something called Custom Fields (ACF), which I don’t want to pay for,

manually build everything with PHP using loops and string concatenation, which I’d like to avoid,

or fetch everything to the front and handle it with JavaScript.

Since my site will need to pull different sets of data on different pages (meaning I’ll need to iterate over that data and build layouts dynamically), if the answer is that Elementor can’t really handle this, then it seems like I would’ve been better off just skipping WordPress and doing it the traditional way.

So I’m wondering: what’s the actual advantage of using WordPress for these kinds of sites? Is it only useful when the content is fixed—static text and images?

PHP mysqli reusable functions for SELECT and INSERT/UPDATE queries [closed]

I’m learning PHP and wanted to create two simple reusable functions for MySQL database access —
one for selecting data, and one for modifying data.

I’m not sure if this is a good and safe way to handle it.
Can this structure cause any issues (for example: performance, error handling, or security problems)?

Here’s my code:

<?php
function getData($sql) {
    $db = new mysqli("localhost", "root", "", "cars");
    if ($db->connect_errno != 0) {
        return $db->connect_errno;
    }

    $result = $db->query($sql);
    if ($db->errno != 0) {
        return $db->error;
    }

    if ($result->num_rows == 0) {
        return [];
    }

    return $result->fetch_all(MYSQLI_ASSOC);
}

function setData($sql) {
    $db = new mysqli("localhost", "root", "", "cars");
    if ($db->connect_errno != 0) {
        return $db->connect_errno;
    }

    $db->query($sql);
    if ($db->errno != 0) {
        return $db->error;
    }

    return $db->affected_rows > 0 ? true : false;
}
?>

I expected these two functions to simplify my database access:

  • adatel() should return all selected rows as an array
  • adatVal() should return true/false depending on whether the query affected rows

It seems to work, but I want to make sure this is a good approach.
Should I use prepared statements instead, or is this fine for small school projects?

Laravel production error: “No application encryption key has..” and artisan key:generate fails [closed]

I deployed a Laravel 10 application (Ubuntu 22.04, Nginx, PHP 8.2-FPM). When hitting the login endpoint from my Vue frontend I got a 500 and this log entry:

production.ERROR: No application encryption key has been specified.

When I tried to run php artisan key:generate

I got instead:
In StreamHandler.php line 156:
The stream or file “/var/www/html/laravel-api/storage/logs/laravel.log” could not be opened in append mode: Failed to open stream: Permission denied
The exception occurred while attempting to log: file_put_contents(/var/www/html/laravel-api/.env): Failed to open stream: Permission denied

Environment

Laravel 10
Ubuntu 22.04
Nginx + PHP 8.2-FPM (user: www-data)
Manual deployment (no Docker)
The app had been built with php artisan config:cache earlier (before APP_KEY was properly generated) and after that all requests returned 500.

I suspect it is both a permissions issue and a cached configuration problem, but I want to confirm the correct, secure remediation steps (without using chmod 777).

What I tried:

php artisan config:clear → still the same error
chmod -R 777 storage → temporarily allows key:generate (I know this is insecure)
Re-ran php artisan key:generate but php artisan config:show app.key still empty
Cleared browser cache / restarted php-fpm (no change)
Rebuilt config cache too early (php artisan config:cache) while APP_KEY was still missing
Expected:

Laravel should have a valid APP_KEY in .env
php artisan config:show app.key should display the base64 key
No 500 error on authenticated endpoints

Trying to set HttpOnly but no changes are working

After a PenTest was told to set the HttpOnly flag on all of our cookies.

We have an Ubuntu Server 22.04 & 24.04 LAMP stack with a WordPress website. I have made changes to

  1. /etc/apache2/apache2.conf
  2. /etc/php/8.3/php.ini
  3. /var/www/html/wp-config.php

Each time, after clearing cookies in an Incognito (or InPrivate) Window and refreshing I see in the developer view in the browser the flags for HttpOnly or Secure are not checked.

what I have done

APACHE

First I make sure mod_headers is enabled by verifying headers.conf is located in /etc/apache2/mods-available/ and /etc/apache2/mods-enabled/.

In the /etc/apache2/apache2.conf file I add

<VirtualHost *:80>
Header edit Set-Cookie "(?i)^((?:(?!;\s?HttpOnly).)+)$" "$1; HttpOnly"
# or
 Header edit Set-Cookie ^(.*)$ "$1;HttpOnly"
</VirtualHost>

PHP

I add to or make sure these lines are uncommented in the /etc/php/8.3/fpm/php.ini file the following commands.

session.cookie_httponly = 1
session.cookie_secure = 1
session.use_only_cookies = 1

For good measure I set this also in the /etc/php/8.3/cli/php.ini file as well.

WP-CONFIG.PHP

The third place I’ve read to make setting changes is in the wp-config.php file found at the root of the WordPress site. So in my case, at /var/www/html/wp-config.php.

@ini_set('session.cookie_httponly', true); 
@ini_set('session.cookie_secure', true); 
@ini_set('session.use_only_cookies', true);

These lines are put in the file between these two commented lines in the file.

/* Add any custom values between this line and the "stop editing" line. */

/* That's all, stop editing! Happy publishing. */

RESTART APACHE

Then I test and restart Apache

# check the syntax
sudo apachectl -t

# restart
sudo systemctl restart apache2

# check
sudo systemctl status apache2

CHECK THE BROWSER

Then I delete the cookies, refresh the page or close the browser and re-open the site in Incognito or InPrivate mode, but the cookies never show HttpOnly or Secure being flagged.

Azure App Service (PHP 7.x) showing “No Request” after cloning, both original and cloned apps not responding [closed]

I have a PHP 7.x web app hosted on Azure App Service.
It was running perfectly fine until I used the “Clone App” option in the Azure Portal to duplicate it within the same resource group and region.

After cloning, both the original and the cloned apps stopped responding, when I click Browse, the browser just shows: No Request

Here’s what I’ve checked so far:

Both App Services are in Running state

Files (including index.php) exist under /site/wwwroot/ in Kudu

Kudu console works fine

Stack in Configuration → General settings is still set to PHP 7.x

Created a simple phpinfo.php file → same “No Request” output

Restarted both apps multiple times

It looks like IIS or the FastCGI PHP handler isn’t processing requests anymore after cloning, but there are no errors in the log stream.

Is there a way to restore PHP handling or reconfigure FastCGI after cloning?
Or does the clone process break PHP runtime mapping in some way?

Join API endpoints and write out result

I would like to join endpoints and return result (in WordPress). After I get the results, I want to import with WP All Import plugin, Hotels and Locations. I’m using Traveler theme.

The API is: https://www.postman.com/filipnet-team/karpaten-webservice/collection/4gcaetc/karpaten-webservice

I made a diagramm too:
enter image description here

It should be joined the following way: Countries (->CountryDetails) -> Regions -> Resorts -> Hotels (->HotelDetails)

The results must be shown in a blank page.

What I tried – The other parts are working fine, except the 6th, where I tried to join the API Endpoints – Error: No countries returned from API / There has been a critical error on this website:

// ----------------------------
// 0. API_SECRET_TOKEN API 
// ----------------------------
// Generate a long random token once and paste it here
define('API_SECRET_TOKEN', 'a5ef2d6a717f47ddaf78d98a04e28e20');

add_action('init', function () {

    $api_pages = array(
        'kcountrylist-api' => array( /*CountryList*/
            'endpoint' => 'https://webservice.karpaten.ro/webservice/',
            'method'   => 'POST',
            'headers'  => array('Content-Type' => 'text/xml; charset=utf-8'),
            'body'     => '<root>
                           <head>
                           <auth>
                           <username>XML.naturatravel</username>
                           <password>naturatravel</password>
                           </auth>
                           <service>GetCountriesList</service>
                           </head>
                           <main></main>
                           </root>'
        ),
        'kcountrydet-api' => array( /*CountryDetails*/
            'endpoint' => 'https://webservice.karpaten.ro/webservice/',
            'method'   => 'POST',
            'headers'  => array('Content-Type' => 'text/xml; charset=utf-8'),
            'body'     => '<root>
                           <head>
                           <auth>
                           <username>XML.naturatravel</username>
                           <password>naturatravel</password>
                           </auth>
                           <service>GetCountryDetails</service>
                           </head>
                           <main>
                           <CountryID>{{CountryID}}</CountryID>
                           </main>
                           </root>'
        ),
        'kregionlist-api' => array( /*RegionList*/
            'endpoint' => 'https://webservice.karpaten.ro/webservice/',
            'method'   => 'POST',
            'headers'  => array('Content-Type' => 'text/xml; charset=utf-8'),
            'body'     => '<root>
                           <head>
                           <auth>
                           <username>XML.naturatravel</username>
                           <password>naturatravel</password>
                           </auth>
                           <service>GetRegionsList</service>
                           </head>
                           <main>
                           <CountryID>{{CountryID}}</CountryID>
                           </main>
                           </root>'
        ),
        'kregiondet-api' => array( /*RegionDetails*/
            'endpoint' => 'https://webservice.karpaten.ro/webservice/',
            'method'   => 'POST',
            'headers'  => array('Content-Type' => 'text/xml; charset=utf-8'),
            'body'     => '<?xml version="1.0" encoding="UTF-8"?>
                           <root>
                           <head>
                           <auth>
                           <username>XML.naturatravel</username>
                           <password>naturatravel</password>
                           </auth>
                           <service>GetRegionDetails</service>
                           </head>
                           <main>
                           <RegionID>{{RegionID}}</RegionID>
                           </main>
                           </root>'
        ),
        'kresortlist-api' => array( /*ResortList*/
            'endpoint' => 'https://webservice.karpaten.ro/webservice/',
            'method'   => 'POST',
            'headers'  => array('Content-Type' => 'text/xml; charset=utf-8'),
            'body'     => '<?xml version="1.0" encoding="UTF-8"?>
                           <root>
                           <head>
                           <auth>
                           <username>XML.naturatravel</username>
                           <password>naturatravel</password>
                           </auth>
                           <service>GetResortsList</service>
                           </head>
                           <main>
                           <CountryID>{{CountryID}}</CountryID>
                           <RegionID>{{RegionID}}</RegionID>
                           </main>
                           </root>'
        ),
        'kresortdet-api' => array( /*ResortDet*/
            'endpoint' => 'https://webservice.karpaten.ro/webservice/',
            'method'   => 'POST',
            'headers'  => array('Content-Type' => 'text/xml; charset=utf-8'),
            'body'     => '<?xml version="1.0" encoding="UTF-8"?>
                           <root>
                           <head>
                           <auth>
                           <username>XML.naturatravel</username>
                           <password>naturatravel</password>
                           </auth>
                           <service>GetResortDetails</service>
                           </head>
                           <main>
                           <ResortID>{{ResortID}}</ResortID>
                           </main>
                           </root>'
        ),
        'khotelslist-api' => array( /*Hotels*/
            'endpoint' => 'https://webservice.karpaten.ro/webservice/',
            'method'   => 'POST',
            'headers'  => array('Content-Type' => 'text/xml; charset=utf-8'),
            'body'     => '<?xml version="1.0" encoding="UTF-8"?>
                           <root>
                           <head>
                           <auth>
                           <username>XML.naturatravel</username>
                           <password>naturatravel</password>
                           </auth>
                           <service>GetHotelsList</service>
                           </head>
                           <main>
                                <!-- <ResortID>{{ResortID}}</ResortID> -->
                                <RegionID>{{RegionID}}</RegionID>
                                <CountryID>{{CountryID}}</CountryID>
                           </main>
                           </root>'
        ),
        'khoteldet-api' => array( /*Hotel Details*/
            'endpoint' => 'https://webservice.karpaten.ro/webservice/',
            'method'   => 'POST',
            'headers'  => array('Content-Type' => 'text/xml; charset=utf-8'),
            'body'     => '<?xml version="1.0" encoding="UTF-8"?>
                           <root>
                           <head>
                           <auth>
                           <username>XML.naturatravel</username>
                           <password>naturatravel</password>
                           </auth>
                           <service>GetHotelDetails</service>
                           </head>
                           <main>
                                <HotelID>{{HotelID}}</HotelID>
                           </main>
                           </root>'
        )
    );

    if (isset($_GET['api_page']) && isset($api_pages[$_GET['api_page']])) {

        if (!isset($_GET['token']) || $_GET['token'] !== API_SECRET_TOKEN) {
            status_header(403);
            wp_die('Forbidden: Invalid token');
        }

        $config = $api_pages[$_GET['api_page']];
        $method = strtoupper($config['method']);

        $body = $config['body'];

        // ---- Handle dynamic parameters ----
        if ($method === 'POST') {
            foreach ($_GET as $key => $value) {
                if (in_array($key, ['api_page','token','format'])) continue;
                $value = sanitize_text_field($value);
                $body = str_replace('{{'.$key.'}}', $value, $body);
            }

            if (stripos($config['headers']['Content-Type'], 'json') !== false) {
                $body_arr = json_decode($body, true);
                if (isset($body_arr['root']['main'])) {
                    foreach ($_GET as $key => $value) {
                        if (in_array($key, ['api_page','token','format'])) continue;
                        $body_arr['root']['main'][$key] = sanitize_text_field($value);
                    }
                }
                $body = json_encode($body_arr);
            }
            elseif (stripos($config['headers']['Content-Type'], 'xml') !== false) {
                $body = preg_replace('/&(?![a-zA-Z#0-9]+;)/', '&amp;', $body);
                $xml = new DOMDocument();
                $xml->loadXML($body);

                $main = $xml->getElementsByTagName('main')->item(0);
                foreach ($_GET as $key => $value) {
                    if (in_array($key, ['api_page','token','format'])) continue;
                    $found = $xml->getElementsByTagName($key)->item(0);
                    if (!$found) {
                        $el = $xml->createElement($key, htmlspecialchars($value, ENT_XML1 | ENT_COMPAT, 'UTF-8'));
                        $main->appendChild($el);
                    } else {
                        $found->nodeValue = htmlspecialchars($value, ENT_XML1 | ENT_COMPAT, 'UTF-8');
                    }
                }
                $body = $xml->saveXML();
            }
        }

        // ---- Build request ----
        $args = array(
            'method'  => $method,
            'headers' => $config['headers']
        );

        if ($method === 'POST') {
            $args['body'] = $body;
            $url = $config['endpoint'];
        } else {
            $params = $_GET;
            unset($params['api_page'], $params['token'], $params['format']);
            $url = add_query_arg($params, $config['endpoint']);
        }

        // ---- Send ----
        $response = wp_remote_request($url, $args);

        if (is_wp_error($response)) {
            wp_die($response->get_error_message());
        }

        $body = wp_remote_retrieve_body($response);

        // ---- Clean ----
        $body = html_entity_decode($body, ENT_QUOTES | ENT_HTML5 | ENT_SUBSTITUTE, 'UTF-8');
        $body = preg_replace('/>s+</', '><', $body);
        $body = preg_replace('/<!--.*?-->/s', '', $body);
        //$body = preg_replace('/<![CDATA[(.*?)]]>/', '$1', $body);
        $body = preg_replace('/&(?![a-zA-Z#0-9]+;)/', '&amp;', $body);

        // ---- Output ----
        $format = isset($_GET['format']) ? strtolower($_GET['format']) : 'json';

        if ($format === 'xml') {
            if (stripos($body, '<?xml') === 0) {
                header('Content-Type: application/xml; charset=utf-8');
            } else {
                header('Content-Type: application/json; charset=utf-8');
            }
            echo $body;
            exit;
        }

        // Else: convert to JSON
        $data = null;
        if (stripos($body, '<?xml') === 0 || stripos($body, '<root') !== false) {
            libxml_use_internal_errors(true);
            $xml = simplexml_load_string($body, 'SimpleXMLElement', LIBXML_NOCDATA);
            if ($xml !== false) {
                $data = simplexml_to_array_keep_html($xml,false);
            }
        }

        if ($data === null) {
            $json = json_decode($body, true);
            if (json_last_error() === JSON_ERROR_NONE) {
                $data = $json;
            } else {
                $data = ['raw' => $body];
            }
        }

        header('Content-Type: application/json; charset=utf-8');
        echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
        exit;
    }
});
function simplexml_to_array_keep_html($xml, $encode_html = true)
{
    $arr = [];

    foreach ($xml->children() as $key => $child) {
        if ($child->count() > 0) {
            $value = simplexml_to_array_keep_html($child, $encode_html);
        } else {
            $raw = trim($child->asXML());
            $inner = preg_replace('/^<[^>]+>|</[^>]+>$/', '', $raw);

            // Encode HTML if requested
            if ($encode_html) {
                $inner = htmlentities($inner, ENT_QUOTES | ENT_HTML5, 'UTF-8');
            }

            $value = $inner;
        }

        if (isset($arr[$key])) {
            if (!is_array($arr[$key]) || !isset($arr[$key][0])) {
                $arr[$key] = [$arr[$key]];
            }
            $arr[$key][] = $value;
        } else {
            $arr[$key] = $value;
        }
    }

    return $arr;
}
// ----------------------------
// 5. Examples
// ----------------------------
/*1. https://test.naturatravel.ro/?api_page=kcountrylist-api&token=a5ef2d6a717f47ddaf78d98a04e28e20*/
/*2. https://test.naturatravel.ro/?api_page=kcountrydet-api&token=a5ef2d6a717f47ddaf78d98a04e28e20&CountryID=187*/
/*3. https://test.naturatravel.ro/?api_page=kregionlist-api&token=a5ef2d6a717f47ddaf78d98a04e28e20&CountryID=215*/
/*4. https://test.naturatravel.ro/?api_page=kregiondet-api&token=a5ef2d6a717f47ddaf78d98a04e28e20&RegionID=325*/
/*5. https://test.naturatravel.ro/?api_page=kresortlist-api&token=a5ef2d6a717f47ddaf78d98a04e28e20&CountryID=215&RegionID=325*/
/*6. https://test.naturatravel.ro/?api_page=kresortdet-api&token=a5ef2d6a717f47ddaf78d98a04e28e20&ResortID=2133*/
/*7. https://test.naturatravel.ro/?api_page=khotelslist-api&token=a5ef2d6a717f47ddaf78d98a04e28e20&RegionID=75&CountryID=7 */
/*8. https://test.naturatravel.ro/?api_page=khoteldet-api&token=a5ef2d6a717f47ddaf78d98a04e28e20&HotelID=29274 */
// ----------------------------
// Preserve HTML in post_content
// ----------------------------
add_filter('wpgetapi_api_to_posts_before_insert_post', function($post_data, $item, $map) {

    // Apply only to st_location posts
    if ($post_data['post_type'] === 'st_location') {

        foreach($map as $field_map) {
            if($field_map['post_field'] === 'post_content') {
                $api_field = $field_map['api_field'];
                if(isset($item[$api_field])) {
                    // Insert raw HTML from API
                    $post_data['post_content'] = $item[$api_field];
                }
            }
        }
    }

    return $post_data;

}, 10, 3);
// ----------------------------
// 6. Custom Combined Hotels API
// ----------------------------
// ----------------------------
// 0. API_SECRET_TOKEN
// ----------------------------
//define('API_SECRET_TOKEN', 'a5ef2d6a717f47ddaf78d98a04e28e20');

// ----------------------------
// Helper: fetch JSON from URL
// ----------------------------
function fetch_json($url) {
    $res = wp_remote_get($url);
    if (is_wp_error($res)) return null;
    $body = wp_remote_retrieve_body($res);
    $data = json_decode($body, true);
    return $data ?: null;
}

// ----------------------------
// Full Hotels JSON
// ----------------------------
add_action('init', function() {

    if(isset($_GET['api_page']) && $_GET['api_page'] === 'kfull-hotels-json') {

        if(!isset($_GET['token']) || $_GET['token'] !== API_SECRET_TOKEN){
            status_header(403);
            wp_die('Forbidden: Invalid token');
        }

        $endpoint = 'https://test.naturatravel.ro/';
        $token = $_GET['token'];
        $final = [];

        // 1️⃣ Countries
        $countries_data = fetch_json("$endpoint?api_page=kcountrylist-api&token=$token");
        if(!$countries_data || !isset($countries_data['GetCountriesList']['CountriesList']['Country'])) {
            wp_die('No countries returned from API');
        }

        $countries = (array)$countries_data['GetCountriesList']['CountriesList']['Country'];

        foreach($countries as $country) {
            $c_id = $country['ID'];
            $c_name = $country['Name'];
            $c_code = $country['CodIso2'] ?? '';

            // 2️⃣ CountryDetails
            $c_det_data = fetch_json("$endpoint?api_page=kcountrydet-api&token=$token&CountryID=$c_id");
            $c_desc = $c_det_data['GetCountryDetails']['Country']['Description'] ?? '';

            // 3️⃣ Regions list
            $regions_data = fetch_json("$endpoint?api_page=kregionlist-api&token=$token&CountryID=$c_id");
            $regions_arr = [];
            if(isset($regions_data['GetRegionsList']['RegionsList']['Region'])) {
                $regions = (array)$regions_data['GetRegionsList']['RegionsList']['Region'];
                foreach($regions as $region) {
                    $r_id = $region['ID'];
                    $r_name = $region['Name'];

                    // 4️⃣ Resort list
                    $resorts_data = fetch_json("$endpoint?api_page=kresortlist-api&token=$token&CountryID=$c_id&RegionID=$r_id");
                    $resorts_arr = [];
                    if(isset($resorts_data['GetResortsList']['ResortsList']['Resort'])) {
                        $resorts = (array)$resorts_data['GetResortsList']['ResortsList']['Resort'];
                        foreach($resorts as $resort) {
                            $res_id = $resort['ID'];
                            $res_name = $resort['Name'];

                            // 5️⃣ Hotels list
                            $hotels_data = fetch_json("$endpoint?api_page=khotelslist-api&token=$token&CountryID=$c_id&RegionID=$r_id");
                            $hotels_arr = [];
                            if(isset($hotels_data['GetHotelsList']['HotelsList']['Hotel'])) {
                                $hotels = (array)$hotels_data['GetHotelsList']['HotelsList']['Hotel'];
                                foreach($hotels as $hotel) {
                                    $h_id = $hotel['ID'];
                                    $h_name = $hotel['Name'];
                                    $h_stars = $hotel['Stars'] ?? '';

                                    // 6️⃣ HotelDetails
                                    $h_det_data = fetch_json("$endpoint?api_page=khoteldet-api&token=$token&HotelID=$h_id");
                                    $h_desc = $h_det_data['GetHotelDetails']['Hotel']['Description'] ?? '';
                                    $h_policies = $h_det_data['GetHotelDetails']['Hotel']['Policies'] ?? '';

                                    $hotels_arr[] = [
                                        'hotel_id' => $h_id,
                                        'hotel_name' => $h_name,
                                        'stars' => $h_stars,
                                        'description' => $h_desc,
                                        'policies' => $h_policies
                                    ];
                                }
                            }

                            $resorts_arr[] = [
                                'resort_id' => $res_id,
                                'resort_name' => $res_name,
                                'hotels' => $hotels_arr
                            ];
                        }
                    }

                    $regions_arr[] = [
                        'region_id' => $r_id,
                        'region_name' => $r_name,
                        'resorts' => $resorts_arr
                    ];
                }
            }

            $final[] = [
                'country_id' => $c_id,
                'country_name' => $c_name,
                'country_code' => $c_code,
                'description' => $c_desc,
                'regions' => $regions_arr
            ];
        }

        header('Content-Type: application/json; charset=utf-8');
        echo json_encode($final, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
        exit;
    }

});

how to make a html contact form send message using html [duplicate]

I’m currently doing a project that involves making a contact form for a website. I would like to know how can I make the form send the message to the recipient using html and php. the php code also appears every time I click the send button.

This is what I’ve tried so far.

<form action="POST" action="st.php">
  <p>
    <label for="first">First Name:</label>
    <input type="text" id="firstname" name="user_first">
  </p>
  <p>
    <label for="last">Last name:</label>
    <input type="text" id="lastname" name="user_last">
  </p>
  <p>
    <label for="email">Email:</label>
    <input type="text" id="email" name="user_email">
  </p>
  <p>
    <label for="message">Message:</label>
    <textarea  type="text" id="message" name="user_message"></textarea>
  </p>
  <p class="button">
    <button type="submit" id="submit" name="submit">Send</button>
  </p>
</form>
if ($_SERVER["REQUEST_METHOD"] == "POST") {
    // Get POST data
    $firstname = isset($_POST['firstname']) ? strip_tags(trim($_POST['firstname'])) : '';
    $lastname = isset($_POST['lastname']) ? strip_tags(trim($_POST['lastname'])) : '';
    $email = isset($_POST['Email']) ? trim($_POST['Email']) : '';
    $message = isset($_POST['message']) ? strip_tags(trim($_POST['message'])) : '';
    $button = isset($_POST['button']) ? strip_tags(trim($_POST['button'])) : '';

    // Validate form fields
    if (empty($first)) {
        $errors[] = 'First Name is empty';
    }
    
    if (empty($last)) {
        $errors[] = 'Last Name is empty';
    }


    if (empty($email)) {
        $errors[] = 'Email is empty';
    } else if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        $errors[] = 'Email is invalid';
    }

    if (empty($message)) {
        $errors[] = 'Message is empty';
    }
    

    // If no errors, send email
    if (empty($errors)) {
        // Recipient email address (replace with your own)
        $recipient = "[email protected]";
?>

How to Connect Sanmar API in WordPress WooCommerce REST API? [closed]

I’m trying to integrate the Sanmar Web Services API with my WooCommerce store in WordPress. I want to import products and keep inventory updated using the API.

I have received the following from Sanmar:

WSDL URL for API

SFTP access to download CSV files (SanMar_SDL_N.csv, SanMar_EPDD.csv, sanmar_dip.txt)

Credentials (Username & Password) via a secure Bitwarden link

My questions:

How can I connect the Sanmar API directly with WooCommerce?

Which WordPress/WooCommerce plugins are recommended for SOAP API integration?

How to handle bulk product import (getProductBulkInfo) and inventory updates efficiently?

Do I need to create a custom plugin or is there a ready solution?

I’m new to WooCommerce REST API and SOAP integration, so any guidance, code snippets, or plugin recommendations will be really helpful.

Thanks in advance!

note: I purchased the WooCommerce API Product Sync with WooCommerce REST APIs plugin to connect Sanmar API and sync products and inventory automatically , or any easy way to connect.

Getting WooCommerce “View Product” Link To Appear Everywhere

I have this code that I have added to WP site using the Snippets plugin. The code is intended to display a “View Product” link in addition to the “Add to Cart” button for WooCommerce.

The code works! It shows the “View Product” link as needed.

THE ISSUE:

The issue however is that the “View Product” link only appears on the Shop page (which displays all items on the store) and on the item Categories/Tags pages.

The “View Product” link doesn’t appear for Related Products, Cross Sells, or Handpicked Products, etc.

I need the “View Product” link to appear for all items wherever they are displayed on the website.

I could really use some help with this. Thanks in advance!

This is the code that I have implemented:

    /**
* @snippet Add ‘View Product’ button before ‘Add to Cart’ in WooCommerce
* @compatible WC 6.3.1
*/
add_action('woocommerce_after_shop_loop_item', 'wptechnic_custom_button_view_product', 5 );
function wptechnic_custom_button_view_product() {
    global $product;

    /* Ignore for Variable and Group products
    if( $product->is_type('variable') || $product->is_type('grouped') ) return;
    */

    // Display the custom button
    echo '<a style="margin-right:5px" class="button wptechnic-custom-button-view-product" href="' . esc_attr( $product->get_permalink() ) . '">' . __('View product') . '</a>';
}

How can I invoke aplay from nginx/php

I am trying to make aplay play a wav file on my Beaglebone running Debian (trixie).

I can do it from an ordinary login user no problem.

aplay -D dmix:CARD=Device,DEV=0 somefile.wav

works perfectly and plays the sound on a USB speaker, so I believe the hardware is all just fine. The problem arises when I try and invoke aplay from a PHP script running under nginx. That is running under user www-data so I added www-data to the audio group. Then I was able to do this:

sudo -u www-data aplay -l

which shows me the active audio devices and I can also tell it to play the audio file, and it does successfully. It only fails when it is running in the real environment ie under nginx. In that environment aplay returns a status 1 and I can’t tell why.

More specifically, PHP runs a shell_exec to invoke a small bash script that invokes aplay and logs a few things. I can run this bash script from my login user and it works, and under nginx/php it logs its output just fine so I know it is running and that it is invoking the wav file I want it to.

The target file name is definitely correct. I use the full path and I log it. I can copy/paste from the log and invoke aplay with that file and it works fine. Permissions on the file are -rw-rw-r-- so it is readable by anyone. It is owned by a different user, though.

User www-data has no ~/.asoundrc file and neither does my login user.

I know this can work because I had it running on this machine up until I rebuilt it. I’m fairly sure there is some, hopefully obvious, trick I have forgotten from the first time I set it up, though that was pre-trixie.

Doctrine referenceone gives only superclass

I’m refactoring old symfony 3 and doctrine 1.3 project;

I have 2 classes in same table: Journal and Conference mapped by their same parent Periodic.

/**
 * @ODMDocument(collection="periodics")
 * @ODMInheritanceType("SINGLE_COLLECTION")
 * @ODMDiscriminatorField("periodic_type")
 * @ODMDiscriminatorMap({
        "journal"=BundleDocumentJournal::class,
        "conference"=BundleDocumentConference::class,
    })
 * @GedmoSoftDeleteable(fieldName="deletedAt", timeAware=false)
 */
class Periodic {
...
}

/**
 * @ODMDocument()
 */
class Conference extends Periodic {
...
}

/**
 * @ODMDocument()
 */
class Journal extends Periodic {
...
}

I have also a class Assembly with referenceOne to Periodic:

class Assembly{
    /**
     * @ODMReferenceOne(
     *     targetDocument=Periodic::class,
     *     nullable=true
     * )
     */
    protected $periodic;

    /**
     * @return mixed
     */
    public function getPeriodic()
    {
        return $this->periodic;
    }

    /**
     * @param mixed $periodic
     * @return $this
     */
    public function setPeriodic($periodic)
    {
        $this->periodic = $periodic;
        return $this;
    }
}

The is a problem with getting Periodic from Assembly as I got object of Periodic class not Journal or Conference.
Please advice how to deal with it.