fronde 0.3.4 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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 %}