Advertisement
  1. Code
  2. CMS

Erstellen eines CMS: goPress

Scroll to top
Read Time: 39 min
This post is part of a series called Building a CMS.
Building a CMS: Structure and Styling

German (Deutsch) translation by Nikol Angelowa (you can also view the original English article)

Go ist eine netzwerkorientierte Programmiersprache, die von Google entwickelt wurde und das Schreiben netzwerkbezogener Programme vereinfacht. Mit den vielen großartigen Bibliotheken, aus denen Sie auswählen können, ist es ein Kinderspiel, eine Webanwendung zum Laufen zu bringen.

In diesem Tutorial werde ich ein Content Management System (CMS) mit Go und einigen Hilfsbibliotheken erstellen. Dieses CMS verwendet die Site-Datenstruktur, wie im ersten Lernprogramm Erstellen eines CMS: Struktur und Stil beschrieben.

Entwicklungs-Setup mit Go

Der einfachste Weg, die Programmiersprache go auf einem Mac zu installieren, ist Homebrew. Wenn Sie Homebrew noch nicht installiert haben, zeigt Ihnen das Tutorial Homebrew Demystified: OS Xs Ultimate Package Manager, wie. Befolgen Sie für andere Plattformen einfach die Anweisungen auf der Go-Download-Seite.

Geben Sie in einem Terminal Folgendes ein:

1
brew install go

Erstellen Sie in Ihrem Home-Verzeichnis das Verzeichnis go. In der Go-Sprache werden alle heruntergeladenen Bibliotheken dort gespeichert. Fügen Sie Ihrer .bashrc-Datei und / oder .zshrc-Datei diese Zeile hinzu:

1
export GOPATH="/Users/<your user name>/go"

Wenn Sie fish verwenden, fügen Sie dies Ihrer config.fish-Datei hinzu:

1
set -xg GOPATH "/Users/<your user name>/go"

Als nächstes müssen Sie die Bibliotheken installieren. Die goWeb-Bibliothek stellt das Webserver-Framework bereit, die amber-Bibliothek gibt den Jade-äquivalenten HTML-Präprozessor und BlackFriday übersetzt Markdown in den richtigen HTML-Code. Außerdem verwende ich die Handlebars-Bibliothek für Vorlagen. Um diese Bibliotheken zu installieren, müssen Sie Folgendes in das Projektverzeichnis eingeben:

1
go get github.com/hoisie/web
2
go get github.com/eknkc/amber
3
go get github.com/russross/blackfriday
4
go get github.com/murz/go-handlebars/handlebars

Nachdem sich Go und die erforderlichen Bibliotheken auf Ihrem System befinden, besteht der nächste Schritt darin, mit dem Codieren zu beginnen. Die goPress-Bibliothek enthält fünf Dateien, während das Hauptprogramm eine Datei zum Aufrufen der Bibliotheksfunktionen ist.

goPress.go-Bibliotheksdatei

Ich wollte, dass der Server so schnell wie möglich ist. Um dies zu erreichen, befindet sich alles, was wiederverwendbar ist, im Speicher. Daher behält eine globale Variable alle Assets und die Homepage bei. Sie könnten den Server mit allen im Speicher befindlichen Assets entwerfen, dies würde jedoch dazu führen, dass der Speicher auf großen Websites aufgebläht wird. Da die Homepage die am häufigsten geladene Seite sein sollte, wird sie auch gespeichert.

Erstellen Sie mithilfe der im letzten Lernprogramm eingerichteten Dateistruktur ein neues Verzeichnis im src-Verzeichnis mit dem Namen goPress. Hier werden alle goPress-Bibliotheksdateien abgelegt. Die erste Datei ist goPress.go. Erstellen Sie die Datei und geben Sie den folgenden Code ein.

1
package goPress
2
3
//

4
// Package:          goPress

5
//

6
// Description:		This package is for the goPress CMS

7
//     				written in the go programming

8
//					language made by Google. This package

9
// 					defines everything for a full

10
//					CMS.

11
//

12
13
//

14
// Import the libraries we use for this program.

15
//

16
import (
17
	"encoding/json"
18
	"github.com/hoisie/web"
19
	"io/ioutil"
20
	"log"
21
	"os"
22
	"strings"
23
)
24
25
//

26
// Define a structure to contain all the information

27
// important to the CMS. Capticalized

28
// variables within the structure is imported 

29
// and exported.

30
//

31
type goPressData struct {
32
	CurrentLayout  string
33
	CurrentStyling string
34
	ServerAddress  string
35
	SiteTitle      string
36
	Sitebase       string
37
	TemplatBase    string
38
	CapatchaWidth  int
39
	CapatchaHeight int
40
	Cache          bool
41
	MainBase       string
42
	content        map[string]string
43
	layoutBase     string
44
	mainpg         string
45
	postbase       string
46
	scripts        string
47
	stylesheet     string
48
	stylingBase    string
49
	template       string
50
}
51
52
var SiteData = new(goPressData)
53
var ServerParamFile string = "server.json"

Die package-Anweisung oben teilt dem Compiler mit, dass diese Datei Teil einer Paketbibliothek ist, und gibt den Namen der Bibliothek an. Jede Datei in diesem Verzeichnis muss diese oben haben, um Teil der Datei zu sein.

Als Nächstes importieren Sie alle Bibliotheken, auf die in dieser Datei verwiesen wird. Wenn Sie eine Bibliothek auflisten und nicht verwenden, beschwert sich der Compiler. Dies hilft, Ihren Code sauber und ordentlich zu halten.

Nach dem Laden der Bibliotheken definiere ich die verschiedenen Datenstrukturen und globalen Variablen, die das CMS verwenden wird. Diese Globals sind bibliotheksglobal. Außerhalb der Bibliothek können Variablen, die mit einem Großbuchstaben beginnen, referenziert werden, wenn der Bibliotheksname dies festlegt.

Fügen Sie als Nächstes diese Funktion derselben Datei hinzu:

1
//

2
// Function:		GetGlobals

3
//

4
// Description:		This function is used to create the

5
// 					global variables initialize the

6
//					global variables.

7
//

8
// Inputs:

9
//

10
func GetGlobals() {
11
	//

12
	// Load the Server Parameters from a file.

13
	//

14
	LoadServerParameters()
15
16
	//

17
	// Setup the basic paths to everything.

18
	//

19
	SiteData.layoutBase = SiteData.TemplatBase + "layouts/"
20
	SiteData.stylingBase = SiteData.TemplatBase + "styling/"
21
	SiteData.postbase = SiteData.Sitebase + "posts/"
22
23
	//

24
	// Create the content array that will hold the site 

25
	// fragments. Set the title now.

26
	//

27
	SiteData.content = make(map[string]string)
28
	SiteData.content["title"] = SiteData.SiteTitle
29
30
	//

31
	// Log that the data is being loaded.

32
	//

33
	log.Println("Loading data for site: " + SiteData.SiteTitle)
34
35
	//

36
	// Get all the basic information that is generic and 

37
	// in the styles and layout directories.

38
	// These will then be over written if a new default 

39
	// in the site area is found. This gives

40
	// the flexibility to load defaults from a directory 

41
	// without having to make sure that all

42
	// the necessary ones are loaded.

43
	//

44
45
	//

46
	// Get the 404 page contents

47
	//

48
	SiteData.content["404"] = GetPageContents(SiteData.stylingBase + SiteData.CurrentStyling + "/404")
49
50
	//

51
	// Get the sidebar contents

52
	//

53
	SiteData.content["sidebar"] = GetPageContents(SiteData.stylingBase + SiteData.CurrentStyling + "/sidebar")
54
55
	//

56
	// Get the footer contents

57
	//

58
	SiteData.content["footer"] = GetPageContents(SiteData.stylingBase + SiteData.CurrentStyling + "/footer")
59
60
	//

61
	// Get the template contents

62
	//

63
	SiteData.template = GetPageContents(SiteData.layoutBase + SiteData.CurrentLayout + "/template")
64
65
	//

66
	// Get the header contents

67
	//

68
	SiteData.content["header"] = GetPageContents(SiteData.stylingBase + SiteData.CurrentStyling + "/header")
69
70
	//

71
	// Get the main page contents

72
	//

73
	SiteData.mainpg = GetPageContents(SiteData.Sitebase + "pages/" + "main")
74
75
	//

76
	// The following will load page parts from the 

77
	// "parts" directory for the site. These might

78
	// overload those already defined or add new stuff 

79
	// that the users site templates

80
	// will need.

81
	//

82
	partsdir := SiteData.Sitebase + "parts/"
83
84
	//

85
	// Read the directory.

86
	//

87
	fileList, err := ioutil.ReadDir(partsdir)
88
	if err != nil {
89
		//

90
		// Error reading the directory.

91
		//

92
		log.Printf("Error reading directory: %s\n", partsdir)
93
	} else {
94
		//

95
		// Get the number of items in the directory list.

96
		//

97
		count := len(fileList)
98
99
		//

100
		// Loop through each directory element.

101
		//

102
		for i := 0; i < count; i++ {
103
			if !fileList[i].IsDir() {
104
				//

105
				// It is a file. Read it and add to the 

106
				// scripts variable.

107
				//

108
				filename := fileList[i].Name()
109
				parts := strings.Split(filename, ".")
110
				if filename != ".DS_Store" {
111
					SiteData.content[parts[0]] = LoadFile(partsdir + filename)
112
				}
113
			}
114
		}
115
	}
116
117
	//

118
	// Clear out the global variables not set.

119
	//

120
	SiteData.scripts = ""
121
	SiteData.stylesheet = ""
122
}

Die GetGlobals-Funktion lädt alle global gespeicherten Informationen für die Site. Eine auf dem Dateinamen basierende Hash-Map (ohne Erweiterung) speichert die Daten aus der Serverdatei, dem Layoutverzeichnis und dem Stilverzeichnis. Dann wird alles im Verzeichnis site/parts in dieselbe Struktur gestellt. Auf diese Weise muss der Benutzer keine Datei im Verzeichnis site/parts in das Verzeichnis site/parts einfügen, wenn die Site nur die im Thema angegebenen Standardeinstellungen verwenden möchte.

Fügen Sie in derselben Datei die folgenden Funktionen hinzu:

1
//

2
// Function:		SaveServerParameters

3
//

4
// Description:		This function is for saving the 

5
// 					authorization secret for DropBox.

6
//

7
// Inputs:

8
//

9
func SaveServerParameters() {
10
	if wfile, err := os.Create(ServerParamFile); err == nil {
11
		enc := json.NewEncoder(wfile)
12
		enc.Encode(&SiteData)
13
		wfile.Close()
14
	} else {
15
		log.Println("Writing Server file denied.")
16
	}
17
}
18
19
//

20
// Function:		LoadServerParameters

21
//

22
// Description:		This function is used to load the 

23
//					parameters for this server.

24
//

25
// Inputs:

26
//

27
func LoadServerParameters() {
28
	if wfile, err := os.Open(ServerParamFile); err == nil {
29
		enc := json.NewDecoder(wfile)
30
		enc.Decode(&SiteData)
31
		wfile.Close()
32
		log.Println("Read the " + ServerParamFile + " server parameter file. Site Title is: " + SiteData.SiteTitle)
33
	} else {
34
		log.Println("No Server File found.")
35
	}
36
}

Dies sind die Hilfsfunktionen SaveServerParameters() und LoadServerParameters(). Diese Funktionen speichern und laden die verschiedenen Servereinstellungen in die Datei server.json.

Die nächsten Funktionen dienen zum Erstellen von Routen und unseren Standardrouten. Fügen Sie diese Funktionen derselben Datei hinzu:

1
//

2
// Function:		DefaultRoutes

3
//

4
// Description:		This function sets the default 

5
//					routes for a CMS.

6
//

7
// Inputs:

8
//

9
func DefaultRoutes() {
10
	SetGetRoute("/", Mainpage)
11
	SetGetRoute("/sitemap.xml", SiteMap)
12
	SetGetRoute("/stylesheets.css", GetStylesheets)
13
	SetGetRoute("/scripts.js", GetScripts)
14
	SetGetRoute("/theme/images/(.*)", LoadThemeImage)
15
	SetGetRoute("/(favicon.ico)", ImagesLoad)
16
	SetGetRoute("/images/(.*)", ImagesLoad)
17
	SetGetRoute("/posts/([a-zA-Z0-9]*)/([a-zA-Z0-9]*)", PostIndex)
18
	SetGetRoute("/posts/([a-zA-Z0-9]*)/([a-zA-Z0-9]*)/(.*)", PostPages)
19
	SetGetRoute("/(.*)", TopPages)
20
}
21
22
//

23
// Function:		SetGetRoute

24
//

25
// Description:		This function gives an easy access 

26
//					to the web variable setup in this 

27
//					library.

28
//

29
// Inputs:

30
// 		route 		Route to setup

31
//		handler		Function to run that route.

32
//

33
func SetGetRoute(route string, handler interface{}) {
34
	web.Get(route, handler)
35
}
36
37
//

38
// Function:		StartServer

39
//

40
// Description:		This function is for starting the web 

41
//					server using the SiteData 

42
//					configuration.

43
//

44
// Inputs:

45
//

46
func StartServer(serverAddress string) {
47
	web.Run(serverAddress)
48
}

Die Funktion DefaultRoutes() erstellt die Standardrouten für unser CMS. Die Funktionen für diese Routen befinden sich in den anderen Bibliotheksdateien. Die SetGetRoute() erstellt jede Route. Dies ist einfach ein Wrapper über die goWeb-Bibliotheksfunktion, der einen regulären Ausdruck verwendet, um das Format der Route zu definieren, und eine Funktion, die ausgeführt wird, wenn dieser Ausdruck wahr ist. Wenn Sie jemals das Sinatra-Framework für Ruby oder das Express-Framework für Node.js verwendet haben, sind Sie mit diesem Setup vertraut.

Die Erstellungsreihenfolge für die Routen ist wichtig. Wenn die erste Route einen regulären Ausdruck enthält, der mit allem übereinstimmt, ist der Rest der Routen nicht verfügbar. Die erste Route würde sie alle fangen. Daher habe ich zuerst die spezifischsten Routen und zuletzt die allgemeineren Routen definiert.

Die Funktion StartServer() startet den Webserver. Es ruft die goWeb-Funktion Run() auf, die die Adresse für den Server übernimmt.

Im gesamten Code nutze ich die Funktion log.PrintLn(). Dadurch wird die mit einem Datums- und Zeitstempel versehene Nachricht an die Konsole gedruckt. Dies eignet sich hervorragend zum Debuggen, wird aber auch für die Verkehrsanalyse verwendet.

PagesPosts.go-Bibliotheksdatei

Erstellen Sie als Nächstes die Datei PagesPosts.go im selben Verzeichnis. Diese Datei enthält den gesamten Code für die Arbeit mit Seiten und Beitragstypen. Eine Seite ist einfach eine Webseite. Ein Beitrag ist alles, was im Laufe der Zeit erstellt wurde: Nachrichtenbeiträge, Blogbeiträge, Tutorials usw. Fügen Sie in dieser Datei den folgenden Code hinzu:

1
package goPress
2
3
import (
4
	"bytes"
5
	"encoding/json"
6
	"github.com/eknkc/amber"
7
	"github.com/hoisie/web"
8
	"github.com/murz/go-handlebars/handlebars"
9
	"github.com/russross/blackfriday"
10
	"io/ioutil"
11
	"log"
12
	"os"
13
	"strings"
14
	"time"
15
)

Wie in der Datei goPress.go beginnt sie mit der package-Deklaration und der Liste der zu importierenden Bibliotheken. Diese Datei verwendet jede Bibliothek, die wir für go heruntergeladen haben.

1
//

2
// Function: 		Mainpage

3
//

4
// Description: 	This function is used to generate 

5
// 					and display the main page for the

6
// 					web site. This function will guide 

7
// 					the user to setup the DropBox

8
// 					account if this is the first time 

9
// 					being ran or the dropbox 

10
// 					authorization secret gets zeroed.

11
//

12
// Inputs:

13
//					ctx 	Contents from the request

14
//

15
func Mainpage(ctx *web.Context) string {
16
	//

17
	// Render the main page.

18
	//

19
	page := RenderPageContents(ctx, SiteData.mainpg, SiteData.Sitebase+"pages/main")
20
21
	return page
22
}

Die Funktion Mainpage() zeigt die Startseite der Site an. Es ist einfach ein Wrapper für die Funktion RenderPageContents(), die die zu rendernde Hauptindexseite angibt. RenderPageContents() erledigt die ganze eigentliche Arbeit.

1
//

2
// Function: 		SiteMap

3
//

4
// Description: 	This function is to give a site map 

5
//					to requesters.

6
//

7
// Inputs:

8
//					ctx 	Contents from the request

9
//

10
func SiteMap(ctx *web.Context) string {
11
	var contents string
12
13
	wfile, err := os.Open(SiteData.Sitebase + "sitemap.xml")
14
	if err == nil {
15
		bcontents, _ := ioutil.ReadAll(wfile)
16
		contents = string(bcontents)
17
		wfile.Close()
18
	}
19
	return contents
20
}

Die SiteMap()-Funktion gibt die Sitemap an den Anforderer weiter. Die Informationen werden aus der Datei sitemap.xml oben im Site-Verzeichnis abgerufen.

1
//

2
// Function: 		PostPages

3
//

4
// Description: 	This function generates the needed 

5
//					post page.

6
//

7
// Inputs:

8
//                    ctx        What the browser sends

9
//                    posttype   The type of post

10
//                    postname   The name of the post 

11
//									type instance

12
//                    val        The name of the post 

13
//									page to display

14
//

15
func PostPages(ctx *web.Context, posttype string, postname string, val string) string {
16
	//

17
	// Get the page contents and process it.

18
	//

19
	pgloc := SiteData.postbase + posttype + "/" + postname + "/" + val
20
	return RenderPageContents(ctx, GetPageContents(pgloc), pgloc)
21
}

Die Funktion PostPages() zeigt den richtigen angeforderten Beitrag an. Dies führt wiederum nur den Aufruf der Funktion RenderPageContents() ein, die die gesamte Hauptarbeit erledigt.

1
//

2
// Function:          PostIndex

3
//

4
// Description:       This function generates the needed post index.

5
//

6
// Inputs:

7
//                    ctx       What the browser sends

8
//                    posttype  The type of post

9
//                    postname  The name of the post type instance

10
//

11
func PostIndex(ctx *web.Context, posttype string, postname string) string {
12
	//

13
	// Get the page contents and process it.

14
	//

15
	pgloc := SiteData.postbase + posttype + "/" + postname + "/index"
16
	return RenderPageContents(ctx, GetPageContents(pgloc), pgloc)
17
}

Die Funktion PostIndex() fasst die Informationen für einen Post-Index zusammen und gibt sie an die Funktion RenderPageContents() weiter.

1
//

2
// Function: 		topPages

3
//

4
// Description: 	This function will generate a 

5
//					"static" top level page that is not

6
// 					a post page.

7
//

8
// Inputs:

9
//                    val         The name of the top level page

10
//

11
func TopPages(ctx *web.Context, val string) string {
12
	//

13
	// Look for the markdown of the page.

14
	//

15
	pgloc := SiteData.Sitebase + "pages/" + val
16
	return RenderPageContents(ctx, GetPageContents(pgloc), pgloc)
17
}

Die Funktion topPages() richtet die Funktion RenderPageContents() für eine Standardseite ein. Alle Seiten befinden sich im Verzeichnis pages/.

1
//

2
// Function: 		GetPageContents

3
//

4
// Description: 	This function is used to retrieve 

5
//					the page contents. It will first look 

6
//					for a markdown page, then for a html 

7
//					page, and then it looks for an amber

8
//					page.

9
//

10
// Inputs:

11
//					filename 		The name of the file

12
//

13
func GetPageContents(filename string) string {
14
	//

15
	// Assume the page can not be found.

16
	//

17
	contents := SiteData.content["404"]
18
19
	//

20
	// Let's look for a markdown version first.

21
	//

22
	wfile, err := os.Open(filename + ".md")
23
	if err == nil {
24
		bcontents, _ := ioutil.ReadAll(wfile)
25
		wfile.Close()
26
		contents = string(blackfriday.MarkdownCommon(bcontents))
27
		//

28
		// Double quotes were turned into &ldquo; and

29
		// &rdquo;. Turn them back. Without this, and 

30
		// Handlebar macros will be broken.

31
		//

32
		contents = strings.Replace(contents, "&ldquo;", "\"", -1)
33
		contents = strings.Replace(contents, "&rdquo;", "\"", -1)
34
	} else {
35
		//

36
		// It must be an html. Look for that.

37
		//

38
		wfile, err = os.Open(filename + ".html")
39
		if err == nil {
40
			bcontents, _ := ioutil.ReadAll(wfile)
41
			contents = string(bcontents)
42
			wfile.Close()
43
		} else {
44
			//

45
			// It must be an amber. Look for that.

46
			//

47
			wfile, err = os.Open(filename + ".amber")
48
			if err == nil {
49
				wfile.Close()
50
				template, err2 := amber.CompileFile(filename+".amber", amber.Options{true, false})
51
				if err2 != nil {
52
					//

53
					// Bad amber file.

54
55
					log.Println("Amber file bad: " + filename)
56
				} else {
57
					//

58
					// Put the default site info.

59
					//

60
					pgData := SiteData.content
61
62
					//

63
					// read in that pages specific data 

64
					// to be added to the rest

65
					// of the data. It is stored at the 

66
					// same place, but in a json

67
					// file.

68
					//

69
					if wfile, err := os.Open(filename + ".json"); err == nil {
70
						//

71
						// Load the json file of extra 

72
						// data for this page. This could 

73
						// override the standard data as 

74
						// well.

75
						//

76
						enc := json.NewDecoder(wfile)
77
						enc.Decode(&pgData)
78
						wfile.Close()
79
					} else {
80
						log.Println("The page: " + filename + " did not have a json file.")
81
					}
82
83
					pgData["PageName"] = filename
84
85
					//

86
					// The amber source compiles okay. 

87
					// Run the template and return

88
					// the results.

89
					//

90
					var b bytes.Buffer
91
					template.Execute(&b, pgData)
92
					contents = b.String()
93
				}
94
			} else {
95
				//

96
				// A file could not be found.

97
				//

98
				log.Println("Could not find file:  " + filename)
99
			}
100
		}
101
	}
102
103
	//

104
	// Return the file contains obtained.

105
	//

106
	return contents
107
}

Die Funktion GetPageContents() lädt den Inhalt aller Seiten / Beiträge. Zunächst wird der nicht gefundene 404-Seiteninhalt aus der globalen Datenstruktur geladen. Die Funktion sucht dann zuerst nach einer Markdown-Datei, dann nach einer HTML-Datei und dann nach einer Amber-Datei. Als Nächstes konvertiert die Routine alle Markdown- und Amber-Inhalte in HTML. Der Amber-Datei sind möglicherweise Daten in einer JSON-Datei zugeordnet. Diese Datendatei wird auch für die Verarbeitung der Amber-Datei geladen.

Die Markdown-Verarbeitung in Blackfriday hat Konsequenzen für den Lenkerprozessor. Der Blackfriday-Markdown für den HTML-Prozessor ändert alle doppelten Anführungszeichen in das HTML-Escape-Äquivalent (&ldquo; und &rdquo;). Da dies für das Rendern nicht zu 100% erforderlich ist, habe ich die Änderung anschließend rückgängig gemacht. Dadurch bleiben alle Lenker-Makros, die doppelte Anführungszeichen verwenden, funktionsfähig.

Wenn Sie weitere Dateiformattypen wünschen, fügen Sie diese einfach hier hinzu. Diese Routine lädt jeden Inhaltstyp.

1
//

2
// Function: 		RenderPageContents

3
//

4
// Description: 	This function is used to process 

5
//					and render the contents of a page.

6
// 					It can be the main page, or a post 

7
//					page, or any page. It accepts the

8
// 					input as the contents for the page 

9
//					template, run the page template

10
// 					with it, process all shortcodes and 

11
//					embedded codes, and return the

12
// 					results.

13
//

14
// Inputs:

15
//				ctx				The calling context

16
// 				contents 		The pages main contents.

17
//				filename		The name of the file the 

18
//								contents was taken from.

19
//

20
func RenderPageContents(ctx *web.Context, contents string, filename string) string {
21
	//

22
	// Set the header information

23
	//

24
	SetStandardHeader(ctx)
25
26
	//

27
	// Put the default site info.

28
	//

29
	pgData := SiteData.content
30
31
	//

32
	// Add data specific to this page.

33
	//

34
	pgData["content"] = contents
35
36
	//

37
	// read in that pages specific data to be added to 

38
	// the rest of the data. It is stored at the same 

39
	// place, but in a json file.

40
	//

41
	if wfile, err := os.Open(filename + ".json"); err == nil {
42
		//

43
		// Load the json file of extra data for this 

44
		// page. This could override the standard data as 

45
		// well.

46
		//

47
		enc := json.NewDecoder(wfile)
48
		enc.Decode(&pgData)
49
		wfile.Close()
50
	} else {
51
		log.Println("The page: " + filename + " did not have a json file.")
52
	}
53
54
	//

55
	// Set the Page Name data field.

56
	//

57
	pgData["PageName"] = filename
58
59
	//

60
	// Register the helpers.

61
	//

62
	// NOTICE:	All helpers can not have spaces in the 

63
	//			parameter. Therefore, all of these 

64
	//			helpers assume a "-" is a space. It gets 

65
	//			translated to a space before using.

66
	//

67
	// Helper: 			save

68
	//

69
	// Description:     This helper allows you do define 

70
	//					macros for expanding inside the 

71
	//					template. You give it a name, 

72
	//					"|", and text to expand into. 

73
	//					Currently, all spaces have to be 

74
	//					"-".

75
	//

76
	handlebars.RegisterHelper("save", func(params ...interface{}) string {
77
		if text, ok := params[0].(string); ok {
78
			parts := strings.Split(text, "|")
79
			content := strings.Replace(parts[1], "-", " ", -1)
80
			pgData[parts[0]] = content
81
			return content
82
		}
83
		return ""
84
	})
85
86
	//

87
	//  The format has to use these sets of constants:

88
	//            Stdlongmonth = "January"

89
	//            Stdmonth = "Jan"

90
	//            Stdnummonth = "1"

91
	//            Stdzeromonth = "01"

92
	//            Stdlongweekday = "Monday"

93
	//            Stdweekday = "Mon"

94
	//            Stdday = "2"

95
	//            Stdunderday = "_2"

96
	//            Stdzeroday = "02"

97
	//            Stdhour = "15"

98
	//            stdHour12 = "3"

99
	//            stdZeroHour12 = "03"

100
	//            Stdminute = "4"

101
	//            Stdzerominute = "04"

102
	//            Stdsecond = "5"

103
	//            Stdzerosecond = "05"

104
	//            Stdlongyear = "2006"

105
	//            Stdyear = "06"

106
	//            Stdpm = "Pm"

107
	//            Stdpm = "Pm"

108
	//            Stdtz = "Mst"

109
	//

110
	//  Helper:			date

111
	//

112
	//  Description: 	This helper prints the current 

113
	//					date/time in the format

114
	// 					given. Please refer to the above 

115
	//					chart for proper format codes. 

116
	//					EX:  07/20/2015 is "01/02/2006"

117
	//

118
	handlebars.RegisterHelper("date", func(params ...interface{}) string {
119
		if format, ok := params[0].(string); ok {
120
			format = strings.Replace(format, "-", " ", -1)
121
			tnow := time.Now()
122
			return tnow.Format(format)
123
		}
124
		return ""
125
	})
126
127
	//

128
	// Render the current for the first pass.

129
	//

130
	page := handlebars.Render(SiteData.template, pgData)
131
132
	//

133
	// Process any shortcodes on the page.

134
	//

135
	page1 := ProcessShortCodes(page)
136
137
	//

138
	// Render new content from Short Code and filters.

139
	//

140
	page2 := handlebars.Render(page1, pgData)
141
142
	//

143
	// Return the results.

144
	//

145
	return page2}

RenderPageContents() ist die Hauptfunktion zum Erstellen einer Webseite. Nachdem der Standardheader für die Antwort festgelegt wurde, erstellt diese Routine eine Datenstruktur und füllt sie mit dem Standardinhalt, dem Seiteninhalt und einer zugehörigen JSON-Datei für die Seite. Der Templater "Lenker" verwendet die Datenstruktur, um die gesamte Seite zu rendern.

Als nächstes definiert die Routine alle Hilfsfunktionen des Lenkers. Derzeit gibt es zwei: save Helper und date Helper. Wenn Sie weitere Hilfsfunktionen wünschen, können Sie diese hier zu Ihrem Projekt hinzufügen.

Der save-Helfer verwendet zwei Parameter: einen Namen, der durch ein | vom Inhalt getrennt ist. Da die Hilfsparameter für den Lenker keine Leerzeichen enthalten dürfen, verwenden die Parameter ein - anstelle eines Leerzeichens. Auf diese Weise können Sie pro Seite Vorlagenvariablen im Kontext der Seite erstellen. Beispielsweise platziert das Makro {{save site|Custom-Computer-Tools}} Custom Computer Tools an der Definitionsstelle und an einer anderen Stelle auf der Seite mit {{site}}.

Der date-Helfer verwendet eine Formatzeichenfolge und erstellt das richtige Datum gemäß dieser Formatzeichenfolge. Beispielsweise erzeugt das Makro {{date January-2,-2006}} an diesem Tag den October 13, 2015.

Der Handlebars-Templater verarbeitet die Seite zweimal: bevor Shortcodes gerendert werden, falls sich Shortcodes in der Vorlagenerweiterung befinden, und nach dem Ausführen der Shortcodes, falls ein Shortcode Handlebars-Vorlagenaktionen hinzufügt. Am Ende gibt die Funktion den vollständigen HTML-Inhalt für die angeforderte Seite zurück.

1
//

2
// Function: 		SetStandardHeader

3
//

4
// Description: 	This function is used as a one place 

5
//					for setting the standard

6
// 					header information.

7
//

8
// Inputs:

9
//

10
func SetStandardHeader(ctx *web.Context) {
11
	//

12
	// Set caching for the item

13
	//

14
	ctx.SetHeader("Cache-Control", "public", false)
15
16
	//

17
	// Set the maximum age to one month (30 days)

18
	//

19
	ctx.SetHeader("Cache-Control", "max-age=2592000", false)
20
21
	//

22
	// Set the name to gpPress for the server type.

23
	//

24
	ctx.SetHeader("Server", "goPress - a CMS written in go from Custom Computer Tools: http://customct.com.", true)
25
}

Die Funktion SetStandardHeader() legt alle benutzerdefinierten Headerelemente in der Antwort fest. Hier legen Sie die Serverinformationen und alle Caching-Steuerelemente fest.

Images.go-Bibliotheksdatei

Die nächste Datei, an der gearbeitet werden muss, ist die Datei Images.go und alle Funktionen, die zum Senden eines Bildes an den Browser erforderlich sind. Da dies ein vollständiger Webserver sein wird, muss er sich mit den Binärdaten des Sendens eines Bildes befassen. Erstellen Sie die Datei Images.go und fügen Sie diesen Code ein:

1
package goPress
2
3
import (
4
	"github.com/hoisie/web"
5
	"io"
6
	"io/ioutil"
7
	"log"
8
	"math/big"
9
	"os"
10
	"path/filepath"
11
)
12
13
//

14
// Function:          ImagesLoad

15
//

16
// Description:       This function is called to upload an image for the

17
//                    images directory.

18
//

19
// Inputs:

20
//                    val        Name of the image with relative path

21
//

22
func ImagesLoad(ctx *web.Context, val string) {
23
	LoadImage(ctx, SiteData.Sitebase+"images/"+val)
24
}
25
26
//

27
// Function:		 LoadThemeImage

28
//

29
// Description:		 This function loads images from the theme's directory.

30
//

31
// Inputs

32
//					 image        Name of the image file to load

33
//

34
func LoadThemeImage(ctx *web.Context, image string) {
35
	LoadImage(ctx, SiteData.stylingBase+SiteData.CurrentStyling+"/images/"+image)
36
}

Diese Bibliotheksdatei beginnt genau wie die anderen: eine Paketdeklaration und die Bibliotheksdeklarationen. Die ImagesLoad()-Funktion und die LoadThemeImage()-Funktion richten einen Aufruf der LoadImage()-Funktion ein, um die eigentliche Arbeit zu erledigen. Diese Funktionen ermöglichen das Laden von Bildern aus dem Site-Verzeichnis oder aus dem aktuellen Themenverzeichnis.

1
//

2
// Function: 		LoadImage

3
//

4
// Description: 	This function does the work of 

5
//					loading an image file and passing it 

6
//					on.

7
//

8
// Inputs:

9
//

10
func LoadImage(ctx *web.Context, val string) {
11
	//

12
	// Get the file extension.

13
	//

14
	fileExt := filepath.Ext(val)
15
16
	//

17
	// Set the http header based on the file type.

18
	//

19
	SetStandardHeader(ctx)
20
	ctx.ContentType(fileExt)
21
	if fileExt == ".svg" {
22
		//

23
		// This is a text based file. Read it and send to the browser.

24
		//

25
		wfile, err := os.Open(val)
26
		if err == nil {
27
			bcontents, _ := ioutil.ReadAll(wfile)
28
			wfile.Close()
29
			ctx.WriteString(string(bcontents))
30
		}
31
	} else {
32
		//

33
		// This is a binary based file. Read it and sent the contents to the browser.

34
		//

35
		fi, err := os.Open(val)
36
37
		//

38
		// Set the size of the binary coming down the pipe. Chrome has to have this value

39
		// one larger than real.

40
		//

41
		finfo, _ := os.Stat(val)
42
		i := big.NewInt(finfo.Size())
43
		ctx.SetHeader("Accept-Ranges", "bytes", true)
44
		ctx.SetHeader("Content-Length", i.String(), true)
45
46
		if err != nil {
47
			log.Println(err)
48
			return
49
		}
50
		defer fi.Close()
51
52
		//

53
		// Create a buffer to contain the image data. Binary images usually get

54
		// very big.

55
		//

56
		buf := make([]byte, 1024)
57
58
		//

59
		// Go through the binary file 1K at a time and send to the browser.

60
		//

61
		for {
62
			//

63
			// Read a buffer full.

64
			//

65
			n, err := fi.Read(buf)
66
			if err != nil && err != io.EOF {
67
				log.Println(err)
68
				break
69
			}
70
71
			//

72
			// If nothing was read, then exit.

73
			//

74
			if n == 0 {
75
				break
76
			}
77
78
			//

79
			// Write the binary buffer to the browser.

80
			//

81
			n2, err := ctx.Write(buf[:n])
82
			if err != nil {
83
				log.Println(err)
84
				break
85
			} else if n2 != n {
86
				log.Println("Error in sending " + val + " to the browser. Amount read does not equal the amount sent.")
87
				break
88
			}
89
		}
90
	}
91
}

Die Funktion LoadImage() überprüft den Bildtyp. Wenn es sich um eine SVG-Datei handelt, wird sie als einfacher Text geladen. Angenommen, alle anderen Dateitypen sind Binärdateien, werden sie von der Routine sorgfältiger geladen. Es werden Binärdateien in 1K-Blöcken hochgeladen.

StyleSheetScripts.go-Bibliotheksdatei

Die nächste Datei dient zum Laden von CSS und JavaScript. Da unser Build-Skript das gesamte CSS und JavaScript zu einer einzigen Datei kompiliert, sind diese Funktionen sehr einfach. Erstellen Sie die Datei StyleSheetScripts.go und fügen Sie die folgenden Zeilen hinzu:

1
package goPress
2
3
import (
4
	"github.com/hoisie/web"
5
	"io/ioutil"
6
	"log"
7
	"os"
8
)
9
10
//

11
// Function: 		GetStylesheets

12
//

13
// Description: 	This function is used to produce 

14
//					the stylesheet to the user.

15
//

16
// Inputs:

17
//

18
func GetStylesheets(ctx *web.Context) string {
19
	//

20
	// See if we have already loaded them or not. If so, 

21
	// just return the pre-loaded stylesheet.

22
	//

23
	ctx.SetHeader("Content-Type", "text/css", true)
24
	SetStandardHeader(ctx)
25
	tmp := ""
26
	if SiteData.stylesheet == "" {
27
		tmp = LoadFile(SiteData.Sitebase + "css/final/final.css")
28
		//

29
		// If we are testing, we do not want the server 

30
		// to cache the stylesheets. Therefore, if 

31
		// cache is true, cache them. Otherwise,

32
		// do not.

33
		//

34
		if SiteData.Cache == true {
35
			SiteData.stylesheet = tmp
36
		}
37
	} else {
38
		//

39
		// We have a cached style sheet. Send it to the 

40
		// browser.

41
		//

42
		tmp = SiteData.stylesheet
43
	}
44
45
	//

46
	// Return the stylesheet.

47
	//

48
	return tmp
49
}
50
51
//

52
// Function: 		GetScripts

53
//

54
// Description: 	This function is to load JavaScripts 

55
//					to the browser. This will

56
// 					actually load all the JavaScript 

57
//					files into one compressed file

58
// 					for uploading to the browser.

59
//

60
// Inputs:

61
//

62
func GetScripts(ctx *web.Context) string {
63
	//

64
	// See if we have already loaded them or not. If so, 

65
	// just return the pre-loaded scripts.

66
	//

67
	ctx.SetHeader("Content-Type", "text/javascript", true)
68
	SetStandardHeader(ctx)
69
	tmp := ""
70
	if SiteData.scripts == "" {
71
		tmp = LoadFile(SiteData.Sitebase + "js/final/final.js")
72
73
		//

74
		// If we are testing, we do not want the server 

75
		// to cache the scripts. Therefore, if cache is 

76
		// true, cache them. Otherwise, do not.

77
		//

78
		if SiteData.Cache == true {
79
			SiteData.scripts = tmp
80
		}
81
	} else {
82
		//

83
		// We have a cached style sheet. Send it to the 

84
		// browser.

85
		//

86
		tmp = SiteData.scripts
87
	}
88
89
	//

90
	// Return the resulting compiled stylesheet.

91
	//

92
	return tmp
93
}
94
95
//

96
// Function: 		LoadFile

97
//

98
// Description: 	This function if for loading 

99
//					individual file contents.

100
//

101
// Inputs

102
//					 file 		name of the file to be 

103
//								loaded

104
//

105
func LoadFile(file string) string {
106
	ret := ""
107
	log.Println("Loading file: " + file)
108
	wfile, err := os.Open(file)
109
	if err == nil {
110
		bcontents, err := ioutil.ReadAll(wfile)
111
		err = err
112
		ret = string(bcontents)
113
		wfile.Close()
114
	} else {
115
		//

116
		// Could not read the file.

117
		//

118
		log.Println("Could not read: " + file)
119
	}
120
	return ret
121
}

Diese Datei hat drei Funktionen. Die Funktion GetStylesheets() lädt die kompilierte CSS-Datei. Die Funktion GetScripts() lädt die kompilierte JavaScript-Datei. Wenn das Cache-Flag gesetzt ist, werden diese beiden Funktionen den Inhalt zwischenspeichern. Ich schalte das Cache-Flag während des Testens aus. Die LoadFile()-Funktion ist eine einfache Funktion zum Laden von Dateien zum Abrufen des Dateiinhalts.

Shortcodes.go-Bibliotheksdatei

Ich wollte zwar einen schnellen Server, aber auch viel Flexibilität. Um die Flexibilität zu erreichen, gibt es zwei verschiedene Arten von Makroerweiterungen: direkte Lenkererweiterung und Shortcode-Erweiterung.

Der Unterschied besteht darin, dass die Lenkererweiterung eine einfache Erweiterung mit geringer Logik ist, während die Shortcode-Erweiterung alles ist, was Sie in das System programmieren können: Herunterladen von Informationen von einer externen Site, Verarbeiten von Informationen mit einem externen Programm oder so ziemlich alles.

Erstellen Sie die Datei Shortcodes.go und platzieren Sie diese darin:

1
package goPress
2
3
//

4
// Library: 		Shortcodes

5
//

6
// Description: 	This library gives the functionality 

7
//					of shortcodes in the CMS. A shortcode 

8
//					runs a function with specified 

9
//					argument and a surrounded contents. 

10
//					The function should process the 

11
//					contents according to the arguments 

12
//					and return a string for placement 

13
//					into the page. Shortcodes are 

14
//					recursively processed, therefore you 

15
//					can have different shortcodes inside 

16
//					of other shortcodes.  You can not 

17
//					have the same shortcode inside of 

18
//					itself.

19
//

20
import (
21
	"bytes"
22
	"log"
23
	"regexp"
24
)
25
26
//

27
// Type: 			ShortcodeFunction

28
//

29
// Description: 	This type defines a function that 

30
//					implements a shortcode. The function

31
// 					should receive two strings and return 

32
//					a string.

33
//

34
type ShortcodeFunction func(string, string) string
35
36
//

37
// Library Variables:

38
//

39
// 			shortcodeStack 		This array of functions 

40
//								holds all of the 

41
//								shortcodes usable by the 

42
//								CMS. You add shortcodes 

43
//								using the AddShortCode 

44
//								function.

45
//

46
var (
47
	shortcodeStack map[string]ShortcodeFunction
48
)
49
50
//

51
// Library Function:

52
//

53
//				init 		This function is called upon 

54
//							library use to initialize any 

55
//							variables used for the 

56
//							library before anyone can 

57
//							make a call to a library 

58
//							function.

59
//

60
61
func init() {
62
	shortcodeStack = make(map[string]ShortcodeFunction)
63
}

Diese Datei beginnt wie alle anderen mit einer Deklaration des verwendeten Pakets und der verwendeten Bibliotheken. Bei der Definition eines speziellen ShortcodeFunction-Variablentyps, einer Bibliotheksvariablen und einer init()-Funktion ist dies jedoch schnell anders. Bibliotheksvariablen werden nur von einer Funktion der Bibliothek gesehen. Diese Bibliotheksvariable, shortcodeStack, ist eine Zuordnung von Zeichenfolgen zu einer Funktion.

Mit den Funktionen der Bibliothek init() können Sie Code ausführen, bevor andere Aufrufe an die Bibliothek erfolgen. Hier initialisiere ich die shortcodeStack-Datenstruktur für die Liste der Shortcodes.

1
//

2
// Function: 		AddShortCode

3
//

4
// Description: 	This function adds a new shortcode to 

5
//					be used.

6
//

7
// Inputs

8
// 		name           Name of the shortcode

9
// 		funct          function to process the shortcode

10
//

11
func AddShortCode(name string, funct ShortcodeFunction) {
12
	shortcodeStack[name] = funct
13
}

Mit der Funktion AddShortCode() können Sie eine Funktion zum Verarbeiten eines Shortcodes für alle Shortcodes in die Bibliotheksvariable laden.

1
//

2
// Function:		ProcessShortCodes

3
//

4
// Description: 	This function takes in a string, 

5
//					searches for shortcodes, process the 

6
//					shortcode, put the results into the 

7
//					string, and then return the fully 

8
//					processed string.

9
//

10
// Inputs

11
//				page 	String with possible shortcodes

12
//

13
func ProcessShortCodes(page string) string {
14
	//

15
	// Create a work buffer.

16
	//

17
	var buff bytes.Buffer
18
19
	//

20
	// Search for shortcodes. We first capture a 

21
	// shortcode.

22
	//

23
	r, err := regexp.Compile(`\-\[([^\]]*)\]\-`)
24
	if err == nil {
25
		match := r.FindString(page)
26
		if match != "" {
27
			//

28
			// Get the indexes to the matching area.

29
			//

30
			index := r.FindStringIndex(page)
31
32
			//

33
			// Save all the text before the shortcode 

34
			// into the buffer.

35
			//

36
			buff.WriteString(page[0:index[0]])
37
38
			//

39
			// Get everything that is left after the 

40
			// shortcode.

41
			//

42
			remander := page[index[1]:]
43
44
			//

45
			// Separate the strings out and setup 

46
			// variables for their contents.

47
			//

48
			submatch := r.FindStringSubmatch(match)
49
			name := ""
50
			contents := ""
51
			args := ""
52
53
			//

54
			// Get the name of the shortcode and separate 

55
			// the extra arguments.

56
			//

57
			r3, err3 := regexp.Compile(`(\w+)(.*)`)
58
			if err3 == nil {
59
				submatch2 := r3.FindStringSubmatch(submatch[1])
60
61
				//

62
				// The first submatch is the name of the 

63
				// shortcode.

64
				//

65
				name = submatch2[1]
66
67
				//

68
				// The second submatch, if any, are the 

69
				// arguments for the shortcode.

70
				//

71
				args = submatch2[2]
72
			} else {
73
				//

74
				// Something happened to the internal 

75
				// matching.

76
				//

77
				name = submatch[1]
78
				args = ""
79
			}
80
81
			//

82
			// Find the end of the shortcode.

83
			//

84
			final := "\\-\\[\\/" + name + "\\]\\-"
85
			r2, err2 := regexp.Compile(final)
86
			if err2 == nil {
87
				index2 := r2.FindStringIndex(remander)
88
				if index2 != nil {
89
					//

90
					// Get the contents and what is left 

91
					// over after the closing of the 

92
					// shortcode.

93
					//

94
					contents = remander[:index2[0]]
95
					remander2 := remander[index2[1]:]
96
97
					//

98
					// If it is a real shortcode, then 

99
					// run it!

100
					//

101
					if shortcodeStack[name] != nil {
102
						//

103
						// See if there is any shortcodes 

104
						// inside the contents area.

105
						//

106
						contents = ProcessShortCodes(contents)
107
108
						//

109
						// Run the shortcode and add it's 

110
						// result to the buffer.

111
						//

112
						buff.WriteString(shortcodeStack[name](args, contents))
113
					}
114
115
					//

116
					// Process any remaining shortcodes.

117
					//

118
					buff.WriteString(ProcessShortCodes(remander2))
119
120
				} else {
121
					//

122
					// We have a bad shortcode 

123
					// definition.  All shortcodes have 

124
					// to be closed. Therefore,

125
					// simply do not process anything and 

126
					// tell the logs.

127
					//

128
					log.Println("Bad Shortcode definition. It was not closed.  Name:  " + name)
129
					buff.WriteString(page[index[0]:index[1]])
130
					buff.WriteString(ProcessShortCodes(remander))
131
				}
132
			} else {
133
				//

134
				// There was an error in the regular 

135
				// expression for closing the shortcode.

136
				//

137
				log.Println("The closing shortcode's regexp did not work!")
138
			}
139
		} else {
140
			//

141
			// No shortcodes, just copy the page to the 

142
			// buffer.

143
			//

144
			buff.WriteString(page)
145
		}
146
	} else {
147
		//

148
		// If the Regular Expression is invalid, tell the 

149
		// world!

150
		//

151
		log.Println("RegEx: Invalid expression.")
152
	}
153
154
	//

155
	// Return the resulting buffer.

156
	//

157
	return buff.String()
158
}

Die ProcessShortCodes()-Funktion verwendet eine Zeichenfolge, die den Inhalt der Webseite darstellt, und sucht nach allen darin enthaltenen Shortcodes. Wenn Sie also einen Shortcode namens box haben, fügen Sie ihn in folgendem Format in Ihre Webseite ein:

1
-[box args="some items"]-
2
<p>This should be inside the box.</p>
3
-[/box]-

Alles nach dem Leerzeichen im Shortcode-Öffner sind die Argumente für die Verarbeitung des Shortcodes. Die Formatierung der Argumente hängt von der zu verarbeitenden Shortcode-Funktion ab.

Alle Funktionscodes müssen einen abschließenden Funktionscode haben. Was sich im öffnenden und schließenden Shortcode befindet, ist der Vorgang für Shortcodes, bevor er an die Shortcode-Verarbeitungsfunktion gesendet wird. Ich verwende das -[]-, um einen Shortcode zu definieren, damit die Inline-JavaScript-Indizierung nicht als Shortcode verwechselt wird.

1
//

2
// Function: 		ShortcodeBox

3
//

4
// Description: 	This shortcode is used to put the 

5
//					surrounded HTML in a box div.

6
//

7
// Inputs:

8
//			parms 		The parameters used by the 

9
//						shortcode

10
//			context		The HTML enclosed by the opening 

11
//						and closing shortcodes.

12
//

13
func ShortcodeBox(parms string, context string) string {
14
	return ("<div class='box'>" + context + "</div>")
15
}
16
17
//

18
// Function: 		ShortcodeColumn1

19
//

20
// Description: 	This shortcode is used to put the 

21
//					surrounded HTML in the first column.

22
//

23
// Inputs:

24
//			parms 		The parameters used by the 

25
//						shortcode

26
//			context		The HTML enclosed by the opening 

27
//						and closing shortcodes.

28
//

29
func ShortcodeColumn1(parms string, context string) string {
30
	return ("<div class='col1'>" + context + "</div>")
31
}
32
33
//

34
// Function: 		ShortcodeColumn2

35
//

36
// Description: 	This shortcode is used to put the 

37
//					surrounded HTML in the second column.

38
//

39
// Inputs:

40
//			parms 		The parameters used by the 

41
//						shortcode

42
//			context		The HTML enclosed by the opening 

43
//						and closing shortcodes.

44
//

45
func ShortcodeColumn2(parms string, context string) string {
46
	return ("<div class='col2'>" + context + "</div>")
47
}
48
49
//

50
// Function:		ShortcodePHP

51
//

52
// Description:		This shortcode is for surrounding a 

53
//					code block and formatting it's look 

54
//					and feel properly. This one is for a 

55
//					PHP code block.

56
//

57
// Inputs:

58
//			parms 		The parameters used by the 

59
//						shortcode

60
//			context		The HTML enclosed by the opening 

61
//						and closing shortcodes.

62
//

63
func ShortcodePHP(params string, context string) string {
64
	return ("<div class='showcode'><pre type='syntaxhighlighter' class='brush: php'>" + context + "</pre></div>")
65
}
66
67
//

68
// Function:		ShortcodeJS

69
//

70
// Description:		This shortcode is for surrounding a 

71
//					code block and formatting it's look 

72
//					and feel properly. This one is for a 

73
//					JavaScript code block.

74
//

75
// Inputs:

76
//			parms 		The parameters used by the 

77
//						shortcode

78
//			context		The HTML enclosed by the opening 

79
//						and closing shortcodes.

80
//

81
func ShortcodeJS(params string, context string) string {
82
	return ("<div class='showcode'><pre type='syntaxhighlighter' class='brush: javascript'>" + context + "</pre></div>")
83
}
84
85
//

86
// Function:		ShortcodeHTML

87
//

88
// Description:		This shortcode is for surrounding a 

89
//					code block and formatting it's look 

90
//					and feel properly. This one is for a 

91
//					HTML code block.

92
//

93
// Inputs:

94
//			parms 		The parameters used by the 

95
//						shortcode

96
//			context		The HTML enclosed by the opening 

97
//						and closing shortcodes.

98
//

99
func ShortcodeHTML(params string, context string) string {
100
	return ("<div class='showcode'><pre type='syntaxhighlighter' class='brush: html'>" + context + "</pre></div>")
101
}
102
103
//

104
// Function:		ShortcodeCSS

105
//

106
// Description:		This shortcode is for surrounding a 

107
//					code block and formatting it's look 

108
//					and feel properly. This one is for a 

109
//					CSS code block.

110
//

111
// Inputs:

112
//			parms 		The parameters used by the 

113
//						shortcode

114
//			context		The HTML enclosed by the opening 

115
//						and closing shortcodes.

116
//

117
func ShortcodeCSS(params string, context string) string {
118
	return ("<div class='showcode'><pre type='syntaxhighlighter' class='brush: css'>" + context + "</pre></div>")
119
}
120
121
//

122
// Function: 		LoadDefaultShortcodes

123
//

124
// Description: 	This function is used to load in all 

125
//					the default shortcodes.

126
//

127
// Inputs:

128
//

129
func LoadDefaultShortcodes() {
130
	AddShortCode("Box", ShortcodeBox)
131
	AddShortCode("Column1", ShortcodeColumn1)
132
	AddShortCode("Column2", ShortcodeColumn2)
133
	AddShortCode("php", ShortcodePHP)
134
	AddShortCode("javascript", ShortcodeJS)
135
	AddShortCode("html", ShortcodeHTML)
136
	AddShortCode("css", ShortcodeCSS)
137
}

Der letzte Codeabschnitt definiert sieben einfache Shortcodes und fügt sie mit der Funktion LoadDefaultShortcodes() dem Shortcode-Array hinzu. Wenn Sie eine andere Funktionalität wünschen, müssen Sie diesen Code nur ändern und er wird überall auf Ihrer Website aktualisiert.

goPressServer.go Hauptprogrammdatei

Die letzte zu erstellende Datei ist die Hauptprogrammdatei. Erstellen Sie oben im Entwicklungsverzeichnis die Datei goPressServer.go und platzieren Sie diese Informationen:

1
package main
2
3
import (
4
	"./src/goPress"
5
)
6
7
//

8
// Function: 		main

9
//

10
// Description: 	This is the main function that is 

11
//					called whenever the program is

12
// 					executed. It will load the globals, 

13
//					set the different routes, and

14
// 					start the server.

15
//

16
// Inputs:

17
//

18
func main() {
19
	//

20
	// Load the default Shortcodes.

21
	//

22
	goPress.LoadDefaultShortcodes()
23
24
	//

25
	// Load all global variables.

26
	//

27
	goPress.GetGlobals()
28
29
	//

30
	// Setup the Default routes.

31
	//

32
	goPress.DefaultRoutes()
33
34
	//

35
	// Run the web server

36
	//

37
	goPress.StartServer(goPress.SiteData.ServerAddress)
38
}

Die main()-Funktion ist die Routine, die beim Ausführen des Programms aufgerufen wird. Zuerst werden die Shortcodes eingerichtet, globale Variablen geladen, die Standardrouten festgelegt und dann der Server gestartet.

Kompilieren und Ausführen

Um das gesamte Programm zu kompilieren, wechseln Sie in das oberste Verzeichnis, in dem sich die Datei goPressServer.go befindet, und geben Sie Folgendes ein:

1
go build goPressServer.go

Wenn alle Dateien vorhanden sind, sollte sie auf den Mac- und Linux-Systemen mit goPressServer und unter Windows mit goPressServer.exe kompiliert werden.

Running goPressServer in the TerminalRunning goPressServer in the TerminalRunning goPressServer in the Terminal

Wenn Sie das Programm in einem Terminal ausführen, werden die Protokollanweisungen mit Datum und Uhrzeit wie oben angezeigt.

The Front Page From the ServerThe Front Page From the ServerThe Front Page From the Server

Wenn Sie Ihren Browser unter der Adresse des Servers öffnen, wird die Startseite angezeigt. Sie sehen den Beispiel-Shortcode und die zwei verschiedenen verwendeten Lenker-Hilfsfunktionen. Sie haben jetzt Ihren eigenen Webserver!

Wie Sie sehen, habe ich die Startseite geändert und dem ursprünglichen Site-Design im Tutorial Erstellen eines CMS: Struktur drei weitere Seiten hinzugefügt. Ich habe auch die JavaScript-Bibliothek Syntax Highlighter im Verzeichnis site/js/ hinzugefügt, um den Code auf der Webseite mithilfe des Shortcodes anzuzeigen.

Alle diese Änderungen dienen dazu, den Lenker und die Shortcode-Verarbeitung zu demonstrieren. Da Syntax Highlighter jedoch nicht gut mit der Komprimierung funktioniert, habe ich die JavaScript-Komprimierung aus der Gulp-Datei entfernt. Alle Änderungen befinden sich in der Download-Datei dieses Tutorials.

Es gibt einen neuen Kurs, Go Fundamentals for Building Web Servers, der eine großartige Einführung in die Go-Sprache und deren Programmierung bietet.

Abschluss

Nachdem Sie nun wissen, wie Sie einen einfachen, aber leistungsstarken Webserver mit der Sprache go erstellen, ist es Zeit für Sie, zu experimentieren. Erstellen Sie neue Seiten, Beiträge, einbettbare Teile und Shortcodes. Diese einfache Plattform ist weitaus schneller als die Verwendung von WordPress und liegt ganz in Ihrer Hand. Erzählen Sie mir in den Kommentaren unten von Ihrem Server.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.