Browse Source

make hugo-mastodon-comments a submodule

max.mehl 3 weeks ago
parent
commit
98475f605b
No account linked to committer's email address

+ 3
- 0
.gitmodules View File

@@ -4,3 +4,6 @@
4 4
 [submodule "themes/hugo-snap-gallery"]
5 5
 	path = themes/hugo-snap-gallery
6 6
 	url = https://src.mehl.mx/mxmehl/hugo-snap-gallery
7
+[submodule "themes/hugo-mastodon-comments"]
8
+	path = themes/hugo-mastodon-comments
9
+	url = https://src.mehl.mx/mxmehl/hugo-mastodon-comments

+ 1
- 0
themes/hugo-mastodon-comments

@@ -0,0 +1 @@
1
+Subproject commit b9090e34553c731b287b2752a5c8fe628ad4b741

+ 0
- 2
themes/hugo-mastodon-comments/.gitignore View File

@@ -1,2 +0,0 @@
1
-static/comments/config.php
2
-static/comments/cache-*.json

+ 0
- 26
themes/hugo-mastodon-comments/layouts/partials/comments.html View File

@@ -1,26 +0,0 @@
1
-{{ if ne .Params.page true }}
2
-<div class="comments-container">
3
-    <h5>Comments</h5>
4
-    <noscript>
5
-      <p class="bg-info" style="text-align: center; padding: 5px;">
6
-        Comments are only visible with JavaScript enabled. They are
7
-        dynamically  loaded from Mastodon. The code to display the
8
-        comments is Free Software and <a
9
-        href="https://src.mehl.mx/mxmehl/hugo-mastodon-comments">completely
10
-        transparent</a>.
11
-      </p>
12
-    </noscript>
13
-    <div id="statistics">
14
-        <div id="like-count-container"></div><div id="reblog-count-container"></div><div id="reply-count-container"></div>
15
-    </div>
16
-    <div class="clear"></div>
17
-    <div id="comments"></div>
18
-    <div class="clear"></div>
19
-    <div id="reference"></div>
20
-</div>
21
-{{ end }}
22
-
23
-<script>var RelPermalink="{{ .RelPermalink }}"</script>
24
-<script>var MastodonUser="{{ .Site.Params.mastodoncomments.user }}"</script>
25
-<script>var BlogRegex="{{ .Site.Params.mastodoncomments.regex }}"</script>
26
-<script>var CommentsContact="{{ .Site.Params.mastodoncomments.contact }}"</script>

+ 0
- 11
themes/hugo-mastodon-comments/static/comments/config.sample.php View File

@@ -1,11 +0,0 @@
1
-<?php
2
-$config = [
3
-    'mastodon-instance' => 'https://mastodon.social',  // URL of your Mastodon instance
4
-    'user-id' => 379833,      // Your Mastodon-ID. A bit tricky to find out, the API might help
5
-    // the URL of your blog. All toots are searched for this string
6
-    // please use https?:// as schema
7
-    'search-url' => 'https?://fsfe.org',
8
-    'cache_toots' => 300,     // seconds to cache toots
9
-    'cache_comments' => 300,  // seconds to cache comments (per ID)
10
-    'debug' => false          // writes some debug messages in error_log
11
-];

+ 0
- 51
themes/hugo-mastodon-comments/static/comments/getcomments.js View File

@@ -1,51 +0,0 @@
1
-$(document).ready(function() {
2
-
3
-    // check if we show a blog post or not. Regex is defined in site-wide config
4
-    var patt = new RegExp(BlogRegex);
5
-    var isArticle = patt.test(RelPermalink);
6
-    if (isArticle === false) {
7
-        console.log("Not a blog post, no need to search for comments");
8
-        return;
9
-    }
10
-
11
-    $.ajax({
12
-        url: "/comments/getcomments.php",
13
-        type: "get",
14
-        data: {
15
-            search : RelPermalink
16
-        },
17
-        success: function(data) {
18
-            var stats = data.stats;
19
-            var root = data.stats.root;
20
-            $("#like-count-container").append('<div id="mastodon-like-count"><a href="' + stats.url + '"><span title="Likes"><i class="fa fa-star"></i>' + stats.favs + '</span></a></div></div>');
21
-            $("#reblog-count-container").append('<div id="mastodon-reblog-count"><a href="' + stats.url + '"><span title="Reblogs"><i class="fa fa-retweet"></i>' + stats.reblogs + '</span></a></div></div>');
22
-            $("#reply-count-container").append('<div id="mastodon-reply-count"><a href="' + stats.url + '"><span title="Comments"><i class="fa fa-comments"></i>' + stats.replies + '</span></a></div></div>');
23
-            var comments = data.comments;
24
-            $.each(comments, function(key, value) {
25
-                var timestamp = Date.parse(value.date);
26
-                var date = new Date(timestamp);
27
-                var comment = "<div class='comment' id='" + key + "'>";
28
-                comment += "<img class='avatar' src='" + value.author.avatar + "' />";
29
-                comment += "<div class='author'><a class='displayName' href='" + value.author.url + "'>" + value.author.display_name + "</a> wrote at ";
30
-                comment += "<a class='date' href='" + value.url + "'>" + date.toDateString() + ', ' + date.toLocaleTimeString() + "</a></div>";
31
-                comment += "<div class='toot'>" + value.toot + "</div>";
32
-                comment += "</div>";
33
-                var parentComment = document.getElementById(value.reply_to);
34
-                if (value.reply_to === root || parentComment === null) {
35
-                    $("#comments").append(comment);
36
-                } else {
37
-                    var selector = '#'+value.reply_to;
38
-                    $(selector).append(comment);
39
-                }
40
-            });
41
-            if (parseInt(root) > 0) {
42
-                $("#reference").append("<a href='" + MastodonUser + "/statuses/" + root + "'>Join the discussion on Mastodon!</a>");
43
-            } else {
44
-                $("#comments").empty();
45
-                $("#statistics").empty();
46
-                $("#reference").append("Comments are handled by my <a href='" + MastodonUser + "'>Mastodon account</a>. Sadly this article wasn't published at Mastodon. Feel free to <a href='" + CommentsContact + "'>send me a mail</a> if you want to share your thoughts regarding this topic.");
47
-            }
48
-
49
-        }
50
-    });
51
-});

+ 0
- 249
themes/hugo-mastodon-comments/static/comments/getcomments.php View File

@@ -1,249 +0,0 @@
1
-<?php
2
-
3
-/* load config. You normally don't want to edit something here */
4
-require_once 'config.php';
5
-$instance = $config['mastodon-instance'];
6
-$uid = $config['user-id'];
7
-$searchurl = $config['search-url'];
8
-$search = isset($_GET['search']) ? strtolower($_GET['search']) : '';
9
-$debug_on = $config['debug'];
10
-/* cache files */
11
-$ctt = $config['cache_toots'];
12
-$dbt = "cache-toots.json";
13
-$ctc = $config['cache_comments'];
14
-$dbc = "cache-comments_%id.json";
15
-
16
-/* Exit if search empty */
17
-if (empty($search)) {
18
-  debug("No proper search given");
19
-  die();
20
-}
21
-
22
-/* MISC FUNCTIONS */
23
-function debug($data) {
24
-  global $debug_on;
25
-  if ($debug_on === true) {
26
-    error_log("[getcomments.php] " . print_r($data, TRUE));
27
-  }
28
-}
29
-
30
-/* CACHE FUNCTIONS */
31
-/* write data to file */
32
-function write_db($db, $data, $id) {
33
-  // if $id is given, it's a comments file. Replace placeholder in filename
34
-  if ($id) {
35
-    $db = str_replace('%id', $id, $db);
36
-  }
37
-  $file['toots'] = $data;
38
-  $file['timestamp'] = time();
39
-  // encode and write file
40
-  $encoded = json_encode($file, JSON_PRETTY_PRINT);
41
-  file_put_contents($db, $encoded, LOCK_EX);
42
-}
43
-/* delete file */
44
-function delete_db($db, $id) {
45
-  // if $id is given, it's a comments file. Replace placeholder in filename
46
-  if ($id) {
47
-    $db = str_replace('%id', $id, $db);
48
-  }
49
-  unlink($db);
50
-}
51
-/* access data from file */
52
-function read_db($db, &$data, $cachetime, &$cachebreak, $id) {
53
-  // if $id is given, it's a comments file. Replace placeholder in filename
54
-  if ($id) {
55
-    $db = str_replace('%id', $id, $db);
56
-  }
57
-  // if DB does not exist, create it with empty array
58
-  if (! file_exists($db)) {
59
-    // if $data empty (usually with $toots, not with comment's $result), populate with empty array
60
-    if (empty($data)) {
61
-      $data = array();
62
-    }
63
-    touch($db);
64
-    write_db($db, $data, $id);
65
-    $cachebreak = true;
66
-  }
67
-  $file = file_get_contents($db, true);
68
-  $data = json_decode($file, true);
69
-
70
-  // check if timestamp in cache file too old
71
-  if (empty($data['timestamp']) || ($data['timestamp'] + $cachetime < time())) {
72
-    $cachebreak = true;
73
-  }
74
-
75
-  $data = $data['toots'];
76
-}
77
-
78
-/* TOOT FUNCTIONS */
79
-function collectToots($instance, $uid, $min_id, $searchurl) {
80
-  $raw = file_get_contents("$instance/api/v1/accounts/$uid/statuses?exclude_reblogs=true&exclude_replies=true&limit=50&min_id=$min_id");
81
-  $json_complete = json_decode($raw, true);
82
-  $json = array();
83
-  foreach ($json_complete as $toot) {
84
-    $json[] = array('id' => $toot['id'], 'date' => $toot['created_at'] ,'url' => analyzeToot($instance, $toot['id'], $searchurl));
85
-  }
86
-  return($json);
87
-}
88
-/* Find out if a toot contains the searched URL */
89
-function analyzeToot($instance, $id, $searchurl) {
90
-  debug("Searching for $searchurl in $id");
91
-  $raw = file_get_contents("$instance/api/v1/statuses/$id");
92
-  $json = json_decode($raw, true);
93
-
94
-  // search for $searchurl inside of <a> tags, until (and excluding) a "
95
-  preg_match("|$searchurl.+?(?=\")|i", $json['content'], $matches);
96
-
97
-  if(!empty($matches)) {
98
-    return(strtolower($matches[0]));  // take first match inside toot
99
-  } else {
100
-    return("");
101
-  }
102
-}
103
-/* of context, extract the interesting bits */
104
-function filterComments($descendants, $root, &$result) {
105
-  // go through each comment
106
-  foreach ($descendants as $d) {
107
-    $result['comments'][$d['id']] = [
108
-      'author' => [
109
-        'display_name' => $d['account']['display_name'] ? $d['account']['display_name'] : $d['account']['username'],
110
-        'avatar' => $d['account']['avatar_static'],
111
-        'url' => $d['account']['url']
112
-      ],
113
-      'toot' => $d['content'],
114
-      'date' => $d['created_at'],
115
-      'url' => $d['uri'],
116
-      'reply_to' => $d['in_reply_to_id'],
117
-      'root' => $root,
118
-    ];
119
-  }
120
-  return $result;
121
-}
122
-/* get /context of toot */
123
-function tootContext($instance, $id, &$result) {
124
-  $raw = file_get_contents("$instance/api/v1/statuses/$id/context");
125
-  $json = json_decode($raw, true);
126
-  filterComments($json['descendants'], $id, $result);
127
-}
128
-/* extract stats info from toot */
129
-function filterStats($stats) {
130
-  $result = [
131
-    'reblogs' => (int)$stats['reblogs_count'],
132
-    'favs' => (int)$stats['favourites_count'],
133
-    'replies' => (int)$stats['replies_count'],
134
-    'url' => $stats['url']
135
-  ];
136
-  return $result;
137
-}
138
-/* for toot, extract interesting statistics */
139
-function tootStats($instance, $id, &$result) {
140
-  debug("Checking ID $id");
141
-  $raw = file_get_contents("$instance/api/v1/statuses/$id");
142
-  $json = json_decode($raw, true);
143
-  $newStats = filterStats($json);
144
-  $result['stats']['reblogs'] += $newStats['reblogs'];
145
-  $result['stats']['favs'] += $newStats['favs'];
146
-  $result['stats']['replies'] += $newStats['replies'];
147
-  if (empty($result['stats']['url'])) {
148
-    $result['stats']['url'] = $newStats['url'];
149
-  }
150
-}
151
-
152
-/***************
153
- * START PROGRAM
154
- ***************/
155
-
156
-/* check whether the cached file containing all toots is older than max. cache time */
157
-// this at the same time loads the cached DB, either way
158
-$cachebreak = false;
159
-read_db($dbt, $toots, $ctt, $cachebreak, false);
160
-
161
-if ($cachebreak) {
162
-  /* Collect all the toots */
163
-  /* get id of latest cached toot, and set as $min_id */
164
-  debug("Toots cache outdated. Checking for new toots");
165
-  if (!empty($toots['0']['id'])) {
166
-    $min_id_cached = $toots['0']['id'];
167
-    $min_id = $min_id_cached;
168
-  } else {
169
-    /* if cached toots do not exist, start from oldest toot */
170
-    $min_id = "0";
171
-    $min_id_cached = "0";
172
-  }
173
-
174
-  /* test whether there are new toots available */
175
-  // Search for toots older than the cached latest toot ID ($min_id)
176
-  $uptodate = false;
177
-  while ($uptodate === false) {
178
-    $toots = array_merge(collectToots($instance, $uid, $min_id, $searchurl), $toots);
179
-    $min_id_new = $toots['0']['id']; // the latest ID of the recent search
180
-
181
-    if ($min_id_new === $min_id) {
182
-      // min_id is the latest, let's write the new DB and end this loop
183
-      $uptodate = true;
184
-      debug("Toots up-to-date. Rewrite cache DB.");
185
-      write_db($dbt, $toots, false);
186
-    } else {
187
-      // next round looks for toots newer than the newly found ID
188
-      debug("Newer toots than in cache found. Starting another search for new toots");
189
-      $min_id = $min_id_new;
190
-    }
191
-  }
192
-} else {
193
-  debug("Toots cache is up-to-date");
194
-}
195
-
196
-// create empty $result
197
-$result_empty = ['comments' => [], 'stats' => ['reblogs' => 0, 'favs' => 0, 'replies' => 0, 'url' => '', 'root' => 0]];
198
-$result = $result_empty;
199
-
200
-/* check if URL from $search exists in $toots */
201
-$id = array_keys(
202
-  array_filter(
203
-    array_column($toots, 'url'),
204
-    function ($value) use ($search) {
205
-      return (strpos($value, $search) !== false);
206
-    }
207
-  )
208
-);
209
-if (empty($id)) {
210
-  debug("Blog URL \"$search\" has not been found");
211
-} else {
212
-  // if multiple toots with the searched URL exist, take the oldest one (largest array index)
213
-  $id = $toots[end($id)]['id'];
214
-
215
-  /* read cached comments, or reload new comments if cached data too old */
216
-  $cachebreak = false;
217
-  read_db($dbc, $result, $ctc, $cachebreak, $id);
218
-
219
-  if ($cachebreak) {
220
-    debug("Comments cache for $id outdated. Checking for new comments");
221
-    // delete old cache file, otherwise the stats would add up
222
-    delete_db($dbc, $id);
223
-    // re-create empty $result and new cache file
224
-    $result = $result_empty;
225
-    read_db($dbc, $result, $ctc, $cachebreak, $id);
226
-    /* Extract comments and stats from toot */
227
-    tootContext($instance, $id, $result);
228
-    tootStats($instance, $id, $result);
229
-    // FIXME: At the moment the API doesn't return the correct replies count so I count it manually
230
-    $result['stats']['replies'] = count($result['comments']);
231
-    $result['stats']['root'] = $id;
232
-
233
-    write_db($dbc, $result, $id);
234
-  } else {
235
-    debug("Comments cache for $id up-to-date. Returning cached comments");
236
-  }
237
-}
238
-
239
-// headers for not caching the results
240
-header('Cache-Control: no-cache, must-revalidate');
241
-header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
242
-
243
-// headers to tell that result is JSON
244
-header('Content-type: application/json');
245
-
246
-// actually output result as JSON, to be digested by getcomments.js
247
-echo json_encode($result);
248
-
249
-?>

+ 0
- 66
themes/hugo-mastodon-comments/static/comments/mastodon-comments.css View File

@@ -1,66 +0,0 @@
1
-.comments-container {
2
-  text-align: left;
3
-  padding-bottom: 15px;
4
-}
5
-
6
-.comments-container .comment .avatar {
7
-  float: left;
8
-  width: 50px;
9
-  height: 50px;
10
-  margin-left: 16px;
11
-  margin-right: 16px;
12
-  border-radius: 50%;
13
-  padding-top: 0;
14
-}
15
-
16
-.comments-container #reference {
17
-  text-align: center;
18
-  font-size: 16px;
19
-}
20
-
21
-.comments-container .comment {
22
-  margin-top: 50px;
23
-  margin-bottom: 50px;
24
-  font-size: 16px;
25
-  padding-left: 20px;
26
-}
27
-
28
-.comments-container .toot {
29
-  padding-left: 82px;
30
-}
31
-
32
-.comments-container .author {
33
-  padding-top: 10px;
34
-  padding-bottom: 10px;
35
-}
36
-
37
-.comments-container #mastodon-like-count,
38
-.comments-container #mastodon-reblog-count,
39
-.comments-container #mastodon-reply-count
40
- {
41
-  float: right;
42
-  font-size: 16px;
43
-  background: #eee;
44
-  padding: 3px 10px;
45
-  margin: 30px 5px;
46
-  border-radius: 5px;
47
-}
48
-
49
-.comments-container #mastodon-like-count a,
50
-.comments-container #mastodon-reblog-count a,
51
-.comments-container #mastodon-reply-count a
52
- {
53
-  color: #333333;
54
-}
55
-
56
-.comments-container #mastodon-like-count a:hover,
57
-.comments-container #mastodon-reblog-count a:hover,
58
-.comments-container #mastodon-reply-count a:hover
59
- {
60
-  color: black;
61
-  text-decoration: none;
62
-}
63
-
64
-.comments-container .fa {
65
-    padding-right: 10px;
66
-}

+ 0
- 16
themes/hugo-mastodon-comments/theme.yaml View File

@@ -1,16 +0,0 @@
1
-# theme.yaml configuration file
2
-
3
-name: Mastodon Comments
4
-license: AGPL-3.0-or-later
5
-licenselink: 
6
-description: Hugo theme component for scraping comments on a Mastodon post containing a site's address
7
-homepage: 
8
-tags:
9
-  - component
10
-features:
11
-  - comments
12
-min_version: 0.40.0
13
-
14
-author:
15
-  name: Björn Schießle, Max Mehl
16
-  homepage: 

Loading…
Cancel
Save