├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ └── barely-test.yml ├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── MANIFEST.in ├── README.md ├── barely ├── __init__.py ├── blueprints │ ├── blank │ │ └── config.yaml │ ├── blog │ │ ├── COPY-TO-CONFIG.yaml │ │ ├── blog │ │ │ ├── 2022-04-01-building-a-blog-with-barely │ │ │ │ └── post.md │ │ │ └── page.md │ │ ├── metadata.yaml │ │ ├── page.md │ │ ├── res │ │ │ ├── favicon.ico │ │ │ ├── highlight │ │ │ │ └── one-dark.css │ │ │ ├── icons │ │ │ │ ├── facebook.svg │ │ │ │ ├── reddit.svg │ │ │ │ ├── share.svg │ │ │ │ ├── twitter.svg │ │ │ │ └── ycombinator.svg │ │ │ ├── socialcover.png │ │ │ ├── style.sass │ │ │ └── theme.js │ │ ├── rss │ │ │ └── rss.md │ │ └── templates │ │ │ ├── overview.html │ │ │ ├── page.html │ │ │ ├── partials │ │ │ ├── base.html │ │ │ ├── preview.html │ │ │ └── socials.html │ │ │ ├── post.html │ │ │ ├── rss.html │ │ │ └── tag.html │ └── default │ │ ├── config.yaml │ │ ├── metadata.yaml │ │ ├── page.md │ │ ├── res │ │ ├── favicon.ico │ │ └── style.sass │ │ └── templates │ │ ├── page.html │ │ └── partials │ │ └── base.html ├── cli.py ├── common │ ├── __init__.py │ ├── config.py │ ├── decorators.py │ └── empty_config.yaml ├── core │ ├── ChangeTracker.py │ ├── EventHandler.py │ ├── ProcessingPipeline.py │ └── __init__.py ├── plugins │ ├── PluginManager.py │ ├── __init__.py │ ├── backup │ │ ├── LocalBackup │ │ │ ├── __init__.py │ │ │ ├── localbackup.py │ │ │ └── test_localbackup.py │ │ ├── __init__.py │ │ └── git │ │ │ ├── __init__.py │ │ │ ├── git.py │ │ │ └── test_git.py │ ├── content │ │ ├── AutoSEO │ │ │ ├── __init__.py │ │ │ ├── autoseo.py │ │ │ └── test_autoseo.py │ │ ├── Collections │ │ │ ├── __init__.py │ │ │ ├── collections.py │ │ │ └── test_collections.py │ │ ├── Forms │ │ │ ├── __init__.py │ │ │ ├── forms.py │ │ │ └── test_forms.py │ │ ├── Gallery │ │ │ ├── __init__.py │ │ │ ├── gallery.py │ │ │ └── test_gallery.py │ │ ├── Highlight │ │ │ ├── __init__.py │ │ │ ├── highlight.py │ │ │ └── test_highlight.py │ │ ├── Minify │ │ │ ├── __init__.py │ │ │ ├── minify.py │ │ │ └── test_minify.py │ │ ├── Pixelizer │ │ │ ├── __init__.py │ │ │ ├── pixelizer.py │ │ │ └── test_pixelizer.py │ │ ├── ReadingTime │ │ │ ├── __init__.py │ │ │ ├── readingtime.py │ │ │ └── test_readingtime.py │ │ ├── Timestamps │ │ │ ├── __init__.py │ │ │ ├── test_timestamps.py │ │ │ └── timestamps.py │ │ ├── ToC │ │ │ ├── __init__.py │ │ │ ├── test_toc.py │ │ │ └── toc.py │ │ └── __init__.py │ └── publication │ │ ├── __init__.py │ │ └── sftp │ │ ├── __init__.py │ │ ├── sftp.py │ │ └── test_sftp.py └── tests │ ├── __init__.py │ ├── ressources │ ├── EventHandler │ │ ├── affected │ │ │ ├── base.md │ │ │ ├── extendsdeeper.md │ │ │ ├── left.extendsbase.md │ │ │ ├── left.left.extendsbase.md │ │ │ ├── left.left.extendschild.md │ │ │ ├── left.parentless.md │ │ │ ├── left.right.completelyalone.md │ │ │ ├── right.left.completelyalone.md │ │ │ ├── right.left.deeper.md │ │ │ ├── right.right.extendsparentless.md │ │ │ └── templates │ │ │ │ ├── base.html │ │ │ │ ├── extendsdeeper.html │ │ │ │ ├── left │ │ │ │ ├── extendsbase.html │ │ │ │ ├── left │ │ │ │ │ ├── extendsbase.html │ │ │ │ │ └── extendschild.html │ │ │ │ ├── parentless.html │ │ │ │ └── right │ │ │ │ │ └── completelyalone.html │ │ │ │ └── right │ │ │ │ ├── left │ │ │ │ ├── completelyalone.html │ │ │ │ └── deeper.html │ │ │ │ └── right │ │ │ │ └── extendsparentless.html │ │ ├── force │ │ │ ├── config.yaml │ │ │ ├── dir │ │ │ │ ├── _subpage │ │ │ │ │ ├── subimage.jpg │ │ │ │ │ └── subpage.md │ │ │ │ ├── image.png │ │ │ │ └── page.md │ │ │ ├── file.txt │ │ │ ├── metadata.yaml │ │ │ └── templates │ │ │ │ └── template.html │ │ ├── getparent │ │ │ ├── _sub │ │ │ │ └── child.md │ │ │ ├── none │ │ │ │ └── none │ │ │ │ │ └── parentless.md │ │ │ ├── parent.md │ │ │ └── x_parent.md │ │ └── type │ │ │ ├── binary.mp4 │ │ │ ├── file.css │ │ │ └── notype │ ├── PluginManager │ │ ├── content │ │ │ ├── P1.py │ │ │ └── P2.py │ │ ├── empty │ │ │ └── collateral │ │ └── other │ │ │ ├── P3Dir │ │ │ └── P3.py │ │ │ └── P4.py │ ├── Plugins │ │ ├── AutoSEO │ │ │ ├── a.png │ │ │ └── b.jpg │ │ └── Gallery │ │ │ ├── a.png │ │ │ ├── b.jpg │ │ │ └── c.png │ ├── ProcessingPipeline │ │ ├── content_files │ │ │ ├── EMPTY.md │ │ │ ├── MULTI_YAML.md │ │ │ ├── ONE_YAML_ONE_MD.md │ │ │ ├── ONLY_MD.md │ │ │ └── ONLY_YAML.md │ │ ├── delete │ │ │ ├── dir │ │ │ │ └── collateral │ │ │ └── file.txt │ │ ├── move │ │ │ ├── fro.txt │ │ │ ├── fro │ │ │ │ └── collateral │ │ │ ├── fro2.txt │ │ │ └── fro2 │ │ │ │ └── collateral │ │ ├── pipes │ │ │ ├── _subpage │ │ │ │ └── subpage_dev.md │ │ │ ├── generic_dev.txt │ │ │ ├── image_dev.png │ │ │ ├── page_dev.md │ │ │ ├── page_with_child_dev.md │ │ │ ├── subpage_dev.md │ │ │ └── text_dev.txt │ │ ├── subpages │ │ │ └── _subpage │ │ │ │ └── sub.md │ │ ├── template.md │ │ ├── template.subtemplate.md │ │ ├── templates │ │ │ ├── page_dev.html │ │ │ ├── page_with_child_dev.html │ │ │ ├── subpage_dev.html │ │ │ └── template.html │ │ ├── test_load.png │ │ ├── test_read.md │ │ └── test_save.png │ ├── config.yaml │ └── empty │ │ └── empty │ ├── test_ChangeTracker.py │ ├── test_EventHandler.py │ ├── test_PluginManager.py │ └── test_ProcessingPipeline.py ├── docs ├── README.md ├── about.md ├── barely-demo.gif ├── blueprints.md ├── detailed-overview.md ├── getting-started.md ├── lighthouse.md ├── logo.png ├── modular-pages.md ├── plugins.md └── plugins │ ├── autoseo.md │ ├── collections.md │ ├── forms.md │ ├── gallery.md │ ├── git.md │ ├── highlight.md │ ├── localbackup.md │ ├── minify.md │ ├── pixelizer.md │ ├── readingtime.md │ ├── sftp.md │ ├── timestamps.md │ └── toc.md ├── flake.lock ├── flake.nix ├── setup.cfg └── setup.py /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Enter command '...' 16 | 2. Do the following action: '....' 17 | 3. See error 18 | 19 | **Expected behavior** 20 | A clear and concise description of what you expected to happen. 21 | 22 | **Screenshots** 23 | If applicable, add screenshots to help explain your problem. 24 | 25 | **Desktop (please complete the following information):** 26 | - OS: [e.g. Ubuntu] 27 | - Release [e.g. 1.0.0] 28 | 29 | **Additional context** 30 | Add any other context about the problem here. 31 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/barely-test.yml: -------------------------------------------------------------------------------- 1 | name: barely test 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Set up Python 3.9 17 | uses: actions/setup-python@v2 18 | with: 19 | python-version: 3.9 20 | - name: Install dependencies 21 | run: | 22 | python -m pip install --upgrade pip 23 | pip install . 24 | - name: Run Testsuite 25 | run: | 26 | barely test 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # barely-specific 2 | blueprints/devroot/config.yaml 3 | barely/tests/ref/backups/backups/ 4 | barely/tests/ref/backups/webroot/ 5 | barely/tests/ref/backups/devroot/ 6 | 7 | # Byte-compiled / optimized / DLL files 8 | __pycache__/ 9 | *.py[cod] 10 | *$py.class 11 | 12 | # C extensions 13 | *.so 14 | 15 | # Distribution / packaging 16 | .Python 17 | build/ 18 | develop-eggs/ 19 | dist/ 20 | downloads/ 21 | eggs/ 22 | .eggs/ 23 | lib/ 24 | lib64/ 25 | parts/ 26 | sdist/ 27 | var/ 28 | wheels/ 29 | share/python-wheels/ 30 | *.egg-info/ 31 | .installed.cfg 32 | *.egg 33 | MANIFEST 34 | 35 | # PyInstaller 36 | # Usually these files are written by a python script from a template 37 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 38 | *.manifest 39 | *.spec 40 | 41 | # Installer logs 42 | pip-log.txt 43 | pip-delete-this-directory.txt 44 | 45 | # Unit test / coverage reports 46 | htmlcov/ 47 | .tox/ 48 | .nox/ 49 | .coverage 50 | .coverage.* 51 | .cache 52 | nosetests.xml 53 | coverage.xml 54 | *.cover 55 | *.py,cover 56 | .hypothesis/ 57 | .pytest_cache/ 58 | cover/ 59 | 60 | # Translations 61 | *.mo 62 | *.pot 63 | 64 | # Django stuff: 65 | *.log 66 | local_settings.py 67 | db.sqlite3 68 | db.sqlite3-journal 69 | 70 | # Flask stuff: 71 | instance/ 72 | .webassets-cache 73 | 74 | # Scrapy stuff: 75 | .scrapy 76 | 77 | # Sphinx documentation 78 | docs/_build/ 79 | 80 | # PyBuilder 81 | .pybuilder/ 82 | target/ 83 | 84 | # Jupyter Notebook 85 | .ipynb_checkpoints 86 | 87 | # IPython 88 | profile_default/ 89 | ipython_config.py 90 | 91 | # pyenv 92 | # For a library or package, you might want to ignore these files since the code is 93 | # intended to run in multiple environments; otherwise, check them in: 94 | # .python-version 95 | 96 | # pipenv 97 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 98 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 99 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 100 | # install all needed dependencies. 101 | #Pipfile.lock 102 | 103 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 104 | __pypackages__/ 105 | 106 | # Celery stuff 107 | celerybeat-schedule 108 | celerybeat.pid 109 | 110 | # SageMath parsed files 111 | *.sage.py 112 | 113 | # Environments 114 | .env 115 | .venv 116 | env/ 117 | venv/ 118 | ENV/ 119 | env.bak/ 120 | venv.bak/ 121 | 122 | # Spyder project settings 123 | .spyderproject 124 | .spyproject 125 | 126 | # Rope project settings 127 | .ropeproject 128 | 129 | # mkdocs documentation 130 | /site 131 | 132 | # mypy 133 | .mypy_cache/ 134 | .dmypy.json 135 | dmypy.json 136 | 137 | # Pyre type checker 138 | .pyre/ 139 | 140 | # pytype static type analyzer 141 | .pytype/ 142 | 143 | # Cython debug symbols 144 | cython_debug/ 145 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [Unreleased] 8 | - nothing 9 | 10 | ## [1.2.2] - 2025-04-16 11 | ### Added 12 | - `--no-aftermath` / `-n` option for `barely rebuild` to skip aftermath question 13 | 14 | ### Removed 15 | - AutoSummary plugin, due to packaging difficulties and unidiomatic installation 16 | 17 | ## [1.2.1] - 2025-04-16 18 | ### Added 19 | - Nix Flake containing both a dev shell and the barely package 20 | 21 | ## [1.2.0] - 2025-04-16 22 | ### Changed 23 | - include the full `meta` field of collectibles in the exhibition list 24 | - use any `meta` field for sorting collections 25 | 26 | ### Fixed 27 | - PIL naming (ANTIALIAS->LANCZOS) 28 | 29 | ## [1.1.4] - 2022-04-07 30 | ### Added 31 | - new "blog" blueprint - read about it here: [https://notablog.io/blog/2022-04-01-building-a-blog-with-barely/](https://notablog.io/blog/2022-04-01-building-a-blog-with-barely/) 32 | 33 | ## [1.1.2] - 2022-04-05 34 | ### Added 35 | - "publish: false" in a page can disable rendering of a page. Can also be used as a global toggle 36 | - Collections: added ORDER_KEY and ORDER_REVERSE options. Can be used to configure the order of posts within collection pages. 37 | 38 | ### Changed 39 | - Collections: "created" timestamps take precedence over "edited" timestamps 40 | 41 | ## [1.1.0] - 2022-04-03 42 | ### Added 43 | - Collections: the OVERVIEW_CONTENT field allows to specify a markdown file to be used for the Collection overview page's content 44 | 45 | ### Fixed 46 | - no longer ignores "meta" fields already set on a page. Previously they were overridden in the meta parsing process 47 | - Collections: if a page belonging to a collection was not modified after a rebuild, it would not be passed through the plugin pipeline. Among other side effects, this did not allow for Timestamp- and ReadingTime-integration for post previews 48 | - Timestamp: no longer panics if a file vanishes 49 | - ToC: indented ToC HTML was not accessibility friendly 50 | 51 | ### Changed 52 | - ReadingTime: if the plugin was configured with WPM_FAST and WPM_SLOW values being identical, or if the text was very short, the fast and slow estimate could be identical. In this case, the plugin now simply shows "0" instead of "0 - 0" (for example) 53 | - the "content_raw" field utilized by some plugins now only contains the unparsed markdown content, where previously it also included the yaml headers 54 | 55 | ## [1.0.5] - 2022-02-23 56 | ### Fixed 57 | - autoSEO: fixed double "/" issue in image URLs 58 | 59 | ### Changed 60 | - silently ignores FileNotFound errors instead of throwing an exception, since usually, a temp file is at fault 61 | 62 | ## [1.0.4] - 2021-09-06 63 | ### Added 64 | - "--desktop" flag for lighthouse (default is mobile) 65 | 66 | ### Fixed 67 | - autoSEO: no longer crashes when no image can be found; ignores modular subpages 68 | - pixelizer: save original image without blowing up its size 69 | - minify: configuring minify no longer disables it 70 | - toc: don't generate tocs for modular subpages 71 | - highlight: regex non-greedy, previously wrong behaviour when multiple code blocks on a page; eliminated duplicate stylesheet generation 72 | 73 | ### Changed 74 | - enabled git plugin by default 75 | - higher resilience against errors (non-critical errors get logged instead of excepted) 76 | - autoSEO: use site_name as fallback for title 77 | - highlight: accepts more conventional / markdown-style lexer notation; un-escape code before highlighting 78 | - collections: category & overview pages now passed through enabled plugins; use summary as preview, if it exists; preview-image does no longer have to be in the same directory 79 | 80 | ## [1.0.3] - 2021-09-01 81 | ### Fixed 82 | - faulty system blueprints path made barely unable to find any blueprints 83 | 84 | ### Changed 85 | - to ensure at least somewhat "unique" alt-tags in galleries, include the number-position of the image in the gallery 86 | - original/fallback images will no longer be processed by PIL in the Pixelizer plugin, but rather just be copied; their filesizes got blown up before, and the step was needless anyways 87 | 88 | ## [1.0.2] - 2021-08-27 89 | ### Changed 90 | - clean up raw_content before AutoSummary consumes it 91 | 92 | ### Fixed 93 | - robots.txt no longer weirdly indented 94 | - sitemap generation now works after fixing a typo (hmtl -> html) 95 | 96 | ## [1.0.0] - 2021-08-26 97 | ### Added 98 | - lighthouse CLI integration 99 | - AutoSEO plugin 100 | - AutoSummary plugin 101 | - Gallery plugin 102 | - Minify plugin 103 | - Pixelizer plugin 104 | - global `-d` debugging-flag 105 | - this changelog! 106 | 107 | ### Changed 108 | - moved from BETA to STABLE 109 | - switched version numbering scheme from `v_095` to more readable `v1.0.0` 110 | - proper logging instead of print() 111 | - simplified the default blueprint to make it more usable 112 | 113 | ### Fixed 114 | - various small performance improvements, largely due to eliminating duplicate function calls 115 | 116 | ### Removed 117 | - Minimizer plugin, obsolete thanks to Minify and Pixelizer 118 | 119 | ## [0.9.5] - 2021-07-19 120 | ### Added 121 | - `-l` flag for light rebuilds (no re-rendering of images, preserves webroot) 122 | - `-s` flag to start the live server after a rebuild 123 | 124 | ### Fixed 125 | - bugs preventing forms and collections from being compiled correctly 126 | - unability to render an item no longer crashes barely 127 | 128 | ## [0.9.0] - 2021-06-19 129 | Initial beta release 130 | 131 | [Unreleased]: https://github.com/charludo/barely/compare/v1.2.2...HEAD 132 | [1.2.2]: https://github.com/charludo/barely/compare/v1.2.1...v1.2.2 133 | [1.2.1]: https://github.com/charludo/barely/compare/v1.2.0...v1.2.1 134 | [1.2.0]: https://github.com/charludo/barely/compare/v1.1.4...v1.2.0 135 | [1.1.4]: https://github.com/charludo/barely/compare/v1.1.2...v1.1.4 136 | [1.1.2]: https://github.com/charludo/barely/compare/v1.1.0...v1.1.2 137 | [1.1.0]: https://github.com/charludo/barely/compare/v1.0.5...v1.1.0 138 | [1.0.5]: https://github.com/charludo/barely/compare/v1.0.4...v1.0.5 139 | [1.0.4]: https://github.com/charludo/barely/compare/v1.0.3...v1.0.4 140 | [1.0.3]: https://github.com/charludo/barely/compare/v1.0.2...v1.0.3 141 | [1.0.2]: https://github.com/charludo/barely/compare/v1.0.0...v1.0.2 142 | [1.0.0]: https://github.com/charludo/barely/compare/v_095...v1.0.0 143 | [0.9.5]: https://github.com/charludo/barely/compare/v_090...v_095 144 | [0.9.0]: https://github.com/charludo/barely/releases/tag/v_090 145 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing 2 | 3 | Contributions are what make the open source community such an amazing place. Any contributions you make are **very** much welcome! 4 | 5 | 1. Fork the Project 6 | 2. Create your Feature Branch (`git checkout -b feature/NewFeature`) 7 | 3. Commit your Changes (`git commit -m 'Add some NewFeature'`) 8 | 4. Push to the Branch (`git push origin feature/NewFeature`) 9 | 5. Open a Pull Request 10 | 11 | **If you have written a plugin or created a blueprint and think others might benefit, please do share via the same way!!** 12 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include barely * 2 | recursive-include docs * 3 | recursive-include blueprints * 4 | include LICENSE 5 | include README.md 6 | -------------------------------------------------------------------------------- /barely/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/__init__.py -------------------------------------------------------------------------------- /barely/blueprints/blank/config.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/blueprints/blank/config.yaml -------------------------------------------------------------------------------- /barely/blueprints/blog/COPY-TO-CONFIG.yaml: -------------------------------------------------------------------------------- 1 | TIMESTAMPS: 2 | FORMAT: "%Y-%m-%d" 3 | 4 | TOC: 5 | LIST_ELEMENT: ul 6 | MIN_DEPTH: 2 7 | 8 | HIGHLIGHT: 9 | ASSETS_DIR: res 10 | LEXER: Rust 11 | THEME: one-dark 12 | 13 | PIXELIZER: 14 | TARGETS: 15 | - lg 660 75 16 | - md 510 70 17 | - sm 360 65 18 | LAYOUTS: 19 | - "(max-width: 800px) 100vw" 20 | - "660px" 21 | 22 | AUTO_SUMMARY: 23 | KEYWORDS: true 24 | 25 | AUTO_SEO: 26 | PRIORITY: 991 27 | 28 | COLLECTIONS: 29 | PRIORITY: 990 30 | PAGE: tags 31 | OVERVIEW_TITLE: List of all tags 32 | OVERVIEW_TEMPLATE: overview.html 33 | COLLECTION_TEMPLATE: tag.html 34 | ORDER_KEY: date 35 | ORDER_REVERSE: true 36 | -------------------------------------------------------------------------------- /barely/blueprints/blog/blog/page.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: All Bloposts 3 | exhibits: 4 | - all 5 | --- 6 | -------------------------------------------------------------------------------- /barely/blueprints/blog/metadata.yaml: -------------------------------------------------------------------------------- 1 | site_url: https://default.com 2 | site_name: barely blog 3 | site_description: default description 4 | site_keywords: 5 | - python 6 | - barely 7 | favicon: /res/favicon.ico 8 | title_image: /res/socialcover.png 9 | blogrepo: yourrepo 10 | -------------------------------------------------------------------------------- /barely/blueprints/blog/page.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Welcome! 3 | exhibits: 4 | - featured 5 | --- 6 | 7 | # Welcome! 8 | 9 | Welcome to this blog. At the moment, there's not much here . 10 | -------------------------------------------------------------------------------- /barely/blueprints/blog/res/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/blueprints/blog/res/favicon.ico -------------------------------------------------------------------------------- /barely/blueprints/blog/res/highlight/one-dark.css: -------------------------------------------------------------------------------- 1 | pre { line-height: 125%; } 2 | td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } 3 | span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } 4 | td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } 5 | span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } 6 | hl .hll { background-color: #ffffcc } 7 | hl { background: #282C34; color: #ABB2BF } 8 | hl .hlc { color: #7F848E } /* Comment */ 9 | hl .hlerr { color: #ABB2BF } /* Error */ 10 | hl .hlesc { color: #ABB2BF } /* Escape */ 11 | hl .hlg { color: #ABB2BF } /* Generic */ 12 | hl .hlk { color: #C678DD } /* Keyword */ 13 | hl .hll { color: #ABB2BF } /* Literal */ 14 | hl .hln { color: #E06C75 } /* Name */ 15 | hl .hlo { color: #56B6C2 } /* Operator */ 16 | hl .hlx { color: #ABB2BF } /* Other */ 17 | hl .hlp { color: #ABB2BF } /* Punctuation */ 18 | hl .hlch { color: #7F848E } /* Comment.Hashbang */ 19 | hl .hlcm { color: #7F848E } /* Comment.Multiline */ 20 | hl .hlcp { color: #7F848E } /* Comment.Preproc */ 21 | hl .hlcpf { color: #7F848E } /* Comment.PreprocFile */ 22 | hl .hlc1 { color: #7F848E } /* Comment.Single */ 23 | hl .hlcs { color: #7F848E } /* Comment.Special */ 24 | hl .hlgd { color: #ABB2BF } /* Generic.Deleted */ 25 | hl .hlge { color: #ABB2BF } /* Generic.Emph */ 26 | hl .hlgr { color: #ABB2BF } /* Generic.Error */ 27 | hl .hlgh { color: #ABB2BF } /* Generic.Heading */ 28 | hl .hlgi { color: #ABB2BF } /* Generic.Inserted */ 29 | hl .hlgo { color: #ABB2BF } /* Generic.Output */ 30 | hl .hlgp { color: #ABB2BF } /* Generic.Prompt */ 31 | hl .hlgs { color: #ABB2BF } /* Generic.Strong */ 32 | hl .hlgu { color: #ABB2BF } /* Generic.Subheading */ 33 | hl .hlgt { color: #ABB2BF } /* Generic.Traceback */ 34 | hl .hlkc { color: #E5C07B } /* Keyword.Constant */ 35 | hl .hlkd { color: #C678DD } /* Keyword.Declaration */ 36 | hl .hlkn { color: #C678DD } /* Keyword.Namespace */ 37 | hl .hlkp { color: #C678DD } /* Keyword.Pseudo */ 38 | hl .hlkr { color: #C678DD } /* Keyword.Reserved */ 39 | hl .hlkt { color: #E5C07B } /* Keyword.Type */ 40 | hl .hlld { color: #ABB2BF } /* Literal.Date */ 41 | hl .hlm { color: #D19A66 } /* Literal.Number */ 42 | hl .hls { color: #98C379 } /* Literal.String */ 43 | hl .hlna { color: #E06C75 } /* Name.Attribute */ 44 | hl .hlnb { color: #E5C07B } /* Name.Builtin */ 45 | hl .hlnc { color: #E5C07B } /* Name.Class */ 46 | hl .hlno { color: #E06C75 } /* Name.Constant */ 47 | hl .hlnd { color: #61AFEF } /* Name.Decorator */ 48 | hl .hlni { color: #E06C75 } /* Name.Entity */ 49 | hl .hlne { color: #E06C75 } /* Name.Exception */ 50 | hl .hlnf { color: #61AFEF; font-weight: bold } /* Name.Function */ 51 | hl .hlnl { color: #E06C75 } /* Name.Label */ 52 | hl .hlnn { color: #E06C75 } /* Name.Namespace */ 53 | hl .hlnx { color: #E06C75 } /* Name.Other */ 54 | hl .hlpy { color: #E06C75 } /* Name.Property */ 55 | hl .hlnt { color: #E06C75 } /* Name.Tag */ 56 | hl .hlnv { color: #E06C75 } /* Name.Variable */ 57 | hl .hlow { color: #56B6C2 } /* Operator.Word */ 58 | hl .hlp-Marker { color: #ABB2BF } /* Punctuation.Marker */ 59 | hl .hlw { color: #ABB2BF } /* Text.Whitespace */ 60 | hl .hlmb { color: #D19A66 } /* Literal.Number.Bin */ 61 | hl .hlmf { color: #D19A66 } /* Literal.Number.Float */ 62 | hl .hlmh { color: #D19A66 } /* Literal.Number.Hex */ 63 | hl .hlmi { color: #D19A66 } /* Literal.Number.Integer */ 64 | hl .hlmo { color: #D19A66 } /* Literal.Number.Oct */ 65 | hl .hlsa { color: #98C379 } /* Literal.String.Affix */ 66 | hl .hlsb { color: #98C379 } /* Literal.String.Backtick */ 67 | hl .hlsc { color: #98C379 } /* Literal.String.Char */ 68 | hl .hldl { color: #98C379 } /* Literal.String.Delimiter */ 69 | hl .hlsd { color: #98C379 } /* Literal.String.Doc */ 70 | hl .hls2 { color: #98C379 } /* Literal.String.Double */ 71 | hl .hlse { color: #98C379 } /* Literal.String.Escape */ 72 | hl .hlsh { color: #98C379 } /* Literal.String.Heredoc */ 73 | hl .hlsi { color: #98C379 } /* Literal.String.Interpol */ 74 | hl .hlsx { color: #98C379 } /* Literal.String.Other */ 75 | hl .hlsr { color: #98C379 } /* Literal.String.Regex */ 76 | hl .hls1 { color: #98C379 } /* Literal.String.Single */ 77 | hl .hlss { color: #98C379 } /* Literal.String.Symbol */ 78 | hl .hlbp { color: #E5C07B } /* Name.Builtin.Pseudo */ 79 | hl .hlfm { color: #56B6C2; font-weight: bold } /* Name.Function.Magic */ 80 | hl .hlvc { color: #E06C75 } /* Name.Variable.Class */ 81 | hl .hlvg { color: #E06C75 } /* Name.Variable.Global */ 82 | hl .hlvi { color: #E06C75 } /* Name.Variable.Instance */ 83 | hl .hlvm { color: #E06C75 } /* Name.Variable.Magic */ 84 | hl .hlil { color: #D19A66 } /* Literal.Number.Integer.Long */ -------------------------------------------------------------------------------- /barely/blueprints/blog/res/icons/facebook.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /barely/blueprints/blog/res/icons/reddit.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /barely/blueprints/blog/res/icons/share.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /barely/blueprints/blog/res/icons/twitter.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /barely/blueprints/blog/res/icons/ycombinator.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /barely/blueprints/blog/res/socialcover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/blueprints/blog/res/socialcover.png -------------------------------------------------------------------------------- /barely/blueprints/blog/res/theme.js: -------------------------------------------------------------------------------- 1 | prev = localStorage.getItem("theme"); 2 | if (prev) { 3 | setTheme(prev); 4 | } 5 | 6 | var toggleLight = document.getElementById("toggle--light"); 7 | var toggleDark = document.getElementById("toggle--dark"); 8 | var toggleRust = document.getElementById("toggle--rust"); 9 | 10 | toggleLight.addEventListener("click", function(){setTheme("theme--light")}); 11 | toggleDark.addEventListener("click", function(){setTheme("theme--dark")}); 12 | toggleRust.addEventListener("click", function(){setTheme("theme--rust")}); 13 | 14 | function setTheme(t) { 15 | document.body.className = t; 16 | localStorage.setItem("theme", t); 17 | } 18 | 19 | function toast(a) { 20 | navigator.clipboard.writeText(a); 21 | var x = document.getElementById("snackbar"); 22 | x.className = "show"; 23 | setTimeout(function(){ x.className = x.className.replace("show", ""); }, 3000); 24 | } 25 | 26 | window.toast = toast; 27 | -------------------------------------------------------------------------------- /barely/blueprints/blog/rss/rss.md: -------------------------------------------------------------------------------- 1 | --- 2 | extension: xml 3 | exhibits: 4 | - all 5 | --- 6 | -------------------------------------------------------------------------------- /barely/blueprints/blog/templates/overview.html: -------------------------------------------------------------------------------- 1 | {% extends "partials/base.html" %} 2 | 3 | {% block body %} 4 |
#{{ collection["name"] }} {{ [collection["size"]] }}
8 | {% endif %} 9 | {% endfor %} 10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /barely/blueprints/blog/templates/page.html: -------------------------------------------------------------------------------- 1 | {% extends "partials/base.html" %} 2 | 3 | {% block body %} 4 | {{ content }} 5 | {% for exhibit in exhibits %} 6 | 7 | {% for collectible in exhibits[exhibit] %} 8 | {% include "partials/preview.html" %} 9 | {% endfor %} 10 | {% endfor %} 11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /barely/blueprints/blog/templates/partials/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ seo_tags }} 5 | 6 | 7 | 8 | {% block extra_css %}{% endblock %} 9 | 10 | 11 |{{ collectible["date"] }} / {{ collectible["reading_time"] }} min
3 |{{ collectible["preview"] }} [read more]
5 |{{ created }}{% if ed and ed|string != created|string %} (ed. {{ ed }}){% endif %}{% if collections %} / #{{ collections[0] }}{% endif %} / 6 | {{ reading_time }} min 7 |
8 |{{ summary }}
11 |18 |20 | {% endif %} 21 |All code examples are available in this GitHub repository!
19 |
25 |28 | 37 | {% include "partials/socials.html" %} 38 |This is a 100% static website. Therefore, no direct commenting method is available. However, feel free to discuss this article on GitHub! 26 |
27 |
blocks with pygments
3 | """
4 | import re
5 | import os
6 | import html
7 | from pygments import highlight
8 | from pygments.formatters import HtmlFormatter
9 | from pygments.lexers import guess_lexer, get_lexer_by_name
10 | from barely.plugins import PluginBase
11 | from barely.core.ProcessingPipeline import write_file
12 |
13 |
14 | class Highlight(PluginBase):
15 | # make js-highlighting unnecessary by providing hightlighted pure html/css code blocks
16 |
17 | def __init__(self):
18 | super().__init__()
19 | standard_config = {
20 | "PRIORITY": 20,
21 | "CLASS_PREFIX": "hl",
22 | "LINE_NOS": "table",
23 | "TABSIZE": 4,
24 | "ENCODING": "utf-8",
25 | "THEME": "default",
26 | "LEXER": "",
27 | "ASSETS_DIR": "assets"
28 | }
29 | try:
30 | self.plugin_config = standard_config | self.config["HIGHLIGHT"]
31 | except KeyError:
32 | self.plugin_config = standard_config
33 |
34 | def register(self):
35 | return "Highlight", self.plugin_config["PRIORITY"], [self.config["PAGE_EXT"]]
36 |
37 | def action(self, *args, **kwargs):
38 | if "item" in kwargs:
39 | item = kwargs["item"]
40 | self.additional_styles = set()
41 |
42 | # accept page-level config for this plugin
43 | try:
44 | self.page_config = self.plugin_config | item["meta"]["highlight"]
45 | except KeyError:
46 | self.page_config = self.plugin_config.copy()
47 |
48 | item["content"] = re.sub(r".*?)\")?>(?P.*?)
", self._handle_code, item["content"], flags=re.S)
49 | item["action"] = "rendered, highlighted"
50 | item["additional_styles"] = list(self.additional_styles)
51 | yield item
52 |
53 | def _handle_code(self, match):
54 | code = html.unescape(match.group("code"))
55 |
56 | # if no lexer is set anywhere: guess it
57 | lexer_args = {
58 | "tabsize": self.page_config["TABSIZE"],
59 | "encoding": self.page_config["ENCODING"]
60 | }
61 | lexer = guess_lexer(code, **lexer_args)
62 |
63 | # use global (or page-level!) config lexer, if set
64 | try:
65 | lexer = get_lexer_by_name(self.page_config["LEXER"], **lexer_args)
66 | except Exception:
67 | pass
68 |
69 | # "best case": lexer is set right in the code snippet; obviously use it
70 | try:
71 | lexer_name = match.group("lang")
72 | lexer = get_lexer_by_name(lexer_name, **lexer_args)
73 | except Exception:
74 | pass
75 |
76 | formatter_args = {
77 | "classprefix": self.page_config["CLASS_PREFIX"],
78 | "linenos": self.page_config["LINE_NOS"],
79 | "style": self.page_config["THEME"]
80 | }
81 | formatter = HtmlFormatter(**formatter_args)
82 |
83 | css_path = os.path.join(self.config["ROOT"]["DEV"], self.page_config["ASSETS_DIR"], "highlight", self.page_config["THEME"]) + ".css"
84 | if not os.path.exists(css_path):
85 | css = formatter.get_style_defs(self.page_config["CLASS_PREFIX"])
86 | self.additional_styles.add(f"/{self.page_config['ASSETS_DIR']}/highlight/{self.page_config['THEME']}.css")
87 | write_file([
88 | {
89 | "destination": css_path,
90 | "origin": f"Code Hightlight Style: {self.page_config['THEME']}",
91 | "action": "generated styles",
92 | "output": css
93 | }
94 | ])
95 | return f"<{self.page_config['CLASS_PREFIX']}>{highlight(code, lexer, formatter)}{self.page_config['CLASS_PREFIX']}>"
96 |
--------------------------------------------------------------------------------
/barely/plugins/content/Highlight/test_highlight.py:
--------------------------------------------------------------------------------
1 | import re
2 | import unittest
3 | from mock import patch
4 | from barely.plugins.content.Highlight.highlight import Highlight
5 |
6 |
7 | class TestHighlight(unittest.TestCase):
8 |
9 | maxDiff = None
10 |
11 | def test___init__(self):
12 | golden = {
13 | "PRIORITY": 20,
14 | "CLASS_PREFIX": "hl",
15 | "LINE_NOS": "table",
16 | "TABSIZE": 4,
17 | "ENCODING": "utf-8",
18 | "THEME": "default",
19 | "LEXER": "",
20 | "ASSETS_DIR": "assets"
21 | }
22 |
23 | hl = Highlight()
24 | self.assertDictEqual(golden, hl.plugin_config)
25 |
26 | hl.config["HIGHLIGHT"] = {"PRIORITY": -1}
27 | hl.__init__()
28 |
29 | golden["PRIORITY"] = -1
30 | self.assertDictEqual(golden, hl.plugin_config)
31 |
32 | # reset
33 | del hl.config["HIGHLIGHT"]
34 | hl.__init__()
35 |
36 | def test_register(self):
37 | hl = Highlight()
38 | name, prio, ext = hl.register()
39 |
40 | self.assertEqual(name, "Highlight")
41 | self.assertEqual(prio, 20)
42 | self.assertEqual(ext, ["md"])
43 |
44 | @patch("barely.plugins.content.Highlight.highlight.write_file")
45 | def test_action(self, write_file):
46 | item = {
47 | "action": "rendered",
48 | "content": """
49 |
50 | def test_register(self):
51 | hl = Highlight()
52 | name, prio, ext = hl.register()
53 |
54 | self.assertEqual(name, "Highlight")
55 | self.assertEqual(prio, 20)
56 | self.assertEqual(ext, ["md"])
57 | """
58 | }
59 |
60 | golden_hl = """
61 | 1\n2\n3\n4\n5\n6\n7
63 |
def test_register(self):\n hl =
65 | Highlight()\n name, prio
66 | , ext = hl.
67 | register()\n\n self.
68 | assertEqual(name, "Highlight")\n self.assertEqual(
70 | prio, 20)\n self
71 | .assertEqual(ext,
72 | ["md"])\n
\n
73 | """
74 |
75 | hl = Highlight()
76 | hl.plugin_config["LEXER"] = "python"
77 |
78 | result = list(hl.action(item=item.copy()))[0]
79 |
80 | self.assertEqual(re.sub(r'[\s+]', '', golden_hl), re.sub(r'[\s+]', '', result["content"]))
81 | self.assertEqual("rendered, highlighted", result["action"])
82 | self.assertListEqual(["/assets/highlight/default.css"], result["additional_styles"])
83 | self.assertTrue(write_file.called)
84 |
85 | write_file.reset_mock()
86 |
87 | item = {
88 | "action": "rendered",
89 | "content": """
90 |
91 | fn main() {
92 | println!("Hello World!");
93 | }
94 | """,
95 | "meta": {
96 | "highlight": {
97 | "THEME": "pastie"
98 | }
99 | }
100 | }
101 |
102 | golden_hl = """
103 | 1\n2\n
104 | 3
fn
105 | main() {\n
106 | println!("Hello World!");
107 | \n }\n
\n
108 | """
109 |
110 | result = list(hl.action(item=item.copy()))[0]
111 |
112 | self.assertEqual(re.sub(r'[\s+]', '', golden_hl), re.sub(r'[\s+]', '', result["content"]))
113 | self.assertEqual("rendered, highlighted", result["action"])
114 | self.assertListEqual(["/assets/highlight/pastie.css"], result["additional_styles"])
115 | self.assertTrue(write_file.called)
116 |
--------------------------------------------------------------------------------
/barely/plugins/content/Minify/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/plugins/content/Minify/__init__.py
--------------------------------------------------------------------------------
/barely/plugins/content/Minify/minify.py:
--------------------------------------------------------------------------------
1 | """
2 | Minify provides functons to minimize
3 | javascript. It also functions
4 | as a sass/scss parser.
5 | """
6 | import os
7 | import sass
8 | from calmjs.parse import es5, exceptions as js_exceptions
9 | from calmjs.parse.unparsers.es5 import minify_print
10 | from barely.plugins import PluginBase
11 |
12 |
13 | class Minify(PluginBase):
14 | # Minify provides functions to compile and reduce scss and js in size
15 |
16 | def __init__(self):
17 | super().__init__()
18 |
19 | standard_config = {
20 | "PRIORITY": 3,
21 | "JS_OBFUSCATE": True,
22 | "JS_OBFUSCATE_GLOBALS": True,
23 | "CSS_INCLUDE_COMMENTS": False,
24 | "CSS_OUTPUT_STYLE": "compressed"
25 | }
26 | try:
27 | self.plugin_config = standard_config | self.config["MINIFY"]
28 | except KeyError:
29 | self.plugin_config = standard_config
30 | self.func_map = {
31 | "js": self.minimize_js,
32 | "sass,scss": self.minimize_css
33 | }
34 | self.register_for = sum([group.split(",") for group in self.func_map.keys()], [])
35 |
36 | def register(self):
37 | return "Minify", self.plugin_config["PRIORITY"], self.register_for
38 |
39 | def action(self, *args, **kwargs):
40 | if "item" in kwargs:
41 | item = kwargs["item"]
42 | for key, func in self.func_map.items():
43 | if item["extension"] in key:
44 | yield func(item)
45 |
46 | def minimize_css(self, item):
47 | try:
48 | indented = True if item["extension"] == "sass" else False
49 | compiled = sass.compile(string=item["content_raw"], output_style=self.plugin_config["CSS_OUTPUT_STYLE"],
50 | indented=indented, source_comments=self.plugin_config["CSS_INCLUDE_COMMENTS"])
51 | item["destination"] = os.path.splitext(item["destination"])[0] + ".css"
52 | item["action"] = "compiled"
53 | item["output"] = compiled
54 | except sass.CompileError as e:
55 | self.logger.error(f"SASS Compile {e}")
56 | return item
57 |
58 | def minimize_js(self, item):
59 | try:
60 | minified = minify_print(es5(item["output"]), obfuscate=self.plugin_config["JS_OBFUSCATE"],
61 | obfuscate_globals=self.plugin_config["JS_OBFUSCATE_GLOBALS"])
62 | item["output"] = minified
63 | item["action"] = "compiled"
64 | except js_exceptions.ECMASyntaxError as e:
65 | self.logger.error(f"JS Syntax Error: {e}")
66 | return item
67 |
--------------------------------------------------------------------------------
/barely/plugins/content/Minify/test_minify.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | from mock import patch
3 | from unittest.mock import MagicMock
4 | from barely.plugins.content.Minify.minify import Minify
5 |
6 |
7 | class TestMinify(unittest.TestCase):
8 |
9 | def test___init__(self):
10 | mini = Minify()
11 |
12 | standard_config = {
13 | "PRIORITY": 3,
14 | "JS_OBFUSCATE": True,
15 | "JS_OBFUSCATE_GLOBALS": True,
16 | "CSS_INCLUDE_COMMENTS": False,
17 | "CSS_OUTPUT_STYLE": "compressed"
18 | }
19 |
20 | self.assertDictEqual(standard_config, mini.plugin_config)
21 | self.assertListEqual(["js", "sass", "scss"], mini.register_for)
22 |
23 | def test_register(self):
24 | mini = Minify()
25 | name, prio, ext = mini.register()
26 |
27 | self.assertEqual(name, "Minify")
28 | self.assertEqual(prio, 3)
29 | self.assertEqual(ext, ["js", "sass", "scss"])
30 |
31 | @patch("barely.plugins.content.Minify.minify.minify_print")
32 | @patch("barely.plugins.content.Minify.minify.es5")
33 | @patch("barely.plugins.content.Minify.minify.sass")
34 | def test_action(self, sass, es5, minifyjs):
35 | sass.compile = MagicMock(return_value="compiled")
36 | minifyjs.return_value = "smallerjs"
37 |
38 | item = {
39 | "destination": "",
40 | "output": "",
41 | "extension": "",
42 | "content_raw": ""
43 | }
44 |
45 | mini = Minify()
46 |
47 | # SASS
48 | item["destination"] = "style.sass"
49 | item["extension"] = "sass"
50 | result = list(mini.action(item=item.copy()))[0]
51 | self.assertEqual(result["destination"], "style.css")
52 | self.assertEqual(result["output"], "compiled")
53 | self.assertEqual(result["action"], "compiled")
54 |
55 | # JS
56 | item["extension"] = "js"
57 | result = list(mini.action(item=item.copy()))[0]
58 | self.assertEqual(result["output"], "smallerjs")
59 | self.assertEqual(result["action"], "compiled")
60 |
--------------------------------------------------------------------------------
/barely/plugins/content/Pixelizer/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/plugins/content/Pixelizer/__init__.py
--------------------------------------------------------------------------------
/barely/plugins/content/Pixelizer/pixelizer.py:
--------------------------------------------------------------------------------
1 | """
2 | Pixelizer resizes, compresses, and
3 | converts images into web-friendly
4 | formats. It also transforms
5 | tags in tags with fallback.
6 | """
7 | import re
8 | import os
9 | from PIL import Image
10 | from barely.plugins import PluginBase
11 |
12 |
13 | class Pixelizer(PluginBase):
14 | # Minify provides functions to compile and reduce scss and js in size
15 |
16 | def __init__(self):
17 | super().__init__()
18 | try:
19 | standard_config = {
20 | "PRIORITY": 3,
21 | "TARGETS": [
22 | "lg 1000 70",
23 | "md 650 70",
24 | "sm 300 70"
25 | ],
26 | "LAYOUTS": [
27 | "(max-width: 1000px) 100vw",
28 | "1000px"
29 | ]
30 | }
31 | self.plugin_config = standard_config | self.config["PIXELIZER"]
32 |
33 | for i, t in enumerate(self.plugin_config["TARGETS"]):
34 | slug, width, quality = t.split()
35 | self.plugin_config["TARGETS"][i] = {
36 | "slug": slug,
37 | "width": int(width),
38 | "quality": int(quality)
39 | }
40 |
41 | self.func_map = {
42 | "png,jpg,jpeg,tif,tiff,bmp": self.process_image,
43 | self.config["PAGE_EXT"]: self.process_page
44 | }
45 | self.register_for = sum([group.split(",") for group in self.func_map.keys()], [])
46 | except KeyError:
47 | self.plugin_config = {"PRIORITY": -1}
48 | self.register_for = []
49 |
50 | def register(self):
51 | return "Pixelizer", self.plugin_config["PRIORITY"], self.register_for
52 |
53 | def action(self, *args, **kwargs):
54 | if "item" in kwargs:
55 | item = kwargs["item"]
56 | for key, func in self.func_map.items():
57 | if item["extension"] in key:
58 | yield from func(item)
59 |
60 | def process_image(self, item):
61 | item["action"] = "processed"
62 | filename = os.path.splitext(item["destination"])[0]
63 |
64 | for type in ["webp", item["extension"]]:
65 | for target in self.plugin_config["TARGETS"]:
66 | self.logger.debug(f"Started processing for type: {type}, target: {target['slug']}")
67 | variant = item.copy()
68 | variant["image"] = item["image"].copy()
69 | try:
70 | _, original_y = item["image"].size
71 | target_x = target["width"]
72 | size = target_x, original_y
73 | self.logger.debug(f"Original size: {item['image'].size}, New size: {size}")
74 | variant["image"].thumbnail(size, Image.LANCZOS)
75 | variant["quality"] = target["quality"]
76 | variant["destination"] = f"{filename}-{target['slug']}.{type}"
77 | variant["extension"] = type
78 | except Exception as e:
79 | self.logger.error(f"An Error occured while handling the image: {e}")
80 | self.logger.debug(f"Finished processing for type: {type}, target: {target['slug']}")
81 | yield variant
82 |
83 | # copy the original as fallback
84 | item["copymode"] = True
85 | yield item
86 |
87 | def process_page(self, item):
88 | try:
89 | if "none" in item["meta"]["PIXELIZER"]:
90 | yield item
91 | return
92 | except KeyError:
93 | pass
94 |
95 | try:
96 | self.item_config = self.plugin_config.copy() | {
97 | "LAYOUTS": item["meta"]["PIXELIZER"]
98 | }
99 | except KeyError:
100 | self.item_config = self.plugin_config.copy()
101 |
102 | item["content"] = re.sub(r"
.*)[\"'])?\s+src=[\"'](?P\S+)\.(?P[a-zA-Z]+)[\"'](?:\s+alt=[\"'](?P.*)[\"'])?\s*[/]?>", self._generate_tag, item["content"])
103 |
104 | yield item
105 |
106 | def _generate_tag(self, match):
107 | file = match.group("file")
108 | ext = match.group("ext")
109 | alt = ""
110 |
111 | if match.group("alt2") is not None:
112 | alt = match.group("alt2")
113 | elif match.group("alt1") is not None:
114 | alt = match.group("alt1")
115 |
116 | if ext not in self.register_for:
117 | return f"
"
118 |
119 | sizes = ", ".join(self.item_config["LAYOUTS"])
120 |
121 | components = [""]
122 |
123 | for type in ["webp", ext]:
124 | c = f""
132 |
133 | components.append(c)
134 |
135 | components.append(f"
")
136 | components.append(" ")
137 |
138 | return "\n".join(components)
139 |
--------------------------------------------------------------------------------
/barely/plugins/content/Pixelizer/test_pixelizer.py:
--------------------------------------------------------------------------------
1 | import re
2 | import unittest
3 | from mock import patch
4 | from unittest.mock import MagicMock
5 | from barely.plugins.content.Pixelizer.pixelizer import Pixelizer
6 |
7 |
8 | class TestPixelizer(unittest.TestCase):
9 |
10 | def test___init__(self):
11 | pix = Pixelizer()
12 | self.assertDictEqual({"PRIORITY": -1}, pix.plugin_config)
13 | self.assertListEqual([], pix.register_for)
14 |
15 | golden = {
16 | "PRIORITY": 2,
17 | "TARGETS": [
18 | {
19 | "slug": "lg",
20 | "width": 1000,
21 | "quality": 70
22 | },
23 | {
24 | "slug": "md",
25 | "width": 650,
26 | "quality": 70
27 | },
28 | {
29 | "slug": "sm",
30 | "width": 300,
31 | "quality": 70
32 | }
33 | ],
34 | "LAYOUTS": [
35 | "(max-width: 1000px) 100vw",
36 | "1000px"
37 | ]
38 | }
39 |
40 | golden_register_for = ["png", "jpg", "jpeg", "tif", "tiff", "bmp", "md"]
41 |
42 | pix.config["PIXELIZER"] = {"PRIORITY": 2}
43 | pix.__init__()
44 |
45 | self.assertDictEqual(golden, pix.plugin_config)
46 | self.assertListEqual(golden_register_for, pix.register_for)
47 |
48 | # reset
49 | del pix.config["PIXELIZER"]
50 | pix.__init__()
51 |
52 | def test_register(self):
53 | pix = Pixelizer()
54 | name, prio, ext = pix.register()
55 |
56 | self.assertEqual(name, "Pixelizer")
57 | self.assertEqual(prio, -1)
58 | self.assertEqual(ext, [])
59 |
60 | @patch("barely.plugins.content.Pixelizer.pixelizer.Image")
61 | def test_action(self, image):
62 | # Testing the image handling
63 | image.thumbnail = MagicMock()
64 | image.size = 1920, 720
65 |
66 | item = {
67 | "destination": "/path/to/somewhere.png",
68 | "output": "",
69 | "extension": "png",
70 | "content_raw": "",
71 | "image": image
72 | }
73 |
74 | pix = Pixelizer()
75 | pix.config["PIXELIZER"] = {"PRIORITY": 1}
76 | pix.__init__()
77 |
78 | # Image
79 | result = list(pix.action(item=item.copy()))
80 |
81 | self.assertEqual(len(result), 7)
82 |
83 | self.assertEqual(result[0]["quality"], 70)
84 | self.assertEqual(result[0]["destination"], "/path/to/somewhere-lg.webp")
85 | self.assertEqual(result[0]["extension"], "webp")
86 |
87 | self.assertEqual(result[5]["quality"], 70)
88 | self.assertEqual(result[5]["destination"], "/path/to/somewhere-sm.png")
89 | self.assertEqual(result[5]["extension"], "png")
90 |
91 | self.assertEqual(result[6]["destination"], "/path/to/somewhere.png")
92 | self.assertEqual(result[6]["action"], "processed")
93 | self.assertTrue(result[6]["copymode"])
94 |
95 | # Testing the page handling
96 |
97 | item = {
98 | "extension": "md",
99 | "content": """
100 |
101 |
102 |
103 | """
104 | }
105 |
106 | golden = """
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 | """
115 | result = list(pix.action(item=item.copy()))[0]
116 |
117 | self.assertEqual(re.sub(r'[\s+]', '', result["content"]), re.sub(r'[\s+]', '', golden))
118 |
119 | # "none"
120 | item = {
121 | "extension": "md",
122 | "content": """
123 |
124 |
125 |
126 | """,
127 | "meta": {
128 | "PIXELIZER": "none"
129 | }
130 | }
131 | result = list(pix.action(item=item.copy()))[0]
132 | self.assertDictEqual(result, item)
133 |
134 | # unsupported type
135 | item = {
136 | "extension": "md",
137 | "content": """
138 |
139 |
140 |
141 | """
142 | }
143 |
144 | golden = {
145 | "extension": "md",
146 | "content": """
147 |
148 |
149 |
150 | """
151 | }
152 | result = list(pix.action(item=item.copy()))[0]
153 | self.assertDictEqual(result, golden)
154 |
155 | del pix.config["PIXELIZER"]
156 | pix.__init__()
157 |
--------------------------------------------------------------------------------
/barely/plugins/content/ReadingTime/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/plugins/content/ReadingTime/__init__.py
--------------------------------------------------------------------------------
/barely/plugins/content/ReadingTime/readingtime.py:
--------------------------------------------------------------------------------
1 | """
2 | estimate how long it will take to read a page
3 | """
4 | from barely.plugins import PluginBase
5 |
6 |
7 | class ReadingTime(PluginBase):
8 | # very rough estimate of the reading time in minutes
9 |
10 | def __init__(self):
11 | super().__init__()
12 | standard_config = {
13 | "PRIORITY": 850,
14 | "WPM_FAST": 265,
15 | "WPM_SLOW": 90,
16 | "SEPARATOR": " - "
17 | }
18 | try:
19 | self.plugin_config = standard_config | self.config["READING_TIME"]
20 | except KeyError:
21 | self.plugin_config = standard_config
22 |
23 | def register(self):
24 | return "ReadingTime", self.plugin_config["PRIORITY"], [self.config["PAGE_EXT"]]
25 |
26 | def action(self, *args, **kwargs):
27 | if "item" in kwargs:
28 | item = kwargs["item"]
29 | word_count = len(item["content_raw"].split())
30 | slow = word_count // int(self.plugin_config["WPM_SLOW"])
31 | fast = word_count // int(self.plugin_config["WPM_FAST"])
32 | if slow != fast:
33 | item["meta"]["reading_time"] = f"{fast}{self.plugin_config['SEPARATOR']}{slow}"
34 | else:
35 | item["meta"]["reading_time"] = str(slow)
36 | yield item
37 |
--------------------------------------------------------------------------------
/barely/plugins/content/ReadingTime/test_readingtime.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | from barely.plugins.content.ReadingTime.readingtime import ReadingTime
3 |
4 |
5 | class TestReadingTime(unittest.TestCase):
6 |
7 | def test___init__(self):
8 | golden = {
9 | "PRIORITY": 850,
10 | "WPM_FAST": 265,
11 | "WPM_SLOW": 90,
12 | "SEPARATOR": " - "
13 | }
14 | rt = ReadingTime()
15 | self.assertDictEqual(golden, rt.plugin_config)
16 |
17 | rt.config["READING_TIME"] = {"PRIORITY": -1}
18 | rt.__init__()
19 |
20 | golden["PRIORITY"] = -1
21 | self.assertDictEqual(golden, rt.plugin_config)
22 |
23 | # reset
24 | del rt.config["READING_TIME"]
25 | rt.__init__()
26 |
27 | def test_register(self):
28 | rt = ReadingTime()
29 | name, prio, ext = rt.register()
30 |
31 | self.assertEqual(name, "ReadingTime")
32 | self.assertEqual(prio, 850)
33 | self.assertEqual(ext, ["md"])
34 |
35 | def test_action(self):
36 | item = {
37 | "content_raw": """
38 | Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam
39 | voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit
40 | amet.
41 | Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam
42 | voluptua.
43 | At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet
44 | Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam
45 | voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet
46 |
47 | Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et
48 | accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit ame,
49 | consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.
50 |
51 | Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum
52 | iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto
53 | odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi.
54 |
55 | Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer
56 | """,
57 | "meta": {}
58 | }
59 |
60 | rt = ReadingTime()
61 | result = list(rt.action(item=item))[0]
62 |
63 | self.assertEqual(result["meta"]["reading_time"], "1 - 3")
64 |
--------------------------------------------------------------------------------
/barely/plugins/content/Timestamps/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/plugins/content/Timestamps/__init__.py
--------------------------------------------------------------------------------
/barely/plugins/content/Timestamps/test_timestamps.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | from mock import patch
3 | from barely.plugins.content.Timestamps.timestamps import Timestamps
4 |
5 |
6 | class TestTimestamps(unittest.TestCase):
7 |
8 | def test___init__(self):
9 | ts = Timestamps()
10 | self.assertDictEqual({"PRIORITY": -1}, ts.plugin_config)
11 |
12 | ts.config["TIMESTAMPS"] = {"PRIORITY": 2}
13 | ts.__init__()
14 |
15 | golden = {
16 | "PRIORITY": 2,
17 | "FORMAT": "%d.%m.%Y"
18 | }
19 |
20 | self.assertDictEqual(golden, ts.plugin_config)
21 |
22 | # reset
23 | del ts.config["TIMESTAMPS"]
24 | ts.__init__()
25 |
26 | def test_register(self):
27 | ts = Timestamps()
28 | name, prio, ext = ts.register()
29 |
30 | self.assertEqual(name, "Timestamps")
31 | self.assertEqual(prio, -1)
32 | self.assertEqual(ext, ["md"])
33 |
34 | @patch("barely.plugins.content.Timestamps.timestamps.getctime")
35 | @patch("barely.plugins.content.Timestamps.timestamps.getmtime")
36 | def test_action(self, getmtime, getctime):
37 | getctime.return_value = 1622928404
38 | getmtime.return_value = 1622928404
39 |
40 | ts = Timestamps()
41 | ts.plugin_config = {
42 | "PRIORITY": 3,
43 | "FORMAT": "%d.%m.%Y"
44 | }
45 |
46 | item = {
47 | "origin": "",
48 | "meta": {}
49 | }
50 |
51 | result = list(ts.action(item=item))[0]
52 |
53 | self.assertEqual(result["meta"]["created"], "05.06.2021")
54 | self.assertEqual(result["meta"]["edited"], "05.06.2021")
55 |
56 | item = {
57 | "origin": "",
58 | "meta": {
59 | "created": "custom",
60 | "edited": "custom"
61 | }
62 | }
63 |
64 | result = list(ts.action(item=item))[0]
65 |
66 | self.assertEqual(result["meta"]["created"], "custom")
67 | self.assertEqual(result["meta"]["edited"], "custom")
68 |
--------------------------------------------------------------------------------
/barely/plugins/content/Timestamps/timestamps.py:
--------------------------------------------------------------------------------
1 | """
2 | timestamp pages with their creation date
3 | and latest edit date, however do
4 | respect user overrides
5 | """
6 | from datetime import datetime
7 | from os.path import getctime, getmtime
8 | from barely.plugins import PluginBase
9 |
10 |
11 | class Timestamps(PluginBase):
12 | # Timestamps guesses what the user is interested in and sets timestamps accordingly
13 |
14 | def __init__(self):
15 | super().__init__()
16 | try:
17 | standard_config = {
18 | "PRIORITY": 3,
19 | "FORMAT": "%d.%m.%Y"
20 | }
21 | self.plugin_config = standard_config | self.config["TIMESTAMPS"]
22 | except KeyError:
23 | self.plugin_config = {"PRIORITY": -1}
24 |
25 | def register(self):
26 | return "Timestamps", self.plugin_config["PRIORITY"], [self.config["PAGE_EXT"]]
27 |
28 | def action(self, *args, **kwargs):
29 | if "item" in kwargs:
30 | item = kwargs["item"]
31 |
32 | try:
33 | ctime = datetime.fromtimestamp(getctime(item["origin"])).strftime(self.plugin_config["FORMAT"])
34 | mtime = datetime.fromtimestamp(getmtime(item["origin"])).strftime(self.plugin_config["FORMAT"])
35 |
36 | if "created" not in item["meta"]: # if not set by user, set Created Time
37 | item["meta"]["created"] = ctime
38 | if "edited" not in item["meta"]: # if not set by user, set Modified Time
39 | item["meta"]["edited"] = mtime
40 | except FileNotFoundError:
41 | self.logger.debug(f"{item['origin']} not found.")
42 |
43 | yield item
44 |
--------------------------------------------------------------------------------
/barely/plugins/content/ToC/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/plugins/content/ToC/__init__.py
--------------------------------------------------------------------------------
/barely/plugins/content/ToC/test_toc.py:
--------------------------------------------------------------------------------
1 | import re
2 | import unittest
3 | from barely.plugins.content.ToC.toc import ToC
4 |
5 |
6 | class TestToC(unittest.TestCase):
7 |
8 | maxDiff = None
9 |
10 | def test___init__(self):
11 | golden = {
12 | "PRIORITY": 2,
13 | "MIN_DEPTH": 1,
14 | "MAX_DEPTH": 4,
15 | "LIST_ELEMENT": "ul"
16 | }
17 |
18 | toc = ToC()
19 | self.assertDictEqual(golden, toc.plugin_config)
20 |
21 | toc.config["TOC"] = {"PRIORITY": 12}
22 | toc.__init__()
23 | golden["PRIORITY"] = 12
24 |
25 | self.assertDictEqual(golden, toc.plugin_config)
26 |
27 | # reset
28 | del toc.config["TOC"]
29 | toc.__init__()
30 |
31 | def test_register(self):
32 | toc = ToC()
33 | name, prio, ext = toc.register()
34 |
35 | self.assertEqual(name, "ToC")
36 | self.assertEqual(prio, 2)
37 | self.assertEqual(ext, ["md"])
38 |
39 | def test_action(self):
40 | item = {
41 | "content": """
42 |
43 |
44 |
45 |
46 | A 2
47 |
48 | B 2
49 |
50 | C 3
51 |
52 | D 2
53 |
54 | E 3
55 |
56 | F 4
57 |
58 | G 4
59 |
60 | H 3
61 |
62 |
63 |
64 | """,
65 | "meta": {}
66 | }
67 |
68 | golden = {
69 | "content": """
70 |
71 |
72 |
73 |
74 | A 2
75 |
76 | B 2
77 |
78 | C 3
79 |
80 | D 2
81 |
82 | E 3
83 |
84 | F 4
85 |
86 | G 4
87 |
88 | H 3
89 |
90 |
91 |
92 | """,
93 | "meta": {
94 | "toc": """
95 |
96 |
119 |
120 | """
121 | }
122 | }
123 |
124 | toc = ToC()
125 | result = list(toc.action(item=item))
126 | self.assertEqual(1, len(result))
127 |
128 | self.assertEqual(item["content"], golden["content"])
129 | self.assertEqual(re.sub(r'[\s+]', '', item["meta"]["toc"]), re.sub(r'[\s+]', '', golden["meta"]["toc"]))
130 |
--------------------------------------------------------------------------------
/barely/plugins/content/ToC/toc.py:
--------------------------------------------------------------------------------
1 | """
2 | build a Table of Contents
3 | from the heading, up to a
4 | specified depth. insert IDs
5 | into the tags to be able
6 | to link to them
7 | """
8 | import re
9 | from barely.plugins import PluginBase
10 |
11 |
12 | class ToC(PluginBase):
13 | # build a linked table of contents out of the html headings
14 |
15 | TOC = []
16 |
17 | def __init__(self):
18 | super().__init__()
19 | standard_config = {
20 | "PRIORITY": 2,
21 | "MIN_DEPTH": 1,
22 | "MAX_DEPTH": 4,
23 | "LIST_ELEMENT": "ul"
24 | }
25 | try:
26 | self.plugin_config = standard_config | self.config["TOC"]
27 | except KeyError:
28 | self.plugin_config = standard_config
29 |
30 | def register(self):
31 | return "ToC", self.plugin_config["PRIORITY"], [self.config["PAGE_EXT"]]
32 |
33 | def action(self, *args, **kwargs):
34 | if "item" in kwargs:
35 | self.TOC = []
36 | item = kwargs["item"]
37 | # should the generation be attempted a second time, the result will be an empty ToC!
38 | if "toc" not in item["meta"] and "parent_meta" not in item["meta"]:
39 | item["content"] = re.sub(r"(.+)<\/h\d{1}>", self._handle_matches, item["content"])
40 | item["meta"]["toc"] = "\n".join(self._generate_toc())
41 | yield item
42 |
43 | def _handle_matches(self, match):
44 | level = match.group(1)
45 | heading = match.group(2)
46 | slug = re.sub(r"[^0-9a-zA-Z]+", "-", heading).lower()
47 | if int(level) >= self.plugin_config["MIN_DEPTH"] and int(level) <= self.plugin_config["MAX_DEPTH"]:
48 | self.TOC.append((int(level), heading, slug))
49 | return f'{heading} '
50 | else:
51 | return match.group(0)
52 |
53 | def _generate_toc(self):
54 | cur_lvl = self.plugin_config["MIN_DEPTH"] - 1
55 |
56 | yield ''
57 | for index, (level, heading, slug) in enumerate(self.TOC):
58 |
59 | opened_layer = False
60 | while level > cur_lvl:
61 | if opened_layer:
62 | yield ""
63 | yield f"<{self.plugin_config['LIST_ELEMENT']}>"
64 | opened_layer = True
65 | cur_lvl += 1
66 |
67 | while level < cur_lvl:
68 | yield f"{self.plugin_config['LIST_ELEMENT']}> "
69 | cur_lvl -= 1
70 |
71 | yield ""
72 | yield f'{heading}'
73 | if len(self.TOC) - 1 == index or self.TOC[index + 1][0] <= cur_lvl:
74 | yield " "
75 |
76 | while self.plugin_config["MIN_DEPTH"] <= cur_lvl:
77 | yield f"{self.plugin_config['LIST_ELEMENT']}>"
78 | if cur_lvl != self.plugin_config["MIN_DEPTH"]:
79 | yield ""
80 | cur_lvl -= 1
81 | yield ""
82 |
--------------------------------------------------------------------------------
/barely/plugins/content/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/plugins/content/__init__.py
--------------------------------------------------------------------------------
/barely/plugins/publication/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/plugins/publication/__init__.py
--------------------------------------------------------------------------------
/barely/plugins/publication/sftp/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/plugins/publication/sftp/__init__.py
--------------------------------------------------------------------------------
/barely/plugins/publication/sftp/sftp.py:
--------------------------------------------------------------------------------
1 | """
2 | transfer only the changed files
3 | (save bandwidth) to an sftp server
4 | """
5 | import os
6 | import pysftp
7 | from barely.plugins import PluginBase
8 |
9 |
10 | class SFTP(PluginBase):
11 | """ copy to remote server; only new/changed though """
12 |
13 | def __init__(self):
14 | super().__init__()
15 | try:
16 | standard_config = {
17 | "PRIORITY": 90,
18 | "HOSTNAME": "",
19 | "USER": "",
20 | "PASSWORD": "",
21 | "KEY": "",
22 | "ROOT": ""
23 | }
24 | self.plugin_config = standard_config | self.config["SFTP"]
25 | except KeyError:
26 | self.plugin_config = {"PRIORITY": -1}
27 |
28 | def register(self):
29 | return "sftp", self.plugin_config["PRIORITY"]
30 |
31 | def action(self, *args, **kwargs):
32 | try:
33 | if self.plugin_config["KEY"] != "":
34 | conn = pysftp.Connection(self.plugin_config["HOSTNAME"],
35 | username=self.plugin_config["USER"],
36 | private_key=self.plugin_config["KEY"],
37 | private_key_pass=True)
38 | else:
39 | conn = pysftp.Connection(self.plugin_config["HOSTNAME"],
40 | username=self.plugin_config["USER"],
41 | password=self.plugin_config["PASSWORD"])
42 |
43 | conn.put_r(os.path.join(self.config["ROOT"]["WEB"], ""), self.plugin_config["ROOT"], preserve_mtime=True)
44 | conn.close()
45 | self.logger.info(f"published via SFTP to {self.plugin_config['HOSTNAME']}")
46 | except KeyError:
47 | self.logger.error("SFTP configuration is incomplete or invalid.")
48 |
--------------------------------------------------------------------------------
/barely/plugins/publication/sftp/test_sftp.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | from mock import patch
3 | from barely.plugins.publication.sftp.sftp import SFTP
4 |
5 |
6 | class TestSFTP(unittest.TestCase):
7 |
8 | def test___init__(self):
9 | sftp = SFTP()
10 | self.assertDictEqual({"PRIORITY": -1}, sftp.plugin_config)
11 |
12 | sftp.config["SFTP"] = {"PRIORITY": 2}
13 | sftp.__init__()
14 |
15 | golden = {
16 | "PRIORITY": 2,
17 | "HOSTNAME": "",
18 | "USER": "",
19 | "PASSWORD": "",
20 | "KEY": "",
21 | "ROOT": ""
22 | }
23 |
24 | self.assertDictEqual(golden, sftp.plugin_config)
25 |
26 | # reset
27 | del sftp.config["SFTP"]
28 | sftp.__init__()
29 |
30 | def test_register(self):
31 | sftp = SFTP()
32 | name, prio = sftp.register()
33 |
34 | self.assertEqual(name, "sftp")
35 | self.assertEqual(prio, -1)
36 |
37 | @patch("barely.plugins.publication.sftp.sftp.pysftp.Connection")
38 | def test_action(self, Connection):
39 | sftp = SFTP()
40 | sftp.plugin_config = {
41 | "PRIORITY": 2,
42 | "HOSTNAME": "",
43 | "USER": "",
44 | "PASSWORD": "",
45 | "KEY": "",
46 | "ROOT": ""
47 | }
48 | sftp.action()
49 | Connection.assert_called_with("", username="", password="")
50 |
51 | Connection.reset_mock()
52 |
53 | sftp.plugin_config = {
54 | "PRIORITY": 2,
55 | "HOSTNAME": "",
56 | "USER": "",
57 | "PASSWORD": "",
58 | "KEY": "key",
59 | "ROOT": ""
60 | }
61 | sftp.action()
62 | Connection.assert_called_with("", username="", private_key="key", private_key_pass=True)
63 |
--------------------------------------------------------------------------------
/barely/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/__init__.py
--------------------------------------------------------------------------------
/barely/tests/ressources/EventHandler/affected/base.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/EventHandler/affected/base.md
--------------------------------------------------------------------------------
/barely/tests/ressources/EventHandler/affected/extendsdeeper.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/EventHandler/affected/extendsdeeper.md
--------------------------------------------------------------------------------
/barely/tests/ressources/EventHandler/affected/left.extendsbase.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/EventHandler/affected/left.extendsbase.md
--------------------------------------------------------------------------------
/barely/tests/ressources/EventHandler/affected/left.left.extendsbase.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/EventHandler/affected/left.left.extendsbase.md
--------------------------------------------------------------------------------
/barely/tests/ressources/EventHandler/affected/left.left.extendschild.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/EventHandler/affected/left.left.extendschild.md
--------------------------------------------------------------------------------
/barely/tests/ressources/EventHandler/affected/left.parentless.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/EventHandler/affected/left.parentless.md
--------------------------------------------------------------------------------
/barely/tests/ressources/EventHandler/affected/left.right.completelyalone.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/EventHandler/affected/left.right.completelyalone.md
--------------------------------------------------------------------------------
/barely/tests/ressources/EventHandler/affected/right.left.completelyalone.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/EventHandler/affected/right.left.completelyalone.md
--------------------------------------------------------------------------------
/barely/tests/ressources/EventHandler/affected/right.left.deeper.md:
--------------------------------------------------------------------------------
1 | include: "test.jpg"
2 |
--------------------------------------------------------------------------------
/barely/tests/ressources/EventHandler/affected/right.right.extendsparentless.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/EventHandler/affected/right.right.extendsparentless.md
--------------------------------------------------------------------------------
/barely/tests/ressources/EventHandler/affected/templates/base.html:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/EventHandler/affected/templates/base.html
--------------------------------------------------------------------------------
/barely/tests/ressources/EventHandler/affected/templates/extendsdeeper.html:
--------------------------------------------------------------------------------
1 | {%extends 'right/left/deeper.html'%}
2 |
3 |
--------------------------------------------------------------------------------
/barely/tests/ressources/EventHandler/affected/templates/left/extendsbase.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 | {% include "left/parentless.html" %}
3 |
--------------------------------------------------------------------------------
/barely/tests/ressources/EventHandler/affected/templates/left/left/extendsbase.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
--------------------------------------------------------------------------------
/barely/tests/ressources/EventHandler/affected/templates/left/left/extendschild.html:
--------------------------------------------------------------------------------
1 | {% extends "left/extendsbase.html" %}
2 |
--------------------------------------------------------------------------------
/barely/tests/ressources/EventHandler/affected/templates/left/parentless.html:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/EventHandler/affected/templates/left/parentless.html
--------------------------------------------------------------------------------
/barely/tests/ressources/EventHandler/affected/templates/left/right/completelyalone.html:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/EventHandler/affected/templates/left/right/completelyalone.html
--------------------------------------------------------------------------------
/barely/tests/ressources/EventHandler/affected/templates/right/left/completelyalone.html:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/EventHandler/affected/templates/right/left/completelyalone.html
--------------------------------------------------------------------------------
/barely/tests/ressources/EventHandler/affected/templates/right/left/deeper.html:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/EventHandler/affected/templates/right/left/deeper.html
--------------------------------------------------------------------------------
/barely/tests/ressources/EventHandler/affected/templates/right/right/extendsparentless.html:
--------------------------------------------------------------------------------
1 | {% include "left/parentless.html" %}
2 |
--------------------------------------------------------------------------------
/barely/tests/ressources/EventHandler/force/config.yaml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/EventHandler/force/config.yaml
--------------------------------------------------------------------------------
/barely/tests/ressources/EventHandler/force/dir/_subpage/subimage.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/EventHandler/force/dir/_subpage/subimage.jpg
--------------------------------------------------------------------------------
/barely/tests/ressources/EventHandler/force/dir/_subpage/subpage.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/EventHandler/force/dir/_subpage/subpage.md
--------------------------------------------------------------------------------
/barely/tests/ressources/EventHandler/force/dir/image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/EventHandler/force/dir/image.png
--------------------------------------------------------------------------------
/barely/tests/ressources/EventHandler/force/dir/page.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/EventHandler/force/dir/page.md
--------------------------------------------------------------------------------
/barely/tests/ressources/EventHandler/force/file.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/EventHandler/force/file.txt
--------------------------------------------------------------------------------
/barely/tests/ressources/EventHandler/force/metadata.yaml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/EventHandler/force/metadata.yaml
--------------------------------------------------------------------------------
/barely/tests/ressources/EventHandler/force/templates/template.html:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/EventHandler/force/templates/template.html
--------------------------------------------------------------------------------
/barely/tests/ressources/EventHandler/getparent/_sub/child.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/EventHandler/getparent/_sub/child.md
--------------------------------------------------------------------------------
/barely/tests/ressources/EventHandler/getparent/none/none/parentless.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/EventHandler/getparent/none/none/parentless.md
--------------------------------------------------------------------------------
/barely/tests/ressources/EventHandler/getparent/parent.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/EventHandler/getparent/parent.md
--------------------------------------------------------------------------------
/barely/tests/ressources/EventHandler/getparent/x_parent.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/EventHandler/getparent/x_parent.md
--------------------------------------------------------------------------------
/barely/tests/ressources/EventHandler/type/binary.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/EventHandler/type/binary.mp4
--------------------------------------------------------------------------------
/barely/tests/ressources/EventHandler/type/file.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/EventHandler/type/file.css
--------------------------------------------------------------------------------
/barely/tests/ressources/EventHandler/type/notype:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/EventHandler/type/notype
--------------------------------------------------------------------------------
/barely/tests/ressources/PluginManager/content/P1.py:
--------------------------------------------------------------------------------
1 | from barely.plugins import PluginBase
2 |
3 |
4 | class POne(PluginBase):
5 | def register(self):
6 | return "P1", 3, ["pdf", "png"]
7 |
8 | def action(self, item):
9 | yield item
10 |
--------------------------------------------------------------------------------
/barely/tests/ressources/PluginManager/content/P2.py:
--------------------------------------------------------------------------------
1 | from barely.plugins import PluginBase
2 |
3 |
4 | class PTwo(PluginBase):
5 | def register(self):
6 | return "P2", 1, ["md", "png"]
7 |
8 | def action(self, item):
9 | yield item
10 |
--------------------------------------------------------------------------------
/barely/tests/ressources/PluginManager/empty/collateral:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/PluginManager/empty/collateral
--------------------------------------------------------------------------------
/barely/tests/ressources/PluginManager/other/P3Dir/P3.py:
--------------------------------------------------------------------------------
1 | from barely.plugins import PluginBase
2 |
3 |
4 | class PThree(PluginBase):
5 | def register(self):
6 | return "P3", 3
7 |
8 | def action(self, item):
9 | yield item
10 |
--------------------------------------------------------------------------------
/barely/tests/ressources/PluginManager/other/P4.py:
--------------------------------------------------------------------------------
1 | from barely.plugins import PluginBase
2 |
3 |
4 | class PFour(PluginBase):
5 | def register(self):
6 | return "P4", 1
7 |
8 | def action(self, item):
9 | yield item
10 |
--------------------------------------------------------------------------------
/barely/tests/ressources/Plugins/AutoSEO/a.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/Plugins/AutoSEO/a.png
--------------------------------------------------------------------------------
/barely/tests/ressources/Plugins/AutoSEO/b.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/Plugins/AutoSEO/b.jpg
--------------------------------------------------------------------------------
/barely/tests/ressources/Plugins/Gallery/a.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/Plugins/Gallery/a.png
--------------------------------------------------------------------------------
/barely/tests/ressources/Plugins/Gallery/b.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/Plugins/Gallery/b.jpg
--------------------------------------------------------------------------------
/barely/tests/ressources/Plugins/Gallery/c.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/Plugins/Gallery/c.png
--------------------------------------------------------------------------------
/barely/tests/ressources/ProcessingPipeline/content_files/EMPTY.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/ProcessingPipeline/content_files/EMPTY.md
--------------------------------------------------------------------------------
/barely/tests/ressources/ProcessingPipeline/content_files/MULTI_YAML.md:
--------------------------------------------------------------------------------
1 | ---
2 | value: a
3 | ---
4 | value2: b
5 | ---
6 | # Title
7 |
--------------------------------------------------------------------------------
/barely/tests/ressources/ProcessingPipeline/content_files/ONE_YAML_ONE_MD.md:
--------------------------------------------------------------------------------
1 | ---
2 | value: a
3 | ---
4 | # Title
5 |
--------------------------------------------------------------------------------
/barely/tests/ressources/ProcessingPipeline/content_files/ONLY_MD.md:
--------------------------------------------------------------------------------
1 | # Title
2 |
--------------------------------------------------------------------------------
/barely/tests/ressources/ProcessingPipeline/content_files/ONLY_YAML.md:
--------------------------------------------------------------------------------
1 | ---
2 | value: a
3 | ---
4 |
--------------------------------------------------------------------------------
/barely/tests/ressources/ProcessingPipeline/delete/dir/collateral:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/ProcessingPipeline/delete/dir/collateral
--------------------------------------------------------------------------------
/barely/tests/ressources/ProcessingPipeline/delete/file.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/ProcessingPipeline/delete/file.txt
--------------------------------------------------------------------------------
/barely/tests/ressources/ProcessingPipeline/move/fro.txt:
--------------------------------------------------------------------------------
1 | 1
2 |
--------------------------------------------------------------------------------
/barely/tests/ressources/ProcessingPipeline/move/fro/collateral:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/ProcessingPipeline/move/fro/collateral
--------------------------------------------------------------------------------
/barely/tests/ressources/ProcessingPipeline/move/fro2.txt:
--------------------------------------------------------------------------------
1 | 2
2 |
--------------------------------------------------------------------------------
/barely/tests/ressources/ProcessingPipeline/move/fro2/collateral:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/ProcessingPipeline/move/fro2/collateral
--------------------------------------------------------------------------------
/barely/tests/ressources/ProcessingPipeline/pipes/_subpage/subpage_dev.md:
--------------------------------------------------------------------------------
1 | Child
2 |
--------------------------------------------------------------------------------
/barely/tests/ressources/ProcessingPipeline/pipes/generic_dev.txt:
--------------------------------------------------------------------------------
1 | multi
2 | line
3 |
--------------------------------------------------------------------------------
/barely/tests/ressources/ProcessingPipeline/pipes/image_dev.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/ProcessingPipeline/pipes/image_dev.png
--------------------------------------------------------------------------------
/barely/tests/ressources/ProcessingPipeline/pipes/page_dev.md:
--------------------------------------------------------------------------------
1 | ---
2 | value: a
3 | ---
4 | # Title
5 |
--------------------------------------------------------------------------------
/barely/tests/ressources/ProcessingPipeline/pipes/page_with_child_dev.md:
--------------------------------------------------------------------------------
1 | ---
2 | modular:
3 | - subpage
4 | parent_meta: pm
5 | ---
6 | # Title Parent
7 |
--------------------------------------------------------------------------------
/barely/tests/ressources/ProcessingPipeline/pipes/subpage_dev.md:
--------------------------------------------------------------------------------
1 | ---
2 | value: a
3 | ---
4 | # Title
5 |
--------------------------------------------------------------------------------
/barely/tests/ressources/ProcessingPipeline/pipes/text_dev.txt:
--------------------------------------------------------------------------------
1 | multi
2 | line
3 |
--------------------------------------------------------------------------------
/barely/tests/ressources/ProcessingPipeline/subpages/_subpage/sub.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/ProcessingPipeline/subpages/_subpage/sub.md
--------------------------------------------------------------------------------
/barely/tests/ressources/ProcessingPipeline/template.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/ProcessingPipeline/template.md
--------------------------------------------------------------------------------
/barely/tests/ressources/ProcessingPipeline/template.subtemplate.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/ProcessingPipeline/template.subtemplate.md
--------------------------------------------------------------------------------
/barely/tests/ressources/ProcessingPipeline/templates/page_dev.html:
--------------------------------------------------------------------------------
1 | {{ value }}
2 | {{ content }}
3 |
--------------------------------------------------------------------------------
/barely/tests/ressources/ProcessingPipeline/templates/page_with_child_dev.html:
--------------------------------------------------------------------------------
1 | {{ content }}
2 | {{ sub_pages[0] }}
3 |
--------------------------------------------------------------------------------
/barely/tests/ressources/ProcessingPipeline/templates/subpage_dev.html:
--------------------------------------------------------------------------------
1 | {{ value }}
2 | {{ content }}
3 | {{ parent_meta }}
4 |
--------------------------------------------------------------------------------
/barely/tests/ressources/ProcessingPipeline/templates/template.html:
--------------------------------------------------------------------------------
1 | {{ content }}
2 |
--------------------------------------------------------------------------------
/barely/tests/ressources/ProcessingPipeline/test_load.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/ProcessingPipeline/test_load.png
--------------------------------------------------------------------------------
/barely/tests/ressources/ProcessingPipeline/test_read.md:
--------------------------------------------------------------------------------
1 | multi
2 | line
3 |
--------------------------------------------------------------------------------
/barely/tests/ressources/ProcessingPipeline/test_save.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/ProcessingPipeline/test_save.png
--------------------------------------------------------------------------------
/barely/tests/ressources/config.yaml:
--------------------------------------------------------------------------------
1 | ROOT:
2 | DEV: .
3 | WEB: web
4 | PAGE_EXT: md
5 |
--------------------------------------------------------------------------------
/barely/tests/ressources/empty/empty:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/barely/tests/ressources/empty/empty
--------------------------------------------------------------------------------
/barely/tests/test_ChangeTracker.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | from mock import patch
3 | from unittest.mock import MagicMock
4 | from watchdog.observers import Observer
5 | from watchdog.events import PatternMatchingEventHandler
6 | from barely.core.ChangeTracker import ChangeTracker
7 |
8 |
9 | class TestChangeTracker(unittest.TestCase):
10 |
11 | @classmethod
12 | def setUpClass(self):
13 | with patch.object(ChangeTracker, "__init__", lambda x: None):
14 | self.CT = ChangeTracker()
15 |
16 | def test___init__(self):
17 | EH_no = ChangeTracker()
18 | self.assertFalse(EH_no.handler_available)
19 |
20 | with patch("barely.core.ChangeTracker.ChangeTracker.register_handler") as reg:
21 | ChangeTracker(lambda x: x)
22 | self.assertTrue(reg.called)
23 |
24 | def test_register_handler(self):
25 | with patch.object(PatternMatchingEventHandler, "__init__", lambda v, w, x, y, z: None):
26 | with patch.object(Observer, "schedule") as obs:
27 | self.CT.register_handler(lambda x: None)
28 | self.assertTrue(obs.called)
29 |
30 | @patch("signal.getsignal")
31 | @patch("signal.signal")
32 | @patch("livereload.Server")
33 | @patch("multiprocessing.Process")
34 | @patch("watchdog.observers.Observer")
35 | def test_track(self, observer, server, serverprocess, signal, getsignal):
36 | self.CT.handler_available = False
37 | with self.assertRaises(Exception) as context:
38 | self.CT.track()
39 | self.assertTrue("No available handler. Not tracking." in str(context.exception))
40 |
41 | self.CT.verbose = False
42 | self.CT.handler_available = True
43 | self.CT.observer = observer
44 | self.CT.observer.start = MagicMock()
45 | self.CT.liveserver.start = MagicMock()
46 | serverprocess.start = MagicMock()
47 | server.serve = MagicMock()
48 | server.watch = MagicMock()
49 | getsignal.return_value = None
50 |
51 | def loop_action():
52 | self.CT.tracking = False
53 | with patch("barely.core.ChangeTracker.ChangeTracker.empty_buffer"):
54 | self.CT.track(loop_action)
55 | self.CT.liveserver.kill()
56 | self.assertTrue(self.CT.observer.start.called)
57 | self.assertTrue(signal.called)
58 | self.assertTrue(getsignal.called)
59 | self.assertTrue(server.watch)
60 |
61 | def test_buffer(self):
62 | self.CT.eventbuffer = []
63 |
64 | event_1a = type('obj', (object,), {"src_path": 1})
65 | event_1b = type('obj', (object,), {"dest_path": 1, "src_path": 123})
66 | event_2 = type('obj', (object,), {"src_path": 2})
67 |
68 | self.CT.buffer(event_1a)
69 | self.CT.buffer(event_2)
70 | self.CT.buffer(event_1b)
71 |
72 | self.assertEqual(2, len(self.CT.eventbuffer))
73 | self.assertEqual(2, self.CT.eventbuffer[0].src_path)
74 | self.assertEqual(2, self.CT.eventbuffer[0].relevant_path)
75 | self.assertEqual(1, self.CT.eventbuffer[1].dest_path)
76 | self.assertEqual(1, self.CT.eventbuffer[1].relevant_path)
77 |
78 | @patch("barely.core.EventHandler.EventHandler")
79 | def test_empty_buffer(self, EH):
80 | def enbuffer(item):
81 | emptied_buffer.append(item)
82 | emptied_buffer = []
83 | original_buffer = [1, 2, 3]
84 |
85 | self.CT.EH = EH
86 | self.CT.EH.notify = MagicMock(side_effect=enbuffer)
87 | self.CT.eventbuffer = original_buffer.copy()
88 |
89 | self.CT.empty_buffer()
90 |
91 | self.assertListEqual(original_buffer, emptied_buffer)
92 | self.assertListEqual([], self.CT.eventbuffer)
93 |
94 | @patch("signal.signal")
95 | @patch("multiprocessing.Process")
96 | @patch("watchdog.observers.Observer")
97 | def test_stop(self, observer, liveserver, signal):
98 | self.CT.observer = observer
99 | self.CT.observer.stop = MagicMock()
100 | self.CT.observer.join = MagicMock()
101 | self.CT.liveserver = liveserver
102 | self.CT.liveserver.join = MagicMock()
103 | self.CT.tracking = True
104 | self.CT.original_sigint = None
105 |
106 | self.CT.stop(None, None)
107 |
108 | self.assertTrue(self.CT.observer.stop.called)
109 | self.assertTrue(self.CT.observer.join.called)
110 | self.assertTrue(self.CT.liveserver.join.called)
111 | self.assertTrue(signal.called)
112 | self.assertFalse(self.CT.tracking)
113 |
--------------------------------------------------------------------------------
/barely/tests/test_EventHandler.py:
--------------------------------------------------------------------------------
1 | import os
2 | import unittest
3 | from mock import patch
4 | from barely.common.config import config
5 | from barely.core.EventHandler import EventHandler
6 | from watchdog.events import FileCreatedEvent, FileModifiedEvent
7 | from watchdog.events import FileDeletedEvent, DirModifiedEvent
8 | from watchdog.events import FileMovedEvent, DirMovedEvent
9 |
10 |
11 | class TestEventHandler(unittest.TestCase):
12 |
13 | @classmethod
14 | def setUpClass(self):
15 | os.chdir("EventHandler")
16 | self.EH = EventHandler()
17 |
18 | @classmethod
19 | def tearDownClass(self):
20 | os.chdir("..")
21 |
22 | @patch("barely.core.ProcessingPipeline.init_jinja")
23 | def test_init_pipeline(self, jinja):
24 | self.EH.init_pipeline(1)
25 | self.assertTrue(jinja.called)
26 |
27 | @patch("barely.core.ProcessingPipeline.process")
28 | @patch("barely.core.EventHandler.EventHandler._determine_type")
29 | @patch("barely.core.ProcessingPipeline.move")
30 | @patch("barely.core.ProcessingPipeline.delete")
31 | @patch("barely.core.EventHandler.EventHandler._get_parent_page")
32 | @patch("barely.core.EventHandler.EventHandler._get_affected")
33 | @patch("barely.core.EventHandler.EventHandler._get_web_path")
34 | def test_notify(self, _get_web_path, _get_affected, _get_parent_page, delete, move, _determine_type, process):
35 | def join(*args):
36 | return os.path.join(*args)
37 |
38 | def catch_response(response):
39 | response_items.append(response[0])
40 | response_items = []
41 |
42 | # a bit counterintuitive, but easier to test:
43 | # only the origin ever changes with these mocks
44 | _get_web_path.return_value = "destination"
45 | _get_affected.side_effect = lambda x: [x[10:]]
46 | _get_parent_page.return_value = "parent"
47 | delete.return_value = None
48 | move.return_value = None
49 | _determine_type.return_value = ("TYPE", "ext")
50 | process.side_effect = catch_response
51 |
52 | golden_item = {
53 | "origin": "origin",
54 | "destination": "destination",
55 | "type": "TYPE",
56 | "extension": "ext"
57 | }
58 |
59 | # template modified
60 | golden_item["origin"] = "tpl"
61 | self.EH.notify(FileModifiedEvent(src_path=join("templates", "tpl")))
62 | self.assertDictEqual(golden_item, response_items[-1])
63 |
64 | # template moved
65 | golden_item["origin"] = "dest"
66 | self.EH.notify(FileMovedEvent(src_path=join("templates", "origin"), dest_path=join("templates", "dest")))
67 | self.assertDictEqual(golden_item, response_items[-1])
68 |
69 | # metadata.yaml
70 | golden_item["origin"] = ""
71 | self.EH.notify(FileModifiedEvent(src_path="metadata.yaml"))
72 | self.assertDictEqual(golden_item, response_items[-1])
73 |
74 | # subpage
75 | golden_item["origin"] = "parent"
76 | self.EH.notify(FileModifiedEvent(src_path=join("", "_subpage", "origin.md")))
77 | self.assertDictEqual(golden_item, response_items[-1])
78 |
79 | # deleted
80 | self.EH.notify(FileDeletedEvent(src_path="trash"))
81 | self.assertTrue(delete.called)
82 |
83 | # moved
84 | _get_web_path.side_effect = lambda x: x
85 | self.EH.notify(DirMovedEvent(src_path="from", dest_path="to"))
86 | self.assertTrue(move.called)
87 | _get_web_path.side_effect = None
88 |
89 | # created file
90 | golden_item["origin"] = "file.png"
91 | self.EH.notify(FileCreatedEvent(src_path="file.png"))
92 | self.assertDictEqual(golden_item, response_items[-1])
93 |
94 | # config.yaml, .git (other/pointless)
95 | self.EH.notify(FileModifiedEvent(src_path="config.yaml"))
96 | self.EH.notify(FileModifiedEvent(src_path=".git"))
97 |
98 | # DirModifiedEvent (other/pointless)
99 | self.EH.notify(DirModifiedEvent(src_path="dir"))
100 |
101 | # number of unique responses we should have received
102 | self.assertEqual(5, len(list({r["origin"]: r for r in response_items}.values())))
103 |
104 | @patch("barely.core.EventHandler.EventHandler.notify")
105 | def test_force_rebuild(self, notify):
106 | notifications = []
107 |
108 | def collect_notifications(notification):
109 | notifications.append(notification.src_path)
110 |
111 | os.chdir("force")
112 |
113 | notify.side_effect = collect_notifications
114 | self.EH.force_rebuild("devroot")
115 |
116 | golden_notified = {
117 | os.path.join(".", "dir", "_subpage", "subimage.jpg"),
118 | os.path.join(".", "dir", "page.md"),
119 | os.path.join(".", "dir", "image.png"),
120 | os.path.join(".", "file.txt")
121 | }
122 | self.assertSetEqual(golden_notified, set(notifications))
123 |
124 | notifications = []
125 | self.EH.force_rebuild("dir")
126 | golden_notified = {
127 | os.path.join(".", "dir", "_subpage", "subimage.jpg"),
128 | os.path.join(".", "dir", "page.md"),
129 | os.path.join(".", "dir", "image.png"),
130 | }
131 | self.assertSetEqual(golden_notified, set(notifications))
132 |
133 | self.EH.force_rebuild(os.path.join(".", "dir"))
134 | self.assertSetEqual(golden_notified, set(notifications))
135 |
136 | notifications = []
137 | self.EH.force_rebuild("file.txt")
138 | golden_notified = {
139 | os.path.join(".", "file.txt")
140 | }
141 | self.assertSetEqual(golden_notified, set(notifications))
142 |
143 | notifications = []
144 | self.EH.force_rebuild("devroot", True)
145 | golden_notified = {
146 | os.path.join(".", "dir", "page.md"),
147 | os.path.join(".", "file.txt")
148 | }
149 | self.assertSetEqual(golden_notified, set(notifications))
150 |
151 | os.chdir("..")
152 |
153 | @patch.dict(config, {"ROOT": {"DEV": "." + os.sep, "WEB": ""}, "PAGE_EXT": "md"})
154 | @patch("barely.core.EventHandler.EventHandler._find_children")
155 | def test__get_affected(self, _f_c):
156 | def join(*args):
157 | return os.path.join("templates", *args)
158 |
159 | base = join("base.html")
160 | l_r_completelyalone = join("left", "right", "completelyalone.html")
161 | l_l = join("left", "left")
162 |
163 | os.chdir("affected")
164 | _f_c.return_value = []
165 | self.assertSetEqual({"base.md"}, set(self.EH._get_affected(base)))
166 | self.assertSetEqual({"left.right.completelyalone.md"}, set(self.EH._get_affected(l_r_completelyalone)))
167 | self.assertSetEqual({"left.left.extendsbase.md", "left.left.extendschild.md"}, set(self.EH._get_affected(l_l)))
168 |
169 | os.chdir("..")
170 |
171 | def test__find_children(self):
172 | def join(*args):
173 | return os.path.join("templates", *args) + ".html"
174 |
175 | base = join("base")
176 | extendsdeeper = join("extendsdeeper")
177 | l_extendsbase = join("left", "extendsbase")
178 | l_parentless = join("left", "parentless")
179 | l_l_extendsbase = join("left", "left", "extendsbase")
180 | l_l_extendschild = join("left", "left", "extendschild")
181 | r_l_deeper = join("right", "left", "deeper")
182 | r_r_extendsparentless = join("right", "right", "extendsparentless")
183 |
184 | os.chdir("affected")
185 |
186 | golden_base = {l_extendsbase, l_l_extendsbase, l_l_extendschild}
187 | self.assertSetEqual(golden_base, set(self.EH._find_children(base)))
188 |
189 | golden_parentless = {l_extendsbase, r_r_extendsparentless, l_l_extendschild}
190 | self.assertSetEqual(golden_parentless, set(self.EH._find_children(l_parentless)))
191 |
192 | golden_childless = set()
193 | self.assertSetEqual(golden_childless, set(self.EH._find_children(extendsdeeper)))
194 |
195 | golden_reverse = {extendsdeeper}
196 | self.assertSetEqual(golden_reverse, set(self.EH._find_children(r_l_deeper)))
197 |
198 | golden_once = {l_l_extendschild}
199 | self.assertSetEqual(golden_once, set(self.EH._find_children(l_extendsbase)))
200 |
201 | os.chdir("..")
202 |
203 | def test__determine_type(self):
204 | os.chdir("type")
205 | self.assertTupleEqual(("PAGE", "md"), self.EH._determine_type("file.md"))
206 | self.assertTupleEqual(("IMAGE", "png"), self.EH._determine_type("file.png"))
207 | self.assertTupleEqual(("TEXT", "css"), self.EH._determine_type("file.css"))
208 | self.assertTupleEqual(("GENERIC", "mp4"), self.EH._determine_type("binary.mp4"))
209 | self.assertTupleEqual(("GENERIC", "NOTYPE"), self.EH._determine_type("notype"))
210 | os.chdir("..")
211 |
212 | @patch.dict(config, {"PAGE_EXT": "md", "ROOT": {"WEB": "web", "DEV": "dev"}})
213 | def test__get_web_path(self):
214 | devroot = config["ROOT"]["DEV"]
215 | webroot = config["ROOT"]["WEB"]
216 |
217 | def join(*args):
218 | return os.path.join(*args)
219 |
220 | def get(file):
221 | return self.EH._get_web_path(join(devroot, file))
222 |
223 | self.assertEqual(join(webroot, "index.html"), get("test.md"))
224 | self.assertEqual(join(webroot, "generic.txt"), get("generic.txt"))
225 | self.assertEqual(join(webroot, "noext"), get("noext"))
226 |
227 | def test__get_parent_page(self):
228 | os.chdir("getparent")
229 | parent = self.EH._get_parent_page(os.path.join("_sub", "child.md"))
230 | self.assertEqual("parent.md", parent)
231 |
232 | parent = self.EH._get_parent_page("_sub")
233 | self.assertEqual("parent.md", parent)
234 |
235 | with self.assertRaises(IndexError) as context:
236 | self.EH._get_parent_page(os.path.join("none", "none", "parentless.md"))
237 | self.assertTrue("Child page has no parent!" in str(context.exception))
238 | os.chdir("..")
239 |
--------------------------------------------------------------------------------
/barely/tests/test_PluginManager.py:
--------------------------------------------------------------------------------
1 | import os
2 | import unittest
3 | from mock import patch
4 | from unittest.mock import MagicMock
5 | from barely.plugins import PluginBase
6 | from barely.common.config import config
7 | from barely.plugins.PluginManager import PluginManager
8 |
9 |
10 | class SamplePlugin(PluginBase):
11 | def action(self, item):
12 | yield item
13 |
14 | def register(self):
15 | return "Base", -1, []
16 |
17 |
18 | class TestPluginBase(unittest.TestCase):
19 |
20 | def test___init__(self):
21 | mock_config = {}
22 | with patch.dict(config, mock_config):
23 | SP = SamplePlugin()
24 | self.assertIsNotNone(SP.config)
25 |
26 | def test_register(self):
27 | plugin = SamplePlugin()
28 | registration_info = plugin.register()
29 | self.assertTupleEqual(("Base", -1, []), registration_info)
30 |
31 | def test_action(self):
32 | plugin = SamplePlugin()
33 | golden_item = {
34 | "test": "dict"
35 | }
36 |
37 | test_item = plugin.action(item=golden_item)
38 |
39 | self.assertDictEqual(golden_item, list(test_item)[0])
40 |
41 |
42 | class TestPluginManager(unittest.TestCase):
43 |
44 | @classmethod
45 | def setUpClass(self):
46 | with patch.object(PluginManager, "__init__", lambda x: None):
47 | self.PM = PluginManager()
48 |
49 | os.chdir("PluginManager")
50 |
51 | @classmethod
52 | def tearDownClass(self):
53 | os.chdir("..")
54 |
55 | @patch("barely.plugins.PluginManager.PluginManager.discover_plugins")
56 | def test___init__(self, discover):
57 | mock_config = {
58 | "PLUGIN_PATHS": {
59 | "SYS": {
60 | "CONTENT": "",
61 | "BACKUP": "",
62 | "PUBLICATION": ""
63 | },
64 | "USER": {
65 | "CONTENT": "",
66 | "BACKUP": "",
67 | "PUBLICATION": ""
68 | }
69 | }
70 | }
71 | with patch.dict(config, mock_config):
72 | PM = PluginManager()
73 | self.assertTrue(discover.called)
74 | self.assertIsNotNone(PM.plugins_content)
75 | self.assertIsNotNone(PM.plugins_backup)
76 | self.assertIsNotNone(PM.plugins_publication)
77 |
78 | def test_discover_plugins(self):
79 | # Content Plugins register with filetypes and a priority
80 | test_dict = self.PM.discover_plugins(["content", "empty"])
81 |
82 | self.assertEqual(3, len(test_dict))
83 | self.assertIn("md", test_dict)
84 | self.assertIn("pdf", test_dict)
85 | self.assertIn("png", test_dict)
86 |
87 | self.assertEqual(1, len(test_dict["md"]))
88 | self.assertTrue(issubclass(type(test_dict["md"][0]), PluginBase))
89 |
90 | self.assertEqual(2, len(test_dict["png"]))
91 | self.assertTrue(issubclass(type(test_dict["png"][0]), PluginBase))
92 | self.assertTrue(issubclass(type(test_dict["png"][1]), PluginBase))
93 |
94 | self.assertTrue(test_dict["png"][0].register()[0] == "P2")
95 | self.assertTrue(test_dict["png"][1].register()[0] == "P1")
96 |
97 | self.assertEqual(1, len(test_dict["pdf"]))
98 | self.assertTrue(issubclass(type(test_dict["pdf"][0]), PluginBase))
99 |
100 | # Backup/Publication Plugins only register with their class
101 | test_list = self.PM.discover_plugins(["other"], type_content=False)
102 | self.assertEqual(2, len(test_list))
103 | self.assertTrue(issubclass(type(test_list[0]), PluginBase))
104 | self.assertTrue(issubclass(type(test_list[1]), PluginBase))
105 | self.assertTrue(test_list[0].register()[0] == "P4")
106 | self.assertTrue(test_list[1].register()[0] == "P3")
107 |
108 | def test_hook_content(self):
109 | # Sample Item, to be processed by plugins registered for the "test" extension
110 | item = {
111 | "extension": "test"
112 | }
113 |
114 | # The first sample plugin returns 2 items for every input item, merely duplicating it
115 | sample_plugin1 = SamplePlugin()
116 | sample_plugin1.action = MagicMock(return_value=[item, item])
117 |
118 | # The second plugin is the constant 1-function
119 | sample_plugin2 = SamplePlugin()
120 | sample_plugin2.action = MagicMock(return_value=1)
121 |
122 | # Both plugins are "registered" for the test extension
123 | self.PM.plugins_content = {
124 | "test": [sample_plugin1, sample_plugin2]
125 | }
126 |
127 | # The hook should return two "1"s.
128 | returned_content = self.PM.hook_content(item)
129 | self.assertEqual([1, 1], returned_content)
130 |
131 | def test_hook_backup(self):
132 | sample_plugin = SamplePlugin()
133 | sample_plugin.action = MagicMock()
134 |
135 | self.PM.plugins_backup = [sample_plugin]
136 |
137 | self.assertFalse(sample_plugin.action.called)
138 | self.PM.hook_backup()
139 | self.assertTrue(sample_plugin.action.called)
140 |
141 | def test_hook_publication(self):
142 | sample_plugin1 = SamplePlugin()
143 | sample_plugin1.action = MagicMock()
144 |
145 | sample_plugin2 = SamplePlugin()
146 | sample_plugin2.action = MagicMock()
147 |
148 | self.PM.plugins_publication = [sample_plugin1, sample_plugin2]
149 |
150 | self.assertFalse(sample_plugin1.action.called)
151 | self.assertFalse(sample_plugin2.action.called)
152 | self.PM.hook_publication()
153 | self.assertTrue(sample_plugin1.action.called)
154 | self.assertTrue(sample_plugin2.action.called)
155 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # Welcome to the barely docs!
2 |
3 | 1. [About](about.md)
4 | 2. [Getting started](getting-started.md)
5 | 3. [Detailed Overview](detailed-overview.md)
6 | 4. [Modular Pages](modular-pages.md)
7 | 5. [Plugins](plugins.md)
8 | - [Writing your own Plugins](plugins.md#writing-your-own-plugins)
9 | 6. [Blueprints](blueprints.md)
10 | 7. [Lighthouse](lighthouse.md)
11 |
--------------------------------------------------------------------------------
/docs/about.md:
--------------------------------------------------------------------------------
1 | # About barely
2 |
3 | barely was built out of frustration with the readily available site generators, frameworks and CMS, which mostly fall into two categories: not providing crucial features; or providing such an overload of them that gettig started with the system takes longer than just building the site by hand.
4 |
5 | barely won't be the right tool for everyone and every project, and neither does it try to be. But it might be the right tool for your project, if:
6 |
7 | * you want to build a static webpage
8 | * you want to do so rapidly, with the barest minimum of setup and configuration
9 | * you value live reloading of every one of your changes, including SCSS/SASS, images and templates
10 | * you are satisfied with the featureset expected of a typical website or blog
11 |
12 | In those circumstances, barely aims to give you as smooth an experience as possible, by following these design principles:
13 |
14 | ## Simplicity
15 |
16 | All your files live in one directory (your devroot). You have - at most - two config files, one for configuring barelys behaviour, one for global metadata. You don't have to touch either one if you don't want to.
17 |
18 | barely renders markdown content and jinja2 templates into HTML pages. That's it. (OK, that's only it if you deactivate all the awesome [plugins](plugins.md) barely ships with.)
19 |
20 | ## Workflow
21 |
22 | If you start barely by typing `barely live` (or just `barely`), a live server starts and opens your project in your preferred browser. Any changes you save - be it in a page file, its yaml configuration, a template or even CSS/JS/SASS/... get reflected immediately. This makes working a breeze.
23 |
24 | When you've finished, simply hit `Ctrl+C`, and press enter on barelys prompt to push your changes to git, publish the site to your sftp server, or any other action you've specified.
25 |
26 | Since building performant and SEO-friendly websites is always important, barely comes bundled with a Google **Lighthouse** CLI option, letting you quickly generate reports about your sites health.
27 |
28 | ## Extensibility
29 |
30 | barely comes with 10 [plugins](plugins.md) that make working even easier, like automatically compressing images, compiling SASS, generating complete HTML forms out of yaml, and managing Collections (barelys catch-all term for things like tags or categories of posts and pages).
31 |
32 | Should you still miss some functionality, chances are you can implement it in minutes, thanks to barelys super simple [plugins API](plugins.md#writing-your-own-plugins).
33 |
34 | [< back](README.md)
35 |
--------------------------------------------------------------------------------
/docs/barely-demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/docs/barely-demo.gif
--------------------------------------------------------------------------------
/docs/blueprints.md:
--------------------------------------------------------------------------------
1 | # Blueprints
2 |
3 | Back in the [Basics](/README.md/#basics), we have already briefly covered blueprints. They are pretty much exactly what you would expect: re-usable project templates that you can instantiate into new projects. Other frameworks might call them themes.
4 |
5 | You can list all available blueprints with:
6 | ```console
7 | $ barely blueprints
8 | [barely][ core][ INFO] :: found 2 blueprints:
9 | -> default
10 | -> blank
11 | ```
12 |
13 | The help menu hints at a way to also create your own blueprints:
14 | ```console
15 | $ barely blueprints --help
16 | Usage: barely blueprints [OPTIONS]
17 |
18 | list all available blueprints, or create a new one
19 |
20 | Options:
21 | -n, --new TEXT create a reusable blueprint from the current project
22 | --help Show this message and exit.
23 | ```
24 |
25 | Executing `barely blueprints --new "name"` will create a new blueprint out of your current project, and you can freely use it from now on:
26 |
27 | ```console
28 | $ barely blueprints
29 | [barely][ core][ INFO] :: found 3 blueprints:
30 | -> name
31 | -> default
32 | -> blank
33 | ```
34 |
35 | [< back](README.md)
36 |
--------------------------------------------------------------------------------
/docs/detailed-overview.md:
--------------------------------------------------------------------------------
1 | # Detailed Overview
2 |
3 | There are a couple of things that are important to know about how barely works. If you've used similar frameworks before, you'll probably already be familiar with most of these things. barely doesn't try to reinvent the wheel.
4 |
5 | 1. Templates:
6 |
7 | - all your templates live in the `templates/` folder inside your devroot. If, for whatever reason, you want to place them somewhere else, set the `TEMPLATES_DIR` variable in your `config.yaml`.
8 |
9 | - you can organize your templates freely inside that folder, including the use of subfolders.
10 |
11 | - you set a template for a page by naming the pages markdown file according to this scheme:
12 |
13 | - you have the following template: `templates/something.html`. To use it, name any markdown file `something.md`.
14 |
15 | - if your template lives in a subfolder, you specify it with a `.`: to use `templates/subdir/other.html`, name your markdown file `subdir.other.md`.
16 |
17 | - templates can include or extend other templates:
18 |
19 | - `{% include "subdir/other.html" %}`
20 |
21 | - `{% extends "something.html" %}`
22 |
23 | - for any additional information regarding templates, please refer to the [official jinja2 documentation](https://jinja.palletsprojects.com/en/3.0.x/).
24 |
25 | 2. YAML & Markdown
26 |
27 | If you are not familiar with Markdown yet, GitHub has an [excellent guide](https://guides.github.com/features/mastering-markdown/) on it. You will create a Markdown file corresponding to every page of your website.
28 |
29 | Inside of every markdown file, you can specify variables to be used either in some plugin, or in your templates. To do so, the first line of the file has to be `---`, and the same delimiter has to be used before any markdown contents.
30 |
31 | Inbetween the delimiters, you can use normal YAML syntax:
32 | ```yaml
33 | ---
34 | title: "Page title for use in a template!"
35 | description: "..."
36 | nested:
37 | - value
38 | - something else
39 | ---
40 | ```
41 | These variables can be used like any others in your templates: `{{ title }}`.
42 |
43 | Sometimes, plugins expect (or allow) for configuration on a page level (e.g. the [Highlight](plugins/highlight.md) plugin). Additionally, barely pays attention to two page level configurations:
44 | ```yaml
45 | extension: specify a extension different from html (without a leading dot). E.g.: php, xml,...
46 | publish: if set to false, this page will not be rendered. Useful for drafting.
47 | ```
48 |
49 | Of course, both of these may also be set at a global level.
50 |
51 | Both the initial YAML section and any Markdown are optional. If you want to, your file can be completely empty. In that case, the template specified by the filename will still get rendered as usual.
52 |
53 |
54 | 3. Configuration Files
55 |
56 | You can utilize two configuration files:
57 | - `config.yaml`: configure barely's behaviour. You have to at least specify the paths to your webroot and devroot, like this:
58 | ```yaml
59 | ROOT:
60 | DEV: /[...]/devroot
61 | WEB: /[...]/webroot
62 | ```
63 |
64 | barely also sets some standard values which you can optionally override:
65 | ```yaml
66 | TEMPLATES_DIR: templates
67 | PAGE_EXT: md
68 | IMAGE_EXT:
69 | - jpg
70 | - jpeg
71 | - png
72 | IGNORE:
73 | - .git
74 | ```
75 |
76 | **Note:** `config.yaml` is also the place for [plugin configurations](plugins.md)
77 |
78 | - `metadata.yaml`: set global variables. You can leave this file empty or completely remove it.
79 |
80 | 4. Other Files
81 |
82 | Any other files will get copied over into your webroot (possibly after being processed by your enabled plugins), as long as they aren't set to be ignored in your `config.yaml`.
83 |
84 | ## Modular Pages
85 |
86 | See the [Modular Pages](modular-pages.md) page.
87 |
88 | ## Plugins
89 |
90 | See the [Plugins](plugins.md) page.
91 |
92 | ## Blueprints
93 |
94 | See the [Blueprints](blueprints.md) page.
95 |
96 | [< back](README.md)
97 |
--------------------------------------------------------------------------------
/docs/getting-started.md:
--------------------------------------------------------------------------------
1 | # Getting Started
2 |
3 | Good news: Getting started with barely is super easy! This guide elaborates on the [Quickstart Guide](/README.md#quickstart) in the README.
4 |
5 | ### Prerequisites
6 |
7 | Make sure you have python >= 3.9 installed:
8 | ```console
9 | $ python -V
10 | Python 3.9.x
11 | ```
12 |
13 | (On Windows: `py -V`)
14 |
15 | It is highly recommended to create a virtual environment for barely, otherwise some parts may not work:
16 | ```console
17 | $ python -m venv .venv
18 | $ . .venv/bin/activate
19 | (.venv) $
20 | ```
21 |
22 | (On Windows: `py -m venv .venv` and `.venv\Scripts\activate`)
23 |
24 | ### Installation
25 |
26 | Now, simply install barely like any other package:
27 | ```console
28 | (.venv) $ pip install barely
29 | ```
30 |
31 | (On Windows: `py -m pip install barely`)
32 |
33 | That's it! Congrats!
34 |
35 |
36 | ## Usage Basics
37 |
38 | Now let's get familiar with using barely!
39 |
40 | 1. Type `barely --help` to get an overview over available commands and options:
41 | ```console
42 | $ barely --help
43 | Usage: barely [OPTIONS] COMMAND [ARGS]...
44 |
45 | barely reduces static website development to its key parts, by automatically
46 | rendering jinja2 templates and Markdown content into HTML. A simple plugin
47 | interface allows for easy extensibility, and the built-in live web server
48 | makes on-the-fly development as comfortable as possible.
49 |
50 | Options:
51 | -d, --debug set logging level to debug
52 | --help Show this message and exit.
53 |
54 | Commands:
55 | live* starts a live server, opens your project in the browser and...
56 | blueprints list all available blueprints, or create a new one
57 | lighthouse use Google Lighthouse to evaluate a page for SEO- and...
58 | new create a new barely project (optionally with a blueprint)
59 | rebuild (re)build the entire project
60 | test run the testsuite to verify the install
61 | ```
62 |
63 | 2. Try typing `barely`, `barely live`, or `barely rebuild`:
64 | ```console
65 | $ barely
66 | [barely][ core][ERROR] :: could not find 'config.yaml'. Exiting
67 | ```
68 | What happened? barely is telling us that we aren't currently in a barely project directory. For a directory to count as a project, it has to contain a `config.yaml` file, which in turn has to specify the devroot (where we will work) and the webroot (where barely renders to).
69 |
70 | So lets change that!
71 |
72 | 3. Create a new project with `barely new`:
73 | ```console
74 | $ barely new
75 | [barely][ core][ INFO] :: setting up new project with parameters:
76 | -> webroot: webroot
77 | -> devroot: devroot
78 | -> blueprint: default
79 | [barely][ core][ INFO] :: setting up basic config...
80 | [barely][ core][ INFO] :: done.
81 | ```
82 | Sweet! barely created two new subdirectories, `devroot` and `webroot`. The project was also created with a blueprint, namely `default`, which is why our `devroot` is not empty. We will learn about blueprints in a second.
83 |
84 | BTW: you can easily change the project creation parameters, see for reference:
85 | ```console
86 | $ barely new --help
87 | Usage: barely new [OPTIONS]
88 |
89 | create a new barely project (optionally with a blueprint)
90 |
91 | Options:
92 | -b, --blueprint TEXT instantiate project from a blueprint
93 | -w, --webroot TEXT location for the generated static files
94 | -d, --devroot TEXT project directory, for development files
95 | --help Show this message and exit.
96 | ```
97 |
98 | 4. Let's have a look around!
99 | ```console
100 | $ cd devroot
101 | $ tree .
102 | .
103 | ├── config.yaml
104 | ├── metadata.yaml
105 | ├── template.md
106 | └── templates
107 | ├── template2.html
108 | └── template.html
109 |
110 | 1 directory, 5 files
111 | ```
112 | - `config.yaml` contains all the configuration for barely and its plugins. Right now, it only contains the absolute paths of the devroot and webroot
113 | - `metadata.yaml` is a place you can put any values you want to use in multiple places across your project, be it metadata or any other variables
114 | - `template.md` is the Markdown file for the root page of the website. Its contents will get rendered into `webroot/index.html` with the `templates/template.md` template
115 | - `templates/` contains all your templates
116 |
117 | 6. Let's build the project!
118 | ```console
119 | $ barely rebuild
120 | [barely][ core][ INFO] :: registering plugins...
121 | [barely][ core][ INFO] :: 7 plugins registered.
122 | [barely][ core][ INFO] :: rebuilding devroot...
123 | -> deleted /[...]/webroot
124 | [barely][ core][ INFO] :: event at /[...]/devroot/template.md
125 | -> rendered, highlighted /[...]/devroot/template.md -> /[...]/webroot/index.html
126 | [barely][ core][ INFO] :: rebuild complete.
127 | [barely][ core][ INFO] :: Finalizing plugin ReadingTime...
128 | [barely][ core][ INFO] :: Finalizing plugin ToC...
129 | [barely][ core][ INFO] :: Finalizing plugin AutoSEO...
130 | [barely][ core][ INFO] :: Finalizing plugin Highlight...
131 | [barely][ core][ INFO] :: Finalizing plugin Forms...
132 | [barely][ core][ INFO] :: Finalizing plugin Minify...
133 | [barely][ core][ INFO] :: Finalizing plugin Gallery...
134 | [barely][ core][ INFO] :: ..
135 | -> Do you want to Publish / Backup / do both?
136 | -> *[n]othing | [p]ublish | [b]ackup | [Y]do both :: n
137 | [barely][ core][ INFO] :: exited.
138 | ```
139 |
140 | And then start the live server:
141 | ```console
142 | $ barely
143 | [barely][ core][ INFO] :: registering plugins...
144 | [barely][ core][ INFO] :: 7 plugins registered.
145 | [barely][ core][ INFO] :: started tracking...
146 | ```
147 |
148 | We could also have combined those two steps with the `-s` flag like this: `barely rebuild -s`, to start the live server immediately after rebuilding.
149 |
150 | Your favorite browser should open, and you will be greeted with the rendered version of `template.md`.
151 |
152 | Now is a good time to play around a bit with your sample project - make some changes to the contents, the templates or add a stylesheet and watch the page update in real time!
153 |
154 | When you feel comfortable with the workings of barely, move on to the [next section](detailed-overview.md).
155 |
156 | [< back](README.md)
157 |
--------------------------------------------------------------------------------
/docs/lighthouse.md:
--------------------------------------------------------------------------------
1 | # Lighthouse
2 |
3 | Google Lighthouse is an open-source project that can generate highly insightful metrics about a single page of your website. These metrics contain scores about the SEO-friendliness of your site, its accessibility, and much more.
4 |
5 | [Learn more about it here.](https://developers.google.com/web/tools/lighthouse)
6 |
7 | There is no native python implementation for Lighthouse, rather, if you want to use it, you will have to have the following installed:
8 |
9 | - node
10 | - [lighthouse](https://www.npmjs.com/package/lighthouse) (npm package)
11 | - Chrome or Chromium
12 |
13 | These are **not** dependencies of barely. You only have to install these if you want to use Lighthouse.
14 |
15 | To generate a report of your root page:
16 | ```console
17 | $ barely lighthouse
18 | [barely][ core][ INFO] :: Starting evaluation using lighthouse 8.3.0...
19 | [barely][ core][ INFO] :: Finished the evaluation! Opening the result now.
20 | ```
21 |
22 | You can also specify any other page to be evaluated, or force the evaluation of the desktop version of a page:
23 | ```console
24 | $ barely lighthouse --help
25 | Usage: barely lighthouse [OPTIONS]
26 |
27 | use Google Lighthouse to evaluate a page for SEO- and accessibility scores
28 |
29 | Options:
30 | -d, --desktop evaluate full-width page. default is mobile.
31 | -p, --page TEXT specify a page to be evaluated other than the root
32 | --help Show this message and exit.
33 | ```
34 |
35 | [< back](README.md)
36 |
--------------------------------------------------------------------------------
/docs/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charludo/barely/4bddbdd414ddd8524d6fb7daffbafbb3383b4619/docs/logo.png
--------------------------------------------------------------------------------
/docs/modular-pages.md:
--------------------------------------------------------------------------------
1 | # Modular Pages
2 |
3 | Sometimes, a page can contain many different sections, all with their own content, structure and design. An example of this are websites in the OnePage style, or with a long landing page. You might have distinct sections, like "About", "Services", and "Contact" on one page. Since there isn't really a way to split the contents of one pages markdown (it will all get rendered into a single ``), barely features modular pages.
4 |
5 | These differ from normal pages in that they won't get rendered on their own, and thus are not linkable in a menu. Instead, they get "absorbed" into the parent page.
6 |
7 | ## Defining a modular page
8 |
9 | To define a modular page, simply put the "modular" argument into that pages configuration:
10 | ```yaml
11 | ---
12 | title: My Parent Page
13 | modular:
14 | - about
15 | - services
16 | - contact
17 | ---
18 | ```
19 |
20 | Here, we told barely that our parent page has three children: about, services, and contact. For each of those, barely expects a folder with that exact name, lead by an `underscore`.
21 | Inside those folders, you can define pages and their templates, as well as any additional ressources, just like you would with any normal page.
22 |
23 | Notably, the names in our `modular` variable only reflect what the child pages are called, **not** what template they use. So our directory could (for example) look like this,
24 | ```console
25 | $ tree .
26 | .
27 | ├── _about
28 | │ └── child.md
29 | ├── _contact
30 | │ └── contactform.md
31 | ├── parent.md
32 | └── _services
33 | └── child.md
34 | ```
35 | where _about and _services share the same template, but _contact does not.
36 |
37 | Also note that subpages get handed to plugins just like any other pages! They can not, however, have subpages of their own.
38 |
39 | To display the subpages, the template for your parent.md has to look somewhat like this:
40 | ```html
41 | {% for sp in sub_pages %}
42 | {{ sp }}
43 | {% endfor %}
44 | ```
45 |
46 | You can not access subpages by their name, `sub_pages` is a list containing the rendered subpages, ordered the same way that you specified them in back in the parents YAML.
47 |
48 | ## Organization
49 |
50 | It's good style to keep your modular templates somewhat separate from your normal ones. This is as easy as creating a `modular/` folder inside your `templates/` directory, then naming the subpages like `modular.child.md`.
51 |
52 | [< back](README.md)
53 |
--------------------------------------------------------------------------------
/docs/plugins/autoseo.md:
--------------------------------------------------------------------------------
1 | # AutoSEO
2 |
3 | Type: `content`
4 | Enabled by default: `true`
5 |
6 | Generate all the pesky meta-tags from a very limited configuration. This plugin can save you a lot of time, and make your pages much more appealing to Google & Co.
7 |
8 | AutoSEO is quite smart about figuring out good values for tags, and has multiple fallback sources for every one of them. Most of the time you will get away with a very basic config (see below).
9 |
10 | Set up your `metadata.yaml` like this:
11 |
12 | ```yaml
13 | site_url: the URL at which you plan on publishing your website
14 | site_name: name of your website
15 | site_description: optional (fallback) if no page-specific description is found
16 | site_keywords: keywords befitting to all pages
17 | favicon: favicon
18 | twitter_site: your twitter profile
19 | twitter_creator: what twitter user to attribute content to. better specified on a per-page-basis.
20 | ```
21 |
22 | **ALL** of these are optional.
23 |
24 | Then make sure you have a `title` specified in the meta of your pages (`site_name` will be used as fallback). Either provide a summary, or a description.
25 |
26 | You can specify a title_image, if none is found, the plugin will extract one from your markdown; if none is found again, it will look for one in the page's directory in the devroot.
27 |
28 | You can also override these values specifically for social media (OpenGraph/Facebook and Twitter).
29 |
30 | All in all, in addition to the above global values, these attributes are respected:
31 |
32 | - title
33 | - title_image
34 | - description
35 | - summary
36 | - keywords
37 | - robots
38 | - SEO:
39 | - title
40 | - title_image
41 | - title_image_alt
42 | - description
43 | - site_name
44 | - twitter_card
45 | - twitter_site
46 | - twitter_creator
47 | ---
48 |
49 | **Note:** autoSEO ignores modular subpages - its configuration has to happen within the parent's yaml config.
50 |
51 | Use the generated tags in the `` of your base template:
52 |
53 | ```HTML
54 |
55 |
56 | {{ seo_tags }}
57 | ...
58 |
59 | ...
60 |
61 | ```
62 |
63 | If you have not manually created a `robots.txt` and/or `sitemap.txt` already, the plugin will do it for you.
64 |
65 | ---
66 | **config.yaml key:** AUTO_SEO
67 |
68 | |argument |default value |explanation |
69 | |-----------------------|-------------------|-----------------------------------------------|
70 | |PRIORITY |30 | |
71 | |MISC_TAGS |true |auto-generate charset & viewport tags |
72 |
--------------------------------------------------------------------------------
/docs/plugins/collections.md:
--------------------------------------------------------------------------------
1 | # Collections
2 |
3 | Type: `content`
4 | Enabled by default: `false`
5 |
6 | Collections allow you to include previews of other pages on a page, or to add your page to a collection.
7 | **Note**: might not update properly during live mode, but will catch up when quitting barely.
8 |
9 |
10 |
11 | ---
12 |
13 | Add a page to collections like this:
14 | ```yaml
15 | collections:
16 | - col1
17 | - col2
18 | ```
19 |
20 | Exhibit pages belonging to collections like this:
21 | ```yaml
22 | exhibits:
23 | - col3
24 | - col4
25 | ```
26 |
27 | You can then display exhibits in your templates:
28 | ```html
29 | {% for exhibit in exhibits %}
30 | {% for collectible in exhibit %}
31 | {{ collectible["title"] }}
32 | {{ collectible["preview"] }}
33 | {{ collectible["href"] }}
34 | {% endfor %}
35 | {% endfor %}
36 | ```
37 |
38 | Exhibits contain the following tags / information:
39 | ```yaml
40 | title: title of the exhibit
41 | preview: the first few words of the page
42 | raw: the entire markdown content of the page
43 | href: link to the page
44 | image: link to image, if title_image was specified on the original page
45 | date: modification or creation time of the page
46 | reading_time: reading time as provided by ReadingTime plugin
47 | ```
48 |
49 | Inside the OVERVIEW_TEMPLATE, list the available collections like this:
50 | ```html
51 | {% for collection in collections_list %}
52 | {{ collection["name"] }}
53 | {{ collection["size"] }}
54 | {{ collection['href'] }}
55 | {% endfor %}
56 | ```
57 | where the `size` is the number of collectibles in the collection.
58 |
59 | ---
60 | **config.yaml key:** COLLECTIONS
61 | |argument |default value |explanation |
62 | |-------------------|-------------------|-----------------------------------------------------------------------------------------|
63 | |PRIORITY |999 | |
64 | |PAGE |categories |where collection overview pages get rendered to |
65 | |SUMMARY_LENGTH |100 |cut-off for summaries of pages / posts |
66 | |OVERVIEW_TITLE | |title of the collections overview page |
67 | |OVERVIEW_TEMPLATE | |template for the collections overview |
68 | |OVERVIEW_CONTENT | |points to markdown file containingcontent of OVERVIEW page |
69 | |COLLECTION_TEMPLATE| |template for the individual collection pages |
70 | |ORDER_KEY |timestamp |specify a key by which collectibles are sorted on collection pages and within exhibitions|
71 | |ORDER_REVERSE |true |reverses the sorting order |
72 |
--------------------------------------------------------------------------------
/docs/plugins/forms.md:
--------------------------------------------------------------------------------
1 | # Forms
2 |
3 | Type: `content`
4 | Enabled by default: `true`
5 |
6 | Specify forms in YAML syntax!
7 |
8 | ---
9 |
10 | A sample form could look like this:
11 | ```yaml
12 | forms:
13 | example:
14 | action: sample.php
15 | classes: large
16 |
17 | group-basic:
18 | legend: Basic Data
19 | name:
20 | type: text
21 | required: true
22 | placeholder: Name
23 | mail:
24 | type: email
25 | required: true
26 | placeholder: E-Mail
27 |
28 | topic:
29 | type: select
30 | multiple: false
31 | default: standard
32 | options:
33 | n1: something
34 | n2: standard
35 | n3: something else
36 |
37 | message:
38 | type: textarea
39 | required: true
40 | classes: extra
41 | value: Pre-filled with this
42 |
43 | gdpr:
44 | type: checkbox
45 | value: agreed
46 | label: Agree to GDPR
47 | name: gdpr-checkbox
48 |
49 | send:
50 | type: button
51 | value: Send Now
52 | action: submit
53 | ```
54 |
55 | You can then display the form in your templates:
56 | ```html
57 | {{ form_example }}
58 | ```
59 |
60 | Forms can contain the following attributes:
61 | ```yaml
62 | classes: extra classes
63 | action: what to to do on submit
64 | ```
65 |
66 | Groups can have the following attributes:
67 | ```yaml
68 | classes: extra classes
69 | legend: a legend for the fieldset
70 | ```
71 |
72 | Form fields can have the following attributes:
73 | ```yaml
74 | classes: extra classes
75 | required: bool
76 | label: false or label text
77 | label-after: false or label text
78 | type: type of input
79 | options: needed by select, radio types
80 | default: default value
81 | multiple: bool (for select)
82 | placeholder: placeholder value
83 | rows: for textarea
84 | cols: for textarea
85 | ```
86 | ---
87 | **config.yaml key:** FORMS
88 | |argument |default value |explanation |
89 | |-------------------|-------------------|-----------------------------------------------|
90 | |PRIORITY |5 | |
91 |
--------------------------------------------------------------------------------
/docs/plugins/gallery.md:
--------------------------------------------------------------------------------
1 | # Gallery
2 |
3 | Type: `content`
4 | Enabled by default: `true`
5 |
6 | Turn simple markdown into a list of images (aka, a gallery).
7 |
8 | Default priority is lower than that of Pixelizer, meaning the Gallery will receive the full optimization benefits of Pixelizer.
9 |
10 | ---
11 |
12 | Markdown-Syntax:
13 |
14 | ```md
15 | [name ]!!(image-folder)
16 | ```
17 |
18 | The name of the gallery and the image folder are required. Sorting key and direction are optional; however, it is not possible to specify `direction` without `sort`, since these are positional arguments.
19 |
20 | Each image will receive an `alt` tag consisting of the gallery name, and the image's position within the gallery.
21 |
22 | ---
23 | **config.yaml key:** GALLERY
24 | |argument |default value |explanation |
25 | |-------------------|-------------------|-----------------------------------------------|
26 | |PRIORITY |2 | |
27 | |DEFAULT_SORT |name |name or time |
28 | |DEFAULT_DIRECTION |asc |asc or desc |
29 | |GALLERY_CLASS |gallery |class for the wrapping `div` |
30 |
31 | The wrapping `` also gets an ID of `gallery-{name}`.
32 |
--------------------------------------------------------------------------------
/docs/plugins/git.md:
--------------------------------------------------------------------------------
1 | # Git
2 |
3 | Type: `backup`
4 | Enabled by default: `true`
5 |
6 | Pushes to a remote origin whenever you are done rebuilding / live editing (unless you answer barelys exit prompt wih "no" or "publish only").
7 |
8 | **Note:** git has to be installed. On Windows, GitHub Desktop is **not** sufficient. Check with `git --version`.
9 |
10 | **Note**: the repository and its origin have to already be initialized. This plugin can not do that for you.
11 |
12 | ---
13 | **config.yaml key:** GIT
14 | |argument |default value |explanation |
15 | |-----------------------|-------------------|-----------------------------------------------|
16 | |PRIORITY |40 | |
17 | |MESSAGE |barely auto commit |gets appended with a timestamp |
18 | |REMOTE_NAME |origin | |
19 |
--------------------------------------------------------------------------------
/docs/plugins/highlight.md:
--------------------------------------------------------------------------------
1 | # Highlight
2 |
3 | Type: `content`
4 | Enabled by default: `true`
5 |
6 | Lex and highlight `markdown code blocks`. You can configure a global theme and lexer, and then override both in page configs; you can also specify the lexer on a per-code-block-level like this:
7 |
8 | ```python
9 |
10 |
11 |
12 | ```
13 |
14 | ---
15 |
16 | For a full list of features, check [pygments.org](https://pygments.org/)!
17 |
18 | ---
19 | **config.yaml key:** HIGHLIGHT
20 | |argument |default value |explanation |
21 | |-------------------|-------------------|-----------------------------------------------|
22 | |PRIORITY |20 | |
23 | |CLASS_PREFIX |hl |in case of conflicting styles |
24 | |LINE_NOS |table | |
25 | |TABSIZE |4 | |
26 | |ENCODING |utf-8 | |
27 | |THEME |default |check pygments.org for a list |
28 | |LEXER | |empty means guessing |
29 | |ASSETS_DIR |assets |where to place theme css files |
30 |
--------------------------------------------------------------------------------
/docs/plugins/localbackup.md:
--------------------------------------------------------------------------------
1 | # Local Backup
2 |
3 | Type: `backup`
4 | Enabled by default: `false`
5 |
6 | Keeps a limited number of project copies on your local machine.
7 |
8 | **Please use the Git plugin for actual backups!**
9 |
10 | Restoring a backup is a manual task.
11 |
12 | ---
13 | **config.yaml key:** LOCAL_BACKUP
14 | |argument |default value |explanation |
15 | |-----------------------|-------------------|---------------------------------------------------|
16 | |PRIORITY |30 | |
17 | |MAX |10 |number of backups kept before deleting the oldest |
18 | |BAKROOT |[next to devroot] | |
19 |
--------------------------------------------------------------------------------
/docs/plugins/minify.md:
--------------------------------------------------------------------------------
1 | # Minify
2 |
3 | Type: `content`
4 | Enabled by default: `true`
5 |
6 | - compress JS files (js)
7 | - compile and compress SASS/CSS (sass, scss)
8 |
9 |
10 | ---
11 | **config.yaml key:** MINIFY
12 | |argument |default value |explanation |
13 | |-----------------------|-------------------|-----------------------------------------------|
14 | |PRIORITY |3 | |
15 | |JS_OBFUSCATE |true | |
16 | |JS_OBFUSCATE_GLOBALS |true | |
17 | |CSS_INCLUDE_COMMENTS |false | |
18 | |CSS_OUTPUT_STYLE |compressed | |
19 |
--------------------------------------------------------------------------------
/docs/plugins/pixelizer.md:
--------------------------------------------------------------------------------
1 | # Pixelizer
2 |
3 | Type: `content`
4 | Enabled by default: `false`
5 |
6 | - compress and resize images (png, jpg, jpeg, tif, tiff, bmp)
7 | - also convert these images to webp
8 | - turn `
` tags in markdown files into `` tags in HTML, offering webp (and the original format as a fallback) to the browser
9 | - targets for resizing/compression, as well as the layouts of the `` tag, are configurable freely
10 |
11 | ---
12 |
13 | With the standard config, this:
14 |
15 | ```md
16 | 
17 | ```
18 |
19 | will be turned into this:
20 |
21 | ```HTML
22 |
23 |
24 |
25 |
26 |
27 | ```
28 |
29 | instead of this:
30 |
31 | ```HTML
32 |
33 | ```
34 |
35 | Additionally, all 6 variants will be created (resized, quality changed, format changed), and the original will be copied as fallback.
36 |
37 | ---
38 | **config.yaml key:** PIXELIZER
39 | |argument |default value |explanation |
40 | |--------------|-------------------|--------------------------------------------|
41 | |PRIORITY |3 | |
42 | |TARGETS |- lg 1000 70
- md 650 70
- sm 300 70|syntax: \ \ \|
43 | |LAYOUTS |- (max-width: 1000px) 100vw
- 1000px |these are essentially media queries. Wrap in `"..."` in yaml! |
44 |
--------------------------------------------------------------------------------
/docs/plugins/readingtime.md:
--------------------------------------------------------------------------------
1 | # Reading Time
2 |
3 | Type: `content`
4 | Enabled by default: `true`
5 |
6 | Estimates the reading time in minutes for a page or post. Common feature for blogs.
7 |
8 | If WPM_FAST does not equal WPM_SLOW, displays an estimate of `x - y` minutes. Otherwise, only a single number is displayed.
9 |
10 | ---
11 |
12 | Display the reading time like this in your template:
13 | ```html
14 | {{ reading_time }}
15 | ```
16 |
17 | ---
18 | **config.yaml key:** READING_TIME
19 | |argument |default value |explanation |
20 | |-----------------------|-------------------|-----------------------------------------------|
21 | |PRIORITY |850 | |
22 | |WPM_FAST |265 |for fast readers |
23 | |WPM_SLOW |90 |for slow readers |
24 | |SEPARATOR | - |separator between the fast and slow values |
25 |
--------------------------------------------------------------------------------
/docs/plugins/sftp.md:
--------------------------------------------------------------------------------
1 | # SFTP
2 |
3 | Type: `publication`
4 | Enabled by default: `false`
5 |
6 | Uploads your webroot to an SFTP Webserver after you have exited barely.
7 |
8 | Works with either a username/password, or an SSH key.
9 |
10 | ---
11 | **config.yaml key:** SFTP
12 | |argument |default value |explanation |
13 | |-----------------------|-------------------|-----------------------------------------------------------------------------------|
14 | |PRIORITY |90 | |
15 | |HOSTNAME | | |
16 | |username | | |
17 | |PASSWORD | | |
18 | |KEY | |if a key is specified, it will be preferred over a password |
19 | |ROOT | |dir for the files on the server. check with your host for a correct absolute path! |
20 |
--------------------------------------------------------------------------------
/docs/plugins/timestamps.md:
--------------------------------------------------------------------------------
1 | # Timestamps
2 |
3 | Type: `content`
4 | Enabled by default: `false`
5 |
6 | Timestamp your pages and posts. Generates stamps for the creation and last modified times. Displays them in a custom time format.
7 |
8 | **Note**: On Unix systems, there is no "creation" timestamp. It always gets reset to the last modified time.
9 |
10 | ---
11 |
12 | Display the timestamps like this in your template:
13 | ```html
14 | {{ created }}
15 | {{ edited }}
16 | ```
17 |
18 | ---
19 | **config.yaml key:** TIMESTAMPS
20 | |argument |default value |explanation |
21 | |-----------------------|-------------------|-----------------------------------------------|
22 | |PRIORITY |3 | |
23 | |FORMAT |%d.%m.%Y | |
24 |
--------------------------------------------------------------------------------
/docs/plugins/toc.md:
--------------------------------------------------------------------------------
1 | # Table of Contents
2 |
3 | Type: `content`
4 | Enabled by default: `true`
5 |
6 | Creates a nicely structured table of contents by assigning IDs to your headings and linking to them. You can specify the min and max levels of headings, and whether it should be an unordered list, an ordered list, or some other wrapper tag should be used.
7 |
8 | ---
9 |
10 | Display the table of contents like this in your template:
11 | ```html
12 | {{ toc }}
13 | ```
14 |
15 | **Note:** unfortunately, the ToC does not support modular subpages, as their positioning and order heavily depensd on the jinja2 template used. ToCs can still be generated for the parent page, however.
16 |
17 | ---
18 | **config.yaml key:** TOC
19 | |argument |default value |explanation |
20 | |-----------------------|-------------------|-----------------------------------------------|
21 | |PRIORITY |2 | |
22 | |MIN_DEPTH |1 | |
23 | |MAX_DEPTH |4 | |
24 | |LIST_ELEMENT |ul |ol for ordered list |
25 |
--------------------------------------------------------------------------------
/flake.lock:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": {
3 | "nixpkgs": {
4 | "locked": {
5 | "lastModified": 1718714799,
6 | "narHash": "sha256-FUZpz9rg3gL8NVPKbqU8ei1VkPLsTIfAJ2fdAf5qjak=",
7 | "owner": "nixos",
8 | "repo": "nixpkgs",
9 | "rev": "c00d587b1a1afbf200b1d8f0b0e4ba9deb1c7f0e",
10 | "type": "github"
11 | },
12 | "original": {
13 | "owner": "nixos",
14 | "ref": "nixos-unstable",
15 | "repo": "nixpkgs",
16 | "type": "github"
17 | }
18 | },
19 | "root": {
20 | "inputs": {
21 | "nixpkgs": "nixpkgs",
22 | "utils": "utils"
23 | }
24 | },
25 | "systems": {
26 | "locked": {
27 | "lastModified": 1681028828,
28 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
29 | "owner": "nix-systems",
30 | "repo": "default",
31 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
32 | "type": "github"
33 | },
34 | "original": {
35 | "owner": "nix-systems",
36 | "repo": "default",
37 | "type": "github"
38 | }
39 | },
40 | "utils": {
41 | "inputs": {
42 | "systems": "systems"
43 | },
44 | "locked": {
45 | "lastModified": 1710146030,
46 | "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
47 | "owner": "numtide",
48 | "repo": "flake-utils",
49 | "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
50 | "type": "github"
51 | },
52 | "original": {
53 | "owner": "numtide",
54 | "repo": "flake-utils",
55 | "type": "github"
56 | }
57 | }
58 | },
59 | "root": "root",
60 | "version": 7
61 | }
62 |
--------------------------------------------------------------------------------
/flake.nix:
--------------------------------------------------------------------------------
1 | {
2 | inputs = {
3 | utils.url = "github:numtide/flake-utils";
4 | nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
5 | };
6 |
7 | outputs = { self, nixpkgs, utils }:
8 | utils.lib.eachDefaultSystem (system:
9 | let
10 | pkgs = import nixpkgs { inherit system; };
11 | in
12 | {
13 | packages.default = pkgs.python312Packages.buildPythonPackage rec {
14 | pname = "barely";
15 | version = "1.2.2";
16 |
17 | src = pkgs.fetchPypi {
18 | inherit pname version;
19 | hash = "sha256-/gliqfkPwnhZilFXltNXuOjQnJoJ9u0SnktqlLmRfTo=";
20 | };
21 |
22 |
23 | doCheck = false;
24 |
25 | pyproject = true;
26 | build-system = [ pkgs.python312Packages.setuptools ];
27 |
28 | propagatedBuildInputs = with pkgs.python312Packages; [
29 | pip
30 | click
31 | click-default-group
32 | coloredlogs
33 | mock
34 | pyyaml
35 | watchdog
36 | pillow
37 | gitpython
38 | pygments
39 | libsass
40 | pysftp
41 | livereload
42 | binaryornot
43 | jinja2
44 | mistune
45 | calmjs
46 | ] ++ [ pkgs.python312Full ];
47 | };
48 |
49 | devShells.default = pkgs.mkShell {
50 | packages = with pkgs; [
51 | python39Full
52 | python39Packages.pip
53 | python39Packages.platformdirs
54 |
55 | ruff
56 | djlint
57 | ];
58 |
59 | LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath [ pkgs.stdenv.cc.cc.lib pkgs.file ];
60 |
61 | shellHook = /* bash */ ''
62 | set_if_unset() {
63 | if [ -z "$(eval \$$1)" ]; then
64 | export "$1"="$2"
65 | fi
66 | }
67 |
68 | # Setting LD_LIBRARY_PATH can cause issues on non-NixOS systems
69 | if ! command -v nixos-version &> /dev/null; then
70 | unset LD_LIBRARY_PATH
71 | fi
72 |
73 | SOURCE_DATE_EPOCH=$(date +%s)
74 | VENV=.venv
75 | if [ -d $VENV ]; then
76 | source ./$VENV/bin/activate
77 | fi
78 | export PYTHONPATH=`pwd`/$VENV/${pkgs.python39Full.sitePackages}/:$PYTHONPATH
79 | '';
80 | };
81 | });
82 | }
83 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [metadata]
2 | description-file = README.md
3 |
4 | [flake8]
5 | ignore = E501
6 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | import pathlib
2 |
3 | import setuptools
4 |
5 | # The directory containing this file
6 | HERE = pathlib.Path(__file__).parent
7 |
8 | # The text of the README file
9 | README = (HERE / "README.md").read_text()
10 |
11 | setuptools.setup(
12 | name="barely",
13 | version="1.2.2",
14 | description="barely is a lightweight, but highly extensible static site generator written in pure python.",
15 | long_description=README,
16 | long_description_content_type="text/markdown",
17 | keywords=["static site generator", "jinja2", "markdown", "web development", "blog"],
18 | url="https://notablog.io",
19 | download_url="https://github.com/charludo/barely/archive/refs/tags/v1.2.2.tar.gz",
20 | author="Charlotte Hartmann Paludo",
21 | author_email="barely@charlotteharludo.com",
22 | license="GPL-3.0",
23 | packages=setuptools.find_packages(),
24 | include_package_data=True,
25 | zip_safe=False,
26 | entry_points={"console_scripts": ["barely = barely.cli:run"]},
27 | python_requires=">=3.9.0",
28 | install_requires=[
29 | "click>=8.0.0",
30 | "click-default-group>=1.2.2",
31 | "coloredlogs>=15.0.0",
32 | "mock>=4.0.0",
33 | "pyyaml>=5.3.0",
34 | "watchdog>=2.0.0",
35 | "pillow>=8.0.0",
36 | "GitPython>=3.0.0",
37 | "pygments>=2.5.0",
38 | "libsass>=0.21.0",
39 | "pysftp>=0.2.5",
40 | "livereload>=2.5.0",
41 | "binaryornot>=0.4.0",
42 | "jinja2>=3.0.0",
43 | "mistune>=2.0.0rc1",
44 | "calmjs>=3.3.0",
45 | ],
46 | classifiers=[
47 | "Development Status :: 5 - Production/Stable",
48 | "Intended Audience :: Developers",
49 | "Topic :: Text Processing :: Markup :: HTML",
50 | "Topic :: Text Processing :: General",
51 | "Topic :: Software Development :: Build Tools",
52 | "Topic :: Software Development",
53 | "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
54 | "Natural Language :: English",
55 | "Programming Language :: Python :: 3.9",
56 | "Environment :: Console",
57 | "Operating System :: OS Independent",
58 | ],
59 | )
60 |
--------------------------------------------------------------------------------