Skip to content

Commit

Permalink
Richtext product page fields (#7754)
Browse files Browse the repository at this point in the history
* updated some fields to rich text, and updated criterion files to reflect richtext

* migrations

* Update product_criterion.html

* updated css

* updated css

* Update product_criterion.html

* updated classnames and updated css for only p elements.

* updated factory for products to use rich text, and also added migration to update existing fields with content

* updated defaults for tests and percy

* Update product.scss

* Updated AI helptext for richtext

* Update buyersguide.py

* Update buyersguide.py

* Update 0049_pni_criterion_richtext_fields.py

* implemented review feedback

* updated two more fields

* user can control section was missing helptext

Co-authored-by: Daniel Miranda <daniel@mozillafoundation.org>
  • Loading branch information
danielfmiranda and Daniel Miranda authored Nov 9, 2021
1 parent 3c04321 commit b4d04d1
Show file tree
Hide file tree
Showing 9 changed files with 207 additions and 64 deletions.
4 changes: 2 additions & 2 deletions network-api/networkapi/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,9 +280,9 @@ def setUp(self):
data_control_policy_is_bad=True,
company_track_record='Needs Improvement',
track_record_is_bad=True,
track_record_details='What kind of track record are we talking about?',
track_record_details='<p> What kind of track record are we talking about? </p>',
offline_capable='Yes',
offline_use_description='Although it is unclear how offline capabilities work',
offline_use_description='<p> Although it is unclear how offline capabilities work </p>',
uses_ai='NA',
ai_is_transparent='No',
ai_helptext='The AI is a black box and no one knows how it works',
Expand Down
4 changes: 2 additions & 2 deletions network-api/networkapi/wagtailpages/factory/buyersguide.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,9 +217,9 @@ def create_general_product_visual_regression_product(seed, pni_homepage):
data_control_policy_is_bad=True,
company_track_record='Needs Improvement',
track_record_is_bad=True,
track_record_details='What kind of track record are we talking about?',
track_record_details='<p> What kind of track record are we talking about? </p>',
offline_capable='Yes',
offline_use_description='Although it is unclear how offline capabilities work',
offline_use_description='<p> Although it is unclear how offline capabilities work </p>',
uses_ai='NA',
ai_is_transparent='No',
ai_helptext='The AI is a black box and no one knows how it works',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Generated by Django 3.1.11 on 2021-10-28 02:20

from django.db import migrations
import wagtail.core.fields


class Migration(migrations.Migration):

dependencies = [
('wagtailpages', '0050_auto_20211104_2126'),
]

operations = [
migrations.AlterField(
model_name='generalproductpage',
name='ai_helptext',
field=wagtail.core.fields.RichTextField(blank=True, help_text='Helpful text around AI to show on the product page', max_length=5000),
),
migrations.AlterField(
model_name='generalproductpage',
name='how_can_you_control_your_data',
field=wagtail.core.fields.RichTextField(blank=True, help_text='How does this product let you control your data?', max_length=5000),
),
migrations.AlterField(
model_name='generalproductpage',
name='offline_use_description',
field=wagtail.core.fields.RichTextField(blank=True, help_text='Describe how this product can be used offline.', max_length=5000),
),
migrations.AlterField(
model_name='generalproductpage',
name='track_record_details',
field=wagtail.core.fields.RichTextField(blank=True, help_text='Describe the track record of this company here.', max_length=5000),
),
migrations.AlterField(
model_name='productpage',
name='how_does_it_use_data_collected',
field=wagtail.core.fields.RichTextField(blank=True, help_text='How does this product use the data collected?', max_length=5000),
),
migrations.AlterField(
model_name='productpage',
name='manage_vulnerabilities_helptext',
field=wagtail.core.fields.RichTextField(blank=True, max_length=5000),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# This file is made to loop through all existing PNI products and update the following fields to richtext:
# - Track record description
# - How does the company use this data?
# - How can you control your data?
# - General AI description
# - Manages security vulnerabilities Description

from django.db import migrations
from networkapi.wagtailpages.utils import get_default_locale


# Forward migration content handler
def update_fields_to_rich_text(product_qs):

for product in product_qs:

# If the page has no fields that need updating, do not save. This will speed up the process.
needs_saving = False

if hasattr(product, 'how_does_it_use_data_collected') and product.how_does_it_use_data_collected != "":
product.how_does_it_use_data_collected = f"<p> {product.how_does_it_use_data_collected} </p>"
needs_saving = True

elif hasattr(product, 'manage_vulnerabilities_helptext') and product.manage_vulnerabilities_helptext != "":
product.manage_vulnerabilities_helptext = f"<p> {product.manage_vulnerabilities_helptext} </p>"
needs_saving = True

elif hasattr(product, 'track_record_details') and product.track_record_details != "":
product.track_record_details = f"<p> {product.track_record_details} </p>"
needs_saving = True

elif hasattr(product, 'offline_use_description') and product.offline_use_description != "":
product.offline_use_description = f"<p> {product.offline_use_description} </p>"
needs_saving = True

elif hasattr(product, 'ai_helptext') and product.ai_helptext != "":
product.ai_helptext = f"<p> {product.ai_helptext} </p>"
needs_saving = True

if needs_saving:
product.save()
print(f"Saved product: {product.title}")




def gather_products_and_update_fields(apps, schema):
GeneralProductPage = apps.get_model('wagtailpages', 'GeneralProductPage')
SoftwareProductPage = apps.get_model('wagtailpages', 'SoftwareProductPage')

(DEFAULT_LOCALE, DEFAULT_LOCALE_ID) = get_default_locale()

product_pages = [
GeneralProductPage.objects.filter(locale_id=DEFAULT_LOCALE_ID),
SoftwareProductPage.objects.filter(locale_id=DEFAULT_LOCALE_ID)
]

for product_set in product_pages:
update_fields_to_rich_text(product_set)



class Migration(migrations.Migration):

dependencies = [
('wagtailpages', '0051_auto_20211028_0220'),
]

operations = [
migrations.RunPython(
code=gather_products_and_update_fields
)
]
31 changes: 18 additions & 13 deletions network-api/networkapi/wagtailpages/pagemodels/products.py
Original file line number Diff line number Diff line change
Expand Up @@ -526,9 +526,10 @@ class ProductPage(AirtableMixin, FoundationMetadataPageMixin, Page):
)

# How does it use this data?
how_does_it_use_data_collected = models.TextField(
verbose_name='how does the company use this data?',
how_does_it_use_data_collected = RichTextField(
max_length=5000,
features=['bold', 'italic', 'link'],
help_text='How does this product use the data collected?',
blank=True,
)
data_collection_policy_is_bad = models.BooleanField(
Expand Down Expand Up @@ -581,10 +582,10 @@ class ProductPage(AirtableMixin, FoundationMetadataPageMixin, Page):
manage_vulnerabilities = ExtendedYesNoField(
verbose_name='manages security vulnerabilities',
)
manage_vulnerabilities_helptext = models.TextField(
verbose_name='description',
manage_vulnerabilities_helptext = RichTextField(
max_length=5000,
blank=True
features=['bold', 'italic', 'link'],
blank=True,
)
privacy_policy = ExtendedYesNoField(
)
Expand Down Expand Up @@ -1141,9 +1142,10 @@ class GeneralProductPage(ProductPage):

# How can you control your data

how_can_you_control_your_data = models.TextField(
verbose_name='how can you control your data?',
how_can_you_control_your_data = RichTextField(
max_length=5000,
features=['bold', 'italic', 'link'],
help_text='How does this product let you control your data?',
blank=True,
)

Expand All @@ -1166,9 +1168,10 @@ class GeneralProductPage(ProductPage):
verbose_name='mini-ding for bad track record'
)

track_record_details = models.TextField(
verbose_name='known track record description',
track_record_details = RichTextField(
max_length=5000,
features=['bold', 'italic', 'link'],
help_text='Describe the track record of this company here.',
blank=True,
)

Expand All @@ -1178,9 +1181,10 @@ class GeneralProductPage(ProductPage):
verbose_name='can this product be used offline?',
)

offline_use_description = models.TextField(
verbose_name='offline description',
offline_use_description = RichTextField(
max_length=5000,
features=['bold', 'italic', 'link'],
help_text='Describe how this product can be used offline.',
blank=True,
)

Expand All @@ -1189,9 +1193,10 @@ class GeneralProductPage(ProductPage):
uses_ai = ExtendedYesNoField(
verbose_name='does the product use AI?',
)
ai_helptext = models.TextField(
verbose_name='general AI description',
ai_helptext = RichTextField(
max_length=5000,
features=['bold', 'italic', 'link'],
help_text='Helpful text around AI to show on the product page',
blank=True,
)
ai_is_untrustworthy = ExtendedYesNoField(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,18 +213,18 @@ <h3 class="tw-mb-4 tw-font-zilla tw-text-2xl tw-leading-7">{% trans "What data d
</section>

{% trans "How does the company use this data?" as how_does_it_use_data_collected %}
{% include "fragments/product_criterion.html" with label=how_does_it_use_data_collected value=product.how_does_it_use_data_collected ding=product.data_collection_policy_is_bad %}
{% include "fragments/product_criterion.html" with label=how_does_it_use_data_collected value=product.how_does_it_use_data_collected ding=product.data_collection_policy_is_bad rich_text_value=True %}

{% if product.product_type == 'general' %}

{% trans "How can you control your data?" as how_can_you_control_your_data %}
{% include "fragments/product_criterion.html" with label=how_can_you_control_your_data value=product.how_can_you_control_your_data ding=product.data_control_policy_is_bad %}
{% include "fragments/product_criterion.html" with label=how_can_you_control_your_data value=product.how_can_you_control_your_data ding=product.data_control_policy_is_bad rich_text_value=True %}

{% trans "What is the company’s known track record of protecting users’ data?" as company_track_record_label %}
{% include "fragments/product_criterion.html" with label=company_track_record_label value=product.company_track_record|track_record help=product.track_record_details ding=product.track_record_is_bad %}
{% include "fragments/product_criterion.html" with label=company_track_record_label value=product.company_track_record|track_record help=product.track_record_details ding=product.track_record_is_bad rich_help_text=True %}

{% trans "Can this product be used offline?" as offline_capable %}
{% include "fragments/product_criterion.html" with label=offline_capable value=product.offline_capable help=product.offline_use_description %}
{% include "fragments/product_criterion.html" with label=offline_capable value=product.offline_capable help=product.offline_use_description rich_help_text=True %}

{% endif %}

Expand All @@ -250,7 +250,7 @@ <h3 class="tw-mb-4 tw-font-zilla tw-text-2xl tw-leading-7">{% trans "What data d
{% include "fragments/product_criterion.html" with show_value_as_symbol=False value=product.uses_encryption help=product.uses_encryption_helptext label=encryption %}
{% include "fragments/product_criterion.html" with show_value_as_symbol=False value=product.strong_password help=product.strong_password_helptext label=strong_password %}
{% include "fragments/product_criterion.html" with show_value_as_symbol=False value=product.security_updates help=product.security_updates_helptext label=security_updates %}
{% include "fragments/product_criterion.html" with show_value_as_symbol=False value=product.manage_vulnerabilities help=product.manage_vulnerabilities_helptext label=manages_vulnerabilities %}
{% include "fragments/product_criterion.html" with show_value_as_symbol=False value=product.manage_vulnerabilities help=product.manage_vulnerabilities_helptext label=manages_vulnerabilities rich_help_text=True %}
{% include "fragments/product_criterion.html" with show_value_as_symbol=False value=product.privacy_policy help=product.privacy_policy_helptext label=privacy_policy %}
</div>

Expand All @@ -259,7 +259,7 @@ <h3 class="tw-mb-4 tw-font-zilla tw-text-2xl tw-leading-7">{% trans "What data d
<section class="criterion-group">
{% with product.uses_ai|extended_yes_no as uses_ai %}
{% trans "Does the product use AI?" as uses_ai_label %}
{% include "fragments/product_criterion.html" with label=uses_ai_label value=uses_ai info="/privacynotincluded/about/methodology" %}
{% include "fragments/product_criterion.html" with label=uses_ai_label value=uses_ai info="/privacynotincluded/about/methodology" help=product.ai_helptext rich_help_text=True %}

{% if uses_ai != "No" %}
{% trans "Is this AI untrustworthy?" as ai_is_untrustworthy_label %}
Expand All @@ -275,10 +275,9 @@ <h3 class="tw-mb-4 tw-font-zilla tw-text-2xl tw-leading-7">{% trans "What kind o
</section>

{% trans "Does the user have control over the AI features?" as ai_is_transparent %}
{% include "fragments/product_criterion.html" with label=ai_is_transparent value=product.ai_is_transparent|extended_yes_no %}
{% include "fragments/product_criterion.html" with label=ai_is_transparent value=product.ai_is_transparent|extended_yes_no help=product.ai_can_user_control_helptext rich_help_text=True %}

<p class="pni-product-smaller-body mb-0">{{product.ai_helptext}}</p>
{% endif %}
{% endif %}
{% endwith %}
</section>
{% endif %}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
{% load i18n %}
{% load i18n wagtailcore_tags %}

{% if value != hide_value %}
<section class="criterion value-{{value|lower}} {% if no_border %}no-border{% endif%} {% if ding %}show-ding{% endif %} {% if class %}{{class}}{% endif %}" {% if ding %}aria-label="{% trans "This section requires attention." %}"{% endif %}>
{% include "./product_criterion_primary_info.html" with show_value_as_symbol=show_value_as_symbol label=label value=value info=info %}
{% include "./product_criterion_primary_info.html" with show_value_as_symbol=show_value_as_symbol label=label value=value rich_text_value=rich_text_value %}

{% if help != None and help != "" %}
<p class="pni-product-helptext mb-0 mt-3">{{ help }}</p>
{% if rich_help_text %}
<div class="criterion-richtext-help mb-0 mt-3">{{ help|richtext }}</div>
{% else %}
<p class="pni-product-helptext mb-0 mt-3">{{ help }}</p>
{% endif %}
{% endif %}
</section>
{% endif %}
Original file line number Diff line number Diff line change
@@ -1,42 +1,47 @@
{% load i18n static %}
{% load i18n static wagtailcore_tags %}

<div class="primary-info">
<h3 class="tw-mb-4 tw-font-zilla tw-text-2xl tw-leading-7 tw-flex tw-items-center">
{{ label }}
{% if info != None and info != "" %}
<a href="{{info}}">
<img src="{% static "_images/buyers-guide/icon-info-circle.svg" %}" alt="{% trans "information" context "icon description for screen readers" %}" class="tw-h-4 tw-w-4 tw-ml-2 tw-mb-1">
</a>
{% endif %}
</h3>

<p class="rating pni-product-smaller-body mb-0">
{% if value == "CD" %}
{% if show_value_as_symbol %}
<img src="{% static "_images/buyers-guide/score/icon-unknown.svg" %}" alt="{% trans "Can’t determine" context "Unknown rating" %}">
{% else %}
{% trans "Can’t determine" context "Unknown rating" %}
{% endif %}
{% elif value == "NA" %}
{% trans "Not applicable" %}
{% elif value == "Yes" %}
{% if show_value_as_symbol %}
<img src="{% static "_images/buyers-guide/score/icon-yes.svg" %}" alt="{% trans "Yes" %}">
{% else %}
{% trans "Yes" %}
{% endif %}
{% elif value == "No" %}
{% if show_value_as_symbol %}
<img src="{% static "_images/buyers-guide/score/icon-no.svg" %}" alt="{% trans "No" %}">
{% else %}
{% trans "No" %}
<h3 class="tw-mb-4 tw-font-zilla tw-text-2xl tw-leading-7 tw-flex tw-items-center">
{{ label }}
{% if info != None and info != "" %}
<a href="{{info}}">
<img src="{% static "_images/buyers-guide/icon-info-circle.svg" %}" alt="{% trans "information" context "icon description for screen readers" %}" class="tw-h-4 tw-w-4 tw-ml-2 tw-mb-1">
</a>
{% endif %}
</h3>
{% if rich_text_value %}
<div class="criterion-richtext-value mb-0">
{{ value|richtext }}
</div>
{% else %}
{% if show_value_as_symbol %}
<img src="{% get_static_prefix %}_images/buyers-guide/score/icon-{{value|lower}}.svg" alt="{{value}}">
<p class="rating pni-product-smaller-body mb-0">
{% if value == "Can’t Determine" or value == "CD"%}
{% if show_value_as_symbol %}
<img src="{% static "_images/buyers-guide/score/icon-unknown.svg" %}" alt="{% trans "Can’t determine" context "Unknown rating" %}">
{% else %}
{% trans "Can’t Determine" context "Unknown rating" %}
{% endif %}
{% elif value == "NA" %}
{% trans "Not Applicable" %}
{% elif value == "Yes" %}
{% if show_value_as_symbol %}
<img src="{% static "_images/buyers-guide/score/icon-yes.svg" %}" alt="{% trans "Yes" %}">
{% else %}
{% trans "Yes" %}
{% endif %}
{% elif value == "No" %}
{% if show_value_as_symbol %}
<img src="{% static "_images/buyers-guide/score/icon-no.svg" %}" alt="{% trans "No" %}">
{% else %}
{% trans "No" %}
{% endif %}
{% else %}
{{ value }}
{% if show_value_as_symbol %}
<img src="{% get_static_prefix %}_images/buyers-guide/score/icon-{{value|lower}}.svg" alt="{{value}}">
{% else %}
{{ value }}
{% endif %}
{% endif %}
{% endif %}
</p>
</p>
{% endif %}
</div>
13 changes: 13 additions & 0 deletions source/sass/buyers-guide/views/product.scss
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,19 @@
margin-bottom: 0.75rem;
}
}

.criterion-richtext-value {
p {
@extend .pni-product-smaller-body;
margin-bottom: unset;
}
}
.criterion-richtext-help {
p {
@extend .pni-product-helptext;
margin-bottom: unset;
}
}
}

.dotted-section {
Expand Down

0 comments on commit b4d04d1

Please sign in to comment.