`_,
and then add and commit files under the ``assets`` directory there as usual. Open a PR, and once it's merged
it will be automatically uploaded to the S3 bucket and be available on the domain.
For example, if you add a file to the repo under ``assets/pdf/the-dude-abides.pdf``, it will be available
as https://assets.mozilla.net/pdf/the-dude-abides.pdf. Once that is done you can link to that URL from bedrock
as you would any other URL.
Writing Migrations
------------------
Bedrock uses Django's built-in Migrations framework for its database migrations, and has no custom
database routing, etc. So, no big surprises here – write things as you regularly would.
*However*, as with any complex system, care needs to be taken with schema changes that
drop or rename database columns. Due to the way the rollout process works (ask for
details directly from the team), an absent column can cause some of the rollout to
enter a crashloop.
To avoid this, split your changes across releases, such as below.
For column renames:
* Release 1: Add your new column
* Release 2: Amend the codebase to use it instead of the old column
* Release 3: Clean up - drop the old, deprecated column, which should not be referenced in code at this point.
For column drops:
* Release 1: Update all code that uses the relevant column, so that nothing interacts with it any more.
* Release 2: Clean up - drop the old, deprecated column.
With both paths, check for any custom schema or data migrations that might reference the deprecated column.
Writing Views
-------------
You should rarely need to write a view for mozilla.org. Most pages are
static and you should use the `page` function documented above.
If you need to write a view and the page is translated or translatable
then it should use the `l10n_utils.render()` function to render the
template.
.. code-block:: python
from lib import l10n_utils
def my_view(request):
# do your fancy things
ctx = {'template_variable': 'awesome data'}
return l10n_utils.render(request, 'app/template.html', ctx)
Make sure to namespace your templates by putting them in a directory
named after your app, so instead of templates/template.html they would
be in templates/blog/template.html if `blog` was the name of your app.
If you prefer to use Django's Generic View classes we have a convenient
helper for that. You can use it either to create a custom view class of
your own, or use it directly in a `urls.py` file.
.. code-block:: python
# app/views.py
from lib.l10n_utils import L10nTemplateView
class FirefoxRoxView(L10nTemplateView):
template_name = 'app/firefox-rox.html'
# app/urls.py
urlpatterns = [
# from views.py
path('firefox/rox/', FirefoxRoxView.as_view()),
# directly
path('firefox/sox/', L10nTemplateView.as_view(template_name='app/firefox-sox.html')),
]
The `L10nTemplateView` functionality is mostly in a template mixin called `LangFilesMixin` which
you can use with other generic Django view classes if you need one other than `TemplateView`.
Variation Views
~~~~~~~~~~~~~~~
We have a generic view that allows you to easily create and use a/b testing
templates. If you'd like to have either separate templates or just a template
context variable for switching, this will help you out. For example.
.. code-block:: python
# urls.py
from django.conf.urls import url
from bedrock.utils.views import VariationTemplateView
urlpatterns = [
url(r'^testing/$',
VariationTemplateView.as_view(template_name='testing.html',
template_context_variations=['a', 'b']),
name='testing'),
]
This will give you a context variable called `variation` that will either be an empty
string if no param is set, or `a` if `?v=a` is in the URL, or `b` if `?v=b` is in the
URL. No other options will be valid for the `v` query parameter and `variation` will
be empty if any other value is passed in for `v` via the URL. So in your template code
you'd simply do the following:
.. code-block:: jinja
{% if variation == 'b' %}This is the B variation of our test. Enjoy!
{% endif %}
If you'd rather have a fully separate template for your test, you can use the
`template_name_variations` argument to the view instead of `template_context_variations`.
.. code-block:: python
# urls.py
from django.conf.urls import url
from bedrock.utils.views import VariationTemplateView
urlpatterns = [
url(r'^testing/$',
VariationTemplateView.as_view(template_name='testing.html',
template_name_variations=['1', '2']),
name='testing'),
]
This will not provide any extra template context variables, but will instead look for
alternate template names. If the URL is `testing/?v=1`, it will use a template named
`testing-1.html`, if `v=2` it will use `testing-2.html`, and for everything else it will
use the default. It simply puts a dash and the variation value between the template
file name and file extension.
It is theoretically possible to use the template name and template context versions
of this view together, but that would be an odd situation and potentially inappropriate
for this utility.
You can also limit your variations to certain locales. By default the variations will work
for any localization of the page, but if you supply a list of locales to the `variation_locales`
argument to the view then it will only set the variation context variable or alter the template
name (depending on the options explained above) when requested at one of said locales. For example,
the template name example above could be modified to only work for English or German like so
.. code-block:: python
# urls.py
from django.conf.urls import url
from bedrock.utils.views import VariationTemplateView
urlpatterns = [
url(r'^testing/$',
VariationTemplateView.as_view(template_name='testing.html',
template_name_variations=['1', '2'],
variation_locales=['en-US', 'de']),
name='testing'),
]
Any request to the page in for example French would not use the alternate template even if a
valid variation were given in the URL.
.. note::
If you'd like to add this functionality to an existing Class-Based View, there is
a mixin that implements this pattern that should work with most views:
`bedrock.utils.views.VariationMixin`.
Geo Template View
~~~~~~~~~~~~~~~~~
Now that we have our CDN configured properly, we can also just swap out templates
per request country. This is very similar to the above, but it will simply use
the proper template for the country from which the request originated.
.. code-block:: python
from bedrock.base.views import GeoTemplateView
class CanadaIsSpecialView(GeoTemplateView):
geo_template_names = {
'CA': 'mozorg/canada-is-special.html',
}
template_name = 'mozorg/everywhere-else-is-also-good.html'
For testing purposes while you're developing or on any deployment that is not
accessed via the production domain (www.mozilla.org) you can append your URL
with a ``geo`` query param (e.g. ``/firefox/?geo=DE``) and that will take
precedence over the country from the request header.
Other Geo Stuff
~~~~~~~~~~~~~~~
There are a couple of other tools at your disposal if you need to change things
depending on the location of the user. You can use the
``bedrock.base.geo.get_country_from_request`` function in a view and it will
return the country code for the request (either from the CDN or the query param,
just like above).
.. code-block:: python
from bedrock.base.geo import get_country_from_request
def dude_view(request):
country = get_country_from_request(request)
if country == 'US':
# do a thing for the US
else:
# do the default thing
The other convenience available is that the country code, either from the CDN
or the query param, is avilable in any template in the ``country_code`` variable.
This allows you to change anything about how the template renders based on the
location of the user.
.. code-block:: jinja
{% if country_code == 'US' %}
GO MURICA!
{% else %}
Yay World!
{% endif %}
Coding Style
------------
Bedrock uses the following open source tools to follow coding styles and conventions,
as well as applying automatic code formatting:
* `black `_ for Python code formatting.
* `flake8 `_ for Python style and code quality rules.
* `isort `_ for Python import ordering consistency.
* `Prettier `_ for JavaScript code formatting.
* `ESLint `_ for JavaScript code quality rules.
* `Stylelint `_ for Sass/CSS style and code quality rules.
For front-end HTML & CSS conventions, bedrock uses Mozilla's Protocol design system for
building components. You can read the `Protocol documentation site `_
for more information.
Mozilla also has some more general coding styleguides available, although some of
these are now rather outdated:
* `Mozilla Python Style Guide `_
* `Mozilla HTML Style Guide `_
* `Mozilla JS Style Guide `_
* `Mozilla CSS Style Guide `_
Test coverage
-------------
When the Python tests are run, a coverage report is generated, showing which lines of the
codebase have tests that execute them, and which do not. You can view this report in your
browser at ``file:///path/to/your/checkout/of/bedrock/python_coverage/index.html``.
When adding code, please aim to provide solid test coverage, using the coverage report as
a guide. This doesn't necessarily mean every single line needs a test, and 100% coverage
doesn't mean 0% defects.
Configuring your Code Editor
----------------------------
Bedrock includes an `.editorconfig` file in the root directory that you can
use with your code editor to help maintain consistent coding styles. Please
see `editorconfig.org `_. for a list of supported
editors and available plugins.
Working with Protocol
---------------------
Bedrock uses the `Protocol Design System `_ to quickly produce consistent, stable components.
When we find we are frequently re-using code for a particular Protocol component (i.e.
`Split `_), we convert it to a
`macro template `_ with parameters for customization.
You can find parameter definitions for the available Protocol macros below.
Picto
~~~~~
- title
String indicating heading text (usually a translation id wrapped in ftl function)
Default: None
Example: ``title=ftl('misinformation-why-trust-firefox')``
- heading_level
Number indicating heading level for title text. Should be based on semantic meaning, not presentational styling.
Default: 3
Example: ``heading_level=2``
- body
A boolean attribute. If true, it will show the contents of the card, if false, it will hide the contents.
Default: False
Example: ``body=True``
- image_url
image location to be used. Start it off with ‘img/…’.
Default: ''
Example: ``image_url='img/icons/mountain-purple.svg'``
- base_el
The element the content of the picto will be read as in HTML. For example, if the Picto macro is wrapped in a ul tag, the base_el would be an li tag.
Default: div
Example: ``base_el='li'``
- class
String adding class(es) to the base el tag.
Default: None
Example: ``class='trust'``
- image_width
Number indicating width of image.
Default: 64
Example: ``image_width='153px'``
- include_highres_image
Boolean that determines whether the image can also appear in high res.
Default: False
Example: ``include_highres_image=True``
- l10n_image
Boolean to indicate if image has translatable text.
Default: False
Example: ``l10n_image=True``
- lazy_loading
Boolean to provide “lazy” value for “loading” attribute. This will be “eager” by default. Lazy loading defers fetching of images to a browser decision based on user scroll and connection.
Default: False
Example: ``lazy_loading=True``
Call out
~~~~~~~~
- title
**Required**. String indicating heading text (usually a translation id wrapped in ftl function).
Default: N/A
Example: ``title=ftl('firefox-privacy-hub-read-the-privacy-notice-for')``
- desc
String indicating paragraph text (usually a translation id wrapped in ftl function).
Default: None
Example: ``desc=ftl('firefox-channel-test-beta-versions-of-firefox-ios')``
- class
String adding class(es) to the section tag.
Default: None
Example: ``class='mzp-t-firefox ' + product_class``
- include_cta
Boolean indicating whether or not to include the body of the macro call (usually a mix of text and html).
Default: None
Example: ``include_cta=True``
- heading_level
Number indicating heading level for title text. Should be based on semantic meaning, not presentational styling.
Default: 2
Example: ``heading_level=1``
Split
~~~~~
- block_id
String providing id to the section tag (usually if it needs to be used as an in-page link).
Default: None
Example: ``id='nextgen``
- base_el
String for block HTML tag not required.
Default: section
Example: ``base_el='aside'``
- block_class
String providing class(es) to the section tag.
Default: None
Example: ``block_class='mzp-l-split-reversed mzp-l-split-center-on-sm-md``
- theme_class
String providing theme class(es) to a container div tag inside the section.
Default: None
Example: ``theme_class='mzp-t-dark'``
- body_class
String providing class(es) to the body (text content) div inside the section.
Default: None
Example: ``Not currently in use``
- image_url
Path to image location.
Default: None
Example: ``image_url=’img/firefox/accounts/trailhead/value-respect.jpg’``
- media_class
String providing class(es) to the media div inside the section.
Default: None
Example: ``media_class='mzp-l-split-h-center'``
- include_highres_image
Boolean that determines whether the image can also appear in high res.
Default: False
Example: ``include_highres_image=True``
- l10n_image
Boolean to indicate if image has translatable text.
Default: False
Example: ``l10n_image=True``
- image_alt
String providing alt text to the image.
Default: ''
Example: ``Not currently in use``
- media_after
Boolean to determine if image appears before or after text when stacked on mobile size screens.
Default: False
Example: ``media_after=True``
- media_include
Path to video media.
Default: None
Example: ``media_include='firefox/facebookcontainer/includes/video.html'``
Billboard
~~~~~~~~~
- title
**Required**. String indicating heading text (usually a translation id wrapped in ftl function).
Default: N/A
Example: ``title=ftl('about-the-mozilla-manifesto')``
- ga_title
**Required**. String providing value for data-link-name attribute on cta.
Default: N/A
Example: ``ga_title='The Mozilla Manifesto'``
- desc
**Required**.String indicating paragraph text (usually a translation id wrapped in ftl function).
Default: N/A
Example: ``desc=ftl('about-the-principles-we-wrote-in')``
- link_cta
**Required**. String indicating link text (usually a translation id wrapped in an ftl function).
Default: N/A
Example: ``link_cta=ftl('about-read-the-manifesto')``
- link_url
**Required**. String or url helper function provides href value for cta link.
Default: N/A
Example: ``link_url=url('mozorg.about.manifesto')``
- image_url
**Required**. Path to image location.
Default: N/A
Example: ``image_url='img/home/2018/billboard-healthy-internet.png'``
- include_highres_image
Boolean that determines whether the image can also appear in high res.
Default: False
Example: ``include_highres_image=True``
- reverse
Uses default layout: mzp-l-billboard-rightReverse will switch to billboard (text) left.
Default: False
Example: ``reverse=True``
- heading_level
Number indicating heading level for title text. Should be based on semantic meaning, not presentational styling.
Default: 2
Example: ``heading_level=1``
Feature Card
~~~~~~~~~~~~
- title
String indicating heading text (usually a translation id wrapped in ftl function).
Default: None
Example: ``title=ftl('firefox-home-firefox-browser')``
- ga_title
String used as an identifying name on a link for google analytics. Only used if link_url and link_cta are provided as well.
Default: None
Example: ``ga_title='Firefox Windows'``
- image_url
Path to image location.
Default: N/A
Example: ``image_url=’img/firefox/accounts/trailhead/value-respect.jpg’``
- class
String adding class(es) to the section tag.
Default: None
Example: ``class=’mzp-l-card-feature-left-half t-mozvpn’``
- link_url
String or url helper function provides href value for cta link. Only used if link_cta is provided as well.
Default: None
Example: ``link_url=url('firefox.privacy.index')``
- link_cta
String indicating link text (usually a translation id wrapped in an ftl function). Only used if link_url is provided as well.
Default: None
Example: ``link_cta=ftl('ui-learn-more')``
- include_highres_image
Boolean that determines whether the image can also appear in high res.
Default: False
Example: ``include_highres_image=True``
- l10n_image
Boolean to indicate if image has translatable text.
Default: False
Example: ``l10n_image=True``
- aspect_ratio
aspect_ratio String with an mzp class name indicating desired aspect ratio (adds class to section tag).
Default: False
Example: ``aspect_ratio='mzp-has-aspect-3-2'``
- heading_level
Number indicating heading level for title text. Should be based on semantic meaning, not presentational styling.
Default: 2
Example: ``heading_level=3``
- media_after
Boolean to determine if image appears before or after text when stacked on mobile size screens.
Default: False
Example: ``media_after=True``
Card
~~~~
- youtube_id
String indicating the Youtube ID found at the end of a Youtube video URL. Used when we are embedding a video to the card rather than an image.
Default: N/A
Example: ``youtube_id='aHpCLDQ_2ns'``
- title
**Required**. String indicating heading text (usually a translation id wrapped in ftl function).
Default: N/A
Example: ``title=ftl('about-the-mozilla-manifesto')``
- ga_title
**Required**. String providing value for data-link-name attribute on cta.
Default: N/A
Example: ``ga_title='The Mozilla Manifesto'``
- desc
**Required**. String indicating paragraph text (usually a translation id wrapped in ftl function).
Default: N/A
Example: ``desc=ftl('about-the-principles-we-wrote-in')``
- aspect_ratio
String indicating size/aspect ratio of the card (make sure to have it even if it’s in a defined Card Layout.
Default: N/A
Example: ``aspect_ratio=’mzp-has-aspect-16-9’``
- link_url
**Required**. String or url helper function provides href value for cta link.
Default: N/A
Example: ``link_url=url('mozorg.about.manifesto')``
- image_url
**Required**. Path to image location.
Default: N/A
Example: ``image_url='img/home/2018/billboard-healthy-internet.png'``
- include_highres_image
**Required**. Boolean that determines whether the image can also appear in high res.
Default: N/A
Example: ``include_highres_image=True``
- l10n_image
Boolean to indicate if image has translatable text.
Default: False
Example: ``l10n_image=True``
- heading_level
Number indicating heading level for title text. Should be based on semantic meaning, not presentational styling.
Default: 3
Example: ``heading_level=2``
- attributes
A generic parameter to add any extra attributes to the component, such as data or aria attributes. Note that the quotes will pass through unescaped.
Default: N/A
Example: ``attributes='aria-role="menuitem"'``