Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

* [PR-564](https://github.com/itk-dev/deltag.aarhus.dk/pull/564)
Added hoeringsportal_anonymous_edit module
* [PR-557](https://github.com/itk-dev/deltag.aarhus.dk/pull/557)
* Change dialogue proposal backend
* Add seperate view for dialogue proposal comments
Expand Down
2 changes: 2 additions & 0 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,8 @@ tasks:
# We need the pretix service to load public meeting fixtures (cf. https://docs.docker.com/compose/how-tos/environment-variables/envvars/#compose_profiles)
- COMPOSE_PROFILES=pretix COMPOSE_UP_WAIT=true task compose-up
- task drush -- --yes pm:enable hoeringsportal_base_fixtures $(find web/modules/custom -type f -name 'hoeringsportal_*_fixtures.info.yml' -exec basename -s .info.yml {} \;)
- task drush -- sql:query "TRUNCATE hoeringsportal_anonymous_edit_content"
- task drush -- sql:query "TRUNCATE hoeringsportal_anonymous_edit_owners"
- task drush -- --yes content-fixtures:load
- task drush -- --yes pm:uninstall content_fixtures
# Update states on public meetings.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ targetEntityType: comment
bundle: early_inclusion_comment
mode: default
content:
author:
weight: 1
region: content
settings: { }
third_party_settings: { }
field_comment:
type: string_textarea
weight: 0
Expand All @@ -19,6 +24,5 @@ content:
placeholder: ''
third_party_settings: { }
hidden:
author: true
langcode: true
subject: true
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ dependencies:
- field.field.node.dialogue_proposal.field_dialogue_proposal_descr
- field.field.node.dialogue_proposal.field_image_upload
- field.field.node.dialogue_proposal.field_location
- field.field.node.dialogue_proposal.field_owner_email
- field.field.node.dialogue_proposal.field_owner_name
- image.style.thumbnail
- node.type.dialogue_proposal
module:
Expand Down Expand Up @@ -61,6 +63,22 @@ content:
localplanids: 0
localplanids_node: 0
third_party_settings: { }
field_owner_email:
type: email_default
weight: 27
region: content
settings:
placeholder: ''
size: 60
third_party_settings: { }
field_owner_name:
type: string_textfield
weight: 26
region: content
settings:
size: 60
placeholder: ''
third_party_settings: { }
title:
type: string_textfield
weight: 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ dependencies:
- field.field.node.dialogue_proposal.field_dialogue_proposal_descr
- field.field.node.dialogue_proposal.field_image_upload
- field.field.node.dialogue_proposal.field_location
- field.field.node.dialogue_proposal.field_owner_email
- field.field.node.dialogue_proposal.field_owner_name
- node.type.dialogue_proposal
module:
- comment
Expand Down Expand Up @@ -75,6 +77,21 @@ content:
third_party_settings: { }
weight: 4
region: content
field_owner_email:
type: basic_string
label: hidden
settings: { }
third_party_settings: { }
weight: 8
region: content
field_owner_name:
type: string
label: hidden
settings:
link_to_entity: false
third_party_settings: { }
weight: 7
region: content
flag_support_proposal:
settings: { }
third_party_settings: { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ dependencies:
- field.field.node.dialogue_proposal.field_dialogue_proposal_descr
- field.field.node.dialogue_proposal.field_image_upload
- field.field.node.dialogue_proposal.field_location
- field.field.node.dialogue_proposal.field_owner_email
- field.field.node.dialogue_proposal.field_owner_name
- node.type.dialogue_proposal
module:
- user
Expand Down Expand Up @@ -43,6 +45,8 @@ hidden:
field_dialogue: true
field_image_upload: true
field_location: true
field_owner_email: true
field_owner_name: true
langcode: true
links: true
published_at: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ dependencies:
- field.field.node.dialogue_proposal.field_dialogue_proposal_descr
- field.field.node.dialogue_proposal.field_image_upload
- field.field.node.dialogue_proposal.field_location
- field.field.node.dialogue_proposal.field_owner_email
- field.field.node.dialogue_proposal.field_owner_name
- node.type.dialogue_proposal
module:
- user
Expand All @@ -30,6 +32,8 @@ hidden:
field_dialogue_proposal_descr: true
field_image_upload: true
field_location: true
field_owner_email: true
field_owner_name: true
langcode: true
links: true
published_at: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ dependencies:
- field.field.node.dialogue_proposal.field_dialogue_proposal_descr
- field.field.node.dialogue_proposal.field_image_upload
- field.field.node.dialogue_proposal.field_location
- field.field.node.dialogue_proposal.field_owner_email
- field.field.node.dialogue_proposal.field_owner_name
- node.type.dialogue_proposal
module:
- user
Expand Down Expand Up @@ -40,6 +42,8 @@ hidden:
field_dialogue_proposal_descr: true
field_image_upload: true
field_location: true
field_owner_email: true
field_owner_name: true
langcode: true
published_at: true
search_api_excerpt: true
1 change: 1 addition & 0 deletions config/sync/core.extension.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ module:
file_resup: 0
filter: 0
flag: 0
hoeringsportal_anonymous_edit: 0
hoeringsportal_audit_log: 0
hoeringsportal_citizen_proposal: 0
hoeringsportal_citizen_proposal_archiving: 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ default_value_callback: ''
settings:
default_mode: 1
per_page: 50
anonymous: 0
anonymous: 1
form_location: true
preview: 0
field_type: comment
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
uuid: 35add208-8c2f-4144-b903-93fed0eb95b6
langcode: da
status: true
dependencies:
config:
- field.storage.node.field_owner_email
- node.type.dialogue_proposal
id: node.dialogue_proposal.field_owner_email
field_name: field_owner_email
entity_type: node
bundle: dialogue_proposal
label: Email
description: ''
required: false
translatable: false
default_value: { }
default_value_callback: ''
settings: { }
field_type: email
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
uuid: 83eab418-a994-48a6-a9af-650b0e49fa99
langcode: da
status: true
dependencies:
config:
- field.storage.node.field_owner_name
- node.type.dialogue_proposal
id: node.dialogue_proposal.field_owner_name
field_name: field_owner_name
entity_type: node
bundle: dialogue_proposal
label: Name
description: ''
required: false
translatable: false
default_value: { }
default_value_callback: ''
settings: { }
field_type: string
12 changes: 12 additions & 0 deletions config/sync/field.storage.node.field_dialogue_proposal_config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@ settings:
-
value: use_image_on_proposals
label: 'Anvend billede på dialogforslag'
-
value: use_email_on_proposals
label: 'Use email on proposals'
-
value: use_name_on_proposals
label: 'Use name on proposals'
-
value: use_email_on_proposal_comments
label: 'Use email on proposal comments'
-
value: use_name_on_proposal_comments
label: 'Use name on proposal comments'
allowed_values_function: ''
module: options
locked: false
Expand Down
18 changes: 18 additions & 0 deletions config/sync/field.storage.node.field_owner_email.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
uuid: a71e22bc-742a-48a5-b517-67bfbfac116c
langcode: da
status: true
dependencies:
module:
- node
id: node.field_owner_email
field_name: field_owner_email
entity_type: node
type: email
settings: { }
module: core
locked: false
cardinality: 1
translatable: true
indexes: { }
persist_with_no_fields: false
custom_storage: false
21 changes: 21 additions & 0 deletions config/sync/field.storage.node.field_owner_name.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
uuid: 9af78cfb-064e-46cc-9a39-9b10d6c19519
langcode: da
status: true
dependencies:
module:
- node
id: node.field_owner_name
field_name: field_owner_name
entity_type: node
type: string
settings:
max_length: 255
case_sensitive: false
is_ascii: false
module: core
locked: false
cardinality: 1
translatable: true
indexes: { }
persist_with_no_fields: false
custom_storage: false
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
uuid: 16550f09-0b5f-4b26-9763-f22c0b980ff4
langcode: da
status: true
dependencies:
module:
- hoeringsportal_anonymous_edit
id: hoeringsportal_anonymous_edit.content_recover
configuration:
email_body:
content:
value: "Gå til <a href=\"{{ recover_url }}\">{{ recover_url }}</a> for at finde dit eget indhold på deltag.aarhus.dk.\r\n"
format: email_html
email_subject:
value: 'Dit indhold på deltag.aarhus.dk'
100 changes: 100 additions & 0 deletions web/modules/custom/hoeringsportal_anonymous_edit/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Anonymous edit

This module keeps track of anonymous user's content and allows them to update
and delete it.

The edit access is based on a long-lived cookie,
`hoeringsportal_anonymous_edit_token`, in a browser. If the user deletes the
cookie (or uses another browser), the cookie can be restored (or recovered?) via
a URL sent to the user's email address (see [Recovering the edit
token](#recovering-the-edit-token)).

When content, i.e. an instance of
[`EntityInterface`](https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Entity%21EntityInterface.php/interface/EntityInterface/11.x),
is created by an anonymous user, the module dispatches an
`HoeringsportalAnonymousEditEvent` event to let other modules tell if the
content supports editing by anonymous users. It supported, the module ensures
that an edit token (cookie) is set in the users browser and the token is
attached to the content and any future content created by the user.

In addition to telling if the content is supported, the event subscriber can
also set an email address to be associated with the content. This email can
later be used to recover the edit token if need be.

See
[AnonymousEditSubscriber.php](../hoeringsportal_dialogue/src/EventSubscriber/AnonymousEditSubscriber.php)
for an example event subscriber implementation.

The module implements
[`hook_entity_access`](https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Entity%21entity.api.php/function/hook_entity_access/11.x)
to allow anonymous users to update and delete content matching the current edit
token.

## Configuration

Anonymous user names (!) are generated on demand using a string format pattern:

``` php
# settings.local.php
$settings['hoeringsportal_anonymous_edit']['owner_name_pattern'] = 'Bruger %1$d'; // The default value
```

## Content list

An anonymous user with a valid edtit token can find a list of its content on
`/hoeringsportal-anonymous-edit/content`. The content is grouped by content type
and bundle.

The content list uses the
[`hoeringsportal-anonymous-edit-content-index.html.twig`](templates/hoeringsportal-anonymous-edit-content-index.html.twig)
template file which can — and probably should be — overridden in the theme.

## Recovering the edit token

If the user looses its edit token, the token can be recovered if an email has
been attached to a piece of content created by the user.

The `hoeringsportal_anonymous_edit.content_request` route
(`/hoeringsportal-anonymous-edit/content/request`) lets the user enter an email
address and request an email with a link to recover the edit token.

The `hoeringsportal_anonymous_edit.content_recover` route
(`/hoeringsportal-anonymous-edit/content/recover/{token}`) asks the user to
confirm the email address, and if the email matches the token, the edit token is
restored and set as en edit token.

The recover email subject and body is managed on
`/admin/config/system/mailer/policy/hoeringsportal_anonymous_edit.content_recover`.
Twig can be used in both fields and the following variable is available (cf.
[src/Plugin/EmailBuilder/EmailBuilder.php](src/Plugin/EmailBuilder/EmailBuilder.php)):

| Name | Description |
|-------------|--------------------------|
| recover_url | The absolute recover URL |

## Development

By default only errors (and higher levels) are logged. During development the
module's log level can be set lower, e.g.

``` php
# settings.local.php
$settings['hoeringsportal_anonymous_edit']['log_level'] = \Drupal\Core\Logger\RfcLogLevel::DEBUG;
```

Show log messages with

``` shell
drush watchdog:show --type=hoeringsportal_anonymous_edit --extended
```

Peek in the database table:

``` shell
drush sql:query --extra=--table "SELECT * FROM hoeringsportal_anonymous_edit"
```

---

* [ ] What happens if the same email is attached to multiple tokens?
* [ ] What happens if a user has used multiple tokens?
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
name: "hoeringsportal_anonymous_edit"
type: module
description: "Allow anonymous users to edit their content"
package: Custom
core_version_requirement: ^10 || ^11

dependencies:
- drupal:symfony_mailer
Loading