Add blog functionality #1
@@ -1,6 +0,0 @@
|
|||||||
---
|
|
||||||
title: "{{ replace .Name "-" " " | title }}"
|
|
||||||
date: {{ .Date }}
|
|
||||||
draft: true
|
|
||||||
---
|
|
||||||
|
|
||||||
11
config.toml
@@ -1,14 +1,14 @@
|
|||||||
baseurl = "https://mehl.mx/"
|
baseurl = "https://mehl.mx/"
|
||||||
languageCode = "en-GB"
|
languageCode = "en-GB"
|
||||||
title = "Max Mehl"
|
title = "Max Mehl"
|
||||||
theme = [ "hugo-sustain", "hugo-cloak-email" ]
|
theme = [ "hugo-sustain", "hugo-cloak-email", "hugo-mastodon-comments" ]
|
||||||
|
|
||||||
[blackfriday]
|
[blackfriday]
|
||||||
# preserves linebreaks, and transforms to <br />
|
# preserves linebreaks, and transforms to <br />
|
||||||
extensions = [ "hardLineBreak" ]
|
extensions = [ "hardLineBreak" ]
|
||||||
|
|
||||||
[permalinks]
|
[permalinks]
|
||||||
post = "/:year/:month/:day/:slug"
|
blog = "/blog/:year/:slug"
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
avatar = "profile.jpg"
|
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"
|
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 assets can be linked with their paths relative to static/
|
||||||
custom_css = ['css/custom.css']
|
custom_css = ['css/custom.css', 'comments/mastodon-comments.css']
|
||||||
custom_js = []
|
custom_js = ['comments/getcomments.js']
|
||||||
|
|
||||||
|
[params.blog]
|
||||||
|
headline = "Thoughts about tech, politics, and more"
|
||||||
|
|
||||||
[params.social]
|
[params.social]
|
||||||
Github = "mxmehl"
|
Github = "mxmehl"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ date: 2014-09-28
|
|||||||
linktitle: Creating a New Theme
|
linktitle: Creating a New Theme
|
||||||
title: Creating a New Theme
|
title: Creating a New Theme
|
||||||
highlight: true
|
highlight: true
|
||||||
draft: true
|
draft: false
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,9 @@ categories = [
|
|||||||
"golang",
|
"golang",
|
||||||
]
|
]
|
||||||
highlight = "true"
|
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
|
Hugo uses the excellent [go][] [html/template][gohtmltemplate] library for
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
title = "Getting Started with Hugo"
|
title = "Getting Started with Hugo"
|
||||||
description = ""
|
description = ""
|
||||||
tags = [
|
tags = [
|
||||||
"radio lockdown",
|
"RadioLockdown",
|
||||||
"golang",
|
"golang",
|
||||||
"hugo",
|
"hugo",
|
||||||
"development",
|
"development",
|
||||||
@@ -13,7 +13,7 @@ categories = [
|
|||||||
"golang",
|
"golang",
|
||||||
]
|
]
|
||||||
highlight = "true"
|
highlight = "true"
|
||||||
draft = true
|
draft = false
|
||||||
+++
|
+++
|
||||||
|
|
||||||
## Step 1. Install Hugo
|
## Step 1. Install Hugo
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ date: 2014-03-10
|
|||||||
linktitle: Migrating from Jekyll
|
linktitle: Migrating from Jekyll
|
||||||
title: Migrate to Hugo from Jekyll
|
title: Migrate to Hugo from Jekyll
|
||||||
highlight: "true"
|
highlight: "true"
|
||||||
draft: true
|
draft: false
|
||||||
---
|
---
|
||||||
|
|
||||||
## Move static content to `static`
|
## Move static content to `static`
|
||||||
|
|||||||
25
content/blog/test.md
Executable file
@@ -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.
|
||||||
BIN
static/img/placeholder.jpg
Normal file
|
After Width: | Height: | Size: 979 KiB |
1
themes/hugo-mastodon-comments/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
static/comments/config.php
|
||||||
14
themes/hugo-mastodon-comments/layouts/partials/comments.html
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<div class="comments-container">
|
||||||
|
<h5>Comments</h5>
|
||||||
|
<div id="statistics">
|
||||||
|
<div id="like-count-container"></div><div id="reblog-count-container"></div><div id="reply-count-container"></div>
|
||||||
|
</div>
|
||||||
|
<div class="clear"></div>
|
||||||
|
<div id="comments"></div>
|
||||||
|
<div class="clear"></div>
|
||||||
|
<div id="reference"></div>
|
||||||
|
<!--<p class="reference"><a href="https://mastodon.social/users/bjoern/statuses/{{ .Params.toot }}">Join the discussion on Mastodon!</a></p>-->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>var MastodonTootId="{{ .Params.toot }}"</script>
|
||||||
|
<script>var RelPermalink="{{ .RelPermalink }}"</script>
|
||||||
975
themes/hugo-mastodon-comments/static/comments/Mastodon_api.php
Normal file
@@ -0,0 +1,975 @@
|
|||||||
|
<?php
|
||||||
|
// defined('BASEPATH') OR exit('No direct script access allowed');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Mastodon_api
|
||||||
|
*
|
||||||
|
* PHP version 7.1
|
||||||
|
*
|
||||||
|
* Mastodon https://mastodon.social/
|
||||||
|
* API LIST https://github.com/tootsuite/documentation/blob/master/Using-the-API/API.md
|
||||||
|
*
|
||||||
|
* @author KwangSeon Yun <middleyks@hanmail.net>
|
||||||
|
* @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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
$config = [
|
||||||
|
'mastodon-instance' => 'https://mastodon.social',
|
||||||
|
'user-id' => 549759,
|
||||||
|
'threshold' => 300,
|
||||||
|
'token' => '47598kdjhfgkdg894kejg834joejg43'
|
||||||
|
];
|
||||||
50
themes/hugo-mastodon-comments/static/comments/getcomments.js
Normal file
@@ -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('<div id="mastodon-like-count"><a href="' + stats.url + '"><span title="Likes"><i class="fa fa-star"></i>' + stats.favs + '</span></a></div></div>');
|
||||||
|
$("#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>');
|
||||||
|
$("#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>');
|
||||||
|
var comments = data.comments;
|
||||||
|
$.each(comments, function(key, value) {
|
||||||
|
var timestamp = Date.parse(value.date);
|
||||||
|
var date = new Date(timestamp);
|
||||||
|
var comment = "<div class='comment' id='" + key + "'>";
|
||||||
|
comment += "<img class='avatar' src='" + value.author.avatar + "' />";
|
||||||
|
comment += "<div class='author'><a class='displayName' href='" + value.author.url + "'>" + value.author.display_name + "</a> wrote at ";
|
||||||
|
comment += "<a class='date' href='" + value.url + "'>" + date.toDateString() + ', ' + date.toLocaleTimeString() + "</a></div>";
|
||||||
|
comment += "<div class='toot'>" + value.toot + "</div>";
|
||||||
|
comment += "</div>";
|
||||||
|
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("<a href='https://mastodon.social/users/bjoern/statuses/" + root + "'>Join the discussion on Mastodon!</a>");
|
||||||
|
} else {
|
||||||
|
$("#comments").empty();
|
||||||
|
$("#statistics").empty();
|
||||||
|
$("#reference").append("Comments are handled by my <a href='https://mastodon.social/@bjoern'>Mastodon account</a>. Sadly this article wasn't published at Mastodon. Feel free to <a href='https://www.schiessle.org/contact/'>send me a mail</a> if you want to share your thoughts regarding this topic.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
189
themes/hugo-mastodon-comments/static/comments/getcomments.php
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (c) 2018 Bjoern Schiessle <bjoern@schiessle.org>
|
||||||
|
*
|
||||||
|
* @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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
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);
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
16
themes/hugo-mastodon-comments/theme.yaml
Normal file
@@ -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:
|
||||||
|
Before Width: | Height: | Size: 241 KiB |
|
Before Width: | Height: | Size: 117 KiB |
@@ -1,24 +1,36 @@
|
|||||||
{{ partial "head" . }}
|
{{ partial "head" . }}
|
||||||
<body>
|
<body>
|
||||||
<div id="wrap">
|
<div id="wrap">
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
{{ partial "header" . }}
|
{{ partial "header" . }}
|
||||||
<div class="container">
|
<div class="container archive">
|
||||||
<h1>Archive</h1>
|
<h1>{{ .Site.Params.Blog.Headline }}
|
||||||
<div class="panel panel-default">
|
{{ if (isset .Data "Singular") }}
|
||||||
<div class="panel-body">
|
<br />({{ .Data.Singular | humanize }}: {{lower .Title}})
|
||||||
|
<span class="article-rss"><a href="/{{ .Data.Plural }}/{{lower .Title}}/index.xml" title="RSS Feed"><i class="fa fa-rss" aria-hidden="true"></i></a></span>
|
||||||
|
{{ else }}
|
||||||
|
<span class="article-rss"><a href="/index.xml" title="RSS Feed"><i class="fa fa-rss" aria-hidden="true"></i></a></span>
|
||||||
|
{{ end }}
|
||||||
|
</h1>
|
||||||
|
{{ if .Site.Params.Social.CommentsProvider }}
|
||||||
|
<p><a href="{{ .Site.Params.Social.CommentsProvider }}">Subscribe</a> to my Blog via Diaspora, Mastodon, Friendica or GNU Social. Never miss a article! Reshare, like and discuss it!</p>
|
||||||
|
{{ end }}
|
||||||
|
<p class="categories">
|
||||||
|
<a class="label label-success" href="/blog">All</a>
|
||||||
|
{{ range $name, $taxonomy := .Site.Taxonomies.categories }}
|
||||||
|
<a class="label label-success" href="{{ "/categories/" | relLangURL }}{{ $name | urlize }}">
|
||||||
|
{{ humanize $name }}
|
||||||
|
</a>
|
||||||
|
{{ end }}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="panel panel-default panel-overview">
|
||||||
|
<div class="blogpost">
|
||||||
{{ range .Data.Pages.ByPublishDate.Reverse }}
|
{{ range .Data.Pages.ByPublishDate.Reverse }}
|
||||||
<div class="row">
|
<div class="blog-summary">
|
||||||
<div class="col-sm-4 col-md-4 col-lg-4">
|
<h4><strong><a href="{{ .RelPermalink }}">{{ .Title | markdownify }}</a></strong></h4>
|
||||||
<h5 style="text-align: right">
|
<div class="metadata">Posted on {{ .Date.Format "2 January 2006" }} - Tagged with {{ range $i, $tag := .Params.tags }}{{ if $i }}, {{ end }}<a href="/tags/{{ lower $tag }}">{{ $tag }}</a>{{ end }}</div>
|
||||||
{{ .Date.Format "January 2, 2006" }}
|
<p>{{ .Summary }} <a class="learn-more" href="{{ .RelPermalink }}"></a></p>
|
||||||
</h5>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-8 col-md-8 col-lg-8">
|
|
||||||
<h5 style="text-align: left">
|
|
||||||
<strong><a href="{{ .RelPermalink }}">{{ .Title | markdownify }}</a></strong>
|
|
||||||
</h5>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
@@ -27,5 +39,5 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- Footer -->
|
<!-- Footer -->
|
||||||
{{ partial "footer" . }}
|
{{ partial "footer" . }}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -6,24 +6,27 @@
|
|||||||
{{ partial "header" . }}
|
{{ partial "header" . }}
|
||||||
|
|
||||||
<div class="container {{ replace (.LinkTitle | lower) " " "_" }}"> <!-- add link title add class -->
|
<div class="container {{ replace (.LinkTitle | lower) " " "_" }}"> <!-- add link title add class -->
|
||||||
<div class="blog-post">
|
|
||||||
<h1>{{ .Title }}</h1>
|
<h1>{{ .Title }}</h1>
|
||||||
</div>
|
|
||||||
{{ if ne .Params.page true }}
|
{{ if ne .Params.page true }}
|
||||||
<div class="blog-title">
|
<div>
|
||||||
<h4>
|
<p class="categories">
|
||||||
{{ .Date.Format "January 2, 2006" }}
|
{{ .Date.Format "2 January 2006" }}
|
||||||
|
|
||||||
{{ range .Params.tags }}
|
{{ range .Params.tags }}
|
||||||
<span class="label label-success">{{ . }}</span>
|
<a class="label label-success" href="/tags/{{ lower . }}">{{ . }}</a>
|
||||||
|
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</h4>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<div class="blogpost">
|
<div class="blogpost">
|
||||||
|
{{ if isset .Params "headerimage" }}
|
||||||
|
<div class="header-image">
|
||||||
|
<img src="/img/{{ .Params.headerImage }}" />
|
||||||
|
<p>{{ with .Params.headercredits }}{{ . | markdownify }}{{ end }}</p>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
{{ .Content }}
|
{{ .Content }}
|
||||||
<!-- only show related posts if param is true, or if it's not a page -->
|
<!-- only show related posts if param is true, or if it's not a page -->
|
||||||
{{ if or (eq .Params.related true) (ne .Params.page true) }}
|
{{ if or (eq .Params.related true) (ne .Params.page true) }}
|
||||||
@@ -34,7 +37,7 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-4 col-md-4 col-lg-4">
|
<div class="col-sm-4 col-md-4 col-lg-4">
|
||||||
<h6 style="text-align: right">
|
<h6 style="text-align: right">
|
||||||
{{ .Date.Format "January 2, 2006" }}
|
{{ .Date.Format "2 January 2006" }}
|
||||||
</h6>
|
</h6>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-8 col-md-8 col-lg-8">
|
<div class="col-sm-8 col-md-8 col-lg-8">
|
||||||
@@ -49,6 +52,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
|
{{ partial "comments.html" . }}
|
||||||
{{ partial "social" . }}
|
{{ partial "social" . }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -11,13 +11,24 @@ div.footer {
|
|||||||
.container a:hover {
|
.container a:hover {
|
||||||
color: #12437A;
|
color: #12437A;
|
||||||
}
|
}
|
||||||
div.container.links .label-success {
|
.container .label-success {
|
||||||
background-color: rgba(92, 139, 184, 0.6);
|
background-color: rgba(92, 139, 184, 0.6);
|
||||||
|
color: #FFF;
|
||||||
}
|
}
|
||||||
div.container h1 {
|
.container a.label-success:hover {
|
||||||
|
background-color: rgba(92, 139, 184, 0.9);
|
||||||
|
color: #FFF;
|
||||||
|
}
|
||||||
|
.container h1 {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #0D76EC;
|
color: #0D76EC;
|
||||||
}
|
}
|
||||||
|
.container h4 a {
|
||||||
|
color: #0D76EC;
|
||||||
|
}
|
||||||
|
.container h4 a:hover {
|
||||||
|
color: #0060CC;
|
||||||
|
}
|
||||||
|
|
||||||
/* Avoid scrollbar jumps in centering
|
/* Avoid scrollbar jumps in centering
|
||||||
* see https://css-tricks.com/eliminate-jumps-in-horizontal-centering-by-forcing-a-scroll-bar/
|
* see https://css-tricks.com/eliminate-jumps-in-horizontal-centering-by-forcing-a-scroll-bar/
|
||||||
@@ -63,3 +74,49 @@ div.contact p {
|
|||||||
.social-links li a:hover i {
|
.social-links li a:hover i {
|
||||||
transform: scale(1);
|
transform: scale(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* BLOG */
|
||||||
|
.categories {
|
||||||
|
font-size: 18px;
|
||||||
|
margin: 1em 0 3em 0;
|
||||||
|
}
|
||||||
|
.blog-summary {
|
||||||
|
padding-bottom: 1em;
|
||||||
|
}
|
||||||
|
.metadata {
|
||||||
|
font-size: 0.8em;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
.blogpost .header-image img {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
}
|
||||||
|
.blogpost .header-image p {
|
||||||
|
opacity: 0.7;
|
||||||
|
font-size: 0.8em;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 1em;
|
||||||
|
margin-bottom: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.learn-more {
|
||||||
|
font-size: 1.8em;
|
||||||
|
line-height: 0.8em;
|
||||||
|
font-weight: 700;
|
||||||
|
font-variant: small-caps;
|
||||||
|
text-transform: lowercase;
|
||||||
|
padding: 0 1em 0 .3em;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
a.learn-more::after {
|
||||||
|
position: absolute;
|
||||||
|
right: 0.3em;
|
||||||
|
content: " » ";
|
||||||
|
opacity: .8;
|
||||||
|
transition: all 150ms linear;
|
||||||
|
}
|
||||||
|
a.learn-more:hover::after {
|
||||||
|
right: 0;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 644 KiB After Width: | Height: | Size: 644 KiB |
|
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 141 KiB |
|
Before Width: | Height: | Size: 797 KiB After Width: | Height: | Size: 797 KiB |
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |