add an external md file to a php page

… and, of course see it as html, with the same graphical appearance of any other web text.

A way can be use a very light javascript, md-block.js, di Lea Verou.

Then you can add a function to your php website, like the following:

<?php
/* to call a file Markdown from a given folder */

function scheda_md($file)
{
    global $yourwebsite;

    $file = basename($file);

    return '<md-block src="' .
           $yourwebsite .
           '/the-path/where-are-md-files/from-yourwebsite-root' .
           htmlspecialchars($file, ENT_QUOTES, 'UTF-8') .
           '"></md-block>';
}
?>

And in the page where you want to show that md file you can use this code:

<?= scheda_md('the-md-name.md') ?>



mysql multiple tables rename

To rename many mysql/mariadb tables with a same prefix, you can uso this script (in a terminal)

mysql -u root -p -N -e " SELECT CONCAT('RENAME TABLE \', table_name, '\ TO \', REPLACE(table_name, 'oldprefix_', 'newprefix_'), '\;') FROM information_schema.tables WHERE table_schema = 'your-database' AND table_name LIKE 'oldprefix_%'; " > renamed.sql 

In this way will created the file renamed.sql (same tables, with different names), that you then can use to replace your database, with

mysql -u root -p yourdatabase < renamed.sql

If you have a wordpress database you have also to check options and meta tables, removing oldprefixes, with:

UPDATE oldprefix_options
SET option_name = REPLACE(option_name, 'oldprefix_', 'newprefix_')
WHERE option_name LIKE 'oldprefix\_%';

and

UPDATE abc_usermeta
SET meta_key = REPLACE(meta_key, 'oldprefix_', 'newprefix_')
WHERE meta_key LIKE 'oldprefix\_%';

And finally, you could check if there are any oldprefix, in options with

SELECT option_name FROM newprefix_options WHERE option_name LIKE 'oldprefix\_%';

same for meta:

SELECT meta_key FROM newprefix_usermeta WHERE meta_key LIKE 'oldprefix\_%';

Automatically link youtube videos

You could try with a php function the following:

<?php
function getYouTubeVideosByTag($tag, $maxResults = 6) {
    $apiKey = '[your youtube API key]';
    $channelId = '[your-youtube-channel-ID';

    // Forza formato hashtag
    $query = urlencode('#' . $tag);

    $url = "https://www.googleapis.com/youtube/v3/search?part=snippet&type=video&order=date&maxResults=$maxResults&q=$query&channelId=$channelId&key=$apiKey";

    $response = file_get_contents($url);

    if ($response === FALSE) {
        return [];
    }

    $data = json_decode($response, true);

    $videos = [];

    if (!empty($data['items'])) {
        foreach ($data['items'] as $item) {
            if (!isset($item['id']['videoId'])) continue;

            $videos[] = [
                'title' => $item['snippet']['title'],
                'videoId' => $item['id']['videoId'],
                'description' => $item['snippet']['description'],
                // 'thumbnail' => $item['snippet']['thumbnails']['medium']['url']
            ];
        }
    }

    return $videos;
}
?>

Obviously you have to link this function in every web page you want links your videos, and in these pages you can call that function by a code like this:

<?php
if (!empty($youtubetag)) {

    if (is_array($youtubetag)) {
        $query = implode(' OR ', array_map(fn($t) => '#' . $t, $youtubetag));
        $videos = getYouTubeVideos($query, 5);
    } else {
        $videos = getYouTubeVideosByTag($youtubetag, 3);
    }

    if (!empty($videos)) {
        ?>

        <section class="youtube-videos">
        <h2>🎙️ Our podcast (YouTube)</h2>

            <ul>
                <?php foreach ($videos as $video): ?>
                    <li class="podcast">🎙️
                        <a href="https://www.youtube.com/watch?v=<?= $video['videoId'] ?>"><strong><?= htmlspecialchars($video['title']) ?></strong></a>:
                       <?= htmlspecialchars(substr($video['description'], 0, 120), ENT_QUOTES | ENT_HTML5, 'UTF-8', false) ?>
                    </li>
                <?php endforeach; ?>
            </ul>
        </section>

        <?php
    }
}
?>

Finally, you should define, possibly in the head of each php page, where you want to link youtube videos, the variable $youtubetag:

$youtubetag="my tag";
or 
$youtubetag="['mytag1', 'mytag2']";

How to toggle the opened <details>

If you have two <details> tags, and you want to close one, when you open the other, you can use this short js:

<script>
const details = document.querySelectorAll("details");

details.forEach(d => {
  d.addEventListener("toggle", () => {
    if (d.open) {
      details.forEach(other => {
        if (other !== d) {
          other.removeAttribute("open");
        }
      });
    }
  });
});
</script>

How to avoid double slash

If your link points to a folder it should ends with a slash (/), but if you use a php variable to define it and you sometimes need to add some page at the end of your link, you could have a problem: the double slash (//).
I.g. you could have this variable php

$mypath="mypath1/mypath2/";

The final slash is recommended to avoid a redirect (that you would have with $mypath="mypath1/mypath2).
And you can call that variable in all your webpages pointing at that folder, in this way:

<a href=”<?php echo $mypath ?>”>some name</a>

But if you have to point to a specific page in that folder, you shouldn’t use this code:

<a href="<?php echo "$mypath/a-specific-page.php" ?>">some other name</a>

otherwise you will get, in the final html, a double slash. No way, course to write $mypatha-specific-page.php , because in this case your would call a not existing variable ($mypatha-specific-page.php ).

A good solution could then be this

<a href="<?php echo $mypath . 'a-specific-page.php' ?>">some other name</a>


PhpMyAdmin: wildcards as default in search tab

The new PhpMyAdmin, the 5.2.3, unlike the previous, the 5.2.2, has “like” instead of “like %…%” as default search command.

If you prefer the previous default you can do it following these instructions .

I managed to bo back to the previous default modifying only the file
phpMyAdmin/libraries/classes/Types.php
at rows 99 -100

from

return [
'LIKE',
'LIKE %...%',

to
return [
'LIKE %...%',
'LIKE',


semantic tags

Html5 helps to use semantic tags, but only in some cases.

Dates

I.g. for a date you can use <time>

    <p>La conferenza si terrà il <time datetime="2025-11-14">14 novembre 2025</time>.</p>

Books

Far more complicated is the way for the books. There is not a tag such as <book>. Unfortunately, the Internet is not for the truth, but for commercial purposes, so you have to follow a very long and annoying way:

     <h1>List of Books</h1>

    <div itemscope itemtype="http://schema.org/Book">
        <h2 itemprop="name">The Great Gatsby</h2>
        <img src="cover-gatsby.jpg" alt="Cover of The Great Gatsby" itemprop="image">
        <p itemprop="author">F. Scott Fitzgerald</p>
        <p itemprop="description">The Great Gatsby is a novel written by F. Scott Fitzgerald and published in 1925. The story is set in 1922 during the Jazz Age and the economic prosperity of the United States.</p>
    </div>

    <div itemscope itemtype="http://schema.org/Book">
        <h2 itemprop="name">1984</h2>
        <img src="cover-1984.jpg" alt="Cover of 1984" itemprop="image">
        <p itemprop="author">George Orwell</p>
        <p itemprop="description">1984 is a dystopian novel written by George Orwell and published in 1949. The story is set in a future dystopia where the totalitarian government controls every aspect of citizens' lives.</p>
    </div>

    <div itemscope itemtype="http://schema.org/Book">
        <h2 itemprop="name">Moby Dick</h2>
        <img src="cover-moby-dick.jpg" alt="Cover of Moby Dick" itemprop="image">
        <p itemprop="author">Herman Melville</p>
        <p itemprop="description">Moby Dick is a novel written by Herman Melville and published in 1851. The story follows the obsessive quest of Captain Ahab to hunt down the white whale, Moby Dick.</p>
    </div>

another example:

<tr itemscope itemtype="http://schema.org/Book"><td><span itemprop="name">Traumdeutung</span></td>
<td><span itemprop="alternativeHeadline"> L'interpretazione dei sogni</span></td><td><span itemprop="datePublished" content="1900">1900</span></td></tr>
<tr itemscope itemtype="http://schema.org/Book"><td><span itemprop="name">Zur Psychopathlogie des Altagslebens</span></td>
<td><span itemprop="alternativeHeadline"> Psicopatologia della vita quotidiana</span></td><td><span itemprop="datePublished" content="1901">1901</span></td></tr>

other book’s tags

<span itemprop=”locationCreated”>

<span itemprop=”bookEdition”> (for Edition)

<span itemprop=”author”> (for Author)

Movies

You could use a code like the following:

<div itemscope itemtype="http://schema.org/Movie">
  <h1 itemprop="name">The Godfather</h1>
  <h2 itemprop="alternateName">Il Padrino</h2>
  <img itemprop="image" src="https://example.com/locandina_il_padrino.jpg" alt="Locandina del film Il Padrino">
  <p>Regista: <span itemprop="director">Francis Ford Coppola</span></p>
  <p>Data di rilascio: <span itemprop="releaseDate">15 marzo 1972</span></p>
</div>
more here.

esport and show FF bookmarks in a webpage

With the help of IA I managed to get Firefox bookmarks

import os
import json
import sqlite3
import shutil
import tempfile

profile_name = "your-profile"  # <-- cambia con il tuo
profile_path = os.path.expanduser(f"~/.mozilla/firefox/{profile_name}")
db_path = os.path.join(profile_path, "places.sqlite")

if not os.path.isfile(db_path):
    print(f"Il file places.sqlite non esiste: {db_path}")
    exit(1)

# Copia temporanea per evitare blocchi
tmp_dir = tempfile.gettempdir()
temp_db_path = os.path.join(tmp_dir, "places_copy.sqlite")
shutil.copy2(db_path, temp_db_path)

conn = sqlite3.connect(temp_db_path)
conn.row_factory = sqlite3.Row
cursor = conn.cursor()

# Preleva tutte le informazioni rilevanti
query = """
SELECT
    b.id,
    b.title,
    b.parent,
    b.type,
    b.fk,
    b.dateAdded,
    p.url,
    ia.content AS description
FROM moz_bookmarks b
LEFT JOIN moz_places p ON b.fk = p.id
LEFT JOIN moz_items_annos ia ON b.id = ia.item_id
LEFT JOIN moz_anno_attributes a_attr ON ia.anno_attribute_id = a_attr.id
WHERE (b.type = 1 OR b.type = 2)
  AND (a_attr.name IS NULL OR a_attr.name = 'bookmarkProperties/description')
ORDER BY b.parent, b.position
"""

cursor.execute(query)
rows = cursor.fetchall()

# Costruisci dizionario gerarchico
items = {row["id"]: {
    "id": row["id"],
    "title": row["title"] if row["title"] else "(senza titolo)",
    "type": "folder" if row["type"] == 2 else "bookmark",
    "url": row["url"] if row["type"] == 1 else None,
    "description": row["description"],
    "children": []
} for row in rows}

# Organizza gerarchia
root = []
for row in rows:
    item = items[row["id"]]
    parent_id = row["parent"]
    if parent_id in items:
        items[parent_id]["children"].append(item)
    else:
        root.append(item)

# Salva in JSON
with open("bookmarks_full.json", "w", encoding="utf-8") as f:
    json.dump(root, f, ensure_ascii=False, indent=2)

print(f"Esportazione completata: {len(root)} cartelle o elementi di primo livello.")
conn.close()
os.remove(temp_db_path)

also selecting a specific bookmark folder

import os
import sqlite3
import shutil
import tempfile
import sys

profile_name = "your-FF-profile"
folder_name_to_export = "[the folder name]"  # Nome della cartella da esportare
output_path = "bookmarks-FF-[a name].php"

# Percorso del profilo
profile_path = os.path.expanduser(f"~/.mozilla/firefox/{profile_name}")
db_path = os.path.join(profile_path, "places.sqlite")

if not os.path.isfile(db_path):
    print(f"Il file places.sqlite non esiste: {db_path}")
    sys.exit(1)

# Copia temporanea del DB
tmp_dir = tempfile.gettempdir()
temp_db_path = os.path.join(tmp_dir, "places_copy.sqlite")
shutil.copy2(db_path, temp_db_path)

conn = sqlite3.connect(temp_db_path)
conn.row_factory = sqlite3.Row
cursor = conn.cursor()

# 1. Query per cartelle e segnalibri (senza tag)
query = """
SELECT
  b.id,
  b.title,
  b.parent,
  b.type,
  b.fk,
  p.url
FROM moz_bookmarks b
LEFT JOIN moz_places p ON b.fk = p.id
WHERE b.type IN (1, 2)
ORDER BY b.parent, b.position
"""

cursor.execute(query)
rows = cursor.fetchall()

# Costruzione struttura
items = {row["id"]: {
    "id": row["id"],
    "title": row["title"] or "(senza titolo)",
    "type": "folder" if row["type"] == 2 else "bookmark",
    "url": row["url"] if row["type"] == 1 else None,
    "description": None,
    "children": []
} for row in rows}

# Costruzione gerarchia
for row in rows:
    item = items[row["id"]]
    parent_id = row["parent"]
    if parent_id in items:
        items[parent_id]["children"].append(item)

# 2. Query per associare tag reali ai segnalibri
tag_query = """
SELECT
  b.id AS bookmark_id,
  GROUP_CONCAT(tg.title, ', ') AS tags
FROM moz_bookmarks b
LEFT JOIN moz_bookmarks bt ON bt.fk = b.fk
LEFT JOIN moz_bookmarks tg ON tg.id = bt.parent AND tg.parent = -3
WHERE b.type = 1
GROUP BY b.id
"""

cursor.execute(tag_query)
tag_rows = cursor.fetchall()

# Assegna i tag ai segnalibri nella struttura
for row in tag_rows:
    bookmark_id = row["bookmark_id"]
    tags = row["tags"]
    if bookmark_id in items:
        items[bookmark_id]["description"] = tags if tags else "(senza tag)"

# Cerca la cartella da esportare
matching_folders = [item for item in items.values()
                    if item["type"] == "folder" and item["title"] == folder_name_to_export]

if not matching_folders:
    print(f"Nessuna cartella trovata con nome '{folder_name_to_export}'.")
    conn.close()
    os.remove(temp_db_path)
    sys.exit(1)

# Funzione per scrivere array PHP
def to_php_array(item, indent=2):
    space = ' ' * indent
    lines = [f"{space}["]
    lines.append(f"{space}  'title' => '{item['title'].replace('\'', '\\\'')}',")
    if item.get("url"):
        lines.append(f"{space}  'url' => '{item['url'].replace('\'', '\\\'')}',")
    if item.get("description"):
        lines.append(f"{space}  'description' => '{item['description'].replace('\'', '\\\'')}',")
    if item.get("children"):
        lines.append(f"{space}  'children' => [")
        for child in item["children"]:
            lines.append(to_php_array(child, indent + 4) + ",")
        lines.append(f"{space}  ],")
    lines.append(f"{space}]")
    return "\n".join(lines)

# Scrittura file PHP
with open(output_path, "w", encoding="utf-8") as f:
    f.write("<?php\nreturn [\n")
    for folder in matching_folders:
        f.write(to_php_array(folder, indent=2))
        f.write(",\n")
    f.write("];\n")

print(f"Esportazione completata in {output_path}")

conn.close()
os.remove(temp_db_path)

and output them in a php webpage

<?php
$segnalibri = include 'bookmarks-FF-[a name].php';

function stampaSegnalibri($items) {
    foreach ($items as $item) {
        echo "<div style='margin-bottom:8px'>";

        if (isset($item['url'])) {
            // titolo come link
            $title_html = htmlspecialchars($item['title'], ENT_QUOTES);
            $url_html = htmlspecialchars($item['url'], ENT_QUOTES);
            echo "<a href=\"$url_html\" target=\"_blank\">$title_html</a>";
        } else {
            // solo titolo testo normale
            echo "<strong>" . htmlspecialchars($item['title'], ENT_QUOTES) . "</strong>";
        }

        // descrizione (se presente)
        if (!empty($item['description'])) {
            $desc_html = htmlspecialchars($item['description'], ENT_QUOTES);
            echo "<br><small style='color: #555;'>$desc_html</small>";
        }

        // figli (ricorsivo)
        if (isset($item['children']) && count($item['children']) > 0) {
            echo "<div style='margin-left:20px; margin-top:4px;'>";
            stampaSegnalibri($item['children']);
            echo "</div>";
        }

        echo "</div>";
    }
}

stampaSegnalibri($segnalibri);
?>

The problem is that FF doesn’t have a description field, and I didn’t manage tgo get it from another filed, such as ‘tags’ one.

sitemap php script

A script like the following can generate automatically your sitemap (in xml format):

<?php
function genera_sitemap($path) {
  $sitemap = '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
  $sitemap .= '<urlset
  xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9
      http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">' . "\n";

  // Scansiona le directory e sottodirectory del sito
  $dir = './';
  $files = scandir($dir);
  foreach ($files as $file) {
    if ($file != '.' && $file != '..' && strpos($file, '.') !== 0) {
      if (is_dir($dir . $file)) {
        // Scansiona la sottodirectory
        $sitemap .= aggiungi_sottodirectory($dir . $file . '/', $path) . "\n";
      } else {
        // Aggiungi il file alla sitemap
        if (preg_match('/\.php$/', $file) && $file != 'sql-pass.php') {
          $sitemap .= '<url>' . "\n";
          $sitemap .= ' <loc>' . $path . '/' . $file . '</loc>' . "\n";
          $sitemap .= ' <lastmod>' . date('c', filemtime($dir . $file)) . '</lastmod>' . "\n";
          //$sitemap .= ' <changefreq>daily</changefreq>' . "\n";
          $sitemap .= ' <priority>1.0</priority>' . "\n";
          $sitemap .= '</url>' . "\n";
        }
      }
    }
  }

  $sitemap .= '</urlset>' . "\n";

  // Salva la sitemap su file
  $file = 'sitemap.xml';
  $fp = fopen($file, 'w');
  fwrite($fp, $sitemap);
  fclose($fp);
}

function aggiungi_sottodirectory($dir, $path) {
  $sitemap = '';
  $files = scandir($dir);
  foreach ($files as $file) {
    if ($file != '.' && $file != '..' && strpos($file, '.') !== 0) {
      if ($file == 'index.php') {
        $priority = '1.0';}
      elseif (preg_match('/testi/', $file)) {
          $priority = 0.2;
        }
      else {
        $priority = 0.6;
        }
      if (is_dir($dir . $file)) {
        // Scansiona la sottodirectory
        $sitemap .= aggiungi_sottodirectory($dir . $file . '/', $path) . "\n";
      } else {
        // Aggiungi il file alla sitemap
        if (preg_match('/\.php$/', $file)) {
          $sitemap .= '<url>' . "\n";
          $sitemap .= ' <loc>' . $path . substr($dir, 2) . $file . '</loc>' . "\n";
          $sitemap .= ' <lastmod>' . date('c', filemtime($dir . $file)) . '</lastmod>' . "\n";
          //$sitemap .= ' <changefreq>daily</changefreq>' . "\n";
          $sitemap .= ' <priority>' . $priority . '</priority>' . "\n";
          $sitemap .= '</url>' . "\n";
        }
      }
    }
  }
  return $sitemap;
}
?>

You can call it from different websites, with a code like the following (assuming that your script is genera-sitemap-commune.inc:

<?php
$path="https:[your-desired-path]";
include "genera-sitemap-commune.inc";
genera_sitemap($path);
?>