1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
|
// Copyright (C) 2021 nytpu
// SPDX-License-Identifier: AGPL-3.0-only
// For more license details, see LICENSE or <https://www.gnu.org/licenses/agpl-3.0.html>.
// This file incorporates code from makeworld's amfora:
// https://github.com/makeworld-the-better-one/amfora/blob/master/subscriptions/structs.go
// Original source is Copyright 2020 makeworld and is licensed under the
// terms of the GNU General Public License Version 3.0.
// https://www.gnu.org/licenses/gpl-3.0-standalone.html
// package core implements the structs and other necessary resources for storing
// and managing feeds and pages
package core
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sync"
"golang.nytpu.com/comitium/localizations"
)
// Global instance of FullData
var Data = FullData{
FeedsMu: &sync.RWMutex{},
PagesMu: &sync.RWMutex{},
// Maps are created in init()
}
// the Header of feeds.gmi.
// set in Init, either to the contents of header.gmi or to the default string
var Header string
// Timeout for requests
var Timeout int
var Localizer *localizations.Localizer
// have to do this instead of the automatically called init() because
// initialization stuff requires parsing of command line args first
func Init(dataPath string, localizer *localizations.Localizer) error {
Localizer = localizer
// initialize comitium.json & data maps
f, err := os.Open(filepath.Join(dataPath, "comitium.json"))
if err == nil {
// File exists and could be opened
fi, err := f.Stat()
if err == nil && fi.Size() > 0 {
// File is not empty
jsonBytes, err := ioutil.ReadAll(f)
f.Close()
if err != nil {
return fmt.Errorf("read comitium.json error: %w", err)
}
err = json.Unmarshal(jsonBytes, &Data)
if err != nil {
return fmt.Errorf("comitium.json is corrupted: %w", err)
}
}
f.Close()
} else if !os.IsNotExist(err) {
// There's an error opening the file, but it's not bc is doesn't exist
return fmt.Errorf("open comitium.json error: %w", err)
}
if Data.Feeds == nil {
Data.Feeds = make(map[string]*Feed)
}
if Data.Pages == nil {
Data.Pages = make(map[string]*Page)
}
// initialize header.gmi
f, err = os.Open(filepath.Join(dataPath, "header.gmi"))
if err == nil {
fi, err := f.Stat()
if err == nil && fi.Size() > 0 {
headerBytes, err := ioutil.ReadAll(f)
f.Close()
if err != nil {
return fmt.Errorf("read header.gmi error: %w", err)
}
Header = string(headerBytes)
}
f.Close()
} else if !os.IsNotExist(err) {
return fmt.Errorf("open header.gmi error: %w", err)
}
if Header == "" {
Header = TranslateString("# %s\n\n", "files.feed_default_header", nil)
}
Timeout = 10
return nil
}
// WriteJSON writes a FullData to disk
func (d *FullData) WriteJSON(dataPath string) error {
// use .new so if there's a corruption when writing the old file is intact
path := filepath.Join(dataPath, "comitium.json.new")
d.RLock()
jsonBytes, err := json.MarshalIndent(&d, "", "\t")
d.RUnlock()
if err != nil {
return err
}
err = ioutil.WriteFile(path, jsonBytes, 0666)
if err != nil {
return err
}
// if full write was successful then overwrite original
err = os.Rename(path, filepath.Join(dataPath, "comitium.json"))
return err
}
// TranslateString takes a template string, a localizer tag, and a localizer
// replacement set and will get the localized string from the localizer tag,
// replace everything using the localizer replacements, and then substitute that
// string into the template string.
func TranslateString(template, localizerTag string, replacements *localizations.Replacements) string {
return TranslateStringLocalizer(Localizer, template, localizerTag, replacements)
}
// TranslateStringLocalizer takes a localizations.Localizer, template string, a
// localizer tag, and a localizer replacement set and will get the localized
// string from the localizer tag, replace everything using the localizer
// replacements, and then substitute that string into the template string.
// Should not be used once core.Init() is called, instead use TranslateString().
func TranslateStringLocalizer(localizer *localizations.Localizer, template, localizerTag string, replacements *localizations.Replacements) string {
return fmt.Sprintf(template, localizer.Get(localizerTag, replacements))
}
|