Joomla 4 | MySQL 5.7 | Python 3.9 | Selenium 4.16 |
C'est quand même sympa les tags sur les articles Joomla. Ça permet de personnaliser certaines pages, créer des thématiques, des filtres... Ils font aussi office de sous-catégories, mais sans les inconvénients des sous-catégories (plusieurs tags possibles pour un même article). Et en plus, ça attire l'œil, notamment sous les titres ou dans un joli nuage de tags. Bref : moi j'aime bien 😀
Le seul truc relou, c'est que les utilisateurs ne pensent jamais à les mettre, arf !
Du coup je me suis dis : Ok, on va automatiser tout ça ! L'avantage est aussi d'être exhaustif quant à l'utilisation des tags, et ainsi de pouvoir les utiliser dans la logique même de son site.
Ci-dessous un exemple de trigger SQL assignant automatiquement des tags aux articles d'un site Joomla en fonction de leur titre.
Ajout des tags pendant l'enregistrement d'un article
Les grands principes sont les suivants :
- On déclenche le
TRIGGER
dans la table#_content
avec unAFTER UPDATE
- Recherche d'une chaîne de caractères afin de choisir le tag pertinent
- Précision de la (ou les) catégorie(s) d'articles qui seront ciblées
- Suppression d'éventuels tags existants dans l'article
- Récupération de l'id de contenu de l'article (table
#_ucm_content
) - Récupération de l'id du tag choisi (table
#_tags
) - Insertion des ids du contenu et du tag dans la table
#_contentitem_tag_map
- Séparation de toutes les insertions (avec une suite de conditions
IF
, sinon ça bloque une clé de la BDD Joomla pendant l'enregistrement de l'article)
Les tags en question doivent être créés en amont bien sûr, et il faudra enrichir la procédure selon les thématiques récurrentes de votre site.
Réfléchissez bien à la pertinence des chaînes de caractères à rechercher, c'est le plus important. N'hésitez pas à être explicite dans ces recherches, afin d'éviter les erreurs. Vous pouvez mettre un grand nombre de recherches pour un seul et même tag. Personnellement je scrute 50 chaînes, pour moins de 10 tags. C'est ultra-rapide et totalement invisible pour l'utilisateur (sauf quand il voit les tags apparaître 😁).
USE MaBdd ; DROP TRIGGER IF EXISTS TriggerRemplissageTag ; DELIMITER // CREATE TRIGGER TriggerRemplissageTag AFTER UPDATE ON joo_content FOR EACH ROW BEGIN -- SUPRIMER LES TAGS EXISTANTS SUR L'ARTICLE DELETE FROM joo_contentitem_tag_map WHERE content_item_id = NEW.id ; -- TAG INTERNATIONAL IF NEW.catid = 8 AND NEW.title LIKE "%London%" THEN -- GET core_content_id SET @CoreId = (SELECT core_content_id FROM joo_ucm_content WHERE core_content_item_id = NEW.id) ; -- GET TAG ID SET @TagId = (SELECT id FROM joo_tags WHERE title = "International") ; -- DELETE TAG DELETE FROM joo_contentitem_tag_map WHERE content_item_id = NEW.id AND tag_id = @TagId ; -- FILL TAG IN CONTENT INSERT INTO joo_contentitem_tag_map (type_alias, core_content_id, content_item_id, tag_id, tag_date, type_id) VALUES ('com_content.article', COALESCE(@CoreId,0), NEW.id, @TagId, NOW(), 1) ; END IF ; -- TAG SUCCESS STORY IF NEW.catid = 8 AND NEW.title LIKE "%étudiant%" THEN -- GET TAG ID SET @TagId = (SELECT id FROM joo_tags WHERE title = "Success story") ; -- DELETE TAG DELETE FROM joo_contentitem_tag_map WHERE content_item_id = NEW.id AND tag_id = @TagId ; -- GET core_content_id SET @CoreId = (SELECT core_content_id FROM joo_ucm_content WHERE core_content_item_id = NEW.id) ; -- FILL TAG IN CONTENT INSERT INTO joo_contentitem_tag_map (type_alias, core_content_id, content_item_id, tag_id, tag_date, type_id) VALUES ('com_content.article', COALESCE(@CoreId,0), NEW.id, @TagId, NOW(), 1) ; END IF ; -- TAG GÉOMATIQUE IF NEW.catid = 8 AND (BINARY NEW.title LIKE "%OSM%" OR NEW.title LIKE "%Open Street Map%" OR NEW.title LIKE "%OpenStreetMap%") THEN -- GET TAG ID SET @TagId = (SELECT id FROM joo_tags WHERE title = "Géomatique") ; -- DELETE TAG DELETE FROM joo_contentitem_tag_map WHERE content_item_id = NEW.id AND tag_id = @TagId ; -- GET core_content_id SET @CoreId = (SELECT core_content_id FROM joo_ucm_content WHERE core_content_item_id = NEW.id) ; -- FILL TAG IN CONTENT INSERT INTO joo_contentitem_tag_map (type_alias, core_content_id, content_item_id, tag_id, tag_date, type_id) VALUES ('com_content.article', COALESCE(@CoreId,0), NEW.id, @TagId, NOW(), 1) ; END IF ; -- TAG UNIVERSITÉ IF NEW.catid = 8 AND NEW.title LIKE "%insertion pro%" THEN -- GET TAG ID SET @TagId = (SELECT id FROM joo_tags WHERE title = "Université") ; -- DELETE TAG DELETE FROM joo_contentitem_tag_map WHERE content_item_id = NEW.id AND tag_id = @TagId ; -- GET core_content_id SET @CoreId = (SELECT core_content_id FROM joo_ucm_content WHERE core_content_item_id = NEW.id) ; -- FILL TAG IN CONTENT INSERT INTO joo_contentitem_tag_map (type_alias, core_content_id, content_item_id, tag_id, tag_date, type_id) VALUES ('com_content.article', COALESCE(@CoreId,0), NEW.id, @TagId, NOW(), 1) ; END IF ; -- TAG ÉVÉNEMENT IF NEW.catid = 8 AND NEW.title LIKE "%hackathon%" THEN -- GET TAG ID SET @TagId = (SELECT id FROM joo_tags WHERE title = "Événement") ; -- DELETE TAG DELETE FROM joo_contentitem_tag_map WHERE content_item_id = NEW.id AND tag_id = @TagId ; -- GET core_content_id SET @CoreId = (SELECT core_content_id FROM joo_ucm_content WHERE core_content_item_id = NEW.id) ; -- FILL TAG IN CONTENT INSERT INTO joo_contentitem_tag_map (type_alias, core_content_id, content_item_id, tag_id, tag_date, type_id) VALUES ('com_content.article', COALESCE(@CoreId,0), NEW.id, @TagId, NOW(), 1) ; END IF ; END ;// DELIMITER ;
Omax ! Ce trigger va ajouter les tags souhaités par le webmaster sans effort pour l'utilisateur 😎
Ok mais... Quid des articles existants ? Et si je veux rajouter un tag ? Et si je m'aperçois qu'il manque la recherche de telle ou telle chaîne pour être efficace ?
C'est là que Python intervient, avec la bibliothèque Selenium.
Remplir les tags des articles existants
La version 4.16 de Selenium est vraiment cool. Plus besoin d'aller chercher le bon driver, correspondant à la bonne version de son navigateur, cela toutes les 2 semaines...
Les grands principes sont les suivants :
- Export d'un XML contenant les titres de tous les articles du site (le champ
title
de la table#_content
) - on a là une piste d'amélioration : il serait plus efficace d'aller chercher la liste directement en SQL - Connexion à l'administration du site (ajoutez vos accès)
- Accès à la page de gestion des articles
- Modification du nombre d'articles affichés (All)
- Ouverture d'un article
- Enregistrement de l'article (sans aucune modification, c'est le trigger qui fera le taf) - et là je me dis que c'était peut-être inutile, un simple
UPDATE
directement en base suffirait-il à déclencher leTRIGGER
? 😅
from selenium import webdriver import time from selenium.webdriver.common.by import By import xml.etree.ElementTree as ET from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.action_chains import ActionChains browser = webdriver.Chrome() browser.get('https://www.google.com/') tree = ET.parse('C:/Users/Georges/Downloads/MonFichierXml.xml') root = tree.getroot() browser.maximize_window() browser.get('https://mon-site-joomla.fr/administrator') # CONNEXION username = browser.find_element("id", "mod-login-username") password = browser.find_element("id", "mod-login-password") username.send_keys('MonSuperUtilisateur') password.send_keys('MotDePasse') browser.find_element("id", "btn-login-submit").click() browser.get('https://mon-site-joomla.fr/administrator/index.php?option=com_content&view=articles') time.sleep(1) browser.find_element("id", "list_limit").click() time.sleep(1) browser.find_element("xpath", "//select/option[@value='0']").click() for my_title in root.findall('MonFichierXml'): xml_title = my_title.find('title').text xml_title = xml_title.replace(' ', ' ') print(xml_title) element = browser.find_element(By.LINK_TEXT, xml_title) time.sleep(1) browser.execute_script("arguments[0].click();", element) time.sleep(1) browser.find_element("id", "save-group-children-save").click() time.sleep(1) browser.quit()
Et voilà ! Y'a plus qu'à lancer le bouzin ! Cela autant de fois que nécessaire si vous modifiez vos tags ou votre trigger.
Et en plus, c'est rigolo 😂