fronde 0.3.4 → 0.5.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.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/bin/fronde +15 -30
  3. data/lib/ext/nil_time.rb +25 -0
  4. data/lib/ext/r18n.rb +37 -0
  5. data/lib/ext/time.rb +39 -0
  6. data/lib/ext/time_no_time.rb +23 -0
  7. data/lib/fronde/cli/commands.rb +97 -104
  8. data/lib/fronde/cli/data/Rakefile +8 -0
  9. data/lib/fronde/cli/data/config.yml +13 -0
  10. data/lib/fronde/cli/data/gitignore +6 -0
  11. data/lib/fronde/cli/data/zsh_completion +37 -0
  12. data/lib/fronde/cli/helpers.rb +55 -0
  13. data/lib/fronde/cli/opt_parse.rb +140 -0
  14. data/lib/fronde/cli/throbber.rb +110 -0
  15. data/lib/fronde/cli.rb +42 -42
  16. data/lib/fronde/config/data/org-config.el +25 -0
  17. data/lib/fronde/config/data/ox-fronde.el +158 -0
  18. data/lib/fronde/config/data/themes/umaneti/css/htmlize.css +364 -0
  19. data/lib/fronde/config/data/themes/umaneti/css/style.css +250 -0
  20. data/lib/fronde/config/data/themes/umaneti/img/bottom.png +0 -0
  21. data/lib/fronde/config/data/themes/umaneti/img/content.png +0 -0
  22. data/lib/fronde/config/data/themes/umaneti/img/tic.png +0 -0
  23. data/lib/fronde/config/data/themes/umaneti/img/top.png +0 -0
  24. data/lib/fronde/config/helpers.rb +62 -0
  25. data/lib/fronde/config/lisp.rb +80 -0
  26. data/lib/fronde/config.rb +148 -98
  27. data/lib/fronde/emacs.rb +23 -20
  28. data/lib/fronde/index/atom_generator.rb +55 -66
  29. data/lib/fronde/index/data/all_tags.org +19 -0
  30. data/lib/fronde/index/data/template.org +26 -0
  31. data/lib/fronde/index/data/template.xml +37 -0
  32. data/lib/fronde/index/org_generator.rb +72 -88
  33. data/lib/fronde/index.rb +57 -86
  34. data/lib/fronde/org/file.rb +299 -0
  35. data/lib/fronde/org/file_extracter.rb +101 -0
  36. data/lib/fronde/org.rb +105 -0
  37. data/lib/fronde/preview.rb +43 -39
  38. data/lib/fronde/slug.rb +54 -0
  39. data/lib/fronde/source/gemini.rb +34 -0
  40. data/lib/fronde/source/html.rb +67 -0
  41. data/lib/fronde/source.rb +209 -0
  42. data/lib/fronde/sync/neocities.rb +220 -0
  43. data/lib/fronde/sync/rsync.rb +46 -0
  44. data/lib/fronde/sync.rb +32 -0
  45. data/lib/fronde/templater.rb +101 -71
  46. data/lib/fronde/version.rb +1 -1
  47. data/lib/tasks/cli.rake +33 -0
  48. data/lib/tasks/org.rake +58 -43
  49. data/lib/tasks/site.rake +66 -31
  50. data/lib/tasks/sync.rake +37 -40
  51. data/lib/tasks/tags.rake +11 -7
  52. data/locales/en.yml +61 -14
  53. data/locales/fr.yml +69 -14
  54. metadata +77 -95
  55. data/lib/fronde/config/lisp_config.rb +0 -340
  56. data/lib/fronde/config/org-config.el +0 -19
  57. data/lib/fronde/config/ox-fronde.el +0 -121
  58. data/lib/fronde/org_file/class_methods.rb +0 -72
  59. data/lib/fronde/org_file/extracter.rb +0 -72
  60. data/lib/fronde/org_file/htmlizer.rb +0 -43
  61. data/lib/fronde/org_file.rb +0 -298
  62. data/lib/fronde/utils.rb +0 -229
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'open-uri'
5
+ require_relative '../version'
6
+ require_relative '../org'
7
+ require_relative 'helpers'
8
+
9
+ require_relative '../../ext/r18n'
10
+ using R18nPatch
11
+
12
+ module Fronde
13
+ module Config
14
+ # This module contains utilitary methods to ease ~org-config.el~
15
+ # file generation
16
+ module Lisp
17
+ # Generate emacs lisp configuration file for Org and write it.
18
+ #
19
+ # This method saves the generated configuration in the file
20
+ # ~org-config.el~ at the root of your project, overwriting it if it
21
+ # existed already.
22
+ #
23
+ # @return [Integer] the length written (as returned by the
24
+ # underlying ~File.write~ method call)
25
+ def write_org_lisp_config
26
+ workdir = Dir.pwd
27
+ all_projects = sources.map(&:org_config).flatten
28
+ all_themes = org_generate_themes(all_projects)
29
+ FileUtils.mkdir_p "#{workdir}/var/lib"
30
+ content = Helpers.render_liquid_template(
31
+ File.read(File.expand_path('./data/org-config.el', __dir__)),
32
+ 'version' => Fronde::VERSION,
33
+ 'work_dir' => workdir,
34
+ 'fronde_data_dir' => File.expand_path('data', __dir__),
35
+ 'org_version' => Fronde::Org.current_version,
36
+ 'long_date_fmt' => R18n.t.full_datetime_format.to_s,
37
+ 'author' => { 'email' => get('author_email', ''),
38
+ 'name' => get('author') },
39
+ 'domain' => get('domain'),
40
+ 'all_projects' => all_projects + all_themes
41
+ )
42
+ File.write("#{workdir}/var/lib/org-config.el", content)
43
+ end
44
+
45
+ private
46
+
47
+ def theme_directory(theme)
48
+ # User theme first to allow overwriting
49
+ directory = File.expand_path("themes/#{theme}")
50
+ return directory if Dir.exist? directory
51
+
52
+ directory = File.expand_path("data/themes/#{theme}", __dir__)
53
+ return directory if Dir.exist? directory
54
+
55
+ raise Errno::ENOENT, "Theme #{theme} not found"
56
+ end
57
+
58
+ def org_theme_config(theme)
59
+ { 'base-directory' => theme_directory(theme),
60
+ # rubocop:disable Layout/LineLength
61
+ 'base-extension' => %w[css js gif jpg png svg otf ttf woff2?].join('\\\\|'),
62
+ 'publishing-directory' => "#{get('html_public_folder')}/assets/#{theme}",
63
+ # rubocop:enable Layout/LineLength
64
+ 'publishing-function' => 'org-publish-attachment',
65
+ 'recursive' => true }
66
+ end
67
+
68
+ def org_generate_themes(projects)
69
+ all_themes = projects.filter_map { |project| project['theme'] }
70
+ all_themes << get('theme')
71
+ all_themes.uniq.compact.filter_map do |theme|
72
+ next if theme == 'default'
73
+
74
+ { 'name' => "theme-#{theme}",
75
+ 'attributes' => org_theme_config(theme) }
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
data/lib/fronde/config.rb CHANGED
@@ -1,44 +1,62 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'yaml'
4
- require 'fronde/config/lisp_config'
4
+ require 'r18n-core'
5
+ require 'singleton'
6
+
7
+ require_relative 'config/lisp'
8
+ require_relative 'source'
5
9
 
6
10
  module Fronde
7
- # Wrapper for configuration
8
- #
9
- # This class is a singleton interface, which share the static website
10
- # being build settings among different steps or tasks.
11
- #
12
- # It expects the website author to holds their custom settings in a
13
- # YAML file named ~config.yml~ available at the root of their
14
- # project.
15
- #
16
- # For example, with the given config file:
17
- #
18
- # #+begin_src
19
- # ---
20
- # title: My website
21
- # author: Alice Doe
22
- # #+end_src
23
- #
24
- # Settings will be available like this:
25
- #
26
- # #+begin_src
27
- # Fronde::Config.get('author')
28
- # => "Alice Doe"
29
- # #+end_src
30
- class Config
31
- extend Fronde::LispConfig
32
-
33
- class << self
11
+ module Config
12
+ # Wrapper for configuration
13
+ #
14
+ # This class is a singleton interface, which share the static website
15
+ # being build settings among different steps or tasks.
16
+ #
17
+ # It expects the website author to holds their custom settings in a
18
+ # YAML file named ~config.yml~ available at the root of their
19
+ # project.
20
+ #
21
+ # For example, with the given config file:
22
+ #
23
+ # #+begin_src
24
+ # ---
25
+ # title: My website
26
+ # author: Alice Doe
27
+ # #+end_src
28
+ #
29
+ # Settings will be available like this:
30
+ #
31
+ # #+begin_src
32
+ # Fronde::CONFIG.get('author')
33
+ # => "Alice Doe"
34
+ # #+end_src
35
+ class Store
36
+ include Singleton
37
+
38
+ attr_reader :sources
39
+
40
+ def initialize
41
+ @default_settings = {
42
+ 'author' => ENV['USER'] || '',
43
+ 'domain' => '',
44
+ 'lang' => Fronde::Config::Helpers.extract_lang_from_env('en'),
45
+ 'html_public_folder' => 'public_html',
46
+ 'gemini_public_folder' => 'public_gmi',
47
+ 'templates' => [], 'theme' => 'default'
48
+ }.freeze
49
+ # Do not load sources now to avoid dependency loop on config
50
+ @sources = nil
51
+ @config = load_settings
52
+ end
53
+
54
+ include Fronde::Config::Lisp
55
+
34
56
  # Access the current website settings
35
57
  #
36
- # If necessary, this method will load settings from a config
37
- # file.
38
- #
39
58
  # @return [Hash] the website settings
40
59
  def settings
41
- load_settings
42
60
  @config
43
61
  end
44
62
 
@@ -56,7 +74,6 @@ module Fronde
56
74
  # @param default the default value to use if ~setting~ is absent
57
75
  # @return the setting value or nil
58
76
  def get(setting, default = nil)
59
- load_settings
60
77
  if setting.is_a? Array
61
78
  value = @config.dig(*setting)
62
79
  else
@@ -65,31 +82,6 @@ module Fronde
65
82
  value || default
66
83
  end
67
84
 
68
- # Save the settings given as a parameter to the ~config.yml~ file.
69
- #
70
- # Not only this method overwrite the old settings, but it replace
71
- # the current shared settings with the ones given in
72
- # parameter. Later call to
73
- # {file:Fronde/Config.html#settings-class_method settings}
74
- # will, obviously, use these new settings.
75
- #
76
- # @param new_config [Hash] the settings to save
77
- # @return [Hash] the new settings after save
78
- def save(new_config)
79
- # Do not save obvious default config values. We'll always try to
80
- # save author and lang as they default on system variables,
81
- # which may be different from a system to another. Thus it may
82
- # be confusing if one use fronde on two different computer and
83
- # these params always change.
84
- default_keys = default_settings.keys
85
- new_config.delete_if do |k, v|
86
- default_keys.include?(k) && v == default_settings[k]
87
- end
88
- File.write 'config.yml', new_config.to_yaml
89
- @config = @sources = nil
90
- load_settings # Reload config, taking default settings into account
91
- end
92
-
93
85
  # Reset settings
94
86
  #
95
87
  # This method is handy for testing purpose. Next call to
@@ -99,7 +91,10 @@ module Fronde
99
91
  #
100
92
  # @return nil
101
93
  def reset
102
- @sources = @config = nil
94
+ # Reload config, taking default settings into account
95
+ @config = load_settings
96
+ @sources = nil
97
+ @sources = load_sources
103
98
  end
104
99
 
105
100
  # Load the given settings as if they comes from the ~config.yml~ file.
@@ -111,68 +106,123 @@ module Fronde
111
106
  # use these new settings.
112
107
  #
113
108
  # @param config [Hash] the settings to artificially load
114
- # @return [Hash] the new settings
109
+ # @return [Fronde::Config::Store] self
115
110
  def load_test(config)
116
- reset
117
- @config = default_settings.merge config
118
- sources
119
- @config
111
+ @config = @default_settings.merge config
112
+ @sources = nil
113
+ @sources = load_sources
114
+ self
120
115
  end
121
116
 
122
117
  # Return the qualified projects sources list.
123
118
  #
124
119
  # @return [Array] the fully qualified projects sources list
125
- def sources
120
+ def load_sources
126
121
  return @sources if @sources
127
- load_settings
128
- default_sources = [{ 'path' => 'src', 'target' => '.' }]
129
- @sources = get('sources', default_sources).map do |s|
130
- build_source(s)
131
- end.compact
122
+
123
+ @sources = remove_inclusion(remove_duplicate(build_sources))
132
124
  end
133
125
 
134
126
  private
135
127
 
136
128
  def load_settings
137
- return @config if @config
138
129
  conf_file = 'config.yml'
130
+ user_conf = {}
139
131
  if File.exist? conf_file
140
- @config = default_settings.merge(YAML.load_file(conf_file)).freeze
141
- else
142
- @config = default_settings
132
+ user_conf = YAML.load_file(conf_file)
133
+ user_conf = Fronde::Config::Helpers.migrate(user_conf)
143
134
  end
135
+ user_conf = @default_settings.merge(user_conf)
136
+ Fronde::Config::Helpers.ensure_expanded_paths(user_conf).freeze
144
137
  end
145
138
 
146
- def extract_lang_from_env(default)
147
- (ENV['LANG'] || default).split('_', 2).first
139
+ def build_sources
140
+ default_sources = [{ 'path' => 'src', 'target' => '.' }]
141
+ get('sources', default_sources).filter_map do |source_conf|
142
+ config = Source.canonical_config source_conf.dup
143
+ unless config['path']
144
+ warn R18n.t.fronde.error.source.no_path(source: config.inspect)
145
+ next
146
+ end
147
+ Source.new_from_config config
148
+ end
148
149
  end
149
150
 
150
- def default_settings
151
- return @default_settings if @default_settings
152
- @default_settings = {
153
- 'author' => (ENV['USER'] || ''),
154
- 'domain' => '',
155
- 'lang' => extract_lang_from_env('en'),
156
- 'public_folder' => 'public_html',
157
- 'templates' => [],
158
- 'theme' => 'default'
159
- }.freeze
151
+ def check_duplicate_and_warn(collection, source, type)
152
+ path = source['path']
153
+ return path unless collection[type].has_key?(path)
154
+
155
+ warn(
156
+ R18n.t.fronde.error.source.duplicate(
157
+ source: source['name'], type: type
158
+ )
159
+ )
160
+ end
161
+
162
+ def remove_duplicate(sources)
163
+ check_paths = {}
164
+ sources.each do |source|
165
+ type = source.type
166
+ check_paths[type] ||= {}
167
+ path = check_duplicate_and_warn check_paths, source, type
168
+ # Avoid duplicate
169
+ next unless path
170
+
171
+ check_paths[type][path] = source
172
+ end
173
+ check_paths
174
+ end
175
+
176
+ def filter_possible_matchs(path, other_paths_list)
177
+ other_paths_list.select do |other_path|
178
+ path != other_path && other_path.start_with?(path)
179
+ end
160
180
  end
161
181
 
162
- def build_source(seed)
163
- opts = { 'recursive' => true, 'is_blog' => false }
164
- case seed
165
- when String
166
- opts['path'] = seed
167
- when Hash
168
- opts.merge! seed
182
+ def warn_on_existing_inclusion(type, other, possible_matchs, sources)
183
+ possible_matchs.each do |match|
184
+ warn(
185
+ R18n.t.fronde.error.source.inclusion(
186
+ source: sources[match]['title'],
187
+ other_source: other, type: type
188
+ )
189
+ )
169
190
  end
170
- return nil unless opts.has_key?('path')
171
- opts['path'] = File.expand_path(opts['path'])
172
- opts['name'] ||= File.basename(opts['path']).sub(/^\./, '')
173
- opts['target'] ||= opts['name']
174
- opts
191
+ end
192
+
193
+ def remove_inclusion(check_paths)
194
+ check_paths.map do |type, sources_by_path|
195
+ skip_paths = []
196
+
197
+ # Check paths in the right order
198
+ sorted_paths = sources_by_path.keys.sort_by(&:length)
199
+ sorted_paths.filter_map do |path|
200
+ next if skip_paths.include?(path)
201
+
202
+ source = sources_by_path[path]
203
+ # If current source is not recursive, there is no possible
204
+ # issue
205
+ next source unless source.recursive?
206
+
207
+ # Ensure that the current source does not embed another one
208
+ possible_matchs = filter_possible_matchs path, sorted_paths
209
+ next source if possible_matchs.empty?
210
+
211
+ skip_paths += possible_matchs
212
+ warn_on_existing_inclusion(
213
+ type, source['title'], possible_matchs, sources_by_path
214
+ )
215
+ end
216
+ end.flatten
175
217
  end
176
218
  end
177
219
  end
220
+
221
+ CONFIG = Config::Store.instance
178
222
  end
223
+
224
+ R18n.default_places = File.expand_path('../../locales', __dir__)
225
+ R18n::Filters.on(:named_variables)
226
+ R18n.set Fronde::CONFIG.get('lang')
227
+
228
+ Fronde::CONFIG.load_sources
data/lib/fronde/emacs.rb CHANGED
@@ -1,37 +1,40 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'fronde/config'
3
+ require_relative 'config'
4
4
 
5
5
  module Fronde
6
6
  # Wraps Gnu/Emacs calls
7
7
  class Emacs
8
- def initialize(file_path: nil, verbose: false)
9
- @file = file_path
8
+ def initialize(verbose: false)
10
9
  @verbose = verbose
10
+ @command = nil
11
11
  end
12
12
 
13
- def publish
14
- command = emacs_command(
15
- '-l ./var/lib/org-config.el', '--eval \'(org-publish "website")\''
16
- )
17
- if @verbose
18
- warn command
19
- return system(command, exception: true)
20
- end
21
- system command, out: '/dev/null', err: '/dev/null', exception: true
13
+ def publish(project = 'website')
14
+ build_command("(org-publish \"#{project}\")")
15
+ run_command
22
16
  end
23
17
 
24
18
  private
25
19
 
26
- def emacs_command(*arguments)
27
- default_emacs = Fronde::Config.get('emacs')
28
- emacs_cmd = [
29
- default_emacs || 'emacs -Q --batch -nw',
30
- '--eval \'(setq enable-dir-local-variables nil)\''
20
+ def run_command
21
+ cmd = @command.join(' ')
22
+ if @verbose
23
+ warn cmd
24
+ return system(cmd, exception: true)
25
+ end
26
+ system cmd, out: File::NULL, err: File::NULL, exception: true
27
+ end
28
+
29
+ def build_command(org_action)
30
+ default_emacs = Fronde::CONFIG.get('emacs')
31
+ @command = [default_emacs || 'emacs -Q --batch -nw']
32
+ @command << '--eval \'(setq inhibit-message t)\'' unless @verbose
33
+ @command += [
34
+ '--eval \'(setq enable-dir-local-variables nil)\'',
35
+ '-l ./var/lib/org-config.el',
36
+ "--eval '#{org_action}'"
31
37
  ]
32
- emacs_cmd << '--eval \'(setq inhibit-message t)\'' unless @verbose
33
- emacs_cmd.concat(arguments)
34
- emacs_cmd.join(' ')
35
38
  end
36
39
  end
37
40
  end
@@ -1,85 +1,74 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'cgi'
4
- require 'fronde/config'
3
+ require_relative '../config'
5
4
 
6
5
  module Fronde
7
- # Embeds Atom feeds sepecific methods
8
- module IndexAtomGenerator
6
+ # Reopen Index class to embed Atom feeds sepecific methods
7
+ class Index
9
8
  def to_atom(index_name = 'index')
10
- content = [atom_header(index_name)]
11
- @index[index_name][0...10].each do |article|
12
- content << atom_entry(article)
13
- end
14
- format '%<content>s</feed>', content: content.join("\n")
9
+ entries = @index[index_name][0...10].map(&:to_h)
10
+ return atom_index(entries) if index_name == 'index'
11
+
12
+ atom_file(index_name, entries)
15
13
  end
16
14
 
17
15
  def write_atom(index_name)
18
- return unless save?
19
- slug = Fronde::OrgFile.slug index_name
20
- FileUtils.mkdir_p "#{@pubdir}/feeds"
21
- atomdest = "#{@pubdir}/feeds/#{slug}.xml"
22
- File.write(atomdest, to_atom(index_name))
16
+ slug = Slug.slug index_name
17
+ atomdest = "#{@project.publication_path}/feeds/#{slug}.xml"
18
+ File.write atomdest, to_atom(index_name)
19
+ end
20
+
21
+ def write_all_feeds(verbose: true)
22
+ FileUtils.mkdir_p "#{@project.publication_path}/feeds"
23
+ @index.each_key do |tag|
24
+ write_atom(tag)
25
+ warn R18n.t.fronde.index.atom_generated(tag: tag) if verbose
26
+ end
23
27
  end
24
28
 
25
29
  private
26
30
 
27
- # Render the Atom feed header.
31
+ # Render an Atom feed file.
28
32
  #
29
- # @param title [String] the title of the current atom feed
30
- # @return [String] the Atom header as a String
31
- def atom_header(title)
32
- domain = Fronde::Config.get('domain')
33
- upddate = @date.rfc3339
34
- if title == 'index'
35
- slug = 'index'
36
- tagurl = domain
37
- title = Fronde::Config.get('title', R18n.t.fronde.index.all_tags)
38
- else
39
- slug = Fronde::OrgFile.slug(title)
40
- tagurl = "#{domain}/tags/#{slug}.html"
41
- title = @tags_names[title]
42
- end
43
- title = CGI.escapeHTML(title)
44
- <<~ENDATOM
45
- <?xml version="1.0" encoding="utf-8"?>
46
- <feed xmlns="http://www.w3.org/2005/Atom"
47
- xmlns:dc="http://purl.org/dc/elements/1.1/"
48
- xmlns:wfw="http://wellformedweb.org/CommentAPI/"
49
- xml:lang="#{Fronde::Config.get('lang')}">
50
-
51
- <title>#{title}</title>
52
- <link href="#{domain}/feeds/#{slug}.xml" rel="self" type="application/atom+xml"/>
53
- <link href="#{tagurl}" rel="alternate" type="text/html" title="#{title}"/>
54
- <updated>#{upddate}</updated>
55
- <author><name>#{Fronde::Config.get('author', '')}</name></author>
56
- <id>urn:md5:#{Digest::MD5.hexdigest(domain)}</id>
57
- <generator uri="https://git.umaneti.net/fronde/about/">Fronde</generator>
58
- ENDATOM
33
+ # @param tag_name [String] the tag name of the current atom feed
34
+ # @param entries [Array] the article to list in this file
35
+ # @return [String] the Atom feed as a String
36
+ def atom_file(tag_name, entries)
37
+ domain = Fronde::CONFIG.get('domain')
38
+ slug = Slug.slug(tag_name)
39
+ tagurl = "#{domain}#{@project.public_absolute_path}tags/#{slug}.html"
40
+ Config::Helpers.render_liquid_template(
41
+ File.read(File.expand_path('./data/template.xml', __dir__)),
42
+ 'title' => @tags_names[tag_name],
43
+ 'lang' => Fronde::CONFIG.get('lang'),
44
+ 'domain' => domain,
45
+ 'slug' => slug,
46
+ 'tagurl' => tagurl,
47
+ 'upddate' => @date.xmlschema,
48
+ 'author' => Fronde::CONFIG.get('author'),
49
+ 'publication_format' => @project['mime_type'],
50
+ 'entries' => entries
51
+ )
59
52
  end
60
53
 
61
- # Render an Atom feed entry.
54
+ # Render the main/index Atom feed.
62
55
  #
63
- # @param article [Fronde::OrgFile] the related org document for this
64
- # entry
65
- # @return [String] the Atom entry as a String
66
- def atom_entry(article)
67
- keywords = article.keywords.map do |k|
68
- "<dc:subject>#{CGI.escapeHTML(k)}</dc:subject>"
69
- end.join
70
- keywords += "\n " if keywords != ''
71
- title = CGI.escapeHTML(article.title)
72
- <<~ENDENTRY
73
- <entry>
74
- <title>#{title}</title>
75
- <link href="#{article.url}" rel="alternate" type="text/html"
76
- title="#{title}"/>
77
- <id>urn:md5:#{Digest::MD5.hexdigest(article.timekey)}</id>
78
- <published>#{article.datestring(:rfc3339)}</published>
79
- <author><name>#{CGI.escapeHTML(article.author)}</name></author>
80
- #{keywords}<content type="html">#{CGI.escapeHTML(article.excerpt)}</content>
81
- </entry>
82
- ENDENTRY
56
+ # @param entries [Array] the article to list in this file
57
+ # @return [String] the Atom feed as a String
58
+ def atom_index(entries)
59
+ domain = Fronde::CONFIG.get('domain')
60
+ Config::Helpers.render_liquid_template(
61
+ File.read(File.expand_path('./data/template.xml', __dir__)),
62
+ 'title' => @project['title'],
63
+ 'lang' => Fronde::CONFIG.get('lang'),
64
+ 'domain' => domain,
65
+ 'slug' => 'index',
66
+ 'tagurl' => domain,
67
+ 'upddate' => @date.xmlschema,
68
+ 'author' => Fronde::CONFIG.get('author'),
69
+ 'publication_format' => @project['mime_type'],
70
+ 'entries' => entries
71
+ )
83
72
  end
84
73
  end
85
74
  end
@@ -0,0 +1,19 @@
1
+ #+title: {{ title }}
2
+ #+author: {{ author }}
3
+ #+language: {{ lang }}
4
+ {% for index in indexes %}
5
+ * {{ index.title }}
6
+ :PROPERTIES:
7
+ {%- if project_type == 'html' %}
8
+ :HTML_CONTAINER_CLASS: index-tags{% endif %}
9
+ :UNNUMBERED: notoc
10
+ :END:
11
+
12
+ {% for tag in index.tags -%}
13
+ {%- if project_type == 'gemini' -%}
14
+ [[{{ domain }}{{ project_path }}tags/{{ tag.slug }}.gmi][{{ tag.title }} ({{ tag.weight }})]]
15
+ {%- else -%}
16
+ - [[{{ domain }}{{ project_path }}tags/{{ tag.slug }}.html][{{ tag.title }}]] ({{ tag.weight }})
17
+ {%- endif %}
18
+ {% endfor %}
19
+ {%- endfor -%}
@@ -0,0 +1,26 @@
1
+ #+title: {{ title }}
2
+ #+author: {{ author }}
3
+ #+language: {{ lang }}
4
+ {%- if project_type == 'html' and slug != '__HOME_PAGE__' %}
5
+ #+html_head_extra: <link rel="alternate" type="application/atom+xml" title="{{ title }}" href="{{ domain }}{{ project_path }}feeds/{{ slug }}.xml" />
6
+ {% endif -%}
7
+ {%- assign last_year = 0 -%}
8
+ {% for article in entries %}
9
+ {% assign cur_year = article.timekey | slice: 0, 4 %}
10
+ {%- unless cur_year == last_year %}
11
+ * {% if cur_year == "0000" %}{{ unsorted }}{% else %}{{ cur_year }}{% endif %}
12
+ :PROPERTIES:
13
+ {%- if project_type == 'html' %}
14
+ :HTML_CONTAINER_CLASS: index-year{% endif %}
15
+ :UNNUMBERED: notoc
16
+ :END:
17
+ {% assign last_year = cur_year %}
18
+ {% endunless -%}
19
+ {%- if project_type == 'gemini' -%}
20
+ [[{{ article.url }}][{% if article.published != '' %}{{ article.published_gemini_index }} {% endif %}{{ article.title }}]]
21
+ {%- else -%}
22
+ - *[[{{ article.url }}][{{ article.title }}]]*
23
+ {%- if article.published != '' %} / {{ article.published }}{% endif -%}
24
+ {%- if article.excerpt != '' %} \\
25
+ {{ article.excerpt }}{% endif %}{% endif %}
26
+ {%- endfor %}