fronde 0.3.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 +7 -0
- data/LICENSE +14 -0
- data/bin/fronde +45 -0
- data/lib/fronde/cli.rb +60 -0
- data/lib/fronde/cli/commands.rb +115 -0
- data/lib/fronde/config.rb +137 -0
- data/lib/fronde/config/lisp_config.rb +269 -0
- data/lib/fronde/config/org-config.el +17 -0
- data/lib/fronde/config/ox-fronde.el +114 -0
- data/lib/fronde/emacs.rb +37 -0
- data/lib/fronde/index.rb +130 -0
- data/lib/fronde/index/atom_generator.rb +86 -0
- data/lib/fronde/index/org_generator.rb +114 -0
- data/lib/fronde/org_file.rb +301 -0
- data/lib/fronde/org_file/class_methods.rb +72 -0
- data/lib/fronde/org_file/extracter.rb +72 -0
- data/lib/fronde/org_file/htmlizer.rb +43 -0
- data/lib/fronde/preview.rb +55 -0
- data/lib/fronde/templater.rb +118 -0
- data/lib/fronde/utils.rb +214 -0
- data/lib/fronde/version.rb +6 -0
- data/lib/tasks/org.rake +96 -0
- data/lib/tasks/site.rake +55 -0
- data/lib/tasks/sync.rake +34 -0
- data/lib/tasks/tags.rake +19 -0
- data/locales/en.yml +37 -0
- data/locales/fr.yml +37 -0
- metadata +267 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
;; Add org-mode to load path
|
2
|
+
(add-to-list 'load-path (expand-file-name "org-__ORG_VER__/lisp" "__WORK_DIR__/lib"))
|
3
|
+
;; Load last version of htmlize.el
|
4
|
+
(load-file (expand-file-name "htmlize.el" "__WORK_DIR__/lib"))
|
5
|
+
|
6
|
+
;; Current project options
|
7
|
+
(setq fronde/version "__VERSION__"
|
8
|
+
fronde/current-work-dir "__WORK_DIR__"
|
9
|
+
user-mail-address "__AUTHOR_EMAIL__"
|
10
|
+
user-full-name "__AUTHOR_NAME__"
|
11
|
+
org-html-metadata-timestamp-format "__LONG_DATE_FMT__"
|
12
|
+
org-publish-project-alist
|
13
|
+
`(__ALL_PROJECTS____THEME_CONFIG__
|
14
|
+
("website" :components (__ALL_PROJECTS_NAMES__))))
|
15
|
+
|
16
|
+
;; Load fronde lib
|
17
|
+
(load-file (expand-file-name "ox-fronde.el" "__FRONDE_DIR__"))
|
@@ -0,0 +1,114 @@
|
|
1
|
+
;;; ox-fronde.el --- Fronde Gem specific helpers for Org Export Engine -*- lexical-binding: t; -*-
|
2
|
+
|
3
|
+
;; Copyright (C) 2011-2019 Free Software Foundation, Inc.
|
4
|
+
|
5
|
+
;; Author: Étienne Deparis <etienne at depar dot is>
|
6
|
+
;; Keywords: org, export
|
7
|
+
|
8
|
+
;; This file is not part of GNU Emacs.
|
9
|
+
|
10
|
+
;; GNU Emacs is free software: you can redistribute it and/or modify
|
11
|
+
;; it under the terms of the GNU General Public License as published by
|
12
|
+
;; the Free Software Foundation, either version 3 of the License, or
|
13
|
+
;; (at your option) any later version.
|
14
|
+
|
15
|
+
;; GNU Emacs is distributed in the hope that it will be useful,
|
16
|
+
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
17
|
+
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
18
|
+
;; GNU General Public License for more details.
|
19
|
+
|
20
|
+
;; You should have received a copy of the GNU General Public License
|
21
|
+
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
22
|
+
|
23
|
+
;;; Commentary:
|
24
|
+
|
25
|
+
;; This library implements specific helpers function, needed by the Ruby
|
26
|
+
;; Gem Fronde, which offers an easy way to publish a static website
|
27
|
+
;; using Org files as sources.
|
28
|
+
|
29
|
+
;;; Code:
|
30
|
+
|
31
|
+
(require 'org)
|
32
|
+
(require 'ox-html)
|
33
|
+
|
34
|
+
;;; Function Declarations
|
35
|
+
|
36
|
+
(defvar fronde/version ""
|
37
|
+
"Version of the current fronde installation")
|
38
|
+
|
39
|
+
(defvar fronde/current-work-dir nil
|
40
|
+
"Location of the current fronde website base directory.")
|
41
|
+
|
42
|
+
(defvar fronde/org-temp-dir nil
|
43
|
+
"Location of the local Org temporary directory (where to place
|
44
|
+
org timestamps and id locations).")
|
45
|
+
|
46
|
+
(defun fronde/org-html-format-spec (upstream info)
|
47
|
+
"Return format specification for preamble and postamble.
|
48
|
+
INFO is a plist used as a communication channel."
|
49
|
+
(let ((output (funcall upstream info)))
|
50
|
+
(push `(?A . ,(format "<span class=\"author\">%s</span>"
|
51
|
+
(org-export-data (plist-get info :author) info)))
|
52
|
+
output)
|
53
|
+
(push `(?k . ,(org-export-data (plist-get info :keywords) info)) output)
|
54
|
+
(push `(?K . ,(format "<ul class=\"keywords-list\">%s</ul>"
|
55
|
+
(mapconcat
|
56
|
+
(lambda (k) (format "<li class=\"keyword\">%s</li>" k))
|
57
|
+
(split-string (or (plist-get info :keywords) "") ",+ *")
|
58
|
+
"\n")))
|
59
|
+
output)
|
60
|
+
(push `(?l . ,(org-export-data (plist-get info :language) info)) output)
|
61
|
+
(push `(?n . ,(format "Fronde %s" fronde/version)) output)
|
62
|
+
(push `(?N . ,(format "<a href=\"https://git.umaneti.net/fronde/about/\">Fronde</a> %s" fronde/version)) output)
|
63
|
+
(push `(?x . ,(org-export-data (plist-get info :description) info)) output)
|
64
|
+
(push `(?X . ,(format "<p>%s</p>"
|
65
|
+
(org-export-data (plist-get info :description) info)))
|
66
|
+
output)))
|
67
|
+
|
68
|
+
(defun fronde/org-i18n-export (link description format)
|
69
|
+
"Export a i18n link"
|
70
|
+
(let* ((splitted-link (split-string link "|"))
|
71
|
+
(path (car splitted-link))
|
72
|
+
(desc (or description path))
|
73
|
+
(lang (cadr splitted-link)))
|
74
|
+
(pcase format
|
75
|
+
(`html (if lang
|
76
|
+
(format "<a href=\"%s\" hreflang=\"%s\">%s</a>"
|
77
|
+
path lang desc)
|
78
|
+
(format "<a href=\"%s\">%s</a>" path desc)))
|
79
|
+
(`latex (format "\\href{%s}{%s}" path desc))
|
80
|
+
(`ascii (format "%s (%s)" desc path))
|
81
|
+
(_ path))))
|
82
|
+
|
83
|
+
(defun fronde/org-i18n-follow (link)
|
84
|
+
"Visit a i18n link"
|
85
|
+
(browse-url (car (split-string link "|"))))
|
86
|
+
|
87
|
+
(org-link-set-parameters "i18n"
|
88
|
+
:export #'fronde/org-i18n-export
|
89
|
+
:follow #'fronde/org-i18n-follow)
|
90
|
+
|
91
|
+
|
92
|
+
;;; Set configuration options
|
93
|
+
|
94
|
+
(setq fronde/org-temp-dir (expand-file-name "var/tmp" fronde/current-work-dir)
|
95
|
+
org-publish-timestamp-directory (expand-file-name "timestamps/" fronde/org-temp-dir)
|
96
|
+
org-id-locations-file (expand-file-name "id-locations.el" fronde/org-temp-dir)
|
97
|
+
make-backup-files nil
|
98
|
+
enable-local-variables :all
|
99
|
+
org-confirm-babel-evaluate nil
|
100
|
+
org-export-with-broken-links t
|
101
|
+
org-html-doctype "html5"
|
102
|
+
org-html-html5-fancy t
|
103
|
+
org-html-htmlize-output-type 'css
|
104
|
+
org-html-text-markup-alist '((bold . "<strong>%s</strong>")
|
105
|
+
(code . "<code>%s</code>")
|
106
|
+
(italic . "<em>%s</em>")
|
107
|
+
(strike-through . "<del>%s</del>")
|
108
|
+
(underline . "<span class=\"underline\">%s</span>")
|
109
|
+
(verbatim . "<code>%s</code>")))
|
110
|
+
(advice-add 'org-html-format-spec :around #'fronde/org-html-format-spec)
|
111
|
+
|
112
|
+
(provide 'ox-fronde)
|
113
|
+
|
114
|
+
;;; ox-fronde.el ends here
|
data/lib/fronde/emacs.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fronde/config'
|
4
|
+
|
5
|
+
module Fronde
|
6
|
+
# Wraps Gnu/Emacs calls
|
7
|
+
class Emacs
|
8
|
+
def initialize(file_path: nil, verbose: false)
|
9
|
+
@file = file_path
|
10
|
+
@verbose = verbose
|
11
|
+
end
|
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
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def emacs_command(*arguments)
|
27
|
+
default_emacs = Fronde::Config.settings['emacs']
|
28
|
+
emacs_cmd = [
|
29
|
+
default_emacs || 'emacs -Q --batch -nw',
|
30
|
+
'--eval \'(setq enable-dir-local-variables nil)\''
|
31
|
+
]
|
32
|
+
emacs_cmd << '--eval \'(setq inhibit-message t)\'' unless @verbose
|
33
|
+
emacs_cmd.concat(arguments)
|
34
|
+
emacs_cmd.join(' ')
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/fronde/index.rb
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
require 'digest/md5'
|
5
|
+
require 'fronde/config'
|
6
|
+
require 'fronde/org_file'
|
7
|
+
require 'fronde/index/atom_generator'
|
8
|
+
require 'fronde/index/org_generator'
|
9
|
+
|
10
|
+
module Fronde
|
11
|
+
# Generates website indexes and atom feeds for all the org documents
|
12
|
+
# keywords.
|
13
|
+
class Index
|
14
|
+
attr_reader :date
|
15
|
+
|
16
|
+
include Fronde::IndexAtomGenerator
|
17
|
+
include Fronde::IndexOrgGenerator
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
@pubdir = Fronde::Config.settings['public_folder']
|
21
|
+
@index = { 'index' => [] }
|
22
|
+
@projects = {}
|
23
|
+
@tags_names = {}
|
24
|
+
@date = DateTime.now
|
25
|
+
feed
|
26
|
+
sort!
|
27
|
+
end
|
28
|
+
|
29
|
+
def entries
|
30
|
+
@index.keys.reject { |k| k == 'index' }
|
31
|
+
end
|
32
|
+
|
33
|
+
def empty?
|
34
|
+
@index['index'].empty?
|
35
|
+
end
|
36
|
+
|
37
|
+
def write_all(verbose: true)
|
38
|
+
@index.each_key do |k|
|
39
|
+
write_org(k)
|
40
|
+
warn "Generated index file for #{k}" if verbose
|
41
|
+
write_atom(k)
|
42
|
+
warn "Generated atom feed for #{k}" if verbose
|
43
|
+
end
|
44
|
+
write_all_blog_home(verbose)
|
45
|
+
end
|
46
|
+
|
47
|
+
def sort_by(kind)
|
48
|
+
if [:name, :weight].include?(kind)
|
49
|
+
tags_sorted = sort_tags_by_name_and_weight["by_#{kind}".to_sym]
|
50
|
+
# Reverse in order to have most important or A near next prompt
|
51
|
+
# and avoid to scroll to find the beginning of the list.
|
52
|
+
return tags_sorted.map do |k|
|
53
|
+
@tags_names[k] + " (#{@index[k].length})"
|
54
|
+
end.reverse
|
55
|
+
end
|
56
|
+
raise ArgumentError, "#{kind} not in [:name, :weight]"
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def feed
|
62
|
+
Fronde::Config.sources.each do |project|
|
63
|
+
next unless project['is_blog']
|
64
|
+
if project['recursive']
|
65
|
+
file_pattern = '**/*.org'
|
66
|
+
else
|
67
|
+
file_pattern = '*.org'
|
68
|
+
end
|
69
|
+
Dir.glob(file_pattern, base: project['path']).map do |s|
|
70
|
+
org_file = File.join(project['path'], s)
|
71
|
+
next if exclude_file?(org_file, project)
|
72
|
+
add_to_indexes(
|
73
|
+
Fronde::OrgFile.new(org_file, project: project)
|
74
|
+
)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def add_to_project_index(article)
|
80
|
+
project = article.project
|
81
|
+
@projects[project['name']] ||= []
|
82
|
+
@projects[project['name']] << article
|
83
|
+
end
|
84
|
+
|
85
|
+
def add_to_indexes(article)
|
86
|
+
@index['index'] << article
|
87
|
+
add_to_project_index article
|
88
|
+
article.keywords.each do |k|
|
89
|
+
slug = Fronde::OrgFile.slug k
|
90
|
+
@tags_names[slug] = k # Overwrite is permitted
|
91
|
+
@index[slug] ||= []
|
92
|
+
@index[slug] << article
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def sort!
|
97
|
+
@index.each do |k, i|
|
98
|
+
@index[k] = i.sort { |a, b| b.timekey <=> a.timekey }
|
99
|
+
end
|
100
|
+
@projects.each do |k, i|
|
101
|
+
@projects[k] = i.sort { |a, b| b.timekey <=> a.timekey }
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def sort_tags_by_name_and_weight
|
106
|
+
tags_sorted = {}
|
107
|
+
all_keys = entries
|
108
|
+
tags_sorted[:by_name] = all_keys.sort
|
109
|
+
tags_sorted[:by_weight] = all_keys.sort do |a, b|
|
110
|
+
@index[b].length <=> @index[a].length
|
111
|
+
end
|
112
|
+
tags_sorted
|
113
|
+
end
|
114
|
+
|
115
|
+
def exclude_file?(file_path, project)
|
116
|
+
# Obviously excluding index itself for blogs
|
117
|
+
return true if file_path == File.join(project['path'], 'index.org')
|
118
|
+
return false unless project['exclude']
|
119
|
+
file_path.match? project['exclude']
|
120
|
+
end
|
121
|
+
|
122
|
+
def save?
|
123
|
+
return true unless empty?
|
124
|
+
Fronde::Config.sources.each do |project|
|
125
|
+
return true if project['is_blog'] && Dir.exist?(project['path'])
|
126
|
+
end
|
127
|
+
false
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cgi'
|
4
|
+
require 'fronde/config'
|
5
|
+
|
6
|
+
module Fronde
|
7
|
+
# Embeds Atom feeds sepecific methods
|
8
|
+
module IndexAtomGenerator
|
9
|
+
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")
|
15
|
+
end
|
16
|
+
|
17
|
+
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
|
+
IO.write(atomdest, to_atom(index_name))
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
# Render the Atom feed header.
|
28
|
+
#
|
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.settings['domain']
|
33
|
+
upddate = @date.rfc3339
|
34
|
+
if title == 'index'
|
35
|
+
slug = 'index'
|
36
|
+
tagurl = domain
|
37
|
+
title = Fronde::Config.settings['title'] || \
|
38
|
+
R18n.t.fronde.index.all_tags
|
39
|
+
else
|
40
|
+
slug = Fronde::OrgFile.slug(title)
|
41
|
+
tagurl = "#{domain}/tags/#{slug}.html"
|
42
|
+
title = @tags_names[title]
|
43
|
+
end
|
44
|
+
title = CGI.escapeHTML(title)
|
45
|
+
<<~ENDATOM
|
46
|
+
<?xml version="1.0" encoding="utf-8"?>
|
47
|
+
<feed xmlns="http://www.w3.org/2005/Atom"
|
48
|
+
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
49
|
+
xmlns:wfw="http://wellformedweb.org/CommentAPI/"
|
50
|
+
xml:lang="#{Fronde::Config.settings['lang']}">
|
51
|
+
|
52
|
+
<title>#{title}</title>
|
53
|
+
<link href="#{domain}/feeds/#{slug}.xml" rel="self" type="application/atom+xml"/>
|
54
|
+
<link href="#{tagurl}" rel="alternate" type="text/html" title="#{title}"/>
|
55
|
+
<updated>#{upddate}</updated>
|
56
|
+
<author><name>#{Fronde::Config.settings['author'] || ''}</name></author>
|
57
|
+
<id>urn:md5:#{Digest::MD5.hexdigest(domain)}</id>
|
58
|
+
<generator uri="https://git.umaneti.net/fronde/about/">Fronde</generator>
|
59
|
+
ENDATOM
|
60
|
+
end
|
61
|
+
|
62
|
+
# Render an Atom feed entry.
|
63
|
+
#
|
64
|
+
# @param article [Fronde::OrgFile] the related org document for this
|
65
|
+
# entry
|
66
|
+
# @return [String] the Atom entry as a String
|
67
|
+
def atom_entry(article)
|
68
|
+
keywords = article.keywords.map do |k|
|
69
|
+
"<dc:subject>#{CGI.escapeHTML(k)}</dc:subject>"
|
70
|
+
end.join
|
71
|
+
keywords += "\n " if keywords != ''
|
72
|
+
title = CGI.escapeHTML(article.title)
|
73
|
+
<<~ENDENTRY
|
74
|
+
<entry>
|
75
|
+
<title>#{title}</title>
|
76
|
+
<link href="#{article.url}" rel="alternate" type="text/html"
|
77
|
+
title="#{title}"/>
|
78
|
+
<id>urn:md5:#{Digest::MD5.hexdigest(article.timekey)}</id>
|
79
|
+
<published>#{article.datestring(:rfc3339)}</published>
|
80
|
+
<author><name>#{CGI.escapeHTML(article.author)}</name></author>
|
81
|
+
#{keywords}<content type="html">#{CGI.escapeHTML(article.excerpt)}</content>
|
82
|
+
</entry>
|
83
|
+
ENDENTRY
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Fronde
|
4
|
+
# Embeds methods responsible for generating an org file for a given
|
5
|
+
# index.
|
6
|
+
module IndexOrgGenerator
|
7
|
+
def to_org(index_name = 'index', is_project: false)
|
8
|
+
return project_home_page(index_name) if is_project
|
9
|
+
return all_tags_index if index_name == 'index'
|
10
|
+
[org_header(index_name),
|
11
|
+
org_articles(@index[index_name])].join("\n")
|
12
|
+
end
|
13
|
+
alias_method :to_s, :to_org
|
14
|
+
|
15
|
+
def write_org(index_name)
|
16
|
+
return unless save?
|
17
|
+
slug = Fronde::OrgFile.slug index_name
|
18
|
+
FileUtils.mkdir 'tags' unless Dir.exist? 'tags'
|
19
|
+
content = to_org index_name
|
20
|
+
orgdest = "tags/#{slug}.org"
|
21
|
+
IO.write(orgdest, content)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def project_home_page(project_name)
|
27
|
+
content = [org_header(project_name, is_tag: false)]
|
28
|
+
if @projects[project_name]&.any?
|
29
|
+
content += org_articles(@projects[project_name])
|
30
|
+
end
|
31
|
+
content.join("\n")
|
32
|
+
end
|
33
|
+
|
34
|
+
def write_all_blog_home(verbose)
|
35
|
+
Fronde::Config.sources.each do |project|
|
36
|
+
next unless project['is_blog']
|
37
|
+
next unless Dir.exist?(project['path'])
|
38
|
+
warn "Generated blog home for #{project['name']}" if verbose
|
39
|
+
orgdest = format('%<root>s/index.org', root: project['path'])
|
40
|
+
IO.write(orgdest, to_org(project['name'], is_project: true))
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def all_tags_index
|
45
|
+
content = [
|
46
|
+
org_header(R18n.t.fronde.index.all_tags, is_tag: false)
|
47
|
+
]
|
48
|
+
sort_tags_by_name_and_weight.each do |t, tags|
|
49
|
+
content << ''
|
50
|
+
content << org_title(R18n.t.fronde.index.send(t), 'index-tags')
|
51
|
+
next if tags.empty?
|
52
|
+
tags.each do |k|
|
53
|
+
content << "- #{tag_published_url(k)} (#{@index[k].length})"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
content.join("\n")
|
57
|
+
end
|
58
|
+
|
59
|
+
def tag_published_url(tag_name)
|
60
|
+
domain = Fronde::Config.settings['domain']
|
61
|
+
title = @tags_names[tag_name]
|
62
|
+
tag_link = "#{domain}/tags/#{tag_name}.html"
|
63
|
+
"[[#{tag_link}][#{title}]]"
|
64
|
+
end
|
65
|
+
|
66
|
+
def org_header(title = nil, is_tag: true)
|
67
|
+
if is_tag
|
68
|
+
title = @tags_names[title]
|
69
|
+
elsif title.nil? || title == 'index'
|
70
|
+
title = Fronde::Config.settings['title']
|
71
|
+
end
|
72
|
+
<<~HEADER.strip
|
73
|
+
#+title: #{title}
|
74
|
+
#+author: #{Fronde::Config.settings['author']}
|
75
|
+
#+language: #{Fronde::Config.settings['lang']}
|
76
|
+
HEADER
|
77
|
+
end
|
78
|
+
|
79
|
+
def org_articles(articles_list)
|
80
|
+
last_year = nil
|
81
|
+
articles_list.map do |article|
|
82
|
+
year_title = ''
|
83
|
+
year = article.timekey.slice(0, 4)
|
84
|
+
if year != last_year
|
85
|
+
year_title = format("\n%<title>s\n", title: org_title(year))
|
86
|
+
last_year = year
|
87
|
+
end
|
88
|
+
year_title + org_entry(article)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def org_entry(article)
|
93
|
+
line = "- *[[#{article.url}][#{article.title}]]*"
|
94
|
+
if article.date
|
95
|
+
art_date = article.datestring(:full, year: false)
|
96
|
+
published = R18n.t.fronde.index.published_on art_date
|
97
|
+
line += " / #{published}"
|
98
|
+
end
|
99
|
+
line += " \\\\\n #{article.excerpt}" if article.excerpt != ''
|
100
|
+
line
|
101
|
+
end
|
102
|
+
|
103
|
+
def org_title(year, html_class = 'index-year')
|
104
|
+
year = R18n.t.fronde.index.unsorted if year == '0000'
|
105
|
+
<<~ENDPROP
|
106
|
+
* #{year}
|
107
|
+
:PROPERTIES:
|
108
|
+
:HTML_CONTAINER_CLASS: #{html_class}
|
109
|
+
:UNNUMBERED: notoc
|
110
|
+
:END:
|
111
|
+
ENDPROP
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|