blog

package module
v0.0.12 Latest Latest
Warning

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

Go to latest
Published: May 22, 2019 License: GPL-3.0 Imports: 25 Imported by: 0

README

Blog

GoDoc view examples

Purpose

The purpose of this package 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). 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 in a user/password list used for authentication when accessing certain URLs in this system. Articles/postings can be added, edited (e.g. for correcting typos etc.), or removed altogether. If the styles coming with the package you can, of course, change them in your own installation.

The articles/postings 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. So I intentionally didn't bother with comments or discussion threading.

Installation

You can use Go to install this package for you:

go get -u github.com/mwat56/go-blog

Usage

After downloading this package you go to its directory and compile

go build _demo/blog/blog.go

which should produce an executable binary. On my system it looks like this:

$ ls -l
total 21296
-rwxrwxr-x 1 matthias matthias 11140070 Mai 19 19:47 blog
-rw-rw-r-- 1 matthias matthias     1001 Mai 18 19:55 blog.ini
drwxrwxr-x 2 matthias matthias     4096 Mai 11 20:11 certs
-rw-rw-r-- 1 matthias matthias     6583 Mai 10 09:39 cmdline.go
-rw-rw-r-- 1 matthias matthias     9330 Mai 18 21:40 config.go
-rw-rw-r-- 1 matthias matthias     1092 Mai 18 19:55 config_test.go
drwxrwxr-x 2 matthias matthias     4096 Mai 19 01:35 css
drwxrwxr-x 3 matthias matthias     4096 Mai 15 19:29 _demo
-rw-rw-r-- 1 matthias matthias      823 Mai  4 17:57 doc.go
-rw------- 1 matthias matthias      587 Mai 19 12:43 go.mod
-rw------- 1 matthias matthias     3837 Mai 19 12:43 go.sum
-rw-rw-r-- 1 matthias matthias     4999 Mai 19 19:55 hashfile.db
drwxrwxr-x 2 matthias matthias     4096 Mai 18 19:59 img
-rw-rw-r-- 1 matthias matthias    32474 Mai  2 13:59 LICENSE
-rw-rw-r-- 1 matthias matthias    20726 Mai 19 16:46 pagehandler.go
-rw-r--r-- 1 matthias matthias      619 Mai  8 10:14 pagehandler_test.go
-rw-rw-r-- 1 matthias matthias     8981 Mai 19 13:30 posting.go
drwxrwxr-x 8 matthias matthias     4096 Mai 19 18:31 postings
-rw-r--r-- 1 matthias matthias    14359 Mai  8 10:12 posting_test.go
-rw-r--r-- 1 matthias matthias     8240 Mai 11 10:15 postlist.go
-rw-r--r-- 1 matthias matthias     7279 Mai 14 09:28 postlist_test.go
-rw-rw-r-- 1 matthias matthias      141 Mai  6 17:30 pwaccess.db
-rw-rw-r-- 1 matthias matthias    20152 Mai 19 20:03 README.md
-rw-rw-r-- 1 matthias matthias    10436 Mai 19 16:31 regex.go
-rw-r--r-- 1 matthias matthias     8190 Mai 19 15:53 regex_test.go
drwxrwxr-x 2 matthias matthias     4096 Mai 18 18:44 static
-rw-rw-r-- 1 matthias matthias     3240 Mai 19 18:13 tags.go
-rw-rw-r-- 1 matthias matthias     1367 Mai 19 01:35 template_vars.md
-rw-rw-r-- 1 matthias matthias     2853 Mai 19 19:33 TODO.md
drwxrwxr-x 3 matthias matthias     4096 Mai 19 13:34 views
-rw-rw-r-- 1 matthias matthias     9656 Mai 10 12:11 views.go
-rw-r--r-- 1 matthias matthias     6009 Mai  8 10:09 views_test.go
$ _

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

$ strip blog
$ ls -l blog
-rwxrwxr-x 1 matthias matthias 8138752 Mai 19 20:05 blog
$ _

As you can see the binary lost about 3MB of its weight.

Let's start with the command line:

$ ./blog -h

Usage: ./blog [OPTIONS]

-blogname string
    Name of this Blog (shown on every page)
    (default "Meine Güte, was für'n Blah!")
-certKey string
    <fileName> the name of the TLS certificate key
    (default "/home/matthias/devel/Go/src/github.com/mwat56/go-blog/certs/server.key")
-certPem string
    <fileName> the name of the TLS certificate PEM
    (default "/home/matthias/devel/Go/src/github.com/mwat56/go-blog/certs/server.pem")
-datadir string
    <dirName> the directory with CSS, IMG, JS, POSTINGS, STATIC, VIEWS sub-directories
    (default "/home/matthias/devel/Go/src/github.com/mwat56/go-blog")
-hashfile string
    <fileName> (optional) the name of a file storing #hashtags and @mentions
    (default "/home/matthias/devel/Go/src/github.com/mwat56/go-blog/hashfile.db")
-ini string
    <fileName> the path/filename of the INI file
-lang string
    (optional) the default language to use  (default "de")
-listen string
    the host's IP to listen at  (default "127.0.0.1")
-log string
    (optional) name of the logfile to write to
    (default "/dev/stdout")
-maxfilesize string
    max. accepted size of uploaded files (default "10MB")
-pa
    (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
    <portNumber> the IP port to listen to  (default 8181)
-realm string
    (optional) <hostName> name of host/domain to secure by BasicAuth
    (default "This Host")
-theme string
    <name> the display theme to use ('light' or 'dark')
    (default "light")
-ua string
    <userName> (optional) user add: add a username to the password file
-uc string
    <userName> (optional) user check: check a username in the password file
-ud string
    <userName> (optional) user delete: remove a username from the password file
-uf string
    <fileName> (optional) user passwords file storing user/passwords for BasicAuth
    (default "/home/matthias/devel/Go/src/github.com/mwat56/go-blog/pwaccess.db")
-ul
    (optional) user list: show all users in the password file
-uu string
    <userName> (optional) user update: update a username in the password file

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

$ _

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

$ cat blog.ini
# Default configuration file

[Default]

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

    # path-/filename of TLS certificate's private key to enable TLS/HTTPS
    # (if empty standard HTTP is used)
    # NOTE: a relative path/name will be combined with `dadadir` (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 `dadadir` (below).
    certPem = ./certs/server.pem

    # The directory root for CSS, FONTS, IMG, JS, POSTINGS, STATIC,
    # and VIEWS sub-directories.
    # NOTE: this should be an absolute path name.
    datadir = ./

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

    # The default language to use:
    lang = de

    # The host's IP to listen at:
    listen = 127.0.0.1

    # The IP port to listen to:
    port = 8181

    # Name of the optional logfile to write to.
    # NOTE: a relative path/name will be combined with `dadadir` (above).
    logfile = /dev/stdout

    # Accepted size of uploaded files
    maxfilesize = 10MB

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

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

    # Web/display theme: `dark` or `light':
    theme = light

# _EoF_
$ _

The program, when started, will first look for the INI file in the current directory and only then parse the commandline arguments; in other words: commandline arguments take precedence over INI entries. The meaning of the different configuration options should be self-explanatory. But let's look at some of the commandline options more closely.

Commandline postings

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

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

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

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

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

User/password file & handling

Only usable from the commandline as well 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.

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

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

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

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

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

$ ./blog -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 a user the -ud will do the trick:

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

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

$ ./blog -ul
matthias

$ _

Since we deleted the testuser1 before only one entry remains.

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

$ ./blog -ua testuser2

 password:
repeat pw:
    added 'testuser2' to list

$ ./blog -uu testuser2

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

$ ./blog -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).

Authentication

But why, you may ask, would we need username/password files anyway? Well, you remember me mentioning that you can add, edit and delete articles/postings? You wouldn't want anyone on the net beeing 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 funtionality 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 both files are not interchangeable because the actual encryption algorithm used by both are different.

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.

URLs

The system uses a number of slightly different URL groups.

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.

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

  • / defines the logical root of the presentation; it's effectivily the same as /n/ (see below).
  • /faq, /imprint, /licence, and /privacy serve static files which have to be filled with content according to your personal and legal needs.
  • /ht/tagname allows you to 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 articles will be shown.
  • /m/ shows the articles of the current month. You can, however, specify the month you're interested in by adding a data part defining the month you want to see (/m/yyyy-mm), like /m/2019-04 to see the acticles/postings from April 2019.
  • /mt/mentionedname allows you to search for @mentionedname (but you'll input it without the at sign @ because that has a special meaning in an URL). Provided the given @mentionedname was actually used in one or more of your articles a list of the respective articles will be shown.
  • /n/ gives you the newest 20 articles/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 you want to see the articles in slices of, say, 10 per page (instead of the default 20/page) you could use the URL /n/10,10 and to see the secong slice user /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 you.
  • /p/1234567890abcdef shows you 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.
  • /s/searchterm can be used to search for articles containing a certain word. All existing articles/postings will be searched for the given searchterm.
  • /w/ shows the articles of the current week. You can, however, specify the week you're interested in by adding a data part defining the week you want to see (/w/yyyy-mm-dd), like /w/2019-04-13 to see the acticles/postings from the week in April 2019 containing the 13th.

And third there's a group of URLs your users won't usually see or use, because by design they are reserved for you. These URLs are protected by an authentication mechanism called BasicAuth; this is where the username/password files 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 setup 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/postings, or add new ones (which you might not like).

  • /a add a new posting. A simple Web form will allow you to input whatever's on your mind.
  • /d/234567890abcdef1 lets you 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 (/p/234567890abcdef1) by selectiing the respective [*] link on the index page and then just replace in the URL the p by a d.
  • /e/34567890abcdef12 lets you edit the article/posting's text identified by 34567890abcdef12. The procedure is the same: go to /p/34567890abcdef12 and replace the p by an e.
  • /r/4567890abcdef123 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.
  • /si (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.
  • /share/https://some.host.domain/somepage lets you share another page URL. Whatever you write after the initial /share/ is considered a remote URL, and a new article will be created and shown to edit.
  • /ss (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.

Files

Right from the start I mentioned that I wanted to avoid external depenencies – 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 arcticles/postings, 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/postings. 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,
  • img/ for image files,
  • postings/ directory root for the articles/postings,
  • 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 anymore.

As mentioned before, it's always advisable to use absolute pathnames, not relative one. The latter are converted into absolute ones 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 ./blog -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 form the command-line or by using the system's /si URL.

Postings

The datadir//postings/ 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 form 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 seem fit. I will, however, not provide any support for you changing the default template structure.

An 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/postings 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.

Licence

Copyright © 2019 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.

Documentation

Overview

Package blog implements a simple blog-server.

Copyright © 2019  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.

Index

Constants

This section is empty.

Variables

View Source
var (
	// AppArguments is the list for the cmdline arguments and INI values.
	AppArguments tAguments
)

Functions

func AddConsolePost

func AddConsolePost() (int64, 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) (int64, error)

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

func AddUser

func AddUser(aUser, aFilename string)

AddUser 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` name of the password file to use.

func CheckUser

func CheckUser(aUser, aFilename string)

CheckUser 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` name of the password file to use.

func DeleteUser

func DeleteUser(aUser, aFilename string)

DeleteUser 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` name of the password file to use.

func ListUser

func ListUser(aFilename string)

ListUser 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` name of the password file to use.

func MDtoHTML

func MDtoHTML(aMarkdown []byte) []byte

MDtoHTML converts the `aMarkdown` data returning HTML data.

`aMarkdown` the raw Markdown text to convert.

func NewID

func NewID() string

NewID returns a new article ID. It is based on the current date/time and given in hexadecimal notation. It's assumend that no more than one ID per nanosecond is needed.

func PostingBaseDirectory

func PostingBaseDirectory() string

PostingBaseDirectory returns the base directory used for storing articles/postings.

func RemoveWhiteSpace

func RemoveWhiteSpace(aPage []byte) []byte

RemoveWhiteSpace removes HTML comments and unneccessary whitespace.

This function removes all unneeded/redundant whitespace and HTML comments from the given <tt>aPage</tt>. This can reduce significantly the amount of data to send to the remote user agent thus saving bandwidth.

func SetPostingBaseDirectory

func SetPostingBaseDirectory(aBaseDir string) (rErr error)

SetPostingBaseDirectory set the base directory used for storing articles/postings.

`aBaseDir` is the base directory to use for storing articles/postings.

func ShowHelp

func ShowHelp()

ShowHelp lists the commandline options to `Stderr`.

func URLparts

func URLparts(aURL string) (rDir, rPath string)

URLparts returns two parts: `rDir` holds the base-directory of `aURL`, `rPath` holds the remaining part of `aURL`.

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

func UpdateUser

func UpdateUser(aUser, aFilename string)

UpdateUser 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` name of the password file to use.

Types

type TDataList

type TDataList map[string]interface{}

TDataList is a list of values to be injected into a template.

func NewDataList

func NewDataList() *TDataList

NewDataList returns a new (empty) TDataList instance.

func (*TDataList) Set

func (dl *TDataList) Set(aKey string, aValue interface{}) *TDataList

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`.

`aKey` is the values's identifier (as used as placeholder in the template).

`aValue` contains the data entry's value.

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 returns a new `TPageHandler` instance.

func (*TPageHandler) Address

func (ph *TPageHandler) Address() string

Address returns the configured `IP:Port` address to use for listening.

func (*TPageHandler) GetErrorPage

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

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

func (*TPageHandler) Len

func (ph *TPageHandler) Len() int

Len returns the length of the internal view list.

func (*TPageHandler) NeedAuthentication

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

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

`aURL` is the URL to check.

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.

`aBaseDir` is a directory storing the postings.

func SearchPostings

func SearchPostings(aText string) *TPostList

SearchPostings traverses the sub-directories of `aBaseDir` looking for `aText` in all posting files.

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`.

`aText` is the text to look for in the postings.

func (*TPostList) Add

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

Add appends `aPosting` to the list.

`aPosting` contains the actual posting's text.

func (*TPostList) Article

func (pl *TPostList) Article(aID string) *TPostList

Article adds the posting identified by `aID` to the list.

func (*TPostList) Day

func (pl *TPostList) Day() *TPostList

Day adds all postings of the current day to the list.

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 opration war successful.

func (*TPostList) Index

func (pl *TPostList) Index(aPosting *TPosting) int

Index returns the 0-based list index of `aPosting`. In case `aPosting` was not found in list the return value will be `-1`.

func (*TPostList) IsSorted

func (pl *TPostList) IsSorted() bool

IsSorted returns `true` if the list is sorted, or `false` otherwise.

func (*TPostList) Len

func (pl *TPostList) Len() int

Len returns the number of postings stored in this list.

func (*TPostList) Month

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

Month adds all postings of a month to the list.

`aYear` the year to lookup; if `0` (zero) the current year is used.

`aMonth` the year's month to lookup; if `0` (zero) the current month is used.

func (*TPostList) Newest

func (pl *TPostList) Newest(aNumber, aStart int) error

Newest adds the last `aNumber` postings to the list.

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

`aNumber` is the number of articles to show.

`aStart` is the start number to use.

func (*TPostList) Sort

func (pl *TPostList) Sort() *TPostList

Sort returns the list sorted by posting IDs in descending order.

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.

`aYear` the year to lookup; if `0` (zero) the current year is used.

`aMonth` the year's month to lookup; if `0` (zero) the current month is used.

`aDay` the month's day to lookup; if `0` (zero) the current day is used.

type TPosting

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

TPosting is a single /article/posting to be injected into a template.

func NewPosting

func NewPosting() *TPosting

NewPosting returns a new posting structure with an empty article text.

func (*TPosting) After

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

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

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

func (*TPosting) Before

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

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

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

func (*TPosting) Clear

func (p *TPosting) Clear() *TPosting

Clear resets the internal fields to their respective zero values.

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

func (*TPosting) Date

func (p *TPosting) Date() string

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

func (*TPosting) Delete

func (p *TPosting) Delete() error

Delete removes the posting/article from the filesystem returning a possible I/O error.

This method does NOT empty the internal fields of the object; for that call the `Clear()` method.

func (*TPosting) Equal

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

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

func (*TPosting) Exists added in v0.0.10

func (p *TPosting) Exists() bool

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

func (*TPosting) ID

func (p *TPosting) ID() string

ID returns the article's identifier.

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

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

func (*TPosting) IsFile

func (p *TPosting) IsFile() bool

IsFile returns whether the posting is stored in the filesystem.

func (*TPosting) Len

func (p *TPosting) Len() int

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

func (*TPosting) Load

func (p *TPosting) Load() error

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

func (*TPosting) Markdown

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

Markdown returns the Markdown of this article.

If the markup is not already in memory this methods tries to read the required data from the filesystem.

In case of I/O errors while accessing the file an empty byte slice is returned.

func (*TPosting) PathFileName

func (p *TPosting) PathFileName() string

PathFileName returns the article's path-/filename.

func (*TPosting) Post

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

Post returns the article's HTML markup.

func (*TPosting) Set

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

Set assigns the article's Markdown text.

`aMarkdown` is the actual Markdown text of the article to assign.

func (*TPosting) Store

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

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

The file is created on disk with mode `0644` (`-rw-r--r--`).

func (*TPosting) Time

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

Time returns the posting's date/time.

type TView

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

TView combines a template and its logical name.

func NewView

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

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

`aBaseDir` is the path to the directory storing the template files.

`aName` is the name of the template file providing the page's main body without the filename extension (i.e. w/o ".gohtml"). `aName` serves as both the main template's name as well as the view's name.

func (*TView) Render

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

Render executes the template using the TView's properties.

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

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

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

func (*TView) RenderedPage

func (v *TView) RenderedPage(aData *TDataList) (rBytes []byte, rErr error)

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

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

type TViewList

type TViewList tViewList

TViewList is a list of `TView` instances (to be used as a template pool).

func NewViewList

func NewViewList() *TViewList

NewViewList returns a new (empty) `TViewList` instance.

func (*TViewList) Add

func (vl *TViewList) Add(aView *TView) *TViewList

Add appends `aView` to the list.

`aView` is the view to add to this list.

The view's name (as specified in the `NewView()` function call) is used as the view's key in this list.

func (*TViewList) Get

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

Get returns the view with `aName`.

`aName` is the name (key) of the `TView` object to retrieve.

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.

func (*TViewList) Render

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

Render executes the template with the key `aName`.

`aName` is the name of the template/view to use.

`aWriter` is a `http.ResponseWriter` to handle the executed template.

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

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

func (*TViewList) RenderedPage

func (vl *TViewList) RenderedPage(aName string, aData *TDataList) (rBytes []byte, rErr error)

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

`aName` is the name of the template/view to use.

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

Directories

Path Synopsis
_demo
blog command

Jump to

Keyboard shortcuts

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