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
@@ -1,340 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'json'
4
- require 'open-uri'
5
- require 'fronde/version'
6
-
7
- def fetch_org_version
8
- # Retrieve last org version from git repository tags page.
9
- tag_rx = Regexp.new(
10
- '<a href=\'/cgit/emacs/org-mode.git/tag/\?h=' \
11
- '(?<tag>release_(?<number>[^\']+))\'>\k<tag></a>'
12
- )
13
- versions = URI(
14
- 'https://git.savannah.gnu.org/cgit/emacs/org-mode.git/refs/'
15
- ).open.readlines.map do |line|
16
- line.match(tag_rx) { |matchdata| matchdata[:number] }
17
- end
18
- versions.compact.first
19
- end
20
-
21
- module Fronde
22
- # This module contains utilitary methods to ease ~org-config.el~
23
- # file generation
24
- module LispConfig
25
- # Fetch and return the last published version of Org.
26
- #
27
- # @return [String] the new x.x.x version string of Org
28
- def org_last_version
29
- return @org_version if @org_version
30
- if File.exist?('var/tmp/last_org_version')
31
- @org_version = File.read('var/tmp/last_org_version')
32
- return @org_version
33
- end
34
- @org_version = fetch_org_version
35
- FileUtils.mkdir_p 'var/tmp'
36
- File.write('var/tmp/last_org_version', @org_version)
37
- @org_version
38
- end
39
-
40
- # Generate emacs lisp configuration file for Org and write it.
41
- #
42
- # This method saves the generated configuration in the file
43
- # ~org-config.el~ at the root of your project, overwriting it if it
44
- # existed already.
45
- #
46
- # @return [Integer] the length written (as returned by the
47
- # underlying ~File.write~ method call)
48
- # rubocop:disable Metrics/MethodLength
49
- def write_org_lisp_config(with_tags: false)
50
- projects = org_generate_projects(with_tags: with_tags)
51
- workdir = Dir.pwd
52
- content = File.read(File.expand_path('./org-config.el', __dir__))
53
- .gsub('__VERSION__', Fronde::VERSION)
54
- .gsub('__WORK_DIR__', workdir)
55
- .gsub('__FRONDE_DIR__', __dir__)
56
- .gsub('__ORG_VER__', org_last_version)
57
- .gsub(
58
- '__ALL_PROJECTS__',
59
- projects.values.join("\n ")
60
- )
61
- .gsub('__THEME_CONFIG__', org_default_theme_config)
62
- .gsub('__ALL_PROJECTS_NAMES__', project_names(projects))
63
- .gsub('__LONG_DATE_FMT__', r18n_full_datetime_format)
64
- .gsub('__AUTHOR_EMAIL__', get('author_email', ''))
65
- .gsub('__AUTHOR_NAME__', get('author'))
66
- FileUtils.mkdir_p "#{workdir}/var/lib"
67
- File.write("#{workdir}/var/lib/org-config.el", content)
68
- end
69
- # rubocop:enable Metrics/MethodLength
70
-
71
- # Generate emacs directory variables file.
72
- #
73
- # This method generate the file ~.dir-locals.el~, which is
74
- # responsible to load fronde Org settings when visiting an Org file
75
- # of this fronde instance.
76
- #
77
- # @return [Integer] the length written (as returned by the
78
- # underlying ~File.write~ method call)
79
- def write_dir_locals
80
- workdir = Dir.pwd
81
- # rubocop:disable Layout/LineLength
82
- File.write(
83
- "#{workdir}/.dir-locals.el",
84
- "((org-mode . ((eval . (load-file \"#{workdir}/var/lib/org-config.el\")))))"
85
- )
86
- # rubocop:enable Layout/LineLength
87
- end
88
-
89
- private
90
-
91
- def r18n_full_datetime_format
92
- locale = R18n.get.locale
93
- date_fmt = R18n.t.fronde.index.full_date_format(
94
- date: locale.full_format
95
- )
96
- date_fmt = locale.year_format.sub('_', date_fmt)
97
- time_fmt = locale.time_format.delete('_').strip
98
- R18n.t.fronde.index.full_date_with_time_format(
99
- date: date_fmt, time: time_fmt
100
- )
101
- end
102
-
103
- def ruby_to_lisp_boolean(value)
104
- return 't' if value == true
105
- 'nil'
106
- end
107
-
108
- def project_names(projects)
109
- names = projects.keys.map do |p|
110
- ["\"#{p}\"", "\"#{p}-assets\""]
111
- end.flatten
112
- names << "\"theme-#{get('theme')}\"" unless get('theme') == 'default'
113
- sources.each do |s|
114
- # Default theme defined in settings is already included
115
- next unless s['theme'] && s['theme'] != get('theme')
116
- # Never include theme named 'default' as it does not rely on any
117
- # file to export.
118
- next if s['theme'] == 'default'
119
- theme = "\"theme-#{s['theme']}\""
120
- next if names.include? theme
121
- names << theme
122
- end
123
- names.join(' ')
124
- end
125
-
126
- # Return the full path to the publication path of a given project
127
- # configuration.
128
- #
129
- # @param project [Hash] a project configuration (as extracted from
130
- # the ~sources~ key)
131
- # @return [String] the full path to the target dir of this project
132
- def publication_path(project)
133
- publish_in = [Dir.pwd]
134
- if project['type'] == 'gemini'
135
- publish_in << get('gemini_public_folder', 'public_gmi')
136
- else
137
- publish_in << get('public_folder')
138
- end
139
- publish_in << project['target'] unless project['target'] == '.'
140
- publish_in.join('/')
141
- end
142
-
143
- # Return the publication function needed for a given project
144
- # configuration.
145
- #
146
- # @param project [Hash] a project configuration (as extracted from
147
- # the ~sources~ key)
148
- # @return [String] the org publication function name
149
- def publication_function(project)
150
- case project['type']
151
- when 'gemini'
152
- 'org-gmi-publish-to-gemini'
153
- else
154
- 'org-html-publish-to-html'
155
- end
156
- end
157
-
158
- def org_project(project_name, opts)
159
- shared_lines = org_project_shared_lines(opts)
160
- project_config = [
161
- org_project_config(project_name, opts, shared_lines),
162
- org_assets_config(project_name, shared_lines)
163
- ]
164
- themeconf = org_theme_config(opts['theme'])
165
- project_config << themeconf unless themeconf == ''
166
- project_config.join("\n ")
167
- end
168
-
169
- def org_default_html_postamble
170
- <<~POSTAMBLE
171
- <p><span class="author">#{R18n.t.fronde.org.postamble.written_by}</span>
172
- #{R18n.t.fronde.org.postamble.with_emacs_html}</p>
173
- <p class="date">#{R18n.t.fronde.org.postamble.last_modification}</p>
174
- <p class="validation">%v</p>
175
- POSTAMBLE
176
- end
177
-
178
- def org_default_gemini_postamble
179
- format(
180
- "📅 %<date>s\n📝 %<author>s %<creator>s",
181
- author: R18n.t.fronde.org.postamble.written_by,
182
- creator: R18n.t.fronde.org.postamble.with_emacs,
183
- date: R18n.t.fronde.org.postamble.last_modification
184
- )
185
- end
186
-
187
- def org_default_html_head
188
- <<~HTMLHEAD
189
- <link rel="stylesheet" type="text/css" media="screen"
190
- href="__DOMAIN__/assets/__THEME__/css/style.css">
191
- <link rel="stylesheet" type="text/css" media="screen"
192
- href="__DOMAIN__/assets/__THEME__/css/htmlize.css">
193
- __ATOM_FEED__
194
- HTMLHEAD
195
- end
196
-
197
- def org_default_html_options(project)
198
- defaults = {
199
- 'html-postamble' => org_default_html_postamble,
200
- 'html-head' => '__ATOM_FEED__',
201
- 'html-head-include-default-style' => 't',
202
- 'html-head-include-scripts' => 't'
203
- }
204
- curtheme = project['theme'] || get('theme')
205
- return defaults if curtheme.nil? || curtheme == 'default'
206
- defaults['html-head'] = org_default_html_head
207
- defaults['html-head-include-default-style'] = 'nil'
208
- defaults['html-head-include-scripts'] = 'nil'
209
- defaults
210
- end
211
-
212
- def org_publish_options(project)
213
- defaults = {
214
- 'section-numbers' => 'nil',
215
- 'with-toc' => 'nil'
216
- }
217
- if project['type'] == 'gemini'
218
- defaults['gemini-postamble'] = org_default_gemini_postamble
219
- else
220
- defaults.merge!(
221
- org_default_html_options(project),
222
- get('org-html', {}),
223
- project['org-html'] || {}
224
- )
225
- end
226
- defaults.merge(project['org-options'] || {})
227
- end
228
-
229
- def expand_vars_in_html_head(head, project)
230
- curtheme = project['theme'] || get('theme')
231
- # Head may be frozen when coming from settings
232
- head = head.gsub('__THEME__', curtheme)
233
- .gsub('__DOMAIN__', get('domain'))
234
- return head.gsub('__ATOM_FEED__', '') unless project['is_blog']
235
- atomfeed = <<~ATOMFEED
236
- <link rel="alternate" type="application/atom+xml" title="Atom 1.0"
237
- href="#{get('domain')}/feeds/index.xml" />
238
- ATOMFEED
239
- head.gsub('__ATOM_FEED__', atomfeed)
240
- end
241
-
242
- def cast_lisp_value(value)
243
- return 't' if value.is_a?(TrueClass)
244
- return 'nil' if value.nil? || value.is_a?(FalseClass)
245
- value.strip.gsub(/"/, '\"')
246
- end
247
-
248
- def build_project_org_headers(project)
249
- orgtplopts = org_publish_options(project)
250
- lisp_keywords = ['t', 'nil', '1', '-1', '0'].freeze
251
- orgtplopts.map do |k, v|
252
- v = expand_vars_in_html_head(v, project) if k == 'html-head'
253
- val = cast_lisp_value(v)
254
- if lisp_keywords.include? val
255
- ":#{k} #{val}"
256
- else
257
- ":#{k} \"#{val}\""
258
- end
259
- end
260
- end
261
-
262
- def org_generate_projects(with_tags: false)
263
- projects = {}
264
- projects_sources = sources
265
- if with_tags
266
- tags_conf = build_source('tags')
267
- tags_conf['recursive'] = false
268
- projects_sources << tags_conf
269
- end
270
- projects_sources.each do |opts|
271
- opts['org_headers'] = build_project_org_headers(opts)
272
- projects[opts['name']] = org_project(opts['name'], opts)
273
- end
274
- projects
275
- end
276
-
277
- def org_default_theme_config
278
- theme_config = org_theme_config(get('theme'))
279
- return theme_config if theme_config == ''
280
- format("\n %<conf>s", conf: theme_config)
281
- end
282
-
283
- def org_theme_config(theme)
284
- return '' if theme.nil? || theme == 'default'
285
- workdir = Dir.pwd
286
- [
287
- format('("theme-%<theme>s"', theme: theme),
288
- format(' :base-directory "%<wd>s/themes/%<theme>s"',
289
- wd: workdir, theme: theme),
290
- # rubocop:disable Layout/LineLength
291
- ' :base-extension "jpg\\\\\\|gif\\\\\\|png\\\\\\|js\\\\\\|css\\\\\\|otf\\\\\\|ttf\\\\\\|woff2?"',
292
- # rubocop:enable Layout/LineLength
293
- ' :recursive t',
294
- format(' :publishing-directory "%<wd>s/%<pub>s/assets/%<theme>s"',
295
- wd: workdir, pub: get('public_folder'), theme: theme),
296
- ' :publishing-function org-publish-attachment)'
297
- ].join("\n ").strip
298
- end
299
-
300
- def org_project_shared_lines(project)
301
- [
302
- format(':base-directory "%<path>s"', path: project['path']),
303
- format(
304
- ':publishing-directory "%<path>s"',
305
- path: publication_path(project)
306
- ),
307
- format(
308
- ':recursive %<rec>s',
309
- rec: ruby_to_lisp_boolean(project['recursive'])
310
- )
311
- ]
312
- end
313
-
314
- def org_project_config(project_name, project, shared_lines)
315
- project_lines = [
316
- format('"%<name>s"', name: project_name),
317
- ':base-extension "org"',
318
- format(
319
- ':publishing-function %<fun>s',
320
- fun: publication_function(project)
321
- )
322
- ] + shared_lines + project['org_headers']
323
- if project['exclude']
324
- project_lines << format(
325
- ':exclude "%<value>s"', value: project['exclude']
326
- )
327
- end
328
- format('(%<pr>s)', pr: project_lines.join("\n "))
329
- end
330
-
331
- def org_assets_config(project_name, shared_lines)
332
- assets_lines = [
333
- format('"%<name>s-assets"', name: project_name),
334
- ':base-extension "jpg\\\\\\|gif\\\\\\|png\\\\\\|svg\\\\\\|pdf"',
335
- ':publishing-function org-publish-attachment'
336
- ] + shared_lines
337
- format('(%<assets>s)', assets: assets_lines.join("\n "))
338
- end
339
- end
340
- end
@@ -1,19 +0,0 @@
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-gmi-timestamp-format "__LONG_DATE_FMT__"
13
- org-publish-project-alist
14
- `(__ALL_PROJECTS____THEME_CONFIG__
15
- ("website" :components (__ALL_PROJECTS_NAMES__))))
16
-
17
- ;; Load fronde lib
18
- (load-file (expand-file-name "ox-gmi.el" "__WORK_DIR__/lib"))
19
- (load-file (expand-file-name "ox-fronde.el" "__FRONDE_DIR__"))
@@ -1,121 +0,0 @@
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
- (require 'ox-gmi)
34
-
35
- ;;; Function Declarations
36
-
37
- (defvar fronde/version ""
38
- "Version of the current fronde installation")
39
-
40
- (defvar fronde/current-work-dir nil
41
- "Location of the current fronde website base directory.")
42
-
43
- (defvar fronde/org-temp-dir nil
44
- "Location of the local Org temporary directory (where to place
45
- org timestamps and id locations).")
46
-
47
- (defun fronde/org-html-format-spec (upstream info)
48
- "Return format specification for preamble and postamble.
49
- INFO is a plist used as a communication channel."
50
- (let ((output (funcall upstream info)))
51
- (push `(?A . ,(format "<span class=\"author\">%s</span>"
52
- (org-export-data (plist-get info :author) info)))
53
- output)
54
- (push `(?k . ,(org-export-data (plist-get info :keywords) info)) output)
55
- (push `(?K . ,(format "<ul class=\"keywords-list\">%s</ul>"
56
- (mapconcat
57
- (lambda (k) (format "<li class=\"keyword\">%s</li>" k))
58
- (split-string (or (plist-get info :keywords) "") ",+ *")
59
- "\n")))
60
- output)
61
- (push `(?l . ,(org-export-data (plist-get info :language) info)) output)
62
- (push `(?n . ,(format "Fronde %s" fronde/version)) output)
63
- (push `(?N . ,(format "<a href=\"https://git.umaneti.net/fronde/about/\">Fronde</a> %s" fronde/version)) output)
64
- (push `(?x . ,(org-export-data (plist-get info :description) info)) output)
65
- (push `(?X . ,(format "<p>%s</p>"
66
- (org-export-data (plist-get info :description) info)))
67
- output)))
68
-
69
- (defun fronde/org-gmi-format-spec (upstream info)
70
- "Return format specification for gemini postamble.
71
- INFO is a plist used as a communication channel."
72
- (let ((output (funcall upstream info)))
73
- (push `(?n . ,(format "Fronde %s" fronde/version)) output)))
74
-
75
- (defun fronde/org-i18n-export (link description format)
76
- "Export a i18n link"
77
- (let* ((splitted-link (split-string link "|"))
78
- (path (car splitted-link))
79
- (desc (or description path))
80
- (lang (cadr splitted-link)))
81
- (pcase format
82
- (`html (if lang
83
- (format "<a href=\"%s\" hreflang=\"%s\">%s</a>"
84
- path lang desc)
85
- (format "<a href=\"%s\">%s</a>" path desc)))
86
- (`latex (format "\\href{%s}{%s}" path desc))
87
- (_ (format "%s (%s)" desc path)))))
88
-
89
- (defun fronde/org-i18n-follow (link)
90
- "Visit a i18n link"
91
- (browse-url (car (split-string link "|"))))
92
-
93
- (org-link-set-parameters "i18n"
94
- :export #'fronde/org-i18n-export
95
- :follow #'fronde/org-i18n-follow)
96
-
97
-
98
- ;;; Set configuration options
99
-
100
- (setq fronde/org-temp-dir (expand-file-name "var/tmp" fronde/current-work-dir)
101
- org-publish-timestamp-directory (expand-file-name "timestamps/" fronde/org-temp-dir)
102
- org-id-locations-file (expand-file-name "id-locations.el" fronde/org-temp-dir)
103
- make-backup-files nil
104
- enable-local-variables :all
105
- org-confirm-babel-evaluate nil
106
- org-export-with-broken-links t
107
- org-html-doctype "html5"
108
- org-html-html5-fancy t
109
- org-html-htmlize-output-type 'css
110
- org-html-text-markup-alist '((bold . "<strong>%s</strong>")
111
- (code . "<code>%s</code>")
112
- (italic . "<em>%s</em>")
113
- (strike-through . "<del>%s</del>")
114
- (underline . "<span class=\"underline\">%s</span>")
115
- (verbatim . "<code>%s</code>")))
116
- (advice-add 'org-html-format-spec :around #'fronde/org-html-format-spec)
117
- (advice-add 'org-gmi--format-spec :around #'fronde/org-gmi-format-spec)
118
-
119
- (provide 'ox-fronde)
120
-
121
- ;;; ox-fronde.el ends here
@@ -1,72 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Fronde
4
- # This module holds class methods for the {Fronde::OrgFile} class.
5
- module OrgFileClassMethods
6
- def source_for_target(file_name)
7
- # file_name may be frozen...
8
- src = file_name.sub(/\.html\z/, '.org')
9
- pubfolder = Fronde::Config.get('public_folder')
10
- src.sub!(/^#{pubfolder}\//, '')
11
- # Look for match in each possible sources. The first found wins.
12
- Fronde::Config.sources.each do |project|
13
- if project['target'] == '.'
14
- origin = File.join(project['path'], src)
15
- else
16
- origin = File.join(
17
- project['path'], src.sub(/^#{project['target']}\//, '')
18
- )
19
- end
20
- return origin if File.exist?(origin)
21
- end
22
- nil
23
- end
24
-
25
- def target_for_source(file_name, project, with_public_folder: true)
26
- return nil if file_name.nil?
27
- # file_name may be frozen...
28
- target = file_name.sub(/\.org\z/, '.html').sub(/^#{Dir.pwd}\//, '')
29
- if project.nil?
30
- subfolder = File.basename(File.dirname(target))
31
- target = File.basename(target)
32
- target = "#{subfolder}/#{target}" if subfolder != '.'
33
- else
34
- project_relative_path = project['path'].sub(/^#{Dir.pwd}\//, '')
35
- target.sub!(/^#{project_relative_path}\//, '')
36
- target = "#{project['target']}/#{target}" if project['target'] != '.'
37
- end
38
- return target unless with_public_folder
39
- pubfolder = Fronde::Config.get('public_folder')
40
- "#{pubfolder}/#{target}"
41
- end
42
-
43
- def project_for_source(file_name)
44
- # Look for match in each possible sources. The first found wins.
45
- Fronde::Config.sources.each do |project|
46
- project_relative_path = project['path'].sub(/^#{Dir.pwd}\//, '')
47
- return project if file_name.match?(/^#{project_relative_path}\//)
48
- end
49
- nil
50
- end
51
-
52
- def slug(title)
53
- title.downcase.tr(' ', '-')
54
- .encode('ascii', fallback: ->(k) { translit(k) })
55
- .gsub(/[^\w-]/, '').delete_suffix('-')
56
- end
57
-
58
- private
59
-
60
- def translit(char)
61
- return 'a' if ['á', 'à', 'â', 'ä', 'ǎ', 'ã', 'å'].include?(char)
62
- return 'e' if ['é', 'è', 'ê', 'ë', 'ě', 'ẽ'].include?(char)
63
- return 'i' if ['í', 'ì', 'î', 'ï', 'ǐ', 'ĩ'].include?(char)
64
- return 'o' if ['ó', 'ò', 'ô', 'ö', 'ǒ', 'õ'].include?(char)
65
- return 'u' if ['ú', 'ù', 'û', 'ü', 'ǔ', 'ũ'].include?(char)
66
- return 'y' if ['ý', 'ỳ', 'ŷ', 'ÿ', 'ỹ'].include?(char)
67
- return 'c' if char == 'ç'
68
- return 'n' if char == 'ñ'
69
- '-'
70
- end
71
- end
72
- end
@@ -1,72 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Fronde
4
- # This module holds extracter methods for the {Fronde::OrgFile} class.
5
- module OrgFileExtracter
6
- private
7
-
8
- # Main method, which will call the other to initialize an
9
- # {Fronde::OrgFile} instance.
10
- def extract_data
11
- @content = File.read @file
12
- @title = extract_title
13
- @subtitle = extract_subtitle
14
- @date = extract_date
15
- @author = extract_author
16
- @keywords = extract_keywords
17
- @lang = extract_lang
18
- @excerpt = extract_excerpt
19
- end
20
-
21
- def extract_date
22
- timerx = '([0-9:]{5})(?::([0-9]{2}))?'
23
- m = /^#\+date: *<([0-9-]{10}) [\w.]+(?: #{timerx})?> *$/i.match(@content)
24
- return nil if m.nil?
25
- @notime = m[2].nil?
26
- if @notime
27
- time = '00:00:00'
28
- else
29
- time = "#{m[2]}:#{m[3] || '00'}"
30
- end
31
- DateTime.strptime("#{m[1]} #{time}", '%Y-%m-%d %H:%M:%S')
32
- end
33
-
34
- def extract_title
35
- m = /^#\+title:(.+)$/i.match(@content)
36
- if m.nil?
37
- # Avoid to leak absolute path
38
- project_relative_path = @file.sub(/^#{Dir.pwd}\//, '')
39
- return project_relative_path
40
- end
41
- m[1].strip
42
- end
43
-
44
- def extract_subtitle
45
- m = /^#\+subtitle:(.+)$/i.match(@content)
46
- return '' if m.nil?
47
- m[1].strip
48
- end
49
-
50
- def extract_author
51
- m = /^#\+author:(.+)$/i.match(@content)
52
- return Fronde::Config.get('author') if m.nil?
53
- m[1].strip
54
- end
55
-
56
- def extract_keywords
57
- m = /^#\+keywords:(.+)$/i.match(@content)
58
- return [] if m.nil?
59
- m[1].split(',').map(&:strip)
60
- end
61
-
62
- def extract_lang
63
- m = /^#\+language:(.+)$/i.match(@content)
64
- return Fronde::Config.get('lang') if m.nil?
65
- m[1].strip
66
- end
67
-
68
- def extract_excerpt
69
- @content.scan(/^#\+description:(.+)$/i).map { |l| l[0].strip }.join(' ')
70
- end
71
- end
72
- end
@@ -1,43 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'fronde/config'
4
- require 'fronde/emacs'
5
-
6
- module Fronde
7
- # This module holds HTML formatter methods for the {Fronde::OrgFile}
8
- # class.
9
- module OrgFileHtmlizer
10
- private
11
-
12
- # Format {Fronde::OrgFile#keywords} list in an HTML listing.
13
- #
14
- # @return [String] the HTML keywords list
15
- def keywords_to_html
16
- domain = Fronde::Config.get('domain')
17
- klist = @keywords.map do |k|
18
- <<~KEYWORDLINK
19
- <li class="keyword">
20
- <a href="#{domain}/tags/#{Fronde::OrgFile.slug(k)}.html">#{k}</a>
21
- </li>
22
- KEYWORDLINK
23
- end.join
24
- "<ul class=\"keywords-list\">#{klist}</ul>"
25
- end
26
-
27
- # Format {Fronde::OrgFile#date} as a HTML `time` tag.
28
- #
29
- # @return [String] the HTML `time` tag
30
- def date_to_html(dateformat = :full)
31
- return '<time></time>' if @date.nil?
32
- "<time datetime=\"#{@date.rfc3339}\">#{datestring(dateformat)}</time>"
33
- end
34
-
35
- # Format {Fronde::OrgFile#author} in a HTML `span` tag with a
36
- # specific class.
37
- #
38
- # @return [String] the author HTML `span`
39
- def author_to_html
40
- "<span class=\"author\">#{@author}</span>"
41
- end
42
- end
43
- end