Compare commits
No commits in common. "main" and "add-newsletter" have entirely different histories.
main
...
add-newsle
5
.gitignore
vendored
|
@ -165,7 +165,4 @@ cython_debug/
|
|||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
.env.production
|
||||
|
||||
media/images/
|
||||
media/original_images/
|
||||
.env.production
|
2
.idea/iamkonstantin-web.iml
generated
|
@ -17,7 +17,7 @@
|
|||
<excludeFolder url="file://$MODULE_DIR$/env" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Python 3.12 virtualenv at ~/Developer/personal/iamkonstantin-web/venv" jdkType="Python SDK" />
|
||||
<orderEntry type="jdk" jdkName="Python 3.13 (iamkonstantin-web)" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
<component name="PyDocumentationSettings">
|
||||
|
|
2
.idea/misc.xml
generated
|
@ -3,5 +3,5 @@
|
|||
<component name="Black">
|
||||
<option name="sdkName" value="Python 3.12 (iamkonstantin-web)" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.12 virtualenv at ~/Developer/personal/iamkonstantin-web/venv" project-jdk-type="Python SDK" />
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.13 (iamkonstantin-web)" project-jdk-type="Python SDK" />
|
||||
</project>
|
|
@ -1 +0,0 @@
|
|||
python 3.12.8
|
7
Makefile
|
@ -1,9 +1,12 @@
|
|||
.PHONY: help bump publish
|
||||
VERSION = 1.9.15
|
||||
.PHONY: help build publish
|
||||
VERSION = 1.5.0
|
||||
|
||||
help:
|
||||
@perl -nle'print $& if m{^[a-zA-Z_-]+:.*?## .*$$}' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
|
||||
|
||||
build:
|
||||
@docker build -t code.headbright.be/konstantin/iamkonstantin:$(VERSION) .
|
||||
|
||||
publish:
|
||||
# something to try: --provenance=false
|
||||
@docker buildx build -t code.headbright.be/konstantin/iamkonstantin:$(VERSION) --platform linux/arm64 --push .
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
from wagtail.api import APIField
|
||||
from wagtail.blocks import (
|
||||
CharBlock,
|
||||
ChoiceBlock,
|
||||
|
@ -15,13 +14,6 @@ class ImageBlock(StructBlock):
|
|||
caption = CharBlock(required=False)
|
||||
attribution = CharBlock(required=False)
|
||||
|
||||
api_fields = [
|
||||
APIField('image'),
|
||||
# Adds a URL to a rendered thumbnail of the image to the API
|
||||
APIField('caption'),
|
||||
APIField('attribution'),
|
||||
]
|
||||
|
||||
class Meta:
|
||||
icon = "image"
|
||||
template = "base/blocks/image_block.html"
|
||||
|
|
|
@ -5,8 +5,6 @@ from urllib.parse import urlparse
|
|||
from base.indexnow import get_key
|
||||
import requests
|
||||
|
||||
from blog.models import BlogPage
|
||||
|
||||
|
||||
@hooks.register('after_publish_page')
|
||||
def after_publish_page(request, page):
|
||||
|
@ -16,9 +14,6 @@ def after_publish_page(request, page):
|
|||
if urlparse(page_url).hostname == "localhost":
|
||||
print("not notifying indexnow for localhost" + get_key() + ", page url: " + page_url)
|
||||
return
|
||||
if urlparse(page_url).path.endswith("--priv") or urlparse(page_url).path.endswith("--priv/"):
|
||||
print("not notifying indexnow for blog page --priv")
|
||||
return
|
||||
session = requests.Session()
|
||||
session.post(
|
||||
"https://api.indexnow.org/indexnow",
|
||||
|
|
|
@ -1,218 +0,0 @@
|
|||
# Generated by Django 5.1.6 on 2025-05-06 17:00
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
from home.models import HomePage
|
||||
|
||||
STRUCT_ORG_FIELDS = [
|
||||
"struct_org_type",
|
||||
"struct_org_name",
|
||||
"struct_org_logo_id",
|
||||
"struct_org_image_id",
|
||||
"struct_org_phone",
|
||||
"struct_org_address_street",
|
||||
"struct_org_address_locality",
|
||||
"struct_org_address_region",
|
||||
"struct_org_address_postal",
|
||||
"struct_org_address_country",
|
||||
"struct_org_geo_lat",
|
||||
"struct_org_geo_lng",
|
||||
"struct_org_hours",
|
||||
"struct_org_actions",
|
||||
"struct_org_extra_json", ]
|
||||
# put here every model names of yours that could have been filled with structured seo data;
|
||||
# order will matter when searching for pages data
|
||||
SEO_MODELS = [HomePage]
|
||||
|
||||
|
||||
def fill_settings_from_pages_struct_org(apps, schema_editor):
|
||||
"""
|
||||
Search for pages where seo struct info was filled and use that to
|
||||
fill new settings struct data
|
||||
"""
|
||||
|
||||
SeoSettings = apps.get_model("wagtailseo", "SeoSettings")
|
||||
Site = apps.get_model("wagtailcore", "Site")
|
||||
|
||||
for site in Site.objects.all().select_related("root_page"):
|
||||
for model_name in SEO_MODELS:
|
||||
model = apps.get_model("home", model_name)
|
||||
page = model.objects.filter(
|
||||
path__startswith=site.root_page.path,
|
||||
depth__gte=site.root_page.depth
|
||||
).order_by(
|
||||
'path'
|
||||
).exclude(struct_org_name__exact="").first()
|
||||
# if you are sure that only root pages were used to fill structured data,
|
||||
# you can directly use:
|
||||
# page = site.root_page.specific if site.root_page.specific._meta.model_name in SEO_MODELS else None
|
||||
if page is not None:
|
||||
seo_settings, _ = SeoSettings.objects.get_or_create(site=site)
|
||||
for field in STRUCT_ORG_FIELDS:
|
||||
setattr(seo_settings, field, getattr(page, field))
|
||||
seo_settings.save()
|
||||
break
|
||||
|
||||
|
||||
def fill_pages_from_settings_struct_org(apps, schema_editor):
|
||||
"""
|
||||
The reverse migration.
|
||||
|
||||
For every site, find the most top-level page inheriting from SeoMixin
|
||||
and fill its struct information using the site's settings
|
||||
"""
|
||||
SeoSettings = apps.get_model("wagtailseo", "SeoSettings")
|
||||
for seo_settings in SeoSettings.objects.all().select_related("site", "site__root_page"):
|
||||
for model_name in SEO_MODELS:
|
||||
model = apps.get_model("home", model_name)
|
||||
page = model.objects.filter(
|
||||
path__startswith=seo_settings.site.root_page.path,
|
||||
depth__gte=seo_settings.site.root_page.depth
|
||||
).order_by('path').first()
|
||||
if page is not None:
|
||||
for field in STRUCT_ORG_FIELDS:
|
||||
setattr(page, field, getattr(seo_settings, field))
|
||||
page.save()
|
||||
break
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('blog', '0014_alter_blogpage_body'),
|
||||
('wagtailimages', '0027_image_description'),
|
||||
("wagtailseo", "0003_seosettings_struct_org_fields"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='blogindexpage',
|
||||
name='struct_org_actions',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='blogindexpage',
|
||||
name='struct_org_address_country',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='blogindexpage',
|
||||
name='struct_org_address_locality',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='blogindexpage',
|
||||
name='struct_org_address_postal',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='blogindexpage',
|
||||
name='struct_org_address_region',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='blogindexpage',
|
||||
name='struct_org_address_street',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='blogindexpage',
|
||||
name='struct_org_extra_json',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='blogindexpage',
|
||||
name='struct_org_geo_lat',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='blogindexpage',
|
||||
name='struct_org_geo_lng',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='blogindexpage',
|
||||
name='struct_org_hours',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='blogindexpage',
|
||||
name='struct_org_image',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='blogindexpage',
|
||||
name='struct_org_logo',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='blogindexpage',
|
||||
name='struct_org_name',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='blogindexpage',
|
||||
name='struct_org_phone',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='blogindexpage',
|
||||
name='struct_org_type',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='blogpage',
|
||||
name='struct_org_actions',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='blogpage',
|
||||
name='struct_org_address_country',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='blogpage',
|
||||
name='struct_org_address_locality',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='blogpage',
|
||||
name='struct_org_address_postal',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='blogpage',
|
||||
name='struct_org_address_region',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='blogpage',
|
||||
name='struct_org_address_street',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='blogpage',
|
||||
name='struct_org_extra_json',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='blogpage',
|
||||
name='struct_org_geo_lat',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='blogpage',
|
||||
name='struct_org_geo_lng',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='blogpage',
|
||||
name='struct_org_hours',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='blogpage',
|
||||
name='struct_org_image',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='blogpage',
|
||||
name='struct_org_logo',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='blogpage',
|
||||
name='struct_org_name',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='blogpage',
|
||||
name='struct_org_phone',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='blogpage',
|
||||
name='struct_org_type',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='blogindexpage',
|
||||
name='og_image',
|
||||
field=models.ForeignKey(blank=True, help_text='Shown when linking to this page on social media. If blank, may show an image from the page, or the default from Settings > SEO.', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image', verbose_name='Preview image'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='blogpage',
|
||||
name='og_image',
|
||||
field=models.ForeignKey(blank=True, help_text='Shown when linking to this page on social media. If blank, may show an image from the page, or the default from Settings > SEO.', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image', verbose_name='Preview image'),
|
||||
),
|
||||
]
|
|
@ -5,8 +5,6 @@ from django.db import models
|
|||
from modelcluster.fields import ParentalKey, ParentalManyToManyField
|
||||
from modelcluster.contrib.taggit import ClusterTaggableManager
|
||||
from taggit.models import TaggedItemBase
|
||||
from wagtail.api import APIField
|
||||
from wagtail.images.api.fields import ImageRenditionField
|
||||
|
||||
from wagtail.models import Page, Orderable
|
||||
from wagtail.fields import RichTextField, StreamField
|
||||
|
@ -76,14 +74,6 @@ class BlogPage(SeoMixin, Page):
|
|||
else:
|
||||
return None
|
||||
|
||||
# Export fields over the API
|
||||
api_fields = [
|
||||
APIField('gallery_images'),
|
||||
# Adds a URL to a rendered thumbnail of the image to the API
|
||||
APIField('body'),
|
||||
APIField('intro'),
|
||||
APIField('tags'),
|
||||
]
|
||||
search_fields = Page.search_fields + [
|
||||
index.SearchField('intro'),
|
||||
index.SearchField('body'),
|
||||
|
@ -132,9 +122,6 @@ class BlogPageGalleryImage(Orderable):
|
|||
FieldPanel('image'),
|
||||
FieldPanel('caption'),
|
||||
]
|
||||
api_fields = [
|
||||
APIField('image'),
|
||||
]
|
||||
|
||||
|
||||
@register_snippet
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
{% block body_class %}template-blogpage{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="flex flex-col justify-center h-full px-0 md:px-4 lg:px-8">
|
||||
<section class="flex flex-col items-center justify-center h-full px-0 md:px-4 lg:px-8">
|
||||
<article class="mb-16 px-1 md:px-4 lg:px-8">
|
||||
<h1>{{ page.title }}</h1>
|
||||
|
||||
|
@ -54,12 +54,12 @@
|
|||
|
||||
{% with tags=page.tags.all %}
|
||||
{% if tags %}
|
||||
<div class="flex items-center justify-center gap-x-4 text-xs my-6">
|
||||
<div class="flex items-center gap-x-4 text-xs my-6">
|
||||
<div class="tags">
|
||||
<h2 class="sr-only">Tags</h2>
|
||||
<ul class="flex">
|
||||
{% for tag in tags %}
|
||||
<li><span class="emoji">🏷</span>
|
||||
<li class="space-x-2"><span class="emoji">🏷️</span>
|
||||
<a class="pr-2" href="{% slugurl 'tags' %}?tag={{ tag }}">{{ tag }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
@ -74,9 +74,13 @@
|
|||
|
||||
</article>
|
||||
|
||||
<nav class="w-full text-center px-2 lg:px-4 return">
|
||||
<nav class="w-full text-center px-2 lg:px-4">
|
||||
<a href="{{ page.get_parent.url }}" class="font-bold">Return to blog</a>
|
||||
</nav>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
{% include 'newsletter/snippets/signup_form.html' %}
|
||||
</section>
|
||||
{% endblock %}
|
|
@ -1,80 +0,0 @@
|
|||
# Generated by Django 5.1.6 on 2025-05-06 17:00
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('home', '0006_alter_homepage_body'),
|
||||
('wagtailimages', '0027_image_description'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='homepage',
|
||||
name='struct_org_actions',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='homepage',
|
||||
name='struct_org_address_country',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='homepage',
|
||||
name='struct_org_address_locality',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='homepage',
|
||||
name='struct_org_address_postal',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='homepage',
|
||||
name='struct_org_address_region',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='homepage',
|
||||
name='struct_org_address_street',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='homepage',
|
||||
name='struct_org_extra_json',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='homepage',
|
||||
name='struct_org_geo_lat',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='homepage',
|
||||
name='struct_org_geo_lng',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='homepage',
|
||||
name='struct_org_hours',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='homepage',
|
||||
name='struct_org_image',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='homepage',
|
||||
name='struct_org_logo',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='homepage',
|
||||
name='struct_org_name',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='homepage',
|
||||
name='struct_org_phone',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='homepage',
|
||||
name='struct_org_type',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='homepage',
|
||||
name='og_image',
|
||||
field=models.ForeignKey(blank=True, help_text='Shown when linking to this page on social media. If blank, may show an image from the page, or the default from Settings > SEO.', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image', verbose_name='Preview image'),
|
||||
),
|
||||
]
|
|
@ -9,13 +9,13 @@
|
|||
>
|
||||
<figure class="w-1/2" style="min-width: 24rem">
|
||||
<figcaption class="sr-only">
|
||||
Hero image
|
||||
</figcaption>
|
||||
<img src="/static/images/my-office.png" alt="Drawing of my computer desk where we see a computer with an open Terminal
|
||||
Drawing of my computer desk where we see a computer with an open Terminal
|
||||
with Elixir code snippets. On the shelf behind we can see programming
|
||||
books for Rust, Elixir and JavaScript. The books are arranges vertically
|
||||
next to a small flower pot. The flower has big green leaves. A cup of
|
||||
steaming coffee sits on the desk, to the left of the computer. The cup has a Python logo on it." />
|
||||
steaming coffee sits on the desk, to the left of the computer. The cup has a Python logo on it.
|
||||
</figcaption>
|
||||
<img src="/static/images/my-office.png" />
|
||||
</figure>
|
||||
<section class="w-100 text-lg dark:text-white mx-2">
|
||||
<h1
|
||||
|
@ -25,8 +25,8 @@
|
|||
><img
|
||||
width="48px"
|
||||
height="48px"
|
||||
alt="waving hand emoji"
|
||||
src="static/images/waving-hand-sign_1f44b.png" /></span>Hi! I'm Konstantin
|
||||
src="static/images/waving-hand-sign_1f44b.png" /></span
|
||||
>Hi! I'm Konstantin
|
||||
</h1>
|
||||
|
||||
</section>
|
||||
|
@ -50,7 +50,7 @@
|
|||
<ul class="mx-auto mt-8 grid max-w-2xl grid-cols-1 gap-x-8 gap-y-20 lg:mx-0 lg:max-w-none lg:grid-cols-3">
|
||||
{% for post in recent_blog_items %}
|
||||
{% with post=post.specific %}
|
||||
<li class="flex flex-col items-center justify-start max-w-xl">
|
||||
<article class="flex flex-col items-center justify-start max-w-xl">
|
||||
<div class="group relative w-full">
|
||||
<h3 class="">
|
||||
<a href="{% pageurl post %}">
|
||||
|
@ -60,7 +60,6 @@
|
|||
</h3>
|
||||
<p class="mt-5 leading-6">{{ post.intro }}</p>
|
||||
</div>
|
||||
|
||||
{% if post.main_image %}
|
||||
<div class="relative mx-auto">
|
||||
{% with post.main_image as main_image %}
|
||||
|
@ -70,7 +69,7 @@
|
|||
<div class="absolute inset-0 rounded-2xl ring-1 ring-inset ring-gray-900/10"></div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</li>
|
||||
</article>
|
||||
{% endwith %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
@ -82,4 +81,7 @@
|
|||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
{% include 'newsletter/snippets/signup_form.html' %}
|
||||
</section>
|
||||
{% endblock content %}
|
|
@ -1,15 +0,0 @@
|
|||
from wagtail.api.v2.views import PagesAPIViewSet
|
||||
from wagtail.api.v2.router import WagtailAPIRouter
|
||||
from wagtail.images.api.v2.views import ImagesAPIViewSet
|
||||
from wagtail.documents.api.v2.views import DocumentsAPIViewSet
|
||||
|
||||
# Create the router. "wagtailapi" is the URL namespace
|
||||
api_router = WagtailAPIRouter('wagtailapi')
|
||||
|
||||
# Add the three endpoints using the "register_endpoint" method.
|
||||
# The first parameter is the name of the endpoint (such as pages, images). This
|
||||
# is used in the URL of the endpoint
|
||||
# The second parameter is the endpoint class that handles the requests
|
||||
api_router.register_endpoint('pages', PagesAPIViewSet)
|
||||
api_router.register_endpoint('images', ImagesAPIViewSet)
|
||||
api_router.register_endpoint('documents', DocumentsAPIViewSet)
|
|
@ -35,7 +35,6 @@ INSTALLED_APPS = [
|
|||
"wagtail.contrib.settings",
|
||||
"wagtail.contrib.forms",
|
||||
"wagtail.contrib.redirects",
|
||||
"wagtail.contrib.simple_translation",
|
||||
"wagtail.embeds",
|
||||
"wagtail.sites",
|
||||
"wagtail.users",
|
||||
|
@ -44,9 +43,6 @@ INSTALLED_APPS = [
|
|||
"wagtail.images",
|
||||
"wagtail.search",
|
||||
"wagtail.admin",
|
||||
"wagtail.locales",
|
||||
"wagtail.api.v2",
|
||||
"rest_framework",
|
||||
"wagtail",
|
||||
"modelcluster",
|
||||
"taggit",
|
||||
|
@ -72,7 +68,6 @@ MIDDLEWARE = [
|
|||
"django.middleware.security.SecurityMiddleware",
|
||||
"wagtail.contrib.redirects.middleware.RedirectMiddleware",
|
||||
"blog.middleware.BlogRedirectMiddleware",
|
||||
"django.middleware.locale.LocaleMiddleware",
|
||||
"django_browser_reload.middleware.BrowserReloadMiddleware"
|
||||
]
|
||||
|
||||
|
@ -133,7 +128,7 @@ AUTH_PASSWORD_VALIDATORS = [
|
|||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/5.0/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = "en"
|
||||
LANGUAGE_CODE = "en-us"
|
||||
|
||||
TIME_ZONE = "UTC"
|
||||
|
||||
|
@ -141,18 +136,6 @@ USE_I18N = True
|
|||
|
||||
USE_TZ = True
|
||||
|
||||
WAGTAIL_I18N_ENABLED = True
|
||||
|
||||
USE_L10N = True # allows dates to be shown in the user's locale
|
||||
|
||||
WAGTAIL_CONTENT_LANGUAGES = LANGUAGES = [
|
||||
('en', "English"),
|
||||
('fr', "French"),
|
||||
('es', "Spanish"),
|
||||
('nl', "Dutch"),
|
||||
]
|
||||
|
||||
WAGTAILSIMPLETRANSLATION_SYNC_PAGE_TREE = False
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/5.0/howto/static-files/
|
||||
|
@ -203,9 +186,6 @@ WAGTAILSEARCH_BACKENDS = {
|
|||
# Base URL to use when referring to full URLs within the Wagtail admin backend -
|
||||
# e.g. in notification emails. Don't include '/admin' or a trailing slash
|
||||
WAGTAILADMIN_BASE_URL = "https://iamkonstantin.eu"
|
||||
WAGTAILAPI_BASE_URL = "https://iamkonstantin.eu"
|
||||
|
||||
WAGTAILAPI_SEARCH_ENABLED = True
|
||||
|
||||
WAGTAIL_CODE_BLOCK_LANGUAGES = (
|
||||
('bash', 'Bash/Shell'),
|
||||
|
|
|
@ -4,8 +4,7 @@
|
|||
<html lang="en" class="h-full antialiased">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<meta name="fediverse:creator" content="@iamkonstantin@mastodon.social" />
|
||||
<meta name="fediverse:creator" content="@konstantin@toot.iamkonstantin.eu" />
|
||||
{% include "wagtailseo/meta.html" %}
|
||||
<title>
|
||||
{% block title %}
|
||||
|
@ -81,6 +80,5 @@
|
|||
{# Override this in templates to add extra javascript #}
|
||||
{% endblock %}
|
||||
{% include "wagtailseo/struct_data.html" %}
|
||||
{% include "wagtailseo/struct_org_data.html" %}
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<a href="#main" class="skip-link">Skip to content</a>
|
||||
|
||||
{% get_site_root as site_root %}
|
||||
<nav class="w-full flex justify-center my-8 main">
|
||||
<nav class="w-full flex justify-center my-8">
|
||||
<ul class="flex space-x-8">
|
||||
<li><a href="{% pageurl site_root %}">{{ site_root.title }}</a></li>
|
||||
{% for menuitem in site_root.get_children.live.in_menu %}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
from django.conf import settings
|
||||
from django.conf.urls.i18n import i18n_patterns
|
||||
from django.urls import include, path
|
||||
from django.contrib import admin
|
||||
from django.views.generic.base import TemplateView
|
||||
|
@ -9,7 +8,6 @@ from wagtail import urls as wagtail_urls
|
|||
from wagtail.documents import urls as wagtaildocs_urls
|
||||
|
||||
from blog.feeds import RssBlogFeed
|
||||
from iamkonstantin_web.api import api_router
|
||||
from newsletter import views as newsletter_views
|
||||
from search import views as search_views
|
||||
from wagtail.contrib.sitemaps.views import sitemap
|
||||
|
@ -27,17 +25,6 @@ urlpatterns = [
|
|||
path('newsletter/thanks', newsletter_views.thanks, name='thanks')
|
||||
]
|
||||
|
||||
urlpatterns += [
|
||||
path('api/v2/', api_router.urls),
|
||||
]
|
||||
|
||||
# Translatable URLs
|
||||
# These will be available under a language code prefix. For example /en/search/
|
||||
urlpatterns += i18n_patterns(
|
||||
path("", include(wagtail_urls)),
|
||||
prefix_default_language=False,
|
||||
)
|
||||
|
||||
if settings.DEBUG:
|
||||
from django.conf.urls.static import static
|
||||
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
|
||||
|
@ -47,12 +34,12 @@ if settings.DEBUG:
|
|||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
urlpatterns += path("__reload__/", include("django_browser_reload.urls")),
|
||||
|
||||
# urlpatterns = urlpatterns + [
|
||||
# # For anything not caught by a more specific rule above, hand over to
|
||||
# # Wagtail's page serving mechanism. This should be the last pattern in
|
||||
# # the list:
|
||||
# path("", include(wagtail_urls)),
|
||||
# # Alternatively, if you want Wagtail pages to be served from a subpath
|
||||
# # of your site, rather than the site root:
|
||||
# # path("pages/", include(wagtail_urls)),
|
||||
# ]
|
||||
urlpatterns = urlpatterns + [
|
||||
# For anything not caught by a more specific rule above, hand over to
|
||||
# Wagtail's page serving mechanism. This should be the last pattern in
|
||||
# the list:
|
||||
path("", include(wagtail_urls)),
|
||||
# Alternatively, if you want Wagtail pages to be served from a subpath
|
||||
# of your site, rather than the site root:
|
||||
# path("pages/", include(wagtail_urls)),
|
||||
]
|
||||
|
|
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 106 KiB |
BIN
media/images/097d70b8f56ef0dbeaefd4a981ccc510.max-165x165.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
media/images/097d70b8f56ef0dbeaefd4a981ccc510.width-1600.png
Normal file
After Width: | Height: | Size: 1.5 MiB |
BIN
media/images/097d70b8f56ef0dbeaefd4a981ccc510.width-400.png
Normal file
After Width: | Height: | Size: 151 KiB |
BIN
media/images/097d70b8f56ef0dbeaefd4a981ccc510.width-800.png
Normal file
After Width: | Height: | Size: 472 KiB |
After Width: | Height: | Size: 5 KiB |
After Width: | Height: | Size: 942 B |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 4.8 KiB |
BIN
media/images/_508d3ac3-6905-4b23-b662-a3604050ac80.width-28.jpg
Normal file
After Width: | Height: | Size: 942 B |
BIN
media/images/moon_and_planets_design.2e16d0ba.fill-320x240.jpg
Normal file
After Width: | Height: | Size: 8.2 KiB |
BIN
media/images/moon_and_planets_design.2e16d0ba.fill-600x338.jpg
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
media/images/moon_and_planets_design.max-165x165.jpg
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
media/images/moon_and_planets_design.original.jpg
Normal file
After Width: | Height: | Size: 89 KiB |
BIN
media/images/pexels-life-of-pix-8892.2e16d0ba.fill-480x320.jpg
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
media/images/pexels-life-of-pix-8892.max-165x165.jpg
Normal file
After Width: | Height: | Size: 6.8 KiB |
BIN
media/images/pexels-life-of-pix-8892.max-800x600.jpg
Normal file
After Width: | Height: | Size: 89 KiB |
BIN
media/images/pexels-life-of-pix-8892.width-800.jpg
Normal file
After Width: | Height: | Size: 89 KiB |
BIN
media/original_images/097d70b8f56ef0dbeaefd4a981ccc510.png
Normal file
After Width: | Height: | Size: 1.5 MiB |
BIN
media/original_images/_508d3ac3-6905-4b23-b662-a3604050ac80.jpeg
Normal file
After Width: | Height: | Size: 94 KiB |
BIN
media/original_images/moon_and_planets_design.jpeg
Normal file
After Width: | Height: | Size: 208 KiB |
BIN
media/original_images/pexels-life-of-pix-8892.jpg
Normal file
After Width: | Height: | Size: 1.7 MiB |
|
@ -1,4 +1,4 @@
|
|||
<div class="py-16 sm:py-24 lg:py-32">
|
||||
<iframe data-w-type="embedded" title="Newsletter" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="https://sgx7y.mjt.lu/wgt/sgx7y/xtn1/form?c=c9b8015e" width="100%" style="height: 0;"></iframe>
|
||||
<iframe data-w-type="embedded" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="https://sgx7y.mjt.lu/wgt/sgx7y/xtn1/form?c=c9b8015e" width="100%" style="height: 0;"></iframe>
|
||||
<script type="text/javascript" src="https://app.mailjet.com/pas-nc-embedded-v1.js"></script>
|
||||
</div>
|
|
@ -4,14 +4,8 @@
|
|||
|
||||
{% block body_class %}template-portfolio{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<section class="flex flex-col items-center justify-center h-full px-0 md:px-4 lg:px-8">
|
||||
<article class="mb-16 px-1 md:px-4 lg:px-8">
|
||||
<h1>{{ page.title }}</h1>
|
||||
<div class="blog-content w-full">
|
||||
{{ page.body }}
|
||||
</div>
|
||||
</article>
|
||||
</section>
|
||||
<h1>{{ page.title }}</h1>
|
||||
|
||||
{{ page.body }}
|
||||
{% endblock %}
|
|
@ -1,8 +1,8 @@
|
|||
Django>=5.2.1,<5.3
|
||||
wagtail>=6.4,<7.1
|
||||
Django>=4.2,<5.2
|
||||
wagtail>=6.3,<6.4
|
||||
whitenoise>=6.6,<7.0
|
||||
wagtailcodeblock>=1.29.0.2,<2.0
|
||||
django-tailwind>=3.6.0
|
||||
django-browser-reload>=1.17
|
||||
django-browser-reload>=1.12
|
||||
Wand==0.6.13
|
||||
wagtail-seo==3.0.0
|
||||
wagtail-seo==2.5.0
|
|
@ -13,11 +13,13 @@
|
|||
<form action="{% url 'search' %}" method="get" class="container">
|
||||
<div class="flex flex-col space-y-4">
|
||||
<label for="query" class="sr-only block text-sm font-medium leading-6">Search</label>
|
||||
<div class="mt-2 flex space-x-4">
|
||||
<div class="mt-2">
|
||||
<input type="text" placeholder="Type search keywords..."
|
||||
class="block w-full rounded-xl border-0 py-1.5 px-2 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-klavender sm:text-sm sm:leading-6"
|
||||
class="block w-full rounded-md border-0 py-1.5 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-klavender sm:text-sm sm:leading-6"
|
||||
id="query" name="query"{% if search_query %} value="{{ search_query }}"{% endif %}>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input type="submit" class="primary-button" value="Search">
|
||||
</div>
|
||||
</div>
|
||||
|
@ -28,7 +30,7 @@
|
|||
</div>
|
||||
</section>
|
||||
|
||||
<section class="h-full px-0 md:px-4 lg:px-8 search">
|
||||
<section class="h-full px-0 md:px-4 lg:px-8">
|
||||
<div class="px-0 md:px-4 lg:px-8">
|
||||
<h2 class="sr-only">Search results</h2>
|
||||
|
||||
|
@ -46,8 +48,6 @@
|
|||
<h4><a href="{% pageurl result %}">{{ result }}</a></h4>
|
||||
{% if result.search_description %}
|
||||
{{ result.search_description }}
|
||||
{% elif result.post.intro %}
|
||||
{{ result.post.intro }}
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
}
|
||||
|
||||
h3 {
|
||||
@apply mt-10 mb-3 text-xl sm:text-2xl lg:text-2xl leading-none font-extrabold tracking-tight text-black dark:text-white;
|
||||
@apply mt-12 mb-2 text-xl sm:text-2xl lg:text-2xl leading-none font-extrabold tracking-tight text-black dark:text-white;
|
||||
}
|
||||
|
||||
h4 {
|
||||
|
@ -56,23 +56,11 @@
|
|||
@apply my-2 mb-3 text-lg leading-relaxed;
|
||||
}
|
||||
|
||||
li {
|
||||
@apply my-2 text-lg;
|
||||
}
|
||||
|
||||
.search li {
|
||||
@apply my-6;
|
||||
}
|
||||
|
||||
.search li h4 {
|
||||
@apply inline;
|
||||
}
|
||||
|
||||
.blog-content ul, .home-content ul {
|
||||
@apply list-none list-inside;
|
||||
}
|
||||
|
||||
.blog-content ul li::before, .home-content ul li::before, .search li::before {
|
||||
.blog-content ul li::before, .home-content ul li::before {
|
||||
content: "👉";
|
||||
font-family: NotoEmoji;
|
||||
@apply inline-block mr-2 py-1;
|
||||
|
@ -89,57 +77,24 @@
|
|||
|
||||
@media print {
|
||||
h1 {
|
||||
@apply m-0 my-2 text-xl;
|
||||
@apply mt-6 mb-4 text-xl;
|
||||
}
|
||||
|
||||
h2 {
|
||||
@apply m-0 mt-2 text-base;
|
||||
@apply mt-4 mb-2 text-base;
|
||||
}
|
||||
|
||||
h3 {
|
||||
@apply m-0 mt-2 text-base;
|
||||
}
|
||||
|
||||
h4 {
|
||||
@apply m-0 text-base;
|
||||
@apply text-base;
|
||||
}
|
||||
|
||||
p {
|
||||
@apply text-sm my-1;
|
||||
}
|
||||
|
||||
li {
|
||||
@apply text-sm my-0;
|
||||
}
|
||||
|
||||
.ppb {
|
||||
page-break-before: always;
|
||||
}
|
||||
|
||||
nav.main {
|
||||
@apply hidden;
|
||||
}
|
||||
|
||||
main {
|
||||
@apply mx-0 px-0;
|
||||
}
|
||||
|
||||
footer {
|
||||
@apply hidden !important;
|
||||
}
|
||||
|
||||
nav.return {
|
||||
@apply hidden;
|
||||
}
|
||||
|
||||
/*.blog-content ul, .home-content ul {*/
|
||||
/* @apply list-none list-inside list-disc;*/
|
||||
/* @apply inline-block mr-0;*/
|
||||
/*}*/
|
||||
|
||||
/*.blog-content ul li::before, .home-content ul li::before, .search li::before {*/
|
||||
/* content: "";*/
|
||||
/*}*/
|
||||
}
|
||||
|
||||
.primary-button {
|
||||
|
@ -157,11 +112,11 @@
|
|||
}
|
||||
|
||||
.tags ul {
|
||||
@apply flex-col sm:flex-row sm:space-x-2 text-center text-sm;
|
||||
@apply flex space-x-4 text-center text-sm;
|
||||
}
|
||||
|
||||
.tags li {
|
||||
@apply p-3 inline-flex items-center rounded-2xl px-2 py-1 text-sm text-black dark:text-white ring-1 ring-inset ring-orange-700/20 dark:ring-white bg-orange-50 dark:bg-transparent;
|
||||
@apply p-4 inline-flex items-center rounded-2xl bg-green-50 px-2 py-1 font-medium text-green-700 ring-1 ring-inset ring-green-600/20;
|
||||
}
|
||||
|
||||
.tags ul li::before {
|
||||
|
@ -170,7 +125,7 @@
|
|||
}
|
||||
|
||||
.tags a {
|
||||
@apply border-b-0 w-full m-0;
|
||||
@apply border-b-0 w-full;
|
||||
}
|
||||
|
||||
.blog-pages a {
|
||||
|
|