nele

package module
v0.20.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 24, 2024 License: GPL-3.0 Imports: 43 Imported by: 0

README

Nele Blog

Golang GoDoc Go Report Issues Size Tag License View examples


Purpose

The purpose of this package/application was twofold initially. On one hand I needed a project to learn the (then to me new) Go language, and on the other hand I wanted a project, that lead me into different domains, like user authentication, configuration, data formats, error handling, filesystem access, data logging, os, network, regex, templating etc. – And, I wanted no external dependencies (like databases etc.). – And, I didn't care for Windows(tm) compatibility since I left the MS-platform about 25 years ago after using it in the 80s and early 90s of the last century. (But who, in his right mind, would want to run a web-service on such a platform anyway?)

That's how I ended up with this little blog-system (for lack of a better word; or: "diary", "notes", …). It's a system that lets you write and add articles from both the command line and a web-interface. It provides options to add, modify and delete entries using a user/password list for authentication when accessing certain URLs in this system. Articles can be added, edited (e.g. for correcting typos etc.), or removed altogether. If you don't like the styles coming with the package you can, of course, change them according to your preferences in your own installation.

The articles you write are then available on the net as web-pages.

It is not, however, a discussion platform. It's supposed to be used as a publication platform, not some kind of social media (where people thrive on insulting each other). So I intentionally didn't bother with comments or discussion threading.

Features

  • Markdown support
  • Multiple user accounts supported
  • No database (like SQLite, MariaDB, etc.) required
  • No JavaScript dependency
  • No cookies needed
  • Privacy aware
  • Simplicity of use

Installation

You can use Go to install this package for you:

go get -u github.com/mwat56/nele

Usage

After downloading this package you go to its directory and compile

go build app/nele.go

which should produce an executable binary. On my system it looked (at a certain point in time) like this:

$ ls -l
total 11420
drwxrwxr-x 12 matthias matthias     4096 Mai 23 18:35 .
drwxrwxr-x 12 matthias matthias     4096 Mai 23 17:58 ..
-rw-rw-r--  1 matthias matthias      474 Apr 27 00:21 addTest.md
drwxrwxr-x  3 matthias matthias     4096 Mai 23 18:14 app
drwxrwxr-x  2 matthias matthias     4096 Mai 23 18:14 certs
-rw-rw-r--  1 matthias matthias     6583 Mai 23 18:14 cmdline.go
-rw-rw-r--  1 matthias matthias    10149 Mai 23 18:20 config.go
-rw-rw-r--  1 matthias matthias     1846 Mai 23 18:14 config_test.go
drwxrwxr-x  2 matthias matthias     4096 Mai 23 18:14 css
-rw-rw-r--  1 matthias matthias      823 Mai 23 18:14 doc.go
drwxrwxr-x  2 matthias matthias     4096 Mai 23 18:14 fonts
drwxrwxr-x  8 matthias matthias     4096 Mai 23 18:10 .git
drwxrwxr-x  3 matthias matthias     4096 Mai 23 17:58 .github
-rw-rw-r--  1 matthias matthias      123 Mai 23 17:58 .gitignore
-rw-------  1 matthias matthias      507 Mai 23 18:11 go.mod
-rw-------  1 matthias matthias     4004 Mai 23 18:11 go.sum
-rw-rw-r--  1 matthias matthias     5010 Mai 23 18:18 hashfile.db
drwxrwxr-x  2 matthias matthias     4096 Mai 23 18:14 img
-rw-rw-r--  1 matthias matthias    32474 Mai 23 17:58 LICENSE
-rwxrwxr-x  1 matthias matthias 11149115 Mai 23 18:19 nele
-rw-rw-r--  1 matthias matthias    21803 Mai 23 18:14 pagehandler.go
-rw-rw-r--  1 matthias matthias      619 Mai 23 18:22 pagehandler_test.go
-rw-rw-r--  1 matthias matthias     9313 Mai 23 18:14 posting.go
drwxrwxr-x  8 matthias matthias     4096 Mai 23 18:01 postings
-rw-rw-r--  1 matthias matthias    15319 Mai 23 18:14 posting_test.go
-rw-rw-r--  1 matthias matthias     8240 Mai 23 18:14 postlist.go
-rw-rw-r--  1 matthias matthias     7279 Mai 23 18:23 postlist_test.go
-rw-rw-r--  1 matthias matthias       70 Mai 23 18:19 pwaccess.db
-rw-rw-r--  1 matthias matthias    22792 Mai 23 18:35 README.md
-rw-rw-r--  1 matthias matthias    10435 Mai 23 18:24 regex.go
-rw-rw-r--  1 matthias matthias     8190 Mai 23 18:14 regex_test.go
drwxrwxr-x  2 matthias matthias     4096 Mai 23 17:58 static
-rw-rw-r--  1 matthias matthias     3656 Mai 23 18:14 tags.go
-rw-rw-r--  1 matthias matthias     3811 Mai 23 17:58 template_vars.md
-rw-rw-r--  2 matthias matthias     3109 Mai 23 18:16 TODO.md
drwxrwxr-x  3 matthias matthias     4096 Mai 23 18:14 views
-rw-rw-r--  1 matthias matthias     6787 Mai 23 18:14 views.go
-rw-rw-r--  1 matthias matthias     6009 Mai 23 18:14 views_test.go
$ _

You can reduce the binary's size by stripping it:

$ strip nele
$ ls -l nele
-rwxrwxr-x 1 matthias matthias 8146912 Mai 23 18:38 nele
$ _

As you can see the binary lost about 3MB of its weight. Or you can further compress your Go binary by using UPX which would downsize the Nele binary to about 2.5MB.

Let's start with the command line:

$ ./nele -h

Usage: ./nele [OPTIONS]

-accessLog string
	<filename> Name of the access logfile to write to
	(default "/home/matthias/nele/access.bla.mwat.de")
-blogName string
	<string> Name of this Blog (shown on every page)
	(default "Meine Güte, was für'n Blah!")
-certKey string
	<fileName> Name of the TLS certificate key
-certPem string
	<fileName> Name of the TLS certificate PEM
-d	dump
-dataDir string
	<dirName> Directory with CSS, FONTS, IMG, SESSIONS, and VIEWS sub-directories
	(default "/home/matthias/nele")
-delWhitespace
	<boolean> Delete superfluous whitespace in generated pages (default true)
-errorlog string
	<filename> Name of the error logfile to write to
	(default "/home/matthias/error.bla.mwat.de")
-gzip
	<boolean> use gzip compression for server responses (default true)
-hashFile string
	<fileName> Name of the file storing #hashtags and @mentions
	(default "/home/matthias/nele/hashfile.db")
-ini string
	<fileName> the path/filename of the INI file to use
	(default "/home/matthias/.nele.ini")
-lang string
	<de|en> Default language to use  (default "de")
-listen string
	<IP number> The host's IP to listen at  (default "127.0.0.1")
-lst
	<boolean> Log a stack trace for recovered runtime errors  (default true)
-mfs string
	<filesize> Max. accepted size of uploaded files (default "10mb")
-pa
	<boolean> (optional) posting add: write a posting from the commandline
-pf string
	<fileName> (optional) post file: name of a file to add as new posting
-port int
	<port number> The IP port to listen to  (default 8181)
-pv
	<boolean> Use page preview images for links (default true)
-realm string
	<hostName> Name of host/domain to secure by BasicAuth
	(default "Matthias' Bla")
-theme string
	<name> The display theme to use ('light' or 'dark')
	(default "dark")
-ua string
	<userName> User add: add a username to the password file
-uc string
	<userName> User check: check a username in the password file
-ud string
	<userName> User delete: remove a username from the password file
-uf string
	<fileName> Passwords file storing user/passwords for BasicAuth
	(default "/home/matthias/nele/pwaccess.db")
-ul
	<boolean> User list: show all users in the password file
-uu string
	<userName> User update: update a username in the password file

Most options can be set in an INI file to keep the command-line short ;-)

$ _

Please note that the default values shown above vary depending on the system where nele is called and especially on the contents of the INI file (it's read before the help text is produced).

To just run the program, however, you'll usually don't need any of those options to input on the commandline. There is an INI file called nele.ini coming with the package, where you can store the most common settings:

$ cat nele.ini
# Nele's default configuration file

[Default]

# Name of the optional logfile to write to.
# NOTE: A relative path/name will be combined with `datadir` (below).
accessLog = ./access.log

# Name of this Blog (shown on every page).
blogName = "Meine Güte, was für'n Blah!"

# path-/filename of the TLS certificate's private key to enable
# TLS/HTTPS (if empty standard HTTP is used).
# NOTE: A relative path/name will be combined with `datadir` (below).
certKey = ./certs/server.key

# path-/filename of TLS (server) certificate to enable TLS/HTTPS
# (if empty standard HTTP is used).
# NOTE: A relative path/name will be combined with `datadir` (below).
certPem = ./certs/server.pem

# The directory root for the "css", "fonts", "img", "postings",
# "static", and "views" sub-directories.
# NOTE: This should be an _absolute_ path name.
dataDir = ./

# Delete superfluous whitespace in generated pages.
delWhitespace = yes

# Name of the optional logfile to write to.
# NOTE: A relative path/name will be combined with `datadir` (above).
errorLog =  ./error.log

# Use gzip compression for server responses.
gzip = true

# The file to store #hashtags and @mentions.
# NOTE: A relative path/name will be combined with `datadir` (above).
hashFile = ./hashfile.db

# The default UI language to use ("de" or "en").
lang = de

# The host's IP number to listen at.
# An empty value means: listen on all interfaces.
listen = 127.0.0.1

# Whether or not log a stack trace for recovered runtime errors.
# NOTE: This is merely a debugging aid and should normally be `false`.
logStack = true

# The IP port to listen to.
port = 8181

# Accepted size of uploaded files.
maxfilesize = 10MB

# Password file for HTTP Basic Authentication.
# NOTE: a relative path/name will be combined with `datadir` (above).
passFile = ./pwaccess.db

# Name of host/domain to secure by BasicAuth.
realm = "This Host"

# Use screenshot images of linked pages.
# NOTE: This feature depends on the external `wkhtmltoimage` binary;
# for more details see: https://godoc.org/github.com/mwat56/screenshot
Screenshot = true

# Web/display theme ("dark" or "light").
theme = dark

# _EoF_
$ _

The program, when started, will first look for the INI file in five different places:

  1. in your (i.e. the current user's) directory (./nele.ini),
  2. in the computer's main config directory (/etc/nele.ini"),
  3. in the current user's home directory (e.g. $HOME/.nele.ini),
  4. in the current user's configuration directory (e.g. $HOME/.config/nele.ini),
  5. in the -ini <filename> commandline option (if given).

All these files (if they exist) are read in the given order at startup before finally parsing the commandline options shown above. So each step overwrites the previous one, the commandline options having the highest priority. – But let's look at some of the commandline options more closely.

Commandline postings

You can post an article directly from the commandline.

./nele -pa allows you to write an article/posting directly on the commandline.

$ ./nele -pa
This is
a test
posting directly
from the commandline.
<Ctrl-D>
2019/05/06 14:57:30 ./nele wrote 54 bytes in a new posting
$ _

./nele -pf <fileName> allows you to include an already existing text file (with possibly some Markdown markup) into the system.

$ ./nele -pf addTest.md
2019/05/06 15:09:27 ./nele stored 474 bytes in a new posting
$ _

These two options (-pa and -pf) are only usable from the commandline.

Authentication

Why, you may ask, would you need an username/password file anyway? Well, you remember me mentioning that you can add, edit and delete articles? You wouldn't want anyone on the net being able to do that, now, would you? For that reason, whenever there's no password file given (either in the INI file or the command-line) all functionality requiring authentication will be disabled. (Better safe than sorry, right?)

Note that the password file generated and used by this system resembles the htpasswd used by the Apache web-server, but both files are not interchangeable because the actual encryption algorithms used by both respectively are different.

User/password file & handling

Only usable from the commandline are the -uXX options, most of which need a username and the name of the password file to use.

Note that whenever you're prompted to input a password this will not be echoed to the console.

The -ua option allows you to add an user/password pair:

$ ./nele -ua testuser1 -uf pwaccess.db

   password:
  repeat pw:
	added 'testuser1' to list
$ _

Again: The password input is not echoed to the console, therefore you don't see it.

Since we have the passfile setting already in our INI file (see above) we can forget the -uf option for the next options.

With -uc you can check a user's password:

$ ./nele -uc testuser1

password:
	'testuser1' password check successful
$ _

This -uc you'll probably never actually use, it was just easy to implement.

If you want to remove an user account the -ud will do the trick (i.e. delete a user):

$ ./nele -ud testuser1
	removed 'testuser1' from list
$ _

When you want to know which users are stored in your password file -ul is your friend:

$ ./nele -ul
matthias

$ _

Since we deleted the testuser1 before only one entry remains.

That only leaves -uu to update (change) a user's password.

$ ./nele -ua testuser2

password:
repeat pw:
	added 'testuser2' to list

$ ./nele -uu testuser2

   password:
  repeat pw:
	updated user 'testuser2' in list

$ ./nele -ul
matthias
testuser2

$ _

First we added (-ua) a new user, then we updated the password (-uu), and finally we asked for the list of users (-ul).

If you set the Screenshot INI- or commandline-option to true there will be a preview image generated – by way of calling the ChromeDP library. Those image files are stored locally (in the ./img/ directory) and may be used as often as you want.

Note that screenshot images are created only for links in a blockquote section:

> [link text](http://www.example.org/one.html)

will be changed to

> [![alt text](/httpwwwexampleorgonehtml.png)](http://www.example.org/one.html)

while

bla [link text](http://www.example.org/one.html) bla

will be left untouched as a normal hyperlink.

This restriction was introduced to avoid messing up the overall layout of a posting: It wouldn't look good if every link in a sentence would be replaced by an image.

The Go library controlling a headless instance of the Chrome browser is ChromeDP and is required for this package to work. Under Linux this browser is usually part of your distribution (as chromium-browser).

Generating a screenshot image usually takes between one and five seconds, depending on the actual web-page in question, bandwidth, traffic etc.; however, it can take considerably longer. To avoid hanging the program the CreateImage() function uses a timeout of half a minute by default.

And, finally, not all web-pages can be rendered properly and turned into an image. In such a case ChromeDP usually aborts with an error and the link in your posting just remains as is (i.e. a normal text link w/o preview/screenshot).

Configuration

The system's configuration takes two steps:

  1. Prepare the required files and directories.
  2. Customise the INI file and/or prepare a script with all needed commandline arguments.
  3. You most probably want to customise the files ./views/imprint.gohtml, ./views/licence.gohtml, and ./views/privacy.gohtml according to your personal requirements.

URLs

The system uses a number of slightly different URL groups.

Static URLs

First, there are the static files served from the css, img, and static directories. The actual location of which you can configure with the datadir INI entry and/or commandline option.

Common URLs

Second, there are the URLs any normal user might see and use:

  • / [r/o]: See the root of the presentation; it's effectively the same as /n/ (see below).
  • /faq, /imprint, /licence, and /privacy [r/o]: Static files which have to be filled with content according to your personal and legal needs.
  • /hl/tagname [r/o]: Search for #tagname (but you'll input it without the number sign # because that has a special meaning in an URL). Provided the given tagname was actually used in one or more of your articles a list of the respective postings will be shown.
  • /m/ [r/o]: See the articles of the current month. One can, however, specify the month one is interested in by adding a data part defining the month one wants to see (/m/yyyy-mm), like /m/2019-04 to see the articles from April 2019.
  • /ml/mentionedname [r/o]: Search for @mentionedname (but one will input it without the at sign @ because that has a special meaning in an URL).
  • /ml [r/o]: See a list of all used @mentions. Provided the given mentionedname was actually used in one or more of your articles a list of the respective articles will be shown.
  • /n/ [r/o]: See the chronologically newest postings. The number of articles to show can be added to the URL like /n/5 to see only five articles, or /n/100 to see a hundred. If one want to see the articles in slices of, say, 10 per page (instead of the default 30/page) one can use the URL /n/10,10 and to see the second slice use /n/10,20, the third with /n/10,30 and so on. However, as long as there are more articles available, there will be a »» link at the bottom of the page to ease the navigation for the reader.
  • /p/1234567890abcdef [r/o]: shows a single article/posting (the ID is automatically generated). This kind of URL your users will see when they choose on another page to see the single article per page by selecting the leading [*] link in the overview page(s).
  • /q/searchterm [r/o]: can be used to search for articles containing a certain word or expression. All existing articles will be searched for the given searchterm.
  • /w/ [r/o]: See the articles of the current week. One can, however, specify the week one is interested in by adding a data part defining the week to see (/w/yyyy-mm-dd), like /w/2019-04-13 to see the articles from the week in April 2019 containing the 13th.
Internal URLs

And, third, there's a group of URLs your users won't see or use, because by design they are reserved for you, the author of your postings. These URLs are protected by an authentication mechanism called BasicAuth (which is supported by browsers for at least thirty years); this is where the username/password file comes in. Only users whose credentials (i.e. username and password) are stored in the password file will be given access to the following URLs. So don't forget to set up an appropriate password file. If you forget that (or the file is not accessible for the program) everybody on the net could read, modify, or delete your articles, or add new ones – which you might not like; therefore the system disables all options that might modify your system.

  • /ap/ [r/w]: Add a new posting. A simple Web form will allow you to input whatever is on your mind.
  • /dp/234567890abcdef1 [r/w]: Change an article/posting's date/time if you feel the need for cosmetic or other reasons. Since you don't usually know/remember the article ID you'll first go to show the article/posting on a single page (/n/) by selecting the respective [*] link on the index page and then just prepend the p by a d in the URL.
  • /ep/34567890abcdef12 [r/w]: Edit the article/posting's text identified by 34567890abcdef12, e.g. to fix typos or correct the grammar.
  • /il [r/w]: Assuming you configured the hashfile INI-/commandline-option this shows you a simple HTML form by which you can start a background process re-initialising the hashlist. It clears the current list and reads all postings to extract the #hashtags and @mentions. Note: You will barely (if ever) need this option; it's mostly a debugging aid.
  • /pv/ [r/w]: Assuming you set the Screenshot INI-/commandline-option to true this shows a simple HTML form by which you can start a background process checking all postings for page preview/screenshot images. Again, this was implemented as a debugging aid and you won't usually use this option.
  • /rp/4567890abcdef123 [r/w]: lets you remove (delete) the article/posting identified by 4567890abcdef123 altogether. Note that there's no undo feature: Once you've deleted an article/posting it's gone.
  • /share/https://some.host.domain/somepage [r/w]: lets you share another page URL. Whatever you write after the initial /share/ is assumed to be a remote URL, and a new article will be created and shown for you to edit.
  • /si/ [r/w] (store image): This shows you a simple HTML form by which you can upload image files into your /img/ directory. Once the upload is done you (i.e. the user) will be presented an edit page in which the uploaded image is used.
  • /ss/ [r/w] (store static): This shows you a simple HTML form by which you can upload static files into your /static/ directory. Once the upload is done you (i.e. the user) will be presented an edit page in which the uploaded file is used.
  • /xt/ [r/w] (eXchange tag): This shows you a simple HTML form by which you can exchange a #hashtag/@mention with another one, or correct its writing. Note that the search for the term to replace is done case-insensitive while the replacement string gets inserted as you write it.

Files

Right at the start I mentioned that I wanted to avoid external dependencies – like databases for example. Well, that's not exactly true (or even possible), because there is one "database" that's always already there, regardless of the operating system: the filesystem. The trick is to figure out how to best use it for our own purposes. The solution I came up with here is to use sort of a timestamp as ID and filename for the articles, and use part of that very timestamp as ID and name for the directory names as well.

Both directory- and file-names are automatically handled by the system. Each directory can hold up to 52 days worth of articles. After extensive experimentation – with hundreds of thousands of automatically generated (and deleted) test files – that number seemed to be a reasonable compromise between directories not growing too big (search times) and keeping the number of directories used low (about seven per year).

All this data (files and directories) will be created under the directory you configure either in the INI file (entry datadir) or on the commandline (option -datadir). Under that directory the program expects several sub-directories:

  • css/ for stylesheet files,
  • fonts/ for font files,
  • img/ for image files,
  • postings/ directory root for the articles,
  • static/ for static files (like e.g. PDF files),
  • views/ for page templates

Apart from setting that datadir option to your liking you don't have to worry about it any more.

As mentioned before, it's always advisable to use absolute pathnames, not relative one. The latter are converted into absolute ones (based on datadir) by the system, but they depend on where you are in the filesystem when you start the program or write the commandline options. You can use ./nele -h to see which directories the program will use (see the example above).

CSS

In the CSS directory (datadir/css) there are currently four files that are used automatically (i.a. hardcoded) by the system: stylesheet.css with some basic styling rules and dark.css and light.css with different settings for mainly colours, thus implementing two different themes for the web-presentation, and there's the fonts.css file setting up the custom fonts to use. The theme INI setting and the -theme commandline option determine which of the two dark and light styles to actually use.

Fonts

The datadir/fonts/ directory contains some freely available fonts used by the CSS files.

Images

The datadir/img/ directory can be used to store, well, images to which you then can link in your articles. You can put there whatever images you like either from the command-line or by using the system's /si URL.

Additionally any page preview/screenshot images are stored here (if set the Screenshot INI- or commandline-option to true).

Postings

The datadir/ directory is the base for storing all the articles. The system creates subdirectories as needed to store new articles. This directory structure is not accessed via a direct URL but used internally by the system.

Static

The datadir/static/ directory can be used to store, well, static files to which you then can link in your articles. You can put there whatever file you like either from the command-line or by using the system's /ss URL.

Views

The datadir/views/ directory holds the templates with which the final HTML pages are generated. Provided that you feel at home working with Go templates you might change them as you see fit. I will, however, not provide any support for you changing the default template structure.

A concise overview of the used templates and which variables they use you'll find in the file template_vars.md

Contents

For all the article you write – either on the commandline or with the web-interface – you can use Markdown to enrich the plain text. In fact, the system expects the postings to be using MarkDown syntax if any markup at all.

Libraries

The following external libraries were used building Nele:

Licence

Copyright © 2019, 2024  M.Watermann, 10247 Berlin, Germany
				All rights reserved
			EMail : <support@mwat.de>

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.

This software 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.

You should have received a copy of the GNU General Public License along with this program. If not, see the GNU General Public License for details.


GFDL

Documentation

Overview

Copyright © 2020, 2024 M.Watermann, 10247 Berlin, Germany

    All rights reserved
EMail : <support@mwat.de>

Package nele implements a simple blog-server.

Copyright © 2019, 2020 M.Watermann, 10247 Berlin, Germany
               All rights reserved
           EMail : <support@mwat.de>

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.

This software 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.

You should have received a copy of the GNU General Public License along with this program. If not, see the [GNU General Public License](http://www.gnu.org/licenses/gpl.html) for details.

Copyright © 2019, 2024 M.Watermann, 10247 Berlin, Germany

    All rights reserved
EMail : <support@mwat.de>

Index

Constants

This section is empty.

Variables

View Source
var (
	// `ErrEmptyPosting` is returned when a `nil` posting is passed to
	// a method.
	ErrEmptyPosting = errors.New("empty post")

	// `ErrSkipAll` can be used by a `TWalkFunc` to skip the [Walk].
	ErrSkipAll = errors.New("signal skipping the remaining directory walk")
)

Functions

func AddConsolePost

func AddConsolePost() (int, error)

AddConsolePost reads data from `StdIn` and saves it as a new posting, returning the number of bytes written and a possible I/O error.

func AddFilePost

func AddFilePost(aFilename string) (int, error)

AddFilePost reads `aFilename` and adds it as a new posting, returning the number of bytes written and a possible I/O error.

`aFilename` The text file to add as a new posting.

func AddTagID

func AddTagID(aList *ht.THashTags, aPosting *TPosting)

`AddTagID()` checks a newly added `aPosting` for #hashtags and @mentions.

Parameters:

  • `aList`: The hashlist to use (update).
  • `aPosting`: The new posting to handle.

func CreateScreenshot added in v0.18.0

func CreateScreenshot(aURL string)

`CreateScreenshot()` generates a screenshot of `aURL` in background.

Parameters:

  • `aURL`: The URL for which to create a screenshot image.

func InitConfig

func InitConfig()

`InitConfig()` reads both the INI values and the commandline arguments.

The steps here are:

(1) read the INI file(s):

(a) read the local `./.nele.ini`,
(b) read the global `/etc/nele.ini`,
(c) read the user-local `~/.nele.ini`,
(d) read the user-local `~/.config/nele.ini`,

(2) merge the commandline arguments with the INI values into the global `AppArgs` variable.

This function is meant to be called first thing in the application's `main()` function.

func InitHashlist

func InitHashlist(aList *ht.THashTags)

`InitHashlist()` initialises the hash list.

Parameters:

  • `aList`: The list of #hashtags/@mentions to update.

func MDtoHTML

func MDtoHTML(aMarkdown []byte) (rHTML []byte)

`MDtoHTML()` converts the `aMarkdown` data and returns HTML data.

Parameters:

  • `aMarkdown` The raw Markdown text to convert.

Returns:

  • `[]byte`: The generated HTML data.

func MarkupCloud

func MarkupCloud(aList *ht.THashTags) []template.HTML

`MarkupCloud()` returns a list with the markup of all existing #hashtags/@mentions.

Parameters:

`aList`: The list of #hashtags/@mentions to use.

func MarkupTags

func MarkupTags(aPage []byte) []byte

`MarkupTags()` returns `aPage` with all #hashtags/@mentions marked up as a HREF links.

Parameters:

  • `aPage`: The HTML page to process.

func PostingBaseDirectory

func PostingBaseDirectory() string

`PostingBaseDirectory()` returns the base directory used for storing the postings.

Returns:

  • `string`: The base directory tu use.

func PrepareLinkScreenshots added in v0.18.0

func PrepareLinkScreenshots(aPosting *TPosting)

`PrepareLinkScreenshots()` updates the external link(s) in `aPosting` to include page screenshot image(s) (if available).

Parameters:

  • `aPosting`: The posting the text of which is going to be processed.

func ReadHashlist added in v0.9.0

func ReadHashlist(aList *ht.THashTags)

`ReadHashlist()` reads all postings to (re-)build the list of #hashtags/@mentions disregarding any pre-existing list.

Parameters:

  • `aList`: The list of #hashtags/@mentions to build.

func RemoveIDTags

func RemoveIDTags(aList *ht.THashTags, aID uint64)

`RemoveIDTags()` removes `aID` from `aList's` items.

Parameters:

  • `aList`: The hashlist to update.
  • `aID`: The ID of the posting to remove.

func RemovePageScreenshots added in v0.18.0

func RemovePageScreenshots(aPosting *TPosting)

`RemovePageScreenshots()` deletes the images used in `aPosting`.

Parameters:

  • `aPosting`: The posting the image(s) of which are going to be deleted.

func RenameIDTags

func RenameIDTags(aList *ht.THashTags, aOldID, aNewID uint64)

`RenameIDTags()` renames all references of `aOldID` to `aNewID`.

Parameters:

  • `aList`: The hashlist to update.
  • `aOldID`: The posting's old ID.
  • `aNewID`: The posting's new ID.

func ReplaceTag

func ReplaceTag(aList *ht.THashTags, aSearchTag, aReplaceTag string)

`ReplaceTag()` replaces the #tags/@mentions in `aList`.

Parameters:

  • `aList`: The hashlist to update.
  • `aSearchTag`: The old #tag/@mention to find.
  • `aReplaceTag`: The new #tag/@mention to use.

func SetPersistence added in v0.20.0

func SetPersistence(aPersistence IPersistence)

`SetPersistence()` sets the persistence layer to actually use.

Parameters:

  • `aPersistence`: The persistence layer to use for storing/retrieving postings.

func SetPostingBaseDirectory

func SetPostingBaseDirectory(aBaseDir string) error

`SetPostingBaseDirectory()` sets the base directory used for storing the postings.

Parameters:

  • `aBaseDir` The base directory to use for storing articles/postings.

Returns:

  • `error`: Any error that might have occurred.

Example:

// Set the base directory to "/path/to/new/base/directory"
err := nele.SetPostingBaseDirectory("/path/to/new/base/directory")
if err != nil {
	log.Fatal(err)
}

// Get the current base directory
fmt.Println(nele.PostingBaseDirectory())

// Set the base directory back to the default value
err = nele.SetPostingBaseDirectory("./postings")
if err != nil {
	log.Fatal(err)
}

func ShowHelp

func ShowHelp()

ShowHelp lists the commandline options to `Stderr`.

func URLparts

func URLparts(aURL string) (rPath, rTail string, rID uint64)

`URLparts()` returns three parts: the base-directory of `aURL`, the remaining part of `aURL`, and a possible posting ID.

Depending on the actual value of `aURL` all return values may be empty or all may be filled; none of the first two will hold a leading slash.

Parameters:

  • `aURL`: The URL as specified by a HTTP request.

Returns:

  • `rPath`: The base-directory of `aURL`.
  • `rTail`: The remaining part of `aURL`.
  • `rID`: A user ID (if art of the URL).

func UpdateScreenshots added in v0.18.0

func UpdateScreenshots()

`UpdateScreenshots()` starts the process to update the screenshot images in all postings.

func UpdateTags

func UpdateTags(aList *ht.THashTags, aPosting *TPosting)

`UpdateTags()` updates the #hashtag/@mention references of `aPosting`.

Parameters:

  • `aList`: The hashlist to update.
  • `aPosting`: The new posting to process.

func UserAdd added in v0.17.0

func UserAdd(aUser, aFilename string)

UserAdd reads a password for `aUser` from the commandline and adds it to `aFilename`.

NOTE: This function does not return but terminates the program with error code `0` (zero) if successful, or `1` (one) otherwise.

`aUser` The username to add to the password file.
`aFilename` The name of the password file to use.

func UserCheck added in v0.17.0

func UserCheck(aUser, aFilename string)

UserCheck reads a password for `aUser` from the commandline and compares it with the one stored in `aFilename`.

NOTE: This function does not return but terminates the program with error code `0` (zero) if successful, or `1` (one) otherwise.

`aUser` The username to check in the password file.
`aFilename` The name of the password file to use.

func UserDelete added in v0.17.0

func UserDelete(aUser, aFilename string)

UserDelete removes the entry for `aUser` from the password list `aFilename`.

NOTE: This function does not return but terminates the program with error code `0` (zero) if successful, or `1` (one) otherwise.

`aUser` The username to remove from the password file.
`aFilename` The name of the password file to use.

func UserList added in v0.17.0

func UserList(aFilename string)

UserList reads `aFilename` and lists all users stored in there.

NOTE: This function does not return but terminates the program with error code `0` (zero) if successful, or `1` (one) otherwise.

`aFilename` The name of the password file to use.

func UserUpdate added in v0.17.0

func UserUpdate(aUser, aFilename string)

UserUpdate reads a password for `aUser` from the commandline and updates the entry in the password list `aFilename`.

NOTE: This function does not return but terminates the program with error code `0` (zero) if successful, or `1` (one) otherwise.

`aUser` The username to remove from the password file.

`aFilename` The name of the password file to use.

Types

type IPersistence added in v0.20.0

type IPersistence interface {
	//
	// `Create()` creates a new persistent posting.
	//
	// If the provided `aPost` is `nil`, an `ErrEmptyPosting` error
	// is returned.
	//
	// Parameters:
	//	- `aPost`: The `TPosting` instance containing the article's data.
	//
	// Returns:
	//	- `int`: The number of bytes stored.
	//	- 'error`: A possible error, or `nil` on success.
	Create(aPost *TPosting) (int, error)

	//
	// `Delete()` removes the posting/article from the persistence layer
	// and returns a possible I/O error.
	//
	// Parameters:
	//	- `aID`: The unique identifier of the posting to delete.
	//
	// Returns:
	//	- 'error`: A possible error, or `nil` on success.
	Read(aID uint64) (*TPosting, error)

	//
	// `Update()` updates the article's data in the persistence layer.
	//
	// It returns the number of bytes written and a possible I/O error.
	//
	// If the provided `aPost` is `nil`, an `ErrEmptyPosting` error
	// is returned.
	//
	// Parameters:
	//	- `aPost`: A `TPosting` instance containing the article's data.
	//
	// Returns:
	//	- `int`: The number of bytes written.
	//	- 'error`:` A possible error, or `nil` on success.
	Update(aPost *TPosting) (int, error)

	//
	// `Delete()` removes the posting/article from the persistence layer.
	//
	// Parameters:
	//	- `aID`: The unique identifier of the posting to delete.
	//
	// Returns:
	//	 - 'error`: A possible error, or `nil` on success.
	Delete(aID uint64) error

	//
	// `Count()` returns the number of postings available.
	//
	// Returns:
	//	 - `int`: The number of available postings, or `0`
	// in case of errors.
	Count() int

	//
	// `Exists()` checks if a post with the given ID exists in the
	// persistence layer.
	//
	// Parameters:
	//
	//	- `aID`: The unique identifier of the posting to check.
	//
	// Returns:
	//
	//	- `bool`: `true` if the post exists, `false` otherwise.
	Exists(aID uint64) bool

	//
	// `PathFileName()` returns the posting's complete path-/filename.
	//
	// NOTE: The actual definition of the path-/filename depends on
	// the implementation of this interface. In a file-based persistence
	// layer it would be a `/path/directory/filename` string.
	// However, in a database-based persistence layer it would be the
	// `/path/file` of the database file.
	//
	// Parameters:
	//	- `aID`: The unique identifier of the posting to handle.
	//
	// Returns:
	//	- `string`: The path-/filename associated with `aID`.
	PathFileName(aID uint64) string

	//
	// `Rename()` renames a posting from its old ID to a new ID.
	//
	// Parameters:
	//	- aOldID: The unique identifier of the posting to be renamed.
	//	- aNewID: The new unique identifier for the new posting.
	//
	// Returns:
	//	- `error`: An error if the operation fails, or `nil` on success.
	Rename(aOldID, aNewID uint64) error

	//
	// `Search()` retrieves a list of postings based on a search term.
	//
	// A zero value of `aLimit` means: no limit alt all.
	//
	// The returned `TPostList` type is a slice of `TPosting` instances,
	// where `TPosting` is a struct representing a single posting. If
	// the returned slice is an empty list then no matching postings were
	// found; if it is `nil` it means there was an error retrieving the
	// matches.
	//
	// Parameters:
	//   - `aText`: The search term to look for.
	//   - `aOffset`: An offset in the result set of the search results.
	//   - `aLimit`: The maximum number of search results to return.
	//
	// Returns:
	//   - `*TPostList`: The list of search results, or `nil` in case of errors.
	//   - `error`: If the search operation fails, or `nil` on success.
	Search(aText string, aOffset, aLimit uint) (*TPostList, error)

	//
	// `Walk()` visits all existing postings, calling `aWalkFunc`
	// for each posting.
	//
	// Parameters:
	//	- `aWalkFunc`: The function to call for each posting.
	//
	// Returns:
	//	- `error`: a possible error occurring the traversal process.
	Walk(aWalkFunc TWalkFunc) error
}

`IPersistence` defines a persistence layer for storing `TPosting` objects. It uses a CRUD interface with some additional methods as documented below.

func Persistence added in v0.20.0

func Persistence() IPersistence

`Persistence()` returns the persistence layer to actually use creating, updating, deleting, searching, and walking through postings.

Returns:

  • `IPersistence`: The persistence layer to use for storing/retrieving postings.

type TAppArgs added in v0.17.0

type TAppArgs struct {
	AccessLog string // (optional) name of page access logfile
	Addr      string // listen address ("1.2.3.4:5678")
	BlogName  string // name/description of this blog
	CertKey   string // TLS certificate key
	CertPem   string // private TLS certificate
	DataDir   string // base directory of application's data

	Dump     bool   // Debug: dump this structure to `StdOut`
	ErrorLog string // (optional) name of page error logfile
	GZip     bool   // send compressed data to remote browser
	HashFile string // file of hashtag/mention database
	// Intl       string // path/filename of the localisation file
	Lang string // default GUI language

	LogStack bool // log stack trace in case of errors

	MaxFileSize int64 // max. upload file size

	Name string // name of the actual program

	PageLength uint // the number of postings to show per page

	PostAdd  bool   // whether to write a posting from commandline
	PostFile string // name of file to post

	Realm      string // host/domain to secure by BasicAuth
	Screenshot bool   // whether to use page screenshots or not
	Theme      string // `dark` or `light` display theme
	UserAdd    string // username to add to password list
	UserCheck  string // username to check in password list
	UserDelete string // username to delete from password list
	UserFile   string // (optional) name of page access logfile
	UserList   bool   // print out a list of current users
	UserUpdate string // username to update in password list
	// contains filtered or unexported fields
}

`TAppArgs` is a collection of commandline arguments and INI values.

var (
	// `AppArgs` holds the commandline arguments and INI values combined.
	//
	// This structure should be considered `R/O` after it was set up
	// by a call to `InitConfig()`.
	AppArgs TAppArgs
)

func (TAppArgs) String added in v0.17.0

func (aa TAppArgs) String() string

`String()` implements the `Stringer` interface returning a (pretty printed) string representation of the current `TAppArgs` instance.

NOTE: This method is meant mostly for debugging purposes.

Returns:

  • `string`: The string representation of the current app configuration.

type TDBpersistence added in v0.20.0

type TDBpersistence struct {
	// contains filtered or unexported fields
}

`TDBpersistence` is a database-based `IPersistence` implementation.

func NewDBpersistence added in v0.20.0

func NewDBpersistence(aName string) *TDBpersistence

`NewDBpersistence()` creates a new instance of `TDBpersistence`.

In case of errors initialising the database connection, the function returns a `nil` value.

Parameters:

  • `aName`: The name of the database file to use.

Returns:

  • `*TDBpersistence`: A persistence instance instance.

func (TDBpersistence) Count added in v0.20.0

func (dbp TDBpersistence) Count() int

`Count()` returns the number of postings currently available.

NOTE: This method is very resource intensive as it has to count all the posts stored in the filesystem.

Returns:

  • `int`: The number of available postings, or `0` in case of errors.

func (TDBpersistence) Create added in v0.20.0

func (dbp TDBpersistence) Create(aPost *TPosting) (int, error)

`Create()` creates a new posting in the filesystem.

If the provided `aPost` is `nil`, an `ErrEmptyPosting` error is returned.

Parameters:

  • `aPost`: The `TPosting` instance containing the article's data.

Returns:

  • `int`: The number of bytes stored.
  • 'error`:` A possible error, or `nil` on success.

func (TDBpersistence) Delete added in v0.20.0

func (dbp TDBpersistence) Delete(aID uint64) error

`Delete()` removes the posting/article from the filesystem and returns a possible I/O error.

Parameters:

  • `aID`: The unique identifier of the posting to delete.

Returns:

  • 'error`: A possible I/O error, or `nil` on success.

Side Effects:

  • Invalidates the internal count cache.

func (TDBpersistence) Exists added in v0.20.0

func (dbp TDBpersistence) Exists(aID uint64) bool

`Exists()` checks if a file with the given ID exists in the filesystem.

It returns a boolean value indicating whether the file exists.

Parameters:

  • `aID`: The unique identifier of the posting to check.

Returns:

  • `bool`: `true` if the file exists, `false` otherwise.

func (TDBpersistence) PathFileName added in v0.20.0

func (dbp TDBpersistence) PathFileName(aID uint64) string

`PathFileName()` returns the posting's complete path-/filename.

The returned path-/filename is in the format:

<base_directory>/<posting_id>.md

Parameters:

  • `aID`: The unique identifier of the posting to handle.

Returns:

  • `*string`: The path-/filename associated with `aID`.

func (TDBpersistence) Read added in v0.20.0

func (dbp TDBpersistence) Read(aID uint64) (*TPosting, error)

`Read()` reads the posting from disk, returning a possible I/O error.

Parameters:

  • `aID`: The unique identifier of the posting to be read.

Returns:

  • `*TPosting`: The `TPosting` instance containing the article's data, or `nil` if the record doesn't exist.
  • 'error`: A possible I/O error, or `nil` on success.

func (TDBpersistence) Rename added in v0.20.0

func (dbp TDBpersistence) Rename(aOldID, aNewID uint64) error

`Rename()` renames a posting from its old ID to a new ID.

Parameters:

  • aOldID: The unique identifier of the posting to be renamed.
  • aNewID: The new unique identifier for the new posting.

Returns:

  • `error`: An error if the operation fails, or `nil` on success.

func (TDBpersistence) Search added in v0.20.0

func (dbp TDBpersistence) Search(aText string, aOffset, aLimit uint) (*TPostList, error)

`Search()` retrieves a list of postings based on a search term.

The method uses SQLite's FTS5 (Full-Text Search) feature to perform the search. If the underlying database does not support FTS5, the method falls back to a LIKE-based search.

A zero value of `aLimit` means: no limit alt all.

The returned `TPostList` type is a slice of `TPosting` instances, where `TPosting` is a struct representing a single posting. If the returned slice is an empty list then no matching postings were found; if it is `nil` it means there was an error retrieving the matches.

Parameters:

  • `aText`: The search query string.
  • `aOffset`: An offset in the database result set of the search results.
  • `aLimit`: The maximum number of search results to return.

Returns:

  • `*TPostList`: The list of search results, or `nil` in case of errors.
  • `error`: If the search operation fails, or `nil` on success.

func (TDBpersistence) Update added in v0.20.0

func (dbp TDBpersistence) Update(aPost *TPosting) (int, error)

`Update()` updates the article's Markdown in the database.

It returns the number of bytes stored and a possible I/O error.

If the provided `aPost` is `nil`, an `ErrEmptyPosting` error is returned.

Parameters:

  • `aPost`: A `TPosting` instance containing the article's data.

Returns:

  • `int`: The number of bytes written to the file.
  • 'error`:` A possible I/O error, or `nil` on success.

Side Effects:

  • Invalidates the internal count cache.

func (TDBpersistence) Walk added in v0.20.0

func (dbp TDBpersistence) Walk(aWalkFunc TWalkFunc) error

`Walk()` visits all existing postings, calling `aWalkFunc` for each posting.

Parameters:

  • `aWalkFunc`: The function to call for each posting.

Returns:

  • `error`: a possible error occurring the traversal process.

type TFSpersistence added in v0.20.0

type TFSpersistence struct {
	// contains filtered or unexported fields
}

`TFSpersistence` is a file-based `IPersistence` implementation.

func NewFSpersistence added in v0.20.0

func NewFSpersistence() *TFSpersistence

`NewFSpersistence()` creates a new instance of `TFSpersistence`.

It does not take any parameters.

Returns:

  • `*TFSpersistence`: A persistence instance instance.

func (TFSpersistence) Count added in v0.20.0

func (fsp TFSpersistence) Count() int

`Count()` returns the number of postings currently available.

NOTE: This method is very resource intensive as it has to count all the posts stored in the filesystem.

Returns:

  • `int32`: The number of available postings, or `0` in case of I/O errors.

Side Effects:

  • Updates the count cache.

func (TFSpersistence) Create added in v0.20.0

func (fsp TFSpersistence) Create(aPost *TPosting) (int, error)

`Create()` creates a new posting in the filesystem.

If the provided `aPost` is `nil`, an `ErrEmptyPosting` error is returned.

Parameters:

  • `aPost`: The `TPosting` instance containing the article's data.

Returns:

  • `int`: The number of bytes written to the file.
  • 'error`:` A possible error, or `nil` on success.

Side Effects:

  • Invalidates the internal count cache.

func (TFSpersistence) Delete added in v0.20.0

func (fsp TFSpersistence) Delete(aID uint64) error

`Delete()` removes the posting/article from the filesystem and returns a possible I/O error.

Parameters:

  • `aID`: The unique identifier of the posting to delete.

Returns:

  • 'error`: A possible I/O error, or `nil` on success.

Side Effects:

  • Invalidates the internal count cache.

func (TFSpersistence) Exists added in v0.20.0

func (fsp TFSpersistence) Exists(aID uint64) bool

`Exists()` checks if a file with the given ID exists in the filesystem.

It returns a boolean value indicating whether the file exists.

Parameters:

  • `aID`: The unique identifier of the posting to check.

Returns:

  • `bool`: `true` if the file exists, `false` otherwise.

func (TFSpersistence) PathFileName added in v0.20.0

func (fsp TFSpersistence) PathFileName(aID uint64) string

`PathFileName()` returns the posting's complete path-/filename.

The returned path-/filename is in the format:

<base_directory>/<posting_id>.md

Parameters:

  • `aID`: The unique identifier of the posting to handle.

Returns:

  • `*string`: The path-/filename associated with `aID`.

func (TFSpersistence) Read added in v0.20.0

func (fsp TFSpersistence) Read(aID uint64) (*TPosting, error)

`Read()` reads the posting from disk, returning a possible I/O error.

Parameters:

  • `aID`: The unique identifier of the posting to be read.

Returns:

  • `*TPosting`: The `TPosting` instance containing the article's data, or `nil` if the file does not exist.
  • 'error`: A possible I/O error, or `nil` on success.

func (TFSpersistence) Rename added in v0.20.0

func (fsp TFSpersistence) Rename(aOldID, aNewID uint64) error

`Rename()` renames a posting from its old ID to a new ID.

Parameters:

  • aOldID: The unique identifier of the posting to be renamed.
  • aNewID: The new unique identifier for the new posting.

Returns:

  • `error`: An error if the operation fails, or `nil` on success.

func (TFSpersistence) Search added in v0.20.0

func (fsp TFSpersistence) Search(aText string, aOffset, aLimit uint) (*TPostList, error)

`Search()` retrieves a list of postings based on a search term.

A zero value of `aLimit` means: no limit alt all.

The returned `TPostList` type is a slice of `TPosting` instances, where `TPosting` is a struct representing a single posting. If the returned slice is an empty list then no matching postings were found; if it is `nil` it means there was an error retrieving the matches.

Parameters:

  • `aText`: The search query string.
  • `aOffset`: An offset in the database result set of the search results.
  • `aLimit`: The maximum number of search results to return.

Returns:

  • `*TPostList`: The list of search results, or `nil` in case of errors.
  • `error`: If the search operation fails, or `nil` on success.

func (TFSpersistence) Update added in v0.20.0

func (fsp TFSpersistence) Update(aPost *TPosting) (int, error)

`Update()` updates the article's Markdown on disk.

It returns the number of bytes written to the file and a possible I/O error.

If the provided `aPost` is `nil`, an `ErrEmptyPosting` error is returned.

Parameters: - `aPost`: A `TPosting` instance containing the article's data.

Returns: - `int`: The number of bytes written to the file. - 'error`:` A possible I/O error, or `nil` on success.

Side Effects: - Invalidates the internal count cache.

func (TFSpersistence) Walk added in v0.20.0

func (fsp TFSpersistence) Walk(aWalkFunc TWalkFunc) error

`Walk()` visits all existing postings, calling `aWalkFunc` for each posting.

Parameters:

  • `aWalkFunc`: The function to call for each posting.

Returns:

  • `error`: a possible error occurring the traversal process.

type TPageHandler

type TPageHandler struct {
	// contains filtered or unexported fields
}

TPageHandler provides the handling of HTTP request/response.

func NewPageHandler

func NewPageHandler() (*TPageHandler, error)

`NewPageHandler()` creates a new `TPageHandler` instance.

The returned object implements the `errorhandler.TErrorPager`, `http.Handler`, and `passlist.TAuthDecider` interfaces.

Returns:

  • `*TPageHandler`: The handler for HTTP request/response.
  • `error`: A possible processing error.

func (*TPageHandler) GetErrorPage

func (ph *TPageHandler) GetErrorPage(aData []byte, aStatus int) []byte

`GetErrorPage()` returns an error page for `aStatus`, implementing the `TErrorPager` interface.

Parameters:

  • `aData`: An error page to use.
  • `aStatus`: The HTTP status to handle.

Returns:

  • `[]byte`: The complete error page to send to the remote caller.

func (*TPageHandler) Len

func (ph *TPageHandler) Len() int

`Len()` returns the length of the internal views list.

Returns:

  • `int`: The number of available views/pages.

func (*TPageHandler) NeedAuthentication

func (ph *TPageHandler) NeedAuthentication(aRequest *http.Request) bool

`NeedAuthentication()` returns `true` if authentication is needed, or `false` otherwise.

Parameters:

  • `aRequest`: The request to check.

Returns:

  • `bool`: Whether or not to require authentication.

func (*TPageHandler) ServeHTTP

func (ph *TPageHandler) ServeHTTP(aWriter http.ResponseWriter, aRequest *http.Request)

`ServeHTTP()` handles the incoming HTTP requests.

type TPostList

type TPostList []TPosting

`TPostList` is a list of postings to be injected into a template/view.

func NewPostList

func NewPostList() *TPostList

NewPostList returns a new (empty) TPostList instance.

func SearchPostings

func SearchPostings(aText string) *TPostList

`SearchPostings()` traverses all postings looking for `aText` in the respective post's text.

The returned `TPostList` can be empty because (a) `aText` could not be compiled into a regular expression, (b) no files to search were found, or (c) no files matched `aText`.

Parameters:

  • `aText`: The text to look for in the postings.

Returns:

  • `*TPostList`: The found list.

func (*TPostList) Add

func (pl *TPostList) Add(aPosting *TPosting) *TPostList

`Add()` appends `aPosting` to the list.

Parameters:

  • `aPosting` contains the actual posting's text.

Returns:

  • `*TPostList`: The updated list.

func (*TPostList) Day

func (pl *TPostList) Day() *TPostList

`Day()` adds all postings of the current day to the list.

Returns:

  • `*TPostList`: A list with the postings of the current day.

func (*TPostList) Delete

func (pl *TPostList) Delete(aPosting *TPosting) (*TPostList, bool)

`Delete()` removes `aPosting` from the list, returning the (possibly modified) list and whether the operation war successful.

Parameters:

  • `aPosting`: The posting to remove from this list.

Returns:

  • `*TPostList`: The updated list.
  • `bool`: Whether `aPosting` was successfully removed.

func (*TPostList) IsSorted

func (pl *TPostList) IsSorted() bool

`IsSorted()` returns whether the list is sorted (in descending order).

Returns:

  • `bool`: `true` if the list is sorted in descending order.

func (*TPostList) Len

func (pl *TPostList) Len() int

`Len()` returns the number of postings stored in this list.

Returns:

`int`: The number of postings in the current list.

func (*TPostList) Month

func (pl *TPostList) Month(aYear int, aMonth time.Month) *TPostList

`Month()` adds all postings of `aMonth` to the list.

Parameters:

  • `aYear`: The year to lookup; if zero the current year is used.
  • `aMonth`: The year's month to lookup; if zero the current month is used.

Returns:

  • `*TPostList`: A list with the postings of the given year and month.

func (*TPostList) Newest

func (pl *TPostList) Newest(aLimit, aOffset int) error

`Newest()` adds the last `aLimit` of postings to the list.

The resulting list is sorted in descending order (newest first) with at most `aLimit` posts.

Parameters:

  • `aLimit`: The number of articles to show.
  • `aOffset`: The start number to use.

Returns:

  • `error`: A possible error during processing of the request.

func (*TPostList) Sort

func (pl *TPostList) Sort() *TPostList

`Sort()` returns the list sorted by posting IDs (i.e. date/time) in descending order.

Returns:

  • `*TPostList`: The current list of postings in descending order.

func (*TPostList) String added in v0.20.0

func (pl *TPostList) String() (rStr string)

`String()` returns a stringified version of the postlist instance.

Note: This is mainly for debugging purposes and has no real life use.

Returns:

  • `string`: The stringified version of the current postlist.

func (*TPostList) Week

func (pl *TPostList) Week(aYear int, aMonth time.Month, aDay int) *TPostList

`Week()` adds all postings of the current week to the list.

Parameters:

  • `aYear` The year to lookup; if zero the current year is used.
  • `aMonth` The year's month to lookup; if zero the current month is used.
  • `aDay` The month's day to lookup; if zero the current day is used.

Returns:

  • `*TPostList`: A list with the postings of the given year, month, and day.

type TPosting

type TPosting struct {
	// contains filtered or unexported fields
}

`TPosting` is a single article/posting..

func NewPosting

func NewPosting(aID uint64, aText string) *TPosting

`NewPosting()` returns a new posting structure with the given article text.

If `aID` is zero, the current time is used to generate a unique ID.

Parameters:

  • `aID`: A uint64 representing the unique identifier of the posting.
  • `aText`: A string containing the initial Markdown text of the article.

Returns:

  • `*TPosting`: A new `TPosting` instance.

func (*TPosting) After

func (p *TPosting) After(aID uint64) bool

`After()` reports whether this posting is younger than the one identified by `aID`.

Parameters:

  • `aID`: The ID of another posting to compare.

Returns:

  • `bool`: Whether the current posting is older than `aID`.

func (*TPosting) Before

func (p *TPosting) Before(aID uint64) bool

`Before()` reports whether this posting is older than the one identified by `aID`.

Parameters:

  • `aID`: The ID of another posting to compare.

Returns:

  • `bool`: Whether the current posting is younger than `aID`.

func (*TPosting) ChangeID added in v0.20.0

func (p *TPosting) ChangeID(aID uint64) error

`ChangeID()` changes the ID of the current posting including the persistence layer.

Note: This method is provided for rare cases when a posting's ID has to be changed.

Parameters:

  • `aID`: is the ID of another posting to compare.

Returns:

  • `error`: A possible error during processing the request.

func (*TPosting) Clear

func (p *TPosting) Clear() *TPosting

`Clear()` resets the text field to its zero value.

This method does NOT remove the file (if any) associated with this posting/article; for that call the `Delete()` method.

Returns:

  • `*TPosting`: The current posting without any text.

func (*TPosting) Date

func (p *TPosting) Date() string

`Date()` returns the posting's date as a formatted string (`yyyy-mm-dd`).

Returns:

  • `string`: The posting's creation date in string format.

func (*TPosting) Delete

func (p *TPosting) Delete() error

`Delete()` removes the posting/article from the persistence layer returning a possible I/O error.

This method does NOT empty the markdown text of the object; for that call the `[Clear]` method.

Returns:

  • `error`: A possible error during processing the request.

func (*TPosting) Equal

func (p *TPosting) Equal(aID uint64) bool

`Equal()` reports whether this posting is of the same time as `aID`.

Parameters:

  • `aID`: The ID of the posting to compare with this one.

Returns:

  • `bool`: `true` if the posting is of the same as `aID`.

func (*TPosting) Exists

func (p *TPosting) Exists() bool

`Exists()` returns whether there is a file with more than zero bytes.

Returns:

  • `bool`: `true` if the posting exists in the persistence layer.

func (*TPosting) ID

func (p *TPosting) ID() uint64

`ID()` returns the article's identifier.

Returns:

  • `uint64`: The posting's unique ID.

func (*TPosting) IDstr added in v0.20.0

func (p *TPosting) IDstr() string

`IDstr()` returns the article's identifier in string format.

The identifier is based on the article's creation time and given in hexadecimal notation.

This method allows the template to validate and use the placeholder `.ID`

Returns:

  • `string`: The article's identifier in string format.

func (*TPosting) LastModified added in v0.17.5

func (p *TPosting) LastModified() string

`LastModified()` returns the last-modified date/time of the posting.

The format of the returned string would be like "Mon, 02 Jan 2006 15:04:05 MST"

Returns:

  • `string`: The article's last modified date.

func (*TPosting) Len

func (p *TPosting) Len() int

`Len()` returns the current length of the posting's Markdown text.

If the markup is not already in memory this method calls `[Load]` to read the text data from the persistence layer.

Returns:

  • `int`: The current length of the posting's text.

func (*TPosting) Load

func (p *TPosting) Load() error

`Load()` reads the Markdown from disk, returning a possible I/O error.

Returns:

  • `error`: A possible error during processing the request.

func (*TPosting) Markdown

func (p *TPosting) Markdown() []byte

`Markdown()` returns the Markdown of this article.

If the markdown is not already in memory this methods calls `[Load]` to read the text data from the persistence layer.

Returns:

  • `[]byte`: The current posting's text.

func (*TPosting) PathFileName

func (p *TPosting) PathFileName() string

`PathFileName()` returns the article's complete path-/filename.

NOTE: The actual definition of the path-/filename depends on the implementation of this interface. In a file-based persistence layer it would be a `/path/directory/filename` string. However, in a database-based persistence layer it would be the `/path/file` of the database file.

Returns:

  • `string`: The current posting's path-/filename.

func (*TPosting) Post

func (p *TPosting) Post() template.HTML

`Post()` returns the article's HTML markup.

This method uses the `Markdown()` method to get the latest version of the article's Markdown text. It then converts this text to HTML using the `MDtoHTML()` function and wraps it with the necessary HTML tags using the `MarkupTags()` function.

The resulting HTML is returned as a `template.HTML` value.

Returns:

  • `template.HTML`: The HTML markup of the current posting's text.

func (*TPosting) Set

func (p *TPosting) Set(aMarkdown []byte) *TPosting

`Set()` assigns the article's Markdown text.

Parameters:

  • `aMarkdown`: The actual Markdown text to assign.

Returns:

  • `*TPosting`: The updated posting.

func (*TPosting) Store

func (p *TPosting) Store() (int, error)

`Store()` writes the article's Markdown to disk returning the number of bytes written and a possible I/O error.

The actual storing is delegated to the persistence layer.

Returns:

  • `int`: The number of bytes written.
  • 'error`: A possible error, or `nil` on success.

func (*TPosting) String

func (p *TPosting) String() string

`String()` returns a stringified version of the posting instance.

Note: This is mainly for debugging purposes and has no real life use.

Returns:

  • `string`: The stringified version of the current posting.

func (*TPosting) Time

func (p *TPosting) Time() time.Time

`Time()` returns the posting's date/time.

Returns:

  • `time.Time`: The current posting's creation time.

type TView

type TView struct {
	// contains filtered or unexported fields
}

`TView` combines a template and its logical name.

func NewView

func NewView(aName string) (*TView, error)

`NewView()` returns a new `TView` with `aName`.

`aName` serves as both the main template's name as well as the view's name; it's given here without the filename extension (i.e. w/o `.gohtml`).

Parameters:

  • `aName`: The name of the template file providing the page's main body.

Returns:

  • `*TView`: A new `TView` instance.
  • `error`: A possible error during processing.

func (*TView) Render

func (v *TView) Render(aWriter http.ResponseWriter, aData *TemplateData) error

`Render()` executes the template using the TView's properties.

`aWriter` is a http.ResponseWriter, or e.g. `os.Stdout` in console apps.

If an error occurs executing the template or writing its output, execution stops, and the method returns without writing anything to the output `aWriter`.

Parameters:

  • `aWriter`: A `http.ResponseWriter` to handle the executed template.
  • `aData`: A list of data to be injected into the template.

Returns:

  • `error`: A possible error during processing.

func (*TView) RenderedPage

func (v *TView) RenderedPage(aData *TemplateData) ([]byte, error)

`RenderedPage()` returns the rendered template/page and a possible Error executing the template.

Parameters: `aData` is a list of data to be injected into the template.

Returns:

  • `error`: A possible error during processing.

type TViewList

type TViewList map[string]*TView

`TViewList` is a list of `TView` instances (to be used as a template pool). It's a map indexed by a name pointing to a view instance.

func NewViewList

func NewViewList() (*TViewList, error)

`NewViewList()` creates a new `TViewList` instance with all available views.

Returns:

  • `*TViewList`: A new `TViewList` instance.
  • `error`: A possible error during processing.

func (*TViewList) Get

func (vl *TViewList) Get(aName string) (*TView, bool)

`Get()` returns the view with `aName`.

If `aName` doesn't exist, the return value is `nil`. The second value (ok) is a `bool` that is `true` if `aName` exists in the list, and `false` if not.

Parameters:

  • `aName`: The name of the `TView` instance to retrieve.

Returns:

  • `*TView`: The `TView` instance.
  • `bool`: `true` if `aName` exists in the list, or `false` otherwise.

func (*TViewList) Len added in v0.20.0

func (vl *TViewList) Len() int

`Len()` returns the number of views in the `TViewList`.

This method implements the `Len()` function from the sort.Interface interface, which is used for sorting TViewList instances.

Returns:

  • `int`: The number of views in the TViewList.

func (*TViewList) Render

func (vl *TViewList) Render(aName string, aWriter http.ResponseWriter, aData *TemplateData) error

`Render()` executes the template identified by `aName`.

If an error occurs executing the template or writing its output, execution stops, and the method returns without writing anything to the output `aWriter`.

Parameters:

  • `aName`: The name of the template/view to use.
  • `aWriter`: A `http.ResponseWriter` to handle the executed template.
  • `aData`: A list of data to be injected into the template.

Returns:

  • `error`: A possible error during processing.

func (*TViewList) RenderedPage

func (vl *TViewList) RenderedPage(aName string, aData *TemplateData) ([]byte, error)

`RenderedPage()` returns the rendered template/page with the key `aName`.

Parameters:

  • `aName`: The name of the template/view to use.
  • `aData`: A list of data to be injected into the template.

Returns:

  • `error`: A possible error during processing.

type TWalkFunc added in v0.20.0

type TWalkFunc func(aID uint64) error

This function type is used by `Walk()`.

Parameters:

  • `aID`: The ID of the posting to handle.

type TemplateData added in v0.11.1

type TemplateData map[string]any

`TemplateData` is a list of values to be injected into a template.

func NewTemplateData added in v0.11.1

func NewTemplateData() *TemplateData

`NewTemplateData()` returns a new (empty) `TemplateData` instance.

Returns:

  • `*TemplateData`: The new data list.

func (TemplateData) Get added in v0.17.2

func (dl TemplateData) Get(aKey string) (rValue any, rOK bool)

`Get()` returns the value associated with `aKey` and `true`.

If `aKey` is not present in the list then the `bool` return value will be `false`.

Parameters:

  • `aKey`: The value's identifier (as used as placeholder in the template).

Returns:

  • `any`: The value associated with `aKey`.
  • `bool`: Indicator whether `aKey` was found.

func (*TemplateData) Set added in v0.11.1

func (dl *TemplateData) Set(aKey string, aValue any) *TemplateData

`Set()` inserts `aValue` identified by `aKey` to the list.

If there's already a list entry with `aKey` its current value gets replaced by `aValue`.

Parameters:

  • `aKey`: The value's identifier (as used as placeholder in the template).
  • `aValue`: The data entry's value.

Returns:

  • `*TemplateData`: The updated data list.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL