Skip to content

Commit

Permalink
Add admin-link to block for BlockAdapter
Browse files Browse the repository at this point in the history
  • Loading branch information
Nigel2392 committed Apr 19, 2024
1 parent 4ec35ae commit 955ac8c
Show file tree
Hide file tree
Showing 15 changed files with 189 additions and 79 deletions.
67 changes: 57 additions & 10 deletions wagtail_fedit/adapters/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,12 @@
)
from django.http import (
HttpRequest,
HttpResponse,
)

from ..utils import (
FeditIFrameMixin,
from ..settings import (
SIGN_SHARED_CONTEXT,
)
from ..utils import (
FeditIFrameMixin,
wrap_adapter,
)

Expand All @@ -35,7 +34,7 @@ def content_id_from_parts(*parts: Any) -> str:
return "-".join(map(slugify, map(str, parts)))


import pickle
import pickle, json, base64



Expand All @@ -56,10 +55,21 @@ def dumps(self, obj):
def loads(self, data):
return pickle.loads(data)

class Base85JSONSerializer:
"""
Simple wrapper around base85 and json to be used in signing.dumps and
signing.loads.
"""

def dumps(self, obj):
return base64.b85encode(json.dumps(obj).encode("utf-8")).decode("utf-8")

def loads(self, data):
return json.loads(base64.b85decode(data).decode("utf-8"))

class BaseAdapter(FeditIFrameMixin):
identifier = None
signer = Signer()
signer: Signer = Signer()
# wrapper_template = None
# run_context_processors = True
required_kwargs = [] # Required keyword arguments for the adapter
Expand All @@ -77,6 +87,26 @@ def __init__(self, object: models.Model, field_name: str, request: HttpRequest,
self.request = request
self.kwargs = kwargs

@classmethod
def usage_string(cls) -> str:
"""
Return a string which describes how to use the adapter.
"""
s = []
for i, token in enumerate(cls.absolute_tokens):
s.append(f"{token}")
if i < len(cls.absolute_tokens) - 1:
s.append(" ")

if cls.absolute_tokens and cls.required_kwargs:
s.append(" ")

for i, kwarg in enumerate(cls.required_kwargs):
s.append(f"{kwarg}=value")
if i < len(cls.required_kwargs) - 1:
s.append(" ")
return "".join(s)

@property
def field_value(self):
"""
Expand Down Expand Up @@ -127,6 +157,12 @@ def get_response_data(self) -> dict:
},
}
}

def get_admin_url(self) -> str:
"""
Return the admin URL for the object.
"""
raise NotImplementedError

def get_toolbar_buttons(self) -> list["FeditToolbarComponent"]:
"""
Expand Down Expand Up @@ -190,17 +226,28 @@ def encode_shared_context(self) -> dict:
"""
if not self.kwargs:
return ""
return self.signer.sign_object(self.kwargs)# , serializer=PickleBlockSerializer)
# return self.signer.sign_object(self.kwargs)# , serializer=PickleBlockSerializer)
if SIGN_SHARED_CONTEXT:
return self.signer.sign_object(self.kwargs, compress=True)

serializer = Base85JSONSerializer()
return serializer.dumps(self.kwargs)

@classmethod
def decode_shared_context(cls, context: str) -> dict:
def decode_shared_context(cls, request: HttpRequest, object: models.Model, field: str, context: str) -> dict:
"""
Decode an encoded contex string back to a dictionary.
"""
if not context:
return {}
return cls.signer.unsign_object(context)# , serializer=PickleBlockSerializer)

if SIGN_SHARED_CONTEXT:
return cls.signer.unsign_object(context)# , serializer=PickleBlockSerializer)
try:
serializer = Base85JSONSerializer()
return serializer.loads(context)
except json.JSONDecodeError:
pass
return {}

class BlockFieldReplacementAdapter(BaseAdapter):
js_constructor = "wagtail_fedit.editors.BlockFieldEditor"
Expand Down
24 changes: 23 additions & 1 deletion wagtail_fedit/adapters/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,13 @@
blocks as block_forms,
)
from .. import utils

from wagtail.admin.admin_url_finder import (
AdminURLFinder,
)
from ..toolbar import (
FeditAdapterComponent,
FeditAdapterAdminLinkButton,
)



Expand All @@ -34,6 +40,9 @@ class BlockAdapter(BlockFieldReplacementAdapter):
"""
identifier = "block"
required_kwargs = ["block"]
absolute_tokens = [ # override; remove "inline"
"admin" # allows for displaying admin URLs
]

def __init__(self, object: models.Model, field_name: str, request: HttpRequest, **kwargs):
super().__init__(object, field_name, request, **kwargs)
Expand All @@ -56,6 +65,19 @@ def __init__(self, object: models.Model, field_name: str, request: HttpRequest,

self.block, _ = result

def get_admin_url(self) -> str:
finder = AdminURLFinder(self.request.user)
url = finder.get_edit_url(self.object)
hash = f"#block-{self.kwargs['block_id']}-section"
return f"{url}{hash}"

def get_toolbar_buttons(self) -> list[FeditAdapterComponent]:
buttons = super().get_toolbar_buttons()
buttons.append(FeditAdapterAdminLinkButton(
self.request, self,
))
return buttons

def get_header_title(self):

model_string = getattr(self.object, "get_admin_display_title", None)
Expand Down
Empty file.
28 changes: 28 additions & 0 deletions wagtail_fedit/management/commands/adapter_help.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from django.core.management.base import BaseCommand, CommandError
from django.core.management.color import color_style, supports_color
from wagtail_fedit.adapters import adapter_registry
from wagtail_fedit.utils import TEMPLATE_TAG_NAME


class Command(BaseCommand):
help = "Print an example of how to use all registered adapters."

def handle(self, *args, **options):
s = [
"Registered Adapters:",
]

for identifier, adapter_class in adapter_registry.adapters.items():
s.append(
f"\t{{% {TEMPLATE_TAG_NAME} {identifier} instance.modelfield {adapter_class.usage_string()} %}}",
)

if supports_color():
style = color_style()
s = style.SUCCESS("\n".join(s))
else:
s = "\n".join(s)
self.stdout.write(s)
self.stdout.write("\n")


11 changes: 11 additions & 0 deletions wagtail_fedit/settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from django.conf import settings



SIGN_SHARED_CONTEXT = getattr(settings, "WAGTAIL_FEDIT_SIGN_SHARED_CONTEXT", True)
"""
Sign the shared context with a secret key.
This is useful to prevent tampering with the shared context.
It will also be compressed with zlib if available.
It might not be in your site's security model to need this.
"""
2 changes: 2 additions & 0 deletions wagtail_fedit/static/wagtail_fedit/css/frontend.css
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,12 @@ html, body {
/* .wagtail-fedit-field-wrapper > .wagtail-fedit-buttons { */
/* position: relative; */
/* } */
.wagtail-fedit-adapter-wrapper > .wagtail-fedit-buttons .wagtail-fedit-toolbar-button,
.wagtail-fedit-adapter-wrapper > .wagtail-fedit-buttons button {
display: inline-block;
vertical-align: middle;
}
.wagtail-fedit-buttons .wagtail-fedit-toolbar-button,
.wagtail-fedit-buttons button {
border: none;
background-color: white;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{% load i18n %}
<a href="{{ admin_url }}" target="_blank" class="wagtail-fedit-adapter-admin-link wagtail-fedit-toolbar-button wagtail-fedit-link-button">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-box-arrow-up-right" viewBox="0 0 16 16" aria-label="{% translate "View in Wagtail admin" %}">
<!-- The MIT License (MIT) -->
<!-- Copyright (c) 2011-2024 The Bootstrap Authors -->
<path fill-rule="evenodd" d="M8.636 3.5a.5.5 0 0 0-.5-.5H1.5A1.5 1.5 0 0 0 0 4.5v10A1.5 1.5 0 0 0 1.5 16h10a1.5 1.5 0 0 0 1.5-1.5V7.864a.5.5 0 0 0-1 0V14.5a.5.5 0 0 1-.5.5h-10a.5.5 0 0 1-.5-.5v-10a.5.5 0 0 1 .5-.5h6.636a.5.5 0 0 0 .5-.5"/>
<path fill-rule="evenodd" d="M16 .5a.5.5 0 0 0-.5-.5h-5a.5.5 0 0 0 0 1h3.793L6.146 9.146a.5.5 0 1 0 .708.708L15 1.707V5.5a.5.5 0 0 0 1 0z"/>
</svg>
</a>
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{% load i18n %}
<button class="wagtail-fedit-block-edit wagtail-fedit-edit-button">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-pencil-square" viewBox="0 0 16 16" aria-label="{% translate "Edit block" %}">
<button class="wagtail-fedit-adapter-edit wagtail-fedit-toolbar-button wagtail-fedit-edit-button">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-pencil-square" viewBox="0 0 16 16" aria-label="{% translate "Edit Field" %}">
<!-- The MIT License (MIT) -->
<!-- Copyright (c) 2011-2024 The Bootstrap Authors -->
<path d="M15.502 1.94a.5.5 0 0 1 0 .706L14.459 3.69l-2-2L13.502.646a.5.5 0 0 1 .707 0l1.293 1.293zm-1.75 2.456-2-2L4.939 9.21a.5.5 0 0 0-.121.196l-.805 2.414a.25.25 0 0 0 .316.316l2.414-.805a.5.5 0 0 0 .196-.12l6.813-6.814z"/>
Expand Down

This file was deleted.

This file was deleted.

3 changes: 2 additions & 1 deletion wagtail_fedit/templatetags/fedit.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
wrap_adapter,
_can_edit,
FEDIT_PREVIEW_VAR,
TEMPLATE_TAG_NAME,
)
from ..hooks import (
REGISTER_CSS,
Expand Down Expand Up @@ -119,7 +120,7 @@ def render(self, context):
return wrap_adapter(request, adapter, context)


@register.tag(name="fedit")
@register.tag(name=TEMPLATE_TAG_NAME)
def do_render_fedit(parser: Parser, token: Token):

tokens = token.split_contents()
Expand Down
51 changes: 27 additions & 24 deletions wagtail_fedit/toolbar.py
Original file line number Diff line number Diff line change
@@ -1,65 +1,68 @@
from django.template.loader import render_to_string
from django.utils.safestring import mark_safe
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from .adapters import BaseAdapter

class FeditToolbarComponent:
template_name = None
permissions: list[str] = []

def __init__(self):
pass
def __init__(self, request):
self.request = request

def get_context_data(self, request):
def get_context_data(self):
return {
"self": self,
"request": request,
"request": self.request,
}

def is_shown(self, request):
if not all([request, request.user.is_authenticated]):
def is_shown(self):
if not all([self.request, self.request.user.is_authenticated]):
return False

if not self.permissions:
return True

return request.user.has_perms(self.permissions)
return self.request.user.has_perms(self.permissions)

def render(self, request):
if not self.is_shown(request):
def render(self):
if not self.is_shown():
return ""

return mark_safe(render_to_string(
self.template_name,
self.get_context_data(request),
self.get_context_data(),
))


class FeditBlockEditButton(FeditToolbarComponent):
class FeditAdapterComponent(FeditToolbarComponent):
def __init__(self, request, adapter: "BaseAdapter"):
super().__init__(request)
self.adapter = adapter

class FeditAdapterEditButton(FeditAdapterComponent):
"""
Required button class for the edit modal to function.
This button is handled by the script in `wagtail_fedit/js/frontend.js`
"""
template_name = "wagtail_fedit/content/buttons/edit_block.html"
template_name = "wagtail_fedit/content/buttons/edit_adapter.html"
permissions = [
"wagtailadmin.access_admin",
]


class FeditFieldEditButton(FeditToolbarComponent):
class FeditAdapterAdminLinkButton(FeditAdapterComponent):
"""
Required button class for the edit modal to function.
This button is handled by the script in `wagtail_fedit/js/frontend.js`
"""
template_name = "wagtail_fedit/content/buttons/edit_field.html"
template_name = "wagtail_fedit/content/buttons/admin_link.html"
permissions = [
"wagtailadmin.access_admin",
]

class FeditAdapterEditButton(FeditToolbarComponent):
"""
Required button class for the edit modal to function.
This button is handled by the script in `wagtail_fedit/js/frontend.js`
"""
template_name = "wagtail_fedit/content/buttons/edit_adapter.html"
permissions = [
"wagtailadmin.access_admin",
]
def get_context_data(self):
return super().get_context_data() | {
"admin_url": self.adapter.get_admin_url(),
}
Loading

0 comments on commit 955ac8c

Please sign in to comment.