picata 0.0.1__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. LICENSE.md +24 -0
  2. README.md +59 -0
  3. components/HelloWorld.tsx +11 -0
  4. entrypoint.tsx +268 -0
  5. manage.py +15 -0
  6. picata/__init__.py +1 -0
  7. picata/apps.py +33 -0
  8. picata/blocks.py +175 -0
  9. picata/helpers/__init__.py +70 -0
  10. picata/helpers/wagtail.py +61 -0
  11. picata/log_utils.py +47 -0
  12. picata/middleware.py +54 -0
  13. picata/migrations/0001_initial.py +264 -0
  14. picata/migrations/0002_alter_article_content_alter_basicpage_content.py +112 -0
  15. picata/migrations/0003_alter_article_content_alter_basicpage_content.py +104 -0
  16. picata/migrations/0004_alter_article_content_alter_basicpage_content.py +105 -0
  17. picata/migrations/0005_socialsettings.py +48 -0
  18. picata/migrations/0006_alter_article_content.py +71 -0
  19. picata/migrations/0007_splitviewpage.py +69 -0
  20. picata/migrations/0008_alter_splitviewpage_content.py +96 -0
  21. picata/migrations/0009_alter_splitviewpage_content.py +111 -0
  22. picata/migrations/0010_alter_splitviewpage_content.py +105 -0
  23. picata/migrations/0011_alter_splitviewpage_options_and_more.py +113 -0
  24. picata/migrations/0012_alter_splitviewpage_content.py +109 -0
  25. picata/migrations/0013_alter_article_content.py +43 -0
  26. picata/migrations/0014_alter_article_content_alter_article_summary.py +24 -0
  27. picata/migrations/0015_alter_article_options_article_tagline_and_more.py +28 -0
  28. picata/migrations/0016_alter_article_options_alter_articletag_options_and_more.py +33 -0
  29. picata/migrations/0017_articletagrelation_alter_article_tags_and_more.py +35 -0
  30. picata/migrations/0018_rename_articletag_pagetag_and_more.py +21 -0
  31. picata/migrations/0019_rename_name_plural_articletype__name_plural.py +18 -0
  32. picata/migrations/0020_rename__name_plural_articletype__pluralised_name.py +18 -0
  33. picata/migrations/0021_rename_article_type_article_page_type.py +18 -0
  34. picata/migrations/0022_homepage.py +28 -0
  35. picata/migrations/__init__.py +0 -0
  36. picata/models.py +486 -0
  37. picata/settings/__init__.py +1 -0
  38. picata/settings/base.py +345 -0
  39. picata/settings/dev.py +94 -0
  40. picata/settings/mypy.py +7 -0
  41. picata/settings/prod.py +12 -0
  42. picata/settings/test.py +6 -0
  43. picata/static/picata/ada-profile.jpg +0 -0
  44. picata/static/picata/ada-social-bear.jpg +0 -0
  45. picata/static/picata/favicon.ico +0 -0
  46. picata/static/picata/fonts/Bitter-Light.ttf +0 -0
  47. picata/static/picata/fonts/Bitter-LightItalic.ttf +0 -0
  48. picata/static/picata/fonts/FiraCode-Light.ttf +0 -0
  49. picata/static/picata/fonts/FiraCode-SemiBold.ttf +0 -0
  50. picata/static/picata/fonts/Sacramento-Regular.ttf +0 -0
  51. picata/static/picata/fonts/ZillaSlab-Bold.ttf +0 -0
  52. picata/static/picata/fonts/ZillaSlab-BoldItalic.ttf +0 -0
  53. picata/static/picata/fonts/ZillaSlab-Light.ttf +0 -0
  54. picata/static/picata/fonts/ZillaSlab-LightItalic.ttf +0 -0
  55. picata/static/picata/fonts/ZillaSlabHighlight-Bold.ttf +0 -0
  56. picata/static/picata/icons.svg +56 -0
  57. picata/templates/picata/3_column.html +28 -0
  58. picata/templates/picata/404.html +11 -0
  59. picata/templates/picata/500.html +13 -0
  60. picata/templates/picata/_post_list.html +24 -0
  61. picata/templates/picata/article.html +20 -0
  62. picata/templates/picata/base.html +135 -0
  63. picata/templates/picata/basic_page.html +10 -0
  64. picata/templates/picata/blocks/icon_link_item.html +7 -0
  65. picata/templates/picata/blocks/icon_link_list.html +4 -0
  66. picata/templates/picata/blocks/icon_link_list_stream.html +3 -0
  67. picata/templates/picata/dl_view.html +18 -0
  68. picata/templates/picata/home_page.html +21 -0
  69. picata/templates/picata/post_listing.html +17 -0
  70. picata/templates/picata/previews/3col.html +73 -0
  71. picata/templates/picata/previews/dl.html +10 -0
  72. picata/templates/picata/previews/split.html +10 -0
  73. picata/templates/picata/previews/theme_gallery.html +158 -0
  74. picata/templates/picata/search_results.html +28 -0
  75. picata/templates/picata/split_view.html +15 -0
  76. picata/templates/picata/tags/site_menu.html +8 -0
  77. picata/templatetags/__init__.py +1 -0
  78. picata/templatetags/absolute_static.py +15 -0
  79. picata/templatetags/menu_tags.py +42 -0
  80. picata/templatetags/stringify.py +23 -0
  81. picata/transformers.py +60 -0
  82. picata/typing/__init__.py +19 -0
  83. picata/typing/wagtail.py +31 -0
  84. picata/urls.py +48 -0
  85. picata/validators.py +36 -0
  86. picata/views.py +80 -0
  87. picata/wagtail_hooks.py +43 -0
  88. picata/wsgi.py +15 -0
  89. picata-0.0.1.dist-info/METADATA +87 -0
  90. picata-0.0.1.dist-info/RECORD +94 -0
  91. picata-0.0.1.dist-info/WHEEL +4 -0
  92. picata-0.0.1.dist-info/licenses/LICENSE.md +24 -0
  93. pygments.sass +382 -0
  94. styles.sass +300 -0
@@ -0,0 +1,23 @@
1
+ """Template tags to transform Python types into human-readable strings."""
2
+
3
+ from django import template
4
+
5
+ register = template.Library()
6
+
7
+
8
+ @register.filter
9
+ def stringify(value: list, quote_style: str | None = None) -> str:
10
+ """Convert a list of strings into a human-readable string with optional quoting."""
11
+ if not isinstance(value, list):
12
+ raise TypeError("The 'stringify' filter currently only supports lists.")
13
+
14
+ quote = "'" if quote_style == "single" else '"' if quote_style == "double" else ""
15
+ quoted_items = [f"{quote}{item!s}{quote}" for item in value]
16
+
17
+ if len(quoted_items) == 0:
18
+ return ""
19
+ if len(quoted_items) == 1:
20
+ return quoted_items[0]
21
+ if len(quoted_items) == 2: # noqa: PLR2004
22
+ return " and ".join(quoted_items)
23
+ return f"{', '.join(quoted_items[:-1])}, and {quoted_items[-1]}"
picata/transformers.py ADDED
@@ -0,0 +1,60 @@
1
+ """Callables to transform the response."""
2
+
3
+ from lxml import etree
4
+
5
+ from hpk.helpers import ALPHANUMERIC_REGEX, get_full_text
6
+
7
+
8
+ def add_heading_ids(tree: etree._Element) -> None:
9
+ """Add a unique id to any heading in <main> missing one, derived from its inner text."""
10
+ seen_ids = set()
11
+ main = tree.xpath("/html/body/main")
12
+ if not main:
13
+ return
14
+
15
+ for heading in main[0].xpath(".//h1|//h2|//h3|//h4|//h5|//h6"):
16
+ if heading.get("id"):
17
+ continue
18
+ heading_text = get_full_text(heading)
19
+ slug = heading_text.lower().replace(" ", "-")
20
+ unique_id = slug
21
+ count = 1
22
+ while unique_id in seen_ids:
23
+ unique_id = f"{slug}-{count}"
24
+ count += 1
25
+ seen_ids.add(unique_id)
26
+ heading.set("id", unique_id)
27
+
28
+
29
+ class AnchorInserter:
30
+ """Transformer to insert anchored pilcrows into targeted elements in the document."""
31
+
32
+ def __init__(self, root: str, targets: str) -> None:
33
+ """Remember the root paths we're to operate on."""
34
+ self.root_xpath = root
35
+ self.targets_xpath = targets
36
+
37
+ def __call__(self, tree: etree._Element) -> None:
38
+ """Inserts anchors into targets within the specified roots."""
39
+ for root_element in tree.xpath(self.root_xpath):
40
+ self._process_targets(root_element, self.targets_xpath)
41
+
42
+ def _process_targets(self, root: etree._Element, targets: str) -> None:
43
+ """Processes targets within a given root element, inserting anchors."""
44
+ for target in root.xpath(targets):
45
+ target_id = target.get("id")
46
+ if not target_id or target.xpath(".//a"):
47
+ continue
48
+
49
+ sanitized_id = self._sanitize_id(target_id)
50
+ if sanitized_id != target_id:
51
+ target.set("id", sanitized_id)
52
+
53
+ # Append an anchored pilcrow to the target element
54
+ anchor = etree.Element("a", href=f"#{target_id}", **{"class": "target-link"})
55
+ anchor.text = "¶"
56
+ target.append(anchor)
57
+
58
+ def _sanitize_id(self, id_value: str) -> str:
59
+ """Sanitize the ID by removing non-alphanumeric characters."""
60
+ return ALPHANUMERIC_REGEX.sub("", id_value)
@@ -0,0 +1,19 @@
1
+ """Reusable types for the hpk project."""
2
+
3
+ from typing import Any, TypedDict
4
+
5
+ from django.http import HttpRequest
6
+
7
+ # Generic arguments and keyword arguments
8
+ Args = tuple[Any, ...]
9
+ Kwargs = dict[str, Any]
10
+
11
+
12
+ class Context(TypedDict):
13
+ """Base class for context dicts passed all around the system."""
14
+
15
+ request: HttpRequest
16
+
17
+
18
+ # Log arguments for structured logging
19
+ LogArg = str | int | float | bool
@@ -0,0 +1,31 @@
1
+ """Types for Wagtail.
2
+
3
+ NB: This module split the hpk.typing module into a package in order to avoid
4
+ circular imports during program initialisation, with the `from wagtail` imports.
5
+ """
6
+
7
+ from typing import Any
8
+
9
+ from wagtail.blocks import StructBlock
10
+ from wagtail.models import Page
11
+
12
+ from . import Context
13
+
14
+
15
+ # StructBlock types
16
+ class BlockRenderContextDict(Context):
17
+ """Context dicts passed to render functions for Blocks."""
18
+
19
+ self: StructBlock
20
+ page: Page
21
+
22
+
23
+ BlockRenderContext = BlockRenderContextDict | None
24
+ BlockRenderValue = dict[str, Any]
25
+
26
+
27
+ class PageContext(Context):
28
+ """Base class for Wagtail Page classes."""
29
+
30
+ self: Page
31
+ page: Page
picata/urls.py ADDED
@@ -0,0 +1,48 @@
1
+ """Top-level URL configuration for the site."""
2
+
3
+ from debug_toolbar.toolbar import debug_toolbar_urls
4
+ from django.conf import settings
5
+ from django.contrib import admin
6
+ from django.urls import include, path, re_path
7
+ from wagtail import urls as wagtail_urls
8
+ from wagtail.admin import urls as wagtailadmin_urls
9
+ from wagtail.contrib.sitemaps.views import sitemap
10
+ from wagtail.documents import urls as wagtaildocs_urls
11
+ from wagtail.images.views.serve import ServeView
12
+
13
+ from hpk.views import search
14
+
15
+ urlpatterns = [
16
+ path("django-admin/", admin.site.urls), # Django Admin
17
+ path("admin/", include(wagtailadmin_urls)), # Wagtail Admin
18
+ path("documents/", include(wagtaildocs_urls)), # Wagtail documents
19
+ re_path(
20
+ r"^images/([^/]*)/(\d*)/([^/]*)/[^/]*$", ServeView.as_view(), name="wagtailimages_serve"
21
+ ),
22
+ path("sitemap.xml", sitemap),
23
+ path("search/", search, name="search"),
24
+ ]
25
+
26
+ # Debug-mode-only URLs
27
+ if settings.DEBUG:
28
+ from django.conf.urls.static import static
29
+ from django.contrib.staticfiles.urls import staticfiles_urlpatterns
30
+ from django.views.generic import RedirectView
31
+
32
+ from hpk.views import debug_shell, preview
33
+
34
+ # Serve static and media files from development server
35
+ urlpatterns += staticfiles_urlpatterns()
36
+ urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
37
+
38
+ # Enable Django Debug Toolbar
39
+ urlpatterns += debug_toolbar_urls()
40
+
41
+ urlpatterns += [
42
+ path("favicon.ico", RedirectView.as_view(url=settings.STATIC_URL + "favicon.ico")),
43
+ path("shell/", debug_shell), # Just raises an exception (to invoke Werkzeug shell access)
44
+ path("preview/<slug:file>/", preview, name="debug_preview"), # templates/previews/<file>
45
+ ]
46
+
47
+ # Let Wagtail take care of the rest
48
+ urlpatterns += [path("", include(wagtail_urls))]
picata/validators.py ADDED
@@ -0,0 +1,36 @@
1
+ """Custom validators for Django fields."""
2
+
3
+ from django.core.exceptions import ValidationError
4
+ from django.core.validators import URLValidator
5
+
6
+
7
+ class HREFValidator:
8
+ """Custom validator for href attributes in HTML anchor tags.
9
+
10
+ Basically it's `URLValidator` but with 'mailto:', 'tel:' and 'file:' added.
11
+ (Doesn't actually validate email addresses, telephone numbers, or file paths.)
12
+ """
13
+
14
+ def __init__(
15
+ self, extra_schemes: list[str] | None = None, url_schemes: list[str] | None = None
16
+ ) -> None:
17
+ """Store schemes we check for and initialise a basic `URLValidator`."""
18
+ self.schemes: list[str] = extra_schemes or ["mailto", "tel", "file", "sms"]
19
+ self.base_validator = URLValidator(schemes=url_schemes)
20
+
21
+ def __call__(self, value: str) -> None:
22
+ """Try validating a custom scheme (e.g. "mailto:…") if the `URLValidator` fails."""
23
+ try:
24
+ self.base_validator(value)
25
+ except ValidationError as err:
26
+ if ":" in value:
27
+ if not any(value.startswith(f"{scheme}:") for scheme in self.schemes):
28
+ raise ValidationError(
29
+ f"'{value}' is not a valid href.", code="invalid_href"
30
+ ) from err
31
+ elif not value.startswith(("/", "#")): # Allow relative links or fragments
32
+ raise ValidationError(
33
+ f"'{value}' is not a valid href.", code="invalid_href"
34
+ ) from err
35
+ else:
36
+ return
picata/views.py ADDED
@@ -0,0 +1,80 @@
1
+ """Top-level views for the site."""
2
+
3
+ # NB: Django's meta-class shenanigans over-complicate type hinting when QuerySets get involved.
4
+ # pyright: reportAttributeAccessIssue=false, reportArgumentType=false
5
+
6
+ import logging
7
+ from typing import TYPE_CHECKING, NoReturn
8
+
9
+ from django.http import HttpRequest, HttpResponse
10
+ from django.shortcuts import render
11
+ from hpk.helpers.wagtail import (
12
+ filter_pages_by_tags,
13
+ filter_pages_by_type,
14
+ page_preview_data,
15
+ visible_pages_qs,
16
+ )
17
+ from hpk.models import ArticleType
18
+
19
+ if TYPE_CHECKING:
20
+ from wagtail.query import PageQuerySet
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+
25
+ def debug_shell(request: HttpRequest) -> NoReturn:
26
+ """Just `assert False`, to force an exception and get to the Werkzeug debug console."""
27
+ logger.info(
28
+ "Raising `assert False` in the `debug_shell` view. "
29
+ "Request details: method=%s, path=%s, user=%s",
30
+ request.method,
31
+ request.path,
32
+ request.user if request.user.is_authenticated else "Anonymous",
33
+ )
34
+ assert False # noqa: B011, PT015, S101
35
+
36
+
37
+ def preview(request: HttpRequest, file: str) -> HttpResponse:
38
+ """Render a named template from the "templates/previews/" directory."""
39
+ return render(request, f"picata/previews/{file}.html")
40
+
41
+
42
+ def search(request: HttpRequest) -> HttpResponse:
43
+ """Render search results from the `query` and `tags` GET parameters."""
44
+ results: dict[str, str | list[str] | set[str]] = {}
45
+
46
+ # Base QuerySet for all pages
47
+ pages: PageQuerySet = visible_pages_qs(request)
48
+
49
+ # Perform search by query
50
+ query_string = request.GET.get("query")
51
+ if query_string:
52
+ pages = pages.search(query_string)
53
+ results["query"] = query_string
54
+
55
+ # Resolve specific pages post-search
56
+ specific_pages = [page.specific for page in pages]
57
+
58
+ # Filter by page types
59
+ page_types_string = request.GET.get("page_types")
60
+ if page_types_string:
61
+ page_type_slugs = {slug.strip() for slug in page_types_string.split(",") if slug.strip()}
62
+ matching_page_types = ArticleType.objects.filter(slug__in=page_type_slugs)
63
+ specific_pages = filter_pages_by_type(specific_pages, page_type_slugs)
64
+ results["page_types"] = [page_type.name for page_type in matching_page_types]
65
+
66
+ # Filter by tags
67
+ tags_string = request.GET.get("tags")
68
+ if tags_string:
69
+ tags = {tag.strip() for tag in tags_string.split(",") if tag.strip()}
70
+ specific_pages = filter_pages_by_tags(specific_pages, tags)
71
+ results["tags"] = tags
72
+
73
+ # Handle empty cases
74
+ if not (query_string or tags_string or page_types_string):
75
+ specific_pages = []
76
+
77
+ # Enhance pages with preview and publication data
78
+ page_previews = [page_preview_data(request, page) for page in specific_pages]
79
+
80
+ return render(request, "picata/search_results.html", {**results, "pages": page_previews})
@@ -0,0 +1,43 @@
1
+ """Wagtail hooks, used to customise view-level behaviour of the Wagtail admin and front-end.
2
+
3
+ See: https://docs.wagtail.org/en/stable/reference/hooks.html
4
+ """
5
+
6
+ import logging
7
+ from typing import ClassVar
8
+
9
+ from django.db.models import QuerySet, Value
10
+ from django.db.models.functions import Coalesce
11
+ from django.http import HttpRequest
12
+ from wagtail import hooks
13
+ from wagtail.models import Page
14
+ from wagtail.snippets.views.snippets import SnippetViewSet
15
+
16
+ from hpk.models import PageTag
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ @hooks.register("construct_explorer_page_queryset") # type: ignore[reportOptionalCall]
22
+ def order_admin_menu_by_date(parent_page: Page, pages: QuerySet, request: HttpRequest) -> QuerySet: # noqa: ARG001
23
+ """Order admin menus latest-at-top for pages with lots of children (like 'blog')."""
24
+ if parent_page.slug == "blog":
25
+ # Sort directly in order_by with Coalesce
26
+ return pages.order_by(
27
+ Coalesce("first_published_at", "latest_revision_created_at", Value("1970-01-01")).desc()
28
+ )
29
+ return pages
30
+
31
+
32
+ class PageTagViewSet(SnippetViewSet):
33
+ """Viewset for managing `PageTag`s."""
34
+
35
+ icon: str = "tag"
36
+ list_display: ClassVar[list[str]] = ["name"]
37
+ search_fields: ClassVar[list[str]] = ["name"]
38
+
39
+
40
+ @hooks.register("register_admin_viewset") # type: ignore[reportOptionalCall]
41
+ def register_article_tag_viewset() -> SnippetViewSet:
42
+ """Make `PageTag`s editable via the Wagtail admin."""
43
+ return PageTagViewSet(model=PageTag)
picata/wsgi.py ADDED
@@ -0,0 +1,15 @@
1
+ """WSGI config for the hpk project.
2
+
3
+ It exposes the WSGI callable as a module-level variable named ``application``.
4
+
5
+ For more information on this file, see
6
+ https://docs.djangoproject.com/en/5.1/howto/deployment/wsgi/
7
+ """
8
+
9
+ import os
10
+
11
+ from django.core.wsgi import get_wsgi_application
12
+
13
+ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "hpk.settings.prod")
14
+
15
+ application = get_wsgi_application()
@@ -0,0 +1,87 @@
1
+ Metadata-Version: 2.4
2
+ Name: picata
3
+ Version: 0.0.1
4
+ Summary: Ada's Wagtail-based CMS & blog
5
+ Project-URL: Documentation, https://github.com/hipikat/picata#readme
6
+ Project-URL: Issues, https://github.com/hipikat/picata/issues
7
+ Project-URL: Source, https://github.com/hipikat/picata
8
+ Author-email: Ada Wright <ada@hpk.io>
9
+ License-Expression: MIT
10
+ License-File: LICENSE.md
11
+ Keywords: blog,cms,django,wagtail
12
+ Classifier: Development Status :: 2 - Pre-Alpha
13
+ Classifier: Framework :: Django CMS
14
+ Classifier: Framework :: Wagtail :: 6
15
+ Classifier: Programming Language :: Python
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Programming Language :: Python :: Implementation :: CPython
18
+ Requires-Python: >=3.13
19
+ Requires-Dist: gunicorn~=23.0.0
20
+ Requires-Dist: lxml~=5.3.0
21
+ Requires-Dist: psutil~=6.1.0
22
+ Requires-Dist: psycopg~=3.2.3
23
+ Requires-Dist: pygments~=2.18.0
24
+ Requires-Dist: python-slugify~=8.0.4
25
+ Requires-Dist: wagtail-modeladmin~=2.1.0
26
+ Requires-Dist: wagtail~=6.2
27
+ Description-Content-Type: text/markdown
28
+
29
+ # Ada's website
30
+
31
+ Wherein I set up a little website, and learn a bunch of stuff as I go.
32
+
33
+ ## What it's made of
34
+
35
+ ### Inside the box
36
+
37
+ - [Wagtail](https://wagtail.org) (on [Django](https://www.djangoproject.com)) is the web framework
38
+ <!-- - [Tailwind CSS](https://tailwindcss.com) for styling -->
39
+
40
+ ### Holding things together
41
+
42
+ - [UV](https://github.com/astral-sh/uv) for all Python project management
43
+ - [Just](https://just.systems) as a command runner
44
+ - [OpenTofu](https://opentofu.org) for DevOps
45
+ - [Postgres](https://www.postgresql.org) for the database
46
+ - [Docker](https://www.docker.com) for local development
47
+
48
+ ## Quickstart
49
+
50
+ ### Requirements
51
+
52
+ - On a Mac:
53
+
54
+ ```shell
55
+ brew install colima docker
56
+ ```
57
+
58
+ ### Run a development server
59
+
60
+ ```shell
61
+ just tofu workspace select dev
62
+ just tofu apply
63
+ ```
64
+
65
+ This will spin up a box on DigitalOcean using the settings defined in
66
+ [infra/variables.tf](infra/variables.tf), and create a DNS A record at
67
+ (workspace).for.(tld), (i.e. dev.for.hpk.io) pointing to the box. The variables
68
+ `do_token` and `ssh_fingerprint` should be defined in
69
+ [infra/secrets.tfvars](infra/secrets.tfvars). Workspace-specific variables are
70
+ defined in infra/envs/(workspace).tfvars; e.g.
71
+ [infra/envs/dev.tfvars](infra/envs/dev.tfvars) defines the 'tags' list for the
72
+ box as `[development]` and sets `cloud_init_config` to point to the
73
+ [cloud-init](https://cloud-init.io) script
74
+ [config/cloud-init-dev.yml](config/cloud-init-dev.yml).
75
+
76
+ The development cloud-init script will:
77
+
78
+ - Install the system packages [`just`](https://just.systems), [`zsh`](https://www.zsh.org),
79
+ [`gunicorn`](https://gunicorn.org), and `tree`
80
+ - Create a 'wagtail' user, with UID 1500
81
+ - Create the 'ada' user, and:
82
+ - install their SSH public keys,
83
+ - install their dotfiles,
84
+ - add them to the 'sudo' and 'wagtail' groups
85
+ - Install [Node](http://nodejs.org) on the system, from the `TF_VAR_NODE_VERSION`
86
+ defined in [.env](.env)
87
+ - Checkout this repository into `/app`, setting the owner and group to 'wagtail'.
@@ -0,0 +1,94 @@
1
+ LICENSE.md,sha256=Bv8sMyZI5NI6DMrfiAvCwIFRLSfJkimLF2KVcUMteKU,1103
2
+ README.md,sha256=tPe8EjMhr2cMwsf-N-gQcTT-IpyE2kIOnwggeJnSPzM,1990
3
+ entrypoint.tsx,sha256=Tk6L2rCk0KrE5kaSL0GFuL3S19yG-BsUgvNAGe32uy8,8820
4
+ manage.py,sha256=v0zbaNjwEksTx39Z9dgoWh8LxZEH1tuCpjLVVmiXBF4,423
5
+ pygments.sass,sha256=zbDYpWda3EoGmjoC3JshZy-_CECNf6WU9abYPF8EHms,6369
6
+ styles.sass,sha256=4Jb-Gxn9AP09pZzwLlCGGfeacCyrncpJAOCYcy-dyB4,8486
7
+ components/HelloWorld.tsx,sha256=Kp7gvhGehfrX1mw0jgr2_D6AueFgqgfMYGkyQgvWekg,180
8
+ picata/__init__.py,sha256=_kL7fTKu02_hXVnOmF1K7llLgpdOeQyAVliL7I5o-Dg,62
9
+ picata/apps.py,sha256=P1qIVWRACiNUA-TY6aIRvt-MyA-3baG82Z_MEpr2fsw,1241
10
+ picata/blocks.py,sha256=iPJc4eQN7_J-KGMMw3Iln31I1-tBluqgaUF-xCxq2Mc,5279
11
+ picata/log_utils.py,sha256=BRdB3PqpFx1XAhIyAzIOyQKiqrjbT3PBmkhH6-wAWJg,1555
12
+ picata/middleware.py,sha256=ycKEVr2GKGmGFQNqFG4ijBmxFuVK11khtiiAiTsbbsk,2064
13
+ picata/models.py,sha256=dra7x1pbRrMpFydgER_j7zPEnrP9Dqqy1n3rDR1Jbgc,15868
14
+ picata/transformers.py,sha256=8VkJJC0Xm8ZMzKxOp07rSnqJA7b-TiL02e_y07Gh5AI,2209
15
+ picata/urls.py,sha256=kTSLS0-f9YaLmhzgg8mW4Oc9W04eXVf_0TZpEnYdnJo,1828
16
+ picata/validators.py,sha256=X4wdIxbCdmuU-gJv45ptTFB7kHR166jkSQBJiTzP3ZU,1517
17
+ picata/views.py,sha256=L_NXtqZogdwP7X1uBIPOB1qnYSNpCCThuI8O7YeS47E,2874
18
+ picata/wagtail_hooks.py,sha256=Y_50IVpV62TEHtg1h8MN0IDkCWAMTVzv0Hd3eiult9M,1497
19
+ picata/wsgi.py,sha256=XdUhwbLt-MKyr9he5PRphYrvKadC8yF0glx1xxQpCgE,392
20
+ picata/helpers/__init__.py,sha256=acN445qKCuRVfInCEyCtx5W1BggloOSrawzdQ-c9m7s,2427
21
+ picata/helpers/wagtail.py,sha256=p_0qw530UcaIBY3AzeTYY6fOUxmVnC9DaP_0_gByrUw,2108
22
+ picata/migrations/0001_initial.py,sha256=q15Mjii63qa397O7eXyM-BSgTDzf1MqzKAJn1To_huc,9721
23
+ picata/migrations/0002_alter_article_content_alter_basicpage_content.py,sha256=WlmmGgbAaBCZvR904W8gAeyxp30fOfi4a0D5Xpiojsk,4490
24
+ picata/migrations/0003_alter_article_content_alter_basicpage_content.py,sha256=i2KD213JKxzM7Kgxlfufxz405IICBoVFJVeNBZuaRlI,4300
25
+ picata/migrations/0004_alter_article_content_alter_basicpage_content.py,sha256=sZ_ky-xTZREr43y9Zrh1DaqUS5ZXi4r7ilL_EZf9L_E,4204
26
+ picata/migrations/0005_socialsettings.py,sha256=flfEf47418UX972SoL0nta2gKjambGFACXpFD_oeY0s,1541
27
+ picata/migrations/0006_alter_article_content.py,sha256=EVTuWtWf74mpD-YvZEzBEdmwHAixzn4R5PxEABl8n0Q,2690
28
+ picata/migrations/0007_splitviewpage.py,sha256=KK67Kw4kbxVm56BZfKj58mDajZfsCpLwspAFegKQgNA,2675
29
+ picata/migrations/0008_alter_splitviewpage_content.py,sha256=Y5aj5rTgclKxAgpPF7wCOxDpyFe4rrVkxa_YsEAoYCc,3829
30
+ picata/migrations/0009_alter_splitviewpage_content.py,sha256=PPu4Hqtm7PkbQYRB_CzqsS-G3-vkAPncPco8Zrn1IXY,4406
31
+ picata/migrations/0010_alter_splitviewpage_content.py,sha256=Re61nL4y6zHRGMhxADkqHNDkfoWQ8bHGbZK74uJXatg,4265
32
+ picata/migrations/0011_alter_splitviewpage_options_and_more.py,sha256=OPtQipM3COYRVf9RaP9G8ec0x3NlyR8wT1er3m11kg8,4527
33
+ picata/migrations/0012_alter_splitviewpage_content.py,sha256=pPERBPW4uduQ-sGops44p2gavJ2hYh5toZ67hmvjhLo,4357
34
+ picata/migrations/0013_alter_article_content.py,sha256=zVn-YC5jhXiG_c6hkD_uhAgu1BCdsqA1j1xfgJj5n1U,1571
35
+ picata/migrations/0014_alter_article_content_alter_article_summary.py,sha256=5RvNPNyjy0dnyOw9RfcuTB8pFlpmg6p3yK8kuR0zgMk,1186
36
+ picata/migrations/0015_alter_article_options_article_tagline_and_more.py,sha256=8ToGgChYwqSoxo5EgwM8SISbQhFt7yewH-fN3raNE_Y,849
37
+ picata/migrations/0016_alter_article_options_alter_articletag_options_and_more.py,sha256=yxDQhzCMOZCl3NPEyYnM1Yw1E5HAn4wN_BPV5PYrMxE,917
38
+ picata/migrations/0017_articletagrelation_alter_article_tags_and_more.py,sha256=3rPB6p0GxwTCvTVQIDKNFBtrf26q99mdmQwoCqpXyM8,1341
39
+ picata/migrations/0018_rename_articletag_pagetag_and_more.py,sha256=0wFB82uAlJZAA7BD1OVTJIWvY5by8fZss7TIVsVLnnY,485
40
+ picata/migrations/0019_rename_name_plural_articletype__name_plural.py,sha256=K58-B-V2ld2gJ7OuHnrQchC7zZvLP2Bnss3bQB5oS84,391
41
+ picata/migrations/0020_rename__name_plural_articletype__pluralised_name.py,sha256=X9zRPk5G6iMwitrr5BTUB9Nk9VqQQ951HfBrQ8_MXD4,405
42
+ picata/migrations/0021_rename_article_type_article_page_type.py,sha256=DtxWYyke-8RmoFqpW817rtrIzVMSV8wqCpS1e911Nxc,399
43
+ picata/migrations/0022_homepage.py,sha256=SYpqIlquJzLh4YozTcJVjR_0fW_v9GRrY75bnAUk0vE,3591
44
+ picata/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
45
+ picata/settings/__init__.py,sha256=5qI40E9aCWsFanUxAnruZi1wXrad3oAwnusglycfPsk,47
46
+ picata/settings/base.py,sha256=APK6EyBUu1C3Q8VX8JciKTQX-iZcARm2BMD54LDgAaY,9273
47
+ picata/settings/dev.py,sha256=6SXFuEH3NTq2L3iWcC49Ky3gxJnPflHE3TtjByzuEOg,2796
48
+ picata/settings/mypy.py,sha256=M-_KU9Qi4twjCHwwuhFbGz9hnIU3TXLH0Sg3XionVNY,148
49
+ picata/settings/prod.py,sha256=iPsqLOdinblT_ayqMW2_GqpNccsXJlWA9-kA015X7Vo,474
50
+ picata/settings/test.py,sha256=H_Wkn_gjsIV0r5HsU-s5HLhW_Oi63VQp4Afj2TIgRhI,244
51
+ picata/static/picata/ada-profile.jpg,sha256=HlewC8MPH_zSr--pUXV5JCEE0emOrn8BIKCQv41WidA,209953
52
+ picata/static/picata/ada-social-bear.jpg,sha256=3jH2kf57boRLS5AD7M-qW-xsBP84didfeM1KN5Xp4tc,128453
53
+ picata/static/picata/favicon.ico,sha256=MJLjMSsDpx9Mjb1v7y8V5oPMX3gLtD4cIDmNAdOgHC8,15406
54
+ picata/static/picata/icons.svg,sha256=LRLgy8bHyZKorDyPxkO2mMdbevKs40sCnhdWoO29pdY,11320
55
+ picata/static/picata/fonts/Bitter-Light.ttf,sha256=JCgJXY06RiEONk2htx2bitpL3zqeGGJ01Z81fwRTwAo,182212
56
+ picata/static/picata/fonts/Bitter-LightItalic.ttf,sha256=A6oI_l_kGd2F9-V0eOYMoydigDPRdahO6aUKMvQXddY,181412
57
+ picata/static/picata/fonts/FiraCode-Light.ttf,sha256=bKCGEsYc36CaTQ_0-STZkfU7PDJXZpyQQDfQD7oJyTQ,188708
58
+ picata/static/picata/fonts/FiraCode-SemiBold.ttf,sha256=BsEuLjCqZkqwFH0_iBxrWH1hrdhDZa9cNM61DkA5hiE,188848
59
+ picata/static/picata/fonts/Sacramento-Regular.ttf,sha256=pbneZ75FSoJrRthYUS4y7sjDAxK-tMT4ZK7iOQQU6cU,64808
60
+ picata/static/picata/fonts/ZillaSlab-Bold.ttf,sha256=7Fo-wbzMoTThzw3ivb2PiLNcB1atOZdGClX22mwHu_Q,248052
61
+ picata/static/picata/fonts/ZillaSlab-BoldItalic.ttf,sha256=xra8p0e0zYZ5gyu5w6BeR8fnOJbZkeoIRFZuRm-FpEI,255272
62
+ picata/static/picata/fonts/ZillaSlab-Light.ttf,sha256=x9c6KiQ-iPpyqCDb2yYgDqi0GW-DNM8YIyTXqehumJ8,239836
63
+ picata/static/picata/fonts/ZillaSlab-LightItalic.ttf,sha256=8pgf5nlQb5DBqfaNKDj2P7iDMRwaW80YjaC3hXb72PA,245200
64
+ picata/static/picata/fonts/ZillaSlabHighlight-Bold.ttf,sha256=MbNNCVxe1Kt-j8rJpognUmTZrh9R9je-35MgwoeL7eU,245180
65
+ picata/templates/picata/3_column.html,sha256=4SYfqTbmAtYICEulpx1RWocBCQUUn32EnZJrcxfVWwA,790
66
+ picata/templates/picata/404.html,sha256=4q8kKGGHWDk9Z8ThW2rP70T3SQrGNGRf153pvYcV7VE,230
67
+ picata/templates/picata/500.html,sha256=1xvvK2uoiZKkc2EuVVnbfg69KmrCJUdplR0vvFtWvKo,369
68
+ picata/templates/picata/_post_list.html,sha256=JpvNAOGmupLQIC6lTyjoyNOyRGMUQwOqEvEaZgSPrsM,915
69
+ picata/templates/picata/article.html,sha256=NBwXpicb_oL2xy3FN8_6KJNbrrQD2rTrNIu1ngOietk,794
70
+ picata/templates/picata/base.html,sha256=gE8pChABBlXUYfyQCkUdVZx9tFHS-cnMEWGNeaHGziI,8140
71
+ picata/templates/picata/basic_page.html,sha256=1FS_xdL6erskk9T-_TP3JWARczVstOLzs2ttvkbPbdk,173
72
+ picata/templates/picata/dl_view.html,sha256=epAV72HR2f0I3GWBEngUtYOQnHSsXs_SsNlhH7u-JbU,494
73
+ picata/templates/picata/home_page.html,sha256=nVDZwlsXfKtdSwQR80e0cHtiX7hf91ws5tYhu_ug_gU,635
74
+ picata/templates/picata/post_listing.html,sha256=3aSWYdPfwvYblE4s7b2P_dOLiiCWcrT1uvXRuDBak5Y,428
75
+ picata/templates/picata/search_results.html,sha256=P5HwdkLX2iQjZFEtD4YOp4Mun17btshjKBw__z6q8Hs,957
76
+ picata/templates/picata/split_view.html,sha256=DqRUKT5wc_-ByibQRZoyDU91omK6TgK7wC3Rp72bbgM,402
77
+ picata/templates/picata/blocks/icon_link_item.html,sha256=t-9LoA00Nv-H031yIL8es89zevbn315jlr5LTiAyM_o,301
78
+ picata/templates/picata/blocks/icon_link_list.html,sha256=7KU-hLOhAdKnoecLcugqWNzO5SgPnN9XfTDs1y60f0c,261
79
+ picata/templates/picata/blocks/icon_link_list_stream.html,sha256=B7yz5Ss_dT_5NIMsVwggnGFGmRgawNQrvnowaJUSUnw,69
80
+ picata/templates/picata/previews/3col.html,sha256=CwnWJpK5dvmVxqhBuu0QSFHWKhZlnc3xEowMQk3ltBo,4473
81
+ picata/templates/picata/previews/dl.html,sha256=ANOqlD3nlj5NF-4sPmya3eo0CNwmxgmUXswmz22Bh30,2095
82
+ picata/templates/picata/previews/split.html,sha256=hqv27u4wKTX8jJiNMazXioyRU9Uajpys_7LVQeI3NtE,2106
83
+ picata/templates/picata/previews/theme_gallery.html,sha256=f10610quPimz6xzpozvpZtdRrTpSP5KFIppuGeOZ308,8007
84
+ picata/templates/picata/tags/site_menu.html,sha256=Tb_BjV88ZH-nRb76alzSXPVUokPC9O2mVByKmeAgQWA,223
85
+ picata/templatetags/__init__.py,sha256=YNGfxmI00gewcJkkUlH219BHimHD_4p5Lz-zXPvgO-U,47
86
+ picata/templatetags/absolute_static.py,sha256=asY9uNFShD5iQCaU-ZjsgXQOJNlmLmkoOvRs2WWhEYE,454
87
+ picata/templatetags/menu_tags.py,sha256=WvsyNHyuD4oiueedWjqnej2PrAQW0FdlGeEYrYd_xbs,1267
88
+ picata/templatetags/stringify.py,sha256=QxStfcCgn29IGinH_bdsZoaiTLL7pz30ObDitlagecs,850
89
+ picata/typing/__init__.py,sha256=Nu2HZtspAQTzxMx6gmRKdDp9DfqmFc5urxfokXp2k-Y,402
90
+ picata/typing/wagtail.py,sha256=r84cf-8AzaWCMULF4HRjl5wmTFKIxw2DwvhxmIDurjA,661
91
+ picata-0.0.1.dist-info/METADATA,sha256=CzIkWSGSTfq45DELALkcS2ReZ-sxZ3sJ-vMpyfRWq7M,2996
92
+ picata-0.0.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
93
+ picata-0.0.1.dist-info/licenses/LICENSE.md,sha256=Bv8sMyZI5NI6DMrfiAvCwIFRLSfJkimLF2KVcUMteKU,1103
94
+ picata-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.27.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,24 @@
1
+ # The MIT License (MIT)
2
+
3
+ Copyright © `2024` `Ada Wright <ada@hpk.io>`
4
+
5
+ Permission is hereby granted, free of charge, to any person
6
+ obtaining a copy of this software and associated documentation
7
+ files (the “Software”), to deal in the Software without
8
+ restriction, including without limitation the rights to use,
9
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the
11
+ Software is furnished to do so, subject to the following
12
+ conditions:
13
+
14
+ The above copyright notice and this permission notice shall be
15
+ included in all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
18
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24
+ OTHER DEALINGS IN THE SOFTWARE.