django-markdown-view 0.0.6b2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. django_markdown_view-0.0.6b2.dist-info/METADATA +312 -0
  2. django_markdown_view-0.0.6b2.dist-info/RECORD +37 -0
  3. django_markdown_view-0.0.6b2.dist-info/WHEEL +5 -0
  4. django_markdown_view-0.0.6b2.dist-info/licenses/LICENSE +27 -0
  5. django_markdown_view-0.0.6b2.dist-info/top_level.txt +1 -0
  6. markdown_view/__init__.py +1 -0
  7. markdown_view/apps.py +12 -0
  8. markdown_view/checks.py +42 -0
  9. markdown_view/constants.py +11 -0
  10. markdown_view/loaders.py +61 -0
  11. markdown_view/markdown_extensions.py +19 -0
  12. markdown_view/models.py +0 -0
  13. markdown_view/registry.py +343 -0
  14. markdown_view/templates/markdown_view/markdown.html +28 -0
  15. markdown_view/tests/__init__.py +0 -0
  16. markdown_view/tests/fixtures/loaders_a/readme.md +4 -0
  17. markdown_view/tests/fixtures/loaders_b/readme.md +4 -0
  18. markdown_view/tests/settings/__init__.py +0 -0
  19. markdown_view/tests/settings/coveralls_settings.py +74 -0
  20. markdown_view/tests/test_checks.py +38 -0
  21. markdown_view/tests/test_loaders.py +68 -0
  22. markdown_view/tests/test_markdown_extensions.py +36 -0
  23. markdown_view/tests/test_registry.py +337 -0
  24. markdown_view/tests/test_settings_validation.py +55 -0
  25. markdown_view/tests/test_views.py +169 -0
  26. markdown_view/tests/testapp/CONTEXT_TEST.md +3 -0
  27. markdown_view/tests/testapp/LOGGED_IN.md +3 -0
  28. markdown_view/tests/testapp/NESTED.md +5 -0
  29. markdown_view/tests/testapp/NO_HEADERS.md +4 -0
  30. markdown_view/tests/testapp/README.md +35 -0
  31. markdown_view/tests/testapp/STAFF.md +3 -0
  32. markdown_view/tests/testapp/UNROUTED.md +4 -0
  33. markdown_view/tests/testapp/__init__.py +0 -0
  34. markdown_view/tests/testapp/apps.py +6 -0
  35. markdown_view/tests/testapp/docs/OTHER.md +8 -0
  36. markdown_view/tests/urls.py +109 -0
  37. markdown_view/views.py +109 -0
@@ -0,0 +1,312 @@
1
+ Metadata-Version: 2.4
2
+ Name: django-markdown-view
3
+ Version: 0.0.6b2
4
+ Summary: Serve .md pages as Django views.
5
+ Author-email: "Ryan J. Sullivan" <ryanj@ryanjsullivan.com>
6
+ License-Expression: BSD-3-Clause
7
+ Project-URL: Homepage, http://github.com/rgs258/django-markdown-view
8
+ Keywords: django,markdown,markdown view,md
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Framework :: Django :: 5.2
11
+ Classifier: Framework :: Django :: 6.0
12
+ Classifier: Framework :: Django
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python
16
+ Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/x-rst
24
+ License-File: LICENSE
25
+ Requires-Dist: django>=5.2
26
+ Requires-Dist: markdown>=3.2
27
+ Dynamic: license-file
28
+
29
+ Django Markdown View
30
+ ====================
31
+
32
+ .. image:: https://github.com/rgs258/django-markdown-view/actions/workflows/test.yml/badge.svg
33
+ :target: https://github.com/rgs258/django-markdown-view/actions/workflows/test.yml
34
+
35
+ .. image:: https://coveralls.io/repos/github/rgs258/django-markdown-view/badge.svg?branch=master
36
+ :target: https://coveralls.io/github/rgs258/django-markdown-view?branch=master
37
+
38
+
39
+ **Serve .md pages as Django views.**
40
+
41
+ This package aims to make it easy to serve .md files on Django sites.
42
+
43
+
44
+ .. contents:: Contents
45
+ :depth: 5
46
+
47
+ Installation
48
+ ------------
49
+
50
+ #. Install with ``pip install django-markdown-view``.
51
+
52
+ #. Add ``'markdown_view'`` to your ``INSTALLED_APPS`` settings.
53
+
54
+ .. code-block:: python
55
+
56
+ INSTALLED_APPS = [
57
+ ...,
58
+ 'markdown_view',
59
+ ...
60
+ ]
61
+
62
+ #. (OPTIONAL) Add ``MARKDOWN_VIEW_BASE_DIR`` or ``BASE_DIR`` to settings
63
+ The dictionary of the application's base. See Settings_ below
64
+
65
+ For example, if settings are in config/settings/base.py, then:
66
+
67
+ .. code-block:: python
68
+
69
+ BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
70
+
71
+
72
+ Usage
73
+ -----
74
+
75
+ Views
76
+ ~~~~~
77
+
78
+ Use one of ``MarkdownView``, ``LoggedInMarkdownView``, or ``StaffMarkdownView``
79
+ from ``markdown_view.views`` to serve a .md file
80
+
81
+ .. code-block:: python
82
+
83
+ from markdown_view.views import StaffMarkdownView
84
+
85
+ path('readme/',
86
+ StaffMarkdownView.as_view(file_name='my_app/README.md'),
87
+ name="readme"),
88
+
89
+ Settings
90
+ ~~~~~~~~
91
+
92
+ All settings are optional. See `<markdown_view/constants.py>`_ for the defaults.
93
+
94
+ * `MARKDOWN_VIEW_BASE_DIR` and `BASE_DIR`
95
+
96
+ When present, the value is taken as a location to append to the list of dirs that
97
+ Django's `django.template.utils.get_app_template_dirs` will return when passed
98
+ `dirname=""`. This is used to locate .md files in the root of the project, e.g.,
99
+ a README.md file. Looks for `BASE_DIR` if `MARKDOWN_VIEW_BASE_DIR` is not found.
100
+
101
+ * `MARKDOWN_VIEW_LOADERS`
102
+
103
+ A list of loaders that locate .md files. The default list includes only
104
+ `markdown_view.loaders.MarkdownLoader` which will by default try to load .md files
105
+ from root directories in INSTALLED_APPS packages much the same as Django's
106
+ `django.template.loaders.app_directories.Loader` looks to load from "templates".
107
+
108
+ * `MARKDOWN_VIEW_LOADER_TEMPLATES_DIR`
109
+
110
+ The name of the directories in INSTALLED_APPS packages in which to locate .md
111
+ files. Defaults to "" in order to locate .md filed in the root directories.
112
+
113
+ * `MARKDOWN_VIEW_EXTENSIONS`
114
+
115
+ The extensions to enable. These extensions are enabled be default:
116
+
117
+ * `toc`:
118
+ generates a Table of Contents. If `toc` is removed from extensions, then
119
+ `MARKDOWN_VIEW_TEMPLATE_USE_TOC` must be set to False.
120
+
121
+ * `tables`:
122
+ enables tables.
123
+
124
+ * `fenced_code`:
125
+ enables code blocks. If `fenced_code` is removed from extensions, then
126
+ `MARKDOWN_VIEW_TEMPLATE_USE_HIGHLIGHT_JS`, which provides the highlighting for
127
+ code blocks, can be disabled.
128
+
129
+ * `markdown_view.markdown_extensions.ImageExtension`:
130
+ makes images responsive in bootstrap4.
131
+
132
+ See https://python-markdown.github.io/extensions/ and
133
+ https://github.com/Python-Markdown/markdown/wiki/Third-Party-Extensions for more
134
+ extensions.
135
+
136
+ You can create your own extensions by following
137
+ https://github.com/Python-Markdown/markdown/wiki/Tutorial-1---Writing-Extensions-for-Python-Markdown
138
+
139
+ * `MARKDOWN_VIEW_TEMPLATE`
140
+
141
+ The Django template that'll be used to render the HTML that is generated from the
142
+ Markdown. Set your own template to style your pages. Context includes:
143
+
144
+ * `markdown_content`:
145
+ The HTML produced from the Markdown.
146
+
147
+ * `use_highlight_js`:
148
+ If highlight.js is enabled.
149
+
150
+ * `use_toc`:
151
+ If the table of contents should be rendered.
152
+
153
+ * `markdown_toc`:
154
+ A table of contents from the headers of the Markdown. Not set when `use_toc`
155
+ is False.
156
+
157
+ * `page_title`:
158
+ A guess at a page title, for now it's the first row of the TOC. Not set when
159
+ `use_toc` is False, or when the Markdown has no headers for the TOC to be
160
+ built from.
161
+
162
+ * `MARKDOWN_VIEW_TEMPLATE_USE_TOC`
163
+
164
+ Whether to render the TOC. If false, in the template context, `use_toc` is False
165
+ and `markdown_toc` and `page_title` are not present.
166
+
167
+ * `MARKDOWN_VIEW_TEMPLATE_USE_HIGHLIGHT_JS`
168
+
169
+ Whether to load and activate the highlight.js library in the template.
170
+
171
+ * `MARKDOWN_VIEW_REWRITE_INTERNAL_LINKS`
172
+
173
+ Defaults to `True`. Markdown source files commonly link to other `.md`
174
+ files using plain, filesystem-relative links (e.g.
175
+ ``[See also](../README-cron.md)``), exactly as they'd resolve on GitHub
176
+ or in a local editor. Since a rendered page is served at its *view's*
177
+ URL rather than at the source file's location on disk, such a link
178
+ would otherwise 404 in the browser.
179
+
180
+ When enabled, `MarkdownView` looks up whether any URL pattern in the
181
+ project serves the linked `.md` file via a `MarkdownView` (or
182
+ subclass), and if so, rewrites the link to that view's actual URL
183
+ instead of leaving the raw relative file path in place. See
184
+ `markdown_view/registry.py <markdown_view/registry.py>`_ for the
185
+ resolution logic. This is best-effort: links to files that aren't
186
+ routed anywhere, or whose route is behind a required URL argument, are
187
+ left exactly as Markdown would otherwise have rendered them -- nothing
188
+ breaks if a linked file simply isn't routed. Set to `False` to disable
189
+ this rewriting entirely.
190
+
191
+ Experimental Settings
192
+ ~~~~~~~~~~~~~~~~~~~~~
193
+
194
+ * `MARKDOWN_VIEW_USE_REQUEST_CONTEXT`
195
+
196
+ If the request context should be used as a base when creating the context with
197
+ which to render the Markdown internally. This is because the Markdown is rendered
198
+ once first in order to prepend it with `{% load static %}`.
199
+ This is not well tested; YMMV.
200
+
201
+ * `MARKDOWN_VIEW_EXTRA_CONTEXT`
202
+
203
+ Any extra context to send to the internal render of the Markdown. Can be used
204
+ to expose context to template tags embedded in the Markdown.
205
+ This is not well tested; YMMV.
206
+
207
+
208
+ Implementation
209
+ --------------
210
+
211
+ At a high level, `MarkdownView` will:
212
+
213
+ #. Use a template loader to locate `.md` given as `file_name`
214
+
215
+ #. Convert the Markdown to HTML
216
+
217
+ #. If `MARKDOWN_VIEW_REWRITE_INTERNAL_LINKS` is enabled, rewrite any
218
+ relative links to other `.md` files that resolve to a file served by a
219
+ registered `MarkdownView` route, to that route's actual URL
220
+
221
+ #. Render as a template, the resulting HTML prepended with
222
+ `{% load static %}`, into several context variables
223
+
224
+ #. Serve the `MARKDOWN_VIEW_TEMPLATE` with the context variables
225
+
226
+ Release Notes and Contributors
227
+ ------------------------------
228
+
229
+ * `Release notes <https://github.com/rgs258/django-markdown-view/releases>`_
230
+ * `Our wonderful contributors <https://github.com/rgs258/django-markdown-view/graphs/contributors>`_
231
+
232
+ Releasing
233
+ ---------
234
+
235
+ Versions are derived from git tags via `setuptools_scm`_, so there is no
236
+ hardcoded ``version`` to bump.
237
+
238
+ To publish a new release to PyPI:
239
+
240
+ #. Merge the changes for the release into ``master``.
241
+
242
+ #. `Create a GitHub Release`_ with a tag matching the desired version
243
+ (e.g. ``0.0.6``).
244
+
245
+ #. Publishing the release triggers the `Publish to PyPI`_ GitHub Actions
246
+ workflow, which builds the sdist/wheel and uploads them to PyPI using
247
+ `PyPI Trusted Publishing`_ (no stored token required).
248
+
249
+ Publishing beta / pre-releases
250
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
251
+
252
+ Beta and other pre-releases can be published to PyPI the same way as a
253
+ regular release, and this is standard practice: PyPI understands
254
+ `PEP 440`_ pre-release version identifiers and will not let a plain
255
+ ``pip install django-markdown-view`` pick them up by default, so betas are
256
+ safe to publish without risking accidental adoption.
257
+
258
+ #. Tag the release commit with an explicit PEP 440 pre-release suffix
259
+ instead of a plain version, e.g. ``0.0.6b1`` for a beta, or ``0.0.6rc1``
260
+ for a release candidate. ``setuptools_scm`` will pick up the tag as-is.
261
+
262
+ #. `Create a GitHub Release`_ for that tag and check the **"Set as a
263
+ pre-release"** box. This only affects how the release is labeled on
264
+ GitHub; PyPI determines pre-release status purely from the version
265
+ string.
266
+
267
+ #. Publishing the release triggers the same `Publish to PyPI`_ workflow,
268
+ which uploads the pre-release to PyPI. It will appear in the release
269
+ history but will not be installed by ``pip install
270
+ django-markdown-view``.
271
+
272
+ #. Users who want to try it must opt in, e.g. ``pip install --pre
273
+ django-markdown-view`` or by pinning the exact version, such as
274
+ ``pip install django-markdown-view==0.0.6b1``.
275
+
276
+ #. If further fixes are needed, bump the pre-release number (``0.0.6b2``,
277
+ ``0.0.6b3``, ...) rather than reusing one, since PyPI does not allow
278
+ re-uploading files for a version that has already been published.
279
+ Once the release is stable, tag and publish the final version without
280
+ a suffix (e.g. ``0.0.6``), which then becomes installable by default.
281
+
282
+ .. _setuptools_scm: https://github.com/pypa/setuptools_scm
283
+ .. _`Create a GitHub Release`: https://github.com/rgs258/django-markdown-view/releases/new
284
+ .. _`Publish to PyPI`: https://github.com/rgs258/django-markdown-view/actions/workflows/publish.yml
285
+ .. _`PyPI Trusted Publishing`: https://docs.pypi.org/trusted-publishers/
286
+ .. _`PEP 440`: https://peps.python.org/pep-0440/#pre-releases
287
+
288
+ Contributing
289
+ ------------
290
+
291
+ All contributions are very welcomed. Propositions, problems, bugs, and
292
+ enhancement are tracked with `GitHub issues`_ and patches are submitted
293
+ via `pull requests`_.
294
+
295
+ We use `GitHub Actions`_ coupled with `Coveralls`_ as continuous integration tools.
296
+
297
+ .. _`GitHub issues`: https://github.com/rgs258/django-markdown-view/issues
298
+ .. _`pull requests`: https://github.com/rgs258/django-markdown-view/pulls
299
+ .. _`GitHub Actions`: https://github.com/rgs258/django-markdown-view/actions
300
+ .. _Coveralls: https://coveralls.io/github/rgs258/django-markdown-view
301
+
302
+ Requirements
303
+ ------------
304
+
305
+ We aspire to support the currently supported versions of Django.
306
+
307
+ **The Tested With section describes aspirational goals.**
308
+
309
+ Tested with:
310
+
311
+ * Python: 3.10, 3.11, 3.12, 3.13
312
+ * Django: 5.2, 6.0
@@ -0,0 +1,37 @@
1
+ django_markdown_view-0.0.6b2.dist-info/licenses/LICENSE,sha256=Xun9tu6GoUnr8qy5C3MBQeqJZeuzkw67SQiFzexknls,1542
2
+ markdown_view/__init__.py,sha256=EXOCt-R6NOvU0yLhQcmwQT_U-DVK0TPZYpErkJ6tbko,61
3
+ markdown_view/apps.py,sha256=QwgUCdY8AupkXpT7AOqqigSaPY45bpp7KHeAYvDlMds,332
4
+ markdown_view/checks.py,sha256=ojtG0-R_I1XGc9Bik7C0ZLyM0TB__cIzRKFOWDnEDxk,1537
5
+ markdown_view/constants.py,sha256=BlDRKrMOJ20Pmby40r4FCSiHZ6Hexs2e1janwxkpsPA,580
6
+ markdown_view/loaders.py,sha256=dtZ2X01MGfW5cnGMH78jSQ08SATqsv_amPmBM9jOA14,2221
7
+ markdown_view/markdown_extensions.py,sha256=T_9cRg99vnXyhBA-evqVRRE0kHITx1YitRGJV9IghX8,635
8
+ markdown_view/models.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ markdown_view/registry.py,sha256=bCuPqe3T_iwEpWApeRIwwLqHx5tXwQUJ8heInJ_n9gQ,15919
10
+ markdown_view/views.py,sha256=TMVLbBQ23xTD1NXGrJe0LuMgeFbcYX4HrjkKrdST6_M,4162
11
+ markdown_view/templates/markdown_view/markdown.html,sha256=QyddBcicEe0CnGQFN66BRCNcUr3SUVXBEqwzLm9NCWQ,1232
12
+ markdown_view/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
+ markdown_view/tests/test_checks.py,sha256=dh4LYFj6kD_r7rEoPtsqfbqpJ5sOQmpnoQEi2-zkaq4,1584
14
+ markdown_view/tests/test_loaders.py,sha256=w1-WBFcRjTnYjZWFRFcJVWqbT5B8CYvU0oCwJiz7sZY,2583
15
+ markdown_view/tests/test_markdown_extensions.py,sha256=GHgcI3zeUFi-BisdrHJ8gx_NOXOv10q-vAKjuDuQ_kk,1271
16
+ markdown_view/tests/test_registry.py,sha256=b1Fy6JKVoiCQHQQDyJMyd0Vmbp8taoRf9JUcaQ0tO8M,14339
17
+ markdown_view/tests/test_settings_validation.py,sha256=bWlsbZxvGOlOtJUBtwD8dcBqjJhjx7HJTfDPZdgYLqY,2534
18
+ markdown_view/tests/test_views.py,sha256=aYpujitgvguMwK12t2S5XOEOfT6_tZ7S7_twZzohmVU,7112
19
+ markdown_view/tests/urls.py,sha256=xfj1o_k3iZvd8kox7hzYFn47H67_82M5KVA-KlrUPro,3630
20
+ markdown_view/tests/fixtures/loaders_a/readme.md,sha256=3ZLpASU8bfOp6GY2ExZPrzlnHfCb3LR8qGNpVC_9iLU,227
21
+ markdown_view/tests/fixtures/loaders_b/readme.md,sha256=NwuNR4ZsdXlmp2wE5GvfVNrkYoRzXcHwtqdvU_t72sA,227
22
+ markdown_view/tests/settings/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
+ markdown_view/tests/settings/coveralls_settings.py,sha256=JG-heiLhtoiGwxKL0sb77yIFqPeZGMAKaM4FcDxMgTs,2263
24
+ markdown_view/tests/testapp/CONTEXT_TEST.md,sha256=25kLvqZC8pHZ2DUuEUVABV28to3aMzUKsN-e5rDZPGY,72
25
+ markdown_view/tests/testapp/LOGGED_IN.md,sha256=_ycP4KN2-LCpdapSnskA-6VLs6f7nG3WTdmAGn3VDsw,73
26
+ markdown_view/tests/testapp/NESTED.md,sha256=hXAcDFiEwtG0aZatjSKRzoDiZIOOH3voYMZqfFGBYjc,192
27
+ markdown_view/tests/testapp/NO_HEADERS.md,sha256=YbL0KxChybEQauZ46zltWsQbzKRcvfGxTHzIZ174vkg,237
28
+ markdown_view/tests/testapp/README.md,sha256=xk_cwqo8kS6qNHCMTmmFsgohoMvzrs_tgIpC8e010S4,896
29
+ markdown_view/tests/testapp/STAFF.md,sha256=kJyaIF4JFSjc4QBcX_jAmlJ32_HHzpmWaygPbLeYqGw,68
30
+ markdown_view/tests/testapp/UNROUTED.md,sha256=6xbM35PNQ2e1FJWmihJ8_aTXSN96vYG_6RUrQIibl0A,157
31
+ markdown_view/tests/testapp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
+ markdown_view/tests/testapp/apps.py,sha256=my2snU00q5vjhr0BK_fs9tPVnDTLnk6PM8ezS3lV-ZY,173
33
+ markdown_view/tests/testapp/docs/OTHER.md,sha256=xRf-5jaJCnJiaz_bQaVMI5OVpVcuhWlNVejSedyY57k,231
34
+ django_markdown_view-0.0.6b2.dist-info/METADATA,sha256=zEBuEqrnCqVqmdQ1MzW_KVfiU370_HYV1MQnn7moqzw,11366
35
+ django_markdown_view-0.0.6b2.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
36
+ django_markdown_view-0.0.6b2.dist-info/top_level.txt,sha256=MqRRE0ldzEe17rDjvoESYz22dyeBcPAG9ELXUR5FD7w,14
37
+ django_markdown_view-0.0.6b2.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,27 @@
1
+ Copyright (c) Ryan J. Sullivan and individual contributors.
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without modification,
5
+ are permitted provided that the following conditions are met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright notice,
8
+ this list of conditions and the following disclaimer.
9
+
10
+ 2. Redistributions in binary form must reproduce the above copyright
11
+ notice, this list of conditions and the following disclaimer in the
12
+ documentation and/or other materials provided with the distribution.
13
+
14
+ 3. Neither the name of Django nor the names of its contributors may be used
15
+ to endorse or promote products derived from this software without
16
+ specific prior written permission.
17
+
18
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
22
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1 @@
1
+ markdown_view
@@ -0,0 +1 @@
1
+ default_app_config = "markdown_view.apps.MarkdownViewConfig"
markdown_view/apps.py ADDED
@@ -0,0 +1,12 @@
1
+ from django.apps import AppConfig
2
+ from django.core.checks import register, Tags
3
+
4
+ from markdown_view.checks import markdown_view_check
5
+
6
+
7
+ class MarkdownViewConfig(AppConfig):
8
+ name = "markdown_view"
9
+ verbose_name = "Serve .md pages as Django templates"
10
+
11
+ def ready(self):
12
+ register(markdown_view_check, Tags.security)
@@ -0,0 +1,42 @@
1
+ from django.conf import settings
2
+ from django.core import checks
3
+
4
+ # Expected type for each optional markdown_view setting. Validated by
5
+ # `markdown_view_check()` below (registered as a Django system check in
6
+ # `apps.py`), rather than at import time, so `manage.py check` -- and
7
+ # `override_settings` in tests -- can exercise this validation directly.
8
+ SETTINGS_TYPES = {
9
+ "MARKDOWN_VIEW_LOADERS": list,
10
+ "MARKDOWN_VIEW_LOADER_TEMPLATES_DIR": str,
11
+ "MARKDOWN_VIEW_EXTENSIONS": list,
12
+ "MARKDOWN_VIEW_BASE_DIR": str,
13
+ "MARKDOWN_VIEW_TEMPLATE": str,
14
+ "MARKDOWN_VIEW_TEMPLATE_USE_TOC": bool,
15
+ "MARKDOWN_VIEW_TEMPLATE_USE_HIGHLIGHT_JS": bool,
16
+ "MARKDOWN_VIEW_USE_REQUEST_CONTEXT": bool,
17
+ "MARKDOWN_VIEW_EXTRA_CONTEXT": dict,
18
+ "MARKDOWN_VIEW_REWRITE_INTERNAL_LINKS": bool,
19
+ }
20
+
21
+
22
+ def markdown_view_check(app_configs, **kwargs):
23
+ """
24
+ Validate that any of markdown_view's optional settings the project has
25
+ defined are of the expected type.
26
+ """
27
+ errors = []
28
+
29
+ for variable, instance_type in SETTINGS_TYPES.items():
30
+ if hasattr(settings, variable) and not isinstance(
31
+ getattr(settings, variable), instance_type
32
+ ):
33
+ errors.append(
34
+ checks.Error(
35
+ "Setting %s is not of type %s" % (variable, instance_type),
36
+ hint="Update %s in your settings module to be of type %s."
37
+ % (variable, instance_type),
38
+ id="markdown_view.E001",
39
+ )
40
+ )
41
+
42
+ return errors
@@ -0,0 +1,11 @@
1
+ from markdown_view.markdown_extensions import ImageExtension
2
+
3
+ DEFAULT_MARKDOWN_VIEW_LOADERS = ["markdown_view.loaders.MarkdownLoader", ]
4
+ DEFAULT_MARKDOWN_VIEW_LOADER_TEMPLATES_DIR = ""
5
+ DEFAULT_MARKDOWN_VIEW_EXTENSIONS = ["tables", "fenced_code", "toc", ImageExtension(), ]
6
+ DEFAULT_MARKDOWN_VIEW_TEMPLATE = "markdown_view/markdown.html"
7
+ DEFAULT_MARKDOWN_VIEW_TEMPLATE_USE_TOC = True
8
+ DEFAULT_MARKDOWN_VIEW_TEMPLATE_USE_HIGHLIGHT_JS = True
9
+ DEFAULT_MARKDOWN_VIEW_USE_REQUEST_CONTEXT = False
10
+ DEFAULT_MARKDOWN_VIEW_EXTRA_CONTEXT = {}
11
+ DEFAULT_MARKDOWN_VIEW_REWRITE_INTERNAL_LINKS = True
@@ -0,0 +1,61 @@
1
+ from django.conf import settings
2
+ from django.core.exceptions import SuspiciousFileOperation
3
+ from django.template import Origin
4
+ from django.template.loaders.filesystem import Loader as FilesystemLoader
5
+ from django.template.utils import get_app_template_dirs
6
+ from django.utils._os import safe_join
7
+
8
+ from markdown_view.constants import DEFAULT_MARKDOWN_VIEW_LOADER_TEMPLATES_DIR
9
+
10
+
11
+ class MarkdownLoader(FilesystemLoader):
12
+ """
13
+ A loader that will find a .md file in the context of any installed app or
14
+ application root.
15
+ """
16
+
17
+ def get_dirs(self):
18
+ base_dir = getattr(
19
+ settings,
20
+ "MARKDOWN_VIEW_BASE_DIR",
21
+ getattr(
22
+ settings,
23
+ "BASE_DIR",
24
+ None)
25
+ )
26
+ dirs = [*get_app_template_dirs(
27
+ getattr(
28
+ settings,
29
+ "MARKDOWN_VIEW_LOADER_TEMPLATES_DIR",
30
+ DEFAULT_MARKDOWN_VIEW_LOADER_TEMPLATES_DIR
31
+ )
32
+ )]
33
+ if base_dir:
34
+ dirs.extend([base_dir])
35
+ return dirs
36
+
37
+ def get_template_sources(self, template_name):
38
+ """
39
+ Return an Origin object pointing to an absolute path in each directory
40
+ in template_dirs. For security reasons, if a path doesn't lie inside
41
+ one of the template_dirs it is excluded from the result set.
42
+ """
43
+ if template_name.endswith('.md'):
44
+ template_split = template_name.split("/")
45
+ template_split.reverse()
46
+ template_app_dir = template_split.pop()
47
+ template_split.reverse()
48
+ for template_dir in self.get_dirs():
49
+ if str(template_dir).endswith(template_app_dir):
50
+ try:
51
+ name = safe_join(template_dir, *template_split)
52
+ except SuspiciousFileOperation:
53
+ # The joined path was located outside of this template_dir
54
+ # (it might be inside another one, so this isn't fatal).
55
+ continue
56
+
57
+ yield Origin(
58
+ name=name,
59
+ template_name=template_name,
60
+ loader=self,
61
+ )
@@ -0,0 +1,19 @@
1
+ import re
2
+
3
+ from markdown.treeprocessors import Treeprocessor
4
+ from markdown.extensions import Extension
5
+
6
+
7
+ class InlineImageProcessor(Treeprocessor):
8
+ def run(self, root):
9
+ for element in root.iter("img"):
10
+ src_orig = element.attrib.get('src', '')
11
+ src_re = re.sub(r"[/\\]*static[/\\]*", "", src_orig)
12
+ src = "{{% static '{}' %}}".format(src_re)
13
+ element.attrib["src"] = src
14
+ element.attrib["class"] = "img-fluid"
15
+
16
+
17
+ class ImageExtension(Extension):
18
+ def extendMarkdown(self, md):
19
+ md.treeprocessors.register(InlineImageProcessor(md), 'inlineimageprocessor', 15)
File without changes