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

Sign up to get free protection for your applications and to get access to all the features.
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 = {