alula 0.2.3 → 0.4.0b

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.
Files changed (182) hide show
  1. data/.gitignore +2 -0
  2. data/Gemfile +7 -0
  3. data/Guardfile +9 -0
  4. data/Rakefile +12 -1
  5. data/VERSION +1 -1
  6. data/alula.gemspec +20 -4
  7. data/lib/alula/attachment_processor.rb +77 -0
  8. data/lib/alula/cdn.rb +30 -0
  9. data/lib/alula/cdns/edgecast.rb +16 -0
  10. data/lib/alula/cdns/hosts.rb +14 -0
  11. data/lib/alula/cli.rb +90 -39
  12. data/lib/alula/compressors.rb +22 -10
  13. data/lib/alula/config.rb +141 -0
  14. data/lib/alula/content.rb +113 -0
  15. data/lib/alula/contents/attachment.rb +8 -0
  16. data/lib/alula/contents/item.rb +409 -0
  17. data/lib/alula/contents/metadata.rb +73 -0
  18. data/lib/alula/contents/page.rb +9 -0
  19. data/lib/alula/contents/post.rb +32 -0
  20. data/lib/alula/context.rb +72 -0
  21. data/lib/alula/core_ext.rb +5 -0
  22. data/lib/alula/core_ext/environment.rb +20 -0
  23. data/lib/alula/core_ext/filter.rb +20 -0
  24. data/lib/alula/core_ext/filters/smilies.rb +36 -0
  25. data/lib/alula/core_ext/manifest.rb +30 -0
  26. data/lib/alula/core_ext/tag.rb +100 -0
  27. data/lib/alula/core_ext/tags/attachment.rb +28 -0
  28. data/lib/alula/core_ext/tags/blockquote.rb +21 -0
  29. data/lib/alula/core_ext/tags/image.rb +48 -0
  30. data/lib/alula/core_ext/tags/locale.rb +17 -0
  31. data/lib/alula/core_ext/tags/video.rb +103 -0
  32. data/lib/alula/generator.rb +31 -0
  33. data/lib/alula/generators/feedbuilder.rb +44 -0
  34. data/lib/alula/generators/paginate.rb +88 -0
  35. data/lib/alula/generators/sitemap.rb +26 -0
  36. data/lib/alula/helpers.rb +2 -0
  37. data/lib/alula/helpers/addons.rb +12 -0
  38. data/lib/alula/helpers/assets.rb +56 -0
  39. data/lib/alula/helpers/url_helpers.rb +16 -0
  40. data/lib/alula/plugin.rb +32 -0
  41. data/lib/alula/processor.rb +86 -0
  42. data/lib/alula/processors/dummy.rb +24 -0
  43. data/lib/alula/processors/image.rb +52 -0
  44. data/lib/alula/processors/magick.rb +83 -0
  45. data/lib/alula/processors/video.rb +97 -0
  46. data/lib/alula/processors/zencoder.rb +199 -0
  47. data/lib/alula/progress.rb +95 -0
  48. data/lib/alula/progressbar.rb +66 -0
  49. data/lib/alula/site.rb +331 -262
  50. data/lib/alula/storage.rb +46 -0
  51. data/lib/alula/storages/file_item.rb +43 -0
  52. data/lib/alula/storages/filestorage.rb +96 -0
  53. data/lib/alula/storages/item.rb +12 -0
  54. data/lib/alula/support/commonlogger.rb +30 -0
  55. data/lib/alula/theme.rb +70 -13
  56. data/lib/alula/theme/layout.rb +56 -0
  57. data/lib/alula/theme/view.rb +43 -0
  58. data/lib/alula/version.rb +1 -1
  59. data/locales/en.yml +9 -0
  60. data/locales/fi.yml +10 -0
  61. data/locales/l10n/ar.yml +199 -0
  62. data/locales/l10n/az.yml +199 -0
  63. data/locales/l10n/bg.yml +199 -0
  64. data/locales/l10n/bn-IN.yml +182 -0
  65. data/locales/l10n/bs.yml +242 -0
  66. data/locales/l10n/ca.yml +199 -0
  67. data/locales/l10n/cs.yml +198 -0
  68. data/locales/l10n/csb.yml +210 -0
  69. data/locales/l10n/cy.yml +199 -0
  70. data/locales/l10n/da.yml +199 -0
  71. data/locales/l10n/de-AT.yml +203 -0
  72. data/locales/l10n/de-CH.yml +203 -0
  73. data/locales/l10n/de.yml +203 -0
  74. data/locales/l10n/dsb.yml +215 -0
  75. data/locales/l10n/el.yml +199 -0
  76. data/locales/l10n/en-AU.yml +205 -0
  77. data/locales/l10n/en-CA.yml +214 -0
  78. data/locales/l10n/en-GB.yml +205 -0
  79. data/locales/l10n/en-IN.yml +205 -0
  80. data/locales/l10n/en-US.yml +205 -0
  81. data/locales/l10n/en.yml +205 -0
  82. data/locales/l10n/eo.yml +201 -0
  83. data/locales/l10n/es-AR.yml +205 -0
  84. data/locales/l10n/es-CL.yml +199 -0
  85. data/locales/l10n/es-CO.yml +205 -0
  86. data/locales/l10n/es-MX.yml +205 -0
  87. data/locales/l10n/es-PE.yml +181 -0
  88. data/locales/l10n/es-VE.yml +205 -0
  89. data/locales/l10n/es.yml +199 -0
  90. data/locales/l10n/et.yml +199 -0
  91. data/locales/l10n/eu.yml +199 -0
  92. data/locales/l10n/fa.yml +199 -0
  93. data/locales/l10n/fi.yml +199 -0
  94. data/locales/l10n/fr-CA.yml +207 -0
  95. data/locales/l10n/fr-CH.yml +207 -0
  96. data/locales/l10n/fr.yml +222 -0
  97. data/locales/l10n/fur.yml +199 -0
  98. data/locales/l10n/gl-ES.yml +178 -0
  99. data/locales/l10n/gsw-CH.yml +199 -0
  100. data/locales/l10n/he.yml +201 -0
  101. data/locales/l10n/hi-IN.yml +199 -0
  102. data/locales/l10n/hi.yml +199 -0
  103. data/locales/l10n/hr.yml +237 -0
  104. data/locales/l10n/hsb.yml +214 -0
  105. data/locales/l10n/hu.yml +199 -0
  106. data/locales/l10n/id.yml +200 -0
  107. data/locales/l10n/is.yml +213 -0
  108. data/locales/l10n/it.yml +205 -0
  109. data/locales/l10n/ja.yml +197 -0
  110. data/locales/l10n/kn.yml +199 -0
  111. data/locales/l10n/ko.yml +197 -0
  112. data/locales/l10n/lo.yml +186 -0
  113. data/locales/l10n/lt.yml +182 -0
  114. data/locales/l10n/lv.yml +215 -0
  115. data/locales/l10n/mk.yml +170 -0
  116. data/locales/l10n/mn.yml +205 -0
  117. data/locales/l10n/nb.yml +207 -0
  118. data/locales/l10n/nl.yml +199 -0
  119. data/locales/l10n/nn.yml +160 -0
  120. data/locales/l10n/pl.yml +221 -0
  121. data/locales/l10n/pt-BR.yml +207 -0
  122. data/locales/l10n/pt-PT.yml +207 -0
  123. data/locales/l10n/quotes.yml +24 -0
  124. data/locales/l10n/rm.yml +182 -0
  125. data/locales/l10n/ro.yml +199 -0
  126. data/locales/l10n/ru.yml +257 -0
  127. data/locales/l10n/sk.yml +213 -0
  128. data/locales/l10n/sl.yml +210 -0
  129. data/locales/l10n/sr-Latn.yml +170 -0
  130. data/locales/l10n/sr.yml +170 -0
  131. data/locales/l10n/sv-SE.yml +199 -0
  132. data/locales/l10n/sw.yml +197 -0
  133. data/locales/l10n/th.yml +173 -0
  134. data/locales/l10n/tl.yml +229 -0
  135. data/locales/l10n/tr.yml +199 -0
  136. data/locales/l10n/uk.yml +257 -0
  137. data/locales/l10n/vi.yml +201 -0
  138. data/locales/l10n/wo.yml +205 -0
  139. data/locales/l10n/zh-CN.yml +199 -0
  140. data/locales/l10n/zh-TW.yml +199 -0
  141. data/template/Gemfile.erb +14 -4
  142. data/template/README +16 -0
  143. data/template/config.yml.erb +42 -38
  144. data/test/fixtures/config_001_simple.yml +2 -0
  145. data/test/fixtures/config_002_l10n.yml +5 -0
  146. data/test/fixtures/pages/invalid-page.markdown +1 -0
  147. data/test/fixtures/pages/multilingual-page.markdown +20 -0
  148. data/test/fixtures/pages/section/subpage.markdown +5 -0
  149. data/test/fixtures/pages/simple-page.markdown +7 -0
  150. data/test/fixtures/posts/2012-07-02-invalid-post.markdown +1 -0
  151. data/test/fixtures/posts/2012-07-02-simple.markdown +7 -0
  152. data/test/fixtures/posts/2012-07-03-full-metadata.markdown +8 -0
  153. data/test/fixtures/posts/2012-07-03-multilingual-full-metadata.markdown +20 -0
  154. data/test/fixtures/theme/test/layouts/default.html.erb +1 -0
  155. data/test/fixtures/theme/test/views/page.html.erb +1 -0
  156. data/test/fixtures/theme/test/views/post.html.erb +1 -0
  157. data/test/minitest_helper.rb +14 -0
  158. data/test/test_config.rb +33 -0
  159. data/test/test_content.rb +30 -0
  160. data/test/test_metadata.rb +83 -0
  161. data/test/test_page.rb +81 -0
  162. data/test/test_post.rb +123 -0
  163. data/test/test_storage.rb +23 -0
  164. data/test/test_storage_file.rb +32 -0
  165. data/test/test_theme.rb +45 -0
  166. data/vendor/assets/images/favicon.png +0 -0
  167. data/vendor/assets/images/grey.gif +0 -0
  168. data/vendor/assets/javascripts/jquery.alula.js.coffee +16 -0
  169. data/vendor/{javascripts → assets/javascripts}/jquery.js +0 -0
  170. data/vendor/assets/javascripts/jquery.lazyload.js +210 -0
  171. data/vendor/assets/javascripts/lazyload.js.coffee +15 -0
  172. data/vendor/layouts/feed.xml.builder +19 -0
  173. data/vendor/layouts/sitemap.xml.builder +10 -0
  174. data/vendor/views/feed_post.html.haml +1 -0
  175. metadata +529 -50
  176. data/lib/alula.rb +0 -5
  177. data/lib/alula/assethelper.rb +0 -75
  178. data/lib/alula/plugins.rb +0 -23
  179. data/lib/alula/plugins/assets.rb +0 -82
  180. data/lib/alula/plugins/pagination.rb +0 -121
  181. data/lib/alula/rake_tasks.rb +0 -42
  182. data/lib/alula/tasks.rb +0 -2
@@ -0,0 +1,113 @@
1
+ require 'alula/contents/post'
2
+ require 'alula/contents/page'
3
+ require 'alula/contents/attachment'
4
+
5
+ module Alula
6
+ class Content
7
+ attr_reader :pages
8
+ attr_reader :posts
9
+ attr_reader :attachments
10
+ attr_reader :statics
11
+
12
+ def initialize(opts = {})
13
+ @site = opts.delete(:site)
14
+
15
+ @pages = []
16
+ @posts = []
17
+ @attachments = []
18
+ @statics = []
19
+ end
20
+
21
+ # Load our site content
22
+ def load
23
+ # Load everything we can have
24
+ read_content(:posts, :pages, :attachments, :statics)
25
+
26
+ # Generate our dynamic content (pages, categories, archives, etc. etc.)
27
+ generate_content
28
+ end
29
+
30
+ def by_name(name)
31
+ (self.pages + self.posts + self.attachments).each do |item|
32
+ return item if item.name == name
33
+ end
34
+ nil
35
+ end
36
+
37
+ def by_slug(slug)
38
+ (self.pages + self.posts + self.attachments).each do |item|
39
+ return item if item.slug == slug
40
+ end
41
+ nil
42
+ end
43
+
44
+ private
45
+ def read_content(*types)
46
+ @@lock = Mutex.new
47
+ # Load all posts if requested
48
+ if (types.include?(:posts))
49
+ # Read posts
50
+ @site.progress.create :load_posts, title: "Loading posts", total: @site.storage.posts.count
51
+ @site.progress.display
52
+ @site.storage.posts.each do |item|
53
+ name, entry = item
54
+ post = Post.load(item: entry, site: @site)
55
+ @posts << post unless post.nil?
56
+ @@lock.synchronize { @site.progress.step :load_posts }
57
+ end
58
+ # Sort
59
+ @posts.sort!.reverse!
60
+ @site.progress.finish :load_posts
61
+ end
62
+
63
+ # Load all pages if requested
64
+ if (types.include?(:pages))
65
+ @site.progress.create :load_pages, title: "Loading pages", total: @site.storage.pages.count
66
+ @site.progress.display
67
+
68
+ @site.storage.pages.each do |item|
69
+ name, entry = item
70
+ page = Page.load(item: entry, site: @site)
71
+ @pages << page unless page.nil?
72
+
73
+ @@lock.synchronize { @site.progress.step :load_pages }
74
+ end
75
+ @pages.sort!
76
+ @site.progress.finish :load_pages
77
+ end
78
+
79
+ # Load all pages if requested
80
+ if (types.include?(:attachments))
81
+ @site.progress.create :load_attachments, title: "Loading attachments", total: @site.storage.attachments.count
82
+ @site.progress.display
83
+
84
+ @site.storage.attachments.each do |item|
85
+ name, entry = item
86
+ attachment = Attachment.load(item: entry, site: @site)
87
+ @attachments << attachment unless attachment.nil?
88
+
89
+ @@lock.synchronize { @site.progress.step :load_attachments }
90
+ end
91
+
92
+ @site.progress.finish :load_attachments
93
+ end
94
+
95
+ # Load all statics
96
+ if (types.include?(:statics))
97
+ @site.storage.statics.each do |name, entry|
98
+ @statics << entry
99
+ end
100
+ end
101
+ end
102
+
103
+ def generate_content
104
+ @site.config.generators.each do |type, options|
105
+
106
+ generator = Alula::Generator.load(type: type, options: options, site: @site)
107
+ if generator
108
+ generator.generate
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,8 @@
1
+ require 'alula/contents/item'
2
+
3
+ module Alula
4
+ class Content
5
+ class Attachment < Item
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,409 @@
1
+ require 'alula/contents/metadata'
2
+ require 'alula/core_ext'
3
+ require 'liquid'
4
+ require 'kramdown'
5
+ require 'stringex'
6
+ require 'shellwords'
7
+ require 'htmlentities'
8
+
9
+ module Alula
10
+ class Content
11
+ class Item
12
+ # Instance variables, internal
13
+ # @source :: raw source from file, liquid + markdown
14
+ # @markdown :: parsed liquid, only markdown
15
+ # @body :: parsed markdown, raw content without layout
16
+ # @content :: rendered content, using view, without layout
17
+
18
+ # Metadata, contains all informative data for item
19
+ attr_reader :metadata
20
+
21
+ attr_reader :name
22
+ attr_reader :site
23
+
24
+ attr_accessor :navigation
25
+
26
+ def self.has_payload
27
+ class_variable_set(:@@payload, true)
28
+ end
29
+
30
+ def self.has_payload?
31
+ class_variable_defined?(:@@payload) && class_variable_get(:@@payload)
32
+ end
33
+
34
+ def has_payload?
35
+ self.class.has_payload?
36
+ end
37
+
38
+ def self.load(opts = {})
39
+ # Just return nil right away if we have no item
40
+ return nil if opts[:item].nil?
41
+
42
+ # Return nothing if file is not regular file
43
+ return nil unless opts[:item].exists?
44
+
45
+ # If payload is required by class and file doesn't contain it, just skip it
46
+ return nil if has_payload? && !opts[:item].has_payload?
47
+
48
+ # All ok
49
+ return self.new(opts)
50
+ end
51
+
52
+ def initialize(opts = {}, hooks = {})
53
+ @site = opts.delete(:site)
54
+ @item = opts.delete(:item)
55
+ @name = opts.delete(:name) || @item.name
56
+
57
+ # Set up method overrides
58
+ @hooks = hooks
59
+ # hooks.each do |name, impl|
60
+ # self.class.send(:define_method, name, &impl)
61
+ # end
62
+
63
+ @url = {}
64
+ @path = {}
65
+ @ids = {}
66
+ @navigation = {}
67
+ @substitutes = {}
68
+ @sidebar = {}
69
+
70
+ # Initialize content variables
71
+ flush
72
+
73
+ # Initialize metadata
74
+ @metadata = Metadata.new({
75
+ # Defaults
76
+ date: Time.new(0),
77
+ pin: 500, # Default sorting pin
78
+ layout: 'default',
79
+ view: (self.class.to_s == "Alula::Content::Page" ? "page" : "post"),
80
+
81
+ # Utilities
82
+ base_locale: @site.config.locale,
83
+ environment: @site.config.environment,
84
+
85
+ last_modified: _last_modified,
86
+ generator: nil,
87
+ }.merge(opts))
88
+
89
+ if /^((?<date>(?:\d+-\d+-\d+))-)?(?<slug>(?:.*))(?<extension>(?:\.[^.]+))$/ =~ @name
90
+ @metadata.date = Time.parse(date) unless date.nil? rescue @metadata.date
91
+ @metadata.slug = slug unless slug.nil?
92
+ @metadata.extension = extension unless extension.nil?
93
+ end
94
+
95
+ # If payload requested, read it
96
+ read_payload if has_payload?
97
+ end
98
+
99
+ # Functionality, existence
100
+ def exists?
101
+ @item.exists?
102
+ end
103
+
104
+ def extension
105
+ @item.extension
106
+ end
107
+
108
+ def filepath
109
+ @item.filepath
110
+ end
111
+
112
+ # Sorting
113
+ def <=>(other)
114
+ # Sort by date
115
+ cmp = self.date <=> other.date
116
+
117
+ # Sort by pinning, smaller marks higher in the list
118
+ cmp == 0 and cmp = self.metadata.pin <=> other.metadata.pin
119
+
120
+ # Sort by slug name alphabetically
121
+ cmp == 0 and cmp = self.slug <=> other.slug
122
+
123
+ cmp
124
+ end
125
+
126
+ # Renders actual content of itm using current layout
127
+ # This handles all locales automatically
128
+ def render(locale)
129
+ @content[locale] ||= begin
130
+ _old_locale = self.current_locale
131
+ self.current_locale = locale
132
+
133
+ # Flush if we have generator
134
+ if @hooks[:render]
135
+ instance_exec(locale, &@hooks[:render])
136
+ end
137
+
138
+ # Make sure our content is parsed
139
+ parse_liquid(locale)
140
+ parse_markdown(locale)
141
+
142
+ self.view.render(item: self, content: self.body(locale), locale: locale)
143
+ ensure
144
+ self.current_locale = _old_locale
145
+ end
146
+ end
147
+
148
+ def write
149
+ # Write all languages
150
+ languages = self.metadata.languages || [self.site.config.locale]
151
+
152
+ languages.each do |locale|
153
+ begin
154
+ _old_locale = self.current_locale
155
+ self.current_locale = locale
156
+
157
+ # Render our content
158
+ self.render(locale)
159
+
160
+ output = self.layout.render(item: self, locale: locale) do
161
+ self.content(locale)
162
+ end
163
+
164
+ # Filter output
165
+ self.site.filters.each do |name, filter|
166
+ output = filter.output(output, locale) if filter.respond_to?(:output)
167
+ end
168
+
169
+ # Write content to file
170
+ @site.storage.output_public(self.path(locale)) do
171
+ self.site.compressors.html.compress(output)
172
+ end
173
+ ensure
174
+ self.current_locale = _old_locale
175
+ end
176
+ end
177
+ end
178
+
179
+ # Layout engine
180
+ def layout
181
+ @layout ||= @site.theme.layout(self.metadata.layout)
182
+ end
183
+
184
+ def view
185
+ @view ||= @site.theme.view(self.metadata.view)
186
+ end
187
+
188
+ # Accessors
189
+ def current_locale
190
+ @@current_locale ||= nil
191
+ end
192
+
193
+ def current_locale=(newLocale)
194
+ @@current_locale = newLocale
195
+ end
196
+
197
+ def content(locale = nil)
198
+ @content[(locale || self.current_locale || self.site.config.locale)] ||= begin
199
+ render(locale)
200
+ end
201
+ end
202
+
203
+ def body(locale = nil)
204
+ @body[(locale || self.current_locale || self.site.config.locale)] ||= begin
205
+ parse_markdown(locale)
206
+ end
207
+ end
208
+
209
+ def markdown(locale = nil)
210
+ @markdown[(locale || self.current_locale || self.site.config.locale)] ||= begin
211
+ parse_liquid(locale)
212
+ end
213
+ end
214
+
215
+ def sidebar(locale = nil)
216
+ locale ||= self.current_locale || self.site.config.locale
217
+ @sidebar[locale] ||= begin
218
+ items = self.site.config.content.sidebar.collect do |item|
219
+ case item
220
+ when :pages
221
+ self.site.content.pages.select{|p| p.generator.nil? and p.languages.include?(locale) }
222
+ when :languages
223
+ languages
224
+ .reject{|lang| lang == locale}
225
+ .collect{|lang| Hashie::Mash.new({url: url(lang), title: I18n.t('language_name', locale: lang)}) }
226
+ else
227
+ @content.by_slug(self.config.index)
228
+ end
229
+ end
230
+ items.flatten.select {|i| !i.nil?}
231
+ end
232
+ end
233
+
234
+ def url(locale = nil)
235
+ locale ||= self.current_locale || self.site.config.locale
236
+ @url[locale] ||= begin
237
+ url = if @metadata.permalink(locale)
238
+ @metadata.permalink(locale)
239
+ else
240
+ template = @metadata.template || (self.class.to_s == "Alula::Content::Page" ? @site.config.pagelinks : @site.config.permalinks)
241
+ self.substitutes(locale).inject(template) { |result, token|
242
+ result.gsub(/:#{Regexp.escape token.first}/, token.last)
243
+ }.gsub(/\/\//, '/')
244
+ end
245
+ # Add .html only if we don't have extension already
246
+ if ::File.extname(url).empty?
247
+ url += ".html" unless url[/\/$/] and ::File.extname(url).empty?
248
+ end
249
+ url
250
+ end
251
+ end
252
+
253
+ def id(locale = nil)
254
+ @ids[(locale || self.current_locale || self.site.config.locale)] ||= self.url(locale)
255
+ .gsub(/\.\S+$/, '')
256
+ .gsub(/[\/]/, ' ')
257
+ .to_url
258
+ end
259
+
260
+ def path(locale = nil)
261
+ locale ||= self.current_locale || self.site.config.locale
262
+ @path[locale] ||= begin
263
+ path = ::File.join(CGI.unescape(self.url(locale)))
264
+ path = ::File.join(path, "index.html") unless path[/\/$/].nil?
265
+ path
266
+ end
267
+ end
268
+
269
+ def previous(locale = nil)
270
+ if @hooks[:previous]
271
+ instance_exec(locale, &@hooks[:previous])
272
+ end
273
+ end
274
+
275
+ def next(locale = nil)
276
+ if @hooks[:next]
277
+ instance_exec(locale, &@hooks[:next])
278
+ end
279
+ end
280
+
281
+ def navigation(locale = nil)
282
+ if @hooks[:navigation]
283
+ instance_exec(locale, &@hooks[:navigation])
284
+ end
285
+ end
286
+
287
+ #
288
+ # Resets all cached variables and languages etc.
289
+ def flush
290
+ flush_render
291
+ @markdown = {}
292
+ @body = {}
293
+ end
294
+
295
+ def flush_render
296
+ # Initialize current locale
297
+ self.current_locale ||= nil
298
+ @content = {}
299
+ end
300
+
301
+ # Proxy to metadata
302
+ def method_missing(meth, *args, &blk)
303
+ # Proxy to metadata
304
+ if !meth[/=$/] and metadata.respond_to?(meth)
305
+ # Invalidate some attributes depending on this
306
+ if %w{template name slug}.include?(meth[0..-2])
307
+ var = instance_variable_get("@#{meth[0..-2]}")
308
+ instance_variable_set("@#{meth[0..2]}", var.class.new)
309
+ end
310
+ args.unshift(self.current_locale || @site.config.locale) if args.empty?
311
+ metadata.send(meth, *args)
312
+ else
313
+ super
314
+ end
315
+ end
316
+
317
+ # Substitues for URL
318
+ def substitutes(locale = nil)
319
+ locale ||= self.current_locale || self.site.config.locale
320
+
321
+ @substitutes[locale] ||= begin
322
+ subs = {
323
+ "year" => @metadata.date.strftime('%Y'),
324
+ "month" => @metadata.date.strftime('%m'),
325
+ "day" => @metadata.date.strftime('%d'),
326
+ "locale" => (@site.config.locale == locale && @site.config.hides_base_locale ? "" : locale),
327
+ "name" => CGI.escape(name).gsub('%2F', '/'),
328
+ "slug" => CGI.escape(@metadata.slug(locale)).gsub('%2F', '/'),
329
+ "title" => @metadata.title(locale).to_url,
330
+ }
331
+ if self.metadata.generator
332
+ subs.merge!(self.metadata.generator.substitutes(locale, self))
333
+ end
334
+ subs
335
+ end
336
+ end
337
+
338
+ private
339
+ def _last_modified
340
+ if @hooks[:last_modified]
341
+ return instance_exec(locale, &@hooks[:next])
342
+ end
343
+
344
+ return unless self.class.to_s[/Page|Post/]
345
+ mtime = nil
346
+ unless @item.nil?
347
+ mtime = @item.mtime
348
+ if self.site.git
349
+ rev = %x{git rev-list -n 1 HEAD #{Shellwords.escape(@item.filepath)}}.strip
350
+ time = %x{git show --pretty=format:%ai --abbrev-commit #{rev}|head -1}.strip
351
+ mtime = Time.parse(time) rescue nil
352
+ end
353
+ end
354
+ mtime
355
+ end
356
+
357
+ def read_payload
358
+ # Do not read directly to instance variable as we know there is payload
359
+ return if @item.nil?
360
+
361
+ # source = File.read(@file)
362
+ source = @item.read
363
+ if /^(?<manifest>(?:---\s*\n.*?\n?)^(---\s*$\n?))(?<source>.*)/m =~ source
364
+ @source = source
365
+ @metadata.load(manifest)
366
+ end
367
+ end
368
+
369
+ def parse_liquid(locale)
370
+ @markdown[locale] ||= begin
371
+ begin
372
+ _old_locale = @site.context.locale
373
+ _old_item = @site.context.item
374
+ @site.context.locale = locale
375
+ @site.context.item = self
376
+ Liquid::Template.parse(@source).render(@site.context.to_liquid)
377
+ ensure
378
+ @site.context.locale = _old_locale
379
+ @site.context.item = _old_item
380
+ end
381
+ end
382
+ end
383
+
384
+ def parse_markdown(locale)
385
+ @body[locale] ||= begin
386
+ coder = HTMLEntities.new
387
+ quotes = %w{single.left single.right double.left double.right}.collect {|q|
388
+ coder.encode(I18n.t("quotes.#{q}", locale: locale), :decimal).gsub(/&#(\d+);/, '\1')
389
+ }
390
+
391
+ body = Kramdown::Document.new(markdown(locale), {
392
+ auto_ids: false,
393
+ footnote_nr: 1,
394
+ entity_output: 'as_char',
395
+ html_to_native: true,
396
+ toc_levels: '1..6',
397
+ smart_quotes: quotes,
398
+ }).to_html
399
+
400
+ self.site.filters.each do |name, filter|
401
+ body = filter.process(body, locale) if filter.respond_to?(:process)
402
+ end
403
+
404
+ body
405
+ end
406
+ end
407
+ end
408
+ end
409
+ end