/
modules.go
167 lines (151 loc) · 5.25 KB
/
modules.go
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
// Copyright 2016 NDP Systèmes. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package server
import (
"fmt"
"os"
"path/filepath"
"sort"
"strings"
"github.com/beevik/etree"
"github.com/erlangs/okoo/src/actions"
"github.com/erlangs/okoo/src/i18n"
"github.com/erlangs/okoo/src/menus"
"github.com/erlangs/okoo/src/models"
"github.com/erlangs/okoo/src/templates"
"github.com/erlangs/okoo/src/views"
)
// ResourceDir is the path to the resources directory.
// It is set on startup based on the configuration.
var ResourceDir string
// A Module is a go package that implements business features.
// This struct is used to register modules.
type Module struct {
Name string
PreInit func() // Function to be run before bootstrap but after all calls to init
PostInit func() // Function to be run after initialisation is complete and before server starts
}
// A ModulesList is a list of Module objects
type ModulesList []*Module
// Names returns a list of all module names in this ModuleList.
func (ml *ModulesList) Names() []string {
res := make([]string, len(*ml))
for i, module := range *ml {
res[i] = module.Name
}
return res
}
// Modules is the list of activated modules in the application
var Modules ModulesList
// RegisterModule registers the given module in the server
// This function should be called in the init() function of
// all Hexya Addons.
func RegisterModule(mod *Module) {
Modules = append(Modules, mod)
}
// LoadInternalResources loads all data in the 'resources' directory, that are
// - views,
// - actions,
// - menu items
// Internal resources are defined in XML files.
func LoadInternalResources(resourceDir string) {
loadData(resourceDir, "resources", "xml", loadXMLResourceFile)
}
// LoadDataRecords loads all the data records in the 'data' directory into the database.
// Data records are defined in CSV files.
func LoadDataRecords(resourceDir string) {
loadData(resourceDir, "data", "csv", models.LoadCSVDataFile)
}
// LoadDemoRecords loads all the data records in the 'demo' directory into the database.
// Demo records are defined in CSV files.
func LoadDemoRecords(resourceDir string) {
loadData(resourceDir, "demo", "csv", models.LoadCSVDataFile)
}
// LoadTranslations loads all translation data from the PO files in the 'i18n' directory
// into the translations registry.
func LoadTranslations(resourceDir string, langs []string) {
for i, lang := range langs {
if strings.ToUpper(lang) == "ALL" {
langs = append(langs[:i], append(i18n.GetAllLanguageList(), langs[i+1:]...)...)
}
}
for _, mod := range Modules {
dataDir := filepath.Join(resourceDir, "i18n", mod.Name)
if _, err := os.Stat(dataDir); err != nil {
// No resources dir in this module
continue
}
LoadModuleTranslations(dataDir, langs)
}
}
// LoadModuleTranslations loads the PO files in the given directory for the given languages
func LoadModuleTranslations(i18nDir string, langs []string) {
var poFiles []string
for _, lang := range langs {
abs, _ := filepath.Abs(i18nDir)
dataFiles, err := filepath.Glob(fmt.Sprintf("%s/%s.po", abs, lang))
if err != nil {
log.Panic("Unable to scan directory for data files", "dir", i18nDir, "type", "po", "error", err)
}
poFiles = append(poFiles, dataFiles...)
}
dataFilesSorted := sort.StringSlice(poFiles)
dataFilesSorted.Sort()
for _, dataFile := range dataFilesSorted {
i18n.LoadPOFile(dataFile)
}
}
// loadData loads the files in the given dir with the given extension (without .)
// using the loader function.
func loadData(resourceDir, dir, ext string, loader func(string)) {
for _, mod := range Modules {
dataDir := filepath.Join(resourceDir, dir, mod.Name)
if _, err := os.Stat(dataDir); err != nil {
// No resources dir in this module
continue
}
dataFiles, err := filepath.Glob(fmt.Sprintf("%s/*.%s", dataDir, ext))
if err != nil {
log.Panic("Unable to scan directory for data files", "dir", dataDir, "type", ext, "error", err)
}
dataFilesSorted := sort.StringSlice(dataFiles)
dataFilesSorted.Sort()
for _, dataFile := range dataFilesSorted {
loader(dataFile)
}
}
}
// loadXMLResourceFile loads the data from an XML data file into memory.
func loadXMLResourceFile(fileName string) {
doc := etree.NewDocument()
if err := doc.ReadFromFile(fileName); err != nil {
log.Panic("Error loading XML data file", "file", fileName, "error", err)
}
for _, dataTag := range doc.FindElements("hexya/data") {
for _, object := range dataTag.ChildElements() {
switch object.Tag {
case "view":
views.LoadFromEtree(object)
case "action":
actions.LoadFromEtree(object)
case "menuitem":
menus.LoadFromEtree(object)
case "template":
templates.LoadFromEtree(object)
default:
log.Panic("Unknown XML tag", "filename", fileName, "tag", object.Tag)
}
}
}
}