├── pelican └── plugins │ ├── tests │ ├── __init__.py │ ├── fixtures │ │ ├── external_link.md │ │ ├── internal_link.md │ │ ├── internal_image.md │ │ ├── unknown_internal_link.md │ │ ├── colon_in_prop.md │ │ ├── tags_comma.md │ │ ├── assets │ │ │ └── images │ │ │ │ └── pelican-in-rock.webp │ │ ├── other_list_type.md │ │ └── tags.md │ └── test_obsidian_plugin.py │ └── obsidian │ ├── __init__.py │ └── obsidian.py ├── .gitignore ├── setup.py ├── .github ├── FUNDING.yml └── workflows │ └── test.yml ├── pyproject.toml ├── LICENSE ├── setup.cfg └── README.md /pelican/plugins/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **.egg-info/* 2 | __pycache__ 3 | dist/** 4 | build 5 | -------------------------------------------------------------------------------- /pelican/plugins/obsidian/__init__.py: -------------------------------------------------------------------------------- 1 | from .obsidian import * # noqa 2 | -------------------------------------------------------------------------------- /pelican/plugins/tests/fixtures/external_link.md: -------------------------------------------------------------------------------- 1 | [external](https://example.com) 2 | -------------------------------------------------------------------------------- /pelican/plugins/tests/fixtures/internal_link.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: internal link 3 | --- 4 | 5 | [[tags]] 6 | -------------------------------------------------------------------------------- /pelican/plugins/tests/fixtures/internal_image.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: internal image 3 | --- 4 | 5 | ![[pelican-in-rock.webp]] 6 | -------------------------------------------------------------------------------- /pelican/plugins/tests/fixtures/unknown_internal_link.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: unknown internal link 3 | --- 4 | 5 | [[great-article-not-exist]] 6 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # setup.py 2 | from setuptools import setup 3 | setup( 4 | use_scm_version=True, 5 | setup_requires=['setuptools_scm'], 6 | ) 7 | -------------------------------------------------------------------------------- /pelican/plugins/tests/fixtures/colon_in_prop.md: -------------------------------------------------------------------------------- 1 | --- 2 | category: misc 3 | title: "Hello: There" 4 | Status: published 5 | empty: 6 | --- 7 | 8 | Some text here. 9 | -------------------------------------------------------------------------------- /pelican/plugins/tests/fixtures/tags_comma.md: -------------------------------------------------------------------------------- 1 | --- 2 | Status: published 3 | tags: python,code-formatter,black 4 | Summary: Testing tags. 5 | --- 6 | 7 | Some text here. 8 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [jonathan-s] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | -------------------------------------------------------------------------------- /pelican/plugins/tests/fixtures/assets/images/pelican-in-rock.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonathan-s/pelican-obsidian/HEAD/pelican/plugins/tests/fixtures/assets/images/pelican-in-rock.webp -------------------------------------------------------------------------------- /pelican/plugins/tests/fixtures/other_list_type.md: -------------------------------------------------------------------------------- 1 | --- 2 | Status: published 3 | other: 4 | - python 5 | - code-formatter 6 | - black 7 | Summary: Testing tags. 8 | --- 9 | 10 | Some text here. 11 | -------------------------------------------------------------------------------- /pelican/plugins/tests/fixtures/tags.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: tags 3 | Status: published 4 | tags: 5 | - python 6 | - code-formatter 7 | - black 8 | Summary: Testing tags. 9 | --- 10 | 11 | Some text here. 12 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Python Tests 2 | 3 | on: 4 | push: 5 | branches: [ "*" ] 6 | 7 | jobs: 8 | test: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - uses: actions/checkout@v4 13 | 14 | - name: Set up Python 15 | uses: actions/setup-python@v4 16 | with: 17 | python-version: '3.x' # you can specify a specific version like '3.11' 18 | 19 | - name: Install dependencies 20 | run: | 21 | python -m pip install --upgrade pip 22 | pip install pytest 23 | pip install . 24 | 25 | - name: Run tests 26 | run: | 27 | pytest pelican/plugins/tests 28 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = [ 3 | "setuptools>=45", 4 | "wheel", 5 | "setuptools_scm[toml]>=6.0", 6 | ] 7 | build-backend = "setuptools.build_meta" 8 | 9 | [tool.black] 10 | line-length = 88 11 | target_version = ['py36'] 12 | include = '\.pyi?$' 13 | exclude = ''' 14 | ( 15 | /( 16 | \.eggs # exclude a few common directories in the 17 | | \.git # root of the project 18 | | \.hg 19 | | \.mypy_cache 20 | | \.tox 21 | | \.venv 22 | | _build 23 | | buck-out 24 | | build 25 | | dist 26 | )/ 27 | | foo.py # also separately exclude a file named foo.py in 28 | # the root of the project 29 | ) 30 | ''' 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2021 Jonathan Sundqvist 2 | 3 | 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: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | 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. 8 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = pelican-obsidian 3 | description = Makes pelican markdown files more compatible with Obsidian 4 | long_description = file: README.md 5 | long_description_content_type = text/markdown; charset=UTF-8 6 | url = https://github.com/jonathan-s/pelican-obsidian 7 | author = Jonathan Sundqvist 8 | author_email = jonathan@argpar.se, 9 | license = MIT 10 | license_file = LICENSE 11 | classifiers = 12 | Topic :: Software Development 13 | keywords = 14 | pelican 15 | obsidian 16 | plugin 17 | project_urls = 18 | Documentation = https://github.com/jonathan-s/pelican-obsidian 19 | Source = https://github.com/jonathan-s/pelican-obsidian 20 | Tracker = https://github.com/jonathan-s/pelican-obsidian/issues 21 | Funding = https://github.com/sponsors/jonathan-s 22 | 23 | [options] 24 | zip_safe = True 25 | packages = find_namespace: 26 | platforms = any 27 | include_package_data = True 28 | install_requires = 29 | pelican 30 | pelican-yaml-metadata 31 | pyyaml 32 | python_requires = >=3.7 33 | setup_requires = 34 | setuptools_scm 35 | 36 | [bdist_wheel] 37 | universal = 1 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Obsidian: A Plugin for Pelican 2 | ============================ 3 | 4 | 7 | 8 | Obsidian is a pelican plugin that allows you to use the syntax used within Obsidian and when pelican then renders these posts it won't look weird or out of place. 9 | 10 | Phrased differently, if you don't like that `#` is included in the name of the tag when you name it `#my-tag` and you think that internal pelican links are difficult to remember and would like to use `[[ my link ]]` as an internal link instead this plugin would be for you. 11 | 12 | If the article doesn't exist it will return text only. That way, there is a possibility of clearly separating posts that should belong on the blog and linked as such vs posts that should only belong inside Obsidian. 13 | 14 | 15 | Installation 16 | ------------ 17 | 18 | This plugin can be installed via: 19 | 20 | # not yet on pypi, but when it is you can install it with. 21 | pip install pelican-obsidian 22 | 23 | # meanwhile you can install using this repo. 24 | pip install git+git://github.com/jonathan-s/pelican-obsidian@main#egg=pelican-obsidian 25 | 26 | 27 | Add `'obsidian'` to the `PLUGINS` list in your Pelican config: 28 | 29 | ``` 30 | PLUGINS = [ 31 | 'obsidian', 32 | ] 33 | ``` 34 | 35 | Usage 36 | ----- 37 | 38 | In the tags section you will be able to use `#` without that being reflected in the actual name of the tag. In other words. 39 | 40 | ``` 41 | Tags: #my-tag 42 | 43 | # reflects as 44 | my-tag in the html output. 45 | ``` 46 | 47 | Links follow this format: 48 | 49 | ``` 50 | [[note name]] 51 | [[note name | custom link text]] 52 | ``` 53 | 54 | Files are similar: 55 | 56 | ``` 57 | ![[photo.jpg]] 58 | ![[photo.jpg | custom alt text]] 59 | ``` 60 | 61 | They explain more about the syntax in the section on [how to embed files](https://help.obsidian.md/How+to/Embed+files) 62 | 63 | 64 | Future features 65 | --------------- 66 | - Embed files or sections as described [here](https://help.obsidian.md/How+to/Format+your+notes) 67 | - Task list? 68 | - Support .rst? 69 | - don't generate links for drafts 70 | 71 | 72 | Implemented Features 73 | ----------------- 74 | - Apply the same linking for pages. 75 | 76 | 77 | 86 | 87 | License 88 | ------- 89 | 90 | This project is licensed under the MIT license. 91 | -------------------------------------------------------------------------------- /pelican/plugins/tests/test_obsidian_plugin.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | import pytest 4 | from pelican.generators import ArticlesGenerator 5 | from pelican.tests.support import get_settings 6 | from pelican.plugins.obsidian import ObsidianMarkdownReader, populate_files_and_articles # noqa 7 | 8 | 9 | @pytest.fixture 10 | def obsidian(path): 11 | settings = get_settings() 12 | settings["DEFAULT_CATEGORY"] = "Default" 13 | settings["DEFAULT_DATE"] = (1970, 1, 1) 14 | settings["READERS"] = {"asc": None} 15 | settings["CACHE_CONTENT"] = False 16 | omr = ObsidianMarkdownReader( 17 | settings=settings 18 | ) 19 | cwd = Path.cwd() 20 | source_path = cwd / "pelican" / "plugins" / "tests" / f"fixtures/{path}.md" 21 | generator = ArticlesGenerator( 22 | context=settings, 23 | settings=settings, 24 | path=cwd / "pelican" / "plugins" / "tests" / "fixtures", 25 | theme=settings["THEME"], 26 | output_path=None, 27 | ) 28 | populate_files_and_articles(generator) 29 | obsidian = omr.read(source_path) 30 | return obsidian 31 | 32 | 33 | @pytest.mark.parametrize('path', ["tags"]) 34 | def test_tags_works_correctly(obsidian): 35 | """Tags formatted as yaml list works properly""" 36 | content, meta = obsidian 37 | tags = meta["tags"] 38 | 39 | assert 'Some text here' in content 40 | assert len(tags) == 3 41 | assert 'python' == tags[0].name 42 | assert 'code-formatter' == tags[1].name 43 | assert 'black' == tags[2].name 44 | 45 | 46 | @pytest.mark.parametrize('path', ["tags_comma"]) 47 | def test_tags_works_pelican_way(obsidian): 48 | """Test normal tags""" 49 | content, meta = obsidian 50 | tags = meta["tags"] 51 | 52 | assert 'Some text here' in content 53 | assert len(meta["tags"]) == 3 54 | assert 'python' == tags[0].name 55 | assert 'code-formatter' == tags[1].name 56 | assert 'black' == tags[2].name 57 | 58 | 59 | @pytest.mark.parametrize('path', ["other_list_type"]) 60 | def test_other_list_property(obsidian): 61 | """List is preserved for other list type property""" 62 | content, meta = obsidian 63 | 64 | assert 'Some text here' in content 65 | assert len(meta["other"]) == 3 66 | 67 | 68 | @pytest.mark.parametrize('path', ["unknown_internal_link"]) 69 | def test_internal_link_not_seen_in_article(obsidian): 70 | """ 71 | If linked article has not been processed earlier 72 | content is not linked. 73 | """ 74 | content, meta = obsidian 75 | assert '
great-article-not-exist
' == content 76 | 77 | @pytest.mark.parametrize('path', ["internal_link"]) 78 | def test_internal_link_in_article(obsidian): 79 | """ 80 | If linked article has internal link, it should be linked 81 | """ 82 | content, meta = obsidian 83 | assert '' == content 84 | 85 | @pytest.mark.parametrize('path', ["internal_image"]) 86 | def test_internal_image_in_article(obsidian): 87 | """ 88 | If linked article has internal image, it should be linked 89 | """ 90 | content, meta = obsidian 91 | assert '