summaryrefslogtreecommitdiffstats
path: root/nikola
diff options
context:
space:
mode:
Diffstat (limited to 'nikola')
-rw-r--r--nikola/__init__.py4
-rw-r--r--nikola/__main__.py2
-rw-r--r--nikola/conf.py.in5
-rw-r--r--nikola/data/samplesite/galleries/demo/metadata.sample.yml2
-rw-r--r--nikola/data/samplesite/galleries/demo/tesla_conducts_lg.jpgbin18941 -> 0 bytes
-rw-r--r--nikola/data/samplesite/galleries/demo/tesla_conducts_lg.webpbin0 -> 9838 bytes
-rw-r--r--nikola/data/themes/base-jinja/templates/base.tmpl2
-rw-r--r--nikola/data/themes/base-jinja/templates/base_helper.tmpl2
-rw-r--r--nikola/data/themes/base-jinja/templates/comments_helper_discourse.tmpl27
-rw-r--r--nikola/data/themes/base-jinja/templates/gallery.tmpl2
-rw-r--r--nikola/data/themes/base/assets/css/rst_base.css136
-rw-r--r--nikola/data/themes/base/assets/css/theme.css2
-rw-r--r--nikola/data/themes/base/messages/messages_bn.py49
-rw-r--r--nikola/data/themes/base/messages/messages_el.py34
-rw-r--r--nikola/data/themes/base/messages/messages_eo.py4
-rw-r--r--nikola/data/themes/base/messages/messages_es.py2
-rw-r--r--nikola/data/themes/base/messages/messages_he.py2
-rw-r--r--nikola/data/themes/base/messages/messages_ja.py24
-rw-r--r--nikola/data/themes/base/messages/messages_ko.py12
-rw-r--r--nikola/data/themes/base/messages/messages_mi.py2
-rw-r--r--nikola/data/themes/base/messages/messages_ml.py6
-rw-r--r--nikola/data/themes/base/messages/messages_ta.py49
-rw-r--r--nikola/data/themes/base/messages/messages_tr.py12
-rw-r--r--nikola/data/themes/base/messages/messages_zh_cn.py24
-rw-r--r--nikola/data/themes/base/templates/base.tmpl2
-rw-r--r--nikola/data/themes/base/templates/base_helper.tmpl2
-rw-r--r--nikola/data/themes/base/templates/comments_helper_discourse.tmpl27
-rw-r--r--nikola/data/themes/base/templates/gallery.tmpl2
-rw-r--r--nikola/data/themes/bootblog4-jinja/templates/base_helper.tmpl8
-rw-r--r--nikola/data/themes/bootblog4/templates/base_helper.tmpl8
-rw-r--r--nikola/data/themes/bootstrap4-jinja/README.md4
-rw-r--r--nikola/data/themes/bootstrap4-jinja/templates/base_helper.tmpl8
-rw-r--r--nikola/data/themes/bootstrap4-jinja/templates/listing.tmpl4
-rw-r--r--nikola/data/themes/bootstrap4/README.md4
-rw-r--r--nikola/data/themes/bootstrap4/templates/base_helper.tmpl8
-rw-r--r--nikola/data/themes/bootstrap4/templates/listing.tmpl4
-rw-r--r--nikola/filters.py18
-rw-r--r--nikola/hierarchy_utils.py2
-rw-r--r--nikola/image_processing.py4
-rw-r--r--nikola/log.py2
-rw-r--r--nikola/metadata_extractors.py2
-rw-r--r--nikola/nikola.py172
-rw-r--r--nikola/packages/datecond/LICENSE2
-rw-r--r--nikola/packages/datecond/__init__.py2
-rw-r--r--nikola/packages/pygments_better_html/LICENSE2
-rw-r--r--nikola/packages/pygments_better_html/__init__.py2
-rw-r--r--nikola/plugin_categories.py56
-rw-r--r--nikola/plugin_manager.py264
-rw-r--r--nikola/plugins/basic_import.py2
-rw-r--r--nikola/plugins/command/__init__.py2
-rw-r--r--nikola/plugins/command/auto/__init__.py46
-rw-r--r--nikola/plugins/command/check.py42
-rw-r--r--nikola/plugins/command/console.py2
-rw-r--r--nikola/plugins/command/default_config.py9
-rw-r--r--nikola/plugins/command/deploy.py2
-rw-r--r--nikola/plugins/command/github_deploy.py2
-rw-r--r--nikola/plugins/command/import_wordpress.py9
-rw-r--r--nikola/plugins/command/init.py2
-rw-r--r--nikola/plugins/command/new_page.py4
-rw-r--r--nikola/plugins/command/new_post.py8
-rw-r--r--nikola/plugins/command/orphans.py2
-rw-r--r--nikola/plugins/command/plugin.py2
-rw-r--r--nikola/plugins/command/rst2html/__init__.py4
-rw-r--r--nikola/plugins/command/serve.py2
-rw-r--r--nikola/plugins/command/status.py2
-rw-r--r--nikola/plugins/command/subtheme.py2
-rw-r--r--nikola/plugins/command/theme.py2
-rw-r--r--nikola/plugins/command/version.py2
-rw-r--r--nikola/plugins/compile/__init__.py2
-rw-r--r--nikola/plugins/compile/html.py2
-rw-r--r--nikola/plugins/compile/ipynb.py2
-rw-r--r--nikola/plugins/compile/markdown/__init__.py2
-rw-r--r--nikola/plugins/compile/markdown/mdx_nikola.py2
-rw-r--r--nikola/plugins/compile/markdown/mdx_podcast.py2
-rw-r--r--nikola/plugins/compile/pandoc.py6
-rw-r--r--nikola/plugins/compile/php.py2
-rw-r--r--nikola/plugins/compile/rest/__init__.py4
-rw-r--r--nikola/plugins/compile/rest/chart.py4
-rw-r--r--nikola/plugins/compile/rest/doc.py2
-rw-r--r--nikola/plugins/compile/rest/listing.py2
-rw-r--r--nikola/plugins/compile/rest/media.py2
-rw-r--r--nikola/plugins/compile/rest/post_list.py4
-rw-r--r--nikola/plugins/compile/rest/soundcloud.py2
-rw-r--r--nikola/plugins/compile/rest/thumbnail.py2
-rw-r--r--nikola/plugins/compile/rest/vimeo.py2
-rw-r--r--nikola/plugins/compile/rest/youtube.py2
-rw-r--r--nikola/plugins/misc/__init__.py2
-rw-r--r--nikola/plugins/misc/scan_posts.plugin2
-rw-r--r--nikola/plugins/misc/scan_posts.py2
-rw-r--r--nikola/plugins/misc/taxonomies_classifier.py2
-rw-r--r--nikola/plugins/shortcode/chart.plugin2
-rw-r--r--nikola/plugins/shortcode/chart.py2
-rw-r--r--nikola/plugins/shortcode/emoji.plugin2
-rw-r--r--nikola/plugins/shortcode/gist.plugin2
-rw-r--r--nikola/plugins/shortcode/listing.plugin2
-rw-r--r--nikola/plugins/shortcode/listing.py2
-rw-r--r--nikola/plugins/shortcode/post_list.plugin2
-rw-r--r--nikola/plugins/shortcode/post_list.py2
-rw-r--r--nikola/plugins/shortcode/thumbnail.plugin2
-rw-r--r--nikola/plugins/shortcode/thumbnail.py2
-rw-r--r--nikola/plugins/task/__init__.py2
-rw-r--r--nikola/plugins/task/archive.py2
-rw-r--r--nikola/plugins/task/authors.py2
-rw-r--r--nikola/plugins/task/bundles.plugin2
-rw-r--r--nikola/plugins/task/bundles.py2
-rw-r--r--nikola/plugins/task/categories.py2
-rw-r--r--nikola/plugins/task/copy_assets.py2
-rw-r--r--nikola/plugins/task/copy_files.py2
-rw-r--r--nikola/plugins/task/galleries.py15
-rw-r--r--nikola/plugins/task/gzip.plugin4
-rw-r--r--nikola/plugins/task/gzip.py10
-rw-r--r--nikola/plugins/task/indexes.py2
-rw-r--r--nikola/plugins/task/listings.py11
-rw-r--r--nikola/plugins/task/page_index.py4
-rw-r--r--nikola/plugins/task/pages.py2
-rw-r--r--nikola/plugins/task/posts.py4
-rw-r--r--nikola/plugins/task/redirect.py2
-rw-r--r--nikola/plugins/task/robots.plugin2
-rw-r--r--nikola/plugins/task/robots.py2
-rw-r--r--nikola/plugins/task/scale_images.py2
-rw-r--r--nikola/plugins/task/sitemap.plugin2
-rw-r--r--nikola/plugins/task/sitemap.py4
-rw-r--r--nikola/plugins/task/sources.py2
-rw-r--r--nikola/plugins/task/tags.py2
-rw-r--r--nikola/plugins/task/taxonomies.py2
-rw-r--r--nikola/plugins/template/__init__.py2
-rw-r--r--nikola/plugins/template/jinja.plugin2
-rw-r--r--nikola/plugins/template/jinja.py2
-rw-r--r--nikola/plugins/template/mako.plugin2
-rw-r--r--nikola/plugins/template/mako.py2
-rw-r--r--nikola/post.py10
-rw-r--r--nikola/shortcodes.py2
-rw-r--r--nikola/state.py2
-rw-r--r--nikola/utils.py6
-rw-r--r--nikola/winutils.py2
135 files changed, 929 insertions, 426 deletions
diff --git a/nikola/__init__.py b/nikola/__init__.py
index 200d23d..edf1c93 100644
--- a/nikola/__init__.py
+++ b/nikola/__init__.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -29,7 +29,7 @@
import os
import sys
-__version__ = '8.2.3'
+__version__ = '8.3.0'
DEBUG = bool(os.getenv('NIKOLA_DEBUG'))
SHOW_TRACEBACKS = bool(os.getenv('NIKOLA_SHOW_TRACEBACKS'))
diff --git a/nikola/__main__.py b/nikola/__main__.py
index 03c7a51..cb7ef0b 100644
--- a/nikola/__main__.py
+++ b/nikola/__main__.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/conf.py.in b/nikola/conf.py.in
index d209eb4..d0d5e79 100644
--- a/nikola/conf.py.in
+++ b/nikola/conf.py.in
@@ -1046,8 +1046,9 @@ PRETTY_URLS = ${PRETTY_URLS}
MARKDOWN_EXTENSIONS = ['markdown.extensions.fenced_code', 'markdown.extensions.codehilite', 'markdown.extensions.extra']
# Options to be passed to markdown extensions (See https://python-markdown.github.io/reference/)
-# Default is {} (no config at all)
-# MARKDOWN_EXTENSION_CONFIGS = {}
+# Default is {DEFAULT_LANG: {}} (no config at all)
+# (translatable)
+# MARKDOWN_EXTENSION_CONFIGS = {DEFAULT_LANG: {}}
# Extra options to pass to the pandoc command, empty by default.
diff --git a/nikola/data/samplesite/galleries/demo/metadata.sample.yml b/nikola/data/samplesite/galleries/demo/metadata.sample.yml
index f504573..9f89f33 100644
--- a/nikola/data/samplesite/galleries/demo/metadata.sample.yml
+++ b/nikola/data/samplesite/galleries/demo/metadata.sample.yml
@@ -7,7 +7,7 @@ order: 2
name: tesla4_lg.jpg
order: 0
---
-name: tesla_conducts_lg.jpg
+name: tesla_conducts_lg.webp
caption: Nikola Tesla conducts electricity
order: 1
---
diff --git a/nikola/data/samplesite/galleries/demo/tesla_conducts_lg.jpg b/nikola/data/samplesite/galleries/demo/tesla_conducts_lg.jpg
deleted file mode 100644
index f47d2ae..0000000
--- a/nikola/data/samplesite/galleries/demo/tesla_conducts_lg.jpg
+++ /dev/null
Binary files differ
diff --git a/nikola/data/samplesite/galleries/demo/tesla_conducts_lg.webp b/nikola/data/samplesite/galleries/demo/tesla_conducts_lg.webp
new file mode 100644
index 0000000..084bfb3
--- /dev/null
+++ b/nikola/data/samplesite/galleries/demo/tesla_conducts_lg.webp
Binary files differ
diff --git a/nikola/data/themes/base-jinja/templates/base.tmpl b/nikola/data/themes/base-jinja/templates/base.tmpl
index 8b057db..94f3b34 100644
--- a/nikola/data/themes/base-jinja/templates/base.tmpl
+++ b/nikola/data/themes/base-jinja/templates/base.tmpl
@@ -30,7 +30,7 @@
{% endif %}
{% block extra_js %}{% endblock %}
<script>
- baguetteBox.run('div#content', {
+ baguetteBox.run('main#content', {
ignoreClass: 'islink',
captions: function(element){var i=element.getElementsByTagName('img')[0];return i===undefined?'':i.alt;}});
</script>
diff --git a/nikola/data/themes/base-jinja/templates/base_helper.tmpl b/nikola/data/themes/base-jinja/templates/base_helper.tmpl
index 2d8c0c8..30faac6 100644
--- a/nikola/data/themes/base-jinja/templates/base_helper.tmpl
+++ b/nikola/data/themes/base-jinja/templates/base_helper.tmpl
@@ -82,7 +82,7 @@ lang="{{ lang }}">
<script src="https://polyfill.io/v3/polyfill.js?features=Intl.RelativeTimeFormat.%7Elocale.{{ luxon_locales[lang] }}"></script>
{% endif %}
{% if use_cdn %}
- <script src="https://cdn.jsdelivr.net/npm/luxon@1.28.0/build/global/luxon.min.js" integrity="sha256-l1u7Y5ze+ENf/T9ORPa3E642/uMgHUFa1KnqzFCcWEY=" crossorigin="anonymous"></script>
+ <script src="https://cdn.jsdelivr.net/npm/luxon@3.3.0/build/global/luxon.min.js" integrity="sha256-Nn+JGDrq3PuTxcDfJmmI0Srj5LpfOFlKqEiPwQK7y40=" crossorigin="anonymous"></script>
{% else %}
<script src="/assets/js/luxon.min.js"></script>
{% endif %}
diff --git a/nikola/data/themes/base-jinja/templates/comments_helper_discourse.tmpl b/nikola/data/themes/base-jinja/templates/comments_helper_discourse.tmpl
new file mode 100644
index 0000000..d02b0ee
--- /dev/null
+++ b/nikola/data/themes/base-jinja/templates/comments_helper_discourse.tmpl
@@ -0,0 +1,27 @@
+{# -*- coding: utf-8 -*- #}
+
+{% macro comment_form(url, title, identifier) %}
+ {% if comment_system_id %}
+ <div id="discourse-comments"></div>
+ <script type="text/javascript">
+ DiscourseEmbed = { discourseUrl: '{{ comment_system_id }}',
+ discourseEmbedUrl: '{{ url }}' };
+
+ (function() {
+ var d = document.createElement('script'); d.type = 'text/javascript'; d.async = true;
+ d.src = DiscourseEmbed.discourseUrl + 'javascripts/embed.js';
+ (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(d);
+ })();
+ </script>
+ {% endif %}
+{% endmacro %}
+
+{% macro comment_link(link, identifier) %}
+ {% if comment_system_id %}
+ <a href="{{ link }}#discourse-comments">{{ messages("Comments") }}</a>
+ {% endif %}
+{% endmacro %}
+
+
+{% macro comment_link_script() %}
+{% endmacro %}
diff --git a/nikola/data/themes/base-jinja/templates/gallery.tmpl b/nikola/data/themes/base-jinja/templates/gallery.tmpl
index d425106..40252ff 100644
--- a/nikola/data/themes/base-jinja/templates/gallery.tmpl
+++ b/nikola/data/themes/base-jinja/templates/gallery.tmpl
@@ -32,7 +32,7 @@
{% else %}
<ul>
{% for folder, ftitle in folders %}
- <li><a href="{{ folder }}">📂&nbsp;{{ ftitle|e }}</a></li>
+ <li><a href="{{ folder }}">&#x1f4c2;&nbsp;{{ ftitle|e }}</a></li>
{% endfor %}
</ul>
{% endif %}
diff --git a/nikola/data/themes/base/assets/css/rst_base.css b/nikola/data/themes/base/assets/css/rst_base.css
index fcd7318..d5aa552 100644
--- a/nikola/data/themes/base/assets/css/rst_base.css
+++ b/nikola/data/themes/base/assets/css/rst_base.css
@@ -1,8 +1,8 @@
/* Minimal style sheet for the HTML output of Docutils. */
/* */
/* :Author: Günter Milde, based on html4css1.css by David Goodger */
-/* :Id: $Id: minimal.css 8642 2021-03-26 13:51:14Z milde $ */
-/* :Copyright: © 2015 Günter Milde. */
+/* :Id: $Id: minimal.css 9079 2022-06-19 14:00:56Z milde $ */
+/* :Copyright: © 2015, 2021 Günter Milde. */
/* :License: Released under the terms of the `2-Clause BSD license`_, */
/* in short: */
/* */
@@ -14,11 +14,10 @@
/* */
/* .. _2-Clause BSD license: http://www.spdx.org/licenses/BSD-2-Clause */
-/* This CSS2.1_ stylesheet defines rules for Docutils elements without */
-/* HTML equivalent. It is required to make the document semantic visible. */
-/* */
-/* .. _CSS2.1: http://www.w3.org/TR/CSS2 */
-/* .. _validates: http://jigsaw.w3.org/css-validator/validator$link */
+/* This CSS3 stylesheet defines rules for Docutils elements without */
+/* HTML equivalent. It is required to make the document semantics visible. */
+/* */
+/* .. _validates: http://jigsaw.w3.org/css-validator/validator$link */
/* titles */
p.topic-title,
@@ -43,8 +42,8 @@ p.sidebar-subtitle {
h1 + p.subtitle {
font-size: 1.6em;
}
-h2 + p.section-subtitle, a.toc-backref {
- color: black;
+a.toc-backref {
+ color: inherit;
text-decoration: none;
}
@@ -73,12 +72,15 @@ span.problematic {
margin-top: 0;
margin-bottom: 0;
}
+/* Nested Paragraphs
+p:first-child { margin-top: 0; }
+p:last-child { margin-bottom: 0; }
+details > p:last-child { margin-bottom: 1em; }
+ */
/* Table of Contents */
-.topic.contents { margin: 0.5em 0; }
-.topic.contents ul.auto-toc {
+.contents ul.auto-toc { /* section numbers present */
list-style-type: none;
- padding-left: 1.5em;
}
/* Enumerated Lists */
@@ -97,6 +99,13 @@ dt .classifier:before {
}
/* Field Lists and similar */
/* bold field name, content starts on the same line */
+dl.field-list,
+dl.option-list,
+dl.docinfo,
+dl.footnote,
+dl.citation {
+ display: flow-root;
+}
dl.field-list > dt,
dl.option-list > dt,
dl.docinfo > dt,
@@ -107,7 +116,7 @@ dl.citation > dt {
float: left;
margin: 0;
padding: 0;
- padding-right: 0.5em;
+ padding-right: 0.2em;
}
/* Offset for field content (corresponds to the --field-name-limit option) */
dl.field-list > dd,
@@ -115,6 +124,12 @@ dl.option-list > dd,
dl.docinfo > dd {
margin-left: 9em; /* ca. 14 chars in the test examples, fit all Docinfo fields */
}
+/* start nested lists on new line */
+dd > dl:first-child,
+dd > ul:first-child,
+dd > ol:first-child {
+ clear: left;
+}
/* start field-body on a new line after long field names */
dl.field-list > dd > *:first-child,
dl.option-list > dd > *:first-child
@@ -135,7 +150,6 @@ dl.docinfo > dd.authors > p { margin: 0; }
dl.option-list > dt { font-weight: normal; }
span.option { white-space: nowrap; }
-
/* Footnotes and Citations */
.footnote, .citation { margin: 1em 0; } /* default paragraph skip (Firefox) */
@@ -209,7 +223,6 @@ dt.label > span.fn-backref > a { font-style: italic; }
margin-right: auto;
}
.align-center {
- clear: both;
text-align: center;
margin-left: auto;
margin-right: auto;
@@ -222,6 +235,12 @@ dt.label > span.fn-backref > a { font-style: italic; }
.align-middle { vertical-align: middle; }
.align-bottom { vertical-align: bottom; }
+img.align-left, img.align-center, img.align-right,
+.figure.align-left, .figure.align-center, .figure.align-right,
+object.align-left, object.align-center, object.align-right {
+ display: block;
+}
+
/* reset inner alignment in figures and tables */
figure.align-left, figure.align-right,
table.align-left, table.align-center, table.align-right {
@@ -229,24 +248,14 @@ table.align-left, table.align-center, table.align-right {
}
/* Text Blocks */
-blockquote,
-div.topic,
-aside.topic {
- margin: 1em 2em;
-}
+.topic { margin: 1em 2em; }
.sidebar,
.admonition,
.system-message {
- border: thin solid;
margin: 1em 2em;
+ border: thin solid;
padding: 0.5em 1em;
}
-.sidebar {
- width: 30%;
- max-width: 26em;
- float: right;
- clear: right;
-}
div.line-block { display: block; }
div.line-block div.line-block, pre { margin-left: 2em; }
@@ -258,7 +267,6 @@ pre.code code:before {
}
/* Tables */
-
td > p:first-child, th > p:first-child { margin-top: 0; }
td > p, th > p { margin-bottom: 0; }
@@ -268,11 +276,20 @@ td > p, th > p { margin-bottom: 0; }
padding-right: 0.5em /* separate table cells */
}
+table > caption {
+ text-align: left;
+ margin-top: 0.2em;
+ margin-bottom: 0.2em;
+}
+table.captionbelow {
+ caption-side: bottom;
+}
+
/* CSS31_ style sheet for the output of Docutils HTML writers. */
/* Rules for easy reading and pre-defined style variants. */
/* */
/* :Author: Günter Milde, based on html4css1.css by David Goodger */
-/* :Id: $Id: plain.css 8636 2021-03-19 00:23:33Z milde $ */
+/* :Id: $Id: plain.css 9081 2022-06-19 20:23:12Z milde $ */
/* :Copyright: © 2015 Günter Milde. */
/* :License: Released under the terms of the `2-Clause BSD license`_, */
/* in short: */
@@ -284,12 +301,27 @@ td > p, th > p { margin-bottom: 0; }
/* This file is offered as-is, without any warranty. */
/* */
/* .. _2-Clause BSD license: http://www.spdx.org/licenses/BSD-2-Clause */
-/* .. _CSS3: http://www.w3.org/TR/CSS3 */
+/* .. _CSS3: https://www.w3.org/Style/CSS/ */
/* Document Structure */
/* ****************** */
+/* Table of Contents */
+ul.auto-toc > li > p {
+ padding-left: 1em;
+ text-indent: -1em;
+}
+nav.contents ul {
+ padding-left: 1em;
+}
+main > nav.contents ul ul ul ul:not(.auto-toc) {
+ list-style-type: '\2B29\ ';
+}
+main > nav.contents ul ul ul ul ul:not(.auto-toc) {
+ list-style-type: '\2B1D\ ';
+}
+
/* Transitions */
hr.docutils {
width: 80%;
@@ -305,30 +337,39 @@ dl > dd {
/* Lists */
/* ===== */
-/* Separate list entries in compound lists */
-dl > dd, ol > li,
-
/* Definition Lists */
/* Indent lists nested in definition lists */
-/* (:only-child is new in CSS 3) */
dd > ul:only-child, dd > ol:only-child { padding-left: 1em; }
/* Description Lists */
/* styled like in most dictionaries, encyclopedias etc. */
+dl.description {
+ display: flow-root;
+}
dl.description > dt {
font-weight: bold;
clear: left;
float: left;
margin: 0;
padding: 0;
- padding-right: 0.5em;
+ padding-right: 0.3em;
+}
+dl.description > dd:after {
+ display: table;
+ content: "";
+ clear: left; /* clearfix for empty descriptions */
}
/* Field Lists */
+dl.field-list > dd,
+dl.docinfo > dd {
+ margin-left: var(--field-indent); /* adapted in media queries or HTML */
+}
+
/* example for custom field-name width */
dl.field-list.narrow > dd {
- margin-left: 5em;
+ --field-indent: 5em;
}
/* run-in: start field-body on same line after long field names */
dl.field-list.run-in > dd p {
@@ -337,8 +378,8 @@ dl.field-list.run-in > dd p {
/* Bibliographic Fields */
-/* generally, bibliographic fields use special definition list dl.docinfo */
-/* but dedication and abstract are placed into "topic" divs */
+/* generally, bibliographic fields use dl.docinfo */
+/* but dedication and abstract are placed into divs */
div.abstract p.topic-title {
text-align: center;
}
@@ -351,6 +392,10 @@ div.dedication p.topic-title {
font-style: normal;
}
+/* disclosures */
+details { padding-left: 1em; }
+summary { margin-left: -1em; }
+
/* Text Blocks */
/* =========== */
@@ -360,14 +405,9 @@ pre.math, pre.code {
font-family: monospace;
}
-/* Block Quotes */
-blockquote > table,
-div.topic > table {
- margin-top: 0;
- margin-bottom: 0;
-}
+/* Block Quotes and Topics */
blockquote p.attribution,
-div.topic p.attribution {
+.topic p.attribution {
text-align: right;
margin-left: 20%;
}
@@ -468,7 +508,9 @@ div.error {
aside.sidebar {
width: 30%;
max-width: 26em;
+ float: right;
+ clear: right;
margin-left: 1em;
- margin-right: -2%;
- background-color: #ffffee;
+ margin-right: -1%;
+ background-color: #fffffa;
}
diff --git a/nikola/data/themes/base/assets/css/theme.css b/nikola/data/themes/base/assets/css/theme.css
index b198c2c..cd09792 100644
--- a/nikola/data/themes/base/assets/css/theme.css
+++ b/nikola/data/themes/base/assets/css/theme.css
@@ -1,7 +1,7 @@
@charset "UTF-8";
/*
- Copyright © 2014-2022 Daniel Aleksandersen and others.
+ Copyright © 2014-2024 Daniel Aleksandersen and others.
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
diff --git a/nikola/data/themes/base/messages/messages_bn.py b/nikola/data/themes/base/messages/messages_bn.py
new file mode 100644
index 0000000..316e4ec
--- /dev/null
+++ b/nikola/data/themes/base/messages/messages_bn.py
@@ -0,0 +1,49 @@
+# -*- encoding:utf-8 -*-
+"""Autogenerated file, do not edit. Submit translations on Transifex."""
+
+MESSAGES = {
+ "%d min remaining to read": "",
+ "(active)": "",
+ "Also available in:": "",
+ "Archive": "",
+ "Atom feed": "",
+ "Authors": "",
+ "Categories": "",
+ "Comments": "",
+ "LANGUAGE": "",
+ "Languages:": "",
+ "More posts about %s": "",
+ "Newer posts": "",
+ "Next post": "",
+ "Next": "",
+ "No posts found.": "",
+ "Nothing found.": "",
+ "Older posts": "",
+ "Original site": "",
+ "Posted:": "",
+ "Posts about %s": "",
+ "Posts by %s": "",
+ "Posts for year %s": "",
+ "Posts for {month_day_year}": "",
+ "Posts for {month_year}": "",
+ "Previous post": "",
+ "Previous": "",
+ "Publication date": "",
+ "RSS feed": "",
+ "Read in English": "",
+ "Read more": "",
+ "Skip to main content": "",
+ "Source": "",
+ "Subcategories:": "",
+ "Tags and Categories": "",
+ "Tags": "",
+ "Toggle navigation": "",
+ "Uncategorized": "",
+ "Up": "",
+ "Updates": "",
+ "Write your page here.": "",
+ "Write your post here.": "",
+ "old posts, page %d": "",
+ "page %d": "",
+ "updated": "",
+}
diff --git a/nikola/data/themes/base/messages/messages_el.py b/nikola/data/themes/base/messages/messages_el.py
index 7021d84..3cba21a 100644
--- a/nikola/data/themes/base/messages/messages_el.py
+++ b/nikola/data/themes/base/messages/messages_el.py
@@ -2,12 +2,12 @@
"""Autogenerated file, do not edit. Submit translations on Transifex."""
MESSAGES = {
- "%d min remaining to read": "",
- "(active)": "",
+ "%d min remaining to read": "%dmin απομένουν για ανάγνωση",
+ "(active)": "(ενεργό)",
"Also available in:": "Διαθέσιμο και στα:",
"Archive": "Αρχείο",
- "Atom feed": "",
- "Authors": "",
+ "Atom feed": "Ροή Atom",
+ "Authors": "Συγγραφείς",
"Categories": "Κατηγορίες",
"Comments": "Σχόλια",
"LANGUAGE": "Ελληνικά",
@@ -15,35 +15,35 @@ MESSAGES = {
"More posts about %s": "Περισσότερες αναρτήσεις για %s",
"Newer posts": "Νεότερες αναρτήσεις",
"Next post": "Επόμενη ανάρτηση",
- "Next": "",
+ "Next": "Επόμενο",
"No posts found.": "Δε βρέθηκαν αναρτήσεις",
"Nothing found.": "Δε βρέθηκε περιεχόμενο",
"Older posts": "Παλαιότερες αναρτήσεις",
"Original site": "Ιστοσελίδα αρχικής ανάρτησης",
"Posted:": "Αναρτήθηκε:",
"Posts about %s": "Αναρτήσεις για %s",
- "Posts by %s": "",
+ "Posts by %s": "Αναρτήσεις από %s",
"Posts for year %s": "Αναρτήσεις για το έτος %s",
"Posts for {month_day_year}": "Αναρτήσεις στις {month_day_year}",
"Posts for {month_year}": "Αναρτήσεις για τον {month_year}",
"Previous post": "Προηγούμενη ανάρτηση",
- "Previous": "",
+ "Previous": "Προηγούμενο",
"Publication date": "Ημερομηνία δημοσίευσης",
- "RSS feed": "",
+ "RSS feed": "Ροή RSS",
"Read in English": "Διαβάστε στα Ελληνικά",
"Read more": "Διαβάστε περισσότερα",
- "Skip to main content": "",
+ "Skip to main content": "Παράλειψη στο κυρίως περιεχόμενο",
"Source": "Πηγαίος κώδικας",
- "Subcategories:": "",
+ "Subcategories:": "Υποκατηγορίες:",
"Tags and Categories": "Ετικέτες και κατηγορίες",
"Tags": "Ετικέτες",
- "Toggle navigation": "",
- "Uncategorized": "",
- "Up": "",
- "Updates": "",
- "Write your page here.": "",
- "Write your post here.": "",
+ "Toggle navigation": "Εναλλαγή πλοήγησης",
+ "Uncategorized": "Χωρίς κατηγορία.",
+ "Up": "Επάνω",
+ "Updates": "Ενημερώσεις",
+ "Write your page here.": "Γράψτε τη σελίδα σας εδώ.",
+ "Write your post here.": "Γράψτε τις αναρτήσεις σας εδώ.",
"old posts, page %d": "σελίδα παλαιότερων αναρτήσεων %d",
"page %d": "σελίδα %d",
- "updated": "",
+ "updated": "ενημερωμένο",
}
diff --git a/nikola/data/themes/base/messages/messages_eo.py b/nikola/data/themes/base/messages/messages_eo.py
index 2793ff5..a2ecf65 100644
--- a/nikola/data/themes/base/messages/messages_eo.py
+++ b/nikola/data/themes/base/messages/messages_eo.py
@@ -24,7 +24,7 @@ MESSAGES = {
"Posts about %s": "Artikoloj pri %s",
"Posts by %s": "Artikoloj de %s",
"Posts for year %s": "Artikoloj de la jaro %s",
- "Posts for {month_day_year}": "Artikoloj de la {month_day_year}",
+ "Posts for {month_day_year}": "Artikoloj de {month_day_year}",
"Posts for {month_year}": "Artikoloj de {month_year}",
"Previous post": "Antaŭa artikolo",
"Previous": "Antaŭa",
@@ -45,5 +45,5 @@ MESSAGES = {
"Write your post here.": "Skribu tie vian artikolon.",
"old posts, page %d": "%da paĝo de malnovaj artikoloj",
"page %d": "paĝo %d",
- "updated": "",
+ "updated": "ĝisdatigita",
}
diff --git a/nikola/data/themes/base/messages/messages_es.py b/nikola/data/themes/base/messages/messages_es.py
index bb58c82..3bbb9d8 100644
--- a/nikola/data/themes/base/messages/messages_es.py
+++ b/nikola/data/themes/base/messages/messages_es.py
@@ -45,5 +45,5 @@ MESSAGES = {
"Write your post here.": "Escriba su publicación aquí.",
"old posts, page %d": "publicaciones antiguas, página %d",
"page %d": "página %d",
- "updated": "",
+ "updated": "actualizado",
}
diff --git a/nikola/data/themes/base/messages/messages_he.py b/nikola/data/themes/base/messages/messages_he.py
index 2de9796..1ae7051 100644
--- a/nikola/data/themes/base/messages/messages_he.py
+++ b/nikola/data/themes/base/messages/messages_he.py
@@ -10,7 +10,7 @@ MESSAGES = {
"Authors": "מחברים",
"Categories": "קטגוריות",
"Comments": "הערות",
- "LANGUAGE": "אנגלית",
+ "LANGUAGE": "עברית",
"Languages:": "שפות:",
"More posts about %s": "עוד פוסטים אודות %s",
"Newer posts": "פוסטים חדשים",
diff --git a/nikola/data/themes/base/messages/messages_ja.py b/nikola/data/themes/base/messages/messages_ja.py
index fd563ad..dbd38e3 100644
--- a/nikola/data/themes/base/messages/messages_ja.py
+++ b/nikola/data/themes/base/messages/messages_ja.py
@@ -4,43 +4,43 @@
MESSAGES = {
"%d min remaining to read": "残りを読むのに必要な時間は%d分",
"(active)": "(有効)",
- "Also available in:": "他の言語で読む:",
+ "Also available in:": "他の言語で読む:",
"Archive": "過去記事一覧",
"Atom feed": "Atomフィード",
"Authors": "著者一覧",
- "Categories": "カテゴリ",
+ "Categories": "カテゴリー",
"Comments": "コメント",
"LANGUAGE": "日本語",
- "Languages:": "言語:",
+ "Languages:": "言語:",
"More posts about %s": "%sに関する記事一覧",
"Newer posts": "新しい記事",
"Next post": "次の記事",
- "Next": "次",
+ "Next": "次へ",
"No posts found.": "記事はありません。",
"Nothing found.": "なにも見つかりませんでした。",
"Older posts": "過去の記事",
- "Original site": "翻訳元のサイト",
- "Posted:": "公開日時:",
+ "Original site": "元のサイト",
+ "Posted:": "公開日時:",
"Posts about %s": "%sについての記事",
"Posts by %s": "%sの記事一覧",
"Posts for year %s": "%s年の記事",
"Posts for {month_day_year}": "{month_day_year}の記事",
"Posts for {month_year}": "{month_year}の記事",
"Previous post": "一つ前の記事",
- "Previous": "前",
+ "Previous": "前へ",
"Publication date": "公開日",
"RSS feed": "RSSフィード",
"Read in English": "日本語で読む",
"Read more": "続きを読む",
"Skip to main content": "本文を読み飛ばす",
"Source": "ソース",
- "Subcategories:": "サブカテゴリ",
- "Tags and Categories": "カテゴリおよびタグ一覧",
+ "Subcategories:": "サブカテゴリー",
+ "Tags and Categories": "カテゴリーおよびタグ一覧",
"Tags": "タグ",
"Toggle navigation": "ナビゲーションを隠す",
- "Uncategorized": "カテゴリなし",
- "Up": "上",
- "Updates": "フィード",
+ "Uncategorized": "カテゴリーなし",
+ "Up": "上へ",
+ "Updates": "更新",
"Write your page here.": "ここに記事を記述してください。",
"Write your post here.": "ここに記事を記述してください。",
"old posts, page %d": "過去の記事 %dページ目",
diff --git a/nikola/data/themes/base/messages/messages_ko.py b/nikola/data/themes/base/messages/messages_ko.py
index 57b6cb9..bc33e9f 100644
--- a/nikola/data/themes/base/messages/messages_ko.py
+++ b/nikola/data/themes/base/messages/messages_ko.py
@@ -6,7 +6,7 @@ MESSAGES = {
"(active)": "(활성됨)",
"Also available in:": "이곳에서도 가능함:",
"Archive": "저장소",
- "Atom feed": "",
+ "Atom feed": "아톰 피드",
"Authors": "작성자",
"Categories": "분류",
"Comments": "댓글",
@@ -22,7 +22,7 @@ MESSAGES = {
"Original site": "출처",
"Posted:": "작성됨:",
"Posts about %s": "%s에 대한 포스트",
- "Posts by %s": "%s에 의해 작성된 글",
+ "Posts by %s": "%s의 작성된 포스트",
"Posts for year %s": "%s년도 포스트",
"Posts for {month_day_year}": "{month_day_year}에 작성된 포스트",
"Posts for {month_year}": "{month_year}에 쓴 포스트",
@@ -34,16 +34,16 @@ MESSAGES = {
"Read more": "더 읽기",
"Skip to main content": "주 콘텐츠로 바로가기",
"Source": "원문",
- "Subcategories:": "서브 카테고리",
+ "Subcategories:": "서브 카테고리:",
"Tags and Categories": "태그와 분류",
"Tags": "태그",
- "Toggle navigation": "",
+ "Toggle navigation": "네비게이션 켜고 끄기",
"Uncategorized": "카테고리 없음",
- "Up": "",
+ "Up": "상위",
"Updates": "업데이트",
"Write your page here.": "여기에 페이지를 작성하세요.",
"Write your post here.": "이곳에 글을 작성하세요.",
"old posts, page %d": "이전 포스트, 페이지 %d",
"page %d": "페이지 %d",
- "updated": "",
+ "updated": "업데이트됨",
}
diff --git a/nikola/data/themes/base/messages/messages_mi.py b/nikola/data/themes/base/messages/messages_mi.py
index 853744a..f9ed574 100644
--- a/nikola/data/themes/base/messages/messages_mi.py
+++ b/nikola/data/themes/base/messages/messages_mi.py
@@ -10,7 +10,7 @@ MESSAGES = {
"Authors": "Kaituhi",
"Categories": "Kāwai",
"Comments": "Nga korero",
- "LANGUAGE": " Māori",
+ "LANGUAGE": "Māori",
"Languages:": "Reo",
"More posts about %s": "Ētahi atu pou e pā ana ki %s",
"Newer posts": "Nga pou hou",
diff --git a/nikola/data/themes/base/messages/messages_ml.py b/nikola/data/themes/base/messages/messages_ml.py
index a818320..e663d6a 100644
--- a/nikola/data/themes/base/messages/messages_ml.py
+++ b/nikola/data/themes/base/messages/messages_ml.py
@@ -2,7 +2,7 @@
"""Autogenerated file, do not edit. Submit translations on Transifex."""
MESSAGES = {
- "%d min remaining to read": "%d മിനിട്ട് കൂടി വായിച്ചു തീരാന്‍ ",
+ "%d min remaining to read": "വായിച്ചു തീരാന്‍ %dമിനിട്ട് കൂടി",
"(active)": "(സജീവം)",
"Also available in:": "ലഭ്യമായ ഭാഷകള്‍:",
"Archive": "ആര്‍കൈവ്",
@@ -12,7 +12,7 @@ MESSAGES = {
"Comments": "കമന്റുകള്‍",
"LANGUAGE": "മലയാളം",
"Languages:": "ഭാഷകള്‍:",
- "More posts about %s": "%s-നെ കുറിച്ചുള്ള കൂടുതല്‍ രചനകള്‍",
+ "More posts about %s": "%s -നെ കുറിച്ചുള്ള കൂടുതല്‍ രചനകള്‍",
"Newer posts": "പുതിയ രചനകള്‍",
"Next post": "അടുത്ത രചന",
"Next": "അടുത്തത്",
@@ -34,7 +34,7 @@ MESSAGES = {
"Read more": "കൂടുതല്‍ വായിക്കുക",
"Skip to main content": "പ്രധാന ഉള്ളടക്കത്തിലേക്ക് നേരെ പോവുക",
"Source": "സ്രോതസ്സ്‌",
- "Subcategories:": "ഉപവിഭാഗങ്ങള്‍:",
+ "Subcategories:": "ഉപവിഭാഗങ്ങള്‍",
"Tags and Categories": "ടാഗുകളും വിഭാഗങ്ങളും",
"Tags": "ടാഗുകള്‍",
"Toggle navigation": "നാവിഗേഷൻ മാറ്റുക",
diff --git a/nikola/data/themes/base/messages/messages_ta.py b/nikola/data/themes/base/messages/messages_ta.py
new file mode 100644
index 0000000..316e4ec
--- /dev/null
+++ b/nikola/data/themes/base/messages/messages_ta.py
@@ -0,0 +1,49 @@
+# -*- encoding:utf-8 -*-
+"""Autogenerated file, do not edit. Submit translations on Transifex."""
+
+MESSAGES = {
+ "%d min remaining to read": "",
+ "(active)": "",
+ "Also available in:": "",
+ "Archive": "",
+ "Atom feed": "",
+ "Authors": "",
+ "Categories": "",
+ "Comments": "",
+ "LANGUAGE": "",
+ "Languages:": "",
+ "More posts about %s": "",
+ "Newer posts": "",
+ "Next post": "",
+ "Next": "",
+ "No posts found.": "",
+ "Nothing found.": "",
+ "Older posts": "",
+ "Original site": "",
+ "Posted:": "",
+ "Posts about %s": "",
+ "Posts by %s": "",
+ "Posts for year %s": "",
+ "Posts for {month_day_year}": "",
+ "Posts for {month_year}": "",
+ "Previous post": "",
+ "Previous": "",
+ "Publication date": "",
+ "RSS feed": "",
+ "Read in English": "",
+ "Read more": "",
+ "Skip to main content": "",
+ "Source": "",
+ "Subcategories:": "",
+ "Tags and Categories": "",
+ "Tags": "",
+ "Toggle navigation": "",
+ "Uncategorized": "",
+ "Up": "",
+ "Updates": "",
+ "Write your page here.": "",
+ "Write your post here.": "",
+ "old posts, page %d": "",
+ "page %d": "",
+ "updated": "",
+}
diff --git a/nikola/data/themes/base/messages/messages_tr.py b/nikola/data/themes/base/messages/messages_tr.py
index 8249f42..1b81b37 100644
--- a/nikola/data/themes/base/messages/messages_tr.py
+++ b/nikola/data/themes/base/messages/messages_tr.py
@@ -6,7 +6,7 @@ MESSAGES = {
"(active)": "(etkin)",
"Also available in:": "Şu dilde de mevcut:",
"Archive": "Arşiv",
- "Atom feed": "",
+ "Atom feed": "Atom besleme",
"Authors": "Yazarlar",
"Categories": "Kategoriler",
"Comments": "Yorumlar",
@@ -15,7 +15,7 @@ MESSAGES = {
"More posts about %s": "%s hakkında diğer yazılar",
"Newer posts": "Daha yeni yazılar",
"Next post": "Sonraki yazı",
- "Next": "",
+ "Next": "Sonraki",
"No posts found.": "Yazı bulunamadı.",
"Nothing found.": "Hiçbir şey bulunamadı.",
"Older posts": "Daha eski yazılar",
@@ -27,7 +27,7 @@ MESSAGES = {
"Posts for {month_day_year}": "{month_day_year} tarihinden itibaren yazılar",
"Posts for {month_year}": "{month_year} göre yazılar",
"Previous post": "Önceki yazı",
- "Previous": "",
+ "Previous": "Önceki",
"Publication date": "Yayınlanma tarihi",
"RSS feed": "RSS kaynağı",
"Read in English": "Türkçe olarak oku",
@@ -37,13 +37,13 @@ MESSAGES = {
"Subcategories:": "Alt kategoriler:",
"Tags and Categories": "Etiketler ve Kategoriler",
"Tags": "Etiketler",
- "Toggle navigation": "",
+ "Toggle navigation": "Gezinme aç / kapa",
"Uncategorized": "Kategorisiz",
- "Up": "",
+ "Up": "Yukarı",
"Updates": "Güncellemeler",
"Write your page here.": "Sayfanızı buraya yazın.",
"Write your post here.": "Yazınızı buraya yazın.",
"old posts, page %d": "eski yazılar, sayfa %d",
"page %d": "sayfa %d",
- "updated": "",
+ "updated": "güncellendi",
}
diff --git a/nikola/data/themes/base/messages/messages_zh_cn.py b/nikola/data/themes/base/messages/messages_zh_cn.py
index df8570b..c8bf3fc 100644
--- a/nikola/data/themes/base/messages/messages_zh_cn.py
+++ b/nikola/data/themes/base/messages/messages_zh_cn.py
@@ -3,8 +3,8 @@
MESSAGES = {
"%d min remaining to read": "剩余阅读时间 %d 分钟",
- "(active)": "(活跃)",
- "Also available in:": "也可用于:",
+ "(active)": "(活跃)",
+ "Also available in:": "其他语言版本:",
"Archive": "文章归档",
"Atom feed": "Atom 源",
"Authors": "作者",
@@ -15,22 +15,22 @@ MESSAGES = {
"More posts about %s": "关于%s 的更多文章 ",
"Newer posts": "较新的文章",
"Next post": "下一篇文章",
- "Next": "项下",
- "No posts found.": "没有找到文章",
+ "Next": "下一篇",
+ "No posts found.": "沒有找到文章。",
"Nothing found.": "没有找到。",
"Older posts": "以前的文章",
"Original site": "原文地址",
"Posted:": "发表于:",
- "Posts about %s": "关于文章 %s",
- "Posts by %s": "文章有%s发布",
- "Posts for year %s": "%s年文章",
- "Posts for {month_day_year}": "{month_day_year}文章",
- "Posts for {month_year}": "{month_year}文章",
+ "Posts about %s": "关于 %s 的文章",
+ "Posts by %s": "由 %s 发布的文章",
+ "Posts for year %s": "%s 年的文章",
+ "Posts for {month_day_year}": "{month_day_year}的文章",
+ "Posts for {month_year}": "{month_year}的文章",
"Previous post": "上一篇文章",
"Previous": "以前",
"Publication date": "发布日期",
"RSS feed": "RSS 源",
- "Read in English": "阅读简体中文",
+ "Read in English": "阅读简体中文版",
"Read more": "阅读更多",
"Skip to main content": "跳到主内容",
"Source": "源文件",
@@ -43,7 +43,7 @@ MESSAGES = {
"Updates": "更新",
"Write your page here.": "在这里书写你的页面。",
"Write your post here.": "在这里书写你的文章。",
- "old posts, page %d": "旧文章页 %d",
+ "old posts, page %d": "旧文章,第 %d 页",
"page %d": "第 %d 页",
- "updated": "更新",
+ "updated": "已更新",
}
diff --git a/nikola/data/themes/base/templates/base.tmpl b/nikola/data/themes/base/templates/base.tmpl
index f071c95..ae9570a 100644
--- a/nikola/data/themes/base/templates/base.tmpl
+++ b/nikola/data/themes/base/templates/base.tmpl
@@ -30,7 +30,7 @@ ${template_hooks['extra_head']()}
% endif
<%block name="extra_js"></%block>
<script>
- baguetteBox.run('div#content', {
+ baguetteBox.run('main#content', {
ignoreClass: 'islink',
captions: function(element){var i=element.getElementsByTagName('img')[0];return i===undefined?'':i.alt;}});
</script>
diff --git a/nikola/data/themes/base/templates/base_helper.tmpl b/nikola/data/themes/base/templates/base_helper.tmpl
index 684165d..a03ae50 100644
--- a/nikola/data/themes/base/templates/base_helper.tmpl
+++ b/nikola/data/themes/base/templates/base_helper.tmpl
@@ -82,7 +82,7 @@ lang="${lang}">
<script src="https://polyfill.io/v3/polyfill.js?features=Intl.RelativeTimeFormat.%7Elocale.${luxon_locales[lang]}"></script>
% endif
% if use_cdn:
- <script src="https://cdn.jsdelivr.net/npm/luxon@1.28.0/build/global/luxon.min.js" integrity="sha256-l1u7Y5ze+ENf/T9ORPa3E642/uMgHUFa1KnqzFCcWEY=" crossorigin="anonymous"></script>
+ <script src="https://cdn.jsdelivr.net/npm/luxon@3.3.0/build/global/luxon.min.js" integrity="sha256-Nn+JGDrq3PuTxcDfJmmI0Srj5LpfOFlKqEiPwQK7y40=" crossorigin="anonymous"></script>
% else:
<script src="/assets/js/luxon.min.js"></script>
% endif
diff --git a/nikola/data/themes/base/templates/comments_helper_discourse.tmpl b/nikola/data/themes/base/templates/comments_helper_discourse.tmpl
new file mode 100644
index 0000000..0f92ea2
--- /dev/null
+++ b/nikola/data/themes/base/templates/comments_helper_discourse.tmpl
@@ -0,0 +1,27 @@
+## -*- coding: utf-8 -*-
+
+<%def name="comment_form(url, title, identifier)">
+ %if comment_system_id:
+ <div id="discourse-comments"></div>
+ <script type="text/javascript">
+ DiscourseEmbed = { discourseUrl: '${comment_system_id}',
+ discourseEmbedUrl: '${url}' };
+
+ (function() {
+ var d = document.createElement('script'); d.type = 'text/javascript'; d.async = true;
+ d.src = DiscourseEmbed.discourseUrl + 'javascripts/embed.js';
+ (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(d);
+ })();
+ </script>
+ %endif
+</%def>
+
+<%def name="comment_link(link, identifier)">
+ %if comment_system_id:
+ <a href="${link}#discourse-comments">${messages("Comments")}</a>
+ %endif
+</%def>
+
+
+<%def name="comment_link_script()">
+</%def>
diff --git a/nikola/data/themes/base/templates/gallery.tmpl b/nikola/data/themes/base/templates/gallery.tmpl
index fef3a86..a32f234 100644
--- a/nikola/data/themes/base/templates/gallery.tmpl
+++ b/nikola/data/themes/base/templates/gallery.tmpl
@@ -32,7 +32,7 @@
% else:
<ul>
% for folder, ftitle in folders:
- <li><a href="${folder}">📂&nbsp;${ftitle|h}</a></li>
+ <li><a href="${folder}">&#x1f4c2;&nbsp;${ftitle|h}</a></li>
% endfor
</ul>
% endif
diff --git a/nikola/data/themes/bootblog4-jinja/templates/base_helper.tmpl b/nikola/data/themes/bootblog4-jinja/templates/base_helper.tmpl
index b35c8d2..be87913 100644
--- a/nikola/data/themes/bootblog4-jinja/templates/base_helper.tmpl
+++ b/nikola/data/themes/bootblog4-jinja/templates/base_helper.tmpl
@@ -64,9 +64,9 @@ lang="{{ lang }}">
{% macro late_load_js() %}
{% if use_cdn %}
- <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
+ <script src="https://code.jquery.com/jquery-3.6.4.min.js" integrity="sha256-oP6HI9z1XaZNBrJURtCoUT5SUnxFr8s3BzRl+cbzUq8=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
- <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.min.js" integrity="sha384-VHvPCCyXqtD5DqJeNxl2dtTyhF78xXNXdkwX1CZeRusQfRKp+tA7hAShOK/B/fQ2" crossorigin="anonymous"></script>
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.min.js" integrity="sha384-+sLIOodYLS7CIrQpBjl+C7nPvqq+FbNUBDunl/OZv93DB7Ln/533i8e/mZXLi/P+" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/baguettebox.js/1.11.1/baguetteBox.min.js" integrity="sha256-ULQV01VS9LCI2ePpLsmka+W0mawFpEA0rtxnezUj4A4=" crossorigin="anonymous"></script>
{% endif %}
{% if use_bundles and use_cdn %}
@@ -86,7 +86,7 @@ lang="{{ lang }}">
<script src="https://polyfill.io/v3/polyfill.js?features=Intl.RelativeTimeFormat.%7Elocale.{{ luxon_locales[lang] }}"></script>
{% endif %}
{% if use_cdn %}
- <script src="https://cdn.jsdelivr.net/npm/luxon@1.28.0/build/global/luxon.min.js" integrity="sha256-l1u7Y5ze+ENf/T9ORPa3E642/uMgHUFa1KnqzFCcWEY=" crossorigin="anonymous"></script>
+ <script src="https://cdn.jsdelivr.net/npm/luxon@3.3.0/build/global/luxon.min.js" integrity="sha256-Nn+JGDrq3PuTxcDfJmmI0Srj5LpfOFlKqEiPwQK7y40=" crossorigin="anonymous"></script>
{% else %}
<script src="/assets/js/luxon.min.js"></script>
{% endif %}
@@ -100,7 +100,7 @@ lang="{{ lang }}">
{% macro html_stylesheets() %}
{% if use_cdn %}
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css" integrity="sha384-zCbKRCUGaJDkqS1kPbPd7TveP5iyJE0EjAuZQTgFLD2ylzuqKfdKlfG/eSrtxUkn" crossorigin="anonymous">
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css" integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/baguettebox.js/1.11.1/baguetteBox.min.css" integrity="sha256-cLMYWYYutHkt+KpNqjg7NVkYSQ+E2VbrXsEvOqU7mL0=" crossorigin="anonymous">
{% endif %}
{% if use_bundles and use_cdn %}
diff --git a/nikola/data/themes/bootblog4/templates/base_helper.tmpl b/nikola/data/themes/bootblog4/templates/base_helper.tmpl
index 33414d8..7ba4e92 100644
--- a/nikola/data/themes/bootblog4/templates/base_helper.tmpl
+++ b/nikola/data/themes/bootblog4/templates/base_helper.tmpl
@@ -64,9 +64,9 @@ lang="${lang}">
<%def name="late_load_js()">
%if use_cdn:
- <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
+ <script src="https://code.jquery.com/jquery-3.6.4.min.js" integrity="sha256-oP6HI9z1XaZNBrJURtCoUT5SUnxFr8s3BzRl+cbzUq8=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
- <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.min.js" integrity="sha384-VHvPCCyXqtD5DqJeNxl2dtTyhF78xXNXdkwX1CZeRusQfRKp+tA7hAShOK/B/fQ2" crossorigin="anonymous"></script>
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.min.js" integrity="sha384-+sLIOodYLS7CIrQpBjl+C7nPvqq+FbNUBDunl/OZv93DB7Ln/533i8e/mZXLi/P+" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/baguettebox.js/1.11.1/baguetteBox.min.js" integrity="sha256-ULQV01VS9LCI2ePpLsmka+W0mawFpEA0rtxnezUj4A4=" crossorigin="anonymous"></script>
% endif
%if use_bundles and use_cdn:
@@ -86,7 +86,7 @@ lang="${lang}">
<script src="https://polyfill.io/v3/polyfill.js?features=Intl.RelativeTimeFormat.%7Elocale.${luxon_locales[lang]}"></script>
%endif
%if use_cdn:
- <script src="https://cdn.jsdelivr.net/npm/luxon@1.28.0/build/global/luxon.min.js" integrity="sha256-l1u7Y5ze+ENf/T9ORPa3E642/uMgHUFa1KnqzFCcWEY=" crossorigin="anonymous"></script>
+ <script src="https://cdn.jsdelivr.net/npm/luxon@3.3.0/build/global/luxon.min.js" integrity="sha256-Nn+JGDrq3PuTxcDfJmmI0Srj5LpfOFlKqEiPwQK7y40=" crossorigin="anonymous"></script>
%else:
<script src="/assets/js/luxon.min.js"></script>
%endif
@@ -100,7 +100,7 @@ lang="${lang}">
<%def name="html_stylesheets()">
% if use_cdn:
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css" integrity="sha384-zCbKRCUGaJDkqS1kPbPd7TveP5iyJE0EjAuZQTgFLD2ylzuqKfdKlfG/eSrtxUkn" crossorigin="anonymous">
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css" integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/baguettebox.js/1.11.1/baguetteBox.min.css" integrity="sha256-cLMYWYYutHkt+KpNqjg7NVkYSQ+E2VbrXsEvOqU7mL0=" crossorigin="anonymous">
% endif
%if use_bundles and use_cdn:
diff --git a/nikola/data/themes/bootstrap4-jinja/README.md b/nikola/data/themes/bootstrap4-jinja/README.md
index bb1b484..8cb5f56 100644
--- a/nikola/data/themes/bootstrap4-jinja/README.md
+++ b/nikola/data/themes/bootstrap4-jinja/README.md
@@ -6,5 +6,5 @@ content layout. For a more blog-style layout, check out `bootblog4`.
Note that unlike previous versions of Bootstrap, icon fonts are not built-in.
You can use Font Awesome for this.
-This theme supports Bootswatch font/color schemes through the `nikola
-bootwatch_theme` command.
+This theme supports Bootswatch and HackerThemes font/color schemes
+through the `nikola subtheme` command.
diff --git a/nikola/data/themes/bootstrap4-jinja/templates/base_helper.tmpl b/nikola/data/themes/bootstrap4-jinja/templates/base_helper.tmpl
index ff1bfa2..167e8df 100644
--- a/nikola/data/themes/bootstrap4-jinja/templates/base_helper.tmpl
+++ b/nikola/data/themes/bootstrap4-jinja/templates/base_helper.tmpl
@@ -64,9 +64,9 @@ lang="{{ lang }}">
{% macro late_load_js() %}
{% if use_cdn %}
- <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
+ <script src="https://code.jquery.com/jquery-3.6.4.min.js" integrity="sha256-oP6HI9z1XaZNBrJURtCoUT5SUnxFr8s3BzRl+cbzUq8=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
- <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.min.js" integrity="sha384-VHvPCCyXqtD5DqJeNxl2dtTyhF78xXNXdkwX1CZeRusQfRKp+tA7hAShOK/B/fQ2" crossorigin="anonymous"></script>
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.min.js" integrity="sha384-+sLIOodYLS7CIrQpBjl+C7nPvqq+FbNUBDunl/OZv93DB7Ln/533i8e/mZXLi/P+" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/baguettebox.js/1.11.1/baguetteBox.min.js" integrity="sha256-ULQV01VS9LCI2ePpLsmka+W0mawFpEA0rtxnezUj4A4=" crossorigin="anonymous"></script>
{% endif %}
{% if use_bundles and use_cdn %}
@@ -86,7 +86,7 @@ lang="{{ lang }}">
<script src="https://polyfill.io/v3/polyfill.js?features=Intl.RelativeTimeFormat.%7Elocale.{{ luxon_locales[lang] }}"></script>
{% endif %}
{% if use_cdn %}
- <script src="https://cdn.jsdelivr.net/npm/luxon@1.28.0/build/global/luxon.min.js" integrity="sha256-l1u7Y5ze+ENf/T9ORPa3E642/uMgHUFa1KnqzFCcWEY=" crossorigin="anonymous"></script>
+ <script src="https://cdn.jsdelivr.net/npm/luxon@3.3.0/build/global/luxon.min.js" integrity="sha256-Nn+JGDrq3PuTxcDfJmmI0Srj5LpfOFlKqEiPwQK7y40=" crossorigin="anonymous"></script>
{% else %}
<script src="/assets/js/luxon.min.js"></script>
{% endif %}
@@ -100,7 +100,7 @@ lang="{{ lang }}">
{% macro html_stylesheets() %}
{% if use_cdn %}
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css" integrity="sha384-zCbKRCUGaJDkqS1kPbPd7TveP5iyJE0EjAuZQTgFLD2ylzuqKfdKlfG/eSrtxUkn" crossorigin="anonymous">
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css" integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/baguettebox.js/1.11.1/baguetteBox.min.css" integrity="sha256-cLMYWYYutHkt+KpNqjg7NVkYSQ+E2VbrXsEvOqU7mL0=" crossorigin="anonymous">
{% endif %}
{% if use_bundles and use_cdn %}
diff --git a/nikola/data/themes/bootstrap4-jinja/templates/listing.tmpl b/nikola/data/themes/bootstrap4-jinja/templates/listing.tmpl
index 56a1b4f..f76b560 100644
--- a/nikola/data/themes/bootstrap4-jinja/templates/listing.tmpl
+++ b/nikola/data/themes/bootstrap4-jinja/templates/listing.tmpl
@@ -6,10 +6,10 @@
{% if folders or files %}
<ul>
{% for name in folders %}
- <li><a href="{{ name|e }}">📂&nbsp;{{ name|e }}</a>
+ <li><a href="{{ name|e }}">&#x1f4c2;&nbsp;{{ name|e }}</a>
{% endfor %}
{% for name in files %}
- <li><a href="{{ name|e }}.html">📄&nbsp;{{ name|e }}</a>
+ <li><a href="{{ name|e }}.html">&#x1f4c4;&nbsp;{{ name|e }}</a>
{% endfor %}
</ul>
{% endif %}
diff --git a/nikola/data/themes/bootstrap4/README.md b/nikola/data/themes/bootstrap4/README.md
index bb1b484..8cb5f56 100644
--- a/nikola/data/themes/bootstrap4/README.md
+++ b/nikola/data/themes/bootstrap4/README.md
@@ -6,5 +6,5 @@ content layout. For a more blog-style layout, check out `bootblog4`.
Note that unlike previous versions of Bootstrap, icon fonts are not built-in.
You can use Font Awesome for this.
-This theme supports Bootswatch font/color schemes through the `nikola
-bootwatch_theme` command.
+This theme supports Bootswatch and HackerThemes font/color schemes
+through the `nikola subtheme` command.
diff --git a/nikola/data/themes/bootstrap4/templates/base_helper.tmpl b/nikola/data/themes/bootstrap4/templates/base_helper.tmpl
index 32ca42b..c5895cd 100644
--- a/nikola/data/themes/bootstrap4/templates/base_helper.tmpl
+++ b/nikola/data/themes/bootstrap4/templates/base_helper.tmpl
@@ -64,9 +64,9 @@ lang="${lang}">
<%def name="late_load_js()">
%if use_cdn:
- <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
+ <script src="https://code.jquery.com/jquery-3.6.4.min.js" integrity="sha256-oP6HI9z1XaZNBrJURtCoUT5SUnxFr8s3BzRl+cbzUq8=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
- <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.min.js" integrity="sha384-VHvPCCyXqtD5DqJeNxl2dtTyhF78xXNXdkwX1CZeRusQfRKp+tA7hAShOK/B/fQ2" crossorigin="anonymous"></script>
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.min.js" integrity="sha384-+sLIOodYLS7CIrQpBjl+C7nPvqq+FbNUBDunl/OZv93DB7Ln/533i8e/mZXLi/P+" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/baguettebox.js/1.11.1/baguetteBox.min.js" integrity="sha256-ULQV01VS9LCI2ePpLsmka+W0mawFpEA0rtxnezUj4A4=" crossorigin="anonymous"></script>
% endif
%if use_bundles and use_cdn:
@@ -86,7 +86,7 @@ lang="${lang}">
<script src="https://polyfill.io/v3/polyfill.js?features=Intl.RelativeTimeFormat.%7Elocale.${luxon_locales[lang]}"></script>
%endif
%if use_cdn:
- <script src="https://cdn.jsdelivr.net/npm/luxon@1.28.0/build/global/luxon.min.js" integrity="sha256-l1u7Y5ze+ENf/T9ORPa3E642/uMgHUFa1KnqzFCcWEY=" crossorigin="anonymous"></script>
+ <script src="https://cdn.jsdelivr.net/npm/luxon@3.3.0/build/global/luxon.min.js" integrity="sha256-Nn+JGDrq3PuTxcDfJmmI0Srj5LpfOFlKqEiPwQK7y40=" crossorigin="anonymous"></script>
%else:
<script src="/assets/js/luxon.min.js"></script>
%endif
@@ -100,7 +100,7 @@ lang="${lang}">
<%def name="html_stylesheets()">
%if use_cdn:
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css" integrity="sha384-zCbKRCUGaJDkqS1kPbPd7TveP5iyJE0EjAuZQTgFLD2ylzuqKfdKlfG/eSrtxUkn" crossorigin="anonymous">
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css" integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/baguettebox.js/1.11.1/baguetteBox.min.css" integrity="sha256-cLMYWYYutHkt+KpNqjg7NVkYSQ+E2VbrXsEvOqU7mL0=" crossorigin="anonymous">
% endif
%if use_bundles and use_cdn:
diff --git a/nikola/data/themes/bootstrap4/templates/listing.tmpl b/nikola/data/themes/bootstrap4/templates/listing.tmpl
index d9a4c56..6d22642 100644
--- a/nikola/data/themes/bootstrap4/templates/listing.tmpl
+++ b/nikola/data/themes/bootstrap4/templates/listing.tmpl
@@ -6,10 +6,10 @@ ${ui.breadcrumbs(crumbs)}
%if folders or files:
<ul>
% for name in folders:
- <li><a href="${name|h}">📂&nbsp;${name|h}</a>
+ <li><a href="${name|h}">&#x1f4c2;&nbsp;${name|h}</a>
% endfor
% for name in files:
- <li><a href="${name|h}.html">📄&nbsp;${name|h}</a>
+ <li><a href="${name|h}.html">&#x1f4c4;&nbsp;${name|h}</a>
% endfor
</ul>
%endif
diff --git a/nikola/filters.py b/nikola/filters.py
index 8deeb89..5bce4e0 100644
--- a/nikola/filters.py
+++ b/nikola/filters.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -359,33 +359,33 @@ def php_template_injection(data):
@apply_to_text_file
def cssminify(data):
- """Minify CSS using https://cssminifier.com/."""
+ """Minify CSS using <https://www.toptal.com/developers/cssminifier>."""
try:
- url = 'https://cssminifier.com/raw'
+ url = 'https://www.toptal.com/developers/cssminifier/api/raw'
_data = {'input': data}
response = requests.post(url, data=_data)
if response.status_code != 200:
- LOGGER.error("can't use cssminifier.com: HTTP status {}", response.status_code)
+ LOGGER.error("Can't use toptal.com CSS Minifier: HTTP status {}", response.status_code)
return data
return response.text
except Exception as exc:
- LOGGER.error("can't use cssminifier.com: {}", exc)
+ LOGGER.error("Can't use toptal.com CSS Minifier: {}", exc)
return data
@apply_to_text_file
def jsminify(data):
- """Minify JS using https://javascript-minifier.com/."""
+ """Minify JS using <https://www.toptal.com/developers/javascript-minifier>."""
try:
- url = 'https://javascript-minifier.com/raw'
+ url = 'https://www.toptal.com/developers/javascript-minifier/api/raw'
_data = {'input': data}
response = requests.post(url, data=_data)
if response.status_code != 200:
- LOGGER.error("can't use javascript-minifier.com: HTTP status {}", response.status_code)
+ LOGGER.error("Can't use toptal.com JavaScript Minifier: HTTP status {}", response.status_code)
return data
return response.text
except Exception as exc:
- LOGGER.error("can't use javascript-minifier.com: {}", exc)
+ LOGGER.error("Can't use toptal.com JavaScript Minifier: {}", exc)
return data
diff --git a/nikola/hierarchy_utils.py b/nikola/hierarchy_utils.py
index 7e8d9b8..929138b 100644
--- a/nikola/hierarchy_utils.py
+++ b/nikola/hierarchy_utils.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/image_processing.py b/nikola/image_processing.py
index 4ac6d93..9f4d49c 100644
--- a/nikola/image_processing.py
+++ b/nikola/image_processing.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2014 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -145,7 +145,7 @@ class ImageProcessor(object):
if bigger_panoramas and w > 3 * h:
size = min(w, max_size * 4), min(w, max_size * 4)
try:
- im.thumbnail(size, Image.ANTIALIAS)
+ im.thumbnail(size, Image.Resampling.LANCZOS)
save_args = {}
if icc_profile:
save_args['icc_profile'] = icc_profile
diff --git a/nikola/log.py b/nikola/log.py
index eedee72..f998427 100644
--- a/nikola/log.py
+++ b/nikola/log.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/metadata_extractors.py b/nikola/metadata_extractors.py
index 4e0ae10..1b33657 100644
--- a/nikola/metadata_extractors.py
+++ b/nikola/metadata_extractors.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Chris Warrick, Roberto Alsina and others.
+# Copyright © 2012-2024 Chris Warrick, Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/nikola.py b/nikola/nikola.py
index 41eeac6..916ca69 100644
--- a/nikola/nikola.py
+++ b/nikola/nikola.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -33,7 +33,9 @@ import functools
import logging
import operator
import os
+import pathlib
import sys
+import typing
import mimetypes
from collections import defaultdict
from copy import copy
@@ -46,27 +48,14 @@ import natsort
import PyRSS2Gen as rss
from pkg_resources import resource_filename
from blinker import signal
-from yapsy.PluginManager import PluginManager
from . import DEBUG, SHOW_TRACEBACKS, filters, utils, hierarchy_utils, shortcodes
from . import metadata_extractors
from .metadata_extractors import default_metadata_extractors_by
from .post import Post # NOQA
+from .plugin_manager import PluginCandidate, PluginInfo, PluginManager
from .plugin_categories import (
- Command,
- LateTask,
- PageCompiler,
- CompilerExtension,
- MarkdownExtension,
- RestExtension,
- MetadataExtractor,
- ShortcodePlugin,
- Task,
- TaskMultiplier,
TemplateSystem,
- SignalHandler,
- ConfigPlugin,
- CommentSystem,
PostScanner,
Taxonomy,
)
@@ -96,6 +85,7 @@ LEGAL_VALUES = {
'DEFAULT_THEME': 'bootblog4',
'COMMENT_SYSTEM': [
'disqus',
+ 'discourse',
'facebook',
'intensedebate',
'isso',
@@ -381,6 +371,9 @@ class Nikola(object):
Takes a site config as argument on creation.
"""
+ plugin_manager: PluginManager
+ _template_system: TemplateSystem
+
def __init__(self, **config):
"""Initialize proper environment for running tasks."""
# Register our own path handlers
@@ -891,6 +884,7 @@ class Nikola(object):
utils.LOGGER.warning('You are currently disabling "{}", but not the following new taxonomy plugins: {}'.format(old_plugin_name, ', '.join(missing_plugins)))
utils.LOGGER.warning('Please also disable these new plugins or remove "{}" from the DISABLED_PLUGINS list.'.format(old_plugin_name))
self.config['DISABLED_PLUGINS'].extend(missing_plugins)
+
# Special-case logic for "render_indexes" to fix #2591
if 'render_indexes' in self.config['DISABLED_PLUGINS']:
if 'generate_rss' in self.config['DISABLED_PLUGINS'] or self.config['GENERATE_RSS'] is False:
@@ -1000,57 +994,50 @@ class Nikola(object):
self.state._set_site(self)
self.cache._set_site(self)
- def _filter_duplicate_plugins(self, plugin_list):
+ # WebP files have no official MIME type yet, but we need to recognize them (Issue #3671)
+ mimetypes.add_type('image/webp', '.webp')
+
+ def _filter_duplicate_plugins(self, plugin_list: typing.Iterable[PluginCandidate]):
"""Find repeated plugins and discard the less local copy."""
- def plugin_position_in_places(plugin):
+ def plugin_position_in_places(plugin: PluginInfo):
# plugin here is a tuple:
# (path to the .plugin file, path to plugin module w/o .py, plugin metadata)
for i, place in enumerate(self._plugin_places):
- if plugin[0].startswith(place):
+ place: pathlib.Path
+ try:
+ # Path.is_relative_to backport
+ plugin.source_dir.relative_to(place)
return i
- utils.LOGGER.warn("Duplicate plugin found in unexpected location: {}".format(plugin[0]))
+ except ValueError:
+ pass
+ utils.LOGGER.warning("Duplicate plugin found in unexpected location: {}".format(plugin.source_dir))
return len(self._plugin_places)
plugin_dict = defaultdict(list)
- for data in plugin_list:
- plugin_dict[data[2].name].append(data)
+ for plugin in plugin_list:
+ plugin_dict[plugin.name].append(plugin)
result = []
- for _, plugins in plugin_dict.items():
+ for name, plugins in plugin_dict.items():
if len(plugins) > 1:
# Sort by locality
plugins.sort(key=plugin_position_in_places)
utils.LOGGER.debug("Plugin {} exists in multiple places, using {}".format(
- plugins[-1][2].name, plugins[-1][0]))
+ name, plugins[-1].source_dir))
result.append(plugins[-1])
return result
def init_plugins(self, commands_only=False, load_all=False):
"""Load plugins as needed."""
- self.plugin_manager = PluginManager(categories_filter={
- "Command": Command,
- "Task": Task,
- "LateTask": LateTask,
- "TemplateSystem": TemplateSystem,
- "PageCompiler": PageCompiler,
- "TaskMultiplier": TaskMultiplier,
- "CompilerExtension": CompilerExtension,
- "MarkdownExtension": MarkdownExtension,
- "RestExtension": RestExtension,
- "MetadataExtractor": MetadataExtractor,
- "ShortcodePlugin": ShortcodePlugin,
- "SignalHandler": SignalHandler,
- "ConfigPlugin": ConfigPlugin,
- "CommentSystem": CommentSystem,
- "PostScanner": PostScanner,
- "Taxonomy": Taxonomy,
- })
- self.plugin_manager.getPluginLocator().setPluginInfoExtension('plugin')
extra_plugins_dirs = self.config['EXTRA_PLUGINS_DIRS']
+ self._loading_commands_only = commands_only
self._plugin_places = [
resource_filename('nikola', 'plugins'),
os.path.expanduser(os.path.join('~', '.nikola', 'plugins')),
os.path.join(os.getcwd(), 'plugins'),
] + [path for path in extra_plugins_dirs if path]
+ self._plugin_places = [pathlib.Path(p) for p in self._plugin_places]
+
+ self.plugin_manager = PluginManager(plugin_places=self._plugin_places)
compilers = defaultdict(set)
# Also add aliases for combinations with TRANSLATIONS_PATTERN
@@ -1069,41 +1056,37 @@ class Nikola(object):
self.disabled_compilers = {}
self.disabled_compiler_extensions = defaultdict(list)
- self.plugin_manager.getPluginLocator().setPluginPlaces(self._plugin_places)
- self.plugin_manager.locatePlugins()
- bad_candidates = set([])
+ candidates = self.plugin_manager.locate_plugins()
+ good_candidates = set()
if not load_all:
- for p in self.plugin_manager._candidates:
+ for p in candidates:
if commands_only:
- if p[-1].details.has_option('Nikola', 'PluginCategory'):
- # FIXME TemplateSystem should not be needed
- if p[-1].details.get('Nikola', 'PluginCategory') not in {'Command', 'Template'}:
- bad_candidates.add(p)
- else:
- bad_candidates.add(p)
+ if p.category != 'Command':
+ continue
elif self.configured: # Not commands-only, and configured
# Remove blacklisted plugins
- if p[-1].name in self.config['DISABLED_PLUGINS']:
- bad_candidates.add(p)
- utils.LOGGER.debug('Not loading disabled plugin {}', p[-1].name)
- # Remove compilers we don't use
- if p[-1].details.has_option('Nikola', 'PluginCategory') and p[-1].details.get('Nikola', 'PluginCategory') in ('Compiler', 'PageCompiler'):
- bad_candidates.add(p)
- self.disabled_compilers[p[-1].name] = p
+ if p.name in self.config['DISABLED_PLUGINS']:
+ utils.LOGGER.debug('Not loading disabled plugin {}', p.name)
+ continue
+ # Remove compilers - will be loaded later based on usage
+ if p.category == "PageCompiler":
+ self.disabled_compilers[p.name] = p
+ continue
# Remove compiler extensions we don't need
- if p[-1].details.has_option('Nikola', 'compiler') and p[-1].details.get('Nikola', 'compiler') in self.disabled_compilers:
- bad_candidates.add(p)
- self.disabled_compiler_extensions[p[-1].details.get('Nikola', 'compiler')].append(p)
- self.plugin_manager._candidates = list(set(self.plugin_manager._candidates) - bad_candidates)
+ if p.compiler and p.compiler in self.disabled_compilers:
+ self.disabled_compiler_extensions[p.compiler].append(p)
+ continue
+ good_candidates.add(p)
- self.plugin_manager._candidates = self._filter_duplicate_plugins(self.plugin_manager._candidates)
- self.plugin_manager.loadPlugins()
+ good_candidates = self._filter_duplicate_plugins(good_candidates)
+ self.plugin_manager.load_plugins(good_candidates)
# Search for compiler plugins which we disabled but shouldn't have
self._activate_plugins_of_category("PostScanner")
if not load_all:
file_extensions = set()
- for post_scanner in [p.plugin_object for p in self.plugin_manager.getPluginsOfCategory('PostScanner')]:
+ for post_scanner in [p.plugin_object for p in self.plugin_manager.get_plugins_of_category('PostScanner')]:
+ post_scanner: PostScanner
exts = post_scanner.supported_extensions()
if exts is not None:
file_extensions.update(exts)
@@ -1122,13 +1105,13 @@ class Nikola(object):
for p in self.disabled_compiler_extensions.pop(k, []):
to_add.append(p)
for _, p in self.disabled_compilers.items():
- utils.LOGGER.debug('Not loading unneeded compiler {}', p[-1].name)
+ utils.LOGGER.debug('Not loading unneeded compiler %s', p.name)
for _, plugins in self.disabled_compiler_extensions.items():
for p in plugins:
- utils.LOGGER.debug('Not loading compiler extension {}', p[-1].name)
+ utils.LOGGER.debug('Not loading compiler extension %s', p.name)
if to_add:
- self.plugin_manager._candidates = self._filter_duplicate_plugins(to_add)
- self.plugin_manager.loadPlugins()
+ extra_candidates = self._filter_duplicate_plugins(to_add)
+ self.plugin_manager.load_plugins(extra_candidates)
# Jupyter theme configuration. If a website has ipynb enabled in post_pages
# we should enable the Jupyter CSS (leaving that up to the theme itself).
@@ -1141,7 +1124,8 @@ class Nikola(object):
self._activate_plugins_of_category("Taxonomy")
self.taxonomy_plugins = {}
- for taxonomy in [p.plugin_object for p in self.plugin_manager.getPluginsOfCategory('Taxonomy')]:
+ for taxonomy in [p.plugin_object for p in self.plugin_manager.get_plugins_of_category('Taxonomy')]:
+ taxonomy: Taxonomy
if not taxonomy.is_enabled():
continue
if taxonomy.classification_name in self.taxonomy_plugins:
@@ -1167,10 +1151,9 @@ class Nikola(object):
# Activate all required compiler plugins
self.compiler_extensions = self._activate_plugins_of_category("CompilerExtension")
- for plugin_info in self.plugin_manager.getPluginsOfCategory("PageCompiler"):
+ for plugin_info in self.plugin_manager.get_plugins_of_category("PageCompiler"):
if plugin_info.name in self.config["COMPILERS"].keys():
- self.plugin_manager.activatePluginByName(plugin_info.name)
- plugin_info.plugin_object.set_site(self)
+ self._activate_plugin(plugin_info)
# Activate shortcode plugins
self._activate_plugins_of_category("ShortcodePlugin")
@@ -1179,10 +1162,8 @@ class Nikola(object):
self.compilers = {}
self.inverse_compilers = {}
- for plugin_info in self.plugin_manager.getPluginsOfCategory(
- "PageCompiler"):
- self.compilers[plugin_info.name] = \
- plugin_info.plugin_object
+ for plugin_info in self.plugin_manager.get_plugins_of_category("PageCompiler"):
+ self.compilers[plugin_info.name] = plugin_info.plugin_object
# Load comment systems, config plugins and register templated shortcodes
self._activate_plugins_of_category("CommentSystem")
@@ -1325,13 +1306,26 @@ class Nikola(object):
self.ALL_PAGE_DEPS['index_read_more_link'] = self.config.get('INDEX_READ_MORE_LINK')
self.ALL_PAGE_DEPS['feed_read_more_link'] = self.config.get('FEED_READ_MORE_LINK')
- def _activate_plugins_of_category(self, category):
+ def _activate_plugin(self, plugin_info: PluginInfo) -> None:
+ plugin_info.plugin_object.set_site(self)
+
+ if plugin_info.category == "TemplateSystem" or self._loading_commands_only:
+ return
+
+ templates_directory_candidates = [
+ plugin_info.source_dir / "templates" / self.template_system.name,
+ plugin_info.source_dir / plugin_info.module_name / "templates" / self.template_system.name
+ ]
+ for candidate in templates_directory_candidates:
+ if candidate.exists() and candidate.is_dir():
+ self.template_system.inject_directory(str(candidate))
+
+ def _activate_plugins_of_category(self, category) -> typing.List[PluginInfo]:
"""Activate all the plugins of a given category and return them."""
# this code duplicated in tests/base.py
plugins = []
- for plugin_info in self.plugin_manager.getPluginsOfCategory(category):
- self.plugin_manager.activatePluginByName(plugin_info.name)
- plugin_info.plugin_object.set_site(self)
+ for plugin_info in self.plugin_manager.get_plugins_of_category(category):
+ self._activate_plugin(plugin_info)
plugins.append(plugin_info)
return plugins
@@ -1395,13 +1389,12 @@ class Nikola(object):
if self._template_system is None:
# Load template plugin
template_sys_name = utils.get_template_engine(self.THEMES)
- pi = self.plugin_manager.getPluginByName(
- template_sys_name, "TemplateSystem")
+ pi = self.plugin_manager.get_plugin_by_name(template_sys_name, "TemplateSystem")
if pi is None:
sys.stderr.write("Error loading {0} template system "
"plugin\n".format(template_sys_name))
sys.exit(1)
- self._template_system = pi.plugin_object
+ self._template_system = typing.cast(TemplateSystem, pi.plugin_object)
lookup_dirs = ['templates'] + [os.path.join(utils.get_theme_path(name), "templates")
for name in self.THEMES]
self._template_system.set_directories(lookup_dirs,
@@ -1921,7 +1914,8 @@ class Nikola(object):
else:
return link
else:
- return os.path.join(*path)
+ # URLs should always use forward slash separators, even on Windows
+ return str(pathlib.PurePosixPath(*path))
def post_path(self, name, lang):
"""Link to the destination of an element in the POSTS/PAGES settings.
@@ -2069,7 +2063,7 @@ class Nikola(object):
yield ft
task_dep = []
- for pluginInfo in self.plugin_manager.getPluginsOfCategory(plugin_category):
+ for pluginInfo in self.plugin_manager.get_plugins_of_category(plugin_category):
for task in flatten(pluginInfo.plugin_object.gen_tasks()):
if 'basename' not in task:
raise ValueError("Task {0} does not have a basename".format(task))
@@ -2078,7 +2072,7 @@ class Nikola(object):
task['task_dep'] = []
task['task_dep'].extend(self.injected_deps[task['basename']])
yield task
- for multi in self.plugin_manager.getPluginsOfCategory("TaskMultiplier"):
+ for multi in self.plugin_manager.get_plugins_of_category("TaskMultiplier"):
flag = False
for task in multi.plugin_object.process(task, name):
flag = True
@@ -2187,7 +2181,7 @@ class Nikola(object):
self.timeline = []
self.pages = []
- for p in sorted(self.plugin_manager.getPluginsOfCategory('PostScanner'), key=operator.attrgetter('name')):
+ for p in sorted(self.plugin_manager.get_plugins_of_category('PostScanner'), key=operator.attrgetter('name')):
try:
timeline = p.plugin_object.scan()
except Exception:
diff --git a/nikola/packages/datecond/LICENSE b/nikola/packages/datecond/LICENSE
index fcc5730..07d9028 100644
--- a/nikola/packages/datecond/LICENSE
+++ b/nikola/packages/datecond/LICENSE
@@ -1,4 +1,4 @@
-Copyright © 2016-2022, Chris Warrick.
+Copyright © 2016-2024, Chris Warrick.
All rights reserved.
Redistribution and use in source and binary forms, with or without
diff --git a/nikola/packages/datecond/__init__.py b/nikola/packages/datecond/__init__.py
index 4e9566c..14065da 100644
--- a/nikola/packages/datecond/__init__.py
+++ b/nikola/packages/datecond/__init__.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# Date Conditionals v0.1.7
-# Copyright © 2015-2022, Chris Warrick.
+# Copyright © 2015-2024, Chris Warrick.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
diff --git a/nikola/packages/pygments_better_html/LICENSE b/nikola/packages/pygments_better_html/LICENSE
index 8312fb8..7e06604 100644
--- a/nikola/packages/pygments_better_html/LICENSE
+++ b/nikola/packages/pygments_better_html/LICENSE
@@ -1,4 +1,4 @@
-Copyright © 2020-2022, Chris Warrick.
+Copyright © 2020-2024, Chris Warrick.
All rights reserved.
Redistribution and use in source and binary forms, with or without
diff --git a/nikola/packages/pygments_better_html/__init__.py b/nikola/packages/pygments_better_html/__init__.py
index 565dcb4..3fbc565 100644
--- a/nikola/packages/pygments_better_html/__init__.py
+++ b/nikola/packages/pygments_better_html/__init__.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
"""Better HTML formatter for Pygments.
-Copyright © 2020-2022, Chris Warrick.
+Copyright © 2020-2024, Chris Warrick.
License: 3-clause BSD.
Portions copyright © 2006-2019, the Pygments authors. (2-clause BSD).
"""
diff --git a/nikola/plugin_categories.py b/nikola/plugin_categories.py
index 14760df..b9eee3a 100644
--- a/nikola/plugin_categories.py
+++ b/nikola/plugin_categories.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -29,12 +29,10 @@
import io
import logging
import os
-import sys
import typing
import doit
from doit.cmd_base import Command as DoitCommand
-from yapsy.IPlugin import IPlugin
from .utils import LOGGER, first_line, get_logger, req_missing
@@ -59,7 +57,7 @@ __all__ = (
)
-class BasePlugin(IPlugin):
+class BasePlugin:
"""Base plugin class."""
logger = None
@@ -67,28 +65,13 @@ class BasePlugin(IPlugin):
def set_site(self, site):
"""Set site, which is a Nikola instance."""
self.site = site
- self.inject_templates()
self.logger = get_logger(self.name)
if not site.debug:
self.logger.level = logging.INFO
- def inject_templates(self):
- """Inject 'templates/<engine>' (if exists) very early in the theme chain."""
- try:
- # Sorry, found no other way to get this
- mod_path = sys.modules[self.__class__.__module__].__file__
- mod_dir = os.path.dirname(mod_path)
- tmpl_dir = os.path.join(
- mod_dir, 'templates', self.site.template_system.name
- )
- if os.path.isdir(tmpl_dir):
- # Inject tmpl_dir low in the theme chain
- self.site.template_system.inject_directory(tmpl_dir)
- except AttributeError:
- # In some cases, __builtin__ becomes the module of a plugin.
- # We couldn’t reproduce that, and really find the reason for this,
- # so let’s just ignore it and be done with it.
- pass
+ def set_module_path(self, module_path):
+ """Set the plugin's module path."""
+ self.module_path = module_path
def inject_dependency(self, target, dependency):
"""Add 'dependency' to the target task's task_deps."""
@@ -359,7 +342,7 @@ class PageCompiler(BasePlugin):
"""Activate all the compiler extension plugins for a given compiler and return them."""
plugins = []
for plugin_info in self.site.compiler_extensions:
- if plugin_info.plugin_object.compiler_name == self.name:
+ if plugin_info.compiler == self.name or plugin_info.plugin_object.compiler_name == self.name:
plugins.append(plugin_info)
return plugins
@@ -371,11 +354,8 @@ class CompilerExtension(BasePlugin):
(a) create a new plugin class for them; or
(b) use this class and filter them yourself.
If you choose (b), you should the compiler name to the .plugin
- file in the Nikola/Compiler section and filter all plugins of
- this category, getting the compiler name with:
- p.details.get('Nikola', 'Compiler')
- Note that not all compiler plugins have this option and you might
- need to catch configparser.NoOptionError exceptions.
+ file in the Nikola/compiler section and filter all plugins of
+ this category, getting the compiler name with `plugin_info.compiler`.
"""
name = "dummy_compiler_extension"
@@ -904,3 +884,23 @@ class Taxonomy(BasePlugin):
Provided is a set of classifications per language (`classifications_per_language`).
"""
return []
+
+
+CATEGORIES = {
+ "Command": Command,
+ "Task": Task,
+ "LateTask": LateTask,
+ "TemplateSystem": TemplateSystem,
+ "PageCompiler": PageCompiler,
+ "TaskMultiplier": TaskMultiplier,
+ "CompilerExtension": CompilerExtension,
+ "MarkdownExtension": MarkdownExtension,
+ "RestExtension": RestExtension,
+ "MetadataExtractor": MetadataExtractor,
+ "ShortcodePlugin": ShortcodePlugin,
+ "SignalHandler": SignalHandler,
+ "ConfigPlugin": ConfigPlugin,
+ "CommentSystem": CommentSystem,
+ "PostScanner": PostScanner,
+ "Taxonomy": Taxonomy,
+}
diff --git a/nikola/plugin_manager.py b/nikola/plugin_manager.py
new file mode 100644
index 0000000..5c1a29a
--- /dev/null
+++ b/nikola/plugin_manager.py
@@ -0,0 +1,264 @@
+# -*- coding: utf-8 -*-
+
+# Copyright © 2012-2024 Chris Warrick and others.
+
+# Permission is hereby granted, free of charge, to any
+# person obtaining a copy of this software and associated
+# documentation files (the "Software"), to deal in the
+# Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the
+# Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice
+# shall be included in all copies or substantial portions of
+# the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
+# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+"""The Nikola plugin manager. Inspired by yapsy."""
+
+import configparser
+import importlib
+import importlib.util
+import time
+import sys
+from dataclasses import dataclass
+from pathlib import Path
+from typing import Dict, List, Optional, Type, TYPE_CHECKING, Set
+
+from .plugin_categories import BasePlugin, CATEGORIES
+from .utils import get_logger
+
+if TYPE_CHECKING:
+ import logging
+
+LEGACY_PLUGIN_NAMES: Dict[str, str] = {
+ "Compiler": "PageCompiler",
+ "Shortcode": "ShortcodePlugin",
+ "Template": "TemplateSystem",
+}
+
+CATEGORY_NAMES: Set[str] = set(CATEGORIES.keys())
+CATEGORY_TYPES: Set[Type[BasePlugin]] = set(CATEGORIES.values())
+
+
+@dataclass(frozen=True)
+class PluginCandidate:
+ """A candidate plugin that was located but not yet loaded (imported)."""
+
+ name: str
+ description: Optional[str]
+ plugin_id: str
+ category: str
+ compiler: Optional[str]
+ source_dir: Path
+ module_name: str
+
+
+@dataclass(frozen=True)
+class PluginInfo:
+ """A plugin that was loaded (imported)."""
+
+ name: str
+ description: Optional[str]
+ plugin_id: str
+ category: str
+ compiler: Optional[str]
+ source_dir: Path
+ module_name: str
+ module_object: object
+ plugin_object: BasePlugin
+
+
+class PluginManager:
+ """The Nikola plugin manager."""
+
+ categories_filter: Dict[str, Type[BasePlugin]]
+ plugin_places: List[Path]
+ logger: "logging.Logger"
+ candidates: List[PluginCandidate]
+ plugins: List[PluginInfo]
+ _plugins_by_category: Dict[str, List[PluginInfo]]
+ has_warnings: bool = False
+
+ def __init__(self, plugin_places: List[Path]):
+ """Initialize the plugin manager."""
+ self.plugin_places = plugin_places
+ self.candidates = []
+ self.plugins = []
+ self._plugins_by_category = {}
+ self.logger = get_logger("PluginManager")
+
+ def locate_plugins(self) -> List[PluginCandidate]:
+ """Locate plugins in plugin_places."""
+ self.candidates = []
+
+ plugin_files: List[Path] = []
+ for place in self.plugin_places:
+ plugin_files += place.rglob("*.plugin")
+
+ for plugin_file in plugin_files:
+ source_dir = plugin_file.parent
+ config = configparser.ConfigParser()
+ config.read(plugin_file)
+ name = config["Core"]["name"]
+ module_name = config["Core"]["module"]
+ plugin_id = f"Plugin {name} from {plugin_file}"
+ description = None
+ if "Documentation" in config:
+ description = config["Documentation"].get("Description")
+ if "Nikola" not in config:
+ self.logger.warning(f"{plugin_id} does not specify Nikola configuration - plugin will not be loaded")
+ self.logger.warning("Please add a [Nikola] section to the {plugin_file} file with a PluginCategory entry")
+ self.has_warnings = True
+ continue
+ category = config["Nikola"].get("PluginCategory")
+ compiler = config["Nikola"].get("Compiler")
+ if not category:
+ self.logger.warning(f"{plugin_id} does not specify any category (Nikola.PluginCategory is missing in .plugin file) - plugin will not be loaded")
+ self.has_warnings = True
+ continue
+ if category in LEGACY_PLUGIN_NAMES:
+ category = LEGACY_PLUGIN_NAMES[category]
+ if category not in CATEGORY_NAMES:
+ self.logger.warning(f"{plugin_id} specifies invalid category '{category}' in the .plugin file - plugin will not be loaded")
+ self.has_warnings = True
+ continue
+ self.logger.debug(f"Discovered {plugin_id}")
+ self.candidates.append(
+ PluginCandidate(
+ name=name,
+ description=description,
+ plugin_id=plugin_id,
+ category=category,
+ compiler=compiler,
+ source_dir=source_dir,
+ module_name=module_name,
+ )
+ )
+ return self.candidates
+
+ def load_plugins(self, candidates: List[PluginCandidate]) -> None:
+ """Load selected candidate plugins."""
+ plugins_root = Path(__file__).parent.parent
+
+ for candidate in candidates:
+ name = candidate.name
+ module_name = candidate.module_name
+ source_dir = candidate.source_dir
+ py_file_location = source_dir / f"{module_name}.py"
+ plugin_id = candidate.plugin_id
+ if not py_file_location.exists():
+ py_file_location = source_dir / module_name / "__init__.py"
+ if not py_file_location.exists():
+ self.logger.warning(f"{plugin_id} could not be loaded (no valid module detected)")
+ self.has_warnings = True
+ continue
+
+ plugin_id += f" ({py_file_location})"
+ full_module_name = module_name
+
+ try:
+ name_parts = list(py_file_location.relative_to(plugins_root).parts)
+ if name_parts[-1] == "__init__.py":
+ name_parts.pop(-1)
+ elif name_parts[-1].endswith(".py"):
+ name_parts[-1] = name_parts[-1][:-3]
+ full_module_name = ".".join(name_parts)
+ except ValueError:
+ pass
+
+ try:
+ spec = importlib.util.spec_from_file_location(full_module_name, py_file_location)
+ module_object = importlib.util.module_from_spec(spec)
+ if full_module_name not in sys.modules:
+ sys.modules[full_module_name] = module_object
+ spec.loader.exec_module(module_object)
+ except Exception:
+ self.logger.exception(f"{plugin_id} threw an exception while loading")
+ self.has_warnings = True
+ continue
+
+ plugin_classes = [
+ c
+ for c in vars(module_object).values()
+ if isinstance(c, type) and issubclass(c, BasePlugin) and c not in CATEGORY_TYPES
+ ]
+ if len(plugin_classes) == 0:
+ self.logger.warning(f"{plugin_id} does not have any plugin classes - plugin will not be loaded")
+ self.has_warnings = True
+ continue
+ elif len(plugin_classes) > 1:
+ self.logger.warning(f"{plugin_id} has multiple plugin classes; this is not supported - plugin will not be loaded")
+ self.has_warnings = True
+ continue
+
+ plugin_class = plugin_classes[0]
+
+ if not issubclass(plugin_class, CATEGORIES[candidate.category]):
+ self.logger.warning(f"{plugin_id} has category '{candidate.category}' in the .plugin file, but the implementation class {plugin_class} does not inherit from this category - plugin will not be loaded")
+ self.has_warnings = True
+ continue
+
+ try:
+ plugin_object = plugin_class()
+ except Exception:
+ self.logger.exception(f"{plugin_id} threw an exception while creating the instance")
+ self.has_warnings = True
+ continue
+ self.logger.debug(f"Loaded {plugin_id}")
+ info = PluginInfo(
+ name=name,
+ description=candidate.description,
+ plugin_id=candidate.plugin_id,
+ category=candidate.category,
+ compiler=candidate.compiler,
+ source_dir=source_dir,
+ module_name=module_name,
+ module_object=module_object,
+ plugin_object=plugin_object,
+ )
+ self.plugins.append(info)
+
+ self._plugins_by_category = {category: [] for category in CATEGORY_NAMES}
+ for plugin_info in self.plugins:
+ self._plugins_by_category[plugin_info.category].append(plugin_info)
+
+ if self.has_warnings:
+ self.logger.warning("Some plugins failed to load. Please review the above warning messages.")
+ # TODO remove following messages and delay in v8.3.1
+ self.logger.warning("You may need to update some plugins (from plugins.getnikola.com) or to fix their .plugin files.")
+ self.logger.warning("Waiting 2 seconds before continuing.")
+ time.sleep(2)
+
+ def get_plugins_of_category(self, category: str) -> List[PluginInfo]:
+ """Get loaded plugins of a given category."""
+ return self._plugins_by_category.get(category, [])
+
+ def get_plugin_by_name(self, name: str, category: Optional[str] = None) -> Optional[PluginInfo]:
+ """Get a loaded plugin by name and optionally by category. Returns None if no such plugin is loaded."""
+ for p in self.plugins:
+ if p.name == name and (category is None or p.category == category):
+ return p
+
+ # Aliases for Yapsy compatibility
+ # TODO: remove in v9
+ def getPluginsOfCategory(self, category: str) -> List[PluginInfo]:
+ """Get loaded plugins of a given category."""
+ self.logger.warning("Legacy getPluginsOfCategory method was used, it may be removed in the future. Please change it to get_plugins_of_category.")
+ return self._plugins_by_category.get(category, [])
+
+ # TODO: remove in v9
+ def getPluginByName(self, name: str, category: Optional[str] = None) -> Optional[PluginInfo]:
+ """Get a loaded plugin by name and optionally by category. Returns None if no such plugin is loaded."""
+ self.logger.warning("Legacy getPluginByName method was used, it may be removed in the future. Please change it to get_plugin_by_name.")
+ return self.get_plugin_by_name(name, category)
diff --git a/nikola/plugins/basic_import.py b/nikola/plugins/basic_import.py
index ca88cfb..ad53572 100644
--- a/nikola/plugins/basic_import.py
+++ b/nikola/plugins/basic_import.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/command/__init__.py b/nikola/plugins/command/__init__.py
index 6c8e81a..e5cd68a 100644
--- a/nikola/plugins/command/__init__.py
+++ b/nikola/plugins/command/__init__.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/command/auto/__init__.py b/nikola/plugins/command/auto/__init__.py
index b13b645..d272c23 100644
--- a/nikola/plugins/command/auto/__init__.py
+++ b/nikola/plugins/command/auto/__init__.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Chris Warrick, Roberto Alsina and others.
+# Copyright © 2012-2024 Chris Warrick, Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -35,6 +35,7 @@ import stat
import subprocess
import sys
import typing
+import urllib.parse
import webbrowser
import blinker
@@ -67,6 +68,17 @@ if sys.platform == 'win32':
asyncio.set_event_loop(asyncio.ProactorEventLoop())
+def base_path_from_siteuri(siteuri: str) -> str:
+ """Extract the path part from a URI such as site['SITE_URL'].
+
+ The path never ends with a "/". (If only "/" is intended, it is empty.)
+ """
+ path = urllib.parse.urlsplit(siteuri).path
+ if path.endswith("/"):
+ path = path[:-1]
+ return path
+
+
class CommandAuto(Command):
"""Automatic rebuilds for Nikola."""
@@ -239,8 +251,10 @@ class CommandAuto(Command):
# Server can be disabled (Issue #1883)
self.has_server = not options['no-server']
+ base_path = base_path_from_siteuri(self.site.config['SITE_URL'])
+
if self.has_server:
- loop.run_until_complete(self.set_up_server(host, port, out_folder))
+ loop.run_until_complete(self.set_up_server(host, port, base_path, out_folder))
# Run an initial build so we are up-to-date. The server is running, but we are not watching yet.
loop.run_until_complete(self.run_initial_rebuild())
@@ -293,9 +307,12 @@ class CommandAuto(Command):
if browser:
# Some browsers fail to load 0.0.0.0 (Issue #2755)
if host == '0.0.0.0':
- server_url = "http://127.0.0.1:{0}/".format(port)
- self.logger.info("Opening {0} in the default web browser...".format(server_url))
- webbrowser.open(server_url)
+ browser_url = "http://127.0.0.1:{0}/{1}".format(port, base_path.lstrip("/"))
+ else:
+ # server_url always ends with a "/":
+ browser_url = "{0}{1}".format(server_url, base_path.lstrip("/"))
+ self.logger.info("Opening {0} in the default web browser...".format(browser_url))
+ webbrowser.open(browser_url)
# Run the event loop forever and handle shutdowns.
try:
@@ -320,13 +337,13 @@ class CommandAuto(Command):
self.wd_observer.join()
loop.close()
- async def set_up_server(self, host: str, port: int, out_folder: str) -> None:
+ async def set_up_server(self, host: str, port: int, base_path: str, out_folder: str) -> None:
"""Set up aiohttp server and start it."""
webapp = web.Application()
webapp.router.add_get('/livereload.js', self.serve_livereload_js)
webapp.router.add_get('/robots.txt', self.serve_robots_txt)
webapp.router.add_route('*', '/livereload', self.websocket_handler)
- resource = IndexHtmlStaticResource(True, self.snippet, '', out_folder)
+ resource = IndexHtmlStaticResource(True, self.snippet, base_path, out_folder)
webapp.router.register_resource(resource)
webapp.on_shutdown.append(self.remove_websockets)
@@ -587,13 +604,11 @@ class NikolaEventHandler:
self.function = function
self.loop = loop
- async def on_any_event(self, event):
- """Handle all file events."""
- await self.function(event)
-
def dispatch(self, event):
"""Dispatch events to handler."""
- self.loop.call_soon_threadsafe(asyncio.ensure_future, self.on_any_event(event))
+ if event.event_type in {"opened", "closed"}:
+ return
+ self.loop.call_soon_threadsafe(asyncio.ensure_future, self.function(event))
class ConfigEventHandler(NikolaEventHandler):
@@ -601,11 +616,10 @@ class ConfigEventHandler(NikolaEventHandler):
def __init__(self, configuration_filename, function, loop):
"""Initialize the handler."""
+ super().__init__(function, loop)
self.configuration_filename = configuration_filename
- self.function = function
- self.loop = loop
- async def on_any_event(self, event):
+ def dispatch(self, event):
"""Handle file events if they concern the configuration file."""
if event._src_path == self.configuration_filename:
- await self.function(event)
+ super().dispatch(event)
diff --git a/nikola/plugins/command/check.py b/nikola/plugins/command/check.py
index f9b701b..5bcbced 100644
--- a/nikola/plugins/command/check.py
+++ b/nikola/plugins/command/check.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -148,6 +148,22 @@ class CommandCheck(Command):
'default': False,
'help': 'Check that remote links work.',
},
+ {
+ 'name': 'timeout',
+ 'long': 'timeout',
+ 'short': 't',
+ 'type': int,
+ 'default': 30,
+ 'help': 'Timeout (in seconds) for HTTP requests in remote checks.',
+ },
+ {
+ 'name': 'ignore_query_strings',
+ 'long': 'ignore-query-strings',
+ 'short': 'q',
+ 'type': bool,
+ 'default': False,
+ 'help': 'Ignore query strings for internal links.',
+ }
]
def _execute(self, options, args):
@@ -160,8 +176,9 @@ class CommandCheck(Command):
else:
self.logger.level = logging.WARNING
failure = False
+ self.timeout = options['timeout']
if options['links']:
- failure |= self.scan_links(options['find_sources'], options['remote'])
+ failure |= self.scan_links(options['find_sources'], options['remote'], options['ignore_query_strings'])
if options['files']:
failure |= self.scan_files()
if options['clean']:
@@ -171,9 +188,10 @@ class CommandCheck(Command):
existing_targets = set([])
checked_remote_targets = {}
+ timeout = None
cache = {}
- def analyze(self, fname, find_sources=False, check_remote=False):
+ def analyze(self, fname, find_sources=False, check_remote=False, ignore_query_strings=False):
"""Analyze links on a page."""
rv = False
self.whitelist = [re.compile(x) for x in self.site.config['LINK_CHECK_WHITELIST']]
@@ -279,19 +297,19 @@ class CommandCheck(Command):
# Check the remote link works
req_headers = {'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0 (Nikola)'} # I’m a real boy!
- resp = requests.head(target, headers=req_headers, allow_redirects=False)
+ resp = requests.head(target, headers=req_headers, allow_redirects=False, timeout=self.timeout)
# Retry client errors (4xx) as GET requests because many servers are broken
if resp.status_code >= 400 and resp.status_code <= 499:
time.sleep(0.5)
- resp = requests.get(target, headers=req_headers, allow_redirects=False)
+ resp = requests.get(target, headers=req_headers, allow_redirects=False, timeout=self.timeout)
# Follow redirects and see where they lead, redirects to errors will be reported twice
if resp.status_code in [301, 302, 307, 308]:
redir_status_code = resp.status_code
time.sleep(0.5)
# Known redirects are retested using GET because IIS servers otherwise get HEADaches
- resp = requests.get(target, headers=req_headers, allow_redirects=True)
+ resp = requests.get(target, headers=req_headers, allow_redirects=True, timeout=self.timeout)
# Permanent redirects should be updated
if redir_status_code in [301, 308]:
self.logger.warning("Remote link moved PERMANENTLY to \"{0}\" and should be updated in {1}: {2} [HTTP: {3}]".format(resp.url, filename, target, redir_status_code))
@@ -353,6 +371,10 @@ class CommandCheck(Command):
else:
target_filename_str = target_filename.decode("utf-8", errors="surrogateescape")
+ if ignore_query_strings and "?" in target_filename_str:
+ target_filename, _, _ = target_filename.rpartition("?")
+ target_filename_str, _, _ = target_filename_str.rpartition("?")
+
if any(pattern.search(target_filename_str) for pattern in self.whitelist):
continue
@@ -371,7 +393,7 @@ class CommandCheck(Command):
self.logger.error(u"Error with: {0} {1}".format(filename, exc))
return rv
- def scan_links(self, find_sources=False, check_remote=False):
+ def scan_links(self, find_sources=False, check_remote=False, ignore_query_strings=False):
"""Check links on the site."""
self.logger.debug("Checking Links:")
self.logger.debug("===============\n")
@@ -387,13 +409,13 @@ class CommandCheck(Command):
for fname in _call_nikola_list(self.site, self.cache)[0]:
if fname.startswith(output_folder):
if '.html' == fname[-5:]:
- if self.analyze(fname, find_sources, check_remote):
+ if self.analyze(fname, find_sources, check_remote, ignore_query_strings):
failure = True
if atom_extension == fname[-len(atom_extension):]:
- if self.analyze(fname, find_sources, False):
+ if self.analyze(fname, find_sources, False, ignore_query_strings):
failure = True
if fname.endswith('sitemap.xml') or fname.endswith('sitemapindex.xml'):
- if self.analyze(fname, find_sources, False):
+ if self.analyze(fname, find_sources, False, ignore_query_strings):
failure = True
if not failure:
self.logger.debug("All links checked.")
diff --git a/nikola/plugins/command/console.py b/nikola/plugins/command/console.py
index 96fee3e..18428f3 100644
--- a/nikola/plugins/command/console.py
+++ b/nikola/plugins/command/console.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Chris Warrick, Roberto Alsina and others.
+# Copyright © 2012-2024 Chris Warrick, Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/command/default_config.py b/nikola/plugins/command/default_config.py
index fddda26..f14c4c8 100644
--- a/nikola/plugins/command/default_config.py
+++ b/nikola/plugins/command/default_config.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -48,7 +48,10 @@ class CommandShowConfig(Command):
def _execute(self, options=None, args=None):
"""Show the default configuration."""
+ init_plugin = self.site.plugin_manager.get_plugin_by_name("init", "Command").plugin_object
+ config = init_plugin.create_configuration_to_string()
+
try:
- print(nikola.plugins.command.init.CommandInit.create_configuration_to_string())
+ print(config)
except Exception:
- sys.stdout.buffer.write(nikola.plugins.command.init.CommandInit.create_configuration_to_string().encode('utf-8'))
+ sys.stdout.buffer.write(config.encode('utf-8'))
diff --git a/nikola/plugins/command/deploy.py b/nikola/plugins/command/deploy.py
index 1896a7a..ddb5a64 100644
--- a/nikola/plugins/command/deploy.py
+++ b/nikola/plugins/command/deploy.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/command/github_deploy.py b/nikola/plugins/command/github_deploy.py
index 1d3e9c0..aa2da74 100644
--- a/nikola/plugins/command/github_deploy.py
+++ b/nikola/plugins/command/github_deploy.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2014-2022 Puneeth Chaganti and others.
+# Copyright © 2014-2024 Puneeth Chaganti and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/command/import_wordpress.py b/nikola/plugins/command/import_wordpress.py
index f3feab1..e6ec45b 100644
--- a/nikola/plugins/command/import_wordpress.py
+++ b/nikola/plugins/command/import_wordpress.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -66,7 +66,7 @@ def install_plugin(site, plugin_name, output_dir=None, show_install_notes=False)
"""Install a Nikola plugin."""
LOGGER.info("Installing plugin '{0}'".format(plugin_name))
# Get hold of the 'plugin' plugin
- plugin_installer_info = site.plugin_manager.getPluginByName('plugin', 'Command')
+ plugin_installer_info = site.plugin_manager.get_plugin_by_name('plugin', 'Command')
if plugin_installer_info is None:
LOGGER.error('Internal error: cannot find the "plugin" plugin which is supposed to come with Nikola!')
return False
@@ -236,10 +236,9 @@ to
self._find_wordpress_compiler()
if self.wordpress_page_compiler is not None:
return self.wordpress_page_compiler
- plugin_info = self.site.plugin_manager.getPluginByName('markdown', 'PageCompiler')
+ plugin_info = self.site.plugin_manager.get_plugin_by_name('markdown', 'PageCompiler')
if plugin_info is not None:
if not plugin_info.is_activated:
- self.site.plugin_manager.activatePluginByName(plugin_info.name)
plugin_info.plugin_object.set_site(self.site)
return plugin_info.plugin_object
else:
@@ -249,7 +248,7 @@ to
"""Find WordPress compiler plugin."""
if self.wordpress_page_compiler is not None:
return
- plugin_info = self.site.plugin_manager.getPluginByName('wordpress', 'PageCompiler')
+ plugin_info = self.site.plugin_manager.get_plugin_by_name('wordpress', 'PageCompiler')
if plugin_info is not None:
if not plugin_info.is_activated:
self.site.plugin_manager.activatePluginByName(plugin_info.name)
diff --git a/nikola/plugins/command/init.py b/nikola/plugins/command/init.py
index 4607758..cf22a44 100644
--- a/nikola/plugins/command/init.py
+++ b/nikola/plugins/command/init.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/command/new_page.py b/nikola/plugins/command/new_page.py
index 94fbc51..6587d70 100644
--- a/nikola/plugins/command/new_page.py
+++ b/nikola/plugins/command/new_page.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina, Chris Warrick and others.
+# Copyright © 2012-2024 Roberto Alsina, Chris Warrick and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -109,5 +109,5 @@ class CommandNewPage(Command):
options['date-path'] = False
# Even though stuff was split into `new_page`, it’s easier to do it
# there not to duplicate the code.
- p = self.site.plugin_manager.getPluginByName('new_post', 'Command').plugin_object
+ p = self.site.plugin_manager.get_plugin_by_name('new_post', 'Command').plugin_object
return p.execute(options, args)
diff --git a/nikola/plugins/command/new_post.py b/nikola/plugins/command/new_post.py
index f66a188..aa96625 100644
--- a/nikola/plugins/command/new_post.py
+++ b/nikola/plugins/command/new_post.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -216,9 +216,7 @@ class CommandNewPost(Command):
def _execute(self, options, args):
"""Create a new post or page."""
global LOGGER
- compiler_names = [p.name for p in
- self.site.plugin_manager.getPluginsOfCategory(
- "PageCompiler")]
+ compiler_names = [p.name for p in self.site.plugin_manager.get_plugins_of_category("PageCompiler")]
if len(args) > 1:
print(self.help())
@@ -298,7 +296,7 @@ class CommandNewPost(Command):
self.print_compilers()
return
- compiler_plugin = self.site.plugin_manager.getPluginByName(
+ compiler_plugin = self.site.plugin_manager.get_plugin_by_name(
content_format, "PageCompiler").plugin_object
# Guess where we should put this
diff --git a/nikola/plugins/command/orphans.py b/nikola/plugins/command/orphans.py
index 169cbba..bde8425 100644
--- a/nikola/plugins/command/orphans.py
+++ b/nikola/plugins/command/orphans.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina, Chris Warrick and others.
+# Copyright © 2012-2024 Roberto Alsina, Chris Warrick and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/command/plugin.py b/nikola/plugins/command/plugin.py
index ae0dead..4fd8e8f 100644
--- a/nikola/plugins/command/plugin.py
+++ b/nikola/plugins/command/plugin.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/command/rst2html/__init__.py b/nikola/plugins/command/rst2html/__init__.py
index 2bf329a..4fdd36f 100644
--- a/nikola/plugins/command/rst2html/__init__.py
+++ b/nikola/plugins/command/rst2html/__init__.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2015-2022 Chris Warrick and others.
+# Copyright © 2015-2024 Chris Warrick and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -44,7 +44,7 @@ class CommandRst2Html(Command):
def _execute(self, options, args):
"""Compile reStructuredText to standalone HTML files."""
- compiler = self.site.plugin_manager.getPluginByName('rest', 'PageCompiler').plugin_object
+ compiler = self.site.plugin_manager.get_plugin_by_name('rest', 'PageCompiler').plugin_object
if len(args) != 1:
print("This command takes only one argument (input file name).")
return 2
diff --git a/nikola/plugins/command/serve.py b/nikola/plugins/command/serve.py
index cbf628c..32cd46b 100644
--- a/nikola/plugins/command/serve.py
+++ b/nikola/plugins/command/serve.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/command/status.py b/nikola/plugins/command/status.py
index ab6fc1e..a22f173 100644
--- a/nikola/plugins/command/status.py
+++ b/nikola/plugins/command/status.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/command/subtheme.py b/nikola/plugins/command/subtheme.py
index b5c5aff..c322336 100644
--- a/nikola/plugins/command/subtheme.py
+++ b/nikola/plugins/command/subtheme.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/command/theme.py b/nikola/plugins/command/theme.py
index 2f99dd8..f7608b5 100644
--- a/nikola/plugins/command/theme.py
+++ b/nikola/plugins/command/theme.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina, Chris Warrick and others.
+# Copyright © 2012-2024 Roberto Alsina, Chris Warrick and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/command/version.py b/nikola/plugins/command/version.py
index 4ec8a46..bdc1357 100644
--- a/nikola/plugins/command/version.py
+++ b/nikola/plugins/command/version.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/compile/__init__.py b/nikola/plugins/compile/__init__.py
index c3abcd4..4517b31 100644
--- a/nikola/plugins/compile/__init__.py
+++ b/nikola/plugins/compile/__init__.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/compile/html.py b/nikola/plugins/compile/html.py
index ff7d37f..9dbf96e 100644
--- a/nikola/plugins/compile/html.py
+++ b/nikola/plugins/compile/html.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/compile/ipynb.py b/nikola/plugins/compile/ipynb.py
index 7d6e528..df1b29a 100644
--- a/nikola/plugins/compile/ipynb.py
+++ b/nikola/plugins/compile/ipynb.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2013-2022 Damián Avila, Chris Warrick and others.
+# Copyright © 2013-2024 Damián Avila, Chris Warrick and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/compile/markdown/__init__.py b/nikola/plugins/compile/markdown/__init__.py
index 31a57d9..052413f 100644
--- a/nikola/plugins/compile/markdown/__init__.py
+++ b/nikola/plugins/compile/markdown/__init__.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/compile/markdown/mdx_nikola.py b/nikola/plugins/compile/markdown/mdx_nikola.py
index af30956..ba14319 100644
--- a/nikola/plugins/compile/markdown/mdx_nikola.py
+++ b/nikola/plugins/compile/markdown/mdx_nikola.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/compile/markdown/mdx_podcast.py b/nikola/plugins/compile/markdown/mdx_podcast.py
index e003f40..0be09a8 100644
--- a/nikola/plugins/compile/markdown/mdx_podcast.py
+++ b/nikola/plugins/compile/markdown/mdx_podcast.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
-# Copyright © 2013-2022 Michael Rabbitt, Roberto Alsina and others.
+# Copyright © 2013-2024 Michael Rabbitt, Roberto Alsina and others.
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the
diff --git a/nikola/plugins/compile/pandoc.py b/nikola/plugins/compile/pandoc.py
index a43c8b0..4082d1d 100644
--- a/nikola/plugins/compile/pandoc.py
+++ b/nikola/plugins/compile/pandoc.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -62,10 +62,10 @@ class CompilePandoc(PageCompiler):
try:
pandoc_options = list(config_options[ext])
except KeyError:
- self.logger.warn('Setting PANDOC_OPTIONS to [], because extension {} is not defined in PANDOC_OPTIONS: {}.'.format(ext, config_options))
+ self.logger.warning('Setting PANDOC_OPTIONS to [], because extension {} is not defined in PANDOC_OPTIONS: {}.'.format(ext, config_options))
pandoc_options = []
else:
- self.logger.warn('Setting PANDOC_OPTIONS to [], because PANDOC_OPTIONS is expected to be of type Union[List[str], Dict[str, List[str]]] but this is not: {}'.format(config_options))
+ self.logger.warning('Setting PANDOC_OPTIONS to [], because PANDOC_OPTIONS is expected to be of type Union[List[str], Dict[str, List[str]]] but this is not: {}'.format(config_options))
pandoc_options = []
return pandoc_options
diff --git a/nikola/plugins/compile/php.py b/nikola/plugins/compile/php.py
index d4c67dc..09f2e09 100644
--- a/nikola/plugins/compile/php.py
+++ b/nikola/plugins/compile/php.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/compile/rest/__init__.py b/nikola/plugins/compile/rest/__init__.py
index e5d3998..e252bae 100644
--- a/nikola/plugins/compile/rest/__init__.py
+++ b/nikola/plugins/compile/rest/__init__.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -77,7 +77,7 @@ class CompileRest(PageCompiler):
meta = {}
if 'title' in document:
meta['title'] = document['title']
- for docinfo in document.traverse(docutils.nodes.docinfo):
+ for docinfo in document.findall(docutils.nodes.docinfo):
for element in docinfo.children:
if element.tagname == 'field': # custom fields (e.g. summary)
name_elem, body_elem = element.children
diff --git a/nikola/plugins/compile/rest/chart.py b/nikola/plugins/compile/rest/chart.py
index 15ccee7..3f3c0dc 100644
--- a/nikola/plugins/compile/rest/chart.py
+++ b/nikola/plugins/compile/rest/chart.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -153,7 +153,7 @@ class Chart(Directive):
def run(self):
"""Run the directive."""
self.options['site'] = None
- html = _site.plugin_manager.getPluginByName(
+ html = _site.plugin_manager.get_plugin_by_name(
'chart', 'ShortcodePlugin').plugin_object.handler(
self.arguments[0],
data='\n'.join(self.content),
diff --git a/nikola/plugins/compile/rest/doc.py b/nikola/plugins/compile/rest/doc.py
index 1d88472..b884aaa 100644
--- a/nikola/plugins/compile/rest/doc.py
+++ b/nikola/plugins/compile/rest/doc.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/compile/rest/listing.py b/nikola/plugins/compile/rest/listing.py
index 48dbe4c..d1b64f0 100644
--- a/nikola/plugins/compile/rest/listing.py
+++ b/nikola/plugins/compile/rest/listing.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/compile/rest/media.py b/nikola/plugins/compile/rest/media.py
index 6d0436d..0611f6f 100644
--- a/nikola/plugins/compile/rest/media.py
+++ b/nikola/plugins/compile/rest/media.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/compile/rest/post_list.py b/nikola/plugins/compile/rest/post_list.py
index 1799790..8f2b446 100644
--- a/nikola/plugins/compile/rest/post_list.py
+++ b/nikola/plugins/compile/rest/post_list.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2013-2022 Udo Spallek, Roberto Alsina and others.
+# Copyright © 2013-2024 Udo Spallek, Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -88,7 +88,7 @@ class PostListDirective(Directive):
date = self.options.get('date')
filename = self.state.document.settings._nikola_source_path
- output, deps = self.site.plugin_manager.getPluginByName(
+ output, deps = self.site.plugin_manager.get_plugin_by_name(
'post_list', 'ShortcodePlugin').plugin_object.handler(
start,
stop,
diff --git a/nikola/plugins/compile/rest/soundcloud.py b/nikola/plugins/compile/rest/soundcloud.py
index 87b1483..71a82a1 100644
--- a/nikola/plugins/compile/rest/soundcloud.py
+++ b/nikola/plugins/compile/rest/soundcloud.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/compile/rest/thumbnail.py b/nikola/plugins/compile/rest/thumbnail.py
index 6f10a7f..af075bb 100644
--- a/nikola/plugins/compile/rest/thumbnail.py
+++ b/nikola/plugins/compile/rest/thumbnail.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2014-2022 Pelle Nilsson and others.
+# Copyright © 2014-2024 Pelle Nilsson and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/compile/rest/vimeo.py b/nikola/plugins/compile/rest/vimeo.py
index b4f89ff..7c5dd6f 100644
--- a/nikola/plugins/compile/rest/vimeo.py
+++ b/nikola/plugins/compile/rest/vimeo.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/compile/rest/youtube.py b/nikola/plugins/compile/rest/youtube.py
index de3f2fa..ef46822 100644
--- a/nikola/plugins/compile/rest/youtube.py
+++ b/nikola/plugins/compile/rest/youtube.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/misc/__init__.py b/nikola/plugins/misc/__init__.py
index 0572088..41c38f7 100644
--- a/nikola/plugins/misc/__init__.py
+++ b/nikola/plugins/misc/__init__.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/misc/scan_posts.plugin b/nikola/plugins/misc/scan_posts.plugin
index f4af811..0fb946c 100644
--- a/nikola/plugins/misc/scan_posts.plugin
+++ b/nikola/plugins/misc/scan_posts.plugin
@@ -8,3 +8,5 @@ Version = 1.0
Website = https://getnikola.com/
Description = Scan posts and create timeline
+[Nikola]
+PluginCategory = PostScanner
diff --git a/nikola/plugins/misc/scan_posts.py b/nikola/plugins/misc/scan_posts.py
index efa797e..bc63631 100644
--- a/nikola/plugins/misc/scan_posts.py
+++ b/nikola/plugins/misc/scan_posts.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/misc/taxonomies_classifier.py b/nikola/plugins/misc/taxonomies_classifier.py
index c6092b3..f53fe54 100644
--- a/nikola/plugins/misc/taxonomies_classifier.py
+++ b/nikola/plugins/misc/taxonomies_classifier.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/shortcode/chart.plugin b/nikola/plugins/shortcode/chart.plugin
index edcbc13..be1fbc6 100644
--- a/nikola/plugins/shortcode/chart.plugin
+++ b/nikola/plugins/shortcode/chart.plugin
@@ -3,7 +3,7 @@ name = chart
module = chart
[Nikola]
-PluginCategory = Shortcode
+PluginCategory = ShortcodePlugin
[Documentation]
author = Roberto Alsina
diff --git a/nikola/plugins/shortcode/chart.py b/nikola/plugins/shortcode/chart.py
index 2b88cfb..fb45245 100644
--- a/nikola/plugins/shortcode/chart.py
+++ b/nikola/plugins/shortcode/chart.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/shortcode/emoji.plugin b/nikola/plugins/shortcode/emoji.plugin
index c9a272c..4c09f03 100644
--- a/nikola/plugins/shortcode/emoji.plugin
+++ b/nikola/plugins/shortcode/emoji.plugin
@@ -3,7 +3,7 @@ name = emoji
module = emoji
[Nikola]
-PluginCategory = Shortcode
+PluginCategory = ShortcodePlugin
[Documentation]
author = Roberto Alsina
diff --git a/nikola/plugins/shortcode/gist.plugin b/nikola/plugins/shortcode/gist.plugin
index b610763..ee62c27 100644
--- a/nikola/plugins/shortcode/gist.plugin
+++ b/nikola/plugins/shortcode/gist.plugin
@@ -3,7 +3,7 @@ name = gist
module = gist
[Nikola]
-PluginCategory = Shortcode
+PluginCategory = ShortcodePlugin
[Documentation]
author = Roberto Alsina
diff --git a/nikola/plugins/shortcode/listing.plugin b/nikola/plugins/shortcode/listing.plugin
index 90fb6eb..70fa1cf 100644
--- a/nikola/plugins/shortcode/listing.plugin
+++ b/nikola/plugins/shortcode/listing.plugin
@@ -3,7 +3,7 @@ name = listing_shortcode
module = listing
[Nikola]
-PluginCategory = Shortcode
+PluginCategory = ShortcodePlugin
[Documentation]
author = Roberto Alsina
diff --git a/nikola/plugins/shortcode/listing.py b/nikola/plugins/shortcode/listing.py
index 3046655..33b0d44 100644
--- a/nikola/plugins/shortcode/listing.py
+++ b/nikola/plugins/shortcode/listing.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2017-2022 Roberto Alsina and others.
+# Copyright © 2017-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/shortcode/post_list.plugin b/nikola/plugins/shortcode/post_list.plugin
index 494a1d8..9c39eb9 100644
--- a/nikola/plugins/shortcode/post_list.plugin
+++ b/nikola/plugins/shortcode/post_list.plugin
@@ -3,7 +3,7 @@ name = post_list
module = post_list
[Nikola]
-PluginCategory = Shortcode
+PluginCategory = ShortcodePlugin
[Documentation]
author = Udo Spallek
diff --git a/nikola/plugins/shortcode/post_list.py b/nikola/plugins/shortcode/post_list.py
index 5b24cc6..df2d022 100644
--- a/nikola/plugins/shortcode/post_list.py
+++ b/nikola/plugins/shortcode/post_list.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2013-2022 Udo Spallek, Roberto Alsina and others.
+# Copyright © 2013-2024 Udo Spallek, Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/shortcode/thumbnail.plugin b/nikola/plugins/shortcode/thumbnail.plugin
index e55d34f..bd36169 100644
--- a/nikola/plugins/shortcode/thumbnail.plugin
+++ b/nikola/plugins/shortcode/thumbnail.plugin
@@ -3,7 +3,7 @@ name = thumbnail
module = thumbnail
[Nikola]
-PluginCategory = Shortcode
+PluginCategory = ShortcodePlugin
[Documentation]
author = Chris Warrick
diff --git a/nikola/plugins/shortcode/thumbnail.py b/nikola/plugins/shortcode/thumbnail.py
index 7a05320..67b235c 100644
--- a/nikola/plugins/shortcode/thumbnail.py
+++ b/nikola/plugins/shortcode/thumbnail.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2017-2022 Roberto Alsina, Chris Warrick and others.
+# Copyright © 2017-2024 Roberto Alsina, Chris Warrick and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/task/__init__.py b/nikola/plugins/task/__init__.py
index 10c54d0..174d82e 100644
--- a/nikola/plugins/task/__init__.py
+++ b/nikola/plugins/task/__init__.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/task/archive.py b/nikola/plugins/task/archive.py
index f083394..44849fa 100644
--- a/nikola/plugins/task/archive.py
+++ b/nikola/plugins/task/archive.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/task/authors.py b/nikola/plugins/task/authors.py
index d966a4f..6a9ab81 100644
--- a/nikola/plugins/task/authors.py
+++ b/nikola/plugins/task/authors.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2015-2022 Juanjo Conti and others.
+# Copyright © 2015-2024 Juanjo Conti and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/task/bundles.plugin b/nikola/plugins/task/bundles.plugin
index 939065b..4afc3ec 100644
--- a/nikola/plugins/task/bundles.plugin
+++ b/nikola/plugins/task/bundles.plugin
@@ -9,5 +9,5 @@ website = https://getnikola.com/
description = Bundle assets
[Nikola]
-PluginCategory = Task
+PluginCategory = LateTask
diff --git a/nikola/plugins/task/bundles.py b/nikola/plugins/task/bundles.py
index c71c255..b0aeb42 100644
--- a/nikola/plugins/task/bundles.py
+++ b/nikola/plugins/task/bundles.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/task/categories.py b/nikola/plugins/task/categories.py
index 11a0407..51b7f6f 100644
--- a/nikola/plugins/task/categories.py
+++ b/nikola/plugins/task/categories.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/task/copy_assets.py b/nikola/plugins/task/copy_assets.py
index fd22e0d..519a552 100644
--- a/nikola/plugins/task/copy_assets.py
+++ b/nikola/plugins/task/copy_assets.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/task/copy_files.py b/nikola/plugins/task/copy_files.py
index 3818808..23ba5cd 100644
--- a/nikola/plugins/task/copy_files.py
+++ b/nikola/plugins/task/copy_files.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/task/galleries.py b/nikola/plugins/task/galleries.py
index a780d76..50a2877 100644
--- a/nikola/plugins/task/galleries.py
+++ b/nikola/plugins/task/galleries.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -32,6 +32,7 @@ import io
import json
import mimetypes
import os
+import pathlib
from collections import OrderedDict
from urllib.parse import urljoin
@@ -544,7 +545,7 @@ class Galleries(Task, ImageProcessor):
except IOError:
excluded_image_name_list = []
- excluded_image_list = ["{0}/{1}".format(gallery_path, i) for i in excluded_image_name_list]
+ excluded_image_list = [os.path.join(gallery_path, i) for i in excluded_image_name_list]
return excluded_image_list
def get_image_list(self, gallery_path):
@@ -737,6 +738,10 @@ class Galleries(Task, ImageProcessor):
else:
img_list, dest_img_list, img_titles = [], [], []
+ def forward_slashes(path):
+ """Given a path, convert directory separators to forward slash, on all platforms."""
+ return str(pathlib.PurePosixPath(*path.split(os.path.sep)))
+
items = []
for img, srcimg, title in list(zip(dest_img_list, img_list, img_titles))[:self.kw["feed_length"]]:
img_size = os.stat(
@@ -744,11 +749,11 @@ class Galleries(Task, ImageProcessor):
self.site.config['OUTPUT_FOLDER'], img)).st_size
args = {
'title': title,
- 'link': make_url(img),
- 'guid': rss.Guid(img, False),
+ 'link': make_url(forward_slashes(img)),
+ 'guid': rss.Guid(forward_slashes(img), False),
'pubDate': self.image_date(srcimg),
'enclosure': rss.Enclosure(
- make_url(img),
+ make_url(forward_slashes(img)),
img_size,
mimetypes.guess_type(img)[0]
),
diff --git a/nikola/plugins/task/gzip.plugin b/nikola/plugins/task/gzip.plugin
index cc078b7..b1aab25 100644
--- a/nikola/plugins/task/gzip.plugin
+++ b/nikola/plugins/task/gzip.plugin
@@ -4,10 +4,10 @@ module = gzip
[Documentation]
author = Roberto Alsina
-version = 1.0
+version = 1.1
website = https://getnikola.com/
description = Create gzipped copies of files
[Nikola]
-PluginCategory = Task
+PluginCategory = TaskMultiplier
diff --git a/nikola/plugins/task/gzip.py b/nikola/plugins/task/gzip.py
index 62523c7..9061807 100644
--- a/nikola/plugins/task/gzip.py
+++ b/nikola/plugins/task/gzip.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -30,6 +30,7 @@ import gzip
import os
import shlex
import subprocess
+import sys
from nikola.plugin_categories import TaskMultiplier
@@ -73,8 +74,11 @@ class GzipFiles(TaskMultiplier):
def create_gzipped_copy(in_path, out_path, command=None):
"""Create gzipped copy of in_path and save it as out_path."""
if command:
- subprocess.check_call(shlex.split(command.format(filename=in_path)))
+ if sys.platform == 'win32':
+ subprocess.check_call(command.format(filename=in_path))
+ else:
+ subprocess.check_call(shlex.split(command.format(filename=in_path)))
else:
- with gzip.GzipFile(out_path, 'wb+') as outf:
+ with gzip.GzipFile(out_path, 'wb+', mtime=0) as outf:
with open(in_path, 'rb') as inf:
outf.write(inf.read())
diff --git a/nikola/plugins/task/indexes.py b/nikola/plugins/task/indexes.py
index 93c119b..8af550d 100644
--- a/nikola/plugins/task/indexes.py
+++ b/nikola/plugins/task/indexes.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/task/listings.py b/nikola/plugins/task/listings.py
index 510411a..dde1c69 100644
--- a/nikola/plugins/task/listings.py
+++ b/nikola/plugins/task/listings.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -107,12 +107,13 @@ class Listings(Task):
"""Render pretty code listings."""
# Things to ignore in listings
ignored_extensions = (".pyc", ".pyo")
+ ignored_files = (".DS_Store",)
def render_listing(in_name, out_name, input_folder, output_folder, folders=[], files=[]):
needs_ipython_css = False
if in_name and in_name.endswith('.ipynb'):
# Special handling: render ipynbs in listings (Issue #1900)
- ipynb_plugin = self.site.plugin_manager.getPluginByName("ipynb", "PageCompiler")
+ ipynb_plugin = self.site.plugin_manager.get_plugin_by_name("ipynb", "PageCompiler")
if ipynb_plugin is None:
msg = "To use .ipynb files as listings, you must set up the Jupyter compiler in COMPILERS and POSTS/PAGES."
utils.LOGGER.error(msg)
@@ -183,7 +184,9 @@ class Listings(Task):
for input_folder, output_folder in self.kw['listings_folders'].items():
for root, dirs, files in os.walk(input_folder, followlinks=True):
- files = [f for f in files if os.path.splitext(f)[-1] not in ignored_extensions]
+ files = [f for f in files
+ if os.path.splitext(f)[-1] not in ignored_extensions and
+ f not in ignored_files]
uptodate = {'c': self.site.GLOBAL_CONTEXT}
@@ -224,7 +227,7 @@ class Listings(Task):
'clean': True,
}, self.kw["filters"])
for f in files:
- if f == '.DS_Store':
+ if f in ignored_files:
continue
ext = os.path.splitext(f)[-1]
if ext in ignored_extensions:
diff --git a/nikola/plugins/task/page_index.py b/nikola/plugins/task/page_index.py
index 4002e5c..9fb2a2d 100644
--- a/nikola/plugins/task/page_index.py
+++ b/nikola/plugins/task/page_index.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -106,6 +106,6 @@ class PageIndex(Taxonomy):
short_destination = dirname + '/' + self.site.config['INDEX_FILE']
for post in post_list:
# If there is an index.html pending to be created from a page, do not generate the page index.
- if post.destination_path(lang, sep='/') == short_destination:
+ if post.destination_path(lang, sep='/').lstrip('/') == short_destination.lstrip('/'):
return False
return True
diff --git a/nikola/plugins/task/pages.py b/nikola/plugins/task/pages.py
index d30cdd0..ae26735 100644
--- a/nikola/plugins/task/pages.py
+++ b/nikola/plugins/task/pages.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/task/posts.py b/nikola/plugins/task/posts.py
index 86c85ae..242ee76 100644
--- a/nikola/plugins/task/posts.py
+++ b/nikola/plugins/task/posts.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -26,6 +26,7 @@
"""Build HTML fragments from metadata and text."""
+import docutils
import os
from copy import copy
@@ -57,6 +58,7 @@ class RenderPosts(Task):
"default_lang": self.site.config["DEFAULT_LANG"],
"show_untranslated_posts": self.site.config['SHOW_UNTRANSLATED_POSTS'],
"demote_headers": self.site.config['DEMOTE_HEADERS'],
+ "docutils_version": docutils.__version__,
}
self.tl_changed = False
diff --git a/nikola/plugins/task/redirect.py b/nikola/plugins/task/redirect.py
index b1262a0..5861eba 100644
--- a/nikola/plugins/task/redirect.py
+++ b/nikola/plugins/task/redirect.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/task/robots.plugin b/nikola/plugins/task/robots.plugin
index 51f7781..81c4c9a 100644
--- a/nikola/plugins/task/robots.plugin
+++ b/nikola/plugins/task/robots.plugin
@@ -9,5 +9,5 @@ website = https://getnikola.com/
description = Generate /robots.txt exclusion file and promote sitemap.
[Nikola]
-PluginCategory = Task
+PluginCategory = LateTask
diff --git a/nikola/plugins/task/robots.py b/nikola/plugins/task/robots.py
index e1d8d00..ff7f67f 100644
--- a/nikola/plugins/task/robots.py
+++ b/nikola/plugins/task/robots.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/task/scale_images.py b/nikola/plugins/task/scale_images.py
index f317a3f..8f1262a 100644
--- a/nikola/plugins/task/scale_images.py
+++ b/nikola/plugins/task/scale_images.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2014-2022 Pelle Nilsson and others.
+# Copyright © 2014-2024 Pelle Nilsson and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/task/sitemap.plugin b/nikola/plugins/task/sitemap.plugin
index c8aa832..8367d8e 100644
--- a/nikola/plugins/task/sitemap.plugin
+++ b/nikola/plugins/task/sitemap.plugin
@@ -9,5 +9,5 @@ website = https://getnikola.com/
description = Generate google sitemap.
[Nikola]
-PluginCategory = Task
+PluginCategory = LateTask
diff --git a/nikola/plugins/task/sitemap.py b/nikola/plugins/task/sitemap.py
index f99f2de..7d40fac 100644
--- a/nikola/plugins/task/sitemap.py
+++ b/nikola/plugins/task/sitemap.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -309,7 +309,7 @@ class Sitemap(LateTask):
# RFC 3339 (web ISO 8601 profile) represented in UTC with Zulu
# zone desgignator as recommeded for sitemaps. Second and
# microsecond precision is stripped for compatibility.
- lastmod = datetime.datetime.utcfromtimestamp(os.stat(p).st_mtime).replace(tzinfo=dateutil.tz.gettz('UTC'), second=0, microsecond=0).isoformat().replace('+00:00', 'Z')
+ lastmod = datetime.datetime.fromtimestamp(os.stat(p).st_mtime, dateutil.tz.tzutc()).replace(second=0, microsecond=0).isoformat().replace('+00:00', 'Z')
return lastmod
diff --git a/nikola/plugins/task/sources.py b/nikola/plugins/task/sources.py
index 107c8fb..4cf376c 100644
--- a/nikola/plugins/task/sources.py
+++ b/nikola/plugins/task/sources.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/task/tags.py b/nikola/plugins/task/tags.py
index 4b0cd8c..61bbd4d 100644
--- a/nikola/plugins/task/tags.py
+++ b/nikola/plugins/task/tags.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/task/taxonomies.py b/nikola/plugins/task/taxonomies.py
index 719cf46..55dfa36 100644
--- a/nikola/plugins/task/taxonomies.py
+++ b/nikola/plugins/task/taxonomies.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/template/__init__.py b/nikola/plugins/template/__init__.py
index 66f98f7..e989a15 100644
--- a/nikola/plugins/template/__init__.py
+++ b/nikola/plugins/template/__init__.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/template/jinja.plugin b/nikola/plugins/template/jinja.plugin
index 629b20e..45dd621 100644
--- a/nikola/plugins/template/jinja.plugin
+++ b/nikola/plugins/template/jinja.plugin
@@ -9,5 +9,5 @@ website = https://getnikola.com/
description = Support for Jinja2 templates.
[Nikola]
-PluginCategory = Template
+PluginCategory = TemplateSystem
diff --git a/nikola/plugins/template/jinja.py b/nikola/plugins/template/jinja.py
index 8aa32aa..e0ddf4a 100644
--- a/nikola/plugins/template/jinja.py
+++ b/nikola/plugins/template/jinja.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/template/mako.plugin b/nikola/plugins/template/mako.plugin
index 2d353bf..a465752 100644
--- a/nikola/plugins/template/mako.plugin
+++ b/nikola/plugins/template/mako.plugin
@@ -9,5 +9,5 @@ website = https://getnikola.com/
description = Support for Mako templates.
[Nikola]
-PluginCategory = Template
+PluginCategory = TemplateSystem
diff --git a/nikola/plugins/template/mako.py b/nikola/plugins/template/mako.py
index 5dc9fe5..9517b05 100644
--- a/nikola/plugins/template/mako.py
+++ b/nikola/plugins/template/mako.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/post.py b/nikola/post.py
index 42df0eb..792e6f0 100644
--- a/nikola/post.py
+++ b/nikola/post.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -350,8 +350,8 @@ class Post(object):
if self.config['__invariant__']:
default_metadata['date'] = datetime.datetime(2013, 12, 31, 23, 59, 59, tzinfo=self.config['__tzinfo__'])
else:
- default_metadata['date'] = datetime.datetime.utcfromtimestamp(
- os.stat(self.source_path).st_ctime).replace(tzinfo=dateutil.tz.tzutc()).astimezone(self.config['__tzinfo__'])
+ default_metadata['date'] = datetime.datetime.fromtimestamp(
+ os.stat(self.source_path).st_ctime, dateutil.tz.tzutc()).astimezone(self.config['__tzinfo__'])
# If time zone is set, build localized datetime.
try:
@@ -1213,12 +1213,12 @@ def get_meta(post, lang):
if lang is None:
# Only perform these checks for the default language
- if 'slug' not in meta:
+ if 'slug' not in meta or not meta['slug']:
# If no slug is found in the metadata use the filename
meta['slug'] = slugify(os.path.splitext(
os.path.basename(post.source_path))[0], post.default_lang)
- if 'title' not in meta:
+ if 'title' not in meta or not meta['title']:
# If no title is found, use the filename without extension
meta['title'] = os.path.splitext(
os.path.basename(post.source_path))[0]
diff --git a/nikola/shortcodes.py b/nikola/shortcodes.py
index cccea2d..8b9c643 100644
--- a/nikola/shortcodes.py
+++ b/nikola/shortcodes.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/state.py b/nikola/state.py
index a054d69..59cd3fc 100644
--- a/nikola/state.py
+++ b/nikola/state.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/utils.py b/nikola/utils.py
index 7157afb..20e3552 100644
--- a/nikola/utils.py
+++ b/nikola/utils.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -669,11 +669,9 @@ def html_tostring_fragment(document):
for start in start_fragments:
if doc.startswith(start):
doc = doc[len(start):].strip()
- print(repr(doc))
for end in end_fragments:
if doc.endswith(end):
doc = doc[:-len(end)].strip()
- print(repr(doc))
return doc
@@ -731,7 +729,7 @@ def load_messages(themes, translations, default_lang, themes_dirs):
del translation
except ImportError as orig:
last_exception = orig
- del(english)
+ del english
sys.path = oldpath
if not all(found.values()):
diff --git a/nikola/winutils.py b/nikola/winutils.py
index b5b8ffa..8f4e8f5 100644
--- a/nikola/winutils.py
+++ b/nikola/winutils.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2022 Roberto Alsina and others.
+# Copyright © 2012-2024 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated