If you call a bash script from a php one, be sure that you have the correct permissions. To grant them, you could have to do this command:
setfacl -m u:www-data:rwx /the/path/where/you/have/to/exec/bash
a thoughtful use of digital
If you call a bash script from a php one, be sure that you have the correct permissions. To grant them, you could have to do this command:
setfacl -m u:www-data:rwx /the/path/where/you/have/to/exec/bash
… 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') ?>
I mean notes that should be correctly shown both in a webpage and in the standalone md file.
You have to modify the js (md-bloc.js), as you can see in the linked file.
In this way you can use the md syntax, such as:
[^1] (for the anchor) and
[^1]: for the footnote.
You can use this code:
find: <h2>(.*?)</h2>
replace: <h2>\L\1\E</h2>
where \L means lowercase (begin) and \E means end (of transformation)
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\_%';
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']";
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>
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>
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 %...%',
toreturn [
'LIKE %...%',
'LIKE',
Html5 helps to use semantic tags, but only in some cases.
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>
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>
<span itemprop=”locationCreated”>
<span itemprop=”bookEdition”> (for Edition)
<span itemprop=”author”> (for Author)
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.
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.