NEWS: add release notes for the v1.2.0 release
[tweeper.git] / src / rss_converter_facebook.com.xsl
1 <!--
2   Stylesheet to convert a Facebook public page to RSS.
3
4   Copyright (C) 2015  Antonio Ospite <ao2@ao2.it>
5
6   This file is part of tweeper.
7
8   This program is free software: you can redistribute it and/or modify
9   it under the terms of the GNU General Public License as published by
10   the Free Software Foundation, either version 3 of the License, or
11   (at your option) any later version.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 -->
21
22 <!--
23   Since June 23rd, 2015 facebook.com deprecated the RSS feed endpoint for public pages:
24   https://developers.facebook.com/docs/apps/changelog#v2_3_90_day_deprecations
25
26   They suggest to use the Graph API but they fail to mention that it does not
27   work anymore without authentication, so it cannot be considered an
28   _equivalent_ solution.
29
30   Luckily we've got Tweeper!
31 -->
32
33 <xsl:stylesheet version="1.0"
34     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
35     xmlns:php="http://php.net/xsl"
36     xsl:extension-element-prefixes="php"
37     exclude-result-prefixes="php">
38
39     <xsl:output method="xml" indent="yes"/>
40
41     <xsl:variable name="BaseURL">
42         <xsl:text>https://facebook.com</xsl:text>
43     </xsl:variable>
44
45     <!--
46          Extract the page id from an element like:
47         <meta property="al:android:url" content="fb://page/793837197390834">
48
49         The page id will be used to build the permalink.
50     -->
51     <xsl:variable
52         name="page-id"
53         select="substring-after(//meta[@property='al:android:url']/@content, 'fb://page/')"/>
54
55     <xsl:template match="//div[contains(@class, 'fbUserStory') or contains(@class, 'userContentWrapper')]">
56         <xsl:variable name="story-id" select=".//input[@name='ft_ent_identifier']/@value"/>
57         <xsl:variable
58             name="item-permalink"
59             select="concat($BaseURL, '/permalink.php?id=', $page-id, '&amp;story_fbid=', $story-id)"/>
60
61         <!-- Get only the first child in order to skip the footer of the content -->
62         <xsl:variable name="item-content" select="div[1]"/>
63
64         <item>
65             <title>
66                 <xsl:variable name="item-title" select="$item-content//p"/>
67                 <xsl:variable name="title-length" select="140"/>
68                 <!-- ellipsize, inspired from http://stackoverflow.com/questions/13622338 -->
69                 <xsl:choose>
70                     <xsl:when test="string-length($item-title) > $title-length">
71                         <xsl:variable name="truncated-length" select="$title-length - 3"/>
72                         <xsl:value-of select="substring($item-title, 1, $truncated-length)"/>
73                         <xsl:text>...</xsl:text>
74                     </xsl:when>
75                     <xsl:otherwise>
76                         <xsl:value-of select="$item-title"/>
77                     </xsl:otherwise>
78                 </xsl:choose>
79             </title>
80             <link>
81                 <xsl:value-of select="$item-permalink"/>
82             </link>
83             <guid>
84                 <xsl:value-of select="$item-permalink"/>
85             </guid>
86             <pubDate>
87                 <xsl:variable name="timestamp" select=".//abbr[@data-shorten]/@data-utime"/>
88                 <xsl:value-of select="php:functionString('Tweeper\Tweeper::epochToRssDate', $timestamp)"/>
89             </pubDate>
90             <description>
91
92                 <!--
93                      Get only the children starting from the one with class="userContent",
94                      this way the content header is skipped
95                 -->
96                 <xsl:variable
97                     name="usercontent-position"
98                     select="count($item-content/div[contains(@class, 'userContent')]/preceding-sibling::*) + 1"/>
99
100                 <xsl:text disable-output-escaping="yes">&lt;![CDATA[</xsl:text>
101                 <xsl:copy-of select="$item-content/div[position() >= $usercontent-position]"/>
102                 <xsl:text disable-output-escaping="yes">]]&gt;</xsl:text>
103             </description>
104         </item>
105     </xsl:template>
106
107     <xsl:template match="/">
108         <xsl:variable name="channel-title" select="//title"/>
109         <xsl:variable name="channel-link" select="//meta[@property='og:url']/@content"/>
110         <xsl:variable name="channel-image" select="//meta[@property='og:image']/@content"/>
111
112         <rss version="2.0">
113             <xsl:attribute name="xml:base"><xsl:value-of select="$BaseURL" /></xsl:attribute>
114             <channel>
115                 <generator>Tweeper</generator>
116                 <title>
117                     <xsl:value-of select="$channel-title"/>
118                 </title>
119                 <link>
120                     <xsl:value-of select="$channel-link"/>
121                 </link>
122                 <description>
123                     <xsl:text disable-output-escaping="yes">&lt;![CDATA[</xsl:text>
124                     <xsl:value-of select="//meta[@property='og:description']/@content"/>
125                     <xsl:text disable-output-escaping="yes">]]&gt;</xsl:text>
126                 </description>
127                 <image>
128                     <title>
129                         <xsl:value-of select="$channel-title"/>
130                     </title>
131                     <link>
132                         <xsl:value-of select="$channel-link"/>
133                     </link>
134                     <url>
135                         <xsl:value-of select="$channel-image"/>
136                     </url>
137                 </image>
138                 <xsl:apply-templates select="//div[contains(@class, 'fbUserStory') or contains(@class, 'userContentWrapper')]"/>
139             </channel>
140         </rss>
141     </xsl:template>
142 </xsl:stylesheet>