picata 0.0.1__py3-none-any.whl → 0.0.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. README.md +7 -57
  2. manage.py +1 -1
  3. picata/__init__.py +1 -1
  4. picata/apps.py +6 -6
  5. picata/blocks.py +3 -2
  6. picata/helpers/wagtail.py +1 -1
  7. picata/middleware.py +1 -1
  8. picata/migrations/0001_initial.py +77 -211
  9. picata/models.py +6 -5
  10. picata/settings/base.py +15 -15
  11. picata/settings/dev.py +4 -2
  12. picata/templates/picata/base.html +3 -3
  13. picata/templates/picata/previews/theme_gallery.html +1 -1
  14. picata/templatetags/absolute_static.py +1 -1
  15. picata/templatetags/menu_tags.py +26 -12
  16. picata/transformers.py +1 -1
  17. picata/typing/__init__.py +1 -1
  18. picata/typing/wagtail.py +1 -1
  19. picata/urls.py +2 -2
  20. picata/views.py +3 -2
  21. picata/wagtail_hooks.py +1 -1
  22. picata/wsgi.py +2 -2
  23. picata-0.0.2.dist-info/METADATA +37 -0
  24. picata-0.0.2.dist-info/RECORD +73 -0
  25. picata/migrations/0002_alter_article_content_alter_basicpage_content.py +0 -112
  26. picata/migrations/0003_alter_article_content_alter_basicpage_content.py +0 -104
  27. picata/migrations/0004_alter_article_content_alter_basicpage_content.py +0 -105
  28. picata/migrations/0005_socialsettings.py +0 -48
  29. picata/migrations/0006_alter_article_content.py +0 -71
  30. picata/migrations/0007_splitviewpage.py +0 -69
  31. picata/migrations/0008_alter_splitviewpage_content.py +0 -96
  32. picata/migrations/0009_alter_splitviewpage_content.py +0 -111
  33. picata/migrations/0010_alter_splitviewpage_content.py +0 -105
  34. picata/migrations/0011_alter_splitviewpage_options_and_more.py +0 -113
  35. picata/migrations/0012_alter_splitviewpage_content.py +0 -109
  36. picata/migrations/0013_alter_article_content.py +0 -43
  37. picata/migrations/0014_alter_article_content_alter_article_summary.py +0 -24
  38. picata/migrations/0015_alter_article_options_article_tagline_and_more.py +0 -28
  39. picata/migrations/0016_alter_article_options_alter_articletag_options_and_more.py +0 -33
  40. picata/migrations/0017_articletagrelation_alter_article_tags_and_more.py +0 -35
  41. picata/migrations/0018_rename_articletag_pagetag_and_more.py +0 -21
  42. picata/migrations/0019_rename_name_plural_articletype__name_plural.py +0 -18
  43. picata/migrations/0020_rename__name_plural_articletype__pluralised_name.py +0 -18
  44. picata/migrations/0021_rename_article_type_article_page_type.py +0 -18
  45. picata/migrations/0022_homepage.py +0 -28
  46. picata-0.0.1.dist-info/METADATA +0 -87
  47. picata-0.0.1.dist-info/RECORD +0 -94
  48. {picata-0.0.1.dist-info → picata-0.0.2.dist-info}/WHEEL +0 -0
  49. {picata-0.0.1.dist-info → picata-0.0.2.dist-info}/licenses/LICENSE.md +0 -0
README.md CHANGED
@@ -1,59 +1,9 @@
1
- # Ada's website
1
+ # Picata
2
2
 
3
- Wherein I set up a little website, and learn a bunch of stuff as I go.
3
+ **This project is very much pre-alpha**
4
4
 
5
- ## What it's made of
6
-
7
- ### Inside the box
8
-
9
- - [Wagtail](https://wagtail.org) (on [Django](https://www.djangoproject.com)) is the web framework
10
- <!-- - [Tailwind CSS](https://tailwindcss.com) for styling -->
11
-
12
- ### Holding things together
13
-
14
- - [UV](https://github.com/astral-sh/uv) for all Python project management
15
- - [Just](https://just.systems) as a command runner
16
- - [OpenTofu](https://opentofu.org) for DevOps
17
- - [Postgres](https://www.postgresql.org) for the database
18
- - [Docker](https://www.docker.com) for local development
19
-
20
- ## Quickstart
21
-
22
- ### Requirements
23
-
24
- - On a Mac:
25
-
26
- ```shell
27
- brew install colima docker
28
- ```
29
-
30
- ### Run a development server
31
-
32
- ```shell
33
- just tofu workspace select dev
34
- just tofu apply
35
- ```
36
-
37
- This will spin up a box on DigitalOcean using the settings defined in
38
- [infra/variables.tf](infra/variables.tf), and create a DNS A record at
39
- (workspace).for.(tld), (i.e. dev.for.hpk.io) pointing to the box. The variables
40
- `do_token` and `ssh_fingerprint` should be defined in
41
- [infra/secrets.tfvars](infra/secrets.tfvars). Workspace-specific variables are
42
- defined in infra/envs/(workspace).tfvars; e.g.
43
- [infra/envs/dev.tfvars](infra/envs/dev.tfvars) defines the 'tags' list for the
44
- box as `[development]` and sets `cloud_init_config` to point to the
45
- [cloud-init](https://cloud-init.io) script
46
- [config/cloud-init-dev.yml](config/cloud-init-dev.yml).
47
-
48
- The development cloud-init script will:
49
-
50
- - Install the system packages [`just`](https://just.systems), [`zsh`](https://www.zsh.org),
51
- [`gunicorn`](https://gunicorn.org), and `tree`
52
- - Create a 'wagtail' user, with UID 1500
53
- - Create the 'ada' user, and:
54
- - install their SSH public keys,
55
- - install their dotfiles,
56
- - add them to the 'sudo' and 'wagtail' groups
57
- - Install [Node](http://nodejs.org) on the system, from the `TF_VAR_NODE_VERSION`
58
- defined in [.env](.env)
59
- - Checkout this repository into `/app`, setting the owner and group to 'wagtail'.
5
+ Picata is the CMS & blog application I've forked off from my personal website.
6
+ It's effectively a ton of pre-made Wagtail "stuff" (models, views, templatetags,
7
+ middleware, hooks, etc.), made generic enough that you can include this app
8
+ in your `INSTALLED_APPS` and have a CMS/blog up-and-running without having to
9
+ spend weeks or months tailoring Wagtail to your needs.
manage.py CHANGED
@@ -5,7 +5,7 @@ from os import environ
5
5
  from sys import argv
6
6
 
7
7
  if __name__ == "__main__":
8
- environ.setdefault("DJANGO_SETTINGS_MODULE", "hpk.settings.dev")
8
+ environ.setdefault("DJANGO_SETTINGS_MODULE", "picata.settings.dev")
9
9
 
10
10
  if len(argv) >= 2: # noqa: PLR2004
11
11
  environ.setdefault("DJANGO_MANAGEMENT_COMMAND", argv[1])
picata/__init__.py CHANGED
@@ -1 +1 @@
1
- """Main "umbrella" package for custom code running hpk.io."""
1
+ """Top-level package for the Picata project."""
picata/apps.py CHANGED
@@ -1,13 +1,13 @@
1
- """Application configuration for the hpk Django app."""
1
+ """Application configuration for the Picata Django app."""
2
2
 
3
3
  from django.apps import AppConfig
4
4
 
5
5
 
6
6
  class Config(AppConfig):
7
- """Configuration class for the hpk Django application."""
7
+ """Configuration class for the Picata Django application."""
8
8
 
9
9
  default_auto_field = "django.db.models.BigAutoField"
10
- name = "hpk"
10
+ name = "picata"
11
11
 
12
12
  def ready(self) -> None:
13
13
  """Configure Wagtail admin with custom models, and register document transformers."""
@@ -15,13 +15,13 @@ class Config(AppConfig):
15
15
  # Register the 'custom article type' model with the Wagtail admin
16
16
  from wagtail_modeladmin.options import modeladmin_register
17
17
 
18
- from hpk.models import ArticleTypeAdmin
18
+ from picata.models import ArticleTypeAdmin
19
19
 
20
20
  modeladmin_register(ArticleTypeAdmin)
21
21
 
22
22
  # Add document transformers to the HTMLProcessingMiddleware
23
- from hpk.middleware import HTMLProcessingMiddleware
24
- from hpk.transformers import AnchorInserter, add_heading_ids
23
+ from picata.middleware import HTMLProcessingMiddleware
24
+ from picata.transformers import AnchorInserter, add_heading_ids
25
25
 
26
26
  ## Add ids to all headings missing them within html > body > main
27
27
  HTMLProcessingMiddleware.add_transformer(add_heading_ids)
picata/blocks.py CHANGED
@@ -3,8 +3,6 @@
3
3
  import pygments
4
4
  from django.forms import CharField
5
5
  from django.utils.html import format_html
6
- from hpk.typing.wagtail import BlockRenderContext, BlockRenderValue
7
- from hpk.validators import HREFValidator
8
6
  from pygments import formatters, lexers
9
7
  from pygments.util import ClassNotFound
10
8
  from wagtail.blocks import (
@@ -20,6 +18,9 @@ from wagtail.blocks import (
20
18
  )
21
19
  from wagtail.images.blocks import ImageChooserBlock
22
20
 
21
+ from picata.typing.wagtail import BlockRenderContext, BlockRenderValue
22
+ from picata.validators import HREFValidator
23
+
23
24
 
24
25
  class HREFField(CharField):
25
26
  """Custom field for href attributes (i.e. URLs but also schemes like 'mailto:')."""
picata/helpers/wagtail.py CHANGED
@@ -8,7 +8,7 @@ from django.http import HttpRequest
8
8
  from wagtail.models import Page
9
9
  from wagtail.query import PageQuerySet
10
10
 
11
- from hpk.models import TaggedPage
11
+ from picata.models import TaggedPage
12
12
 
13
13
  from . import get_models_of_type
14
14
 
picata/middleware.py CHANGED
@@ -8,7 +8,7 @@ from typing import ClassVar
8
8
  from django.http import HttpRequest, HttpResponse
9
9
  from lxml import etree
10
10
 
11
- from hpk.helpers import make_response
11
+ from picata.helpers import make_response
12
12
 
13
13
  logger = logging.getLogger(__name__)
14
14
 
@@ -1,264 +1,130 @@
1
- # Generated by Django 5.1.4 on 2024-12-19 01:11
1
+ # Generated by Django 5.1.5 on 2025-01-24 00:18
2
2
 
3
3
  import django.db.models.deletion
4
4
  import modelcluster.contrib.taggit
5
5
  import modelcluster.fields
6
+ import wagtail.contrib.routable_page.models
6
7
  import wagtail.fields
7
8
  from django.db import migrations, models
8
9
 
9
10
 
10
11
  class Migration(migrations.Migration):
12
+
11
13
  initial = True
12
14
 
13
15
  dependencies = [
14
- ("taggit", "0006_rename_taggeditem_content_type_object_id_taggit_tagg_content_8fc721_idx"),
15
- ("wagtailcore", "0094_alter_page_locale"),
16
+ ('wagtailcore', '0094_alter_page_locale'),
17
+ ('wagtailimages', '0027_image_description'),
16
18
  ]
17
19
 
18
20
  operations = [
19
21
  migrations.CreateModel(
20
- name="Article",
22
+ name='ArticleType',
23
+ fields=[
24
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
25
+ ('name', models.CharField(help_text='Name of the article type.', max_length=100, unique=True)),
26
+ ('_Pluralised_name', models.CharField(blank=True, help_text="Plural form of the article type name (optional). Defaults to appending 's'.", max_length=100)),
27
+ ('slug', models.SlugField(max_length=100, unique=True)),
28
+ ('description', models.TextField(blank=True, help_text='Optional description of this type.')),
29
+ ],
30
+ ),
31
+ migrations.CreateModel(
32
+ name='BasicPage',
21
33
  fields=[
22
- (
23
- "page_ptr",
24
- models.OneToOneField(
25
- auto_created=True,
26
- on_delete=django.db.models.deletion.CASCADE,
27
- parent_link=True,
28
- primary_key=True,
29
- serialize=False,
30
- to="wagtailcore.page",
31
- ),
32
- ),
33
- (
34
- "summary",
35
- wagtail.fields.RichTextField(
36
- blank=True, help_text="A short summary, or tagline for the article."
37
- ),
38
- ),
39
- (
40
- "content",
41
- wagtail.fields.StreamField(
42
- [("section", 5), ("image", 3)],
43
- blank=True,
44
- block_lookup={
45
- 0: (
46
- "wagtail.blocks.CharBlock",
47
- (),
48
- {"help_text": "Heading for this section.", "required": True},
49
- ),
50
- 1: (
51
- "wagtail.blocks.IntegerBlock",
52
- (),
53
- {
54
- "help_text": "Heading level",
55
- "max_value": 6,
56
- "min_value": 1,
57
- "required": True,
58
- },
59
- ),
60
- 2: (
61
- "wagtail.blocks.RichTextBlock",
62
- (),
63
- {
64
- "features": [
65
- "bold",
66
- "italic",
67
- "link",
68
- "ul",
69
- "ol",
70
- "document-link",
71
- ]
72
- },
73
- ),
74
- 3: ("wagtail.images.blocks.ImageChooserBlock", (), {}),
75
- 4: (
76
- "wagtail.blocks.StreamBlock",
77
- [[("rich_text", 2), ("image", 3)]],
78
- {
79
- "help_text": "Content blocks for this section.",
80
- "required": False,
81
- },
82
- ),
83
- 5: (
84
- "wagtail.blocks.StructBlock",
85
- [[("heading", 0), ("level", 1), ("content", 4)]],
86
- {},
87
- ),
88
- },
89
- help_text="Main content for the article.",
90
- ),
91
- ),
34
+ ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')),
35
+ ('content', wagtail.fields.StreamField([('rich_text', 0), ('code', 3), ('image', 4)], blank=True, block_lookup={0: ('wagtail.blocks.RichTextBlock', (), {}), 1: ('wagtail.blocks.TextBlock', (), {'help_text': None, 'required': True}), 2: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('python', 'Python'), ('javascript', 'JavaScript'), ('html', 'HTML'), ('css', 'CSS'), ('bash', 'Bash'), ('plaintext', 'Plain Text')], 'required': False}), 3: ('wagtail.blocks.StructBlock', [[('code', 1), ('language', 2)]], {}), 4: ('wagtail.images.blocks.ImageChooserBlock', (), {})}, help_text='Main content for the page.')),
92
36
  ],
93
37
  options={
94
- "verbose_name": "Article",
95
- "verbose_name_plural": "Articles",
38
+ 'abstract': False,
96
39
  },
97
- bases=("wagtailcore.page",),
40
+ bases=('wagtailcore.page',),
98
41
  ),
99
42
  migrations.CreateModel(
100
- name="ArticleTag",
43
+ name='HomePage',
101
44
  fields=[
102
- (
103
- "id",
104
- models.BigAutoField(
105
- auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
106
- ),
107
- ),
108
- ("name", models.CharField(max_length=100, unique=True, verbose_name="name")),
109
- (
110
- "slug",
111
- models.SlugField(
112
- allow_unicode=True, max_length=100, unique=True, verbose_name="slug"
113
- ),
114
- ),
45
+ ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')),
46
+ ('top_content', wagtail.fields.StreamField([('rich_text', 0), ('image', 1), ('icon_link_lists', 11)], blank=True, block_lookup={0: ('wagtail.blocks.RichTextBlock', (), {}), 1: ('picata.blocks.WrappedImageChooserBlock', (), {}), 2: ('wagtail.blocks.CharBlock', (), {'help_text': 'Optional heading for this list (e.g., Social Links).', 'required': False}), 3: ('wagtail.blocks.IntegerBlock', (), {'default': 2, 'help_text': 'Heading level for the list (1-6).', 'max_value': 6, 'min_value': 1, 'required': False}), 4: ('picata.blocks.HREFBlock', (), {'help_text': 'An optional link field', 'max_length': 255, 'required': False}), 5: ('wagtail.blocks.CharBlock', (), {'help_text': 'The title for the list item.', 'max_length': 50, 'required': True}), 6: ('wagtail.blocks.CharBlock', (), {'help_text': 'Id of the icon in the static/icons.svg file.', 'max_length': 255, 'required': False}), 7: ('wagtail.blocks.StructBlock', [[('href', 4), ('label', 5), ('icon', 6)]], {}), 8: ('wagtail.blocks.ListBlock', (7,), {'help_text': 'The list of items.'}), 9: ('wagtail.blocks.StructBlock', [[('heading', 2), ('heading_level', 3), ('items', 8)]], {}), 10: ('wagtail.blocks.StreamBlock', [[('link_list', 9)]], {'help_text': 'Add one or more heading-and-link-list blocks.', 'required': False}), 11: ('wagtail.blocks.StructBlock', [[('lists', 10)]], {})}, help_text="Content stream above 'Recent posts'")),
47
+ ('bottom_content', wagtail.fields.StreamField([('rich_text', 0), ('image', 1), ('icon_link_lists', 11)], blank=True, block_lookup={0: ('wagtail.blocks.RichTextBlock', (), {}), 1: ('picata.blocks.WrappedImageChooserBlock', (), {}), 2: ('wagtail.blocks.CharBlock', (), {'help_text': 'Optional heading for this list (e.g., Social Links).', 'required': False}), 3: ('wagtail.blocks.IntegerBlock', (), {'default': 2, 'help_text': 'Heading level for the list (1-6).', 'max_value': 6, 'min_value': 1, 'required': False}), 4: ('picata.blocks.HREFBlock', (), {'help_text': 'An optional link field', 'max_length': 255, 'required': False}), 5: ('wagtail.blocks.CharBlock', (), {'help_text': 'The title for the list item.', 'max_length': 50, 'required': True}), 6: ('wagtail.blocks.CharBlock', (), {'help_text': 'Id of the icon in the static/icons.svg file.', 'max_length': 255, 'required': False}), 7: ('wagtail.blocks.StructBlock', [[('href', 4), ('label', 5), ('icon', 6)]], {}), 8: ('wagtail.blocks.ListBlock', (7,), {'help_text': 'The list of items.'}), 9: ('wagtail.blocks.StructBlock', [[('heading', 2), ('heading_level', 3), ('items', 8)]], {}), 10: ('wagtail.blocks.StreamBlock', [[('link_list', 9)]], {'help_text': 'Add one or more heading-and-link-list blocks.', 'required': False}), 11: ('wagtail.blocks.StructBlock', [[('lists', 10)]], {})}, help_text="Content stream rendered under 'Recent posts'")),
115
48
  ],
116
49
  options={
117
- "verbose_name": "Article Tag",
118
- "verbose_name_plural": "Article Tags",
50
+ 'verbose_name': 'home page',
119
51
  },
52
+ bases=('wagtailcore.page',),
120
53
  ),
121
54
  migrations.CreateModel(
122
- name="ArticleType",
55
+ name='PageTag',
123
56
  fields=[
124
- (
125
- "id",
126
- models.BigAutoField(
127
- auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
128
- ),
129
- ),
130
- (
131
- "name",
132
- models.CharField(
133
- help_text="Name of the article type.", max_length=100, unique=True
134
- ),
135
- ),
136
- (
137
- "name_plural",
138
- models.CharField(
139
- blank=True,
140
- help_text="Plural form of the article type name (optional). Defaults to appending 's'.", # noqa: E501
141
- max_length=100,
142
- ),
143
- ),
144
- ("slug", models.SlugField(max_length=100, unique=True)),
145
- (
146
- "description",
147
- models.TextField(blank=True, help_text="Optional description of this type."),
148
- ),
57
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
58
+ ('name', models.CharField(max_length=100, unique=True, verbose_name='name')),
59
+ ('slug', models.SlugField(allow_unicode=True, max_length=100, unique=True, verbose_name='slug')),
149
60
  ],
61
+ options={
62
+ 'abstract': False,
63
+ },
150
64
  ),
151
65
  migrations.CreateModel(
152
- name="BasicPage",
66
+ name='PostGroupPage',
153
67
  fields=[
154
- (
155
- "page_ptr",
156
- models.OneToOneField(
157
- auto_created=True,
158
- on_delete=django.db.models.deletion.CASCADE,
159
- parent_link=True,
160
- primary_key=True,
161
- serialize=False,
162
- to="wagtailcore.page",
163
- ),
164
- ),
165
- (
166
- "content",
167
- wagtail.fields.StreamField(
168
- [("rich_text", 0), ("image", 1)],
169
- blank=True,
170
- block_lookup={
171
- 0: ("wagtail.blocks.RichTextBlock", (), {}),
172
- 1: ("wagtail.images.blocks.ImageChooserBlock", (), {}),
173
- },
174
- help_text="Main content for the page.",
175
- ),
176
- ),
68
+ ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')),
69
+ ('intro', wagtail.fields.RichTextField(blank=True, help_text='An optional introduction to this group.')),
177
70
  ],
178
71
  options={
179
- "verbose_name": "Basic Page",
180
- "verbose_name_plural": "Basic Pages",
72
+ 'verbose_name': 'post listing',
73
+ 'verbose_name_plural': 'post listings',
181
74
  },
182
- bases=("wagtailcore.page",),
75
+ bases=(wagtail.contrib.routable_page.models.RoutablePageMixin, 'wagtailcore.page'),
183
76
  ),
184
77
  migrations.CreateModel(
185
- name="PostGroupPage",
78
+ name='SplitViewPage',
186
79
  fields=[
187
- (
188
- "page_ptr",
189
- models.OneToOneField(
190
- auto_created=True,
191
- on_delete=django.db.models.deletion.CASCADE,
192
- parent_link=True,
193
- primary_key=True,
194
- serialize=False,
195
- to="wagtailcore.page",
196
- ),
197
- ),
198
- (
199
- "intro",
200
- wagtail.fields.RichTextField(
201
- blank=True, help_text="An optional introduction to this group."
202
- ),
203
- ),
80
+ ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')),
81
+ ('content', wagtail.fields.StreamField([('rich_text', 0), ('code', 3), ('image', 4), ('icon_link_lists', 14)], blank=True, block_lookup={0: ('wagtail.blocks.RichTextBlock', (), {}), 1: ('wagtail.blocks.TextBlock', (), {'help_text': None, 'required': True}), 2: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('python', 'Python'), ('javascript', 'JavaScript'), ('html', 'HTML'), ('css', 'CSS'), ('bash', 'Bash'), ('plaintext', 'Plain Text')], 'required': False}), 3: ('wagtail.blocks.StructBlock', [[('code', 1), ('language', 2)]], {}), 4: ('picata.blocks.WrappedImageChooserBlock', (), {}), 5: ('wagtail.blocks.CharBlock', (), {'help_text': 'Optional heading for this list (e.g., Social Links).', 'required': False}), 6: ('wagtail.blocks.IntegerBlock', (), {'default': 2, 'help_text': 'Heading level for the list (1-6).', 'max_value': 6, 'min_value': 1, 'required': False}), 7: ('picata.blocks.HREFBlock', (), {'help_text': 'An optional link field', 'max_length': 255, 'required': False}), 8: ('wagtail.blocks.CharBlock', (), {'help_text': 'The title for the list item.', 'max_length': 50, 'required': True}), 9: ('wagtail.blocks.CharBlock', (), {'help_text': 'Id of the icon in the static/icons.svg file.', 'max_length': 255, 'required': False}), 10: ('wagtail.blocks.StructBlock', [[('href', 7), ('label', 8), ('icon', 9)]], {}), 11: ('wagtail.blocks.ListBlock', (10,), {'help_text': 'The list of items.'}), 12: ('wagtail.blocks.StructBlock', [[('heading', 5), ('heading_level', 6), ('items', 11)]], {}), 13: ('wagtail.blocks.StreamBlock', [[('link_list', 12)]], {'help_text': 'Add one or more heading-and-link-list blocks.', 'required': False}), 14: ('wagtail.blocks.StructBlock', [[('lists', 13)]], {})}, help_text='Main content for the split-view page.')),
204
82
  ],
205
83
  options={
206
- "verbose_name": "Post Group",
207
- "verbose_name_plural": "Post Groups",
84
+ 'verbose_name': 'split-view page',
85
+ 'verbose_name_plural': 'split-view pages',
208
86
  },
209
- bases=("wagtailcore.page",),
87
+ bases=('wagtailcore.page',),
210
88
  ),
211
89
  migrations.CreateModel(
212
- name="ArticleTagItem",
90
+ name='Article',
213
91
  fields=[
214
- (
215
- "id",
216
- models.BigAutoField(
217
- auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
218
- ),
219
- ),
220
- (
221
- "content_object",
222
- modelcluster.fields.ParentalKey(
223
- on_delete=django.db.models.deletion.CASCADE,
224
- related_name="tagged_items",
225
- to="hpk.article",
226
- ),
227
- ),
228
- (
229
- "tag",
230
- models.ForeignKey(
231
- on_delete=django.db.models.deletion.CASCADE,
232
- related_name="%(app_label)s_%(class)s_items",
233
- to="taggit.tag",
234
- ),
235
- ),
92
+ ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')),
93
+ ('tagline', models.CharField(blank=True, help_text='A short tagline for the article.')),
94
+ ('summary', wagtail.fields.RichTextField(blank=True, help_text='A summary to be displayed in previews.')),
95
+ ('content', wagtail.fields.StreamField([('rich_text', 0), ('code', 3), ('image', 4)], blank=True, block_lookup={0: ('wagtail.blocks.RichTextBlock', (), {}), 1: ('wagtail.blocks.TextBlock', (), {'help_text': None, 'required': True}), 2: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('python', 'Python'), ('javascript', 'JavaScript'), ('html', 'HTML'), ('css', 'CSS'), ('bash', 'Bash'), ('plaintext', 'Plain Text')], 'required': False}), 3: ('wagtail.blocks.StructBlock', [[('code', 1), ('language', 2)]], {}), 4: ('wagtail.images.blocks.ImageChooserBlock', (), {})}, help_text='Main content for the article.')),
96
+ ('page_type', models.ForeignKey(blank=True, help_text='Select the type of article.', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='articles', to='picata.articletype')),
236
97
  ],
237
98
  options={
238
- "abstract": False,
99
+ 'abstract': False,
239
100
  },
101
+ bases=('wagtailcore.page',),
240
102
  ),
241
- migrations.AddField(
242
- model_name="article",
243
- name="tags",
244
- field=modelcluster.contrib.taggit.ClusterTaggableManager(
245
- blank=True,
246
- help_text="Tags for the article.",
247
- through="hpk.ArticleTagItem",
248
- to="taggit.Tag",
249
- verbose_name="Tags",
250
- ),
103
+ migrations.CreateModel(
104
+ name='PageTagRelation',
105
+ fields=[
106
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
107
+ ('content_object', modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='tagged_items', to='picata.article')),
108
+ ('tag', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tagged_items', to='picata.pagetag')),
109
+ ],
110
+ options={
111
+ 'abstract': False,
112
+ },
251
113
  ),
252
114
  migrations.AddField(
253
- model_name="article",
254
- name="article_type",
255
- field=models.ForeignKey(
256
- blank=True,
257
- help_text="Select the type of article.",
258
- null=True,
259
- on_delete=django.db.models.deletion.SET_NULL,
260
- related_name="articles",
261
- to="hpk.articletype",
262
- ),
115
+ model_name='article',
116
+ name='tags',
117
+ field=modelcluster.contrib.taggit.ClusterTaggableManager(blank=True, help_text='Tags for the article.', through='picata.PageTagRelation', to='picata.PageTag', verbose_name='Tags'),
118
+ ),
119
+ migrations.CreateModel(
120
+ name='SocialSettings',
121
+ fields=[
122
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
123
+ ('default_social_image', models.ForeignKey(blank=True, help_text='Default image for social media previews.', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image')),
124
+ ('site', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, to='wagtailcore.site')),
125
+ ],
126
+ options={
127
+ 'abstract': False,
128
+ },
263
129
  ),
264
130
  ]
picata/models.py CHANGED
@@ -20,8 +20,6 @@ from django.db.models import (
20
20
  from django.db.models.functions import Coalesce, ExtractYear
21
21
  from django.http import HttpRequest
22
22
  from django.urls import reverse
23
- from hpk.typing import Args, Kwargs
24
- from hpk.typing.wagtail import PageContext
25
23
  from modelcluster.contrib.taggit import ClusterTaggableManager
26
24
  from modelcluster.fields import ParentalKey
27
25
  from taggit.models import TagBase, TaggedItemBase
@@ -37,6 +35,9 @@ from wagtail.query import PageQuerySet
37
35
  from wagtail.search import index
38
36
  from wagtail_modeladmin.options import ModelAdmin
39
37
 
38
+ from picata.typing import Args, Kwargs
39
+ from picata.typing.wagtail import PageContext
40
+
40
41
  from .blocks import (
41
42
  CodeBlock,
42
43
  StaticIconLinkListsBlock,
@@ -103,7 +104,7 @@ class BasePage(Page):
103
104
 
104
105
  def get_context(self, request: HttpRequest, *args: Args, **kwargs: Kwargs) -> BasePageContext:
105
106
  """Gather any publication and preview data available for the page into the context."""
106
- from hpk.helpers.wagtail import page_preview_data
107
+ from picata.helpers.wagtail import page_preview_data
107
108
 
108
109
  context = super().get_context(request, *args, **kwargs)
109
110
  context.update(page_preview_data(request, self))
@@ -356,7 +357,7 @@ class PostGroupPage(RoutablePageMixin, Page):
356
357
  """A top-level page for grouping various types of posts or articles."""
357
358
 
358
359
  template = "picata/post_listing.html"
359
- subpage_types: ClassVar[list[str]] = ["hpk.Article"]
360
+ subpage_types: ClassVar[list[str]] = ["picata.Article"]
360
361
 
361
362
  intro = RichTextField(blank=True, help_text="An optional introduction to this group.")
362
363
 
@@ -465,7 +466,7 @@ class HomePage(BasePage):
465
466
 
466
467
  def get_context(self, request: HttpRequest, *args: Args, **kwargs: Kwargs) -> HomePageContext:
467
468
  """Add content streams and a recent posts list to the context."""
468
- from hpk.helpers.wagtail import page_preview_data
469
+ from picata.helpers.wagtail import page_preview_data
469
470
 
470
471
  recent_posts = Article.objects.live_for_user(request.user).by_date()
471
472
  recent_posts = [page_preview_data(request, post) for post in recent_posts]
picata/settings/base.py CHANGED
@@ -13,8 +13,8 @@ import contextlib
13
13
  from os import getenv
14
14
  from pathlib import Path
15
15
 
16
- from hpk.helpers import get_public_ip
17
- from hpk.log_utils import FormatterWithEverything
16
+ from picata.helpers import get_public_ip
17
+ from picata.log_utils import FormatterWithEverything
18
18
 
19
19
  SRC_DIR = Path(__file__).resolve().parent.parent.parent
20
20
  BASE_DIR = Path(SRC_DIR).parent
@@ -35,7 +35,7 @@ SECRET_KEY = getenv("SECRET_KEY")
35
35
 
36
36
  INSTALLED_APPS = [
37
37
  # Local apps
38
- "hpk.apps.Config",
38
+ "picata.apps.Config",
39
39
  # Wagtail
40
40
  "wagtail.contrib.forms",
41
41
  "wagtail.contrib.redirects",
@@ -72,10 +72,10 @@ MIDDLEWARE = [
72
72
  "django.middleware.clickjacking.XFrameOptionsMiddleware",
73
73
  "django.middleware.security.SecurityMiddleware",
74
74
  "wagtail.contrib.redirects.middleware.RedirectMiddleware",
75
- "hpk.middleware.HTMLProcessingMiddleware",
75
+ "picata.middleware.HTMLProcessingMiddleware",
76
76
  ]
77
77
 
78
- ROOT_URLCONF = "hpk.urls"
78
+ ROOT_URLCONF = "picata.urls"
79
79
 
80
80
  TEMPLATES = [
81
81
  {
@@ -94,7 +94,7 @@ TEMPLATES = [
94
94
  },
95
95
  ]
96
96
 
97
- WSGI_APPLICATION = "hpk.wsgi.application"
97
+ WSGI_APPLICATION = "picata.wsgi.application"
98
98
 
99
99
 
100
100
  # Database
@@ -103,7 +103,7 @@ WSGI_APPLICATION = "hpk.wsgi.application"
103
103
  DATABASES = {
104
104
  "default": {
105
105
  "ENGINE": "django.db.backends.postgresql",
106
- "NAME": getenv("DB_NAME"),
106
+ "NAME": getenv("PICATA_DB"),
107
107
  "USER": getenv("DB_USER"),
108
108
  "PASSWORD": getenv("DB_PASSWORD"),
109
109
  "HOST": getenv("DB_HOST", "localhost"),
@@ -158,14 +158,14 @@ LOGGING = {
158
158
  },
159
159
  "django_log": {
160
160
  "level": "INFO",
161
- "class": "hpk.log_utils.RotatingDailyFileHandler",
161
+ "class": "picata.log_utils.RotatingDailyFileHandler",
162
162
  "filename": LOG_DIR / "django.log",
163
163
  "formatter": "verbose",
164
164
  },
165
- "hpk_log": {
165
+ "picata_log": {
166
166
  "level": "INFO",
167
- "class": "hpk.log_utils.RotatingDailyFileHandler",
168
- "filename": LOG_DIR / "hpk.log",
167
+ "class": "picata.log_utils.RotatingDailyFileHandler",
168
+ "filename": LOG_DIR / "picata.log",
169
169
  "formatter": "verbose",
170
170
  },
171
171
  "warnings_log": {
@@ -178,8 +178,8 @@ LOGGING = {
178
178
  },
179
179
  },
180
180
  "loggers": {
181
- "hpk": {
182
- "handlers": ["hpk_log"],
181
+ "picata": {
182
+ "handlers": ["picata_log"],
183
183
  "level": "INFO",
184
184
  "propagate": True,
185
185
  },
@@ -279,7 +279,7 @@ DEFAULT_FROM_EMAIL = getenv("ADMIN_EMAIL")
279
279
  # Wagtail
280
280
  # See https://docs.wagtail.org/en/stable/
281
281
 
282
- WAGTAIL_SITE_NAME = "Hpk.io"
282
+ WAGTAIL_SITE_NAME = "[Example Site Name]"
283
283
 
284
284
  # Image serving
285
285
  WAGTAILIMAGES_IMAGE_MODEL = "wagtailimages.Image"
@@ -294,7 +294,7 @@ WAGTAILSEARCH_BACKENDS = {
294
294
 
295
295
  # Base URL to use when referring to full URLs within the Wagtail admin backend -
296
296
  # e.g. in notification emails. Don't include '/admin' or a trailing slash
297
- WAGTAILADMIN_BASE_URL = "https://" + getenv("FQDN", "hpk.io")
297
+ WAGTAILADMIN_BASE_URL = "https://" + getenv("FQDN", "example.com")
298
298
 
299
299
  # https://docs.wagtail.org/en/stable/reference/settings.html#general-editing
300
300
  WAGTAILADMIN_RICH_TEXT_EDITORS = {