Site icon Blog ARC Optimizer

Créer un flux RSS en utilisant HTL / Blogs / Perficient

Créer un flux RSS en utilisant HTL / Blogs / Perficient


Saviez-vous que vous pouvez créer un flux RSS dans AEM (Adobe Experience Manager) pour des applications externes comme Eloqua ? Bien qu’AEM fournisse des fonctionnalités prêtes à l’emploi pour les flux RSS, leur personnalisation peut nécessiter des étapes supplémentaires. Vous trouverez ci-dessous plusieurs options pour créer des flux RSS dans AEM ainsi que les étapes pour en créer un à l’aide de HTL.

3 options pour créer un flux RSS dans AEM

  1. Remplacer la fonctionnalité JSP par défaut (approche JSP)
    • Personnalisez le code JSP pour adapter le flux RSS en fonction de vos besoins
    • Cette approche nécessite d’écrire une logique backend en Java et JSP
  2. Créer un servlet pour le flux RSS
    • Implémentez la logique au sein du servlet pour récupérer et formater les données nécessaires dans le flux RSS XML
    • Configurer le servlet pour répondre à des demandes spécifiques pour le point de terminaison du flux RSS
    • Cette approche permet plus de contrôle et de flexibilité sur le processus de génération de flux RSS
  3. Utiliser HTL avec le modèle Sling (approche HTL)
    • Écrire des modèles HTL combinés à un modèle Sling pour générer le flux RSS
    • Tirez parti des modèles Sling pour récupérer des données d’AEM et les formater dans le modèle HTL
    • Cette approche utilise le langage de création de modèles et les modèles de composants modernes d’AEM.
    • HTL est préféré pour les tâches de création de modèles en raison de sa simplicité et de sa lisibilité

Flux RSS attendu

Vous trouverez ci-dessous la réponse du flux permettant à une source externe d’intégrer et d’envoyer des e-mails en conséquence. Ici, les résultats du flux peuvent être filtrés par noms de balises de catégorie (catégorie) à l’aide des paramètres de requête dans l’URL du flux.

  • https://www.demoproject.com/products/aem.rss
  • https://www.demoproject.com/products/aem.rss?category=web
<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
        <atom:link rel="self" href="https://www.demoproject.com/products/aem" />
        <link>https://www.demoproject.com/products/aem</link>
        <title>AEM</title>
        <description />
        <pubDate>Fri, 29 Sep 2023 02:08:26 +0000</pubDate>
        <item>
            <guid>https://www.demoproject.com/products/aem/one.rss.xml</guid>
            <atom:link rel="self" href="https://www.demoproject.com/products/aem/sites" />
            <link>https://www.demoproject.com/products/aem/sites</link>
            <title>Aem Sites</title>
            <description><![CDATA[AEM Sites is the content management system within Adobe Experience Manager that gives you one place to create, manage and deliver remarkable digital experiences across websites, mobile sites and on-site screens.]]></description>
            <pubDate>Tue, 31 Oct 2023 02:23:04 +0000</pubDate>
        </item>
        <item>
            <guid>https://www.demoproject.com/products/aem/two.rss.xml</guid>
            <atom:link rel="self" href="https://www.demoproject.com/products/aem/assets" />
            <link>https://www.demoproject.com/products/aem/assets</link>
            <title>Aem Assets</title>
            <description><![CDATA[Adobe Experience Manager (AEM) Assets is a digital asset management system (DAM) that is built into AEM. It stores and delivers a variety of assets (including images, videos, and documents) with their connected metadata in one secure location.]]></description>
            <pubDate>Thu, 26 Oct 2023 02:21:19 +0000</pubDate>
            <category>pdf,doc,image,web</category>
        </item>
    </channel>
</rss>

Étapes pour créer un flux RSS à l’aide de HTL

  • Créez un fichier HTML sous le composant page
  • Créez un modèle PageFeed Sling qui renvoie des données pour le flux RSS
  • Ajouter une règle de réécriture dans le fichier de règles de réécriture du répartiteur
  • Mettez à jour ignoreUrlParams pour les paramètres requis

Composant de page – RSS-html

Créez un fichier HTML avec le nom « rss.xml.html » sous page composant. « rss.html » ou « rss.xml.html » fonctionnent tous deux très bien pour cela. Ici, la convention de dénomination « rss.xml.html » indique qu’il génère des données XML. PageFeedModel fournit les données JSON de la page pour le flux attendu.

  • La balise de catégorie est rendue uniquement lorsque les propriétés de la page sont créées avec des valeurs de balise
  • CDATA (données de caractères) est une section du contenu de l’élément à restituer uniquement sous forme de données de caractères au lieu de balisage.
<?xml version="1.0" encoding="UTF-8"?>
<sly data-sly-use.model="com.demoproject.aem.core.models.PageFeedModel" />
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <atom:link rel="self" href="https://blogs.perficient.com/2024/05/03/create-an-rss-feed-using-htl/${model.link}"/>
        ${model.linkTag @ context="unsafe"}
        <title>${model.title}</title>
        <description>${model.subTitle}</description>
        <pubDate>${model.publishedDate}</pubDate>
        <sly data-sly-list.childPage="${model.entries}">
            <item>
                <guid>${childPage.feedUrl}</guid>
                <atom:link rel="self" href="${childPage.link}"/>
                ${childPage.linkTag @ context="unsafe"}
                <title>${childPage.title}</title>
                <description><![CDATA[${childPage.description}]]></description>
                <pubDate>${childPage.publishedDate}</pubDate>
                <sly data-sly-test="${childPage.tags}">
                    <category>${childPage.tags}</category>
                </sly>
            </item>
        </sly>
    </channel>
</rss>  

Modèle de flux de page

Il s’agit d’un modèle de composant qui prend currentPage comme racine et récupère une liste de ses pages enfants. Par la suite, il construit dynamiquement des propriétés telles que la date de publication et les catégories en fonction du champ de balise de la page. Ces propriétés permettent de filtrer les résultats en fonction des paramètres de requête. Une fois implémenté, vous pouvez intégrer de manière transparente ce modèle dans votre composant pour restituer le flux RSS.

  • En utilisant currentPage, obtenez les propriétés de la page actuelle sous forme de carte de valeurs
  • Récupérer le titre, la description, la date de publication, le lien vers la page actuelle
  • Récupérer le titre, la description, la date de publication, le lien, les balises (catégories) pour les pages enfants
  • Filtrer la liste des pages enfants en fonction de la valeur du paramètre de requête (catégorie)
//PageFeedModel sample code 
package com.demoproject.aem.core.models;

import com.adobe.cq.export.json.ExporterConstants;
import com.day.cq.commons.Externalizer;
import com.day.cq.commons.jcr.JcrConstants;
import com.day.cq.wcm.api.Page;
import com.day.cq.wcm.api.PageFilter;
import com.demoproject.aem.core.utility.RssFeedUtils;
import lombok.Getter;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.SlingException;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.Exporter;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.SlingObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.PostConstruct;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

@Model(adaptables = {
    Resource.class,
    SlingHttpServletRequest.class
}, resourceType = PageFeedModel.RESOURCE_TYPE, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
@Exporter(name = ExporterConstants.SLING_MODEL_EXPORTER_NAME, extensions = ExporterConstants.SLING_MODEL_EXTENSION)
public class PageFeedModel {

    protected static final String RESOURCE_TYPE = "demoproject/components/page";
    private static final Logger logger = LoggerFactory.getLogger(PageFeedModel.class);
    @SlingObject
    ResourceResolver resourceResolver;
    @SlingObject
    SlingHttpServletRequest request;
    @Inject
    private Page currentPage;
    @Getter
    private String title;
    @Getter
    private String link;
    @Getter
    private String linkTag;
    @Getter
    private String description;
    @Getter
    private List < ChildPageModel > entries;
    @Inject
    private Externalizer externalizer;
    @Getter
    private String feedUrl;
    @Getter
    private String publishedDate;


    @PostConstruct
    protected void init() {
        try {
            ValueMap properties = currentPage.getContentResource().adaptTo(ValueMap.class);
            title = StringEscapeUtils.escapeXml(null != currentPage.getTitle() ? currentPage.getTitle() : properties.get(JcrConstants.JCR_TITLE, String.class));
            description = StringEscapeUtils.escapeXml(properties.get(JcrConstants.JCR_DESCRIPTION, String.class));

            link = RssFeedUtils.getExternaliseUrl(currentPage.getPath(), externalizer, resourceResolver);
            feedUrl = link + ".rss.xml";
            linkTag = RssFeedUtils.setLinkElements(link);

            String category = request.getParameter("category") != null ? request.getParameter("category").toLowerCase().replaceAll("\\s", "") : StringUtils.EMPTY;
            entries = new ArrayList < > ();
            Iterator < Page > childPages = currentPage.listChildren(new PageFilter(false, false));
            while (childPages.hasNext()) {
                Page childPage = childPages.next();
                ChildPageModel childPageModel = resourceResolver.getResource(childPage.getPath()).adaptTo(ChildPageModel.class);
                if (null != childPageModel) {
                    if (StringUtils.isBlank(category)) entries.add(childPageModel);
                    else {
                        String tags = childPageModel.getTags();
                        if (StringUtils.isNotBlank(tags)) {
                            tags = tags.toLowerCase().replaceAll("\\s", "");
                            List tagsList = Arrays.asList(tags.split(","));
                            String[] categoryList = category.split(",");
                            boolean flag = true;
                            for (String categoryStr: categoryList) {
                                if (tagsList.contains(StringEscapeUtils.escapeXml(categoryStr)) && flag) {
                                    entries.add(childPageModel);
                                    flag = false;
                                }
                            }
                        }
                    }
                }
            }
            publishedDate = RssFeedUtils.getPublishedDate(properties);

        } catch (SlingException e) {
            logger.error("Repository Exception {}", e);
        }
    }
}
//ChildPageModel 
package com.demoproject.aem.core.models;

import com.adobe.cq.export.json.ExporterConstants;
import com.day.cq.commons.Externalizer;
import com.day.cq.commons.jcr.JcrConstants;
import com.demoproject.aem.core.utility.RssFeedUtils;
import lombok.Getter;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.sling.api.SlingException;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.Exporter;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.SlingObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.PostConstruct;
import javax.inject.Inject;

@Model(adaptables = {
    Resource.class
}, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
@Exporter(name = ExporterConstants.SLING_MODEL_EXPORTER_NAME, extensions = ExporterConstants.SLING_MODEL_EXTENSION)
public class ChildPageModel {
    private static final Logger logger = LoggerFactory.getLogger(ChildPageModel.class);

    @SlingObject
    Resource resource;

    @Getter
    private String title;

    @Getter
    private String link;

    @Getter
    private String linkTag;

    @Getter
    private String feedUrl;

    @Getter
    private String description;

    @Getter
    private String publishedDate;

    @Getter
    private String tags;

    @Inject
    private Externalizer externalizer;

    @PostConstruct
    protected void init() {
        try {
            if (null != resource) {
                String url = resource.getPath();

                ResourceResolver resourceResolver = resource.getResourceResolver();
                link = RssFeedUtils.getExternaliseUrl(url, externalizer, resourceResolver);
                feedUrl = link + ".rss.xml";
                linkTag = RssFeedUtils.setLinkElements(link);

                ValueMap properties = resource.getChild(JcrConstants.JCR_CONTENT).adaptTo(ValueMap.class);
                title = StringEscapeUtils.escapeXml(properties.get(JcrConstants.JCR_TITLE, String.class));
                description = StringEscapeUtils.escapeXml(properties.get(JcrConstants.JCR_DESCRIPTION, String.class));
                publishedDate = RssFeedUtils.getPublishedDate(properties);
                tags = StringEscapeUtils.escapeXml(RssFeedUtils.getPageTags(properties, resourceResolver));

            }
        } catch (SlingException e) {
            logger.error("Error: " + e.getMessage());
        }
    }
}
//RSS Feed Utils 

package com.demoproject.aem.core.utility;

import com.day.cq.commons.Externalizer;
import com.day.cq.commons.jcr.JcrConstants;
import com.day.cq.tagging.Tag;
import com.day.cq.tagging.TagManager;
import com.day.cq.wcm.api.NameConstants;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ValueMap;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/** 
 * @desc RSS Feed Utils 
 */
@Slf4j
public class RssFeedUtils {

    public static final String FORMAT_DATE = "E, dd MMM yyyy hh:mm:ss Z";
    public static final String CONTENT_PATH = "/content/demoproject/us/en";

    public static String getPublishedDate(ValueMap pageProperties) {
        String publishedDate = StringUtils.EMPTY;
        SimpleDateFormat dateFormat = new SimpleDateFormat(FORMAT_DATE);
        Date updatedDateVal = pageProperties.get(JcrConstants.JCR_LASTMODIFIED, pageProperties.get(JcrConstants.JCR_CREATED, Date.class));
        if (null != updatedDateVal) {
            Date replicatedDate = pageProperties.get(NameConstants.PN_PAGE_LAST_REPLICATED, updatedDateVal);
            publishedDate = dateFormat.format(replicatedDate);
        }
        return publishedDate;
    }

    public static String getExternaliseUrl(String pagePath, Externalizer externalizer, ResourceResolver resourceResolver) {
        String url = StringUtils.EMPTY;
        if (StringUtils.isNotBlank(pagePath) && null != externalizer && null != resourceResolver)
            url = externalizer.publishLink(resourceResolver, resourceResolver.map(pagePath)).replace(CONTENT_PATH, "");

        return url;
    }

    public static String setLinkElements(String link) {
        String url = StringUtils.EMPTY;
        if (StringUtils.isNotBlank(link)) {
            url = "<link>" + link + "</link>";
        }
        return url;
    }

    public static String getPageTags(ValueMap properties, ResourceResolver resourceResolver) {
        String tags = StringUtils.EMPTY;
        String[] pageTags = properties.get(NameConstants.PN_TAGS, String[].class);
        if (pageTags != null) {
            List < String > tagList = new ArrayList < > ();
            TagManager tagManager = resourceResolver.adaptTo(TagManager.class);
            for (String tagStr: pageTags) {
                Tag tag = tagManager.resolve(tagStr);
                if (tag != null) {
                    tagList.add(tag.getName());
                }
            }
            if (!tagList.isEmpty()) tags = String.join(",", tagList);
        }
        return tags;
    }
}

Modifications du répartiteur

demoproject_rewrites.rules

Dans le projet client réécrit.rules (/src/conf.d/rewrites) fichier ajoutez une règle de réécriture pour l’extension .rss. Cette règle de réécriture prend une URL se terminant par .rss et la réécrit pour pointer vers un fichier rss.xml correspondant dans le composant de page, modifiant ainsi l’extension de fichier de .rss à .rss.xml.

#feed rewrite rule
RewriteRule ^/(.*).rss$ /content/demoproject/us/en/$1.rss.xml [PT,L]

100_demoproject_dispatcher_farm.any

Définissez les paramètres d’URL qui ne doivent pas être mis en cache pour le flux RSS. Il est recommandé de configurer le paramètre ignoreUrlParams sous forme de liste verte. En tant que tel, tous les paramètres de requête sont ignorés et seuls les paramètres de requête connus ou attendus sont exemptés (refusés) d’être ignorés.

Lorsqu’un paramètre est ignoré pour une page, la page est mise en cache lors de sa demande initiale. Par conséquent, le système répond ensuite aux requêtes pour la page en utilisant la version mise en cache, quelle que soit la valeur du paramètre dans la requête. Ici, nous ajoutons des paramètres d’URL ci-dessous pour diffuser le contenu en direct comme requis par une application externe.

/ignoreUrlParams {
    /0001 { /glob "*" /type "allow" }
    /0002 { /glob "category" /type "deny" }
    /0003 { /glob "pubdate_gt" /type "deny" }
    /0004 { /glob "pubdate_lt" /type "deny" }
}

Pourquoi HTL est-il meilleur ?

Nous pouvons utiliser cette approche pour produire n’importe quel flux XML, allant au-delà des flux RSS. Nous avons la possibilité d’ajouter des propriétés personnalisées pour adapter le flux à nos besoins spécifiques. De plus, nous pouvons facilement appliquer des filtres à l’aide de paramètres de requête.

Un grand merci à mon directeur, Grace Siwickipour son aide précieuse dans la réflexion sur la mise en œuvre et la réalisation de ce travail de blog.






Source link
Quitter la version mobile