Implementation example

Added example and changed default configuration to use this example by default to be used out of the box.
projects
Domeniko Gentner 3 years ago
parent 87685c4a62
commit 3f7d06fc0f
  1. 21
      __implementation_example/README.md
  2. 6
      __implementation_example/archetypes/default.md
  3. 4
      __implementation_example/config.toml
  4. 94
      __implementation_example/content/blog/readme.md
  5. 61
      __implementation_example/content/blog/stramine.md
  6. 22
      __implementation_example/data/blog/readme.json
  7. 22
      __implementation_example/data/blog/stramine.json
  8. 56
      __implementation_example/layouts/_default/baseof.html
  9. 7
      __implementation_example/layouts/_default/frontpage_article.html
  10. 12
      __implementation_example/layouts/_default/home.html
  11. 0
      __implementation_example/layouts/_default/section.html
  12. 13
      __implementation_example/layouts/_default/single.html
  13. 108
      __implementation_example/layouts/partials/comments.html
  14. 12
      __implementation_example/static/css/labertasche.css
  15. BIN
      __implementation_example/static/css/open-sans-v18-latin-regular.woff2
  16. 169
      __implementation_example/static/css/tuxstash.css.map
  17. BIN
      __implementation_example/static/images/default.jpg
  18. BIN
      __implementation_example/static/images/gravatar/d9eef4df0ae5bfc1a9a9b1e39a99c07f.jpg
  19. 122
      __implementation_example/static/js/labertasche.js
  20. 138
      __implementation_example/static/js/mysite.js
  21. 15
      labertasche.yaml

@ -0,0 +1,21 @@
# How to use this example
Please run
`hugo --bind dev.localhost --baseURL http://dev.localhost --disableLiveReload`
in this folder to view this example. Point your web browser then to
[dev.localhost](http://dev.localhost:1313).
Please also add
`127.0.0.1 dev.localhost`
to
* Windows: `C:\windows\system32\drivers\etc\hosts`
* MAC/Linux: `/etc/hosts`
before running the example.
**There is another readme when you view the page.**

@ -0,0 +1,6 @@
---
title: "{{ replace .Name "-" " " | title }}"
date: {{ .Date }}
draft: true
---

@ -0,0 +1,4 @@
baseURL = "http://dev.localhost/"
languageCode = "en-us"
disableKinds = ["taxonomy", "term"]
ignoreErrors = ["error-disable-taxonomy"]

@ -0,0 +1,94 @@
---
title: "Labertasche minimal implementation example"
date: 2020-12-03 09:00:00
categories: blog
---
This is a minimal example on how to implement Labertasche,
using Bulma CSS. The CSS is not that important, however, it
also shows how to utilize a modal dialogue to give your users
a good experience.
<!--more-->
## Setup
Please modify `mail_credentials.yaml` and make sure mail can be sent.
Everything else is set up. You can run flask with pycharm or on a local
server. It is up to you. I recommend using pycharm with the flask parameters
`--host=dev.localhost --port=1314`. Make sure `dev.localhost` is in your
hosts file and resolves to `127.0.0.1`. This is necessary to set a cookie domain.
The server will not be able to run without.
## Where to start?
Start by reading `layouts/_default/baseof.html`. Notice the Javascript.
It has the default `labertasche.js` included and a custom file, where I
handle the callbacks. In production, you would concat these files using
the Hugo asset pipeline. I've left them separate, so you can see what is custom and what is included.
The next stop should be `single.html`. There you can find the first go block
needed, which adds the comments to each article in Hugo. Query for sections
if you want to exclude certain sections or only allow one, e.g. `blog`.
Last but not least, `comments.html` in the partials folder. This is where
basically all the magic happens. Read the javascript functions as they appear.
Basically, all I am doing is to query the DOM elements and adding/removing
classes as I go, to display certain things. There is also a quick explanation further down.
**Please note**: This version has a modified reply function, so it displays the
hidden field with the reply id.
This does not occur on the production version, but can be helpful for debugging.
## Javascript functions explained
This is a quick and short explanation of all javascript functions. Yes, you may use and modify them.
### labertasche_text_counter()
This function counts the amount of characters put into the text area. This is purely cosmetic and only the first
filter. If users have disabled Javascript, they could circumvent this, so the server checks lengths too.
### labertasche_validate_mail()
This checks if the entered text is a valid mail address, with a regex match. This does not check if the
domain exists or if the mail is _really_ an email, but that is done server side. It's only used to minimize false
requests.
### labertasche_modal_hide()
This hides the modal dialog when the button on the modal is clicked.
### labertasche_comment_not_found()
When a comment is not valid, Labertasche will redirect to `dev.localhost?cnf=true`. This function shows a modal
to inform the user about it. The JS for checking this parameter is in `baseof.html`.
### labertasche_comment_deleted()
Same as above, but with `dev.localhost?deleted=true`. This happens when a user deletes the comment via the link
sent by mail.
### labertasche_post_callback(state)
This is the callback used via the Labertasche post function. It simply displays different modals when certain error
codes are received. This is extremely useful, because you can inform your user about what is happening.
### labertasche_reply_callback(state, comment_id)
The callback for the reply callback. This does a little more, it displays a new button which the user can press to
disable the reply and go to a parent comment. This is useful, because the user does not have to reload the site and
therefore, does not need to type it all again, if the reply was done in error.
## Feedback
Hope this example makes it more comfortable to use Labertasche, please send me a mail or open an issue if anything
is unclear.
## Try it out!
Scroll down and comment. This is only locally. Please note: If livereload is enabled, you may not see all dialogs.
Turn livereload in Hugo off, if you want to see all of them:
`--disableLiveReload`.
The example comments also will disappear when you comment, as they are not included in the database.

@ -0,0 +1,61 @@
---
title: "Stramine ad coniugiale hi Procne"
date: 2020-12-04 08:00:00
categories: blog
---
## Qui velox repperit
Lorem markdownum spatio animas animorum Scyrumve Noctis gramine, fata, sit
cives, cui mea. Abesto Thesea coniecit, in rictus *quem pedis caret* tutaeque
sacra.
1. Urit deae freto nubifer oculi
2. Ferrumque dilata quaeque
3. Mihi luminis color tandem mirum quodque
<!--more-->
## Videt accipiunt habet
Potest rapto: nata honores, primos, laudamus scrutantur in. Similis incursurus
enim inritata postes, est caelo, sis *nondum*, spumantiaque licet tenens
conbibitur excutis levis. Spargit dedere laetissimus liquidi, ad mergit, lintea
*armis erunt esse* aratri, sideraque piceis.
Gestare petentes saevo multoque, ad *esset inhibere* omnibus, iter de Dixerat
dira. Illi mora sed altera ferrum tibi, qui ignis aris nocti quatiens est.
## Amplexus stantes paciscor tot unum
Amens fugit membra flabat gemellam et Venus **protinus**. Gyaros esse tibi exhausta. Nulla sed
numina linguae plura, prosiluit tamen, inscius, cui Phoebus circumspexit
spatiumque **indigenae caecaque**.
if (server + san < w(ospf, webMemory, speed_column +
sli_vaporware_definition)) {
kernelBarFile = archieSmishing;
base_png_click(rte_warm, dongle);
}
var midi = addressP.router(reader.koffice(dslClickKeylogger(rpm, 2,
dataRom), 5));
cd_media = koffice.dos.shortcut(3, html_boot.horizontal_trash_extension(
subdirectory)) - tunneling(login, camelcase_cursor_opacity(
flash_graphic, soap, serp_e_debug));
sdk_lte_software(5);
## Attollit unde fingens
Longeque frangunt, spectant temptavit, reperta invito, tectis face vos mirabile
Cycladas. Reliquit voverat, quattuor imago utinam crudelem rapta, nomina ullos
latuit resurgere. Terraque vitae.
1. Senex et ipse esse cruentior caluere
2. Sub quae
3. Ubi sunt sedens cladis certamine maior hiscere
Aequantibus admota; cuncta sit quod fugias dextra certaminis oro ecce auditis
pater. Fluunt herbas si est. Animam precesque esse gradumque videndo vultum,
lapides, fera **corpora temperat**, adnuit fortis. Se et Ceycis; ille tergo
frondes hospitibus quoque et? Dixit inposuit in cetera pinus triplices convicia;
rupit intus suorum, et?

@ -0,0 +1,22 @@
{
"comments": [
{
"comment_id": 1,
"email": "commenter1@",
"content": "This is an example comment with over 40 characters.",
"created_on": "2020-12-04 12:23:14",
"replied_to": null,
"gravatar": "d9eef4df0ae5bfc1a9a9b1e39a99c07f"
}
],
"replies": [
{
"comment_id": 2,
"email": "commenter2@",
"content": "This is an example reply, to test if this works.",
"created_on": "2020-12-04 12:24:19",
"replied_to": 1,
"gravatar": "d9eef4df0ae5bfc1a9a9b1e39a99c07f"
}
]
}

@ -0,0 +1,22 @@
{
"comments": [
{
"comment_id": 1,
"email": "commenter1@",
"content": "This is an example comment with over 40 characters.",
"created_on": "2020-12-04 12:23:14",
"replied_to": null,
"gravatar": "d9eef4df0ae5bfc1a9a9b1e39a99c07f"
}
],
"replies": [
{
"comment_id": 2,
"email": "commenter2@",
"content": "This is an example reply, to test if this works.",
"created_on": "2020-12-04 12:24:19",
"replied_to": 1,
"gravatar": "d9eef4df0ae5bfc1a9a9b1e39a99c07f"
}
]
}

@ -0,0 +1,56 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, user-scalable=yes">
<link rel="stylesheet" href="/css/labertasche.css" media="screen">
<title>Labertasche Example</title>
</head>
<body class="is-family-sans-serif bg-darkslate">
<section>
<nav class="navbar" role="navigation" aria-label="main navigation">
<a class="navbar-item is-size-4" href="/">
&nbsp;Labertasche Example
</a>
<div class="navbar-start"></div>
<div class="navbar-end"></div>
</nav>
<div class="p-4">
{{ block "main" . }}
{{ end }}
</div>
</section>
<script defer src="/js/labertasche.js"></script>
<script defer src="/js/mysite.js"></script>
<script defer>
document.addEventListener('DOMContentLoaded', () => {
// Comments
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.get("cnf") === "true"){
labertasche_comment_not_found();
}
if (urlParams.get("deleted") === "true"){
labertasche_comment_deleted();
}
});
</script>
<!-- Modal for notifications -->
<div class="modal" id="labertasche-modal">
<div class="modal-background"></div>
<div class="modal-content">
<div class="content p-5 mb-0 is-rounded bg-yayellow has-text-black">
<p class="title">Labertasche</p>
</div>
<div class="content p-5 mb-0 bg-deepmatte has-text-white" id="labertasche-modal-text">
</div>
<div class="content p-5 bg-deepmatte has-text-white border-top">
<button onclick="labertasche_modal_hide();" class="button is-warning" aria-label="close">
OK
</button>
</div>
</div>
<button onclick="labertasche_modal_hide();" class="modal-close is-large" aria-label="close"></button>
</div>
</body>
</html>

@ -0,0 +1,7 @@
{{ block "frontpage_article" . }}
<article class="p-3 bg-deepmatte brdr-yayellow">
<p class="title has-text-white">{{ .Title }}</p>
<p>{{ .Summary }}</p>
<p><a href="{{.RelPermalink}}">Read the whole article...</a></p>
</article>
{{ end }}

@ -0,0 +1,12 @@
{{ define "main" }}
<div class="container">
<div class="content">
{{ $last_article := (.Site.GetPage "blog" .Section).Pages.ByPublishDate }}
{{ range last 2 $last_article }}
<div class="mt-4">
{{ .Render "frontpage_article" }}
</div>
{{ end }}
</div>
</div>
{{ end }}

@ -0,0 +1,13 @@
{{ define "main" }}
<article class="container p-3 bg-deepmatte brdr-yayellow">
<p class="title has-text-white">{{ .Title }}</p>
<div class="content has-text-justified has-text-white">{{ .Content }}</div>
</article>
<article class="container p-3 bg-deepmatte brdr-yayellow mt-5">
<div>
{{ $file := replaceRE "^(.*)[\\/]$" "data$1.json" .Page.RelPermalink }}
{{ .Scratch.Set "location" $file }}
{{ partial "partials/comments" . }}
</div>
</article>
{{ end }}

@ -0,0 +1,108 @@
{{ $location := .Scratch.Get "location" }}
<!--suppress XmlDuplicatedId -->
<h1 class="is-uppercase has-text-white">comments</h1>
<div class="mb-5" id="labertasche-comment-section" data-remote="http://dev.localhost:1314/comments/new">
<div class="control is-expanded">
<input onkeypress="labertasche_validate_mail();"
onfocusout="labertasche_validate_mail();"
maxlength="100"
id="labertasche-mail"
class="input"
type="email"
placeholder="joedoe@example.com">
</div>
<div class="control is-expanded mt-3">
<textarea oninput="labertasche_text_counter();"
id="labertasche-text"
class="textarea"
rows="5"
maxlength="1000"
placeholder="40 minimum characters, type something nice..."></textarea>
<p id="labertasche-text-helper"
class="help is-danger">Characters: <span id="labertasche-counter">0/1000</span></p>
</div>
<div class="control mt-3">
<button onclick="labertasche_post_comment(this, labertasche_post_callback);"
class="button is-warning px-6 mr-4 is-medium"
id="labertasche-comment-button">
<span>Comment</span>
</button>
</div>
</div>
<article>
<div class="media mb-5 brdr-yayellow my-shadow-subtle bg-compliment">
<figure class="media-left ml-0 mb-0">
<p class="image is-128x128">
<img alt="gravatar portrait" src="/images/default.jpg">
</p>
</figure>
<div class="media-content">
<div class="content mr-5 mt-2 has-text-left">
Pinned by <span class="fg-yellow">admin@example.com</span>
<br><br>
<span class="mt-5 has-text-justified">
<span>
Come join the discussion and write something nice. You will have to confirm your comment by mail,
so make sure it is legit and not a throwaway. Only the name part of it will be displayed, so
don't worry about spam.
</span>
</span>
</div>
</div>
</div>
</article>
{{ if (fileExists $location ) }}
{{ $dataJ := getJSON $location }}
{{ range $dataJ.comments }}
<article>
<div class="media mb-5 brdr-yayellow my-shadow-subtle bg-compliment">
<figure class="media-left ml-0 mb-0">
<p class="image is-128x128">
<img alt="gravatar portrait" src="https://www.gravatar.com/avatar/{{.gravatar}}.jpg">
</p>
</figure>
<div class="media-content">
<div class="content mr-5 mt-2">
<a id="comment_{{.comment_id}}" href="#comment_{{.comment_id}}">#{{.comment_id}}</a>
Posted by <span class="fg-yellow">{{.email}}</span> <small>on {{.created_on}}</small>
<br><br>
<span class="mt-5">
{{.content}}
</span>
</div>
<div class="is-fullwidth bg-yayellow has-text-centered">
<a class="has-text-black" href="#labertasche-comment-section"
onclick="labertasche_reply_to({{.comment_id}}, labertasche_reply_callback);">
reply
</a>
</div>
</div>
</div>
</article>
{{ range where $dataJ.replies "replied_to" .comment_id }}
<article>
<div class="media margin-left-128 mb-5 brdr-yayellow my-shadow-subtle bg-compliment">
<figure class="media-left ml-0 mb-0">
<p class="image is-128x128">
<img alt="gravatar portrait" src="https://www.gravatar.com/avatar/{{.gravatar}}.jpg">
</p>
</figure>
<div class="media-content">
<div class="content mr-5 mt-2">
<a id="comment_{{.comment_id}}" href="#comment_{{.comment_id}}">#{{.comment_id}}</a>
Posted by <span class="fg-yellow">{{.email}}</span> <small>on {{.created_on}}&nbsp;
</small>
<br><br>
<span class="mt-5">
{{.content}}
</span>
</div>
</div>
</div>
</article>
{{end}}
{{ end }}
{{ end }}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

@ -0,0 +1,122 @@
//**********************************************************************************
// * _author : Domeniko Gentner
// * _mail : code@tuxstash.de
// * _repo : https://git.tuxstash.de/gothseidank/labertasche
// * _license : This project is under MIT License
// *********************************************************************************/
/*
//Callback example for post. Possible messages:
// post-min-length
// post-max-length
// post-invalid-json
// post-duplicate
// post-internal-server-error
// post-success
// post-before-fetch
function labertasche_callback(state)
{
if (state === "post-before-fetch"){
}
if (state === "post-min-length"){
}
if (state === "post-success"){
}
if (state === "post-fetch-exception" || state === "post-internal-server-error"){
}
if (state === "post-invalid-email"){
}
}
// Callback for initiating and cancelling replies.
// Posstible message: 'on' and 'off'
function labertasche_reply_callback()
{
if (state === "on"){
}
if (state === "off"){
}
}
*/
function labertasche_reply_to(comment_id, callback)
{
let comments = document.getElementById('labertasche-comment-section');
if (comments){
if (document.getElementById('labertasche-replied-to')){
document.getElementById('labertasche-replied-to').remove();
callback('off', comment_id);
if (comment_id === -1){
return false;
}
}
let reply = document.createElement("input");
reply.setAttribute("type", "text");
reply.setAttribute("id", "labertasche-replied-to");
//reply.classList.add("is-hidden");
reply.value = comment_id;
comments.appendChild(reply);
callback('on', comment_id);
}
else{
console.log("Missing text input with id labertasche-comment-section");
}
}
function labertasche_post_comment(btn, callback)
{
let remote = document.getElementById('labertasche-comment-section').dataset.remote;
let comment = document.getElementById('labertasche-text').value.trim();
let mail = document.getElementById('labertasche-mail').value.trim();
let reply = document.getElementById('labertasche-replied-to');
if (mail.length <= 0 || comment.length < 40){
callback('post-min-length');
if(btn) {
return false;
}
return false;
}
let reply_value = null
if (reply != null){
reply_value = reply.value;
}
callback('post-before-fetch');
fetch(remote,
{
mode:"cors",
headers: {
'Access-Control-Allow-Origin':'*',
'Accept': 'application/json',
'Content-Type': 'application/json'
},
method: "POST",
// use real location
body: JSON.stringify({ "email": mail,
"content": comment,
"location": window.location.pathname,
"replied_to": reply_value
})
})
.then(async function(response){
let result = await response.json();
callback(result['status']);
})
.catch(function(exc){
console.log(exc);
callback('post-fetch-exception');
})
// Don't reload the page
return false;
}

@ -0,0 +1,138 @@
function labertasche_text_counter()
{
let txt = document.getElementById('labertasche-text');
let cntr = document.getElementById('labertasche-counter');
let maxlen = txt.getAttribute("maxlength");
let helper = document.getElementById("labertasche-text-helper");
if (cntr && txt){
cntr.innerText = txt.value.length + "/" + maxlen;
if (txt.value.length > 40){
if (helper.classList.contains('is-danger')){
helper.classList.remove("is-danger");
helper.classList.add("is-success");
txt.classList.add('is-success');
txt.classList.remove('is-danger');
}
}
if (txt.value.length < 40){
if (helper.classList.contains('is-success')){
helper.classList.remove("is-success");
helper.classList.add("is-danger");
txt.classList.add('is-danger');
txt.classList.remove('is-success');
}
}
}
}
function labertasche_validate_mail()
{
let email = document.getElementById("labertasche-mail");
let is_valid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.value);
if (is_valid){
email.classList.remove("is-danger")
email.classList.add("is-success")
}
else{
email.classList.add("is-danger")
email.classList.remove("is-success")
}
}
function labertasche_modal_hide()
{
let modal = document.getElementById('labertasche-modal');
if (modal != null){
if (modal.classList.contains("is-active")){
modal.classList.remove('is-active');
}
}
}
function labertasche_comment_not_found()
{
let modal = document.getElementById('labertasche-modal');
let modal_text = document.getElementById('labertasche-modal-text');
modal_text.innerText = "The link you followed was not valid. It either doesn't exist or was already used.";
modal.classList.add('is-active');
}
function labertasche_comment_deleted()
{
let modal = document.getElementById('labertasche-modal');
let modal_text = document.getElementById('labertasche-modal-text');
modal_text.innerText = "Your comment has been deleted. Thank you for being here.";
modal.classList.add('is-active');
}
/*
post-min-length
post-max-length
post-invalid-json
post-duplicate
post-internal-server-error
post-success
post-before-fetch
*/
function labertasche_post_callback(state)
{
// Elements
let modal = document.getElementById('labertasche-modal');
let modal_text = document.getElementById('labertasche-modal-text');
let button = document.getElementById('labertasche-comment-button');
if (state === "post-before-fetch"){
button.classList.add("is-loading");
}
if (state === "post-min-length"){
button.classList.remove("is-loading");
modal_text.innerText = "Your comment was not entered because it is too short. Please write at least 40 characters."
modal.classList.add('is-active');
}
if (state === "post-success"){
button.classList.remove("is-loading");
modal_text.innerText = "Your comment was entered, but you need to confirm it, before it becomes active. Please check your mail!"
modal.classList.add('is-active');
}
if (state === "post-fetch-exception" || state === "post-internal-server-error"){
button.classList.remove("is-loading");
modal_text.innerText = "Your comment was not entered because there was an error, which was recorded and reported automatically.";
modal.classList.add('is-active');
}
if (state === "post-invalid-email"){
button.classList.remove("is-loading");
modal_text.innerText = "The email you have entered appears to be invalid. Please contact me if you think this was in error.";
modal.classList.add('is-active');
}
}
function labertasche_reply_callback(state, comment_id)
{
if (state === "on"){
let comment_btn = document.getElementById('labertasche-comment-button');
let parent = comment_btn.parentElement
let new_btn = document.createElement("button");
new_btn.classList.add("button");
new_btn.classList.add("is-danger");
new_btn.classList.add("is-medium");
new_btn.classList.add("px-6");
new_btn.setAttribute("id", "labertasche-cancel-reply");
new_btn.onclick = function() { labertasche_reply_to(-1, labertasche_reply_callback); }
new_btn.innerHTML = '<span>Cancel Reply</span>';
parent.appendChild(new_btn);
comment_btn.innerHTML = "<span class='is-medium'>Reply to #" + comment_id + "</span>";
}
if (state === "off"){
console.log("off");
let comment_btn = document.getElementById('labertasche-comment-button');
comment_btn.innerHTML = "<span class='is-medium'>Comment</span>";
let cancel = document.getElementById('labertasche-cancel-reply');
if (cancel){
cancel.remove();
}
}
}

@ -6,20 +6,21 @@
# *********************************************************************************/
system:
web_url: "http://comments.example.com" # Url where the comment system is served
blog_url: "http://myblog.example.com" # Url of your website
web_url: "http://dev.localhost:1314/" # Url where the comment system is served
blog_url: "http://dev.localhost:1313/" # Url of your website
cookie-domain: "dev.localhost" # Url where the comment system is served
database_uri: "sqlite:///db/labertasche.db" # Database URI. Default is sqlite.
secret: "123456" # CHANGE ME! THIS IS IMPORTANT!
output: "/path/to/hugo_dir/data" # Base path for the output json
database_uri: "sqlite:///db/labertasche.db" # Database URI. See documentation. Default is sqlite.
secret: "6Gxvb52bIJCm2vfDsmWKzShKp1omrzVG" # CHANGE ME! THIS IS IMPORTANT!
output: "./__implementation_example/data/" # Base path for the output json
debug: false # Leave this as is, this is for development.
send_otp_to_publish: true # Disables confirmation w/ OTP via mail
gravatar:
cache: true # Enable caching of gravatar images
static_dir: "/path/to/hugo_dir//static/images/gravatar/" # Where to store cached images
static_dir: "./__implementation_example/static/images/gravatar/" # Where to store cached images, must exist!
size: 256 # only applies if images are cached,
# otherwise use ?s=size at the end of the gravatar url
dashboard:
username: "admin" # CHANGE ME!
password: "admin" # CHANGE ME!
@ -29,7 +30,7 @@ addons:
# If you want to expand this, please use this list:
# https://www.w3schools.com/charsets/ref_emoji_smileys.asp
# You need to both versions, upper and lowercase, if you want both to works
# You need to add upper and lowercase, if you want both to work.
# This is a simple search and replace action
smileys:
":)": "&#128512"

Loading…
Cancel
Save