First commit

This commit is contained in:
Awiteb 2024-02-17 22:58:54 +03:00
commit e090a4bde5
No known key found for this signature in database
GPG key ID: 16C8AD0B49C39C88
27 changed files with 937 additions and 0 deletions

17
.github/workflows/cd.yml vendored Normal file
View file

@ -0,0 +1,17 @@
on:
push:
branches:
- master
jobs:
deploy:
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Build and deploy zola
uses: shalzz/zola-deploy-action@master
env:
PAGES_BRANCH: gh-pages
TOKEN: ${{ secrets.PAT }}
BUILD_THEMES: true

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
public

4
.gitmodules vendored Normal file
View file

@ -0,0 +1,4 @@
[submodule "themes/tabi"]
path = themes/tabi
url = https://github.com/TheAwiteb/tabi.git
branch = dev

3
README.md Normal file
View file

@ -0,0 +1,3 @@
## My personal blog (Arabic)
This is my personal blog, where I write about programming, open source, and other things that I find interesting. I write in Arabic

198
config.toml Normal file
View file

@ -0,0 +1,198 @@
base_url = "https://awiteb.is-a.dev"
default_language = "ar"
theme = "tabi"
author = "عويتب"
description = "المكان الذي اخرج فيه ما يجول في رأسي، وأحاول أن اقدم شيء مفيدة"
title = "~عويتب"
build_search_index = false
compile_sass = true
generate_feed = true
minify_html = false
taxonomies = [{name = "tags", feed = true}]
[markdown]
highlight_code = true
highlight_theme = "css"
render_emoji = true
smart_punctuation = true
[extra]
# Enable JavaScript theme toggler to allow users to switch between dark/light mode.
# If disabled, your site will only use the theme specified in the `default_theme` variable.
theme_switcher = false
# This setting determines the default theme on load ("light" or "dark").
# To default to the user's OS-level theme, leave it empty or unset.
default_theme = "dark"
# Choose the colourscheme (skin) for the theme. Default is "teal".
# Skin available: blue, lavender, mint, red, sakura, teal, monochrome, lowcontrast_orange, lowcontrast_peach, lowcontrast_pink, indigo_ingot, evangelion
# See them live and learn how to create your own: https://welpo.github.io/tabi/blog/customise-tabi/#skins
# WARNING! "lowcontrast" skins, while aesthetically pleasing, may not provide optimal
# contrast (in light theme) for readability and might not be suitable for all users.
# Furthermore, low contrasting elements will affect your Google Lighthouse rating.
# All other skins have optimal contrast.
skin = "monochrome"
# Set browser theme colour. Can be a single colour or [light, dark].
# Note: Bright colors may be ignored in dark mode.
# More details: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta/name/theme-color
browser_theme_color = "#032c35"
# List additional stylesheets to load site-wide.
# These stylesheets should be located in your site's `static` directory.
# Example: stylesheets = ["extra1.css", "path/extra2.css"]
# You can load a stylesheet for a single post by adding it to the [extra] section of the post's front matter, following this same format.
stylesheets = [
"blog.css",
]
# Show links to previous and next articles at the bottom of posts.
# Can be set at page or section levels, following the hierarchy: page > section > config. See: https://welpo.github.io/tabi/blog/mastering-tabi-settings/#settings-hierarchy
show_previous_next_article_links = true
# Invert order of the links to previous and next articles at the bottom of posts.
# By default, next articles are on the left side of the page and previous articles are on the right side.
# To reverse the order (next articles on the right and previous articles on the left), set it to true.
# Can be set at page or section levels, following the hierarchy: page > section > config. See: https://welpo.github.io/tabi/blog/mastering-tabi-settings/#settings-hierarchy
invert_previous_next_article_links = true
# Whether the navigation for previous/next article should match the full width of the site (same as the navigation bar at the top) or the article width.
# To match the navigation bar at the top, set it to true.
previous_next_article_links_full_width = false
# Enable a copyright notice for the footer, shown between socials and the "Powered by" text.
# $TITLE will be replaced by the website's title.
# $CURRENT_YEAR will be replaced by the current year.
# $AUTHOR will be replaced by the `author` variable.
# $SEPARATOR will be replaced by the `separator` variable.
# Markdown is supported (links, emphasis, etc).
copyright = "© $CURRENT_YEAR $AUTHOR $SEPARATOR المحتوى الموجود في هذا الموقع متاح بموجب ترخيص [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/deed.ar)."
# Remote repository for your Zola site.
# Used for `show_remote_changes` and `show_remote_source` (see below).
# Supports GitHub, GitLab, Gitea, and Codeberg.
remote_repository_url = "https://github.com/TheAwiteb/TheAwiteb.github.io"
# Set this to "auto" to try and auto-detect the platform based on the repository URL.
# Accepted values are "github", "gitlab", "gitea", and "codeberg".
remote_repository_git_platform = "github"
# Branch in the repo hosting the Zola site.
remote_repository_branch = "master"
# Show a link to the commit history of updated posts, right next to the last updated date.
# Can be set at page or section levels, following the hierarchy: page > section > config. See: https://github.com/welpo/tabi/pull/128
show_remote_changes = true
# Show a link to the repository of the site, right next to the "Powered by Zola & tabi" text.
show_remote_source = true
# Add a "copy" button to codeblocks (loads ~700 bytes of JavaScript).
# Can be set at page or section levels, following the hierarchy: page > section > config. See: https://github.com/welpo/tabi/pull/128
copy_button = true
# Show the reading time of a page.
# Can be set at page or section levels, following the hierarchy: page > section > config. See: https://github.com/welpo/tabi/pull/128
show_reading_time = true
# Adds backlinks to footnotes (loads ~500 bytes of JavaScripts).
# Can be set at page or section levels, following the hierarchy: page > section > config. See: https://github.com/welpo/tabi/pull/128
footnote_backlinks = true
# Enable KaTeX for all posts.
# Can be set at page or section levels, following the hierarchy: page > section > config. See: https://github.com/welpo/tabi/pull/128
katex = false
# Quick navigation buttons.
# Adds "go up" and "go to comments" buttons on the bottom right (hidden for mobile).
# Can be set at page or section levels, following the hierarchy: page > section > config. See: https://github.com/welpo/tabi/pull/128
quick_navigation_buttons = false
# Date format used when listing posts (main page, /blog section, tag posts list…)
# Default is "6th July 2049" in English and "%d %B %Y" in other languages.
long_date_format = "%d %B %Y"
# Date format used for blog posts.
# Default is "6th July 2049" in English and "%-d %B %Y" in other languages.
short_date_format = "%d %B %Y"
# Custom separator used in title tag and posts metadata (between date, time to read, and tags).
separator = "•"
# Use a shorter layout for All tags listing.
# Default: tag_name n post[s]
# Compact: tag_name^n (superscript number)
compact_tags = false
# Invert the order of the site title and page title in the browser tab.
# Example: true => "Blog • ~/tabi", false => "~/tabi • Blog"
invert_title_order = true
# Full path after the base URL required. So if you were to place it in "static" it would be "/favicon.png"
favicon = "/favicon.png"
# Path to the fallback image for social media cards (the image shown when sharing a link on WhatsApp, LinkedIn…).
# Read more: https://nikitahl.com/html-meta-tags-for-social-media
# Using an image representative of each post is recommended. You can do so by setting this variable in the [extra] section of a post's front matter.
# If a post doesn't have a social media card, the site's default, below, will be used.
social_media_card = "img/social_cards/index.jpg"
menu = [
{name = "blog", url = "blog/"},
{name = "archive", url = "archive/"},
{name = "projects", url = "projects/"},
]
# Extra menu to show on the footer, below socials section.
footer_menu = [
{url = "about", name = "about", trailing_slash = true},
{url = "pgp", name = "pgp", trailing_slash = true},
]
# The RSS icon will be shown if (1) it's enabled and (2) the following variable is set to true.
feed_icon = true
# Email address for footer's social section.
# Protect against spambots:
# 1. Use base64 for email (convert at https://www.base64encode.org/ or `printf 'your@email.com' | base64`).
# 2. Or, set 'encode_plaintext_email' to true for auto-encoding (only protects on site, not in public repos).
email = "QXdpdGViQHBtLm1l"
# Decoding requires ~400 bytes of JavaScript. If JS is disabled, the email won't be displayed.
encode_plaintext_email = false # Setting is ignored if email is already encoded.
# The icons available can be found in "social_icons" in the "static" folder
socials = [
{name = "github", url = "https://github.com/TheAwiteb/", icon = "github"},
{name = "telegram", url = "https://t.me/TheAwiteb", icon = "telegram"},
{name = "Bassam", url = "https://bassam.social/@me", icon = "mastodon"},
]
# Custom security headers. What urls should your website be able to connect to?
# You need to specify the CSP and the URLs associated with the directive.
# Useful if you want to load remote content safely (embed YouTube videos, which needs frame-src, for example).
# Default directive is self.
# Default config, allows for https remote images and embedding YouTube and Vimeo content.
# This configuration (along with the right webserver settings) gets an A+ in Mozilla's Observatory: https://observatory.mozilla.org
allowed_domains = [
{directive = "font-src", domains = ["'self'", "data:"]},
{directive = "img-src", domains = ["'self'", "https://*", "data:"]},
{directive = "script-src", domains = ["'self'"]},
{directive = "style-src", domains = ["'self'"]},
{directive = "frame-src", domains = ["player.vimeo.com", "https://www.youtube-nocookie.com"]},
]
# Custom subset of characters for the header.
# If set to true, the `static/custom_subset.css` file will be loaded first.
# This avoids a flashing text issue in Firefox.
# Please see https://welpo.github.io/tabi/blog/custom-font-subset/ to learn how to create this file.
custom_subset = false
[extra.utterances]
automatic_loading = true
dark_theme = "photon-dark"
enabled_for_all_posts = true
issue_term = "slug"
label = "💬"
lazy_loading = true
light_theme = "github-light"
repo = "TheAwiteb/TheAwiteb.github.io"

16
content/_index.md Normal file
View file

@ -0,0 +1,16 @@
+++
path = "/"
title = "اخر التدوينات"
sort_by = "date"
template = "section.html"
insert_anchor_links = "left"
[extra]
header = {title = "اهلآ، انا عويتب", img = "/img/awiteb-profile.png"}
section_path = "blog/_index.md"
social_media_card = "img/social_cards/index.jpg"
max_posts = 4
+++
طالب جامعي وهاوي في عالم البرمجة محب للغة Rust وشغوف بالبرمجيات الحرة. هنا اخرج ما يجول في رأسي، وأحاول أن اقدم شيء مفيدة

View file

@ -0,0 +1,7 @@
+++
title = "الأرشيف"
path = "archive"
template = "archive.html"
[extra]
+++

11
content/blog/_index.md Normal file
View file

@ -0,0 +1,11 @@
+++
paginate_by = 5
path = "/blog"
title = "تدويناتي"
sort_by = "date"
template = "section.html"
insert_anchor_links = "left"
[extra]
+++
<!-- social_media_card = "img/social_cards/blog.jpg" -->

7
content/pages/_index.md Normal file
View file

@ -0,0 +1,7 @@
+++
render = false
insert_anchor_links = "left"
[extra]
hide_from_feed = true
+++

View file

@ -0,0 +1,46 @@
+++
title = "عني"
template = "info-page.html"
path = "about"
[extra]
social_media_card = "/img/social_cards/about.jpg"
+++
<details>
<summary>المواضيع</summary>
<!-- toc -->
</details>
بسم الله الرحمن الرحيم، السلام عليكم ورحمة الله وبركاته. اهلآ بكم في مدونتي، هنا ستتعرف عن المدونة وعن الذي يُقدم بها.
## من انا؟
أنا عويتب طالب جامعي في جامعة الملك عبد العزيز أدرس الإعلام ولم أتخصص به بعد، مساهم في البرمجيات الحرة ومفتوحة المصدر ومحب للغة البرمجة Rust.
## المحتوى المُقدم
هنا سوف اكتب مايدور في رأسي وأحاول ان يكون ذو قيمة، في الغالب مايدور في رأسي يتعلق بالبرمجيات و لغة Rust.
## كيف تم بناء المدونة
هذه المدونة مفتوحة المصدر، يمكنك الوصول إلى شِفرتها البرمجية من هنا [TheAwiteb/TheAwiteb.github.io](https://github.com/TheAwiteb/TheAwiteb.github.io) وهذا يعني انه يمكنك بناء مدونتك اليوم بعد اشتقاقها. تستخدم المدونة مُنشى المواقع الثابتة Zola وتستخدم السمة tabi.
### مكان إستضافتها
يتم استضافتها على [GitHub Pages](https://pages.github.com/) وهي خدمة مقدمة من GitHub لإستضافة المواقع التي لاتتطلب خادم لتعمل.
### النطاق المُستخدم
تستخدم المدونة نطاق [is-a.dev](https://is-a.dev) وهو نطاق مقدم من [Akshay Nair](https://github.com/phenax) إلى العامة لأخذ نطاق فرعي منه بشكل مجاني، لمعرفة المزيد يمكنك إلقاء نظرة على صفحة النطاق.
## طرق التواصل معي
طريقة التواصل التي ستستخدمها للتواصل معي تعتمد على سبب التواصل، وهذه هي الطرق ويجب عليك اختيار طريقة التواصل الصحيحة بناءً على سبب تواصل.
### تعليقات المدونة
لكل تدوينة يوجد خانة تعليقات في أسفلها، ستستخدمها إذا كان سبب التواصل هو الإستفسار عن شيء يخص التدوينة نفسها ولا يخرج عن نطاقها.
### البريد الإلكتروني
تواصل معي عبر البريد الالكتروني إذا كان موضوعك لايتطلب الدردشة ويمكن الرد عليه برسالة واحدة. تجد البريد الخاص بي أسفل جميع صفحات المدونة، وستجد ايضآ مفتاح الـ PGP الخاص بي، ومن المهم أن تستخدمه عند إرسالك رسالة لي، [للمزيد](https://awiteb.is-a.dev/pgp/).
### التيليجرام
تواصل معي عبر تطبيق التيليجرام إذا كان موضوعك يتطلب الدردشة ولايمكن الرد عليه برسالة واحدة. ستجد التيليجرام الخاص بي أسفل جميع صفحات المدونة
## الختام
اتمنى ان تنال المدونة على اعجابكم والسلام عليكم ورحمة الله وبركاتة.

View file

@ -0,0 +1,80 @@
+++
title = "مفتاح الـPGP"
template = "info-page.html"
path = "pgp"
[extra]
social_media_card = "/img/social_cards/pgp.jpg"
+++
للتواصل معي بشكل آمن هذا هو مفتاح الـPGP العام الخاص بي:
<details>
<summary>مفتاح الـPGP</summary>
<div dir=ltr>
```plaintext
-----BEGIN PGP PUBLIC KEY BLOCK-----
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
=oSci
-----END PGP PUBLIC KEY BLOCK-----
```
</div>
</details>
## إذا كان بريدك لايدعم الـPGP
إذا كان بريدك الإلكتروني لايدعم ال PGP، يمكنك كتابة رسالتك هنا في [pgp.help] وإرسالها مشفرة وسأقوم بالرد عليك
## تنويه قبل الإرسال
يُفضل إرفاق مفتاحك العام مع الرسالة، ليمكنني الرد عليك بشكل آمن
[pgp.help]: https://pgp.help/#/permalink?pgp=-----BEGIN%20PGP%20PUBLIC%20KEY%20BLOCK-----%0D%0AVersion:%20OpenPGP.js%20v1.5.7%0D%0AComment:%20https:~2F~2Fpgp.help%0D%0A%0D%0AxsFNBGW4vgsBEADhexvUlbMz7X0QecUZgwvpzc1KHRSBTFS5Wgdz1xhOPMnY%0AT11zTLlxRpBdAaGXxJyaxavQqYyi9eqGqGvIhTVUM3zpixAydhPM1Z0gR6Sv%0Ave9AqGjJQDrLwRBujHd0YF8ke4xVHkkaYJGIIWbMp~2FyTVj4InhppBMi6rWRM%0AZyKyOIIuFvPh1IsMxlinEOec8G7~2FCQy8y1v1Js9PakQrSBJMaxRIOuZSacir%0AZU5qUFbthikUynyJ%2BjrEJMwKIg2jeWuDdO2b0928iKj5hOSEu96KOQYuLGbo%0A3VQGMi3PwFZAhrjdeF8Mb5a2BDbPGbtG3u9bhpLTYvrbdD0urStsMXUxVDfO%0AAnt1UQNkrfjhYcNlPyvcMZRbQFgy2nwGminot~2Fi1nEsFHEcYMow2d8sGJYC9%0Ai7A6oC2V4LwAuFzWZ68qgYebBllp7bkAwK7JMgYKRBmxBJD%2B5CP4ihVjcuM9%0AyZr10kFBBqGQUVOLGtEnshXUmJ1iB%2BzYwXBIEVn0cSe1MGUvLIgKb~2F0APItG%0AJJD%2B1hJDnr9RQPDtSGTH67rT3~2FMGQtjR6YiTm7AVCvkbrTx6nBT7jtCs27my%0AL2FT5TrlOTZhBcNrzcczempvJ~2FC%2BQJWIlbMxG1OC77kbi4kZI0ru0cHT3Lhi%0A7PzPIk~2FuZ%2Ba1oX4EAIgIYKVRZlQhD98gDLYMjwARAQABzRVBd2l0ZWIgPEF3%0AaXRlYkBwbS5tZT7CwY4EEwEKADgWIQQEp7LcqFGCpldziCQWyK0LScOciAUC%0AZbi%2BCwIbAwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRAWyK0LScOciATu%0AEACAJdT~2F8w5MOi8lda~2FS2QVCURKhj0HPcHl0Nb7%2BZuhB~2FZvYCM55wFl86eq2%0AvUTdO11FC567TXMbMjxeM~2F4UvPPUdeR6q%2Bvwi4Xvepq0JW0QN8qa9DX3FRfK%0ANonoZKN~2FKwpgdRwuecw1OeaSfZvjkWZjpRIZJG~2F0d5HlLZIPRHLgRCmd2tPq%0AZNV79evijqQnhHaw8AKB9cNGaCUpVp%2BRzfEo7DYd4U2~2FpP%2BI5f7qYq2gjEK3%0Ah1a34Qz0g0nH8q77KKZYyCyXMqa%2BmYWC3~2F0Y6MfRRCoG6NxP6A9SdQAw~2FGu7%0AsfxsprUqT~2FXkGeohcpLcB1AcFOR5fD6FAmEgmmz~2FtNDBKffAl07NnrytibUc%0AESbVO0qSB%2B~2FibO14~2Fi1BlU~2F4JGKLfOrnx9%2BV76gI8shnyAOFDwlj6wPQDrK~2F%0ABUKGjY48T~2FyEkWGiiiV16fUBNp%2Bp2RDIdRX4wEX6ThBzjQXwf43isWTm9HN~2F%0Aarl~2FheD9VFHkbiUQrTXq8qLUl2hqNqmPodLO8~2FyIoJuQIVZBppOAkLHpM%2B6t%0AsLw~2Fcou31msXrwp~2FV76vJzP1bVWsa8bEW%2BjRBiR59fF4gePmULBo~2Fn%2BhoMxP%0AfsQq4~2FG8%2B%2BrvhinBWqjgubmEJDrG0qrELbCj1~2F1C~2F1Uw5labutKFDlHrfw2c%0ALHVHPz0lV6tXatXb0iRDcLcLNM7BTQRluL4LARAArgd~2F9Zp0aBpjfiOHVG47%0ADNnINDk5fkGkhleVBb1HPnCObiQFQte~2FcwJJzkZ4sPu%2BD0Xv88usYCcBwav5%0A4QcBTiQHj6ixjzQz7P%2BD7LHEBLkoj9XJHB9fFXKdt23t2YQ4L2xvh62tlds2%0AyZ4bIy3YehOc%2B%2Bc0USmaRLIxFfCDuPe7nzSKPWtro5T7V1ndRJVxtw8igqzL%0AongALvoNY8i~2F%2BtghuVqcvSXDYnmVJx4ciuI%2BwP0~2FUf0yERJfEXIbSEQaypRY%0A%2BmTZ66zLQG2PU0upywWFmdgRt%2BZumLmb5UTTqYNwBdaRQWg3E3OlFeFGUFKd%0AkeB0NX74Ig1HA~2F%2BFlT9w6Omf9U9OeQdrH4nrNYME0GYb5uskxFbe5omrHrA~2F%0AxCfDVsp65kJSztxtD6VKUwijydmRtfWMtudIkihFhXMd1v0E%2BL%2BIBPkC4neN%0AJJzEqya8xOUocR2NNbQbd7WrzuHnKoXMnJKsVFyaJs3mevZNaCLx1M3rKf6O%0ALKT2y~2F94nx%2BreU%2BaTJ67GPHvoWJiXyaa2eRE1HJ78t1YiZpJh1x0YvXIwofP%0At6XCpAgD75QrvCglVqClKcNAXv~2FwZeC0hWUCqLNuwZ8IX93pOZAP23vNmtji%0Ai~2FgH5QNXRaaJtc%2Ba8ivDr8iPqcUJeph46dM9Mkd25iZ%2BFhw5t5rx621dTFaa%0ALDcAEQEAAcLBdgQYAQoAIBYhBASnstyoUYKmV3OIJBbIrQtJw5yIBQJluL4L%0AAhsMAAoJEBbIrQtJw5yIM5EP~2F2pwGwgKpxPcTOFpAnIqqeR87i~2F8yDe~2FRY%2Bl%0AXgFEmwQhRxWIBPJihISQNCF32Jw47uYxzZG~2Farjim~2FYwVmqHFFqoV02sYOxx%0AYzDUkwGQfoIumO4awb5MbrImr08TPyTi65mUXvJTVWJAQus19QZEF0162c6N%0Acxo7Cuiiujt3h96RdtiH8dY%2BCMYHERfwYi3NP1Yy8LMHeyRjXLOn%2B5vsbnaj%0Ag1aZybFU57psCzfh0nLbdXnlJU08euORPs3Gq%2BsVW60JF9jfKdsMQX0TMVNu%0ACfS3ZtFDPXtrGJcZd%2BsTqYO%2BO2rwtj0vNmaVe892RMGFmHeMhNEa3KZEFngJ%0AykjTSFR~2FzOw%2BRtCwTNdOkH3ZlyC7tC0oFi86mGbiS8zY1TyRvfnbLVcy8D0y%0AmjQ9J35ZTK8HmdQhU1cD04ES1avfO5xksgZITY3Nwahld9PudXPRJcppQUUR%0AoZjK7E28%2BUOBemW~2Fsm%2BO7%2BWjIHAWi0FVfUA6Y~2FcLY87tUDAh4hKjyE0Fg%2BIw%0AsICIu%2BtsCf965AldFEKVsuTwUhLuJInEHEQcWlGoZBfkkxbJl%2BNWWtuEKF08%0A8Z~2FNq3GTmbF2KTKBG1jMWOVRUBtl1rNdh8%2BGhfpVMPvdYTBg0L5Sk~2Fl8Qki7%0AaO8qIJtqbB8SmKxXEU1kiIALlOGmPkHMASb30SrB1zftwZLw%0D%0A%3DJTtY%0D%0A-----END%20PGP%20PUBLIC%20KEY%20BLOCK-----%0D%0A%0D%0A

View file

@ -0,0 +1,9 @@
+++
title = "مشاريعي"
sort_by = "weight"
template = "cards.html"
insert_anchor_links = "left"
[extra]
social_media_card = "/img/social_cards/projects.jpg"
+++

View file

@ -0,0 +1,32 @@
+++
title = "الوسيطة salvo-captcha"
description = "وسيطة (middleware) في إطار العمل salvo تمكنك من إضافة CAPTCHA إلى تطبيقك بسهولة"
weight = 30
[extra]
local_image = "/img/projects/salvo-captcha/banner.png"
canonical_url = "https://awiteb.is-a.dev/projects/doteki/"
+++
<!-- social_media_card = "social_cards/projects_doteki.jpg" -->
هي وسيطة تساعدك على اضافة CAPTCHA إلى تطبيقك بسهولة.
## مثال
![المثال](/img/projects/salvo-captcha/example.gif)
## طريقة إستخدامها
اولآ عليك ان تضيفها في `Cargo.toml` كالتالي:
<div dir=ltr>
```toml
[dependencies]
# ...
salvo-captcha = "0.1"
```
</div>
.... لم يتم الانتهاء من الكتابة

197
scripts/article.py Executable file
View file

@ -0,0 +1,197 @@
#!/usr/bin/env python3
# Script to manage articles.
#
# Current functionalities:
# - Update the header of an article or add a new header (command: update)
#
# License: MIT License
# Copyright: (c) 2024 Awiteb <Awiteb@pm.me>
import sys
import os
import datetime
def help(command: str | None) -> str:
if command is None:
help_message = """Script to manage articles.
Usage: ./scripts/article.py <command>
Commands:
- update: Update the header of an article or add a new header
- new: Create a new article
- card: Create a new social card for an article
- help: Folloing by a command, it will display the help of the command
"""
elif command == "update":
help_message = """Update the header of an article or add a new header
Usage: ./scripts/article.py update <article_name> <header_scope> <header_name> <header_value>
Example: ./scripts/article.py update "hello-world" global "title" "Hello World"
Header scopes: [global, taxonomies, extra]
"""
elif command == "new":
help_message = """Create a new article
Usage: ./scripts/article.py new <article_name>
Example: ./scripts/article.py new "hello-world"
"""
elif command == "card":
help_message = """Create a new social card for an article
Usage: ./scripts/article.py card <article_name>
Example: ./scripts/article.py card "hello-world"
"""
else:
help_message = f"The command `{command}` is not recognized. Available commands: [update, help]"
return help_message
def now() -> str:
"""Return the current date in the format "YYYY-MM-DD"""
return datetime.datetime.now().strftime("%Y-%m-%d")
class Article:
def __init__(self, name: str) -> None:
self.name = name
self.path = f"content/blog/{name}/index.md"
self.headers = {
"global": {},
"taxonomies": {},
"extra": {},
}
self.content = ""
self.__reload_header()
self.__reload_content()
def __reload_header(self) -> None:
"""Reloads the content of the article
The header of the article is between the first and second "+++" in the file.
"""
if not os.path.exists(self.path):
return
with open(self.path, "r") as file:
current_header = "global"
for idx, line in enumerate(file):
if line == "+++\n" and idx > 0:
break
if (line == "+++\n" and idx == 0) or line.isspace():
continue
# Update the current header if the line is a header
if line.startswith("[") and line.endswith("]\n"):
current_header = line[1:-2]
continue
if len((splited_line := line.split("="))) == 2:
key, value = (splited_line[0], splited_line[1])
self.headers[current_header][key.strip()] = value.strip()
def __reload_content(self) -> None:
"""Reloads the content of the article"""
if not os.path.exists(self.path):
return
with open(self.path, "r") as file:
content = file.read()
# Remove the header from the file, and keep the rest as the content of the article
self.content = content[content.index("+++", 3) + 3 :].strip() + "\n"
def export(self) -> None:
"""Rewrite the file with the updated headers and content"""
if not os.path.exists(self.path):
os.makedirs(os.path.dirname(self.path), exist_ok=True)
with open(self.path, "w") as file:
file.write("+++\n")
for scope, headers in self.headers.items():
if scope != "global":
file.write(f"[{scope}]\n")
for key, value in headers.items():
file.write(f"{key} = {value}\n")
file.write("\n")
file.write("+++\n")
file.write(self.content)
def update_header(
article_name: str, header_scope: str, header_name: str, header_value: str
) -> None:
if header_scope not in ["global", "taxonomies", "extra"]:
print(
f"The header scope {header_scope} is not recognized. Available scopes: [global, taxonomies, extra]"
)
sys.exit(1)
article = Article(article_name)
if (
article.headers[header_scope].get(header_name) is not None
and article.headers[header_scope][header_name].startswith('"')
) or all([c.isalpha() or c.isspace() for c in header_value]):
header_value = f'"{header_value}"'
article.headers[header_scope][header_name] = header_value
article.export()
print("The header has been updated successfully.")
def create_new_article(article_name: str) -> None:
article = Article(article_name)
if os.path.exists(article.path):
print(f"The article {article_name} already exists.")
sys.exit(1)
article.headers["global"]["title"] = '""'
article.headers["global"]["description"] = '""'
article.headers["global"]["date"] = now()
article.headers["global"]["updated"] = now()
article.headers["global"]["draft"] = "true"
article.headers["taxonomies"]["tags"] = '["tag1", "tag2"]'
article.content = "Write your content here."
article.export()
print(f"The article {article_name} has been created successfully.")
print(f"Path: {article.path}")
def create_social_card(article_name: str) -> None:
article = Article(article_name)
if not os.path.exists(article.path):
print(f"The article {article_name} does not exist.")
sys.exit(1)
os.system(
f"./scripts/social-cards-zola -i blog/{article_name}/index.md -b http://127.0.0.1:1111 -o static/img/social_cards/"
)
print(f"The social card for the article {article_name} has been created successfully.")
print(f"The url path is: /img/social_cards/blog_{article_name.replace('-', '_')}.jpg")
def main() -> None:
# Check the current working directory, it should be the root of the project
if not (
"content" in os.listdir()
and "scripts" in os.listdir()
and "static" in os.listdir()
):
print("The script should be executed from the root of the project.")
sys.exit(1)
if len(sys.argv) < 2:
print(help(None))
sys.exit(1)
elif sys.argv[1] == "help" and len(sys.argv) == 3:
print(help(sys.argv[2]))
elif sys.argv[1] == "update" and len(sys.argv) == 6:
article_name, header_scope, header_name, header_value = sys.argv[2:]
update_header(article_name, header_scope, header_name, header_value)
elif sys.argv[1] == "new" and len(sys.argv) == 3:
create_new_article(sys.argv[2])
elif sys.argv[1] == "card" and len(sys.argv) == 3:
create_social_card(sys.argv[2])
else:
print(help(None))
sys.exit(1)
sys.exit(0)
if __name__ == "__main__":
main()

228
scripts/social-cards-zola Executable file
View file

@ -0,0 +1,228 @@
#!/usr/bin/env bash
set -eo pipefail
# This script takes a markdown post, crafts the corresponding URL, checks if it's accessible,
# takes a screenshot, and saves it to a specified location.
# It can update the front matter of the post with the path to the generated image (-u | --update-front-matter option).
# It's meant to be used as a pre-commit hook to generate social media cards for Zola sites using the tabi theme.
# More details: https://osc.garden/blog/automating-social-media-cards-zola/
function help_function(){
echo "This script automates the creation of social media cards for Zola websites."
echo "It takes a Markdown post and saves its live screenshot to a specified location."
echo ""
echo "IMPORTANT! It needs to be run from the root of the Zola site."
echo ""
echo "Usage: social-cards-zola [OPTIONS]"
echo ""
echo "Options:"
echo " -h, --help Show this help message and exit."
echo " -b, --base_url URL The base URL where the Zola site is hosted. Default is http://127.0.0.1:1111."
echo " -i, --input INPUT_PATH The relative path to the markdown file of the post/section you want to capture. Should be in the format 'content/blog/post_name.language.md'."
echo " -k, --key KEY The front matter key to update. Default is 'social_media_card'."
echo " -o, --output_path PATH The directory where the generated image will be saved."
echo " -p, --print_output Print the path to the resulting screenshot at the end."
echo " -u, --update-front-matter Update or add the 'social_media_card' key in the front matter of the Markdown file."
echo
echo "Examples:"
echo " social-cards-zola --base_url https://example.com --input content/blog/my_post.md --output_path static/img/social_cards"
echo " social-cards-zola -u -b http://127.0.0.1:1025 -i content/archive/_index.es.md -o static/img"
exit 0
}
function convert_filename_to_url() {
# Remove .md extension.
local post_name="${1%.md}"
# Remove "content/" prefix.
local url="${post_name#content/}"
# Extract language code.
local lang_code="${url##*.}"
if [[ "$lang_code" == "$url" ]]; then
lang_code="" # No language code.
else
lang_code="${lang_code}/" # Add trailing slash.
url="${url%.*}" # Remove the language code from the URL.
fi
# Handle co-located index.md by stripping it and using the directory as the URL.
if [[ "$url" == */index ]]; then
url="${url%/*}" # Remove the /index suffix.
fi
# Remove "_index" suffix.
if [[ "$url" == *"_index"* ]]; then
url="${url%%_index*}"
fi
# Return the final URL with a single trailing slash.
full_url="${lang_code}${url}"
echo "${full_url%/}/"
}
function error_exit() {
echo "ERROR: $1" >&2
exit "${2:-1}"
}
function validate_input_params() {
missing_params=()
if [[ -z "$base_url" ]]; then
missing_params+=("base_url")
fi
if [[ -z "$input" ]]; then
missing_params+=("input")
fi
if [[ -z "$output_path" ]]; then
missing_params+=("output_path")
fi
if [ ${#missing_params[@]} -ne 0 ]; then
error_exit "The following required settings are missing: ${missing_params[*]}. Use -h or --help for usage."
fi
}
function check_dependencies() {
for cmd in "curl" "shot-scraper"; do
if ! command -v $cmd &> /dev/null; then
error_exit "$cmd could not be found. Please install it."
fi
done
}
function fetch_status() {
local retry_count=0
local max_retries=5
local status
while [[ $retry_count -lt $max_retries ]]; do
status=$(curl -s -o /dev/null -I -w "%{http_code}" "${base_url}${post_url}")
if [[ "$status" -eq "200" ]]; then
return
fi
retry_count=$((retry_count + 1))
sleep 2
done
error_exit "Post $input is not accessible. Max retries ($max_retries) reached."
}
function capture_screenshot() {
temp_file=$(mktemp /tmp/social-zola.XXXXXX)
trap 'rm -f "$temp_file"' EXIT
shot-scraper --silent "${base_url}/${post_url}" -w 700 -h 400 --retina --quality 60 -o "$temp_file"
}
function move_file() {
local safe_filename=$(echo "${post_url%/}" | sed 's/[^a-zA-Z0-9]/_/g')
# Create the output directory if it doesn't exist.
mkdir -p "$output_path"
image_filename="${output_path}/${safe_filename:-index}.jpg" # If the filename is empty, use "index".
mv "$temp_file" "$image_filename" || error_exit "Failed to move the file to $image_filename"
}
function update_front_matter {
local md_file_path="$1"
local image_output="${2#static/}"
# Temporary file for awk processing
temp_awk=$(mktemp /tmp/frontmatter.XXXXXX)
awk -v card_path="$image_output" '
# Initialize flags for tracking state.
BEGIN { in_extra=done=front_matter=extra_exists=0; }
# Function to insert the social_media_card path.
function insert_card() { print "social_media_card = \"" card_path "\""; done=1; }
{
# If card has been inserted, simply output remaining lines.
if (done) { print; next; }
# Toggle front_matter flag at its start, denoted by +++
if (/^\+\+\+/ && front_matter == 0) {
front_matter = 1;
print "+++";
next;
}
# Detect [extra] section and set extra_exists flag.
if (/^\[extra\]/) { in_extra=1; extra_exists=1; print; next; }
# Update existing social_media_card.
if (in_extra && /^social_media_card =/) { insert_card(); in_extra=0; next; }
# End of front matter or start of new section.
if (in_extra && (/^\[[a-zA-Z_-]+\]/ || (/^\+\+\+/ && front_matter == 1))) {
insert_card(); # Add the missing social_media_card.
in_extra=0;
}
# Insert missing [extra] section.
if (/^\+\+\+/ && front_matter == 1 && in_extra == 0 && extra_exists == 0) {
print "\n[extra]";
insert_card();
in_extra=0;
front_matter = 0;
print "+++";
next;
}
# Print all other lines as-is.
print;
}' "$md_file_path" > "$temp_awk"
# Move the temporary file back to the original markdown file.
mv "$temp_awk" "$md_file_path"
}
function main() {
while [[ "$#" -gt 0 ]]; do
case "$1" in
-h|--help)
help_function;;
-b|--base_url)
base_url="$2"
shift 2;;
-i|--input)
input="$2"
shift 2;;
-o|--output_path)
output_path="$2"
shift 2;;
-k|--key)
front_matter_key="$2"
shift 2;;
-u|--update-front-matter)
update="true"
shift 1;;
-p|--print_output)
print_output="true"
shift 1;;
*)
error_exit "Unknown option: $1";;
esac
done
validate_input_params
check_dependencies
: "${base_url:="http://127.0.0.1:1111"}"
: "${front_matter_key:="social_media_card"}"
base_url="${base_url%/}/" # Ensure one trailing slash.
post_url="$(convert_filename_to_url "$input")"
fetch_status
capture_screenshot
move_file
if [[ "$update" == "true" ]]; then
update_front_matter "$input" "$image_filename"
fi
if [[ "$print_output" == "true" ]]; then
echo "$image_filename"
fi
}
main "$@"

14
scripts/update-posts.nu Executable file
View file

@ -0,0 +1,14 @@
#! /usr/bin/env nu
def main () {
let paths: list<string> = (git diff --name-only | split row "\n" | filter {|p| $p | str contains "content/blog"});
let posts: string = (ls content/blog | filter {|p| $p.type == "dir"} | each {|p| $p.name | sed 's/content\/blog\///g'});
for post in $posts {
if $"content/blog/($post)/index.md" in $paths {
echo $"`($post)` post has been modified";
python3 ./scripts/article.py card $post
python3 ./scripts/article.py update $post "global" "updated" $"(date now | format date '%Y-%m-%d')"
}
}
}

66
static/blog.css Normal file
View file

@ -0,0 +1,66 @@
@font-face {
font-family: "pfdintextuniversal-regular-1";
src: url(fonts/pfdintextuniversal-regular-1.otf) format("opentype");
}
* {
font-family: "pfdintextuniversal-regular-1" !important;
}
body,
html {
background: #0f1315 !important;
}
::selection {
background: #16222d !important;
color: #e0e0e0 !important;
}
p {
code {
background: #1a2127 !important;
color: #488bc9;
}
}
pre {
background: #121920 !important;
}
a.card {
background: #151a1d !important;
}
a.card:hover {
background: #1a2127 !important;
}
.cards .card:hover .card-description {
color: #D4D4D4 !important;
}
h2,
h3,
h4,
h5,
h6 {
color: #bbb7b7;
}
p {
color: #d3d3d3;
}
a {
color: #436b8c;
}
a:hover {
color: #325069;
background-color: #0f1315;
}
img#banner-home-img {
padding: 0.3rem;
}

BIN
static/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

1
themes/tabi Submodule

@ -0,0 +1 @@
Subproject commit 8537e301f7656870255f9066aed9aa874f7c168d