kontent-jekyll 0.11.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.
@@ -0,0 +1,12 @@
1
+ module Kentico
2
+ module Kontent
3
+ module SiteProcessing
4
+ class CustomSiteProcessor
5
+ def self.for(config)
6
+ class_name = config.custom_site_processor
7
+ class_name && Module.const_get(class_name).new
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,88 @@
1
+ require 'kontent-delivery-sdk-ruby'
2
+
3
+ require 'kontent-jekyll/resolvers/content_link_resolver'
4
+ require 'kontent-jekyll/resolvers/inline_content_item_resolver'
5
+
6
+ require 'kontent-jekyll/constants/kentico_config_keys'
7
+
8
+ require 'kontent-jekyll/version'
9
+ require 'kontent-jekyll/gem_name'
10
+
11
+ module Kentico
12
+ module Kontent
13
+ module SiteProcessing
14
+ include Constants
15
+ include Resolvers
16
+
17
+ class KenticoKontentImporter
18
+ def initialize(config)
19
+ @config = config
20
+ @items = []
21
+ @taxonomy_groups = []
22
+ end
23
+
24
+ def items_by_type(language)
25
+ retrieve_items(language)
26
+ .group_by { |item| item.system.type }
27
+ end
28
+
29
+ def taxonomies
30
+ @taxonomy_groups = retrieve_taxonomies
31
+ end
32
+
33
+ private
34
+
35
+ def inline_content_item_resolver
36
+ @inline_content_item_resolver ||= Resolvers::InlineContentItemResolver.for(@config)
37
+ end
38
+
39
+ def content_link_url_resolver
40
+ @content_link_url_resolver ||= Resolvers::ContentLinkResolver.for(@config)
41
+ end
42
+
43
+ def delivery_client
44
+ project_id = value_for(@config, Constants::KenticoConfigKeys::PROJECT_ID)
45
+ secure_key = value_for(@config, Constants::KenticoConfigKeys::SECURE_KEY)
46
+
47
+ Kentico::Kontent::Delivery::DeliveryClient.new(
48
+ project_id: project_id,
49
+ secure_key: secure_key,
50
+ content_link_url_resolver: content_link_url_resolver,
51
+ inline_content_item_resolver: inline_content_item_resolver
52
+ )
53
+ end
54
+
55
+ def retrieve_taxonomies
56
+ delivery_client
57
+ .taxonomies
58
+ .custom_headers(custom_headers)
59
+ .request_latest_content
60
+ .execute { |response| return response.taxonomies }
61
+ end
62
+
63
+ def retrieve_items(language)
64
+ client = delivery_client.items
65
+ client = client.language(language) if language
66
+ client
67
+ .custom_headers(custom_headers)
68
+ .request_latest_content
69
+ .depth(@config.max_linked_items_depth || 1)
70
+ .execute { |response| return response.items }
71
+ end
72
+
73
+ def value_for(config, key)
74
+ potential_value = config[key]
75
+ return ENV[potential_value.gsub('ENV_', '')] if !potential_value.nil? && potential_value.start_with?('ENV_')
76
+ potential_value
77
+ end
78
+
79
+ # Add extra headers like tracking
80
+ def custom_headers
81
+ {
82
+ 'X-KC-SOURCE' => "#{GEM_NAME} #{VERSION}",
83
+ }
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,249 @@
1
+ require 'date'
2
+
3
+ require 'kontent-jekyll/constants/page_type'
4
+
5
+ require 'kontent-jekyll/resolvers/front_matter_resolver'
6
+ require 'kontent-jekyll/resolvers/content_resolver'
7
+ require 'kontent-jekyll/resolvers/data_resolver'
8
+ require 'kontent-jekyll/resolvers/filename_resolver'
9
+
10
+ require 'kontent-jekyll/utils/normalize_object'
11
+
12
+ module Kentico
13
+ module Kontent
14
+ module SiteProcessing
15
+ ##
16
+ # This class processes the the imported content and populate Jekyll structures.
17
+
18
+ class SiteProcessor
19
+ include Constants
20
+ include Resolvers
21
+ include Utils
22
+
23
+ ##
24
+ # These collections have specific purposes in the original Jekyll generation will be omitted.
25
+
26
+ RESERVED_COLLECTIONS = %w(posts data)
27
+
28
+ def initialize(site, config)
29
+ @site = site
30
+ @config = config
31
+ end
32
+
33
+ ##
34
+ # Populates standard Jekyll pages and collections
35
+
36
+ def process_pages(items_by_type)
37
+ pages_config = @config.pages
38
+ return unless pages_config
39
+
40
+ pages_config.each_pair do |type, page_config|
41
+ pages = items_by_type[type.to_s]
42
+ next unless pages
43
+
44
+ collection_name = page_config&.collection
45
+
46
+ pages_data = []
47
+ pages.each do |page_item|
48
+ content = content_resolver.execute(page_item, page_config)
49
+ front_matter = front_matter_resolver.execute(page_item, PageType::PAGE)
50
+ front_matter = normalize_object(front_matter)
51
+
52
+ mapped_name = filename_resolver.execute(page_item)
53
+ filename = "#{mapped_name}.html"
54
+
55
+ page_data = OpenStruct.new(content: content, collection: collection_name, front_matter: front_matter, filename: filename)
56
+ pages_data << page_data
57
+ end
58
+
59
+ are_pages_from_collection = collection_name && !collection_name.empty? && !RESERVED_COLLECTIONS.include?(collection_name)
60
+
61
+ unless are_pages_from_collection
62
+ @site.pages += pages_data.map { |page_data| create_kentico_page(@site, page_data) }
63
+ next
64
+ end
65
+
66
+ collection_config = @site.config['collections'][collection_name]
67
+ if collection_config
68
+ collection_config['output'] = true unless defined?(collection_config['output'])
69
+ else
70
+ @site.config['collections'][collection_name] = { 'output' => true }
71
+ end
72
+
73
+ collection = @site.collections[collection_name] ||= Jekyll::Collection.new(@site, collection_name)
74
+
75
+ pages_data.each do |page_data|
76
+ path = File.join(@site.source, "_#{collection_name}", page_data.filename)
77
+
78
+ page = create_document(path, @site, collection, page_data)
79
+
80
+ page.instance_eval 'merge_defaults'
81
+ page.instance_eval 'read_post_data'
82
+
83
+ collection.docs << page
84
+ end
85
+
86
+ collection.docs = collection.docs.reverse.uniq(&:path).reverse
87
+ collection.docs.sort!
88
+ end
89
+
90
+ @site.pages = @site.pages.reverse.uniq(&:path).reverse
91
+ @site.pages.sort_by!(&:name)
92
+ end
93
+
94
+ ##
95
+ # Populates posts part of the Jekyll site
96
+
97
+ def process_posts(items_by_type)
98
+ posts_config = @config.posts
99
+ return unless posts_config
100
+
101
+ type = posts_config&.type
102
+
103
+ posts = items_by_type[type.to_s]
104
+ return unless posts
105
+
106
+ posts.each do |post_item|
107
+ content = content_resolver.execute(post_item, posts_config)
108
+ front_matter = front_matter_resolver.execute(post_item, PageType::POST)
109
+ front_matter = normalize_object(front_matter)
110
+
111
+ date = post_item.elements[posts_config.date || 'date'].value
112
+ date_string = DateTime.parse(date).strftime('%Y-%m-%d')
113
+
114
+ mapped_name = filename_resolver.execute(post_item)
115
+ filename = "#{date_string}-#{mapped_name}.html"
116
+
117
+ post_data = OpenStruct.new(content: content, front_matter: front_matter, filename: filename)
118
+
119
+ path = File.join(@site.source, '_posts', filename)
120
+ post = create_document(path, @site, @site.posts, post_data)
121
+
122
+ ##
123
+ # We need to invoke these private methods as they correctly populate certain data automatically.
124
+
125
+ post.instance_eval 'merge_defaults'
126
+ post.instance_eval 'read_post_data'
127
+
128
+ @site.posts.docs << post
129
+ end
130
+
131
+ @site.posts.docs = @site.posts.docs.reverse.uniq(&:path).reverse
132
+ @site.posts.docs.sort!
133
+ end
134
+
135
+ ##
136
+ # Populates data part of the Jekyll site.
137
+
138
+ def process_data(items_by_type)
139
+ config = @config.data
140
+
141
+ data_items = {}
142
+ config && config.each_pair do |item_type, name|
143
+ items = items_by_type[item_type.to_s]
144
+ next unless items
145
+
146
+ name ||= item_type.to_s
147
+ processed_items = items.map do |item|
148
+ data = data_resolver.execute(item)
149
+ normalize_object(data)
150
+ end
151
+
152
+ data_items[name] = (data_items[name] || []) + processed_items
153
+ end
154
+
155
+ @site.data.merge!({ 'items' => data_items })
156
+ end
157
+
158
+ ##
159
+ # Populates data part of the Jekyll site with taxonomies.
160
+
161
+ def process_taxonomies(taxonomies)
162
+ codenames = @config.taxonomies
163
+ return unless codenames && taxonomies
164
+
165
+ filtered_taxonomies = taxonomies.select { |taxonomy| codenames.include? taxonomy.system.codename }
166
+
167
+ processed_taxonomies = {}
168
+ filtered_taxonomies.each do |taxonomy|
169
+ taxonomy_data = normalize_object({
170
+ system: taxonomy.system,
171
+ terms: taxonomy.terms,
172
+ })
173
+
174
+ processed_taxonomies[taxonomy.system.codename] = taxonomy_data
175
+ end
176
+
177
+ @site.data['taxonomies'] = processed_taxonomies
178
+ end
179
+
180
+ private
181
+
182
+ ##
183
+ # Creates a Jekyll::Page.
184
+
185
+ def create_kentico_page(site, page_info)
186
+ page = Jekyll::Page.allocate
187
+
188
+ ## A hack to create a Jekyll::Page with custom constructor without overriding the class
189
+ # because jekyll-redirect-from can work only with Jekyll::Page instances.
190
+ # Once this PR https://github.com/jekyll/jekyll-redirect-from/pull/204 is merged and released
191
+ # we can create a subclass of the Page and simplify the code.
192
+
193
+ page.define_singleton_method(:initialize) do
194
+ @site = site
195
+ @base = site.source
196
+ @dir = ''
197
+ @name = page_info.filename
198
+ @path = if site.in_theme_dir(@base) == @base
199
+ site.in_theme_dir(@base, @dir, @name)
200
+ else
201
+ site.in_source_dir(@base, @dir, @name)
202
+ end
203
+
204
+ self.process(@name)
205
+
206
+ self.data = page_info.front_matter
207
+ self.content = page_info.content
208
+
209
+ data.default_proc = proc do |_, key|
210
+ site.frontmatter_defaults.find(File.join(@dir, @name), type, key)
211
+ end
212
+
213
+ Jekyll::Hooks.trigger :pages, :post_init, self
214
+
215
+ self
216
+ end
217
+
218
+ page.initialize
219
+ end
220
+
221
+ ##
222
+ # Creates a Jekyll::Document. Used for collections.
223
+
224
+ def create_document(path, site, collection, source)
225
+ doc = Jekyll::Document.new(path, site: site, collection: collection)
226
+ doc.content = source.content
227
+ doc.data.merge!(source.front_matter)
228
+ doc
229
+ end
230
+
231
+ def content_resolver
232
+ @content_resolver ||= Resolvers::ContentResolver.new(@config)
233
+ end
234
+
235
+ def filename_resolver
236
+ @filename_resolver ||= Resolvers::FilenameResolver.new(@config)
237
+ end
238
+
239
+ def front_matter_resolver
240
+ @front_matter_resolver ||= Resolvers::FrontMatterResolver.new(@config)
241
+ end
242
+
243
+ def data_resolver
244
+ @data_resolver ||= Resolvers::DataResolver.new(@config)
245
+ end
246
+ end
247
+ end
248
+ end
249
+ end
@@ -0,0 +1,88 @@
1
+ module Kentico
2
+ module Kontent
3
+ module Utils
4
+ ##
5
+ # Transforms any object into easily seriazible format.
6
+ # OpenStruct is converted to hash and Symbol keys are transformed to string keys.
7
+
8
+ def normalize_object(object)
9
+ stringify_all_keys(to_hash(object))
10
+ end
11
+
12
+ private
13
+
14
+ def open_struct_values_to_hash(struct)
15
+ hash = {}
16
+ struct.each_pair do |key, value|
17
+ hash[key] = to_hash(value)
18
+ end
19
+ hash
20
+ end
21
+
22
+ def hash_values_to_hash(hash)
23
+ hash.reduce({}) do |reduced, pair|
24
+ key = pair[0]
25
+ value = pair[1]
26
+ new_pair = { key => to_hash(value) }
27
+ reduced.merge(new_pair)
28
+ end
29
+ end
30
+
31
+ def to_hash(object)
32
+ case object
33
+ when OpenStruct
34
+ open_struct_values_to_hash object
35
+ when Array
36
+ array_values_to_hash object
37
+ when Hash
38
+ hash_values_to_hash object
39
+ else
40
+ object
41
+ end
42
+ end
43
+
44
+ def stringify_all_keys_in_array(array)
45
+ array.map do |item|
46
+ case item
47
+ when Hash then stringify_all_keys_in_hash item
48
+ when Array then stringify_all_keys_in_array item
49
+ else item end
50
+ end
51
+ end
52
+
53
+ def stringify_all_keys_in_hash(hash)
54
+ stringified_hash = {}
55
+
56
+ hash.each do |k, v|
57
+ stringified_hash[k.to_s] =
58
+ case v
59
+ when Array then stringify_all_keys_in_array v
60
+ when Hash then stringify_all_keys_in_hash v
61
+ else v
62
+ end
63
+ end
64
+
65
+ stringified_hash
66
+ end
67
+
68
+ def stringify_all_keys(object)
69
+ case object
70
+ when Hash then stringify_all_keys_in_hash object
71
+ when Array then stringify_all_keys_in_array object
72
+ else object
73
+ end
74
+ end
75
+
76
+ def array_values_to_hash(array)
77
+ array.map do |item|
78
+ case item
79
+ when Array then array_values_to_hash item
80
+ when Hash then hash_values_to_hash item
81
+ when OpenStruct then open_struct_values_to_hash item
82
+ else item
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,5 @@
1
+ module Kentico
2
+ module Kontent
3
+ VERSION = '0.11.0'
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,170 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kontent-jekyll
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.11.0
5
+ platform: ruby
6
+ authors:
7
+ - RadoslavK
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-12-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rack-jekyll
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: '0.5'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '='
67
+ - !ruby/object:Gem::Version
68
+ version: '0.5'
69
+ - !ruby/object:Gem::Dependency
70
+ name: capybara
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '='
74
+ - !ruby/object:Gem::Version
75
+ version: 3.29.0
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '='
81
+ - !ruby/object:Gem::Version
82
+ version: 3.29.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: jekyll
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '='
88
+ - !ruby/object:Gem::Version
89
+ version: 3.8.6
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '='
95
+ - !ruby/object:Gem::Version
96
+ version: 3.8.6
97
+ - !ruby/object:Gem::Dependency
98
+ name: kontent-delivery-sdk-ruby
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '2.0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '2.0'
111
+ description: Kontent Jekyll is utilizing Kentico Kontent, headless CMS, as a content
112
+ repository and integrates it with Jekyll static site generator. You can generate
113
+ posts, pages, collections and data items.
114
+ email:
115
+ - RadoslavK@kentico.com
116
+ executables: []
117
+ extensions: []
118
+ extra_rdoc_files: []
119
+ files:
120
+ - ".gitignore"
121
+ - ".rspec"
122
+ - Gemfile
123
+ - Gemfile.lock
124
+ - LICENSE
125
+ - README.md
126
+ - Rakefile
127
+ - bin/console
128
+ - bin/setup
129
+ - kontent-jekyll.gemspec
130
+ - lib/kontent-jekyll.rb
131
+ - lib/kontent-jekyll/constants/item_element_type.rb
132
+ - lib/kontent-jekyll/constants/kentico_config_keys.rb
133
+ - lib/kontent-jekyll/constants/page_type.rb
134
+ - lib/kontent-jekyll/gem_name.rb
135
+ - lib/kontent-jekyll/generator.rb
136
+ - lib/kontent-jekyll/resolvers/content_link_resolver.rb
137
+ - lib/kontent-jekyll/resolvers/content_resolver.rb
138
+ - lib/kontent-jekyll/resolvers/data_resolver.rb
139
+ - lib/kontent-jekyll/resolvers/filename_resolver.rb
140
+ - lib/kontent-jekyll/resolvers/front_matter_resolver.rb
141
+ - lib/kontent-jekyll/resolvers/inline_content_item_resolver.rb
142
+ - lib/kontent-jekyll/site_processing/custom_site_processor.rb
143
+ - lib/kontent-jekyll/site_processing/kentico_kontent_importer.rb
144
+ - lib/kontent-jekyll/site_processing/site_processor.rb
145
+ - lib/kontent-jekyll/utils/normalize_object.rb
146
+ - lib/kontent-jekyll/version.rb
147
+ homepage: https://github.com/RadoslavK/kontent-jekyll
148
+ licenses:
149
+ - MIT
150
+ metadata: {}
151
+ post_install_message:
152
+ rdoc_options: []
153
+ require_paths:
154
+ - lib
155
+ required_ruby_version: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: 2.6.3
160
+ required_rubygems_version: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - ">="
163
+ - !ruby/object:Gem::Version
164
+ version: '0'
165
+ requirements: []
166
+ rubygems_version: 3.0.3
167
+ signing_key:
168
+ specification_version: 4
169
+ summary: Kentico Kontent plugin for Jekyll
170
+ test_files: []