jekyll-open-sdg-plugins 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.editorconfig +16 -16
- data/.github/workflows/test-pull-requests.yml +17 -0
- data/.gitignore +6 -4
- data/Makefile +33 -0
- data/README.md +7 -7
- data/jekyll-open-sdg-plugins.gemspec +17 -17
- data/lib/jekyll-open-sdg-plugins.rb +14 -13
- data/lib/jekyll-open-sdg-plugins/create_goals.rb +75 -75
- data/lib/jekyll-open-sdg-plugins/create_indicators.rb +67 -67
- data/lib/jekyll-open-sdg-plugins/create_pages.rb +112 -99
- data/lib/jekyll-open-sdg-plugins/fetch_remote_data.rb +167 -167
- data/lib/jekyll-open-sdg-plugins/helpers.rb +94 -66
- data/lib/jekyll-open-sdg-plugins/sdg_variables.rb +439 -417
- data/lib/jekyll-open-sdg-plugins/search_index.rb +92 -92
- data/lib/jekyll-open-sdg-plugins/site_configuration.rb +43 -0
- data/lib/jekyll-open-sdg-plugins/translate_date.rb +98 -72
- data/lib/jekyll-open-sdg-plugins/translate_key.rb +20 -20
- data/lib/jekyll-open-sdg-plugins/translate_metadata_field.rb +111 -111
- data/lib/jekyll-open-sdg-plugins/version.rb +3 -3
- data/tests/Gemfile +6 -0
- data/tests/_config.yml +145 -0
- metadata +8 -4
@@ -1,99 +1,112 @@
|
|
1
|
-
require "jekyll"
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
#
|
11
|
-
# - the
|
12
|
-
# - the
|
13
|
-
# - the
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
# filename
|
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
|
-
end
|
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
|
-
|
1
|
+
require "jekyll"
|
2
|
+
require_relative "helpers"
|
3
|
+
|
4
|
+
module JekyllOpenSdgPlugins
|
5
|
+
class CreatePages < Jekyll::Generator
|
6
|
+
safe true
|
7
|
+
priority :normal
|
8
|
+
|
9
|
+
def generate(site)
|
10
|
+
# If site.create_pages is set, create the 4 required pages. These include:
|
11
|
+
# - the home page: /
|
12
|
+
# - the indicators json page: /indicators.json
|
13
|
+
# - the search results page: /search
|
14
|
+
# - the reporting status page: /reporting-status
|
15
|
+
#
|
16
|
+
# These can be overridden though, with a create_pages.pages setting in
|
17
|
+
# _config.yml, like so:
|
18
|
+
#
|
19
|
+
# create_pages:
|
20
|
+
# pages:
|
21
|
+
# - folder: ''
|
22
|
+
# layout: frontpage
|
23
|
+
# - filename: my-json-file.json
|
24
|
+
# folder: my-subfolder
|
25
|
+
# layout: indicator-json
|
26
|
+
#
|
27
|
+
# Note the optional "filename" setting for when the page needs a specific
|
28
|
+
# filename (as opposed to being "index.html" inside a named folder).
|
29
|
+
#
|
30
|
+
# To use the default 4 pages, simply put:
|
31
|
+
#
|
32
|
+
# create_pages: true
|
33
|
+
if site.config['languages'] and site.config['create_pages']
|
34
|
+
|
35
|
+
default_pages = [
|
36
|
+
{
|
37
|
+
'folder' => '/',
|
38
|
+
'layout' => 'frontpage'
|
39
|
+
},
|
40
|
+
{
|
41
|
+
'folder' => '/reporting-status',
|
42
|
+
'layout' => 'reportingstatus',
|
43
|
+
'title' => 'status.reporting_status',
|
44
|
+
},
|
45
|
+
{
|
46
|
+
'filename' => 'indicators.json',
|
47
|
+
'folder' => '/',
|
48
|
+
'layout' => 'indicator-json',
|
49
|
+
},
|
50
|
+
{
|
51
|
+
'folder' => '/search',
|
52
|
+
'layout' => 'search',
|
53
|
+
'title' => 'search.search',
|
54
|
+
}
|
55
|
+
]
|
56
|
+
pages = default_pages
|
57
|
+
if site.config['create_pages'].is_a?(Hash) and site.config['create_pages'].key?('pages')
|
58
|
+
# Backwards compatability to support the deprecated "pages" key.
|
59
|
+
pages = site.config['create_pages']['pages']
|
60
|
+
elsif site.config['create_pages'].is_a?(Array)
|
61
|
+
pages = site.config['create_pages']
|
62
|
+
end
|
63
|
+
|
64
|
+
# See if we need to "map" any language codes.
|
65
|
+
languages_public = Hash.new
|
66
|
+
if site.config['languages_public']
|
67
|
+
languages_public = opensdg_languages_public(site)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Loop through the languages.
|
71
|
+
site.config['languages'].each_with_index do |language, index|
|
72
|
+
# Get the "public language" (for URLs) which may be different.
|
73
|
+
language_public = language
|
74
|
+
if languages_public[language]
|
75
|
+
language_public = languages_public[language]
|
76
|
+
end
|
77
|
+
# Loop through the pages.
|
78
|
+
pages.each do |page|
|
79
|
+
# Add the language subfolder for all except the default (first) language.
|
80
|
+
dir = index == 0 ? page['folder'] : File.join(language_public, page['folder'])
|
81
|
+
# Create the page.
|
82
|
+
site.collections['pages'].docs << OpenSdgPage.new(site, site.source, dir, page, language)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# A Page subclass used in the `CreatePages` class.
|
90
|
+
class OpenSdgPage < Jekyll::Page
|
91
|
+
def initialize(site, base, dir, page, language)
|
92
|
+
@site = site
|
93
|
+
@base = base
|
94
|
+
|
95
|
+
index_files = (!page.key?('filename') or page['filename'] == 'index.html')
|
96
|
+
@dir = index_files ? File.join(dir, '/') : dir
|
97
|
+
@name = index_files ? 'index.html' : page['filename']
|
98
|
+
|
99
|
+
self.process(@name)
|
100
|
+
self.data = {}
|
101
|
+
self.data['language'] = language
|
102
|
+
|
103
|
+
# Add anything else besides "folder" and "filename". This will catch
|
104
|
+
# things like "layout" and "title", and anything else.
|
105
|
+
page.each do |key, value|
|
106
|
+
if key != 'folder' && key != 'filename'
|
107
|
+
self.data[key] = value
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -1,167 +1,167 @@
|
|
1
|
-
require "jekyll"
|
2
|
-
require 'json'
|
3
|
-
require 'deep_merge'
|
4
|
-
require 'open-uri'
|
5
|
-
require_relative "helpers"
|
6
|
-
|
7
|
-
module JekyllOpenSdgPlugins
|
8
|
-
class FetchRemoteData < Jekyll::Generator
|
9
|
-
safe true
|
10
|
-
priority :highest
|
11
|
-
|
12
|
-
# Fix a Unix path in case we are on Windows.
|
13
|
-
def fix_path(path)
|
14
|
-
path_parts = path.split('/')
|
15
|
-
return path_parts.join(File::SEPARATOR)
|
16
|
-
end
|
17
|
-
|
18
|
-
# Our hardcoded list of pieces of the build that we expect.
|
19
|
-
def get_endpoints()
|
20
|
-
return {
|
21
|
-
'meta' => 'meta/all.json',
|
22
|
-
'headlines' => 'headline/all.json',
|
23
|
-
'schema' => 'meta/schema.json',
|
24
|
-
'reporting' => 'stats/reporting.json',
|
25
|
-
'translations' => 'translations/translations.json',
|
26
|
-
'zip' => 'zip/all_indicators.json'
|
27
|
-
}
|
28
|
-
end
|
29
|
-
|
30
|
-
# Is this path a remote path?
|
31
|
-
def is_path_remote(path)
|
32
|
-
return path.start_with?('http')
|
33
|
-
end
|
34
|
-
|
35
|
-
# Get a build from a local folder on disk or a remote URL on the Internet.
|
36
|
-
def fetch_build(path)
|
37
|
-
|
38
|
-
is_remote = is_path_remote(path)
|
39
|
-
build = {}
|
40
|
-
get_endpoints().each do |key, value|
|
41
|
-
endpoint = is_remote ? path + '/' + value : File.join(path, fix_path(value))
|
42
|
-
|
43
|
-
begin
|
44
|
-
json_file = is_remote ? open(endpoint) : File.open(endpoint)
|
45
|
-
build[key] = JSON.load(json_file)
|
46
|
-
rescue StandardError => e
|
47
|
-
# For backwards compatibility, we allow 'translations' to be missing.
|
48
|
-
if key != 'translations'
|
49
|
-
puts e.message
|
50
|
-
abort 'Unable to read data from: ' + endpoint
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
return build
|
56
|
-
end
|
57
|
-
|
58
|
-
# Predict (before data has been fetched) whether the site is using
|
59
|
-
# translated builds or not.
|
60
|
-
def site_uses_translated_builds(path)
|
61
|
-
|
62
|
-
is_remote = is_path_remote(path)
|
63
|
-
endpoints = get_endpoints()
|
64
|
-
# For a quick test, we just use 'meta'.
|
65
|
-
meta = endpoints['meta']
|
66
|
-
endpoint = is_remote ? path + '/' + meta : File.join(path, fix_path(meta))
|
67
|
-
|
68
|
-
begin
|
69
|
-
json_file = is_remote ? open(endpoint) : File.open(endpoint)
|
70
|
-
rescue StandardError => e
|
71
|
-
# If we didn't find an untranslated 'meta', we assume translated builds.
|
72
|
-
return true
|
73
|
-
end
|
74
|
-
|
75
|
-
# Other wise assume untranslated builds.
|
76
|
-
return false
|
77
|
-
end
|
78
|
-
|
79
|
-
def generate(site)
|
80
|
-
|
81
|
-
# For below, make sure there is at least an empty hash at
|
82
|
-
# site.data.translations.
|
83
|
-
if !site.data.has_key?('translations')
|
84
|
-
site.data['translations'] = {}
|
85
|
-
end
|
86
|
-
|
87
|
-
remote = site.config['remote_data_prefix']
|
88
|
-
local = site.config['local_data_folder']
|
89
|
-
|
90
|
-
if !remote && !local
|
91
|
-
abort 'Site config must include either "remote_data_prefix" or "local_data_folder".'
|
92
|
-
end
|
93
|
-
|
94
|
-
build_location = remote ? remote : File.join(Dir.pwd, local)
|
95
|
-
translated_builds = site_uses_translated_builds(build_location)
|
96
|
-
|
97
|
-
if translated_builds
|
98
|
-
# For translated builds, we get a build for each language, and
|
99
|
-
# place them in "subfolders" (so to speak) of site.data.
|
100
|
-
site.config['languages'].each do |language|
|
101
|
-
data_target = site.data[language]
|
102
|
-
translated_build = remote ? build_location + '/' + language : File.join(build_location, language)
|
103
|
-
data_source = fetch_build(translated_build)
|
104
|
-
if data_target
|
105
|
-
data_target.deep_merge(data_source)
|
106
|
-
else
|
107
|
-
site.data[language] = data_source
|
108
|
-
end
|
109
|
-
end
|
110
|
-
# We move the language-specific translations to the
|
111
|
-
# site.data.translations location, where all translations are kept.
|
112
|
-
site.config['languages'].each do |language|
|
113
|
-
translation_target = site.data['translations'][language]
|
114
|
-
translation_source = site.data[language]['translations']
|
115
|
-
if translation_target
|
116
|
-
translation_target.deep_merge(translation_source)
|
117
|
-
else
|
118
|
-
site.data['translations'][language] = translation_source
|
119
|
-
end
|
120
|
-
end
|
121
|
-
# And there are some parts of the build that don't need to be translated
|
122
|
-
# and should be moved to the top level.
|
123
|
-
first_language = site.config['languages'][0]
|
124
|
-
site.data['reporting'] = site.data[first_language]['reporting']
|
125
|
-
site.data['schema'] = site.data[first_language]['schema']
|
126
|
-
site.data['zip'] = site.data[first_language]['zip']
|
127
|
-
else
|
128
|
-
# For untranslated builds, we download one build only, and place it
|
129
|
-
# in the "root" (so to speak) of site.data. Nothing else is needed.
|
130
|
-
target = site.data
|
131
|
-
source = fetch_build(build_location)
|
132
|
-
target.deep_merge(source)
|
133
|
-
end
|
134
|
-
|
135
|
-
# Finally support the deprecated 'remote_translations' option.
|
136
|
-
# This is deprecated because translations should now be in the
|
137
|
-
# data repository, where they will be fetched in fetch_build().
|
138
|
-
if site.config['remote_translations']
|
139
|
-
key = 'translations'
|
140
|
-
target = site.data[key]
|
141
|
-
site.config['remote_translations'].each do |endpoint|
|
142
|
-
begin
|
143
|
-
source = JSON.load(open(endpoint))
|
144
|
-
if target
|
145
|
-
target.deep_merge(source)
|
146
|
-
else
|
147
|
-
site.data[key] = source
|
148
|
-
end
|
149
|
-
rescue StandardError => e
|
150
|
-
puts e.message
|
151
|
-
abort 'Unable to fetch remote translation from: ' + endpoint
|
152
|
-
end
|
153
|
-
end
|
154
|
-
end
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
# This makes sure that the contents of the "local_data_folder" get copied
|
159
|
-
# into the Jekyll build, so that they can be served from the website.
|
160
|
-
Jekyll::Hooks.register :site, :post_write do |site|
|
161
|
-
if site.config['local_data_folder']
|
162
|
-
source = File.join(Dir.pwd, site.config['local_data_folder'], '.')
|
163
|
-
destination = site.config['destination']
|
164
|
-
FileUtils.cp_r(source, destination)
|
165
|
-
end
|
166
|
-
end
|
167
|
-
end
|
1
|
+
require "jekyll"
|
2
|
+
require 'json'
|
3
|
+
require 'deep_merge'
|
4
|
+
require 'open-uri'
|
5
|
+
require_relative "helpers"
|
6
|
+
|
7
|
+
module JekyllOpenSdgPlugins
|
8
|
+
class FetchRemoteData < Jekyll::Generator
|
9
|
+
safe true
|
10
|
+
priority :highest
|
11
|
+
|
12
|
+
# Fix a Unix path in case we are on Windows.
|
13
|
+
def fix_path(path)
|
14
|
+
path_parts = path.split('/')
|
15
|
+
return path_parts.join(File::SEPARATOR)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Our hardcoded list of pieces of the build that we expect.
|
19
|
+
def get_endpoints()
|
20
|
+
return {
|
21
|
+
'meta' => 'meta/all.json',
|
22
|
+
'headlines' => 'headline/all.json',
|
23
|
+
'schema' => 'meta/schema.json',
|
24
|
+
'reporting' => 'stats/reporting.json',
|
25
|
+
'translations' => 'translations/translations.json',
|
26
|
+
'zip' => 'zip/all_indicators.json'
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
# Is this path a remote path?
|
31
|
+
def is_path_remote(path)
|
32
|
+
return path.start_with?('http')
|
33
|
+
end
|
34
|
+
|
35
|
+
# Get a build from a local folder on disk or a remote URL on the Internet.
|
36
|
+
def fetch_build(path)
|
37
|
+
|
38
|
+
is_remote = is_path_remote(path)
|
39
|
+
build = {}
|
40
|
+
get_endpoints().each do |key, value|
|
41
|
+
endpoint = is_remote ? path + '/' + value : File.join(path, fix_path(value))
|
42
|
+
|
43
|
+
begin
|
44
|
+
json_file = is_remote ? open(endpoint) : File.open(endpoint)
|
45
|
+
build[key] = JSON.load(json_file)
|
46
|
+
rescue StandardError => e
|
47
|
+
# For backwards compatibility, we allow 'translations' to be missing.
|
48
|
+
if key != 'translations'
|
49
|
+
puts e.message
|
50
|
+
abort 'Unable to read data from: ' + endpoint
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
return build
|
56
|
+
end
|
57
|
+
|
58
|
+
# Predict (before data has been fetched) whether the site is using
|
59
|
+
# translated builds or not.
|
60
|
+
def site_uses_translated_builds(path)
|
61
|
+
|
62
|
+
is_remote = is_path_remote(path)
|
63
|
+
endpoints = get_endpoints()
|
64
|
+
# For a quick test, we just use 'meta'.
|
65
|
+
meta = endpoints['meta']
|
66
|
+
endpoint = is_remote ? path + '/' + meta : File.join(path, fix_path(meta))
|
67
|
+
|
68
|
+
begin
|
69
|
+
json_file = is_remote ? open(endpoint) : File.open(endpoint)
|
70
|
+
rescue StandardError => e
|
71
|
+
# If we didn't find an untranslated 'meta', we assume translated builds.
|
72
|
+
return true
|
73
|
+
end
|
74
|
+
|
75
|
+
# Other wise assume untranslated builds.
|
76
|
+
return false
|
77
|
+
end
|
78
|
+
|
79
|
+
def generate(site)
|
80
|
+
|
81
|
+
# For below, make sure there is at least an empty hash at
|
82
|
+
# site.data.translations.
|
83
|
+
if !site.data.has_key?('translations')
|
84
|
+
site.data['translations'] = {}
|
85
|
+
end
|
86
|
+
|
87
|
+
remote = site.config['remote_data_prefix']
|
88
|
+
local = site.config['local_data_folder']
|
89
|
+
|
90
|
+
if !remote && !local
|
91
|
+
abort 'Site config must include either "remote_data_prefix" or "local_data_folder".'
|
92
|
+
end
|
93
|
+
|
94
|
+
build_location = remote ? remote : File.join(Dir.pwd, local)
|
95
|
+
translated_builds = site_uses_translated_builds(build_location)
|
96
|
+
|
97
|
+
if translated_builds
|
98
|
+
# For translated builds, we get a build for each language, and
|
99
|
+
# place them in "subfolders" (so to speak) of site.data.
|
100
|
+
site.config['languages'].each do |language|
|
101
|
+
data_target = site.data[language]
|
102
|
+
translated_build = remote ? build_location + '/' + language : File.join(build_location, language)
|
103
|
+
data_source = fetch_build(translated_build)
|
104
|
+
if data_target
|
105
|
+
data_target.deep_merge(data_source)
|
106
|
+
else
|
107
|
+
site.data[language] = data_source
|
108
|
+
end
|
109
|
+
end
|
110
|
+
# We move the language-specific translations to the
|
111
|
+
# site.data.translations location, where all translations are kept.
|
112
|
+
site.config['languages'].each do |language|
|
113
|
+
translation_target = site.data['translations'][language]
|
114
|
+
translation_source = site.data[language]['translations']
|
115
|
+
if translation_target
|
116
|
+
translation_target.deep_merge(translation_source)
|
117
|
+
else
|
118
|
+
site.data['translations'][language] = translation_source
|
119
|
+
end
|
120
|
+
end
|
121
|
+
# And there are some parts of the build that don't need to be translated
|
122
|
+
# and should be moved to the top level.
|
123
|
+
first_language = site.config['languages'][0]
|
124
|
+
site.data['reporting'] = site.data[first_language]['reporting']
|
125
|
+
site.data['schema'] = site.data[first_language]['schema']
|
126
|
+
site.data['zip'] = site.data[first_language]['zip']
|
127
|
+
else
|
128
|
+
# For untranslated builds, we download one build only, and place it
|
129
|
+
# in the "root" (so to speak) of site.data. Nothing else is needed.
|
130
|
+
target = site.data
|
131
|
+
source = fetch_build(build_location)
|
132
|
+
target.deep_merge(source)
|
133
|
+
end
|
134
|
+
|
135
|
+
# Finally support the deprecated 'remote_translations' option.
|
136
|
+
# This is deprecated because translations should now be in the
|
137
|
+
# data repository, where they will be fetched in fetch_build().
|
138
|
+
if site.config['remote_translations']
|
139
|
+
key = 'translations'
|
140
|
+
target = site.data[key]
|
141
|
+
site.config['remote_translations'].each do |endpoint|
|
142
|
+
begin
|
143
|
+
source = JSON.load(open(endpoint))
|
144
|
+
if target
|
145
|
+
target.deep_merge(source)
|
146
|
+
else
|
147
|
+
site.data[key] = source
|
148
|
+
end
|
149
|
+
rescue StandardError => e
|
150
|
+
puts e.message
|
151
|
+
abort 'Unable to fetch remote translation from: ' + endpoint
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# This makes sure that the contents of the "local_data_folder" get copied
|
159
|
+
# into the Jekyll build, so that they can be served from the website.
|
160
|
+
Jekyll::Hooks.register :site, :post_write do |site|
|
161
|
+
if site.config['local_data_folder']
|
162
|
+
source = File.join(Dir.pwd, site.config['local_data_folder'], '.')
|
163
|
+
destination = site.config['destination']
|
164
|
+
FileUtils.cp_r(source, destination)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|