Skip to content

Commit

Permalink
Add groups descriptions (#1738)
Browse files Browse the repository at this point in the history
* GroupDescription model

* WIP move group edit form to formset

* Fix tests

* Group description edit form

* User group description
  • Loading branch information
rafalp committed Feb 25, 2024
1 parent 4a429fe commit f865276
Show file tree
Hide file tree
Showing 22 changed files with 752 additions and 47 deletions.
1 change: 1 addition & 0 deletions dev-docs/plugins/hooks/reference.md
Expand Up @@ -52,4 +52,5 @@ Hooks instances are importable from the following Python modules:
- [`create_group_hook`](./create-group-hook.md)
- [`delete_group_hook`](./delete-group-hook.md)
- [`set_default_group_hook`](./set-default-group-hook.md)
- [`update_group_description_hook`](./update-group-description-hook.md)
- [`update_group_hook`](./update-group-hook.md)
124 changes: 124 additions & 0 deletions dev-docs/plugins/hooks/update-group-description-hook.md
@@ -0,0 +1,124 @@
# `update_group_description_hook`

This hook wraps the standard function that Misago uses to update user group's description.


## Location

This hook can be imported from `misago.users.hooks`:

```python
from misago.users.hooks import update_group_description_hook
```


## Filter

```python
def custom_update_group_description_filter(
action: UpdateGroupDescriptionHookAction, group: Group, **kwargs
) -> Group:
...
```

A function implemented by a plugin that can be registered in this hook.


### Arguments

#### `action: UpdateGroupDescriptionHookAction`

A standard Misago function used to update an existing user group's description or the next filter function from another plugin.

See the [action](#action) section for details.


#### `group: Group`

A group instance to update.


#### `**kwargs`

A `dict` with group description's attributes to update.


### Additional arguments

Optional arguments in `kwargs` that are not used to update the group but are still provided for use by plugins:


#### `request: Optional[HttpRequest]`

The request object or `None` if it was not provided.


#### `form: Optional[Form]`

Bound `Form` instance that was used to update this group's description.


### Return value

An updated `Group` instance.


## Action

```python
def update_group_description_action(group: Group, **kwargs) -> Group:
...
```

A standard Misago function used to update an existing user group's description or the next filter function from another plugin.


### Arguments

#### `group: Group`

A group instance to update.


#### `**kwargs`

A `dict` with group description's attributes to update.


### Additional arguments

Optional arguments in `kwargs` that are not used to update the group but are still provided for use by plugins:


#### `request: Optional[HttpRequest]`

The request object or `None` if it was not provided.


#### `form: Optional[Form]`

Bound `Form` instance that was used to update this group's description.


### Return value

An updated `Group` instance.


## Example

The code below implements a custom filter function that stores an ID of user who last modified the group description, if its available:

```python
from django.http import HttpRequest
from misago.users.models import Group

@update_group_description_hook.append_filter
def set_group_description_updated_by_id(action, **kwargs) -> Group:
# request key is guaranteed to be set in `kwargs`
if kwargs["request"] and kwargs["request"].user.id:
group.description.plugin_data["updated_by"] = kwargs["request"].user.id

# Call the next function in chain
return action(group, **kwargs)
```
5 changes: 2 additions & 3 deletions dev-docs/plugins/hooks/update-group-hook.md
Expand Up @@ -111,11 +111,10 @@ The code below implements a custom filter function that stores an ID of user who

```python
from django.http import HttpRequest
from misago.postgres.delete import delete_all
from misago.users.models import Group

@create_group_hook.append_filter
def set_group_creator_id(action, **kwargs) -> Group:
@update_group_hook.append_filter
def set_group_updated_by_id(action, **kwargs) -> Group:
# request key is guaranteed to be set in `kwargs`
if kwargs["request"] and kwargs["request"].user.id:
group.plugin_data["updated_by"] = kwargs["request"].user.id
Expand Down
74 changes: 73 additions & 1 deletion misago/admin/groups/forms.py
Expand Up @@ -3,7 +3,13 @@
from django.utils.translation import pgettext_lazy

from ...core.validators import validate_color_hex, validate_css_name, validate_sluggable
from ...users.models import Group
from ...parser.context import create_parser_context
from ...parser.enums import ContentType
from ...parser.factory import create_parser
from ...parser.html import render_ast_to_html
from ...parser.metadata import create_ast_metadata
from ...parser.plaintext import PlainTextFormat, render_ast_to_plaintext
from ...users.models import Group, GroupDescription
from ..forms import YesNoSwitch


Expand Down Expand Up @@ -130,3 +136,69 @@ def __init__(self, *args, **kwargs):
self.fields["copy_permissions"].queryset = Group.objects.exclude(
id=kwargs["instance"].id
)


class EditGroupDescriptionForm(forms.ModelForm):
markdown = forms.CharField(
label=pgettext_lazy("admin group form", "Description"),
help_text=pgettext_lazy(
"admin group form",
"Optional. Group's description in Markdown that will be parsed into HTML displayed on the group's page.",
),
required=False,
widget=forms.Textarea(attrs={"rows": 4}),
)
meta = forms.CharField(
label=pgettext_lazy("admin group form", "Meta description"),
help_text=pgettext_lazy(
"admin group form",
"Optional. Will be used verbatim for the group page's meta description. Leave empty to generate one from the group's description.",
),
required=False,
widget=forms.Textarea(attrs={"rows": 2}),
)

class Meta:
model = GroupDescription
fields = [
"markdown",
"meta",
]

def __init__(self, *args, request, **kwargs):
self.request = request

self.context = None
self.ast = None
self.metadata = None

super().__init__(*args, **kwargs)

def clean(self):
data = super().clean()

if data.get("markdown"):
context = create_parser_context(
self.request,
content_type=ContentType.GROUP_DESCRIPTION,
)
parse = create_parser(context)
ast = parse(data["markdown"])
metadata = create_ast_metadata(context, ast)
data["html"] = render_ast_to_html(context, ast, metadata)

if not data.get("meta"):
data["meta"] = render_ast_to_plaintext(
context, ast, metadata, PlainTextFormat.META_DESCRIPTION
)

self.context = context
self.ast = ast
self.metadata = metadata
else:
data.update({"markdown": None, "html": None})

if not data.get("meta"):
data["meta"] = None

return data
53 changes: 47 additions & 6 deletions misago/admin/groups/views.py
Expand Up @@ -7,6 +7,7 @@
from ...cache.versions import invalidate_cache
from ...categories.enums import CategoryTree
from ...categories.models import Category
from ...forms.formset import BasicFormset
from ...permissions.admin import get_admin_category_permissions
from ...permissions.copy import copy_group_permissions
from ...permissions.models import CategoryGroupPermission, Moderator
Expand All @@ -17,10 +18,11 @@
delete_group,
set_default_group,
update_group,
update_group_description,
)
from ...users.models import Group
from ..views import generic
from .forms import EditGroupForm, NewGroupForm
from .forms import EditGroupDescriptionForm, EditGroupForm, NewGroupForm

INVALID_DEFAULT_GROUP_IDS = (
DefaultGroupId.ADMINS,
Expand Down Expand Up @@ -97,22 +99,61 @@ def handle_form(self, form, request, target):

class EditView(GroupAdmin, generic.ModelFormView):
template_name = "edit.html"
form_class = EditGroupForm
message_submit = pgettext_lazy("admin groups", '"%(name)s" group has been updated.')

def handle_form(self, form, request, target):
copy_permissions = form.cleaned_data.pop("copy_permissions", None)
def get_form(self, form_class, request, target):
formset = BasicFormset()

if request.method == "POST":
group_form = EditGroupForm(
request.POST,
request.FILES,
instance=target,
prefix="group",
)
description_form = EditGroupDescriptionForm(
request.POST,
request.FILES,
instance=target.description,
prefix="description",
request=request,
)
else:
group_form = EditGroupForm(instance=target, prefix="group")
description_form = EditGroupDescriptionForm(
instance=target.description,
prefix="description",
request=request,
)

formset.add_form(group_form)
formset.add_form(description_form)

return formset

def handle_form(self, formset, request, target):
group_form = formset["group"]
description_form = formset["description"]

copy_permissions = group_form.cleaned_data.pop("copy_permissions", None)

target = update_group(
target,
request=request,
form=form,
**form.cleaned_data,
form=group_form,
**group_form.cleaned_data,
)

if copy_permissions:
copy_group_permissions(copy_permissions, target, request)

update_group_description(
target,
request=request,
form=description_form,
**description_form.cleaned_data,
)

invalidate_cache(CacheName.GROUPS, CacheName.PERMISSIONS)

messages.success(request, self.message_submit % {"name": target.name})
Expand Down
29 changes: 19 additions & 10 deletions misago/admin/templates/misago/admin/groups/edit.html
Expand Up @@ -35,37 +35,46 @@
<fieldset>
<legend>{% trans "Basic settings" context "admin group form" %}</legend>

{% form_row form.name %}
{% form_row form.slug %}
{% form_row form.group.name %}
{% form_row form.group.slug %}

</fieldset>
</div>
<div class="form-fieldset">
<fieldset>
<legend>{% trans "Description" context "admin group form" %}</legend>

{% form_row form.description.markdown %}
{% form_row form.description.meta %}

</fieldset>
</div>
<div class="form-fieldset">
<fieldset>
<legend>{% trans "Display and appearance" context "admin group form" %}</legend>

{% form_row form.user_title %}
{% form_row form.color %}
{% form_row form.icon %}
{% form_row form.css_suffix %}
{% form_row form.is_page %}
{% form_row form.is_hidden %}
{% form_row form.group.user_title %}
{% form_row form.group.color %}
{% form_row form.group.icon %}
{% form_row form.group.css_suffix %}
{% form_row form.group.is_page %}
{% form_row form.group.is_hidden %}

</fieldset>
</div>
<div class="form-fieldset">
<fieldset>
<legend>{% trans "Permissions" context "admin group form" %}</legend>

{% form_row form.copy_permissions %}
{% form_row form.group.copy_permissions %}

</fieldset>
</div>
<div class="form-fieldset">
<fieldset>
<legend>{% trans "User profiles" context "admin group form" %}</legend>

{% form_row form.can_see_user_profiles %}
{% form_row form.group.can_see_user_profiles %}

</fieldset>
</div>
Expand Down

0 comments on commit f865276

Please sign in to comment.