diff --git a/archetypes/default.md b/archetypes/default.md
deleted file mode 100644
index 00e77bd..0000000
--- a/archetypes/default.md
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: "{{ replace .Name "-" " " | title }}"
-date: {{ .Date }}
-draft: true
----
-
diff --git a/config.toml b/config.toml
index 60d0e11..9cbbd3d 100755
--- a/config.toml
+++ b/config.toml
@@ -1,14 +1,14 @@
baseurl = "https://mehl.mx/"
languageCode = "en-GB"
title = "Max Mehl"
-theme = [ "hugo-sustain", "hugo-cloak-email" ]
+theme = [ "hugo-sustain", "hugo-cloak-email", "hugo-mastodon-comments" ]
[blackfriday]
# preserves linebreaks, and transforms to
extensions = [ "hardLineBreak" ]
[permalinks]
- post = "/:year/:month/:day/:slug"
+ blog = "/blog/:year/:slug"
[params]
avatar = "profile.jpg"
@@ -17,8 +17,11 @@ extensions = [ "hardLineBreak" ]
keywords = "manager, free software, open source, project, campaign, konstanz, berlin, munster, communication, IT service, computer, politics, administration, FSFE, scouts, founder"
# Custom assets can be linked with their paths relative to static/
- custom_css = ['css/custom.css']
- custom_js = []
+ custom_css = ['css/custom.css', 'comments/mastodon-comments.css']
+ custom_js = ['comments/getcomments.js']
+
+[params.blog]
+ headline = "Thoughts about tech, politics, and more"
[params.social]
Github = "mxmehl"
diff --git a/content/blog/creating-a-new-theme.md b/content/blog/creating-a-new-theme.md
index b6ae663..23a06d2 100755
--- a/content/blog/creating-a-new-theme.md
+++ b/content/blog/creating-a-new-theme.md
@@ -4,7 +4,7 @@ date: 2014-09-28
linktitle: Creating a New Theme
title: Creating a New Theme
highlight: true
-draft: true
+draft: false
---
diff --git a/content/blog/goisforlovers.md b/content/blog/goisforlovers.md
index 99bb6ff..38dff62 100755
--- a/content/blog/goisforlovers.md
+++ b/content/blog/goisforlovers.md
@@ -14,7 +14,9 @@ categories = [
"golang",
]
highlight = "true"
-draft = true
+draft = false
+headerimage = "placeholder.jpg"
+headercredits = "Unsplash, [Dlanor S](https://unsplash.com/photos/2xEQDxB0ss4)"
+++
Hugo uses the excellent [go][] [html/template][gohtmltemplate] library for
diff --git a/content/blog/hugoisforlovers.md b/content/blog/hugoisforlovers.md
index 75b9166..ca49110 100755
--- a/content/blog/hugoisforlovers.md
+++ b/content/blog/hugoisforlovers.md
@@ -2,7 +2,7 @@
title = "Getting Started with Hugo"
description = ""
tags = [
- "radio lockdown",
+ "RadioLockdown",
"golang",
"hugo",
"development",
@@ -13,7 +13,7 @@ categories = [
"golang",
]
highlight = "true"
-draft = true
+draft = false
+++
## Step 1. Install Hugo
diff --git a/content/blog/migrate-from-jekyll.md b/content/blog/migrate-from-jekyll.md
index 2937517..a4fb9d6 100755
--- a/content/blog/migrate-from-jekyll.md
+++ b/content/blog/migrate-from-jekyll.md
@@ -3,7 +3,7 @@ date: 2014-03-10
linktitle: Migrating from Jekyll
title: Migrate to Hugo from Jekyll
highlight: "true"
-draft: true
+draft: false
---
## Move static content to `static`
diff --git a/content/blog/test.md b/content/blog/test.md
new file mode 100755
index 0000000..a82f308
--- /dev/null
+++ b/content/blog/test.md
@@ -0,0 +1,25 @@
++++
+title = "TEST ARTICLE"
+description = ""
+tags = [
+ "test"
+]
+date = "2018-04-02"
+categories = [
+ "Development",
+]
+highlight = "true"
+draft = false
+headerimage = "placeholder.jpg"
+headercredits = "Unsplash, [Dlanor S](https://unsplash.com/photos/2xEQDxB0ss4)"
++++
+
+LOREM IPSUM Hugo uses the excellent [go][] [html/template][gohtmltemplate] library for
+its template engine. It is an extremely lightweight engine that provides a very
+small amount of logic. In our experience that it is just the right amount of
+logic to be able to create a good static website. If you have used other
+template systems from different languages or frameworks you will find a lot of
+similarities in go templates.
+
+This document is a brief primer on using go templates. The [go docs][gohtmltemplate]
+provide more details.
diff --git a/static/img/placeholder.jpg b/static/img/placeholder.jpg
new file mode 100644
index 0000000..8292428
Binary files /dev/null and b/static/img/placeholder.jpg differ
diff --git a/themes/hugo-mastodon-comments/.gitignore b/themes/hugo-mastodon-comments/.gitignore
new file mode 100644
index 0000000..0ed663d
--- /dev/null
+++ b/themes/hugo-mastodon-comments/.gitignore
@@ -0,0 +1 @@
+static/comments/config.php
diff --git a/themes/hugo-mastodon-comments/layouts/partials/comments.html b/themes/hugo-mastodon-comments/layouts/partials/comments.html
new file mode 100644
index 0000000..8d2ad9f
--- /dev/null
+++ b/themes/hugo-mastodon-comments/layouts/partials/comments.html
@@ -0,0 +1,14 @@
+
+
+
+
diff --git a/themes/hugo-mastodon-comments/static/comments/Mastodon_api.php b/themes/hugo-mastodon-comments/static/comments/Mastodon_api.php
new file mode 100644
index 0000000..b57e252
--- /dev/null
+++ b/themes/hugo-mastodon-comments/static/comments/Mastodon_api.php
@@ -0,0 +1,975 @@
+
+ * @copyright KwangSeon Yun
+ * @license https://raw.githubusercontent.com/yks118/Mastodon-api-php/master/LICENSE MIT License
+ * @link https://github.com/yks118/Mastodon-api-php
+ */
+class Mastodon_api {
+ private $mastodon_url = '';
+ private $client_id = '';
+ private $client_secret = '';
+
+ private $token = array();
+ private $scopes = array();
+
+ public function __construct () {}
+
+ public function __destruct () {}
+
+ /**
+ * _post
+ *
+ * curl post
+ *
+ * @param string $url
+ * @param array $data
+ *
+ * @return array $response
+ */
+ private function _post ($url,$data = array()) {
+ $parameters = array();
+ $parameters[CURLOPT_POST] = 1;
+
+ // set access_token
+ if (isset($this->token['access_token'])) {
+ $data['access_token'] = $this->token['access_token'];
+ }
+
+ if (count($data)) {
+ $parameters[CURLOPT_POSTFIELDS] = preg_replace('/([(%5B)]{1})[0-9]+([(%5D)]{1})/','$1$2',http_build_query($data));
+ }
+
+ $url = $this->mastodon_url.$url;
+ $response = $this->get_content_curl($url,$parameters);
+ return $response;
+ }
+
+ /**
+ * _get
+ *
+ * @param string $url
+ * @param array $data
+ *
+ * @return array $response
+ */
+ private function _get ($url,$data = array()) {
+ $parameters = array();
+
+ // set authorization bearer
+ if (isset($this->token['access_token'])) {
+ $authorization = 'Authorization: '.$this->token['token_type'].' '.$this->token['access_token'];
+ $parameters[CURLOPT_HTTPHEADER] = array('Content-Type: application/json',$authorization);
+ }
+
+ $url = $this->mastodon_url.$url;
+ if (count($data)) {
+ $url .= '?' . http_build_query($data);
+ }
+
+ $response = $this->get_content_curl($url,$parameters);
+ return $response;
+ }
+
+ /**
+ * _patch
+ *
+ * @param string $url
+ * @param array $data
+ *
+ * @return array $parameters
+ */
+ private function _patch ($url,$data = array()) {
+ $parameters = array();
+ $parameters[CURLOPT_CUSTOMREQUEST] = 'PATCH';
+
+ // set authorization bearer
+ if (isset($this->token['access_token'])) {
+ $authorization = 'Authorization: '.$this->token['token_type'].' '.$this->token['access_token'];
+ $parameters[CURLOPT_HTTPHEADER] = array('Content-Type: application/json',$authorization);
+ }
+
+ if (count($data)) {
+ $parameters[CURLOPT_POSTFIELDS] = json_encode($data);
+ }
+
+ $url = $this->mastodon_url.$url;
+ $response = $this->get_content_curl($url,$parameters);
+ return $response;
+ }
+
+ /**
+ * _delete
+ *
+ * @param string $url
+ *
+ * @return array $response
+ */
+ private function _delete ($url) {
+ $parameters = array();
+ $parameters[CURLOPT_CUSTOMREQUEST] = 'DELETE';
+
+ // set authorization bearer
+ if (isset($this->token['access_token'])) {
+ $authorization = 'Authorization: '.$this->token['token_type'].' '.$this->token['access_token'];
+ $parameters[CURLOPT_HTTPHEADER] = array('Content-Type: application/json',$authorization);
+ }
+
+ $url = $this->mastodon_url.$url;
+ $response = $this->get_content_curl($url,$parameters);
+ return $response;
+ }
+
+ /**
+ * get_content_curl
+ *
+ * @param string $url
+ * @param array $parameters
+ *
+ * @return array $data
+ */
+ protected function get_content_curl ($url,$parameters = array()) {
+ $data = array();
+
+ // set CURLOPT_USERAGENT
+ if (!isset($parameters[CURLOPT_USERAGENT])) {
+ if (isset($_SERVER['HTTP_USER_AGENT'])) {
+ $parameters[CURLOPT_USERAGENT] = $_SERVER['HTTP_USER_AGENT'];
+ } else {
+ // default IE11
+ $parameters[CURLOPT_USERAGENT] = 'Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko';
+ }
+ }
+
+ // check curl_init
+ if (function_exists('curl_init')) {
+ $ch = curl_init();
+
+ // url 설정
+ curl_setopt($ch,CURLOPT_URL,$url);
+
+ foreach ($parameters as $key => $value) {
+ curl_setopt($ch,$key,$value);
+ }
+
+ // https
+ if (!isset($parameters[CURLOPT_SSL_VERIFYPEER])) {
+ curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
+ }
+ if (!isset($parameters[CURLOPT_SSLVERSION])) {
+ curl_setopt($ch,CURLOPT_SSLVERSION,6);
+ }
+
+ // no header
+ if (!isset($parameters[CURLOPT_HEADER])) {
+ curl_setopt($ch,CURLOPT_HEADER,0);
+ }
+
+ // POST / GET (default : GET)
+ if (!isset($parameters[CURLOPT_POST]) && !isset($parameters[CURLOPT_CUSTOMREQUEST])) {
+ curl_setopt($ch,CURLOPT_POST,0);
+ }
+
+ // response get php value
+ if (!isset($parameters[CURLOPT_RETURNTRANSFER])) {
+ curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
+ }
+
+ // HTTP2
+ if (!isset($parameters[CURLOPT_HTTP_VERSION])) {
+ curl_setopt($ch,CURLOPT_HTTP_VERSION,3);
+ }
+ if (!isset($parameters[CURLINFO_HEADER_OUT])) {
+ curl_setopt($ch,CURLINFO_HEADER_OUT,TRUE);
+ }
+
+ $data['html'] = json_decode(curl_exec($ch),true);
+ $data['response'] = curl_getinfo($ch);
+
+ curl_close($ch);
+ }
+
+ return $data;
+ }
+
+ /**
+ * set_url
+ *
+ * @param string $path
+ */
+ public function set_url ($path) {
+ $this->mastodon_url = $path;
+ }
+
+ /**
+ * set_client
+ *
+ * @param string $id
+ * @param string $secret
+ */
+ public function set_client ($id,$secret) {
+ $this->client_id = $id;
+ $this->client_secret = $secret;
+ }
+
+ /**
+ * set_token
+ *
+ * @param string $token
+ * @param string $type
+ */
+ public function set_token ($token,$type) {
+ $this->token['access_token'] = $token;
+ $this->token['token_type'] = $type;
+ }
+
+ /**
+ * set_scopes
+ *
+ * @param array $scopes read / write / follow
+ */
+ public function set_scopes ($scopes) {
+ $this->scopes = $scopes;
+ }
+
+ /**
+ * create_app
+ *
+ * @param string $client_name
+ * @param array $scopes read / write / follow
+ * @param string $redirect_uris
+ * @param string $website
+ *
+ * @return array $response
+ * int $response['id']
+ * string $response['redirect_uri']
+ * string $response['client_id']
+ * string $response['client_secret']
+ */
+ public function create_app ($client_name,$scopes = array(),$redirect_uris = '',$website = '') {
+ $parameters = array();
+
+ if (count($scopes) == 0) {
+ if (count($this->scopes) == 0) {
+ $scopes = array('read','write','follow');
+ } else {
+ $scopes = $this->scopes;
+ }
+ }
+
+ $parameters['client_name'] = $client_name;
+ $parameters['scopes'] = implode(' ',$scopes);
+
+ if (empty($redirect_uris)) {
+ $parameters['redirect_uris'] = 'urn:ietf:wg:oauth:2.0:oob';
+ } else {
+ $parameters['redirect_uris'] = $redirect_uris;
+ }
+
+ if ($website) {
+ $parameters['website'] = $website;
+ }
+
+ $response = $this->_post('/api/v2/apps',$parameters);
+
+ if (isset($response['html']['client_id'])) {
+ $this->client_id = $response['html']['client_id'];
+ $this->client_secret = $response['html']['client_secret'];
+ }
+
+ return $response;
+ }
+
+ /**
+ * login
+ *
+ * @param string $id E-mail Address
+ * @param string $password Password
+ *
+ * @return array $response
+ * string $response['access_token']
+ * string $response['token_type'] bearer
+ * string $response['scope'] read
+ * int $response['created_at'] time
+ */
+ public function login ($id,$password) {
+ $parameters = array();
+ $parameters['client_id'] = $this->client_id;
+ $parameters['client_secret'] = $this->client_secret;
+ $parameters['grant_type'] = 'password';
+ $parameters['username'] = $id;
+ $parameters['password'] = $password;
+
+ if (count($this->scopes) == 0) {
+ $parameters['scope'] = implode(' ',array('read','write','follow'));
+ } else {
+ $parameters['scope'] = implode(' ',$this->scopes);
+ }
+
+ $response = $this->_post('/oauth/token',$parameters);
+
+ if (isset($response['html']['access_token'])) {
+ $this->token['access_token'] = $response['html']['access_token'];
+ $this->token['token_type'] = $response['html']['token_type'];
+ }
+
+ return $response;
+ }
+
+ /**
+ * accounts
+ *
+ * @see https://your-domain/web/accounts/:id
+ *
+ * @param int $id
+ *
+ * @return array $response
+ * int $response['id']
+ * string $response['username']
+ * string $response['acct']
+ * string $response['display_name'] The name to display in the user's profile
+ * bool $response['locked']
+ * string $response['created_at']
+ * int $response['followers_count']
+ * int $response['following_count']
+ * int $response['statuses_count']
+ * string $response['note'] A new biography for the user
+ * string $response['url']
+ * string $response['avatar'] A base64 encoded image to display as the user's avatar
+ * string $response['avatar_static']
+ * string $response['header'] A base64 encoded image to display as the user's header image
+ * string $response['header_static']
+ */
+ public function accounts ($id) {
+ $response = $this->_get('/api/v2/accounts/'.$id);
+ return $response;
+ }
+
+ /**
+ * accounts_verify_credentials
+ *
+ * Getting the current user
+ *
+ * @return array $response
+ */
+ public function accounts_verify_credentials () {
+ $response = $this->_get('/api/v2/accounts/verify_credentials');
+ return $response;
+ }
+
+ /**
+ * accounts_update_credentials
+ *
+ * Updating the current user
+ *
+ * @param array $parameters
+ * string $parameters['display_name'] The name to display in the user's profile
+ * string $parameters['note'] A new biography for the user
+ * string $parameters['avatar'] A base64 encoded image to display as the user's avatar
+ * string $parameters['header'] A base64 encoded image to display as the user's header image
+ *
+ * @return array $response
+ */
+ public function accounts_update_credentials ($parameters) {
+ $response = $this->_patch('/api/v2/accounts/update_credentials',$parameters);
+ return $response;
+ }
+
+ /**
+ * accounts_followers
+ *
+ * @see https://your-domain/web/accounts/:id
+ *
+ * @param int $id
+ *
+ * @return array $response
+ */
+ public function accounts_followers ($id) {
+ $response = $this->_get('/api/v2/accounts/'.$id.'/followers');
+ return $response;
+ }
+
+ /**
+ * accounts_following
+ *
+ * @see https://your-domain/web/accounts/:id
+ *
+ * @param int $id
+ *
+ * @return array $response
+ */
+ public function accounts_following ($id) {
+ $response = $this->_get('/api/v2/accounts/'.$id.'/following');
+ return $response;
+ }
+
+ /**
+ * accounts_statuses
+ *
+ * @see https://your-domain/web/accounts/:id
+ *
+ * @param int $id
+ *
+ * @return array $response
+ */
+ public function accounts_statuses ($id) {
+ $response = $this->_get('/api/v2/accounts/'.$id.'/statuses');
+ return $response;
+ }
+
+ /**
+ * accounts_own_statuses, only own statuses
+ *
+ * @see https://your-domain/web/accounts/:id
+ *
+ * @param int $id
+ *
+ * @return array $response
+ */
+ public function accounts_own_statuses ($id) {
+ $response = $this->_get('/api/v2/accounts/'.$id.'/statuses?exclude_replies=1');
+ $result = [];
+ foreach ($response['html'] as $r) {
+ if(is_null($r['reblog'])) {
+ $result[] = $r;
+ }
+ }
+
+ $response['html'] = $result;
+
+ return $response;
+ }
+
+ /**
+ * accounts_follow
+ *
+ * @see https://your-domain/web/accounts/:id
+ *
+ * @param int $id
+ *
+ * @return array $response
+ */
+ public function accounts_follow ($id) {
+ $response = $this->_post('/api/v2/accounts/'.$id.'/follow');
+ return $response;
+ }
+
+ /**
+ * accounts_unfollow
+ *
+ * @see https://your-domain/web/accounts/:id
+ *
+ * @param int $id
+ *
+ * @return array $response
+ */
+ public function accounts_unfollow ($id) {
+ $response = $this->_post('/api/v2/accounts/'.$id.'/unfollow');
+ return $response;
+ }
+
+ /**
+ * accounts_block
+ *
+ * @see https://your-domain/web/accounts/:id
+ *
+ * @param int $id
+ *
+ * @return array $response
+ */
+ public function accounts_block ($id) {
+ $response = $this->_post('/api/v2/accounts/'.$id.'/block');
+ return $response;
+ }
+
+ /**
+ * accounts_unblock
+ *
+ * @see https://your-domain/web/accounts/:id
+ *
+ * @param int $id
+ *
+ * @return array $response
+ */
+ public function accounts_unblock ($id) {
+ $response = $this->_post('/api/v2/accounts/'.$id.'/unblock');
+ return $response;
+ }
+
+ /**
+ * accounts_mute
+ *
+ * @see https://your-domain/web/accounts/:id
+ *
+ * @param int $id
+ *
+ * @return array $response
+ */
+ public function accounts_mute ($id) {
+ $response = $this->_post('/api/v2/accounts/'.$id.'/mute');
+ return $response;
+ }
+
+ /**
+ * accounts_unmute
+ *
+ * @see https://your-domain/web/accounts/:id
+ *
+ * @param int $id
+ *
+ * @return array $response
+ */
+ public function accounts_unmute ($id) {
+ $response = $this->_post('/api/v2/accounts/'.$id.'/unmute');
+ return $response;
+ }
+
+ /**
+ * accounts_relationships
+ *
+ * @see https://your-domain/web/accounts/:id
+ *
+ * @param array $parameters
+ * int $parameters['id']
+ *
+ * @return array $response
+ * int $response['id']
+ * bool $response['following']
+ * bool $response['followed_by']
+ * bool $response['blocking']
+ * bool $response['muting']
+ * bool $response['requested']
+ */
+ public function accounts_relationships ($parameters) {
+ $response = $this->_get('/api/v2/accounts/relationships',$parameters);
+ return $response;
+ }
+
+ /**
+ * accounts_search
+ *
+ * @param array $parameters
+ * string $parameters['q']
+ * int $parameters['limit'] default : 40
+ *
+ * @return array $response
+ */
+ public function accounts_search ($parameters) {
+ $response = $this->_get('/api/v2/accounts/search',$parameters);
+ return $response;
+ }
+
+ /**
+ * blocks
+ *
+ * @return array $response
+ */
+ public function blocks () {
+ $response = $this->_get('/api/v2/blocks');
+ return $response;
+ }
+
+ /**
+ * favourites
+ *
+ * @return array $response
+ */
+ public function favourites () {
+ $response = $this->_get('/api/v2/favourites');
+ return $response;
+ }
+
+ /**
+ * follow_requests
+ *
+ * @return array $response
+ */
+ public function follow_requests () {
+ $response = $this->_get('/api/v2/follow_requests');
+ return $response;
+ }
+
+ /**
+ * follow_requests_authorize
+ *
+ * @see https://your-domain/web/accounts/:id
+ *
+ * @param int $id
+ *
+ * @return array $response
+ */
+ public function follow_requests_authorize ($id) {
+ $response = $this->_post('/api/v2/follow_requests/authorize',array('id'=>$id));
+ return $response;
+ }
+
+ /**
+ * follow_requests_reject
+ *
+ * @see https://your-domain/web/accounts/:id
+ *
+ * @param int $id
+ * @return array $response
+ */
+ public function follow_requests_reject ($id) {
+ $response = $this->_post('/api/v2/follow_requests/reject',array('id'=>$id));
+ return $response;
+ }
+
+ /**
+ * follows
+ *
+ * Following a remote user
+ *
+ * @param string $uri username@domain of the person you want to follow
+ * @return array $response
+ */
+ public function follows ($uri) {
+ $response = $this->_post('/api/v2/follows',array('uri'=>$uri));
+ return $response;
+ }
+
+ /**
+ * instance
+ *
+ * Getting instance information
+ *
+ * @return array $response
+ * string $response['uri']
+ * string $response['title']
+ * string $response['description']
+ * string $response['email']
+ */
+ public function instance () {
+ $response = $this->_get('/api/v2/instance');
+ return $response;
+ }
+
+ /**
+ * media
+ *
+ * Uploading a media attachment
+ *
+ * @param string $file_path local path / http path
+ *
+ * @return array $response
+ * int $response['id'] ID of the attachment
+ * string $response['type'] One of: "image", "video", "gifv"
+ * string $response['url'] URL of the locally hosted version of the image
+ * string $response['remote_url'] For remote images, the remote URL of the original image
+ * string $response['preview_url'] URL of the preview image
+ * string $response['text_url'] Shorter URL for the image, for insertion into text (only present on local images)
+ */
+ public function media ($file_path) {
+ $url = $this->mastodon_url.'/api/v2/media';
+ $parameters = $data = array();
+ $parameters[CURLOPT_HTTPHEADER] = array('Content-Type'=>'multipart/form-data');
+ $parameters[CURLOPT_POST] = true;
+
+ // set access_token
+ if (isset($this->token['access_token'])) {
+ $parameters[CURLOPT_POSTFIELDS]['access_token'] = $this->token['access_token'];
+ }
+
+ if (is_file($file_path)) {
+ $mime_type = mime_content_type($file_path);
+
+ $cf = curl_file_create($file_path,$mime_type,'file');
+ $parameters[CURLOPT_POSTFIELDS]['file'] = $cf;
+ }
+
+ $response = $this->get_content_curl($url,$parameters);
+ return $response;
+ }
+
+ /**
+ * mutes
+ *
+ * Fetching a user's mutes
+ *
+ * @return array $response
+ */
+ public function mutes () {
+ $response = $this->_get('/api/v2/mutes');
+ return $response;
+ }
+
+ /**
+ * notifications
+ *
+ * @param int $id
+ *
+ * @return array $response
+ */
+ public function notifications ($id = 0) {
+ $url = '/api/v2/notifications';
+
+ if ($id > 0) {
+ $url .= '/'.$id;
+ }
+
+ $response = $this->_get($url);
+ return $response;
+ }
+
+ /**
+ * notifications_clear
+ *
+ * Clearing notifications
+ *
+ * @return array $response
+ */
+ public function notifications_clear () {
+ $response = $this->_post('/api/v2/notifications/clear');
+ return $response;
+ }
+
+ /**
+ * get_reports
+ *
+ * Fetching a user's reports
+ *
+ * @return array $response
+ */
+ public function get_reports () {
+ $response = $this->_get('/api/v2/reports');
+ return $response;
+ }
+
+ /**
+ * post_reports
+ *
+ * Reporting a user
+ *
+ * @param array $parameters
+ * int $parameters['account_id'] The ID of the account to report
+ * int $parameters['status_ids'] The IDs of statuses to report (can be an array)
+ * string $parameters['comment'] A comment to associate with the report.
+ *
+ * @return array $response
+ */
+ public function post_reports ($parameters) {
+ $response = $this->_post('/api/v2/reports',$parameters);
+ return $response;
+ }
+
+ /**
+ * search
+ *
+ * Searching for content
+ *
+ * @param array $parameters
+ * string $parameters['q'] The search query
+ * string $parameters['resolve'] Whether to resolve non-local accounts
+ *
+ * @return array $response
+ */
+ public function search ($parameters) {
+ $response = $this->_get('/api/v2/search',$parameters);
+ return $response;
+ }
+
+ /**
+ * statuses
+ *
+ * Fetching a status
+ *
+ * @param int $id
+ *
+ * @return array $response
+ */
+ public function statuses ($id) {
+ $response = $this->_get('/api/v2/statuses/'.$id);
+ return $response;
+ }
+
+ /**
+ * statuses_context
+ *
+ * Getting status context
+ *
+ * @param int $id
+ *
+ * @return array $response
+ */
+ public function statuses_context ($id) {
+ $response = $this->_get('/api/v2/statuses/'.$id.'/context');
+ return $response;
+ }
+
+ /**
+ * statuses_card
+ *
+ * Getting a card associated with a status
+ *
+ * @param int $id
+ *
+ * @return array $response
+ */
+ public function statuses_card ($id) {
+ $response = $this->_get('/api/v2/statuses/'.$id.'/card');
+ return $response;
+ }
+
+ /**
+ * statuses_reblogged_by
+ *
+ * Getting who reblogged a status
+ *
+ * @param int $id
+ *
+ * @return array $response
+ */
+ public function statuses_reblogged_by ($id) {
+ $response = $this->_get('/api/v2/statuses/'.$id.'/reblogged_by');
+ return $response;
+ }
+
+ /**
+ * statuses_favourited_by
+ *
+ * Getting who favourited a status
+ *
+ * @param int $id
+ *
+ * @return array $response
+ */
+ public function statuses_favourited_by ($id) {
+ $response = $this->_get('/api/v2/statuses/'.$id.'/favourited_by');
+ return $response;
+ }
+
+ /**
+ * post_statuses
+ *
+ * @param array $parameters
+ * string $parameters['status'] The text of the status
+ * int $parameters['in_reply_to_id'] (optional): local ID of the status you want to reply to
+ * int $parameters['media_ids'] (optional): array of media IDs to attach to the status (maximum 4)
+ * string $parameters['sensitive'] (optional): set this to mark the media of the status as NSFW
+ * string $parameters['spoiler_text'] (optional): text to be shown as a warning before the actual content
+ * string $parameters['visibility'] (optional): either "direct", "private", "unlisted" or "public"
+ *
+ * @return array $response
+ */
+ public function post_statuses ($parameters) {
+ $response = $this->_post('/api/v2/statuses',$parameters);
+ return $response;
+ }
+
+ /**
+ * delete_statuses
+ *
+ * Deleting a status
+ *
+ * @param int $id
+ *
+ * @return array $response empty
+ */
+ public function delete_statuses ($id) {
+ $response = $this->_delete('/api/v2/statuses/'.$id);
+ return $response;
+ }
+
+ /**
+ * statuses_reblog
+ *
+ * Reblogging a status
+ *
+ * @param int $id
+ *
+ * @return array $response
+ */
+ public function statuses_reblog ($id) {
+ $response = $this->_post('/api/v2/statuses/'.$id.'/reblog');
+ return $response;
+ }
+
+ /**
+ * statuses_unreblog
+ *
+ * Unreblogging a status
+ *
+ * @param int $id
+ *
+ * @return array $response
+ */
+ public function statuses_unreblog ($id) {
+ $response = $this->_post('/api/v2/statuses/'.$id.'/unreblog');
+ return $response;
+ }
+
+ /**
+ * statuses_favourite
+ *
+ * Favouriting a status
+ *
+ * @param int $id
+ *
+ * @return array $response
+ */
+ public function statuses_favourite ($id) {
+ $response = $this->_post('/api/v2/statuses/'.$id.'/favourite');
+ return $response;
+ }
+
+ /**
+ * statuses_unfavourite
+ *
+ * Unfavouriting a status
+ *
+ * @param int $id
+ *
+ * @return array $response
+ */
+ public function statuses_unfavourite ($id) {
+ $response = $this->_post('/api/v2/statuses/'.$id.'/unfavourite');
+ return $response;
+ }
+
+ /**
+ * timelines_home
+ *
+ * @return array $response
+ */
+ public function timelines_home () {
+ $response = $this->_get('/api/v2/timelines/home');
+ return $response;
+ }
+
+ /**
+ * timelines_public
+ *
+ * @param array $parameters
+ * bool $parameters['local'] Only return statuses originating from this instance
+ *
+ * @return array $response
+ */
+ public function timelines_public ($parameters = array()) {
+ $response = $this->_get('/api/v2/timelines/public',$parameters);
+ return $response;
+ }
+
+ /**
+ * timelines_tag
+ *
+ * @param string $hashtag
+ * @param array $parameters
+ * bool $parameters['local'] Only return statuses originating from this instance
+ *
+ * @return array $response
+ */
+ public function timelines_tag ($hashtag,$parameters = array()) {
+ $response = $this->_get('/api/v2/timelines/tag/'.$hashtag,$parameters);
+ return $response;
+ }
+}
diff --git a/themes/hugo-mastodon-comments/static/comments/config.sample.php b/themes/hugo-mastodon-comments/static/comments/config.sample.php
new file mode 100644
index 0000000..8cdc8f7
--- /dev/null
+++ b/themes/hugo-mastodon-comments/static/comments/config.sample.php
@@ -0,0 +1,7 @@
+ 'https://mastodon.social',
+ 'user-id' => 549759,
+ 'threshold' => 300,
+ 'token' => '47598kdjhfgkdg894kejg834joejg43'
+];
diff --git a/themes/hugo-mastodon-comments/static/comments/getcomments.js b/themes/hugo-mastodon-comments/static/comments/getcomments.js
new file mode 100644
index 0000000..a7d5576
--- /dev/null
+++ b/themes/hugo-mastodon-comments/static/comments/getcomments.js
@@ -0,0 +1,50 @@
+$(document).ready(function() {
+
+ // check if we show a blog post or not. You might have to adapt this
+ var isArticle = /\/?blog\/\d\d\d\d\/.+$/.test(RelPermalink);
+ if (isArticle === false) {
+ console.log("Not a blog post, no need to search for comments");
+ return;
+ }
+
+ $.ajax({
+ url: "/comments/getcomments.php",
+ type: "get",
+ data: {
+ search : RelPermalink
+ },
+ success: function(data) {
+ var stats = data.stats;
+ var root = data.stats.root;
+ $("#like-count-container").append('');
+ $("#reblog-count-container").append('');
+ $("#reply-count-container").append('');
+ var comments = data.comments;
+ $.each(comments, function(key, value) {
+ var timestamp = Date.parse(value.date);
+ var date = new Date(timestamp);
+ var comment = "";
+ var parentComment = document.getElementById(value.reply_to);
+ if (value.reply_to === root || parentComment === null) {
+ $("#comments").append(comment);
+ } else {
+ var selector = '#'+value.reply_to;
+ $(selector).append(comment);
+ }
+ });
+ if (parseInt(root) > 0) {
+ $("#reference").append("Join the discussion on Mastodon!");
+ } else {
+ $("#comments").empty();
+ $("#statistics").empty();
+ $("#reference").append("Comments are handled by my Mastodon account. Sadly this article wasn't published at Mastodon. Feel free to send me a mail if you want to share your thoughts regarding this topic.");
+ }
+
+ }
+ });
+});
diff --git a/themes/hugo-mastodon-comments/static/comments/getcomments.php b/themes/hugo-mastodon-comments/static/comments/getcomments.php
new file mode 100644
index 0000000..2743773
--- /dev/null
+++ b/themes/hugo-mastodon-comments/static/comments/getcomments.php
@@ -0,0 +1,189 @@
+
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+require_once 'Mastodon_api.php';
+require_once '../../mastodon.feed/config.php';
+
+class CollectMastodonData {
+
+ /** @var \Mastodon_api */
+ private $api;
+
+ /** @var string url of the mastodon instance */
+ private $mastodonUrl = 'https://mastodon.social';
+
+ /** @var string token to authenticate at the mastodon instance */
+ private $bearerToken;
+
+ /** @var int keep cache at least 600 seconds = 10 minutes */
+ private $threshold = 600;
+
+ /** @var string uid on the mastodon instance */
+ private $uid;
+
+ /** @var array cached comments from previous searches */
+ private $commentCache = [];
+
+ private $cacheFile = 'myCommentsCache.json';
+
+ public function __construct($config) {
+ $this->mastodonUrl = $config['mastodon-instance'];
+ $this->bearerToken = $config['token'];
+ $this->uid = $config['user-id'];
+
+ $this->api = new Mastodon_api();
+
+ $this->api->set_url($this->mastodonUrl);
+ $this->api->set_token($this->bearerToken, 'bearer');
+ }
+
+ private function filterComments($descendants, $root, &$result) {
+ foreach ($descendants as $d) {
+ $result['comments'][$d['id']] = [
+ 'author' => [
+ 'display_name' => $d['account']['display_name'] ? $d['account']['display_name'] : $d['account']['username'],
+ 'avatar' => $d['account']['avatar_static'],
+ 'url' => $d['account']['url']
+ ],
+ 'toot' => $d['content'],
+ 'date' => $d['created_at'],
+ 'url' => $d['uri'],
+ 'reply_to' => $d['in_reply_to_id'],
+ 'root' => $root,
+ ];
+ }
+
+ return $result;
+ }
+
+ private function filterStats($stats) {
+ $result = [
+ 'reblogs' => (int)$stats['reblogs_count'],
+ 'favs' => (int)$stats['favourites_count'],
+ 'replies' => (int)$stats['replies_count'],
+ 'url' => $stats['url']
+ ];
+ return $result;
+ }
+
+ private function filterSearchResults($searchResult) {
+ $result = [];
+ if (isset($searchResult['html']['statuses'])) {
+ foreach ($searchResult['html']['statuses'] as $status) {
+ if ($status['in_reply_to_id'] === null) {
+ $result[] = $status['id'];
+ }
+ }
+ }
+
+ sort($result);
+ return $result;
+ }
+
+ /**
+ * find all toots for a given blog post and return the corresponding IDs
+ *
+ * @param string $search
+ * @return array
+ */
+ public function findToots($search) {
+ $result = $this->api->search(['q' => $search]);
+ return $this->filterSearchResults($result);
+ }
+
+ public function getComments($id, &$result) {
+ $raw = file_get_contents("https://mastodon.social/api/v1/statuses/$id/context");
+ $json = json_decode($raw, true);
+ $this->filterComments($json['descendants'], $id, $result);
+ }
+
+ public function getStatistics($id, &$result) {
+ $raw = file_get_contents("https://mastodon.social/api/v1/statuses/$id");
+ $json = json_decode($raw, true);
+ $newStats = $this->filterStats($json);
+ $result['stats']['reblogs'] += $newStats['reblogs'];
+ $result['stats']['favs'] += $newStats['favs'];
+ $result['stats']['replies'] += $newStats['replies'];
+ if (empty($result['stats']['url'])) {
+ $result['stats']['url'] = $newStats['url'];
+ }
+ }
+
+ public function storeCollection($id, $comments) {
+ $timestamp = time();
+ $comments['timestamp'] = $timestamp;
+ $this->commentCache[$id] = $comments;
+ file_put_contents($this->cacheFile, json_encode($this->commentCache));
+ }
+
+ public function getCachedCollection($search) {
+ if (file_exists($this->cacheFile)) {
+ $cachedComments = file_get_contents($this->cacheFile);
+ $cachedCommentsArray = json_decode($cachedComments, true);
+ if (is_array($cachedCommentsArray)) {
+ $this->commentCache = $cachedCommentsArray;
+ $currentTimestamp = time();
+ if (isset($cachedCommentsArray[$search])) {
+ if ((int)$cachedCommentsArray[$search]['timestamp'] + $this->threshold > $currentTimestamp) {
+ unset($cachedCommentsArray[$search]['timestamp']);
+ return $cachedCommentsArray[$search];
+ }
+ }
+ }
+ }
+
+ return [];
+ }
+}
+
+$result = ['comments' => [], 'stats' => ['reblogs' => 0, 'favs' => 0, 'replies' => 0, 'url' => '', 'root' => 0]];
+
+$search = isset($_GET['search']) ? $_GET['search'] : '';
+$collector = new CollectMastodonData($config);
+$ids = [];
+if (!empty($search)) {
+ $oldCollection = $collector->getCachedCollection($search);
+ if (empty($oldCollection)) {
+ $ids = $collector->findToots($search);
+ $result['stats']['root'] = isset($ids[0]) ? $ids[0] : 0;
+ foreach ($ids as $id) {
+ // get comments
+ $newComments = $collector->getComments($id, $result);
+ // get statistics (likes, replies, boosts,...)
+ $collector->getStatistics($id, $result);
+ // FIXME: At the moment the API doesn't return the correct replies count so I count it manually
+ $result['stats']['replies'] = count($result['comments']);
+ }
+ $collector->storeCollection($search, $result);
+ } else {
+ $result = $oldCollection;
+ }
+}
+
+// headers for not caching the results
+header('Cache-Control: no-cache, must-revalidate');
+header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
+
+// headers to tell that result is JSON
+header('Content-type: application/json');
+
+// send the result now
+echo json_encode($result);
diff --git a/themes/hugo-mastodon-comments/static/comments/mastodon-comments.css b/themes/hugo-mastodon-comments/static/comments/mastodon-comments.css
new file mode 100644
index 0000000..615ad72
--- /dev/null
+++ b/themes/hugo-mastodon-comments/static/comments/mastodon-comments.css
@@ -0,0 +1,60 @@
+.comments-container .comment .avatar {
+ float: left;
+ width: 50px;
+ height: 50px;
+ margin-left: 16px;
+ margin-right: 16px;
+ border-radius: 50%;
+}
+
+.comments-container #reference {
+ text-align: center;
+ font-size: 16px;
+}
+
+.comments-container .comment {
+ margin-top: 50px;
+ margin-bottom: 50px;
+ font-size: 16px;
+ padding-left: 20px;
+}
+
+.comments-container .toot {
+ padding-left: 82px;
+}
+
+.comments-container .author {
+ padding-top: 10px;
+ padding-bottom: 10px;
+}
+
+.comments-container #mastodon-like-count,
+.comments-container #mastodon-reblog-count,
+.comments-container #mastodon-reply-count
+ {
+ float: right;
+ font-size: 16px;
+ background: #eee;
+ padding: 3px 10px;
+ margin: 30px 5px;
+ border-radius: 5px;
+}
+
+.comments-container #mastodon-like-count a,
+.comments-container #mastodon-reblog-count a,
+.comments-container #mastodon-reply-count a
+ {
+ color: #333333;
+}
+
+.comments-container #mastodon-like-count a:hover,
+.comments-container #mastodon-reblog-count a:hover,
+.comments-container #mastodon-reply-count a:hover
+ {
+ color: black;
+ text-decoration: none;
+}
+
+.comments-container .fa {
+ padding-right: 10px;
+}
diff --git a/themes/hugo-mastodon-comments/theme.yaml b/themes/hugo-mastodon-comments/theme.yaml
new file mode 100644
index 0000000..c5b6933
--- /dev/null
+++ b/themes/hugo-mastodon-comments/theme.yaml
@@ -0,0 +1,16 @@
+# theme.yaml configuration file
+
+name: Mastodon Comments
+license: AGPL-3.0-or-later
+licenselink:
+description: Hugo theme component for scraping comments on a Mastodon post containing a site's address
+homepage:
+tags:
+ - component
+features:
+ - comments
+min_version: 0.40.0
+
+author:
+ name: Björn Schießle, Max Mehl
+ homepage:
diff --git a/themes/hugo-sustain/images/screenshot.png b/themes/hugo-sustain/images/screenshot.png
deleted file mode 100644
index 2f72814..0000000
Binary files a/themes/hugo-sustain/images/screenshot.png and /dev/null differ
diff --git a/themes/hugo-sustain/images/tn.png b/themes/hugo-sustain/images/tn.png
deleted file mode 100644
index e401ec3..0000000
Binary files a/themes/hugo-sustain/images/tn.png and /dev/null differ
diff --git a/themes/hugo-sustain/layouts/_default/list.html b/themes/hugo-sustain/layouts/_default/list.html
index 211d75a..70e2019 100644
--- a/themes/hugo-sustain/layouts/_default/list.html
+++ b/themes/hugo-sustain/layouts/_default/list.html
@@ -1,31 +1,43 @@
{{ partial "head" . }}
-
-
-
- {{ partial "header" . }}
-
-
Archive
-
-
- {{ range .Data.Pages.ByPublishDate.Reverse }}
-
-
-
- {{ .Date.Format "January 2, 2006" }}
-
-
-
-
- {{ end }}
+
+
+
+ {{ partial "header" . }}
+
+
{{ .Site.Params.Blog.Headline }}
+ {{ if (isset .Data "Singular") }}
+
({{ .Data.Singular | humanize }}: {{lower .Title}})
+
+ {{ else }}
+
+ {{ end }}
+
+ {{ if .Site.Params.Social.CommentsProvider }}
+
Subscribe to my Blog via Diaspora, Mastodon, Friendica or GNU Social. Never miss a article! Reshare, like and discuss it!
+ {{ end }}
+
+ All
+ {{ range $name, $taxonomy := .Site.Taxonomies.categories }}
+
+ {{ humanize $name }}
+
+ {{ end }}
+
+
+
+
+ {{ range .Data.Pages.ByPublishDate.Reverse }}
+
-
+ {{ end }}
+
-
- {{ partial "footer" . }}
-
+
+
+ {{ partial "footer" . }}
+
Comments
+