Skip to content

Commit

Permalink
images: add validator which enforces alt texts on img tags
Browse files Browse the repository at this point in the history
  • Loading branch information
goapunk committed Jan 29, 2024
1 parent 27e8804 commit 65d30ce
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 0 deletions.
20 changes: 20 additions & 0 deletions adhocracy4/images/validators.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import math

import magic
from bs4 import BeautifulSoup
from django.core.exceptions import ValidationError
from django.utils.deconstruct import deconstructible
from django.utils.translation import gettext_lazy as _

image_max_mb = 5
Expand Down Expand Up @@ -74,3 +76,21 @@ def validate_image(
if errors:
raise ValidationError(errors)
return image


@deconstructible
class ImageAltTextValidator:
"""Validate that if the input contains html img tags that all have the alt
attribute set, otherwise raise ValidationError.
"""

message = (_("Please add an alternative text for all images."),)

def __call__(self, value):
soup = BeautifulSoup(value)
images = soup("img", alt=False)
if len(images) > 0:
raise ValidationError(self.message)

def __eq__(self, other):
return isinstance(other, self.__class__)
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Generated by Django 4.2 on 2024-01-29 13:20

import adhocracy4.images.validators
from django.db import migrations
import django_ckeditor_5.fields


class Migration(migrations.Migration):
dependencies = [
("a4projects", "0045_rename_field_m2mtopics_to_topics"),
]

operations = [
migrations.AlterField(
model_name="project",
name="information",
field=django_ckeditor_5.fields.CKEditor5Field(
blank=True,
help_text="This description should tell participants what the goal of the project is, how the project’s participation will look like. It will be always visible in the „Info“ tab on your project’s page.",
validators=[adhocracy4.images.validators.ImageAltTextValidator()],
verbose_name="Description of your project",
),
),
migrations.AlterField(
model_name="project",
name="result",
field=django_ckeditor_5.fields.CKEditor5Field(
blank=True,
help_text="Here you should explain what the expected outcome of the project will be and how you are planning to use the results. If the project is finished you should add a summary of the results.",
validators=[adhocracy4.images.validators.ImageAltTextValidator()],
verbose_name="Results of your project",
),
),
]
3 changes: 3 additions & 0 deletions adhocracy4/projects/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from adhocracy4 import transforms as html_transforms
from adhocracy4.administrative_districts.models import AdministrativeDistrict
from adhocracy4.images import fields
from adhocracy4.images.validators import ImageAltTextValidator
from adhocracy4.maps.fields import PointField
from adhocracy4.models import base

Expand Down Expand Up @@ -224,6 +225,7 @@ class Project(
"participation will look like. It will be always visible "
"in the „Info“ tab on your project’s page."
),
validators=[ImageAltTextValidator()],
)
result = CKEditor5Field(
blank=True,
Expand All @@ -235,6 +237,7 @@ class Project(
"results. If the project is finished you should add a "
"summary of the results."
),
validators=[ImageAltTextValidator()],
)
access = EnumField(
Access, default=Access.PUBLIC, verbose_name=_("Access to the project")
Expand Down
2 changes: 2 additions & 0 deletions changelog/7274.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
- custom migration for iframes to make them work with ckeditor5 (WARNING:
backing up your database before running is recommended).
- added dependency beautifulsoup4
- added an ImageAltTextValidator to enforce alt text on img tags. The validator
is used for project information and result.

### Changed

Expand Down
44 changes: 44 additions & 0 deletions tests/dashboard/test_form_components.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
from adhocracy4.dashboard.components.forms import ProjectDashboardForm
from adhocracy4.dashboard.components.forms import ProjectFormComponent
from adhocracy4.dashboard.forms import ProjectBasicForm
from adhocracy4.dashboard.forms import ProjectInformationForm
from adhocracy4.dashboard.forms import ProjectResultForm
from adhocracy4.images.validators import ImageAltTextValidator
from adhocracy4.modules import models as module_models
from adhocracy4.phases import models as phase_models
from adhocracy4.projects import models as project_models
Expand Down Expand Up @@ -270,3 +273,44 @@ class Component(ModuleFormSetComponent):
phase.start_date = now()
phase.save()
assert component.get_progress(module) == (1, 2)


@pytest.mark.django_db
@pytest.mark.parametrize(
"form_class, field_name",
[(ProjectInformationForm, "information"), (ProjectResultForm, "result")],
)
def test_project_information_form_image_missing_alt_text(
form_class, field_name, project_factory
):
project = project_factory(is_draft=False)
form = form_class(
instance=project,
data={
field_name: "my project description <img></img>",
},
)
assert field_name in form.errors
assert form.errors[field_name].data[0].messages[0] == str(
ImageAltTextValidator.message
)
assert not form.is_valid()


@pytest.mark.django_db
@pytest.mark.parametrize(
"form_class, field_name",
[(ProjectInformationForm, "information"), (ProjectResultForm, "result")],
)
def test_project_information_form_image_has_alt_text(
form_class, field_name, project_factory
):
project = project_factory(is_draft=False)
form = form_class(
instance=project,
data={
field_name: "my project description <img alt='descriptive picture'></img>",
},
)
assert field_name not in form.errors
assert form.is_valid()

0 comments on commit 65d30ce

Please sign in to comment.