├── .editorconfig ├── .eslintignore ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── custom.md │ └── feature_request.md └── pull_request_template.md ├── .gitignore ├── .isort.cfg ├── .prettierignore ├── .therapist.yml ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── Dockerfile ├── Jenkinsfile ├── LICENSE ├── README.md ├── configs └── gunicorn.py ├── data ├── README.md └── devportal_topic_icons │ ├── css.svg │ ├── default-d.svg │ ├── default.svg │ ├── devtools.svg │ ├── extensions.svg │ ├── firefox.svg │ ├── html.svg │ ├── javascript.svg │ ├── mixedreality.svg │ ├── mobile-dev.svg │ ├── open-media-formats.svg │ ├── people.svg │ ├── rust.svg │ ├── search.svg │ ├── security.svg │ ├── speaker.svg │ ├── voice.svg │ ├── web-compat.svg │ ├── webassembly.svg │ └── webthings.svg ├── developerportal ├── __init__.py ├── apps │ ├── __init__.py │ ├── articles │ │ ├── __init__.py │ │ ├── migrations │ │ │ ├── 0001_initial.py │ │ │ ├── 0002_article_body.py │ │ │ ├── 0002_article_date.py │ │ │ ├── 0003_article_intro.py │ │ │ ├── 0003_auto_20190517_1038.py │ │ │ ├── 0004_auto_20190516_0907.py │ │ │ ├── 0005_auto_20190516_1236.py │ │ │ ├── 0006_auto_20190516_1303.py │ │ │ ├── 0007_merge_20190517_1323.py │ │ │ ├── 0008_auto_20190521_0851.py │ │ │ ├── 0009_auto_20190521_1048.py │ │ │ ├── 0010_article_labels.py │ │ │ ├── 0011_auto_20190522_1630.py │ │ │ ├── 0012_article_author.py │ │ │ ├── 0012_article_header_image.py │ │ │ ├── 0012_auto_20190603_1048.py │ │ │ ├── 0013_merge_20190603_1229.py │ │ │ ├── 0014_merge_20190604_0953.py │ │ │ ├── 0015_auto_20190604_1245.py │ │ │ ├── 0016_auto_20190604_1317.py │ │ │ ├── 0017_auto_20190611_1344.py │ │ │ ├── 0018_auto_20190611_1403.py │ │ │ ├── 0019_auto_20190611_1453.py │ │ │ ├── 0020_auto_20190612_0426.py │ │ │ ├── 0021_auto_20190618_0840.py │ │ │ ├── 0022_auto_20190624_1622.py │ │ │ ├── 0023_auto_20190709_0928.py │ │ │ ├── 0024_auto_20190711_1222.py │ │ │ ├── 0025_auto_20190716_1346.py │ │ │ ├── 0026_articles_description.py │ │ │ ├── 0026_auto_20190731_1109.py │ │ │ ├── 0027_article_related_links_mdn.py │ │ │ ├── 0027_merge_20190802_1535.py │ │ │ ├── 0028_merge_20190805_1426.py │ │ │ ├── 0029_auto_20190813_1302.py │ │ │ ├── 0030_auto_20190813_1503.py │ │ │ ├── 0031_auto_20190819_1304.py │ │ │ ├── 0032_update_article_manager.py │ │ │ ├── 0033_add_social_image_field.py │ │ │ ├── 0034_external_person_description.py │ │ │ ├── 0035_soft_rename_Article_as_Post_via_meta.py │ │ │ ├── 0036_data_rename_articles_to_posts.py │ │ │ ├── 0037_rephrase_articles_as_posts.py │ │ │ ├── 0038_rename_related_links_without_data_port.py │ │ │ ├── 0039_copy_image_field_data_to_card_image_field.py │ │ │ ├── 0040_remove_article_image.py │ │ │ ├── 0041_add_3_2_ratio_image.py │ │ │ ├── 0042_update_streamblock.py │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── templates │ │ │ ├── article.html │ │ │ ├── articles.html │ │ │ └── partials │ │ │ │ └── verbose_standfirst.html │ │ ├── tests │ │ │ ├── __init__.py │ │ │ └── test_articles.py │ │ └── wagtail_hooks.py │ ├── common │ │ ├── __init__.py │ │ ├── blocks.py │ │ ├── constants.py │ │ ├── feed.py │ │ ├── fields.py │ │ ├── fixtures │ │ │ ├── common.json │ │ │ ├── common_plus_extras_for_search_tests.json │ │ │ └── full_site_no_images.json │ │ ├── forms.py │ │ ├── helpers.py │ │ ├── image_formats.py │ │ ├── middleware.py │ │ ├── migration_utils.py │ │ ├── migrations │ │ │ ├── 0001_drop_social_auth_app_tables.py │ │ │ ├── 0002_drop_staticbuild_tables.py │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── settings_helpers.py │ │ ├── tasks │ │ │ └── __init__.py │ │ ├── templates │ │ │ ├── button_block.html │ │ │ └── code_snippet_block.html │ │ ├── test_helpers.py │ │ ├── tests │ │ │ ├── __init__.py │ │ │ ├── test_app_filters.py │ │ │ ├── test_app_tags.py │ │ │ ├── test_context_processors.py │ │ │ ├── test_feed.py │ │ │ ├── test_head_rendering.py │ │ │ ├── test_login.py │ │ │ ├── test_middleware.py │ │ │ ├── test_models.py │ │ │ ├── test_regex.py │ │ │ ├── test_settings_helpers.py │ │ │ ├── test_survey_tags.py │ │ │ ├── test_utils.py │ │ │ └── test_views.py │ │ ├── utils.py │ │ ├── validators.py │ │ ├── views.py │ │ └── wagtail_hooks.py │ ├── content │ │ ├── __init__.py │ │ ├── migrations │ │ │ ├── 0001_initial.py │ │ │ ├── 0002_contentpage_hero_image.py │ │ │ ├── 0003_auto_20190813_1503.py │ │ │ ├── 0004_update_content_manager.py │ │ │ ├── 0005_add_social_image_field.py │ │ │ ├── 0006_contentpage_icon.py │ │ │ ├── 0007_contentpage_description.py │ │ │ ├── 0008_contentpage_nav_description.py │ │ │ ├── 0009_contentpage_sidebar.py │ │ │ ├── 0010_copy_hero_image_field_to_card_image_field.py │ │ │ ├── 0011_remove_contentpage_hero_image.py │ │ │ ├── 0012_auto_20200511_1148.py │ │ │ ├── 0013_make_sidebar_optional.py │ │ │ ├── 0014_contentpage_card_image_3_2.py │ │ │ ├── 0015_hide_about_page_from_menu.py │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── templates │ │ │ └── content.html │ │ └── tests │ │ │ ├── __init__.py │ │ │ └── test_content.py │ ├── events │ │ ├── __init__.py │ │ ├── migrations │ │ │ ├── 0001_initial.py │ │ │ ├── 0002_auto_20190704_1334.py │ │ │ ├── 0003_auto_20190708_0858.py │ │ │ ├── 0003_auto_20190709_0928.py │ │ │ ├── 0003_auto_20190709_1241.py │ │ │ ├── 0004_auto_20190708_1100.py │ │ │ ├── 0004_auto_20190711_1501.py │ │ │ ├── 0004_merge_20190711_1925.py │ │ │ ├── 0005_merge_20190711_2019.py │ │ │ ├── 0006_auto_20190712_1231.py │ │ │ ├── 0007_auto_20190718_1424.py │ │ │ ├── 0010_auto_20190718_1551.py │ │ │ ├── 0011_auto_20190718_1724.py │ │ │ ├── 0012_auto_20190725_1124.py │ │ │ ├── 0013_auto_20190731_1054.py │ │ │ ├── 0014_auto_20190813_1302.py │ │ │ ├── 0015_auto_20190813_1503.py │ │ │ ├── 0016_auto_20190814_1600.py │ │ │ ├── 0017_auto_20190819_1304.py │ │ │ ├── 0018_update_event_manager.py │ │ │ ├── 0019_add_social_image_field.py │ │ │ ├── 0020_events_body.py │ │ │ ├── 0021_auto_20200326_1339.py │ │ │ ├── 0022_copy_image_field_data_to_card_image_field.py │ │ │ ├── 0023_remove_event_image.py │ │ │ ├── 0024_add_3_2_ratio_image.py │ │ │ ├── 0025_update_streamblock.py │ │ │ ├── 0026_make_events_page_body_optional.py │ │ │ ├── 0027_add_Events_top_content_and_bottom_content.py │ │ │ ├── 0028_event_is_mozilla_supported_event.py │ │ │ ├── 0029_further_sponsored_events_changes.py │ │ │ ├── 0030_add_Extra_Content_panel_to_Event.py │ │ │ ├── 0031_event_sidebar.py │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── templates │ │ │ ├── event.html │ │ │ └── events.html │ │ ├── tests │ │ │ ├── __init__.py │ │ │ └── test_events.py │ │ └── wagtail_hooks.py │ ├── externalcontent │ │ ├── __init__.py │ │ ├── migrations │ │ │ ├── 0001_initial.py │ │ │ ├── 0002_auto_20190704_1221.py │ │ │ ├── 0003_externalarticle_externalevent_externalvideo.py │ │ │ ├── 0004_auto_20190705_1306.py │ │ │ ├── 0005_auto_20190709_1412.py │ │ │ ├── 0006_auto_20190709_1420.py │ │ │ ├── 0007_auto_20190711_1940.py │ │ │ ├── 0008_externalcontent_description.py │ │ │ ├── 0009_externalarticleauthor.py │ │ │ ├── 0010_auto_20190723_1659.py │ │ │ ├── 0011_auto_20190723_1719.py │ │ │ ├── 0012_auto_20190724_1437.py │ │ │ ├── 0013_externalevent_venue.py │ │ │ ├── 0014_auto_20190725_1225.py │ │ │ ├── 0015_auto_20190725_1226.py │ │ │ ├── 0016_auto_20190731_1226.py │ │ │ ├── 0017_auto_20190806_1041.py │ │ │ ├── 0018_auto_20190807_1244.py │ │ │ ├── 0019_auto_20190813_1302.py │ │ │ ├── 0020_auto_20190813_1503.py │ │ │ ├── 0021_auto_20190814_1600.py │ │ │ ├── 0022_auto_20190819_1304.py │ │ │ ├── 0023_auto_20190925_1338.py │ │ │ ├── 0024_update_externalcontent_manager.py │ │ │ ├── 0025_add_social_image_field.py │ │ │ ├── 0026_external_person_description.py │ │ │ ├── 0027_relabel_external_article_as_external_post.py │ │ │ ├── 0028_rephrase_articles_as_posts.py │ │ │ ├── 0029_rename_image_to_card_image.py │ │ │ ├── 0030_add_3_2_ratio_image.py │ │ │ ├── 0031_update_streamblock.py │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── tests │ │ │ ├── __init__.py │ │ │ └── test_people.py │ │ └── wagtail_hooks.py │ ├── health │ │ ├── __init__.py │ │ ├── tests │ │ │ ├── __init__.py │ │ │ └── test_views.py │ │ ├── urls.py │ │ └── views.py │ ├── home │ │ ├── __init__.py │ │ ├── migrations │ │ │ ├── 0001_initial.py │ │ │ ├── 0002_create_homepage.py │ │ │ ├── 0003_auto_20190528_1305.py │ │ │ ├── 0004_homepagefeaturedarticle.py │ │ │ ├── 0005_auto_20190618_1635.py │ │ │ ├── 0006_auto_20190625_1111.py │ │ │ ├── 0007_auto_20190625_1112.py │ │ │ ├── 0008_auto_20190625_1119.py │ │ │ ├── 0009_auto_20190702_0904.py │ │ │ ├── 0009_homepage_featured.py │ │ │ ├── 0010_auto_20190627_1527.py │ │ │ ├── 0010_auto_20190704_1031.py │ │ │ ├── 0011_auto_20190627_1528.py │ │ │ ├── 0011_auto_20190705_1258.py │ │ │ ├── 0012_auto_20190628_1113.py │ │ │ ├── 0013_auto_20190628_1337.py │ │ │ ├── 0014_auto_20190628_1338.py │ │ │ ├── 0015_auto_20190628_1436.py │ │ │ ├── 0016_auto_20190628_1437.py │ │ │ ├── 0017_auto_20190628_1438.py │ │ │ ├── 0018_merge_20190702_1247.py │ │ │ ├── 0019_merge_20190709_0910.py │ │ │ ├── 0020_auto_20190708_1243.py │ │ │ ├── 0021_auto_20190711_1932.py │ │ │ ├── 0022_auto_20190711_1951.py │ │ │ ├── 0023_auto_20190712_1023.py │ │ │ ├── 0024_auto_20190718_1438.py │ │ │ ├── 0025_homepage_external_promos.py │ │ │ ├── 0026_auto_20190813_1302.py │ │ │ ├── 0027_auto_20190813_1503.py │ │ │ ├── 0028_auto_20190814_1600.py │ │ │ ├── 0029_update_home_manager.py │ │ │ ├── 0030_add_social_image_field.py │ │ │ ├── 0031_homepage_featured_people.py │ │ │ ├── 0032_homepage_show_header.py │ │ │ ├── 0033_rephrase_articles_as_posts.py │ │ │ ├── 0034_support_featuring_video_in_homepage.py │ │ │ ├── 0035_remove_homepage_external_promos.py │ │ │ ├── 0036_update_streamblock.py │ │ │ ├── 0037_remove_homepage_show_header.py │ │ │ ├── 0038_allow_content_page_as_featured.py │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── templates │ │ │ └── home.html │ │ └── tests │ │ │ ├── __init__.py │ │ │ └── test_home.py │ ├── ingestion │ │ ├── __init__.py │ │ ├── admin.py │ │ ├── apps.py │ │ ├── celery.py │ │ ├── constants.py │ │ ├── migrations │ │ │ ├── 0001_initial.py │ │ │ ├── 0002_bootstrap_ingestion_config.py │ │ │ ├── 0003_bootstrap_ingestion_user.py │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── tasks.py │ │ ├── tests │ │ │ ├── __init__.py │ │ │ ├── data │ │ │ │ └── test_images_as_bytearrays.py │ │ │ ├── test_data │ │ │ │ ├── blog.mozvr.com_rss.xml │ │ │ │ └── youtube_feed.xml │ │ │ ├── test_tasks.py │ │ │ └── test_utils.py │ │ └── utils.py │ ├── mozimages │ │ ├── __init__.py │ │ ├── migrations │ │ │ ├── 0001_initial.py │ │ │ └── __init__.py │ │ └── models.py │ ├── people │ │ ├── __init__.py │ │ ├── edit_handlers.py │ │ ├── migrations │ │ │ ├── 0001_initial.py │ │ │ ├── 0002_people.py │ │ │ ├── 0003_auto_20190530_1437.py │ │ │ ├── 0004_auto_20190530_1441.py │ │ │ ├── 0005_person_is_mozillian.py │ │ │ ├── 0006_auto_20190604_1050.py │ │ │ ├── 0007_auto_20190611_1556.py │ │ │ ├── 0008_featuredperson.py │ │ │ ├── 0009_auto_20190625_1122.py │ │ │ ├── 0009_auto_20190626_0848.py │ │ │ ├── 0009_auto_20190626_1353.py │ │ │ ├── 0010_auto_20190625_1258.py │ │ │ ├── 0010_merge_20190627_1318.py │ │ │ ├── 0011_merge_20190627_1428.py │ │ │ ├── 0012_auto_20190702_1059.py │ │ │ ├── 0012_merge_20190628_1108.py │ │ │ ├── 0013_merge_20190702_1247.py │ │ │ ├── 0013_merge_20190702_1300.py │ │ │ ├── 0014_merge_20190702_1619.py │ │ │ ├── 0015_auto_20190709_0928.py │ │ │ ├── 0016_auto_20190711_1915.py │ │ │ ├── 0017_auto_20190718_1424.py │ │ │ ├── 0018_people_keywords.py │ │ │ ├── 0019_remove_people_keywords.py │ │ │ ├── 0020_people_keywords.py │ │ │ ├── 0021_auto_20190731_1057.py │ │ │ ├── 0022_people_description.py │ │ │ ├── 0022_person_websites.py │ │ │ ├── 0023_merge_20190802_1535.py │ │ │ ├── 0024_auto_20190813_1302.py │ │ │ ├── 0025_auto_20190813_1503.py │ │ │ ├── 0026_auto_20190819_1304.py │ │ │ ├── 0027_person_country.py │ │ │ ├── 0028_auto_20190926_1049.py │ │ │ ├── 0029_update_person_manager.py │ │ │ ├── 0030_add_social_image_field.py │ │ │ ├── 0031_person_nickname.py │ │ │ ├── 0032_auto_20191008_1530.py │ │ │ ├── 0033_people_icon.py │ │ │ ├── 0034_people_nav_description.py │ │ │ ├── 0035_copy_image_field_to_card_image_field.py │ │ │ ├── 0036_remove_person_image.py │ │ │ ├── 0037_auto_20200511_1148.py │ │ │ ├── 0038_make_Person_country_optional.py │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── templates │ │ │ ├── people.html │ │ │ └── person.html │ │ ├── tests │ │ │ ├── __init__.py │ │ │ └── test_people.py │ │ └── wagtail_hooks.py │ ├── search │ │ ├── __init__.py │ │ ├── models.py │ │ ├── templates │ │ │ └── site-search.html │ │ ├── tests │ │ │ ├── __init__.py │ │ │ ├── test_utils.py │ │ │ └── test_views.py │ │ ├── urls.py │ │ ├── utils.py │ │ └── views.py │ ├── taskqueue │ │ ├── __init__.py │ │ ├── celery.py │ │ ├── models.py │ │ ├── tasks.py │ │ ├── tests │ │ │ ├── __init__.py │ │ │ ├── test_tasks.py │ │ │ ├── test_utils.py │ │ │ └── test_wagtail_hooks.py │ │ ├── utils.py │ │ └── wagtail_hooks.py │ ├── topics │ │ ├── __init__.py │ │ ├── migrations │ │ │ ├── 0001_initial.py │ │ │ ├── 0002_topics.py │ │ │ ├── 0003_subtopic.py │ │ │ ├── 0004_topicfeaturedarticle.py │ │ │ ├── 0005_auto_20190610_1413.py │ │ │ ├── 0006_auto_20190611_1453.py │ │ │ ├── 0007_auto_20190611_1542.py │ │ │ ├── 0008_auto_20190612_1759.py │ │ │ ├── 0009_topic_color.py │ │ │ ├── 0010_auto_20190618_0840.py │ │ │ ├── 0010_topic_get_started.py │ │ │ ├── 0011_auto_20190614_0932.py │ │ │ ├── 0012_auto_20190614_1319.py │ │ │ ├── 0013_merge_20190618_1635.py │ │ │ ├── 0013_merge_20190619_1357.py │ │ │ ├── 0013_topic_icon.py │ │ │ ├── 0014_merge_20190618_1609.py │ │ │ ├── 0015_merge_20190619_1437.py │ │ │ ├── 0015_merge_20190619_1506.py │ │ │ ├── 0015_merge_20190621_1009.py │ │ │ ├── 0016_auto_20190619_1437.py │ │ │ ├── 0016_auto_20190619_1506.py │ │ │ ├── 0016_auto_20190624_1129.py │ │ │ ├── 0017_merge_20190621_1033.py │ │ │ ├── 0017_merge_20190624_0904.py │ │ │ ├── 0018_merge_20190624_1248.py │ │ │ ├── 0018_merge_20190624_1252.py │ │ │ ├── 0019_merge_20190625_1257.py │ │ │ ├── 0020_auto_20190701_1104.py │ │ │ ├── 0020_auto_20190702_1401.py │ │ │ ├── 0020_topic_featured.py │ │ │ ├── 0021_auto_20190701_1105.py │ │ │ ├── 0021_auto_20190702_1402.py │ │ │ ├── 0022_auto_20190702_1123.py │ │ │ ├── 0023_auto_20190702_1318.py │ │ │ ├── 0024_merge_20190702_1619.py │ │ │ ├── 0025_auto_20190702_1620.py │ │ │ ├── 0026_merge_20190704_1459.py │ │ │ ├── 0027_auto_20190709_0928.py │ │ │ ├── 0027_auto_20190710_1339.py │ │ │ ├── 0028_merge_20190710_1633.py │ │ │ ├── 0029_auto_20190711_1917.py │ │ │ ├── 0030_auto_20190711_1951.py │ │ │ ├── 0031_auto_20190718_1424.py │ │ │ ├── 0033_auto_20190718_1452.py │ │ │ ├── 0034_auto_20190718_1504.py │ │ │ ├── 0035_auto_20190725_1225.py │ │ │ ├── 0036_auto_20190725_1226.py │ │ │ ├── 0037_auto_20190730_1057.py │ │ │ ├── 0038_topic_latest_articles_count.py │ │ │ ├── 0039_auto_20190805_0806.py │ │ │ ├── 0040_auto_20190813_1302.py │ │ │ ├── 0041_auto_20190813_1503.py │ │ │ ├── 0042_auto_20190814_1600.py │ │ │ ├── 0043_auto_20190819_1304.py │ │ │ ├── 0044_update_topic_manager.py │ │ │ ├── 0045_add_social_image_field.py │ │ │ ├── 0046_remove_topic_tabbed_panels_title.py │ │ │ ├── 0047_rephrase_articles_as_posts.py │ │ │ ├── 0048_add_svg_help_text_to_topic_icon_filefield.py │ │ │ ├── 0049_topic_nav_description.py │ │ │ ├── 0050_remove_topic_latest_articles_count.py │ │ │ ├── 0051_remove_tabbed_panel_and_add_recent_work_streamfield.py │ │ │ ├── 0052_auto_20200511_1148.py │ │ │ ├── 0053_update_streamblock.py │ │ │ ├── 0054_remove_topic_color.py │ │ │ ├── 0055_topic_relevant_events.py │ │ │ ├── 0056_allow_content_page_as_featured.py │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── templates │ │ │ ├── topic.html │ │ │ └── topics.html │ │ ├── tests │ │ │ ├── __init__.py │ │ │ └── test_topics.py │ │ └── wagtail_hooks.py │ └── videos │ │ ├── __init__.py │ │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_video_related_links_mdn.py │ │ ├── 0003_auto_20190807_1244.py │ │ ├── 0004_auto_20190813_1302.py │ │ ├── 0005_auto_20190813_1503.py │ │ ├── 0006_auto_20190814_1600.py │ │ ├── 0007_auto_20190819_1304.py │ │ ├── 0008_update_video_manager.py │ │ ├── 0009_add_social_image_field.py │ │ ├── 0010_rename_related_links_without_data_port.py │ │ ├── 0011_copy_image_to_card_image.py │ │ ├── 0012_remove_video_image.py │ │ ├── 0013_add_3_2_ratio_image.py │ │ └── __init__.py │ │ ├── models.py │ │ ├── templates │ │ ├── video.html │ │ └── videos.html │ │ ├── tests │ │ ├── __init__.py │ │ └── test_videos.py │ │ └── wagtail_hooks.py ├── context_processors.py ├── regex.py ├── settings │ ├── __init__.py │ ├── base.py │ ├── dev.py │ ├── local.py.example │ ├── production.py │ ├── test.py │ ├── test_fast.py │ └── worker.py ├── templates │ ├── 403.html │ ├── 404.html │ ├── 429.html │ ├── 500.html │ ├── admin │ │ └── login.html │ ├── atoms │ │ ├── icons │ │ │ ├── close.svg │ │ │ ├── date.svg │ │ │ ├── email.svg │ │ │ ├── facebook.svg │ │ │ ├── github.svg │ │ │ ├── linkedin.svg │ │ │ ├── location.svg │ │ │ ├── mozilla.svg │ │ │ ├── person.svg │ │ │ ├── reading-time.svg │ │ │ ├── tech-speaker.svg │ │ │ ├── twitter.svg │ │ │ └── video.svg │ │ ├── image.html │ │ ├── label.html │ │ └── social-meta.html │ ├── base.html │ ├── base_includes │ │ └── google_analytics.html │ ├── footer.html │ ├── header.html │ ├── molecules │ │ ├── accessibility-nav.html │ │ ├── card-featured.html │ │ ├── cards │ │ │ ├── card-article.html │ │ │ ├── card-event.html │ │ │ ├── card-person.html │ │ │ ├── card-video.html │ │ │ └── card.html │ │ ├── event-details.html │ │ ├── filter-form.html │ │ ├── header-strip.html │ │ ├── image-block.html │ │ ├── item-topic-link.html │ │ ├── navigation │ │ │ └── promotion.html │ │ ├── pagination.html │ │ ├── quarter-page-item.html │ │ ├── resource-share.html │ │ ├── search-result.html │ │ └── site-search-form.html │ ├── organisms │ │ ├── article-read-more.html │ │ ├── featured-events--four-up.html │ │ ├── featured.html │ │ ├── filter-list.html │ │ ├── four-card-row.html │ │ ├── homepage-about.html │ │ ├── homepage-header.html │ │ ├── newsletter-signup.html │ │ ├── partials │ │ │ └── featured-card-selector.html │ │ ├── people-section.html │ │ ├── search-results.html │ │ └── topic-links.html │ ├── robots.txt │ ├── survey.html │ └── wagtailadmin │ │ ├── 404.html │ │ ├── admin_base.html │ │ ├── base.html │ │ ├── home.html │ │ └── login.html ├── templatetags │ ├── app_filters.py │ ├── app_tags.py │ └── survey_tags.py ├── urls.py └── wsgi.py ├── docker-compose.yml ├── docs ├── cdn-behaviour.md ├── deployed_environments.md └── developer-notes.md ├── etc ├── newrelic.ini └── nginx │ ├── nginx.conf │ └── ssl │ ├── README.md │ ├── ca-config.json │ ├── cfssl.json │ └── gen-cert.sh ├── k8s ├── Makefile ├── README.md ├── app.deploy.yaml.j2 ├── app.env.yaml.j2 ├── app.svc.yaml.j2 ├── celery-beat.yaml.j2 ├── celery.yaml.j2 ├── config │ ├── dev.sh │ ├── prod.sh │ └── stage.sh ├── db-backups-cron.yaml.j2 ├── db-migration-job.yaml.j2 ├── hpa.yaml.j2 ├── jenkins-rbac.yaml ├── redirector.deployment.yaml.j2 ├── redirector.ingress.yaml.j2 ├── redirector.svc.yaml.j2 ├── search-index-update-job.yaml.j2 └── wait_for_job.sh ├── locust ├── README.md ├── docker-compose.yml └── locustfile.py ├── manage.py ├── package-lock.json ├── package.json ├── renovate.json ├── requirements.txt ├── scripts ├── ci-setup ├── ci-tests ├── dev-setup └── slack-notify.sh ├── setup.cfg ├── src ├── css │ ├── admin.scss │ ├── atoms │ │ ├── buttons.scss │ │ ├── code.scss │ │ ├── embed.scss │ │ ├── icon.scss │ │ ├── image.scss │ │ ├── label.scss │ │ ├── modal.scss │ │ ├── quote.scss │ │ ├── rich-text.scss │ │ └── typography.scss │ ├── global │ │ ├── base.scss │ │ ├── layout.scss │ │ └── variables.scss │ ├── index.scss │ ├── molecules │ │ ├── accessibility-nav.scss │ │ ├── card-event.scss │ │ ├── card-featured.scss │ │ ├── card-person.scss │ │ ├── card.scss │ │ ├── event-details.scss │ │ ├── header-strip.scss │ │ ├── header.scss │ │ ├── item-topic-link.scss │ │ ├── notification-bar.scss │ │ ├── pagination.scss │ │ ├── resource-share.scss │ │ ├── resource-toolbar.scss │ │ ├── search-result.scss │ │ └── site-search-form.scss │ ├── organisms │ │ ├── article-read-more.scss │ │ ├── featured.scss │ │ ├── filter-form.scss │ │ ├── filter-list.scss │ │ ├── footer.scss │ │ ├── four-card-row.scss │ │ ├── homepage-about.scss │ │ ├── homepage-header.scss │ │ ├── newsletter-signup.scss │ │ ├── people-section.scss │ │ ├── search-results.scss │ │ └── topic-links.scss │ └── templates │ │ ├── article.scss │ │ ├── content-page.scss │ │ ├── event.scss │ │ ├── events.scss │ │ ├── http-error.scss │ │ ├── person.scss │ │ ├── site-search.scss │ │ └── video.scss ├── fonts │ ├── Inter-Bold.woff │ ├── Inter-Bold.woff2 │ ├── Inter-BoldItalic.woff │ ├── Inter-BoldItalic.woff2 │ ├── Inter-Italic.woff │ ├── Inter-Italic.woff2 │ ├── Inter-Regular.woff │ ├── Inter-Regular.woff2 │ ├── ZillaSlab-Bold.woff │ ├── ZillaSlab-Bold.woff2 │ ├── ZillaSlab-Regular.woff │ ├── ZillaSlab-Regular.woff2 │ ├── ZillaSlab-SemiBold.woff │ ├── ZillaSlab-SemiBold.woff2 │ ├── ZillaSlabHighlight-Bold.woff │ ├── ZillaSlabHighlight-Bold.woff2 │ ├── ZillaSlabHighlight-Regular.woff │ └── ZillaSlabHighlight-Regular.woff2 ├── img │ ├── hamburger.svg │ ├── homepage@1x.png │ ├── homepage@2x.png │ ├── http-error.png │ ├── icons │ │ ├── arrow-left-white.svg │ │ ├── arrow-left.svg │ │ ├── article-white.svg │ │ ├── article.svg │ │ ├── audio.svg │ │ ├── close-white.svg │ │ ├── close.svg │ │ ├── content-white.svg │ │ ├── default-d.svg │ │ ├── events-white.svg │ │ ├── expand-white.svg │ │ ├── expand.svg │ │ ├── favicon.ico │ │ ├── favicon_dev.ico │ │ ├── favicon_stage.ico │ │ ├── map-pin.svg │ │ ├── menu-arrow.svg │ │ ├── menu-white.svg │ │ ├── menu.svg │ │ ├── people.svg │ │ ├── search-black.svg │ │ ├── search-white.svg │ │ ├── social │ │ │ ├── facebook │ │ │ │ ├── black.svg │ │ │ │ ├── brand.svg │ │ │ │ └── white.svg │ │ │ ├── firefox │ │ │ │ └── black.svg │ │ │ ├── github │ │ │ │ ├── black.svg │ │ │ │ ├── brand.svg │ │ │ │ └── white.svg │ │ │ ├── instagram │ │ │ │ ├── black.svg │ │ │ │ ├── brand.svg │ │ │ │ └── white.svg │ │ │ ├── mozilla │ │ │ │ ├── black.svg │ │ │ │ └── white.svg │ │ │ ├── pocket │ │ │ │ ├── black.svg │ │ │ │ ├── brand.svg │ │ │ │ └── white.svg │ │ │ ├── protocol │ │ │ │ ├── black.svg │ │ │ │ └── white.svg │ │ │ ├── twitter │ │ │ │ ├── black.svg │ │ │ │ ├── brand.svg │ │ │ │ └── white.svg │ │ │ └── youtube │ │ │ │ ├── black.svg │ │ │ │ ├── brand.svg │ │ │ │ └── white.svg │ │ ├── speaker.svg │ │ ├── video-white.svg │ │ ├── video.svg │ │ └── website.png │ ├── logos │ │ ├── developer │ │ │ ├── black.svg │ │ │ └── white.svg │ │ ├── firefox │ │ │ ├── logo-word-hor-sm-high-res.png │ │ │ └── logo-word-hor-sm.png │ │ ├── mdn-dino-white.svg │ │ ├── mdn.svg │ │ └── mozilla │ │ │ ├── black.png │ │ │ ├── black.svg │ │ │ ├── black@2x.png │ │ │ ├── black@3x.png │ │ │ ├── white.png │ │ │ ├── white.svg │ │ │ ├── white@2x.png │ │ │ └── white@3x.png │ ├── placeholders │ │ ├── article.jpg │ │ ├── event_16_9.jpg │ │ ├── event_3_2.jpg │ │ ├── original │ │ │ ├── event_16_9.jpg │ │ │ ├── event_3_2.jpg │ │ │ ├── person_16_9.jpg │ │ │ ├── person_3_2.jpg │ │ │ ├── post_16_9.jpg │ │ │ └── post_3_2.jpg │ │ ├── person_16_9.jpg │ │ ├── person_3_2.jpg │ │ ├── post_16_9.jpg │ │ └── post_3_2.jpg │ └── promo │ │ ├── firefox-developer@1x.jpg │ │ ├── firefox-developer@2x.jpg │ │ ├── mdn-dino@1x.jpg │ │ ├── mdn-dino@2x.jpg │ │ ├── mdn@1x.jpg │ │ ├── mdn@2x.jpg │ │ ├── newsletter@1x.jpg │ │ └── newsletter@2x.jpg └── js │ ├── admin-extras.js │ ├── atoms │ └── modal.js │ ├── head-includes.js │ ├── index.js │ ├── mozilla-dnthelper.js │ ├── newsletter-subscription.js │ ├── organisms │ ├── filter-form.js │ └── notification-bar.js │ ├── polyfills.js │ ├── task-completion-prompt.js │ ├── tests │ ├── filter-form-mock-html.js │ ├── filter-form.test.js │ ├── newsletter-subscription.test.js │ ├── task-completion-prompt.test.js │ └── utils.test.js │ └── utils.js ├── wagtail-rfcs └── 000-append-comments-for-moderation.md └── webpack.config.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 2 7 | indent_style = space 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.py] 12 | indent_size = 4 13 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | dist 2 | *.scss 3 | *.py 4 | *.pyc 5 | *.html 6 | *.svg 7 | renovate.json 8 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/custom.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Blank issue template 3 | about: Please only use this template if you know why the other two aren't appropriate. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | This changeset addresses ... by ... 2 | 3 | (Related issue #XX) 4 | 5 | ## Key changes: 6 | 7 | - summarise changes here, linking to commits as appropriate 8 | 9 | ## How to test 10 | 11 | - add test steps here, including how to set up relevant data/state 12 | -------------------------------------------------------------------------------- /.isort.cfg: -------------------------------------------------------------------------------- 1 | [settings] 2 | known_first_party=developerportal 3 | known_third_party=boto3,celery,django_countries,modelcluster,pytz,pygments,readtime,social_core,taggit,urllib3,wagtail 4 | known_django=django 5 | default_section=FIRSTPARTY 6 | sections=FUTURE,STDLIB,DJANGO,THIRDPARTY,FIRSTPARTY,LOCALFOLDER 7 | multi_line_output=3 8 | include_trailing_comma=True 9 | force_grid_wrap=0 10 | use_parentheses=True 11 | line_length=88 12 | skip=node_modules,migrations 13 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | *.py 3 | *.pyc 4 | *.html 5 | *.svg 6 | *.json 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | 3 | services: 4 | - docker 5 | 6 | branches: 7 | except: 8 | - dev-push 9 | - stage-push 10 | - prod-push 11 | 12 | env: 13 | global: 14 | - DOCKER_COMPOSE_VERSION="1.24.1" 15 | 16 | before_install: 17 | - echo "Installing newer docker-compose" 18 | - sudo rm /usr/local/bin/docker-compose 19 | - curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose 20 | - chmod +x docker-compose 21 | - sudo mv docker-compose /usr/local/bin 22 | - docker version 23 | - docker-compose version 24 | 25 | install: 26 | - scripts/ci-setup --build 27 | 28 | script: 29 | - scripts/ci-tests 30 | 31 | after_script: 32 | - echo "Cleanup" 33 | - docker-compose down --volumes --remove-orphans 34 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Community Participation Guidelines 2 | 3 | This repository is governed by Mozilla's code of conduct and etiquette guidelines. 4 | For more details, please read the 5 | [Mozilla Community Participation Guidelines](https://www.mozilla.org/about/governance/policies/participation/). 6 | 7 | ## How to Report 8 | 9 | For more information on how to report violations of the Community Participation Guidelines, please read our 10 | [How to Report](https://www.mozilla.org/about/governance/policies/participation/reporting/) page. 11 | -------------------------------------------------------------------------------- /data/README.md: -------------------------------------------------------------------------------- 1 | # What's in /data/ ? 2 | 3 | - `devportal-topic-icons` is a directory of SVG icons that are intended for upload via the Meta -> Theme -> Icon field on pages that appear as child pages in the navigation (all Topic pages, plus the People page and some Content Pages). They've been added to the repo for convenience and require manual addition because they are technically data, not design. 4 | -------------------------------------------------------------------------------- /data/devportal_topic_icons/default-d.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/devportal_topic_icons/html.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data/devportal_topic_icons/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /developerportal/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/__init__.py -------------------------------------------------------------------------------- /developerportal/apps/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/apps/__init__.py -------------------------------------------------------------------------------- /developerportal/apps/articles/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/apps/articles/__init__.py -------------------------------------------------------------------------------- /developerportal/apps/articles/migrations/0002_article_body.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-05-16 15:06 2 | 3 | from django.db import migrations 4 | import wagtail.core.blocks 5 | import wagtail.core.fields 6 | import wagtail.images.blocks 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [("articles", "0001_initial")] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name="article", 16 | name="body", 17 | field=wagtail.core.fields.StreamField( 18 | [ 19 | ("paragraph", wagtail.core.blocks.RichTextBlock()), 20 | ("image", wagtail.images.blocks.ImageChooserBlock()), 21 | ], 22 | default=None, 23 | ), 24 | ) 25 | ] 26 | -------------------------------------------------------------------------------- /developerportal/apps/articles/migrations/0002_article_date.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-05-15 15:19 2 | 3 | import datetime 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [("articles", "0001_initial")] 10 | 11 | operations = [ 12 | migrations.AddField( 13 | model_name="article", 14 | name="date", 15 | field=models.DateField( 16 | default=datetime.date.today, verbose_name="Article date" 17 | ), 18 | ) 19 | ] 20 | -------------------------------------------------------------------------------- /developerportal/apps/articles/migrations/0003_article_intro.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-05-16 09:06 2 | 3 | from django.db import migrations 4 | import wagtail.core.fields 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [("articles", "0002_article_date")] 10 | 11 | operations = [ 12 | migrations.AddField( 13 | model_name="article", 14 | name="intro", 15 | field=wagtail.core.fields.RichTextField(default=None, verbose_name="Intro"), 16 | ) 17 | ] 18 | -------------------------------------------------------------------------------- /developerportal/apps/articles/migrations/0004_auto_20190516_0907.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-05-16 09:07 2 | 3 | from django.db import migrations 4 | import wagtail.core.fields 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [("articles", "0003_article_intro")] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name="article", 14 | name="intro", 15 | field=wagtail.core.fields.RichTextField(default="", verbose_name="Intro"), 16 | ) 17 | ] 18 | -------------------------------------------------------------------------------- /developerportal/apps/articles/migrations/0005_auto_20190516_1236.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-05-16 12:36 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("articles", "0004_auto_20190516_0907")] 9 | 10 | operations = [ 11 | migrations.RemoveField(model_name="article", name="date"), 12 | migrations.RemoveField(model_name="article", name="intro"), 13 | ] 14 | -------------------------------------------------------------------------------- /developerportal/apps/articles/migrations/0006_auto_20190516_1303.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-05-16 13:03 2 | 3 | import datetime 4 | from django.db import migrations, models 5 | import wagtail.core.fields 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [("articles", "0005_auto_20190516_1236")] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name="article", 15 | name="date", 16 | field=models.DateField( 17 | default=datetime.date.today, verbose_name="Article date" 18 | ), 19 | ), 20 | migrations.AddField( 21 | model_name="article", 22 | name="intro", 23 | field=wagtail.core.fields.RichTextField(default="", verbose_name="Intro"), 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /developerportal/apps/articles/migrations/0007_merge_20190517_1323.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-05-17 13:23 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("articles", "0006_auto_20190516_1303"), 10 | ("articles", "0003_auto_20190517_1038"), 11 | ] 12 | 13 | operations = [] 14 | -------------------------------------------------------------------------------- /developerportal/apps/articles/migrations/0010_article_labels.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-05-22 16:03 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [("topics", "0001_initial"), ("articles", "0009_auto_20190521_1048")] 10 | 11 | operations = [ 12 | migrations.AddField( 13 | model_name="article", 14 | name="labels", 15 | field=models.ForeignKey( 16 | blank=True, 17 | null=True, 18 | on_delete=django.db.models.deletion.SET_NULL, 19 | to="topics.Topic", 20 | ), 21 | ) 22 | ] 23 | -------------------------------------------------------------------------------- /developerportal/apps/articles/migrations/0011_auto_20190522_1630.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-05-22 16:30 2 | 3 | from django.db import migrations 4 | import modelcluster.fields 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [("topics", "0001_initial"), ("articles", "0010_article_labels")] 10 | 11 | operations = [ 12 | migrations.RemoveField(model_name="article", name="labels"), 13 | migrations.AddField( 14 | model_name="article", 15 | name="labels", 16 | field=modelcluster.fields.ParentalManyToManyField( 17 | blank=True, related_name="_article_labels_+", to="topics.Topic" 18 | ), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /developerportal/apps/articles/migrations/0012_article_author.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-05-30 16:15 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ("wagtailcore", "0041_group_collection_permissions_verbose_name_plural"), 11 | ("articles", "0011_auto_20190522_1630"), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name="article", 17 | name="author", 18 | field=models.ForeignKey( 19 | blank=True, 20 | null=True, 21 | on_delete=django.db.models.deletion.SET_NULL, 22 | related_name="+", 23 | to="wagtailcore.Page", 24 | ), 25 | ) 26 | ] 27 | -------------------------------------------------------------------------------- /developerportal/apps/articles/migrations/0012_article_header_image.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-05-30 09:07 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ("wagtailimages", "0001_squashed_0021"), 11 | ("articles", "0011_auto_20190522_1630"), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name="article", 17 | name="header_image", 18 | field=models.ForeignKey( 19 | blank=True, 20 | null=True, 21 | on_delete=django.db.models.deletion.SET_NULL, 22 | related_name="+", 23 | to="wagtailimages.Image", 24 | ), 25 | ) 26 | ] 27 | -------------------------------------------------------------------------------- /developerportal/apps/articles/migrations/0012_auto_20190603_1048.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-03 10:48 2 | 3 | from django.db import migrations 4 | import modelcluster.fields 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ("topics", "0003_subtopic"), 11 | ("articles", "0011_auto_20190522_1630"), 12 | ] 13 | 14 | operations = [ 15 | migrations.RemoveField(model_name="article", name="labels"), 16 | migrations.AddField( 17 | model_name="article", 18 | name="topics", 19 | field=modelcluster.fields.ParentalManyToManyField( 20 | blank=True, related_name="_article_topics_+", to="topics.Topic" 21 | ), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /developerportal/apps/articles/migrations/0013_merge_20190603_1229.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-03 12:29 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("articles", "0012_article_header_image"), 10 | ("articles", "0012_article_author"), 11 | ] 12 | 13 | operations = [] 14 | -------------------------------------------------------------------------------- /developerportal/apps/articles/migrations/0014_merge_20190604_0953.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-04 09:53 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("articles", "0012_auto_20190603_1048"), 10 | ("articles", "0013_merge_20190603_1229"), 11 | ] 12 | 13 | operations = [] 14 | -------------------------------------------------------------------------------- /developerportal/apps/articles/migrations/0016_auto_20190604_1317.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-04 13:17 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [("articles", "0015_auto_20190604_1245")] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name="articletopic", 14 | name="topic", 15 | field=models.ForeignKey( 16 | null=True, 17 | on_delete=django.db.models.deletion.CASCADE, 18 | to="topics.Topic", 19 | ), 20 | ) 21 | ] 22 | -------------------------------------------------------------------------------- /developerportal/apps/articles/migrations/0017_auto_20190611_1344.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-11 13:44 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [("articles", "0016_auto_20190604_1317")] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name="article", 14 | name="author", 15 | field=models.ForeignKey( 16 | blank=True, 17 | null=True, 18 | on_delete=django.db.models.deletion.SET_NULL, 19 | related_name="articles", 20 | to="people.Person", 21 | ), 22 | ) 23 | ] 24 | -------------------------------------------------------------------------------- /developerportal/apps/articles/migrations/0019_auto_20190611_1453.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-11 14:53 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [("articles", "0018_auto_20190611_1403")] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name="articletopic", 14 | name="topic", 15 | field=models.ForeignKey( 16 | on_delete=django.db.models.deletion.CASCADE, 17 | related_name="+", 18 | to="topics.Topic", 19 | ), 20 | ) 21 | ] 22 | -------------------------------------------------------------------------------- /developerportal/apps/articles/migrations/0020_auto_20190612_0426.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-12 04:26 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("articles", "0019_auto_20190611_1453")] 9 | 10 | operations = [ 11 | migrations.AlterModelOptions(name="article", options={"ordering": ["-date"]}) 12 | ] 13 | -------------------------------------------------------------------------------- /developerportal/apps/articles/migrations/0021_auto_20190618_0840.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-18 08:40 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("articles", "0020_auto_20190612_0426")] 9 | 10 | operations = [ 11 | migrations.AlterField( 12 | model_name="article", 13 | name="intro", 14 | field=models.TextField(blank=True, default="", max_length=250), 15 | ) 16 | ] 17 | -------------------------------------------------------------------------------- /developerportal/apps/articles/migrations/0022_auto_20190624_1622.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-24 16:22 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ("mozimages", "0001_initial"), 11 | ("articles", "0021_auto_20190618_0840"), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name="article", 17 | name="header_image", 18 | field=models.ForeignKey( 19 | blank=True, 20 | null=True, 21 | on_delete=django.db.models.deletion.SET_NULL, 22 | related_name="+", 23 | to="mozimages.MozImage", 24 | ), 25 | ) 26 | ] 27 | -------------------------------------------------------------------------------- /developerportal/apps/articles/migrations/0023_auto_20190709_0928.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.3 on 2019-07-09 09:28 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("articles", "0022_auto_20190624_1622")] 9 | 10 | operations = [ 11 | migrations.AlterModelOptions( 12 | name="articles", options={"verbose_name_plural": "Articles"} 13 | ) 14 | ] 15 | -------------------------------------------------------------------------------- /developerportal/apps/articles/migrations/0026_articles_description.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.3 on 2019-08-02 09:16 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("articles", "0025_auto_20190716_1346")] 9 | 10 | operations = [ 11 | migrations.AddField( 12 | model_name="articles", 13 | name="description", 14 | field=models.TextField( 15 | blank=True, default="", max_length=250, verbose_name="Description" 16 | ), 17 | ) 18 | ] 19 | -------------------------------------------------------------------------------- /developerportal/apps/articles/migrations/0027_merge_20190802_1535.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.3 on 2019-08-02 15:35 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("articles", "0026_auto_20190731_1109"), 10 | ("articles", "0026_articles_description"), 11 | ] 12 | 13 | operations = [] 14 | -------------------------------------------------------------------------------- /developerportal/apps/articles/migrations/0028_merge_20190805_1426.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-08-05 14:26 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("articles", "0027_merge_20190802_1535"), 10 | ("articles", "0027_article_related_links_mdn"), 11 | ] 12 | 13 | operations = [] 14 | -------------------------------------------------------------------------------- /developerportal/apps/articles/migrations/0029_auto_20190813_1302.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-08-13 13:02 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("articles", "0028_merge_20190805_1426")] 9 | 10 | operations = [ 11 | migrations.AlterField( 12 | model_name="article", 13 | name="card_description", 14 | field=models.TextField( 15 | blank=True, default="", max_length=400, verbose_name="Description" 16 | ), 17 | ), 18 | migrations.AlterField( 19 | model_name="article", 20 | name="description", 21 | field=models.TextField(blank=True, default="", max_length=400), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /developerportal/apps/articles/migrations/0032_update_article_manager.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-09-30 09:15 2 | 3 | from django.db import migrations 4 | import django.db.models.manager 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('articles', '0031_auto_20190819_1304'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterModelManagers( 15 | name='article', 16 | managers=[ 17 | ('published_objects', django.db.models.manager.Manager()), 18 | ], 19 | ), 20 | migrations.AlterModelManagers( 21 | name='articles', 22 | managers=[ 23 | ('published_objects', django.db.models.manager.Manager()), 24 | ], 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /developerportal/apps/articles/migrations/0035_soft_rename_Article_as_Post_via_meta.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.6 on 2019-11-01 10:54 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("articles", "0034_external_person_description")] 9 | 10 | operations = [ 11 | migrations.AlterModelOptions( 12 | name="article", 13 | options={"verbose_name": "post", "verbose_name_plural": "posts"}, 14 | ), 15 | migrations.AlterModelOptions( 16 | name="articles", 17 | options={"verbose_name": "posts", "verbose_name_plural": "posts"}, 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /developerportal/apps/articles/migrations/0039_copy_image_field_data_to_card_image_field.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.12 on 2020-05-06 22:10 2 | 3 | """Copy any image set as `Article.image` to populate the `Article.card_image` field 4 | instead, including updating the latest revision of the page (draft or live)""" 5 | 6 | from django.db import migrations 7 | 8 | from developerportal.apps.common.migration_utils import ( 9 | move_Page_image_to_Page_card_image, 10 | ) 11 | 12 | 13 | def forwards(apps, schema_editor): 14 | PageSubclass = apps.get_model("articles", "Article") 15 | move_Page_image_to_Page_card_image(PageSubclass) 16 | 17 | 18 | class Migration(migrations.Migration): 19 | 20 | dependencies = [("articles", "0038_rename_related_links_without_data_port")] 21 | 22 | operations = [migrations.RunPython(forwards, migrations.RunPython.noop)] 23 | -------------------------------------------------------------------------------- /developerportal/apps/articles/migrations/0040_remove_article_image.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.12 on 2020-05-07 21:13 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('articles', '0039_copy_image_field_data_to_card_image_field'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='article', 15 | name='image', 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /developerportal/apps/articles/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/apps/articles/migrations/__init__.py -------------------------------------------------------------------------------- /developerportal/apps/articles/templates/articles.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load static %} 3 | {% load wagtailcore_tags %} 4 | 5 | {% comment %} For now we're not renaming CSS classes from articles to posts {% endcomment %} 6 | {% block body_class %}articles{% endblock %} 7 | 8 | {% block content %} 9 | 10 | {% static "img/icons/article-white.svg" as page_icon_asset_url %} 11 | {% include "molecules/header-strip.html" with content=page.title element="h1" page_icon_asset_url=page_icon_asset_url %} 12 | 13 |
14 | {% include "organisms/filter-list.html" with type="article_or_video" resources=resources no_resources_message="No relevant posts found" hide_pagination=False %} 15 |
16 | {% include "organisms/newsletter-signup.html" %} 17 | {% endblock content %} 18 | -------------------------------------------------------------------------------- /developerportal/apps/articles/templates/partials/verbose_standfirst.html: -------------------------------------------------------------------------------- 1 | {% load wagtailcore_tags %} 2 |

3 | {{page.read_time}} | {{page.date|date}} 4 |

5 | {% if page.description %} 6 | {{page.description|richtext}} 7 | {% endif %} 8 | -------------------------------------------------------------------------------- /developerportal/apps/articles/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/apps/articles/tests/__init__.py -------------------------------------------------------------------------------- /developerportal/apps/articles/wagtail_hooks.py: -------------------------------------------------------------------------------- 1 | from wagtail.contrib.modeladmin.options import ModelAdmin, modeladmin_register 2 | 3 | from ..common.helpers import ExplorerRedirectAdminURLHelper 4 | from .models import Articles 5 | 6 | 7 | class ArticlesAdmin(ModelAdmin): 8 | model = Articles 9 | menu_icon = "doc-full-inverse" 10 | menu_order = 210 11 | url_helper_class = ExplorerRedirectAdminURLHelper 12 | 13 | 14 | modeladmin_register(ArticlesAdmin) 15 | -------------------------------------------------------------------------------- /developerportal/apps/common/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/apps/common/__init__.py -------------------------------------------------------------------------------- /developerportal/apps/common/image_formats.py: -------------------------------------------------------------------------------- 1 | from wagtail.images.formats import ( 2 | Format, 3 | register_image_format, 4 | unregister_image_format, 5 | ) 6 | 7 | unregister_image_format("fullwidth") 8 | unregister_image_format("left") 9 | unregister_image_format("right") 10 | 11 | register_image_format( 12 | Format("left", "Left-aligned", "richtext-image format-left", "width-300") 13 | ) 14 | register_image_format( 15 | Format("right", "Right-aligned", "richtext-image format-right", "width-300") 16 | ) 17 | register_image_format( 18 | Format("center", "Center-aligned", "richtext-image format-center", "width-300") 19 | ) 20 | -------------------------------------------------------------------------------- /developerportal/apps/common/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/apps/common/migrations/__init__.py -------------------------------------------------------------------------------- /developerportal/apps/common/settings_helpers.py: -------------------------------------------------------------------------------- 1 | """Functionality to help with configuring settings. 2 | DO NOT IMPORT django.conf.settings INTO THIS MODULE because it is imported 3 | into settings.base 4 | """ 5 | import os 6 | 7 | from developerportal.regex import REDIS_DB_URL_PATTERN 8 | 9 | 10 | def _get_redis_url_for_cache(redis_cache_db_number): 11 | _redis_cache_url = os.environ.get("REDIS_URL", "redis://redis:6379") 12 | if REDIS_DB_URL_PATTERN.match(_redis_cache_url): 13 | raise RuntimeError( 14 | "REDIS_URL specifies a specific database, not just the server" 15 | ) 16 | if _redis_cache_url[-1] == "/": 17 | _redis_cache_url = _redis_cache_url[:-1] 18 | return _redis_cache_url + f"/{redis_cache_db_number}" 19 | -------------------------------------------------------------------------------- /developerportal/apps/common/tasks/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/apps/common/tasks/__init__.py -------------------------------------------------------------------------------- /developerportal/apps/common/templates/button_block.html: -------------------------------------------------------------------------------- 1 | {% load wagtailcore_tags %} 2 | {% load app_filters %} 3 | 4 | {% if value.page_link or value.external_link %} 5 |

6 | {% if value.page_link %} 7 | 8 | {% elif value.external_link %} 9 | 10 | {% endif %} 11 | {{ value.text }} 12 | 13 |

{% endif %} 14 | -------------------------------------------------------------------------------- /developerportal/apps/common/templates/code_snippet_block.html: -------------------------------------------------------------------------------- 1 | {% load wagtailcore_tags %} 2 | {% load app_filters %} 3 | 4 |
5 | {{ value.code|syntax_highlight:value.language }} 6 |
7 | -------------------------------------------------------------------------------- /developerportal/apps/common/test_helpers.py: -------------------------------------------------------------------------------- 1 | # Patch wagtail.tests.utils.WagtailPageTests to re-configure use of the ModelBackend 2 | # so that WagtailPageTests.client.login() will work 3 | 4 | from django.test import override_settings 5 | 6 | from wagtail.tests.utils import WagtailPageTests 7 | 8 | 9 | class PatchedWagtailPageTests(WagtailPageTests): 10 | @override_settings( 11 | AUTHENTICATION_BACKENDS=( 12 | "mozilla_django_oidc.auth.OIDCAuthenticationBackend", 13 | "django.contrib.auth.backends.ModelBackend", 14 | ) 15 | ) 16 | def login(self): 17 | return super().login() 18 | -------------------------------------------------------------------------------- /developerportal/apps/common/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/apps/common/tests/__init__.py -------------------------------------------------------------------------------- /developerportal/apps/common/tests/test_models.py: -------------------------------------------------------------------------------- 1 | from unittest import mock 2 | 3 | from django.test import TestCase 4 | 5 | from ...content.models import ContentPage 6 | 7 | 8 | class PageBaseModelTestsUsingConcreteClass(TestCase): 9 | """Tests that use a concrete ContentPage class to test behaviour 10 | from the BasePage""" 11 | 12 | def test_save(self): 13 | 14 | with mock.patch( 15 | "developerportal.apps.common.models.cache.delete_many" 16 | ) as mock_delete_many: 17 | 18 | self.assertEqual(ContentPage._bulk_invalidation_cache_keys, []) 19 | 20 | page = ContentPage(slug="test", path="00010022", depth=2, title="Test") 21 | page._bulk_invalidation_cache_keys = ["foo", "bar", "baz"] 22 | page.save() 23 | mock_delete_many.assert_called_once_with(["foo", "bar", "baz"]) 24 | -------------------------------------------------------------------------------- /developerportal/apps/common/tests/test_survey_tags.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase, override_settings 2 | 3 | 4 | class SurveyTagTests(TestCase): 5 | def test_survey_content_only_rendered_if_survey_url_configured(self): 6 | with override_settings(TASK_COMPLETION_SURVEY_URL="https://example.com/test"): 7 | response = self.client.get("/") 8 | self.assertContains(response, "complete a short survey", 1) 9 | 10 | with override_settings(TASK_COMPLETION_SURVEY_URL=None): 11 | response = self.client.get("/") 12 | self.assertNotContains(response, "complete a short survey") 13 | 14 | with override_settings(TASK_COMPLETION_SURVEY_URL="undefined"): 15 | response = self.client.get("/") 16 | self.assertNotContains(response, "complete a short survey") 17 | -------------------------------------------------------------------------------- /developerportal/apps/common/validators.py: -------------------------------------------------------------------------------- 1 | from django.core.exceptions import ValidationError 2 | 3 | 4 | def check_for_svg_file(obj): 5 | # A very light, naive check that the file at least has an .svg suffix. 6 | # This is NOT 100% safe/guaranteed, but given the users are trusted, this just a 7 | # small layer of protection against accidental oversights, because saving a bitmap 8 | # into this field by accident will cause `app_tags.render_svg()` to fail. 9 | 10 | if obj.file.name.split(".")[-1] != "svg": 11 | raise ValidationError(u"Only SVG images are allowed here.") 12 | -------------------------------------------------------------------------------- /developerportal/apps/common/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | from django.views.decorators.cache import never_cache 3 | 4 | 5 | @never_cache 6 | def rate_limited(request, exception): 7 | """Render a rate-limited exception page""" 8 | response = render(request, "429.html", status=429) 9 | response["Retry-After"] = "60" 10 | return response 11 | -------------------------------------------------------------------------------- /developerportal/apps/content/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/apps/content/__init__.py -------------------------------------------------------------------------------- /developerportal/apps/content/migrations/0002_contentpage_hero_image.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-08-12 10:15 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [("mozimages", "0001_initial"), ("content", "0001_initial")] 10 | 11 | operations = [ 12 | migrations.AddField( 13 | model_name="contentpage", 14 | name="hero_image", 15 | field=models.ForeignKey( 16 | blank=True, 17 | null=True, 18 | on_delete=django.db.models.deletion.SET_NULL, 19 | related_name="+", 20 | to="mozimages.MozImage", 21 | ), 22 | ) 23 | ] 24 | -------------------------------------------------------------------------------- /developerportal/apps/content/migrations/0004_update_content_manager.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-09-30 09:15 2 | 3 | from django.db import migrations 4 | import django.db.models.manager 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('content', '0003_auto_20190813_1503'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterModelManagers( 15 | name='contentpage', 16 | managers=[ 17 | ('published_objects', django.db.models.manager.Manager()), 18 | ], 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /developerportal/apps/content/migrations/0005_add_social_image_field.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.6 on 2019-10-01 15:59 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('mozimages', '0001_initial'), 11 | ('content', '0004_update_content_manager'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='contentpage', 17 | name='social_image', 18 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='mozimages.MozImage'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /developerportal/apps/content/migrations/0006_contentpage_icon.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.11 on 2020-03-15 15:23 2 | 3 | import developerportal.apps.common.validators 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('content', '0005_add_social_image_field'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='contentpage', 16 | name='icon', 17 | field=models.FileField(blank=True, default='', help_text='MUST be a black-on-transparent SVG icon ONLY, with no bitmap embedded in it.', upload_to='contentpage/icons', validators=[developerportal.apps.common.validators.check_for_svg_file]), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /developerportal/apps/content/migrations/0007_contentpage_description.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.11 on 2020-03-15 15:38 2 | 3 | from django.db import migrations 4 | import wagtail.core.fields 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('content', '0006_contentpage_icon'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='contentpage', 16 | name='description', 17 | field=wagtail.core.fields.RichTextField(blank=True, default='', help_text='Optional short text description, max. 400 characters', max_length=400), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /developerportal/apps/content/migrations/0008_contentpage_nav_description.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.11 on 2020-03-18 11:28 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('content', '0007_contentpage_description'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='contentpage', 15 | name='nav_description', 16 | field=models.TextField(blank=True, default='', max_length=400, verbose_name='Navigation description'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /developerportal/apps/content/migrations/0011_remove_contentpage_hero_image.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.12 on 2020-05-07 21:13 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('content', '0010_copy_hero_image_field_to_card_image_field'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='contentpage', 15 | name='hero_image', 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /developerportal/apps/content/migrations/0012_auto_20200511_1148.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.12 on 2020-05-11 11:48 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('content', '0011_remove_contentpage_hero_image'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='contentpage', 16 | name='card_image', 17 | field=models.ForeignKey(blank=True, help_text='An image in 16:9 aspect ratio', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='mozimages.MozImage', verbose_name='Image'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /developerportal/apps/content/migrations/0014_contentpage_card_image_3_2.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.14 on 2020-07-15 10:29 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('mozimages', '0001_initial'), 11 | ('content', '0013_make_sidebar_optional'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='contentpage', 17 | name='card_image_3_2', 18 | field=models.ForeignKey(blank=True, help_text='An image in 3:2 aspect ratio', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='mozimages.MozImage', verbose_name='Image'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /developerportal/apps/content/migrations/0015_hide_about_page_from_menu.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.14 on 2020-07-13 12:00 2 | 3 | from django.db import migrations 4 | 5 | ABOUT_PAGE_SLUG = "about" 6 | 7 | 8 | def forwards(apps, schema_editor): 9 | ContentPage = apps.get_model("content", "ContentPage") 10 | 11 | ContentPage.objects.filter(slug=ABOUT_PAGE_SLUG).update(show_in_menus=False) 12 | 13 | 14 | def backwards(apps, schema_editor): 15 | ContentPage = apps.get_model("content", "ContentPage") 16 | ContentPage.objects.filter(slug=ABOUT_PAGE_SLUG).update(show_in_menus=True) 17 | 18 | 19 | class Migration(migrations.Migration): 20 | 21 | dependencies = [("content", "0014_contentpage_card_image_3_2")] 22 | 23 | operations = [migrations.RunPython(forwards, backwards)] 24 | -------------------------------------------------------------------------------- /developerportal/apps/content/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/apps/content/migrations/__init__.py -------------------------------------------------------------------------------- /developerportal/apps/content/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/apps/content/tests/__init__.py -------------------------------------------------------------------------------- /developerportal/apps/content/tests/test_content.py: -------------------------------------------------------------------------------- 1 | from developerportal.apps.common.test_helpers import PatchedWagtailPageTests 2 | 3 | from ...home.models import HomePage 4 | from ...people.models import People 5 | from ...topics.models import Topic 6 | from ..models import ContentPage 7 | 8 | 9 | class ContentPageTests(PatchedWagtailPageTests): 10 | """Tests for the ContentPage model.""" 11 | 12 | def test_content_page_parent_pages(self): 13 | self.assertAllowedParentPageTypes(ContentPage, {ContentPage, HomePage, Topic}) 14 | 15 | def test_content_page_subpages(self): 16 | self.assertAllowedSubpageTypes(ContentPage, {ContentPage, People}) 17 | -------------------------------------------------------------------------------- /developerportal/apps/events/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/apps/events/__init__.py -------------------------------------------------------------------------------- /developerportal/apps/events/migrations/0003_auto_20190709_0928.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.3 on 2019-07-09 09:28 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("events", "0002_auto_20190704_1334")] 9 | 10 | operations = [ 11 | migrations.AlterModelOptions( 12 | name="events", options={"verbose_name_plural": "Events"} 13 | ) 14 | ] 15 | -------------------------------------------------------------------------------- /developerportal/apps/events/migrations/0003_auto_20190709_1241.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.3 on 2019-07-09 12:41 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("events", "0002_auto_20190704_1334")] 9 | 10 | operations = [ 11 | migrations.AddField( 12 | model_name="event", 13 | name="latitude", 14 | field=models.FloatField(blank=True, null=True), 15 | ), 16 | migrations.AddField( 17 | model_name="event", 18 | name="longitude", 19 | field=models.FloatField(blank=True, null=True), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /developerportal/apps/events/migrations/0004_merge_20190711_1925.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.3 on 2019-07-11 19:25 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("events", "0003_auto_20190709_0928"), 10 | ("events", "0003_auto_20190709_1241"), 11 | ] 12 | 13 | operations = [] 14 | -------------------------------------------------------------------------------- /developerportal/apps/events/migrations/0005_merge_20190711_2019.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.3 on 2019-07-11 20:19 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("events", "0004_auto_20190708_1100"), 10 | ("events", "0004_merge_20190711_1925"), 11 | ("events", "0004_auto_20190711_1501"), 12 | ] 13 | 14 | operations = [] 15 | -------------------------------------------------------------------------------- /developerportal/apps/events/migrations/0010_auto_20190718_1551.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.3 on 2019-07-18 15:51 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("events", "0007_auto_20190718_1424")] 9 | 10 | operations = [ 11 | migrations.RenameField( 12 | model_name="events", old_name="featured_event", new_name="featured" 13 | ) 14 | ] 15 | -------------------------------------------------------------------------------- /developerportal/apps/events/migrations/0014_auto_20190813_1302.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-08-13 13:02 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("events", "0013_auto_20190731_1054")] 9 | 10 | operations = [ 11 | migrations.AlterField( 12 | model_name="event", 13 | name="card_description", 14 | field=models.TextField( 15 | blank=True, default="", max_length=400, verbose_name="Description" 16 | ), 17 | ), 18 | migrations.AlterField( 19 | model_name="event", 20 | name="description", 21 | field=models.TextField(blank=True, default="", max_length=400), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /developerportal/apps/events/migrations/0017_auto_20190819_1304.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-08-19 13:04 2 | 3 | from django.db import migrations 4 | import wagtail.core.fields 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [("events", "0016_auto_20190814_1600")] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name="event", 14 | name="description", 15 | field=wagtail.core.fields.RichTextField( 16 | blank=True, 17 | default="", 18 | help_text="Optional short text description, max. 400 characters", 19 | max_length=400, 20 | ), 21 | ) 22 | ] 23 | -------------------------------------------------------------------------------- /developerportal/apps/events/migrations/0018_update_event_manager.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-09-30 09:15 2 | 3 | from django.db import migrations 4 | import django.db.models.manager 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('events', '0017_auto_20190819_1304'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterModelManagers( 15 | name='event', 16 | managers=[ 17 | ('published_objects', django.db.models.manager.Manager()), 18 | ], 19 | ), 20 | migrations.AlterModelManagers( 21 | name='events', 22 | managers=[ 23 | ('published_objects', django.db.models.manager.Manager()), 24 | ], 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /developerportal/apps/events/migrations/0021_auto_20200326_1339.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.11 on 2020-03-26 13:39 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('events', '0020_events_body'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='event', 15 | name='event_content', 16 | field=models.URLField(blank=True, default='', help_text='Link to a page (in this site or elsewhere) with content about this event.', verbose_name='Event content'), 17 | ), 18 | migrations.AddField( 19 | model_name='event', 20 | name='official_website', 21 | field=models.URLField(blank=True, default='', verbose_name='Official website'), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /developerportal/apps/events/migrations/0022_copy_image_field_data_to_card_image_field.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.12 on 2020-05-07 12:15 2 | 3 | """Copy any image set as `Event.image` to populate the `Event.card_image` field 4 | instead, including updating the latest revision of the page (draft or live)""" 5 | 6 | from django.db import migrations 7 | 8 | from developerportal.apps.common.migration_utils import ( 9 | move_Page_image_to_Page_card_image, 10 | ) 11 | 12 | 13 | def forwards(apps, schema_editor): 14 | PageSubclass = apps.get_model("events", "Event") 15 | move_Page_image_to_Page_card_image(PageSubclass) 16 | 17 | 18 | class Migration(migrations.Migration): 19 | 20 | dependencies = [("events", "0021_auto_20200326_1339")] 21 | 22 | operations = [migrations.RunPython(forwards, migrations.RunPython.noop)] 23 | -------------------------------------------------------------------------------- /developerportal/apps/events/migrations/0023_remove_event_image.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.12 on 2020-05-07 21:13 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('events', '0022_copy_image_field_data_to_card_image_field'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='event', 15 | name='image', 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /developerportal/apps/events/migrations/0028_event_is_mozilla_supported_event.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.13 on 2020-06-29 19:20 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("events", "0027_add_Events_top_content_and_bottom_content")] 9 | 10 | operations = [ 11 | migrations.AddField( 12 | model_name="event", 13 | name="is_mozilla_supported_event", 14 | field=models.BooleanField( 15 | blank=True, 16 | default=False, 17 | null=True, 18 | verbose_name="Is this event supported by Mozilla?", 19 | ), 20 | ) 21 | ] 22 | -------------------------------------------------------------------------------- /developerportal/apps/events/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/apps/events/migrations/__init__.py -------------------------------------------------------------------------------- /developerportal/apps/events/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/apps/events/tests/__init__.py -------------------------------------------------------------------------------- /developerportal/apps/events/wagtail_hooks.py: -------------------------------------------------------------------------------- 1 | from wagtail.contrib.modeladmin.options import ModelAdmin, modeladmin_register 2 | 3 | from ..common.helpers import ExplorerRedirectAdminURLHelper 4 | from .models import Events 5 | 6 | 7 | class EventsAdmin(ModelAdmin): 8 | model = Events 9 | menu_icon = "date" 10 | menu_order = 220 11 | url_helper_class = ExplorerRedirectAdminURLHelper 12 | 13 | 14 | modeladmin_register(EventsAdmin) 15 | -------------------------------------------------------------------------------- /developerportal/apps/externalcontent/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/apps/externalcontent/__init__.py -------------------------------------------------------------------------------- /developerportal/apps/externalcontent/migrations/0002_auto_20190704_1221.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.3 on 2019-07-04 12:21 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("externalcontent", "0001_initial")] 9 | 10 | operations = [ 11 | migrations.RenameField( 12 | model_name="externalcontent", old_name="url", new_name="external_url" 13 | ) 14 | ] 15 | -------------------------------------------------------------------------------- /developerportal/apps/externalcontent/migrations/0004_auto_20190705_1306.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.3 on 2019-07-05 13:06 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("externalcontent", "0003_externalarticle_externalevent_externalvideo") 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name="externalarticle", 15 | name="readtime", 16 | field=models.CharField(blank=True, default="0 min read", max_length=30), 17 | ), 18 | migrations.AddField( 19 | model_name="externalvideo", 20 | name="video_duration", 21 | field=models.CharField(blank=True, default="0:00", max_length=30), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /developerportal/apps/externalcontent/migrations/0008_externalcontent_description.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.3 on 2019-07-23 12:59 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("externalcontent", "0007_auto_20190711_1940")] 9 | 10 | operations = [ 11 | migrations.AddField( 12 | model_name="externalcontent", 13 | name="description", 14 | field=models.TextField(blank=True, default="", max_length=250), 15 | ) 16 | ] 17 | -------------------------------------------------------------------------------- /developerportal/apps/externalcontent/migrations/0012_auto_20190724_1437.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.3 on 2019-07-24 14:37 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("externalcontent", "0011_auto_20190723_1719")] 9 | 10 | operations = [ 11 | migrations.AlterModelOptions( 12 | name="externalcontent", options={"verbose_name_plural": "External Content"} 13 | ) 14 | ] 15 | -------------------------------------------------------------------------------- /developerportal/apps/externalcontent/migrations/0013_externalevent_venue.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.3 on 2019-07-24 15:38 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("externalcontent", "0012_auto_20190724_1437")] 9 | 10 | operations = [ 11 | migrations.AddField( 12 | model_name="externalevent", 13 | name="venue", 14 | field=models.TextField( 15 | blank=True, 16 | default="", 17 | help_text="Full address of the event venue, displayed on the event detail page", 18 | max_length=250, 19 | ), 20 | ) 21 | ] 22 | -------------------------------------------------------------------------------- /developerportal/apps/externalcontent/migrations/0018_auto_20190807_1244.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-08-07 12:44 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("externalcontent", "0017_auto_20190806_1041")] 9 | 10 | operations = [ 11 | migrations.AlterField( 12 | model_name="externalvideo", 13 | name="duration", 14 | field=models.CharField( 15 | blank=True, 16 | help_text="Optional. Video duration in MM:SS format e.g. “12:34”. Shown as a small hint when the video is displayed as a card.", 17 | max_length=30, 18 | null=True, 19 | ), 20 | ) 21 | ] 22 | -------------------------------------------------------------------------------- /developerportal/apps/externalcontent/migrations/0019_auto_20190813_1302.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-08-13 13:02 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("externalcontent", "0018_auto_20190807_1244")] 9 | 10 | operations = [ 11 | migrations.AlterField( 12 | model_name="externalcontent", 13 | name="description", 14 | field=models.TextField(blank=True, default="", max_length=400), 15 | ) 16 | ] 17 | -------------------------------------------------------------------------------- /developerportal/apps/externalcontent/migrations/0022_auto_20190819_1304.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-08-19 13:04 2 | 3 | from django.db import migrations 4 | import wagtail.core.fields 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [("externalcontent", "0021_auto_20190814_1600")] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name="externalcontent", 14 | name="description", 15 | field=wagtail.core.fields.RichTextField( 16 | blank=True, 17 | default="", 18 | help_text="Optional short text description, max. 400 characters", 19 | max_length=400, 20 | ), 21 | ) 22 | ] 23 | -------------------------------------------------------------------------------- /developerportal/apps/externalcontent/migrations/0025_add_social_image_field.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.6 on 2019-10-01 15:59 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('mozimages', '0001_initial'), 11 | ('externalcontent', '0024_update_externalcontent_manager'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='externalcontent', 17 | name='social_image', 18 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='mozimages.MozImage'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /developerportal/apps/externalcontent/migrations/0027_relabel_external_article_as_external_post.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.6 on 2019-11-01 11:51 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('externalcontent', '0026_external_person_description'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterModelOptions( 14 | name='externalarticle', 15 | options={'verbose_name': 'External Post', 'verbose_name_plural': 'External Posts'}, 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /developerportal/apps/externalcontent/migrations/0029_rename_image_to_card_image.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.12 on 2020-05-07 20:43 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("externalcontent", "0028_rephrase_articles_as_posts")] 9 | 10 | operations = [ 11 | migrations.RenameField( 12 | model_name="externalcontent", old_name="image", new_name="card_image" 13 | ) 14 | ] 15 | -------------------------------------------------------------------------------- /developerportal/apps/externalcontent/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/apps/externalcontent/migrations/__init__.py -------------------------------------------------------------------------------- /developerportal/apps/externalcontent/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/apps/externalcontent/tests/__init__.py -------------------------------------------------------------------------------- /developerportal/apps/health/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/apps/health/__init__.py -------------------------------------------------------------------------------- /developerportal/apps/health/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/apps/health/tests/__init__.py -------------------------------------------------------------------------------- /developerportal/apps/health/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | 3 | from . import views 4 | 5 | urlpatterns = [ 6 | url(r"^healthz/?$", views.liveness, name="health.liveness"), 7 | url(r"^readiness/?$", views.readiness, name="health.readiness"), 8 | ] 9 | -------------------------------------------------------------------------------- /developerportal/apps/home/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/apps/home/__init__.py -------------------------------------------------------------------------------- /developerportal/apps/home/migrations/0006_auto_20190625_1111.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-25 11:11 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [("home", "0005_auto_20190618_1635")] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name="homepage", 14 | name="header_image", 15 | field=models.ForeignKey( 16 | blank=True, 17 | null=True, 18 | on_delete=django.db.models.deletion.SET_NULL, 19 | related_name="+", 20 | to="mozimages.MozImage", 21 | ), 22 | ) 23 | ] 24 | -------------------------------------------------------------------------------- /developerportal/apps/home/migrations/0007_auto_20190625_1112.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-25 11:12 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [("home", "0006_auto_20190625_1111")] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name="homepage", 14 | name="header_image", 15 | field=models.ForeignKey( 16 | blank=True, 17 | null=True, 18 | on_delete=django.db.models.deletion.SET_NULL, 19 | related_name="+", 20 | to="wagtailimages.Image", 21 | ), 22 | ) 23 | ] 24 | -------------------------------------------------------------------------------- /developerportal/apps/home/migrations/0008_auto_20190625_1119.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-25 11:19 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [("home", "0007_auto_20190625_1112")] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name="homepage", 14 | name="header_image", 15 | field=models.ForeignKey( 16 | blank=True, 17 | null=True, 18 | on_delete=django.db.models.deletion.SET_NULL, 19 | related_name="+", 20 | to="mozimages.MozImage", 21 | ), 22 | ) 23 | ] 24 | -------------------------------------------------------------------------------- /developerportal/apps/home/migrations/0009_auto_20190702_0904.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-07-02 09:04 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("home", "0008_auto_20190625_1119")] 9 | 10 | operations = [ 11 | migrations.AlterField( 12 | model_name="homepage", 13 | name="button_text", 14 | field=models.CharField(blank=True, default="", max_length=30), 15 | ), 16 | migrations.AlterField( 17 | model_name="homepage", 18 | name="button_url", 19 | field=models.URLField(blank=True, default="", max_length=140), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /developerportal/apps/home/migrations/0009_homepage_featured.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-27 15:23 2 | 3 | from django.db import migrations 4 | import wagtail.core.fields 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [("home", "0008_auto_20190625_1119")] 10 | 11 | operations = [ 12 | migrations.AddField( 13 | model_name="homepage", 14 | name="featured", 15 | field=wagtail.core.fields.StreamField([], blank=True, null=True), 16 | ) 17 | ] 18 | -------------------------------------------------------------------------------- /developerportal/apps/home/migrations/0010_auto_20190704_1031.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.3 on 2019-07-04 10:31 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("home", "0009_auto_20190702_0904")] 9 | 10 | operations = [ 11 | migrations.AlterField( 12 | model_name="homepage", 13 | name="button_url", 14 | field=models.URLField(blank=True, default="", max_length=2048), 15 | ) 16 | ] 17 | -------------------------------------------------------------------------------- /developerportal/apps/home/migrations/0011_auto_20190705_1258.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.3 on 2019-07-05 12:58 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [("home", "0010_auto_20190704_1031")] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name="homepagefeaturedarticle", 14 | name="article", 15 | field=models.ForeignKey( 16 | on_delete=django.db.models.deletion.CASCADE, 17 | related_name="+", 18 | to="wagtailcore.Page", 19 | ), 20 | ) 21 | ] 22 | -------------------------------------------------------------------------------- /developerportal/apps/home/migrations/0018_merge_20190702_1247.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-07-02 12:47 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("home", "0017_auto_20190628_1438"), 10 | ("home", "0009_auto_20190702_0904"), 11 | ] 12 | 13 | operations = [] 14 | -------------------------------------------------------------------------------- /developerportal/apps/home/migrations/0019_merge_20190709_0910.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.3 on 2019-07-09 09:10 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("home", "0018_merge_20190702_1247"), 10 | ("home", "0011_auto_20190705_1258"), 11 | ] 12 | 13 | operations = [] 14 | -------------------------------------------------------------------------------- /developerportal/apps/home/migrations/0024_auto_20190718_1438.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.3 on 2019-07-18 14:38 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("home", "0023_auto_20190712_1023")] 9 | 10 | operations = [ 11 | migrations.AlterField( 12 | model_name="homepage", 13 | name="button_url", 14 | field=models.CharField(blank=True, default="", max_length=2048), 15 | ) 16 | ] 17 | -------------------------------------------------------------------------------- /developerportal/apps/home/migrations/0026_auto_20190813_1302.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-08-13 13:02 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("home", "0025_homepage_external_promos")] 9 | 10 | operations = [ 11 | migrations.AlterField( 12 | model_name="homepage", 13 | name="card_description", 14 | field=models.TextField( 15 | blank=True, default="", max_length=400, verbose_name="Description" 16 | ), 17 | ) 18 | ] 19 | -------------------------------------------------------------------------------- /developerportal/apps/home/migrations/0029_update_home_manager.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-09-30 09:15 2 | 3 | from django.db import migrations 4 | import django.db.models.manager 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('home', '0028_auto_20190814_1600'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterModelManagers( 15 | name='homepage', 16 | managers=[ 17 | ('published_objects', django.db.models.manager.Manager()), 18 | ], 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /developerportal/apps/home/migrations/0030_add_social_image_field.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.6 on 2019-10-01 15:59 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('mozimages', '0001_initial'), 11 | ('home', '0029_update_home_manager'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='homepage', 17 | name='social_image', 18 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='mozimages.MozImage'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /developerportal/apps/home/migrations/0031_homepage_featured_people.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.6 on 2019-10-08 13:59 2 | 3 | from django.db import migrations 4 | import wagtail.core.blocks 5 | import wagtail.core.fields 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('home', '0030_add_social_image_field'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='homepage', 17 | name='featured_people', 18 | field=wagtail.core.fields.StreamField([('person', wagtail.core.blocks.PageChooserBlock(page_type=['people.Person']))], blank=True, help_text='Optional featured people, max. 3', null=True), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /developerportal/apps/home/migrations/0032_homepage_show_header.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.6 on 2019-10-15 15:48 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('home', '0031_homepage_featured_people'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='homepage', 15 | name='show_header', 16 | field=models.BooleanField(default=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /developerportal/apps/home/migrations/0035_remove_homepage_external_promos.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.11 on 2020-03-25 23:00 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('home', '0034_support_featuring_video_in_homepage'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='homepage', 15 | name='external_promos', 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /developerportal/apps/home/migrations/0037_remove_homepage_show_header.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.12 on 2020-05-26 13:05 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('home', '0036_update_streamblock'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='homepage', 15 | name='show_header', 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /developerportal/apps/home/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/apps/home/migrations/__init__.py -------------------------------------------------------------------------------- /developerportal/apps/home/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/apps/home/tests/__init__.py -------------------------------------------------------------------------------- /developerportal/apps/home/tests/test_home.py: -------------------------------------------------------------------------------- 1 | from wagtail.core.models import Page 2 | 3 | from developerportal.apps.common.test_helpers import PatchedWagtailPageTests 4 | 5 | from ...articles.models import Articles 6 | from ...content.models import ContentPage 7 | from ...events.models import Events 8 | from ...people.models import People 9 | from ...topics.models import Topics 10 | from ...videos.models import Videos 11 | from ..models import HomePage 12 | 13 | 14 | class HomePageTests(PatchedWagtailPageTests): 15 | """Tests for the HomePage model.""" 16 | 17 | def test_topic_parent_pages(self): 18 | self.assertAllowedParentPageTypes(HomePage, {Page}) 19 | 20 | def test_home_page_subpages(self): 21 | self.assertAllowedSubpageTypes( 22 | HomePage, {Articles, ContentPage, Events, Topics, People, Videos} 23 | ) 24 | -------------------------------------------------------------------------------- /developerportal/apps/ingestion/__init__.py: -------------------------------------------------------------------------------- 1 | from .celery import app as celery_app 2 | 3 | # Make sure Celery is always imported when Django starts 4 | # so that @shared_task will use this app. 5 | __all__ = ("celery_app",) 6 | -------------------------------------------------------------------------------- /developerportal/apps/ingestion/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | from .models import IngestionConfiguration 4 | 5 | 6 | class IngestionConfigAdmin(admin.ModelAdmin): 7 | model = IngestionConfiguration 8 | 9 | 10 | admin.site.register(IngestionConfiguration, IngestionConfigAdmin) 11 | -------------------------------------------------------------------------------- /developerportal/apps/ingestion/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | from .celery import app as celery_app 4 | 5 | # Make sure Celery is always imported when Django starts 6 | # so that @shared_task will use this app. 7 | __all__ = ("celery_app",) 8 | 9 | 10 | class IngestionConfig(AppConfig): 11 | name = "ingestion" 12 | -------------------------------------------------------------------------------- /developerportal/apps/ingestion/constants.py: -------------------------------------------------------------------------------- 1 | # For the skeleton ingestion user, who counts as the Editor of imported content 2 | INGESTION_USER_USERNAME = "ingestion_user" 3 | INGESTION_USER_FIRST_NAME = "Portal" 4 | INGESTION_USER_LAST_NAME = "Bot" 5 | INGESTION_USER_EMAIL = "Developer" 6 | -------------------------------------------------------------------------------- /developerportal/apps/ingestion/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/apps/ingestion/migrations/__init__.py -------------------------------------------------------------------------------- /developerportal/apps/ingestion/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/apps/ingestion/tests/__init__.py -------------------------------------------------------------------------------- /developerportal/apps/mozimages/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/apps/mozimages/__init__.py -------------------------------------------------------------------------------- /developerportal/apps/mozimages/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/apps/mozimages/migrations/__init__.py -------------------------------------------------------------------------------- /developerportal/apps/mozimages/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | from wagtail.images.models import AbstractImage, AbstractRendition, Image 4 | 5 | 6 | class MozImage(AbstractImage): 7 | # Additional fields: 8 | caption = models.CharField(max_length=255, blank=True) 9 | 10 | admin_form_fields = Image.admin_form_fields + ("caption",) 11 | 12 | 13 | class Rendition(AbstractRendition): 14 | image = models.ForeignKey( 15 | MozImage, on_delete=models.CASCADE, related_name="renditions" 16 | ) 17 | 18 | class Meta: 19 | unique_together = (("image", "filter_spec", "focal_point_key"),) 20 | -------------------------------------------------------------------------------- /developerportal/apps/people/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/apps/people/__init__.py -------------------------------------------------------------------------------- /developerportal/apps/people/edit_handlers.py: -------------------------------------------------------------------------------- 1 | from wagtail.admin.edit_handlers import FieldPanel 2 | 3 | 4 | class CustomLabelFieldPanel(FieldPanel): 5 | def __init__(self, *args, label="Full name", **kwargs): 6 | super().__init__(*args, **kwargs) 7 | self._custom_label = label 8 | 9 | def render_as_field(self): 10 | if hasattr(self, "_custom_label"): 11 | self.bound_field.label = self._custom_label 12 | return super().render_as_field() 13 | -------------------------------------------------------------------------------- /developerportal/apps/people/migrations/0004_auto_20190530_1441.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-05-30 14:41 2 | 3 | from django.db import migrations 4 | import modelcluster.fields 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [("people", "0003_auto_20190530_1437")] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name="person", 14 | name="labels", 15 | field=modelcluster.fields.ParentalManyToManyField( 16 | blank=True, 17 | related_name="_person_labels_+", 18 | to="topics.Topic", 19 | verbose_name="Topics interested in", 20 | ), 21 | ) 22 | ] 23 | -------------------------------------------------------------------------------- /developerportal/apps/people/migrations/0005_person_is_mozillian.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-05-31 08:24 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("people", "0004_auto_20190530_1441")] 9 | 10 | operations = [ 11 | migrations.AddField( 12 | model_name="person", 13 | name="is_mozillian", 14 | field=models.BooleanField(default=True), 15 | ) 16 | ] 17 | -------------------------------------------------------------------------------- /developerportal/apps/people/migrations/0006_auto_20190604_1050.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-04 10:50 2 | 3 | from django.db import migrations 4 | import modelcluster.fields 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [("topics", "0003_subtopic"), ("people", "0005_person_is_mozillian")] 10 | 11 | operations = [ 12 | migrations.RemoveField(model_name="person", name="labels"), 13 | migrations.AddField( 14 | model_name="person", 15 | name="topics", 16 | field=modelcluster.fields.ParentalManyToManyField( 17 | blank=True, 18 | related_name="_person_topics_+", 19 | to="topics.Topic", 20 | verbose_name="Topics interested in", 21 | ), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /developerportal/apps/people/migrations/0009_auto_20190626_0848.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-26 08:48 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("people", "0008_featuredperson")] 9 | 10 | operations = [ 11 | migrations.AlterModelOptions(name="person", options={"ordering": ["title"]}) 12 | ] 13 | -------------------------------------------------------------------------------- /developerportal/apps/people/migrations/0009_auto_20190626_1353.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-26 13:53 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("people", "0008_featuredperson")] 9 | 10 | operations = [ 11 | migrations.AlterModelOptions(name="person", options={"ordering": ["title"]}) 12 | ] 13 | -------------------------------------------------------------------------------- /developerportal/apps/people/migrations/0010_auto_20190625_1258.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-25 12:58 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("people", "0009_auto_20190625_1122")] 9 | 10 | operations = [ 11 | migrations.AlterModelOptions(name="person", options={"ordering": ["title"]}) 12 | ] 13 | -------------------------------------------------------------------------------- /developerportal/apps/people/migrations/0010_merge_20190627_1318.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-27 13:18 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("people", "0009_auto_20190626_0848"), 10 | ("people", "0009_auto_20190626_1353"), 11 | ] 12 | 13 | operations = [] 14 | -------------------------------------------------------------------------------- /developerportal/apps/people/migrations/0011_merge_20190627_1428.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-27 14:28 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("people", "0010_auto_20190625_1258"), 10 | ("people", "0009_auto_20190626_0848"), 11 | ] 12 | 13 | operations = [] 14 | -------------------------------------------------------------------------------- /developerportal/apps/people/migrations/0012_auto_20190702_1059.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-07-02 10:59 2 | 3 | from django.db import migrations 4 | import wagtail.core.fields 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [("people", "0011_merge_20190627_1428")] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name="person", 14 | name="intro", 15 | field=wagtail.core.fields.RichTextField(blank=True, default=""), 16 | ) 17 | ] 18 | -------------------------------------------------------------------------------- /developerportal/apps/people/migrations/0012_merge_20190628_1108.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-28 11:08 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("people", "0010_merge_20190627_1318"), 10 | ("people", "0011_merge_20190627_1428"), 11 | ] 12 | 13 | operations = [] 14 | -------------------------------------------------------------------------------- /developerportal/apps/people/migrations/0013_merge_20190702_1247.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-07-02 12:47 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("people", "0012_auto_20190702_1059"), 10 | ("people", "0012_merge_20190628_1108"), 11 | ] 12 | 13 | operations = [] 14 | -------------------------------------------------------------------------------- /developerportal/apps/people/migrations/0013_merge_20190702_1300.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-07-02 13:00 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("people", "0012_merge_20190628_1108"), 10 | ("people", "0012_auto_20190702_1059"), 11 | ] 12 | 13 | operations = [] 14 | -------------------------------------------------------------------------------- /developerportal/apps/people/migrations/0014_merge_20190702_1619.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-07-02 16:19 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("people", "0013_merge_20190702_1247"), 10 | ("people", "0013_merge_20190702_1300"), 11 | ] 12 | 13 | operations = [] 14 | -------------------------------------------------------------------------------- /developerportal/apps/people/migrations/0015_auto_20190709_0928.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.3 on 2019-07-09 09:28 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("people", "0014_merge_20190702_1619")] 9 | 10 | operations = [ 11 | migrations.AlterModelOptions( 12 | name="people", options={"verbose_name_plural": "People"} 13 | ) 14 | ] 15 | -------------------------------------------------------------------------------- /developerportal/apps/people/migrations/0018_people_keywords.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.3 on 2019-07-23 16:59 2 | 3 | from django.db import migrations 4 | import modelcluster.contrib.taggit 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ("taggit", "0002_auto_20150616_2121"), 11 | ("people", "0017_auto_20190718_1424"), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name="people", 17 | name="keywords", 18 | field=modelcluster.contrib.taggit.ClusterTaggableManager( 19 | blank=True, 20 | help_text="A comma-separated list of tags.", 21 | through="people.PeopleTag", 22 | to="taggit.Tag", 23 | verbose_name="Tags", 24 | ), 25 | ) 26 | ] 27 | -------------------------------------------------------------------------------- /developerportal/apps/people/migrations/0019_remove_people_keywords.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.3 on 2019-07-25 12:25 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("people", "0018_people_keywords")] 9 | 10 | operations = [migrations.RemoveField(model_name="people", name="keywords")] 11 | -------------------------------------------------------------------------------- /developerportal/apps/people/migrations/0020_people_keywords.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.3 on 2019-07-25 12:26 2 | 3 | from django.db import migrations 4 | import modelcluster.contrib.taggit 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ("taggit", "0002_auto_20150616_2121"), 11 | ("people", "0019_remove_people_keywords"), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name="people", 17 | name="keywords", 18 | field=modelcluster.contrib.taggit.ClusterTaggableManager( 19 | blank=True, 20 | help_text="A comma-separated list of tags.", 21 | through="people.PeopleTag", 22 | to="taggit.Tag", 23 | verbose_name="Tags", 24 | ), 25 | ) 26 | ] 27 | -------------------------------------------------------------------------------- /developerportal/apps/people/migrations/0022_people_description.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.3 on 2019-08-02 09:16 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("people", "0021_auto_20190731_1057")] 9 | 10 | operations = [ 11 | migrations.AddField( 12 | model_name="people", 13 | name="description", 14 | field=models.TextField( 15 | blank=True, default="", max_length=250, verbose_name="Description" 16 | ), 17 | ) 18 | ] 19 | -------------------------------------------------------------------------------- /developerportal/apps/people/migrations/0023_merge_20190802_1535.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.3 on 2019-08-02 15:35 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("people", "0022_person_websites"), 10 | ("people", "0022_people_description"), 11 | ] 12 | 13 | operations = [] 14 | -------------------------------------------------------------------------------- /developerportal/apps/people/migrations/0024_auto_20190813_1302.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-08-13 13:02 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("people", "0023_merge_20190802_1535")] 9 | 10 | operations = [ 11 | migrations.AlterField( 12 | model_name="person", 13 | name="card_description", 14 | field=models.TextField( 15 | blank=True, default="", max_length=400, verbose_name="Description" 16 | ), 17 | ) 18 | ] 19 | -------------------------------------------------------------------------------- /developerportal/apps/people/migrations/0026_auto_20190819_1304.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-08-19 13:04 2 | 3 | from django.db import migrations 4 | import wagtail.core.fields 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [("people", "0025_auto_20190813_1503")] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name="people", 14 | name="description", 15 | field=wagtail.core.fields.RichTextField( 16 | blank=True, 17 | default="", 18 | help_text="Optional short text description, max. 400 characters", 19 | max_length=400, 20 | ), 21 | ) 22 | ] 23 | -------------------------------------------------------------------------------- /developerportal/apps/people/migrations/0027_person_country.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-09-25 10:39 2 | 3 | from django.db import migrations 4 | import django_countries.fields 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('people', '0026_auto_20190819_1304'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='person', 16 | name='country', 17 | field=django_countries.fields.CountryField(blank=True, default='', max_length=2), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /developerportal/apps/people/migrations/0028_auto_20190926_1049.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-09-26 10:49 2 | 3 | from django.db import migrations, models 4 | import django_countries.fields 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('people', '0027_person_country'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='person', 16 | name='city', 17 | field=models.CharField(blank=True, default='', max_length=250), 18 | ), 19 | migrations.AlterField( 20 | model_name='person', 21 | name='country', 22 | field=django_countries.fields.CountryField(max_length=2, verbose_name='Country or Region'), 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /developerportal/apps/people/migrations/0029_update_person_manager.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-09-30 09:15 2 | 3 | from django.db import migrations 4 | import django.db.models.manager 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('people', '0028_auto_20190926_1049'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterModelManagers( 15 | name='people', 16 | managers=[ 17 | ('published_objects', django.db.models.manager.Manager()), 18 | ], 19 | ), 20 | migrations.AlterModelManagers( 21 | name='person', 22 | managers=[ 23 | ('published_objects', django.db.models.manager.Manager()), 24 | ], 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /developerportal/apps/people/migrations/0031_person_nickname.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.6 on 2019-10-08 11:40 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('people', '0030_add_social_image_field'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='person', 15 | name='nickname', 16 | field=models.CharField(blank=True, max_length=250, null=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /developerportal/apps/people/migrations/0032_auto_20191008_1530.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.6 on 2019-10-08 15:30 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('people', '0031_person_nickname'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='person', 15 | name='role', 16 | field=models.CharField(choices=[('staff', 'Staff'), ('tech-speaker', 'Tech Speaker'), ('community', 'Community')], default='staff', max_length=250), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /developerportal/apps/people/migrations/0033_people_icon.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.11 on 2020-03-15 15:23 2 | 3 | import developerportal.apps.common.validators 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('people', '0032_auto_20191008_1530'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='people', 16 | name='icon', 17 | field=models.FileField(blank=True, default='', help_text='MUST be a black-on-transparent SVG icon ONLY, with no bitmap embedded in it.', upload_to='people/icons', validators=[developerportal.apps.common.validators.check_for_svg_file]), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /developerportal/apps/people/migrations/0034_people_nav_description.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.11 on 2020-03-18 11:28 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('people', '0033_people_icon'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='people', 15 | name='nav_description', 16 | field=models.TextField(blank=True, default='', max_length=400, verbose_name='Navigation description'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /developerportal/apps/people/migrations/0035_copy_image_field_to_card_image_field.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.12 on 2020-05-07 13:52 2 | 3 | """Copy any image set as `Person.image` to populate the `Person.card_image` field 4 | instead, including updating the latest revision of the page (draft or live)""" 5 | 6 | from django.db import migrations 7 | 8 | from developerportal.apps.common.migration_utils import ( 9 | move_Page_image_to_Page_card_image, 10 | ) 11 | 12 | 13 | def forwards(apps, schema_editor): 14 | PageSubclass = apps.get_model("people", "Person") 15 | move_Page_image_to_Page_card_image(PageSubclass) 16 | 17 | 18 | class Migration(migrations.Migration): 19 | 20 | dependencies = [("people", "0034_people_nav_description")] 21 | 22 | operations = [migrations.RunPython(forwards, migrations.RunPython.noop)] 23 | -------------------------------------------------------------------------------- /developerportal/apps/people/migrations/0036_remove_person_image.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.12 on 2020-05-07 21:13 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('people', '0035_copy_image_field_to_card_image_field'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='person', 15 | name='image', 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /developerportal/apps/people/migrations/0037_auto_20200511_1148.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.12 on 2020-05-11 11:48 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('people', '0036_remove_person_image'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='person', 16 | name='card_image', 17 | field=models.ForeignKey(blank=True, help_text='An image in 16:9 aspect ratio', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='mozimages.MozImage', verbose_name='Image'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /developerportal/apps/people/migrations/0038_make_Person_country_optional.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.14 on 2020-07-06 11:15 2 | 3 | from django.db import migrations 4 | import django_countries.fields 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('people', '0037_auto_20200511_1148'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='person', 16 | name='country', 17 | field=django_countries.fields.CountryField(blank=True, default='', max_length=2, verbose_name='Country or Region'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /developerportal/apps/people/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/apps/people/migrations/__init__.py -------------------------------------------------------------------------------- /developerportal/apps/people/templates/people.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load static %} 3 | {% load wagtailcore_tags %} 4 | {% load wagtailimages_tags %} 5 | 6 | {% block body_class %}people{% endblock %} 7 | 8 | {% block content %} 9 | 10 | {% static "img/icons/people.svg" as page_icon_asset_url %} 11 | {% include "molecules/header-strip.html" with content=page.title element="h1" page_icon_asset_url=page_icon_asset_url %} 12 | 13 |
14 |
15 | {% include "organisms/filter-list.html" with type="person" resources=people no_resources_message="No people found" %} 16 |
17 |
18 | {% include "organisms/newsletter-signup.html" %} 19 | {% endblock content %} 20 | -------------------------------------------------------------------------------- /developerportal/apps/people/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/apps/people/tests/__init__.py -------------------------------------------------------------------------------- /developerportal/apps/people/wagtail_hooks.py: -------------------------------------------------------------------------------- 1 | from wagtail.contrib.modeladmin.options import ModelAdmin, modeladmin_register 2 | 3 | from ..common.helpers import ExplorerRedirectAdminURLHelper 4 | from .models import People 5 | 6 | 7 | class PeopleAdmin(ModelAdmin): 8 | model = People 9 | menu_icon = "group" 10 | menu_order = 230 11 | url_helper_class = ExplorerRedirectAdminURLHelper 12 | 13 | 14 | modeladmin_register(PeopleAdmin) 15 | -------------------------------------------------------------------------------- /developerportal/apps/search/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/apps/search/__init__.py -------------------------------------------------------------------------------- /developerportal/apps/search/models.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/apps/search/models.py -------------------------------------------------------------------------------- /developerportal/apps/search/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/apps/search/tests/__init__.py -------------------------------------------------------------------------------- /developerportal/apps/search/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | 3 | from . import views 4 | 5 | urlpatterns = [url(r"^$", views.site_search, name="search.site_search")] 6 | -------------------------------------------------------------------------------- /developerportal/apps/taskqueue/__init__.py: -------------------------------------------------------------------------------- 1 | from .celery import app as celery_app 2 | 3 | # Make sure Celery is always imported when Django starts 4 | # so that @shared_task will use this app. 5 | __all__ = ("celery_app",) 6 | -------------------------------------------------------------------------------- /developerportal/apps/taskqueue/models.py: -------------------------------------------------------------------------------- 1 | # empty models.py to allow Django to accept the `taskqueue` app 2 | -------------------------------------------------------------------------------- /developerportal/apps/taskqueue/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/apps/taskqueue/tests/__init__.py -------------------------------------------------------------------------------- /developerportal/apps/taskqueue/tests/test_wagtail_hooks.py: -------------------------------------------------------------------------------- 1 | from unittest import mock 2 | 3 | from django.test import TestCase 4 | 5 | from wagtail.core.models import Page 6 | from wagtail.core.signals import page_published, page_unpublished 7 | 8 | 9 | class SignalsTestCase(TestCase): 10 | @mock.patch( 11 | "developerportal.apps.taskqueue.wagtail_hooks.invalidate_entire_cdn.delay" 12 | ) 13 | def test_cdn_invalidation_signal_handler(self, mock_invalidate_entire_cdn__delay): 14 | 15 | cases = [{"signal": page_published}, {"signal": page_unpublished}] 16 | 17 | for case in cases: 18 | mock_invalidate_entire_cdn__delay.reset_mock() 19 | with self.subTest(case=case): 20 | case["signal"].send(sender=Page, instance=None) 21 | mock_invalidate_entire_cdn__delay.assert_called_once_with() 22 | -------------------------------------------------------------------------------- /developerportal/apps/taskqueue/wagtail_hooks.py: -------------------------------------------------------------------------------- 1 | from wagtail.core.signals import page_published, page_unpublished 2 | 3 | from .tasks import invalidate_entire_cdn 4 | 5 | 6 | def purge_cdn_on_publish(signal, **kwargs): 7 | # TODO: refine this to be more selective and only invalidate the 8 | # keys we know we need to. At the moment we're erring on the side 9 | # of caution. 10 | invalidate_entire_cdn.delay() 11 | 12 | 13 | page_published.connect(purge_cdn_on_publish) 14 | page_unpublished.connect(purge_cdn_on_publish) 15 | -------------------------------------------------------------------------------- /developerportal/apps/topics/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/apps/topics/__init__.py -------------------------------------------------------------------------------- /developerportal/apps/topics/migrations/0005_auto_20190610_1413.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-10 14:13 2 | 3 | from django.db import migrations 4 | import wagtail.core.fields 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [("topics", "0004_topicfeaturedarticle")] 10 | 11 | operations = [ 12 | migrations.AlterModelOptions( 13 | name="subtopic", 14 | options={"verbose_name": "Sub-topic", "verbose_name_plural": "Sub-topics"}, 15 | ), 16 | migrations.AddField( 17 | model_name="topic", 18 | name="intro", 19 | field=wagtail.core.fields.RichTextField(default=""), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /developerportal/apps/topics/migrations/0007_auto_20190611_1542.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-11 15:42 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("topics", "0006_auto_20190611_1453")] 9 | 10 | operations = [ 11 | migrations.RenameField( 12 | model_name="topicperson", old_name="author", new_name="person" 13 | ) 14 | ] 15 | -------------------------------------------------------------------------------- /developerportal/apps/topics/migrations/0008_auto_20190612_1759.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-12 17:59 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("topics", "0007_auto_20190611_1542")] 9 | 10 | operations = [ 11 | migrations.AlterField( 12 | model_name="topic", 13 | name="intro", 14 | field=models.CharField(blank=True, default="", max_length=250), 15 | ) 16 | ] 17 | -------------------------------------------------------------------------------- /developerportal/apps/topics/migrations/0009_topic_color.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-12 18:14 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("topics", "0008_auto_20190612_1759")] 9 | 10 | operations = [ 11 | migrations.AddField( 12 | model_name="topic", 13 | name="color", 14 | field=models.CharField( 15 | choices=[ 16 | ("blue", "Blue"), 17 | ("orange", "Orange"), 18 | ("green", "Green"), 19 | ("red", "Red"), 20 | ], 21 | default="blue", 22 | max_length=14, 23 | ), 24 | ) 25 | ] 26 | -------------------------------------------------------------------------------- /developerportal/apps/topics/migrations/0010_auto_20190618_0840.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-18 08:40 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("topics", "0009_topic_color")] 9 | 10 | operations = [ 11 | migrations.AlterField( 12 | model_name="topic", 13 | name="intro", 14 | field=models.TextField(blank=True, default="", max_length=250), 15 | ) 16 | ] 17 | -------------------------------------------------------------------------------- /developerportal/apps/topics/migrations/0013_merge_20190618_1635.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-18 16:35 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("topics", "0010_auto_20190618_0840"), 10 | ("topics", "0012_auto_20190614_1319"), 11 | ] 12 | 13 | operations = [] 14 | -------------------------------------------------------------------------------- /developerportal/apps/topics/migrations/0013_merge_20190619_1357.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-19 13:57 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("topics", "0010_auto_20190618_0840"), 10 | ("topics", "0012_auto_20190614_1319"), 11 | ] 12 | 13 | operations = [] 14 | -------------------------------------------------------------------------------- /developerportal/apps/topics/migrations/0013_topic_icon.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-18 10:27 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("topics", "0012_auto_20190614_1319")] 9 | 10 | operations = [ 11 | migrations.AddField( 12 | model_name="topic", 13 | name="icon", 14 | field=models.FileField(default="", upload_to="topics/icons"), 15 | ) 16 | ] 17 | -------------------------------------------------------------------------------- /developerportal/apps/topics/migrations/0014_merge_20190618_1609.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-18 16:09 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("topics", "0010_auto_20190618_0840"), 10 | ("topics", "0013_topic_icon"), 11 | ] 12 | 13 | operations = [] 14 | -------------------------------------------------------------------------------- /developerportal/apps/topics/migrations/0015_merge_20190619_1437.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-19 14:37 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("topics", "0013_merge_20190618_1635"), 10 | ("topics", "0014_merge_20190618_1609"), 11 | ] 12 | 13 | operations = [] 14 | -------------------------------------------------------------------------------- /developerportal/apps/topics/migrations/0015_merge_20190619_1506.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-19 15:06 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("topics", "0014_merge_20190618_1609"), 10 | ("topics", "0013_merge_20190618_1635"), 11 | ] 12 | 13 | operations = [] 14 | -------------------------------------------------------------------------------- /developerportal/apps/topics/migrations/0015_merge_20190621_1009.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-21 10:09 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("topics", "0014_merge_20190618_1609"), 10 | ("topics", "0013_merge_20190619_1357"), 11 | ("topics", "0013_merge_20190618_1635"), 12 | ] 13 | 14 | operations = [] 15 | -------------------------------------------------------------------------------- /developerportal/apps/topics/migrations/0016_auto_20190619_1437.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-19 14:37 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("topics", "0015_merge_20190619_1437")] 9 | 10 | operations = [ 11 | migrations.AlterField( 12 | model_name="topic", 13 | name="icon", 14 | field=models.FileField(blank=True, default="", upload_to="topics/icons"), 15 | ) 16 | ] 17 | -------------------------------------------------------------------------------- /developerportal/apps/topics/migrations/0016_auto_20190619_1506.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-19 15:06 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("topics", "0015_merge_20190619_1506")] 9 | 10 | operations = [ 11 | migrations.AlterField( 12 | model_name="topic", 13 | name="icon", 14 | field=models.FileField(blank=True, default="", upload_to="topics/icons"), 15 | ) 16 | ] 17 | -------------------------------------------------------------------------------- /developerportal/apps/topics/migrations/0016_auto_20190624_1129.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-24 11:29 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("topics", "0015_merge_20190621_1009")] 9 | 10 | operations = [ 11 | migrations.AlterField( 12 | model_name="topic", 13 | name="icon", 14 | field=models.FileField(blank=True, default="", upload_to="topics/icons"), 15 | ) 16 | ] 17 | -------------------------------------------------------------------------------- /developerportal/apps/topics/migrations/0017_merge_20190621_1033.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-21 10:33 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("topics", "0015_merge_20190621_1009"), 10 | ("topics", "0016_auto_20190619_1506"), 11 | ] 12 | 13 | operations = [] 14 | -------------------------------------------------------------------------------- /developerportal/apps/topics/migrations/0017_merge_20190624_0904.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-24 09:04 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("topics", "0016_auto_20190619_1437"), 10 | ("topics", "0015_merge_20190621_1009"), 11 | ] 12 | 13 | operations = [] 14 | -------------------------------------------------------------------------------- /developerportal/apps/topics/migrations/0018_merge_20190624_1248.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-24 12:48 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("topics", "0017_merge_20190624_0904"), 10 | ("topics", "0017_merge_20190621_1033"), 11 | ] 12 | 13 | operations = [] 14 | -------------------------------------------------------------------------------- /developerportal/apps/topics/migrations/0018_merge_20190624_1252.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-24 12:52 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("topics", "0016_auto_20190624_1129"), 10 | ("topics", "0017_merge_20190624_0904"), 11 | ] 12 | 13 | operations = [] 14 | -------------------------------------------------------------------------------- /developerportal/apps/topics/migrations/0019_merge_20190625_1257.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-06-25 12:57 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("topics", "0018_merge_20190624_1252"), 10 | ("topics", "0018_merge_20190624_1248"), 11 | ] 12 | 13 | operations = [] 14 | -------------------------------------------------------------------------------- /developerportal/apps/topics/migrations/0024_merge_20190702_1619.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-07-02 16:19 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("topics", "0023_auto_20190702_1318"), 10 | ("topics", "0020_topic_featured"), 11 | ] 12 | 13 | operations = [] 14 | -------------------------------------------------------------------------------- /developerportal/apps/topics/migrations/0026_merge_20190704_1459.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.3 on 2019-07-04 14:59 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("topics", "0025_auto_20190702_1620"), 10 | ("topics", "0021_auto_20190702_1402"), 11 | ] 12 | 13 | operations = [] 14 | -------------------------------------------------------------------------------- /developerportal/apps/topics/migrations/0027_auto_20190709_0928.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.3 on 2019-07-09 09:28 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("topics", "0026_merge_20190704_1459")] 9 | 10 | operations = [ 11 | migrations.AlterModelOptions( 12 | name="topics", options={"verbose_name_plural": "Topics"} 13 | ) 14 | ] 15 | -------------------------------------------------------------------------------- /developerportal/apps/topics/migrations/0028_merge_20190710_1633.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.3 on 2019-07-10 16:33 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ("topics", "0027_auto_20190710_1339"), 10 | ("topics", "0027_auto_20190709_0928"), 11 | ] 12 | 13 | operations = [] 14 | -------------------------------------------------------------------------------- /developerportal/apps/topics/migrations/0033_auto_20190718_1452.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.3 on 2019-07-18 14:52 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [("topics", "0031_auto_20190718_1424")] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name="topicfeaturedarticle", 14 | name="article", 15 | field=models.ForeignKey( 16 | on_delete=django.db.models.deletion.CASCADE, 17 | related_name="+", 18 | to="wagtailcore.Page", 19 | ), 20 | ) 21 | ] 22 | -------------------------------------------------------------------------------- /developerportal/apps/topics/migrations/0038_topic_latest_articles_count.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.3 on 2019-08-01 15:29 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("topics", "0037_auto_20190730_1057")] 9 | 10 | operations = [ 11 | migrations.AddField( 12 | model_name="topic", 13 | name="latest_articles_count", 14 | field=models.IntegerField( 15 | choices=[(3, "3"), (6, "6"), (9, "9")], default=3 16 | ), 17 | ) 18 | ] 19 | -------------------------------------------------------------------------------- /developerportal/apps/topics/migrations/0039_auto_20190805_0806.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.3 on 2019-08-05 08:06 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("topics", "0038_topic_latest_articles_count")] 9 | 10 | operations = [ 11 | migrations.AlterField( 12 | model_name="topic", 13 | name="latest_articles_count", 14 | field=models.IntegerField( 15 | choices=[(3, "3"), (6, "6"), (9, "9")], 16 | default=3, 17 | help_text="The number of articles to display for this topic.", 18 | ), 19 | ) 20 | ] 21 | -------------------------------------------------------------------------------- /developerportal/apps/topics/migrations/0040_auto_20190813_1302.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-08-13 13:02 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("topics", "0039_auto_20190805_0806")] 9 | 10 | operations = [ 11 | migrations.AlterField( 12 | model_name="topic", 13 | name="card_description", 14 | field=models.TextField( 15 | blank=True, default="", max_length=400, verbose_name="Description" 16 | ), 17 | ), 18 | migrations.AlterField( 19 | model_name="topic", 20 | name="description", 21 | field=models.TextField(blank=True, default="", max_length=400), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /developerportal/apps/topics/migrations/0043_auto_20190819_1304.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-08-19 13:04 2 | 3 | from django.db import migrations 4 | import wagtail.core.fields 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [("topics", "0042_auto_20190814_1600")] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name="topic", 14 | name="description", 15 | field=wagtail.core.fields.RichTextField( 16 | blank=True, 17 | default="", 18 | help_text="Optional short text description, max. 400 characters", 19 | max_length=400, 20 | ), 21 | ) 22 | ] 23 | -------------------------------------------------------------------------------- /developerportal/apps/topics/migrations/0044_update_topic_manager.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-09-30 09:15 2 | 3 | from django.db import migrations 4 | import django.db.models.manager 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('topics', '0043_auto_20190819_1304'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterModelManagers( 15 | name='topic', 16 | managers=[ 17 | ('published_objects', django.db.models.manager.Manager()), 18 | ], 19 | ), 20 | migrations.AlterModelManagers( 21 | name='topics', 22 | managers=[ 23 | ('published_objects', django.db.models.manager.Manager()), 24 | ], 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /developerportal/apps/topics/migrations/0046_remove_topic_tabbed_panels_title.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.6 on 2019-10-09 16:25 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('topics', '0045_add_social_image_field'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='topic', 15 | name='tabbed_panels_title', 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /developerportal/apps/topics/migrations/0049_topic_nav_description.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.11 on 2020-03-18 11:28 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('topics', '0048_add_svg_help_text_to_topic_icon_filefield'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='topic', 15 | name='nav_description', 16 | field=models.TextField(blank=True, default='', max_length=400, verbose_name='Navigation description'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /developerportal/apps/topics/migrations/0050_remove_topic_latest_articles_count.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.11 on 2020-03-23 17:57 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('topics', '0049_topic_nav_description'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='topic', 15 | name='latest_articles_count', 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /developerportal/apps/topics/migrations/0052_auto_20200511_1148.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.12 on 2020-05-11 11:48 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('topics', '0051_remove_tabbed_panel_and_add_recent_work_streamfield'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='topic', 16 | name='card_image', 17 | field=models.ForeignKey(blank=True, help_text='An image in 16:9 aspect ratio', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='mozimages.MozImage', verbose_name='Image'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /developerportal/apps/topics/migrations/0054_remove_topic_color.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.12 on 2020-05-27 11:00 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('topics', '0053_update_streamblock'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='topic', 15 | name='color', 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /developerportal/apps/topics/migrations/0055_topic_relevant_events.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.14 on 2020-07-03 13:05 2 | 3 | from django.db import migrations 4 | import wagtail.core.blocks 5 | import wagtail.core.fields 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('topics', '0054_remove_topic_color'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='topic', 17 | name='relevant_events', 18 | field=wagtail.core.fields.StreamField([('event', wagtail.core.blocks.PageChooserBlock(page_type=['events.Event', 'externalcontent.ExternalEvent']))], blank=True, help_text='Optional space for featured Events, max. 4.', null=True), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /developerportal/apps/topics/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/apps/topics/migrations/__init__.py -------------------------------------------------------------------------------- /developerportal/apps/topics/templates/topics.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load static %} 3 | {% load app_filters %} 4 | 5 | {% block body_class %}topics{% endblock %} 6 | 7 | {% block content %} 8 | {% static "img/icons/article-white.svg" as page_icon_asset_url %} 9 | {% include "molecules/header-strip.html" with content=page.title element="h1" page_icon_asset_url=page_icon_asset_url %} 10 | 11 |
12 | {% with page.topics.public.live.specific as topics %} 13 | {% if topics %} 14 | {% include "organisms/topic-links.html" with topics=topics pagetheme='directory' %} 15 | {% endif %} 16 | {% endwith %} 17 |
18 | {% endblock content %} 19 | -------------------------------------------------------------------------------- /developerportal/apps/topics/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/apps/topics/tests/__init__.py -------------------------------------------------------------------------------- /developerportal/apps/topics/wagtail_hooks.py: -------------------------------------------------------------------------------- 1 | from wagtail.contrib.modeladmin.options import ModelAdmin, modeladmin_register 2 | 3 | from ..common.helpers import ExplorerRedirectAdminURLHelper 4 | from .models import Topics 5 | 6 | 7 | class TopicsAdmin(ModelAdmin): 8 | model = Topics 9 | menu_icon = "tag" 10 | menu_order = 200 11 | url_helper_class = ExplorerRedirectAdminURLHelper 12 | 13 | 14 | modeladmin_register(TopicsAdmin) 15 | -------------------------------------------------------------------------------- /developerportal/apps/videos/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/apps/videos/__init__.py -------------------------------------------------------------------------------- /developerportal/apps/videos/migrations/0004_auto_20190813_1302.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-08-13 13:02 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [("videos", "0003_auto_20190807_1244")] 9 | 10 | operations = [ 11 | migrations.AlterField( 12 | model_name="video", 13 | name="card_description", 14 | field=models.TextField( 15 | blank=True, default="", max_length=400, verbose_name="Description" 16 | ), 17 | ), 18 | migrations.AlterField( 19 | model_name="video", 20 | name="description", 21 | field=models.TextField(blank=True, default="", max_length=400), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /developerportal/apps/videos/migrations/0007_auto_20190819_1304.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-08-19 13:04 2 | 3 | from django.db import migrations 4 | import wagtail.core.fields 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [("videos", "0006_auto_20190814_1600")] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name="video", 14 | name="description", 15 | field=wagtail.core.fields.RichTextField( 16 | blank=True, 17 | default="", 18 | help_text="Optional short text description, max. 400 characters", 19 | max_length=400, 20 | ), 21 | ) 22 | ] 23 | -------------------------------------------------------------------------------- /developerportal/apps/videos/migrations/0008_update_video_manager.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.4 on 2019-09-30 09:15 2 | 3 | from django.db import migrations 4 | import django.db.models.manager 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('videos', '0007_auto_20190819_1304'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterModelManagers( 15 | name='video', 16 | managers=[ 17 | ('published_objects', django.db.models.manager.Manager()), 18 | ], 19 | ), 20 | migrations.AlterModelManagers( 21 | name='videos', 22 | managers=[ 23 | ('published_objects', django.db.models.manager.Manager()), 24 | ], 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /developerportal/apps/videos/migrations/0011_copy_image_to_card_image.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.12 on 2020-05-07 12:49 2 | 3 | """Copy any image set as `Video.image` to populate the `Video.card_image` field 4 | instead, including updating the latest revision of the page (draft or live)""" 5 | 6 | from django.db import migrations 7 | 8 | from developerportal.apps.common.migration_utils import ( 9 | move_Page_image_to_Page_card_image, 10 | ) 11 | 12 | 13 | def forwards(apps, schema_editor): 14 | PageSubclass = apps.get_model("videos", "Video") 15 | move_Page_image_to_Page_card_image(PageSubclass) 16 | 17 | 18 | class Migration(migrations.Migration): 19 | 20 | dependencies = [("videos", "0010_rename_related_links_without_data_port")] 21 | 22 | operations = [migrations.RunPython(forwards, migrations.RunPython.noop)] 23 | -------------------------------------------------------------------------------- /developerportal/apps/videos/migrations/0012_remove_video_image.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.12 on 2020-05-07 21:13 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('videos', '0011_copy_image_to_card_image'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='video', 15 | name='image', 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /developerportal/apps/videos/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/apps/videos/migrations/__init__.py -------------------------------------------------------------------------------- /developerportal/apps/videos/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/apps/videos/tests/__init__.py -------------------------------------------------------------------------------- /developerportal/apps/videos/tests/test_videos.py: -------------------------------------------------------------------------------- 1 | from developerportal.apps.common.test_helpers import PatchedWagtailPageTests 2 | 3 | from ...home.models import HomePage 4 | from ..models import Video, Videos 5 | 6 | 7 | class VideoTests(PatchedWagtailPageTests): 8 | """Tests for the Video model.""" 9 | 10 | def test_video_parent_pages(self): 11 | self.assertAllowedParentPageTypes(Video, {Videos}) 12 | 13 | def test_video_subpages(self): 14 | self.assertAllowedSubpageTypes(Video, {}) 15 | 16 | 17 | class VideosTests(PatchedWagtailPageTests): 18 | """Tests for the Videos model.""" 19 | 20 | def test_videos_parent_pages(self): 21 | self.assertAllowedParentPageTypes(Videos, {HomePage}) 22 | 23 | def test_videos_subpages(self): 24 | self.assertAllowedSubpageTypes(Videos, {Video}) 25 | -------------------------------------------------------------------------------- /developerportal/apps/videos/wagtail_hooks.py: -------------------------------------------------------------------------------- 1 | from wagtail.contrib.modeladmin.options import ModelAdmin, modeladmin_register 2 | 3 | from ..common.helpers import ExplorerRedirectAdminURLHelper 4 | from .models import Videos 5 | 6 | 7 | class VideosAdmin(ModelAdmin): 8 | model = Videos 9 | menu_icon = "media" 10 | menu_order = 240 11 | url_helper_class = ExplorerRedirectAdminURLHelper 12 | 13 | 14 | modeladmin_register(VideosAdmin) 15 | -------------------------------------------------------------------------------- /developerportal/regex.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | # Spot a REDIS_URL with a DB number associated with it 4 | REDIS_DB_URL_PATTERN = re.compile(r"redis\:\/\/.*\d{4,5}(\/\d{1,2})") 5 | -------------------------------------------------------------------------------- /developerportal/settings/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/developerportal/settings/__init__.py -------------------------------------------------------------------------------- /developerportal/settings/test.py: -------------------------------------------------------------------------------- 1 | from .production import * 2 | 3 | CACHES["default"]["KEY_PREFIX"] = "test" 4 | 5 | DEFAULT_FILE_STORAGE = "django.core.files.storage.FileSystemStorage" 6 | MEDIA_ROOT = "/tmp/test-media/" 7 | 8 | AWS_CLOUDFRONT_DISTRIBUTION_ID = None 9 | -------------------------------------------------------------------------------- /developerportal/settings/test_fast.py: -------------------------------------------------------------------------------- 1 | from .test import * 2 | 3 | DATABASES = {"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}} 4 | -------------------------------------------------------------------------------- /developerportal/settings/worker.py: -------------------------------------------------------------------------------- 1 | # Extra settings that ONLY apply to the Celery workers/schedulers 2 | 3 | from .production import * 4 | -------------------------------------------------------------------------------- /developerportal/templates/atoms/icons/close.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /developerportal/templates/atoms/icons/facebook.svg: -------------------------------------------------------------------------------- 1 | {% load app_tags %} 2 | 3 | {% random_hash as hash %} 4 | Facebook 5 | -------------------------------------------------------------------------------- /developerportal/templates/atoms/icons/location.svg: -------------------------------------------------------------------------------- 1 | {% load app_tags %} 2 | 3 | {% random_hash as hash %} 4 | 5 | -------------------------------------------------------------------------------- /developerportal/templates/atoms/icons/person.svg: -------------------------------------------------------------------------------- 1 | {% load app_tags %} 2 | 3 | {% random_hash as hash %} 4 | 5 | -------------------------------------------------------------------------------- /developerportal/templates/atoms/icons/video.svg: -------------------------------------------------------------------------------- 1 | {% load app_tags %} 2 | 3 | {% random_hash as hash %} 4 | Video 5 | -------------------------------------------------------------------------------- /developerportal/templates/atoms/image.html: -------------------------------------------------------------------------------- 1 |
2 | {{ rendition.alt }} 8 | {% if image.caption %} 9 |
10 | {{ image.caption }} 11 |
12 | {% endif %} 13 |
14 | -------------------------------------------------------------------------------- /developerportal/templates/atoms/label.html: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | 3 | Small element used to label other things 4 | 5 | Args: 6 | - content: plaintext string 7 | - extra_css_classes (optional): string of extra CSS classes, separated by spaces 8 | 9 | {% endcomment %} 10 | 11 | {{content}} 12 | -------------------------------------------------------------------------------- /developerportal/templates/molecules/accessibility-nav.html: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /developerportal/templates/molecules/cards/card.html: -------------------------------------------------------------------------------- 1 | {% if resource.resource_type == "article" %} 2 | {% include "molecules/cards/card-article.html" with resource=resource node_type=node_type show_author=show_author card_number=card_number %} 3 | {% elif resource.resource_type == "event" %} 4 | {% include "molecules/cards/card-event.html" with resource=resource node_type=node_type %} 5 | {% elif resource.resource_type == "person" %} 6 | {% include "molecules/cards/card-person.html" with person=resource node_type=node_type %} 7 | {% elif resource.resource_type == "video" %} 8 | {% include "molecules/cards/card-video.html" with resource=resource node_type=node_type %} 9 | {% endif %} 10 | -------------------------------------------------------------------------------- /developerportal/templates/molecules/image-block.html: -------------------------------------------------------------------------------- 1 | {% load wagtailimages_tags %} 2 | {% load app_tags %} 3 | 4 | {% mime_type block.value.file.name as image_mime_type %} 5 | 6 | {% if image_mime_type == 'image/gif' %} 7 | {% render_gif block.value %} 8 | {% else %} 9 | {% image block.value width-800 as rendition %} 10 | {% include "atoms/image.html" with image=block.value rendition=rendition %} 11 | {% endif %} 12 | -------------------------------------------------------------------------------- /developerportal/templates/molecules/site-search-form.html: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | Component for displaying site-search input. 3 | 4 | Optional params: 5 | - hide_label 6 | - placeholder_text 7 | - search_input_id 8 | 9 | 10 | {% endcomment %} 11 | 12 | 13 |
14 | 17 | 22 | 23 |
24 | -------------------------------------------------------------------------------- /developerportal/templates/organisms/article-read-more.html: -------------------------------------------------------------------------------- 1 | {% load wagtailcore_tags %} 2 | 3 | {% if page.related_resources %} 4 |
5 |
6 |
7 |

More posts on this topic 8 | 9 | See all 10 | 11 |

12 |
13 | 14 |
15 | {% for page in page.related_resources|slice:":3" %} 16 |
17 | {% include "molecules/card-featured.html" with resource=page block_type=page.block_type %} 18 |
19 | {% endfor %} 20 |
21 |
22 |
23 | {% endif %} 24 | -------------------------------------------------------------------------------- /developerportal/templates/organisms/featured-events--four-up.html: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /developerportal/templates/organisms/homepage-about.html: -------------------------------------------------------------------------------- 1 | {% load wagtailcore_tags %} 2 | {% load wagtailimages_tags %} 3 | 4 |
5 |
6 |
7 |
8 |

{{ title }}

9 | {% if subtitle %} 10 |
{{ subtitle }}
11 | {% endif %} 12 |
13 |
14 | {% if button_url and button_text %} 15 |
16 |
17 | {{ button_text }} 18 |
19 |
20 | {% endif %} 21 |
22 |
23 | -------------------------------------------------------------------------------- /developerportal/templates/organisms/homepage-header.html: -------------------------------------------------------------------------------- 1 | {% load wagtailcore_tags %} 2 | {% load wagtailimages_tags %} 3 | {% load static %} 4 | 5 | 6 |
7 |
8 |

{{page.title}}

9 | {% if page.subtitle %} 10 |

{{page.subtitle}}

11 | {% endif %} 12 |
13 |
14 | -------------------------------------------------------------------------------- /developerportal/templates/organisms/partials/featured-card-selector.html: -------------------------------------------------------------------------------- 1 | {% if block.block_type == 'post' or block.block_type == 'event' or block.block_type == 'video' or block.block_type == 'content_page' %} 2 | {% with block.value as page %} 3 | {% include "molecules/card-featured.html" with resource=page.specific block_type=block.block_type %} 4 | {% endwith %} 5 | {% elif block.block_type == 'external_page' %} 6 | {% with block.value as external_page %} 7 | {% include "molecules/card-featured.html" with resource=external_page external_page=True block_type=block.block_type %} 8 | {% endwith %} 9 | {% endif %} 10 | -------------------------------------------------------------------------------- /developerportal/templates/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: /admin/ 3 | Disallow: /django-admin/ 4 | Disallow: /static/ 5 | -------------------------------------------------------------------------------- /developerportal/templates/wagtailadmin/404.html: -------------------------------------------------------------------------------- 1 | {% extends "wagtailadmin/404.html" %} 2 | {% load static %} 3 | 4 | {% block branding_logo %} 5 | {{ site_name }} 6 | {% endblock %} 7 | -------------------------------------------------------------------------------- /developerportal/templates/wagtailadmin/admin_base.html: -------------------------------------------------------------------------------- 1 | {% extends "wagtailadmin/admin_base.html" %} 2 | {% load static app_tags %} 3 | 4 | {% block branding_favicon %} 5 | {% get_favicon_path as favicon_path %} 6 | 7 | {% endblock %} 8 | -------------------------------------------------------------------------------- /developerportal/templates/wagtailadmin/home.html: -------------------------------------------------------------------------------- 1 | {% extends "wagtailadmin/home.html" %} 2 | 3 | {% block branding_welcome %}{{ site_name }}{% endblock %} 4 | -------------------------------------------------------------------------------- /developerportal/templates/wagtailadmin/login.html: -------------------------------------------------------------------------------- 1 | {% extends "wagtailadmin/login.html" %} 2 | {% load i18n app_tags %} 3 | 4 | {% block login_form %} 5 | 6 | {% use_conventional_auth as conventional_auth_enabled %} 7 | 8 | {% if conventional_auth_enabled %} 9 | 10 | {{ block.super }} 11 | 12 | {% else %} 13 | 14 |

Admin

15 | 16 | 17 | 18 | {% trans 'Sign in with Mozilla SSO' %} 19 | 20 | 21 |

If you lack SSO access, please ask your contact within Mozilla.

22 | {% endif %} 23 | {% endblock %} 24 | 25 | -------------------------------------------------------------------------------- /developerportal/templatetags/survey_tags.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from django import template 4 | from django.conf import settings 5 | 6 | logger = logging.getLogger(__name__) 7 | register = template.Library() 8 | 9 | 10 | @register.inclusion_tag("survey.html") 11 | def survey_prompt(): 12 | 13 | survey_url = settings.TASK_COMPLETION_SURVEY_URL 14 | # If this is "undefined" in config, treat it as if absent 15 | if survey_url == "undefined": 16 | survey_url = None 17 | 18 | survey_percentage = settings.TASK_COMPLETION_SURVEY_PERCENTAGE 19 | return {"survey_url": survey_url, "survey_percentage": survey_percentage} 20 | -------------------------------------------------------------------------------- /developerportal/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for developerportal project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.1/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | # Load dev settings by default. To override this, set either: 15 | # - DJANGO_ENV to e.g. production, or; 16 | # - DJANGO_SETTINGS_MODULE to e.g. developerportal.settings.production (takes precedence). # noqa 17 | env = os.environ.setdefault("DJANGO_ENV", "dev") 18 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", f"developerportal.settings.{env}") 19 | 20 | application = get_wsgi_application() 21 | -------------------------------------------------------------------------------- /etc/nginx/ssl/README.md: -------------------------------------------------------------------------------- 1 | # SSL support for the local build 2 | 3 | This folder will contain self-signed certificates for SSL support in local development. 4 | 5 | They are generated/updated each time the local Docker stack is upped using `etc/nginx/ssl/gen-cert.sh` 6 | 7 | See `services:cfssl:entrypoint` in `docker-compose.yml` for where this is hooked in. 8 | -------------------------------------------------------------------------------- /etc/nginx/ssl/ca-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "signing": { 3 | "default": { 4 | "expiry": "168h" 5 | }, 6 | "profiles": { 7 | "server": { 8 | "expiry": "8760h", 9 | "usages": [ 10 | "signing", 11 | "key encipherment", 12 | "server auth" 13 | ] 14 | }, 15 | "client": { 16 | "expiry": "8760h", 17 | "usages": [ 18 | "signing", 19 | "key encipherment", 20 | "client auth" 21 | ] 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /etc/nginx/ssl/cfssl.json: -------------------------------------------------------------------------------- 1 | { 2 | "CN": "developer-portal-127-0-0-1.nip.io", 3 | "hosts": [ 4 | "developer-portal-127-0-0-1.nip.io" 5 | ], 6 | "key": { 7 | "algo": "rsa", 8 | "size": 2048 9 | }, 10 | "names": [ 11 | { 12 | "O": "Mozilla", 13 | "OU": "Developer Portal development", 14 | "C": "US", 15 | "ST": "CA", 16 | "L": "San Francisco" 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /etc/nginx/ssl/gen-cert.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | SSL_DIR=$(dirname "$0") 4 | BASENAME="developer-portal-127-0-0-1.nip.io" 5 | SSL_KEY="${SSL_DIR}/${BASENAME}-key.pem" 6 | SSL_CRT="${SSL_DIR}/${BASENAME}.pem" 7 | 8 | 9 | if [ ! -f "${SSL_KEY}" ] || [ ! -f "${SSL_CRT}" ]; then 10 | cfssl gencert -initca ./cfssl.json | cfssljson -bare ca - 11 | cfssl gencert -ca=./ca.pem -ca-key=./ca-key.pem -config=./ca-config.json -profile=server ./cfssl.json | cfssljson -bare "${BASENAME}" 12 | else 13 | echo "${SSL_KEY} and ${SSL_CRT} already exists." 14 | fi 15 | -------------------------------------------------------------------------------- /k8s/db-migration-job.yaml.j2: -------------------------------------------------------------------------------- 1 | kind: Job 2 | apiVersion: batch/v1 3 | metadata: 4 | name: db-migration 5 | spec: 6 | template: 7 | metadata: 8 | name: db-migration 9 | spec: 10 | restartPolicy: Never 11 | containers: 12 | - name: db-migration 13 | image: {{ APP_IMAGE }}:{{ APP_IMAGE_TAG }} 14 | imagePullPolicy: {{ APP_IMAGE_PULL_POLICY }} 15 | command: 16 | - python 17 | args: 18 | - manage.py 19 | - migrate 20 | - "--noinput" 21 | resources: 22 | requests: 23 | cpu: {{ APP_CPU_REQUEST }} 24 | memory: {{ APP_MEMORY_REQUEST }} 25 | limits: 26 | cpu: {{ APP_CPU_LIMIT }} 27 | memory: {{ APP_MEMORY_LIMIT }} 28 | {% include 'app.env.yaml.j2' %} 29 | -------------------------------------------------------------------------------- /k8s/hpa.yaml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: autoscaling/v1 2 | kind: HorizontalPodAutoscaler 3 | metadata: 4 | name: {{ APP_NAME }} 5 | labels: 6 | app: {{ APP_NAME }} 7 | environment: {{ TARGET_ENVIRONMENT }} 8 | spec: 9 | scaleTargetRef: 10 | apiVersion: apps/v1 11 | kind: Deployment 12 | name: {{ APP_NAME }} 13 | minReplicas: {{ APP_REPLICAS }} 14 | maxReplicas: {{ MAX_APP_REPLICAS }} 15 | targetCPUUtilizationPercentage: 80 16 | 17 | -------------------------------------------------------------------------------- /k8s/redirector.ingress.yaml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Ingress 3 | metadata: 4 | name: {{ REDIRECTOR_NAME }} 5 | namespace: {{ REDIRECTOR_NAMESPACE }} 6 | labels: 7 | app: {{ REDIRECTOR_NAME }} 8 | annotations: 9 | kubernetes.io/ingress.class: "nginx" 10 | nginx.ingress.kubernetes.io/ssl-redirect: "true" 11 | spec: 12 | rules: 13 | {% set domains = REDIRECTOR_HOSTS.split(",") -%} 14 | {% for domain in domains -%} 15 | - host: {{ domain }} 16 | http: 17 | paths: 18 | - backend: 19 | serviceName: {{ REDIRECTOR_NAME }} 20 | servicePort: {{ REDIRECTOR_SERVICE_PORT }} 21 | {% endfor %} 22 | -------------------------------------------------------------------------------- /k8s/redirector.svc.yaml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ REDIRECTOR_NAME }} 5 | namespace: {{ REDIRECTOR_NAMESPACE }} 6 | labels: 7 | app: {{ REDIRECTOR_NAME }} 8 | spec: 9 | selector: 10 | app: {{ REDIRECTOR_NAME }} 11 | ports: 12 | - name: http 13 | port: {{ REDIRECTOR_SERVICE_PORT }} 14 | targetPort: {{ REDIRECTOR_CONTAINER_PORT }} 15 | -------------------------------------------------------------------------------- /k8s/search-index-update-job.yaml.j2: -------------------------------------------------------------------------------- 1 | kind: Job 2 | apiVersion: batch/v1 3 | metadata: 4 | name: search-index-update 5 | spec: 6 | template: 7 | metadata: 8 | name: search-index-update 9 | spec: 10 | restartPolicy: Never 11 | containers: 12 | - name: search-index-update 13 | image: {{ APP_IMAGE }}:{{ APP_IMAGE_TAG }} 14 | imagePullPolicy: {{ APP_IMAGE_PULL_POLICY }} 15 | command: 16 | - python 17 | args: 18 | - manage.py 19 | - wagtail_update_index 20 | resources: 21 | requests: 22 | cpu: {{ APP_CPU_REQUEST }} 23 | memory: {{ APP_MEMORY_REQUEST }} 24 | limits: 25 | cpu: {{ APP_CPU_LIMIT }} 26 | memory: {{ APP_MEMORY_LIMIT }} 27 | {% include 'app.env.yaml.j2' %} 28 | -------------------------------------------------------------------------------- /k8s/wait_for_job.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | GET_JOB_JSON="kubectl -n ${K8S_NAMESPACE} get job ${JOB_NAME} -o json" 4 | 5 | check_job() { 6 | echo "Checking..." 7 | ${GET_JOB_JSON} | jq -e '(.status.succeeded != null) or (.status.failed != null)' 8 | } 9 | 10 | wait_for_job() { 11 | echo "Waiting for job \"${JOB_NAME}\" to complete..." 12 | until check_job 13 | do 14 | sleep 1 15 | done 16 | # Set the exit status based on the job's status. 17 | if ${GET_JOB_JSON} | jq -e '(.status.succeeded != null)' 18 | then 19 | echo "Job \"${JOB_NAME}\" was successful!" 20 | exit 0 21 | else 22 | echo "Job \"${JOB_NAME}\" failed!" 23 | exit 1 24 | fi; 25 | } 26 | 27 | # Wait until the job completes (succeeds or fails), and 28 | # return the exit status based on the status of the job. 29 | wait_for_job 30 | -------------------------------------------------------------------------------- /locust/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | master: 5 | image: locustio/locust 6 | ports: 7 | - '8089:8089' 8 | volumes: 9 | - ./:/mnt/locust 10 | command: -f /mnt/locust/locustfile.py --master -H http://master:8089 11 | 12 | worker: 13 | image: locustio/locust 14 | volumes: 15 | - ./:/mnt/locust 16 | command: -f /mnt/locust/locustfile.py --worker --master-host master 17 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | # Load dev settings by default. To override this, set either: 7 | # - DJANGO_ENV to e.g. production, or; 8 | # - DJANGO_SETTINGS_MODULE to e.g. developerportal.settings.production (takes precedence). 9 | env = os.environ.setdefault("DJANGO_ENV", "dev") 10 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", f"developerportal.settings.{env}") 11 | 12 | from django.core.management import execute_from_command_line 13 | 14 | execute_from_command_line(sys.argv) 15 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ], 5 | "dockerfile": { 6 | "pinDigests": true 7 | }, 8 | "docker-compose": { 9 | "enabled": false 10 | }, 11 | "labels": ["renovate"], 12 | "masterIssue": true, 13 | "rangeStrategy": "pin", 14 | "rebaseStalePrs": true 15 | } 16 | -------------------------------------------------------------------------------- /scripts/ci-tests: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eo pipefail 4 | 5 | echo "Running tests" 6 | docker-compose exec -T app python manage.py test --settings=developerportal.settings.test 7 | docker-compose exec -T static npm run test 8 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | exclude = .*,__pycache__,*.egg,migrations,node_modules 3 | max-line-length = 88 4 | per-file-ignores = 5 | # Ignored to allow wildcard imports in settings 6 | developerportal/settings/*:F401,F403,F405 7 | -------------------------------------------------------------------------------- /src/css/admin.scss: -------------------------------------------------------------------------------- 1 | /* Custom CSS for the Wagtail Admin */ 2 | 3 | .Draftail-block--button-block { 4 | border: 2px dotted gray; 5 | padding: 10px 0px 10px 5px; 6 | a { 7 | background: black; 8 | border: 2px solid black; 9 | color: white; 10 | padding: 3px; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/css/atoms/buttons.scss: -------------------------------------------------------------------------------- 1 | .links-as-buttons { 2 | margin-bottom: $spacing-lg; 3 | a { 4 | @extend .mzp-c-button; 5 | margin-bottom: $spacing-sm; 6 | } 7 | 8 | @media #{$mq-md} { 9 | margin-bottom: 100px; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/css/atoms/embed.scss: -------------------------------------------------------------------------------- 1 | .rich-text img { 2 | height: auto; 3 | max-width: 100%; 4 | } 5 | 6 | .responsive-object { 7 | position: relative; 8 | 9 | .article-body & { 10 | margin-bottom: 24px; 11 | } 12 | } 13 | 14 | .responsive-object iframe, 15 | .responsive-object object, 16 | .responsive-object embed { 17 | height: 100%; 18 | left: 0; 19 | position: absolute; 20 | top: 0; 21 | width: 100%; 22 | } 23 | 24 | iframe { 25 | border: none; 26 | } 27 | -------------------------------------------------------------------------------- /src/css/atoms/icon.scss: -------------------------------------------------------------------------------- 1 | .icon { 2 | // Note that this class includes the close triggers for filter-form.html 3 | display: inline-block; 4 | fill: currentColor; 5 | line-height: 0; 6 | vertical-align: middle; 7 | } 8 | -------------------------------------------------------------------------------- /src/css/atoms/image.scss: -------------------------------------------------------------------------------- 1 | img { 2 | height: auto; 3 | max-width: 100%; 4 | } 5 | 6 | figcaption { 7 | font-size: 14px; 8 | margin: 10px 0; 9 | text-align: center; 10 | } 11 | 12 | .format-left { 13 | float: left; 14 | margin: 0 24px 24px 0; 15 | } 16 | 17 | .format-center { 18 | display: block; 19 | margin: 0 auto 24px; 20 | } 21 | 22 | .format-right { 23 | float: right; 24 | margin: 0 0 24px 24px; 25 | } 26 | -------------------------------------------------------------------------------- /src/css/atoms/label.scss: -------------------------------------------------------------------------------- 1 | .label { 2 | background-color: $color-blue-50; 3 | border-radius: 4px; 4 | color: white; 5 | display: inline-block; 6 | font-size: 0.8em; 7 | font-weight: bold; 8 | line-height: 1.2em; 9 | padding: 4px 6px; 10 | vertical-align: 2px; 11 | width: auto; 12 | margin-left: $spacing-sm; 13 | } 14 | -------------------------------------------------------------------------------- /src/css/atoms/modal.scss: -------------------------------------------------------------------------------- 1 | // Override Protocol default padding for modals on mobile 2 | .mzp-c-modal-window, 3 | .mzp-c-modal-inner { 4 | @media screen and (max-width: 1055px) { 5 | padding: 16px; 6 | } 7 | } 8 | 9 | .mzp-c-modal-inner { 10 | > header h2 { 11 | background: none; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/css/atoms/quote.scss: -------------------------------------------------------------------------------- 1 | blockquote { 2 | clear: both; 3 | font-family: $heading-font-family; 4 | font-size: 24px; 5 | line-height: 32px; 6 | margin: 24px auto; 7 | max-width: 90%; 8 | text-align: center; 9 | 10 | :first-child { 11 | margin-top: 0; 12 | } 13 | 14 | :last-child { 15 | margin-bottom: 0; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/css/atoms/rich-text.scss: -------------------------------------------------------------------------------- 1 | // revert the Protocol styling reset 2 | .rich-text { 3 | clear: both; 4 | 5 | ul { 6 | list-style: disc; 7 | } 8 | 9 | ol { 10 | list-style: decimal; 11 | } 12 | 13 | li { 14 | margin-left: 1.5em; 15 | padding-left: 4px; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/css/global/layout.scss: -------------------------------------------------------------------------------- 1 | // Override Protocol's 25% / 75% layout to be 33% / 67% 2 | 3 | @media #{$mq-md} { 4 | .mzp-l-sidebar.custom-width { 5 | // .custom-width gives us greater specificity so we 6 | // can override mzp-l-sidebar without needing !important 7 | width: 33.333%; 8 | } 9 | 10 | .mzp-l-main.custom-width { 11 | width: 66.665%; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/css/molecules/accessibility-nav.scss: -------------------------------------------------------------------------------- 1 | /* 2 | Keyboard & screen reader skip link menu 3 | ********************************************************************** */ 4 | 5 | .nav-access { 6 | width: 100%; 7 | position: absolute; 8 | top: -20em; 9 | z-index: 1001; 10 | 11 | a { 12 | position: absolute; 13 | left: 0; 14 | right: 0; 15 | background-color: rgba(255, 255, 255, 0.9); 16 | padding: 12px 10px; 17 | font-weight: bold; 18 | text-align: center; 19 | 20 | &:hover, 21 | &:focus { 22 | box-shadow: 3px 3px 5px #aaa; 23 | top: 20em; 24 | text-decoration: none; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/css/molecules/card-event.scss: -------------------------------------------------------------------------------- 1 | address.mzp-c-card-desc { 2 | font-style: normal; 3 | } 4 | -------------------------------------------------------------------------------- /src/css/molecules/card-featured.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/css/molecules/card-featured.scss -------------------------------------------------------------------------------- /src/css/molecules/card-person.scss: -------------------------------------------------------------------------------- 1 | .mzp-c-card.card-person { 2 | .mzp-c-card-media-wrapper { 3 | background-color: transparent; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/css/molecules/notification-bar.scss: -------------------------------------------------------------------------------- 1 | .mzp-c-notification-bar { 2 | @media #{$mq-md} { 3 | margin: $spacing-md auto 0px; 4 | } 5 | .mzp-c-notification-bar-cta { 6 | margin: 0px !important; 7 | text-decoration: underline; 8 | &:hover { 9 | color: $color-marketing-gray-40 !important; 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/css/molecules/pagination.scss: -------------------------------------------------------------------------------- 1 | .pagination-block { 2 | margin-top: $spacing-md; 3 | margin-bottom: $spacing-2xl; 4 | text-align: center; 5 | 6 | & .mzp-c-button { 7 | min-width: 90px; 8 | 9 | @media #{$mq-sm} { 10 | min-width: 135px; 11 | padding: 14px 30px; 12 | } 13 | } 14 | } 15 | 16 | .pagination-current { 17 | display: inline-block; 18 | margin: auto $spacing-md; 19 | } 20 | -------------------------------------------------------------------------------- /src/css/molecules/resource-toolbar.scss: -------------------------------------------------------------------------------- 1 | .resource-toolbar { 2 | align-items: flex-start; 3 | display: flex; 4 | flex-direction: column; 5 | 6 | .resource-share { 7 | margin-top: 24px; 8 | } 9 | 10 | @media screen and (min-width: 600px) { 11 | align-items: center; 12 | flex-direction: row; 13 | justify-content: space-between; 14 | 15 | .resource-share { 16 | margin-top: 0; 17 | } 18 | } 19 | } 20 | 21 | .resource-toolbar-bottom { 22 | align-items: flex-start; 23 | margin-top: 24px; 24 | 25 | @media #{$mq-md} { 26 | margin-top: 48px; 27 | } 28 | 29 | .resource-share { 30 | margin-top: 0; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/css/molecules/site-search-form.scss: -------------------------------------------------------------------------------- 1 | .site-search-form { 2 | margin-bottom: $spacing-xl; 3 | 4 | input.site-search-field { 5 | background: transparent url('/static/img/icons/search-black.svg') 3px 3px 6 | no-repeat; 7 | background-size: 24px auto; 8 | height: 2em; 9 | margin-bottom: $spacing-md; 10 | padding: $spacing-xs $spacing-xs $spacing-xs $spacing-xl; 11 | width: 100%; 12 | -webkit-appearance: none; // override blocking of styling in Safari 13 | 14 | @media #{$mq-md} { 15 | width: 80%; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/css/organisms/article-read-more.scss: -------------------------------------------------------------------------------- 1 | .article-read-more { 2 | .section-header { 3 | padding: $spacing-sm 0px $spacing-xl; 4 | h4 { 5 | font-family: $body-font-family; 6 | 7 | span { 8 | display: inline-block; 9 | padding-left: $spacing-sm; 10 | font-size: 0.6em; 11 | } 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/css/organisms/featured.scss: -------------------------------------------------------------------------------- 1 | .featured-items-wrapper { 2 | .mzp-l-card-half { 3 | margin-bottom: $spacing-md; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/css/organisms/people-section.scss: -------------------------------------------------------------------------------- 1 | .people-section { 2 | h2 { 3 | font-family: $body-font-family; 4 | 5 | font-size: $h4-font-size; 6 | line-height: heading-line-height($h4-font-size); 7 | 8 | margin: $spacing-md 0 $spacing-2xl; 9 | 10 | @media #{$mq-md} { 11 | font-size: $h4-font-size; 12 | line-height: heading-line-height($h4-font-size); 13 | } 14 | 15 | span { 16 | display: inline-block; 17 | padding-left: $spacing-sm; 18 | font-size: 0.6em; 19 | } 20 | } 21 | &.has-tint { 22 | background-color: $color-marketing-gray-20; 23 | h4 { 24 | margin-top: 16px; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/css/organisms/search-results.scss: -------------------------------------------------------------------------------- 1 | .search-results { 2 | .search-results-summary { 3 | color: $color-marketing-gray-60; 4 | } 5 | .no-results { 6 | // padding: 60px 0; 7 | // text-align: center; 8 | height: 20vmax; 9 | 10 | h3::after { 11 | margin-bottom: 0; 12 | } 13 | } 14 | .pagination-block { 15 | margin-top: $spacing-2xl; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/css/organisms/topic-links.scss: -------------------------------------------------------------------------------- 1 | .topic-links { 2 | h2 { 3 | font-family: $body-font-family; 4 | font-size: $h4-font-size-mobile; 5 | line-height: heading-line-height($h4-font-size-mobile); 6 | 7 | @media #{$mq-md} { 8 | font-size: $h4-font-size; 9 | line-height: heading-line-height($h4-font-size); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/css/templates/article.scss: -------------------------------------------------------------------------------- 1 | body.article { 2 | .resource-toolbar { 3 | @media #{$mq-md} { 4 | float: right; 5 | } 6 | margin-bottom: $spacing-2xl; 7 | } 8 | .authors-header { 9 | font-family: $body-font-family; 10 | font-size: $h4-font-size-mobile; // TODO: sweep and rename these variables 11 | line-height: heading-line-height($h4-font-size-mobile); 12 | 13 | @media #{$mq-md} { 14 | font-size: $h4-font-size; 15 | line-height: heading-line-height($h4-font-size); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/css/templates/content-page.scss: -------------------------------------------------------------------------------- 1 | body.content { 2 | figure, 3 | div.rich-text, 4 | p.button-block, 5 | div.responsive-object, 6 | div.syntax-highlight { 7 | margin-bottom: $spacing-2xl; 8 | } 9 | 10 | .standfirst-wrapper { 11 | .rich-text { 12 | margin-bottom: 0px; 13 | } 14 | } 15 | } 16 | 17 | .mzp-has-sidebar { 18 | &.mzp-l-sidebar-right { 19 | &.content-page { 20 | .mzp-l-main { 21 | padding-right: $spacing-2xl; 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/css/templates/event.scss: -------------------------------------------------------------------------------- 1 | body.event { 2 | div.outbound-links { 3 | margin-top: $spacing-lg; 4 | li { 5 | @media #{$mq-md} { 6 | float: left; 7 | margin-right: $spacing-md; 8 | } 9 | a { 10 | display: block; 11 | margin-bottom: $spacing-md; 12 | @media #{$mq-md} { 13 | display: inline-block; 14 | } 15 | } 16 | } 17 | 18 | .resource-share { 19 | a { 20 | display: inherit; 21 | } 22 | display: inline-block; 23 | margin: $spacing-md 0px $spacing-2xl 0; 24 | 25 | @media #{$mq-md} { 26 | float: right; 27 | margin-top: 10px; // for vertical centering 28 | margin-left: $spacing-md; 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/css/templates/events.scss: -------------------------------------------------------------------------------- 1 | body.events { 2 | div.header-strip { 3 | margin-bottom: 0; // to allow for the four-card-row div of featured items 4 | } 5 | 6 | div.events-top-content { 7 | margin-bottom: $spacing-lg; 8 | @media #{$mq-md} { 9 | margin-top: 76px; // + 24px from mzp-l-content == 100px 10 | } 11 | } 12 | 13 | div.events-top-content, 14 | div.events-bottom-content, 15 | div.events-middle-content { 16 | .rich-text { 17 | max-width: 768px; 18 | } 19 | } 20 | 21 | div.four-card-row { 22 | margin-bottom: $spacing-lg; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/css/templates/site-search.scss: -------------------------------------------------------------------------------- 1 | body.site-search { 2 | .mzp-has-sidebar { 3 | display: flex; 4 | flex-direction: column-reverse; 5 | @media #{$mq-md} { 6 | display: block; 7 | flex-direction: row; 8 | } 9 | 10 | .mzp-l-sidebar { 11 | @media #{$mq-md} { 12 | // hidden for non-sm viewports 13 | display: none; 14 | } 15 | } 16 | } 17 | 18 | .pagination-block { 19 | @media #{$mq-md} { 20 | text-align: left; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/css/templates/video.scss: -------------------------------------------------------------------------------- 1 | body.video { 2 | .video-wrapper { 3 | margin-bottom: $spacing-md; 4 | } 5 | 6 | .resource-share { 7 | margin-bottom: $spacing-2xl; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/fonts/Inter-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/fonts/Inter-Bold.woff -------------------------------------------------------------------------------- /src/fonts/Inter-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/fonts/Inter-Bold.woff2 -------------------------------------------------------------------------------- /src/fonts/Inter-BoldItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/fonts/Inter-BoldItalic.woff -------------------------------------------------------------------------------- /src/fonts/Inter-BoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/fonts/Inter-BoldItalic.woff2 -------------------------------------------------------------------------------- /src/fonts/Inter-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/fonts/Inter-Italic.woff -------------------------------------------------------------------------------- /src/fonts/Inter-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/fonts/Inter-Italic.woff2 -------------------------------------------------------------------------------- /src/fonts/Inter-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/fonts/Inter-Regular.woff -------------------------------------------------------------------------------- /src/fonts/Inter-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/fonts/Inter-Regular.woff2 -------------------------------------------------------------------------------- /src/fonts/ZillaSlab-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/fonts/ZillaSlab-Bold.woff -------------------------------------------------------------------------------- /src/fonts/ZillaSlab-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/fonts/ZillaSlab-Bold.woff2 -------------------------------------------------------------------------------- /src/fonts/ZillaSlab-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/fonts/ZillaSlab-Regular.woff -------------------------------------------------------------------------------- /src/fonts/ZillaSlab-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/fonts/ZillaSlab-Regular.woff2 -------------------------------------------------------------------------------- /src/fonts/ZillaSlab-SemiBold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/fonts/ZillaSlab-SemiBold.woff -------------------------------------------------------------------------------- /src/fonts/ZillaSlab-SemiBold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/fonts/ZillaSlab-SemiBold.woff2 -------------------------------------------------------------------------------- /src/fonts/ZillaSlabHighlight-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/fonts/ZillaSlabHighlight-Bold.woff -------------------------------------------------------------------------------- /src/fonts/ZillaSlabHighlight-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/fonts/ZillaSlabHighlight-Bold.woff2 -------------------------------------------------------------------------------- /src/fonts/ZillaSlabHighlight-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/fonts/ZillaSlabHighlight-Regular.woff -------------------------------------------------------------------------------- /src/fonts/ZillaSlabHighlight-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/fonts/ZillaSlabHighlight-Regular.woff2 -------------------------------------------------------------------------------- /src/img/hamburger.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/img/homepage@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/img/homepage@1x.png -------------------------------------------------------------------------------- /src/img/homepage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/img/homepage@2x.png -------------------------------------------------------------------------------- /src/img/http-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/img/http-error.png -------------------------------------------------------------------------------- /src/img/icons/arrow-left-white.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/img/icons/arrow-left.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/img/icons/article-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/img/icons/close-white.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/img/icons/close.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/img/icons/content-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/img/icons/events-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/img/icons/expand-white.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/img/icons/expand.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/img/icons/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/img/icons/favicon.ico -------------------------------------------------------------------------------- /src/img/icons/favicon_dev.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/img/icons/favicon_dev.ico -------------------------------------------------------------------------------- /src/img/icons/favicon_stage.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/img/icons/favicon_stage.ico -------------------------------------------------------------------------------- /src/img/icons/map-pin.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/img/icons/menu-arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/img/icons/menu-white.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/img/icons/menu.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/img/icons/search-black.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/img/icons/search-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/img/icons/social/facebook/black.svg: -------------------------------------------------------------------------------- 1 | icon/social/facebook/black 2 | -------------------------------------------------------------------------------- /src/img/icons/social/facebook/brand.svg: -------------------------------------------------------------------------------- 1 | icon/social/facebook/brand 2 | -------------------------------------------------------------------------------- /src/img/icons/social/facebook/white.svg: -------------------------------------------------------------------------------- 1 | icon/social/facebook/white 2 | -------------------------------------------------------------------------------- /src/img/icons/social/firefox/black.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/img/icons/social/protocol/black.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/img/icons/social/protocol/white.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/img/icons/video-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/img/icons/video.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/img/icons/website.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/img/icons/website.png -------------------------------------------------------------------------------- /src/img/logos/firefox/logo-word-hor-sm-high-res.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/img/logos/firefox/logo-word-hor-sm-high-res.png -------------------------------------------------------------------------------- /src/img/logos/firefox/logo-word-hor-sm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/img/logos/firefox/logo-word-hor-sm.png -------------------------------------------------------------------------------- /src/img/logos/mdn-dino-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/img/logos/mozilla/black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/img/logos/mozilla/black.png -------------------------------------------------------------------------------- /src/img/logos/mozilla/black@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/img/logos/mozilla/black@2x.png -------------------------------------------------------------------------------- /src/img/logos/mozilla/black@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/img/logos/mozilla/black@3x.png -------------------------------------------------------------------------------- /src/img/logos/mozilla/white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/img/logos/mozilla/white.png -------------------------------------------------------------------------------- /src/img/logos/mozilla/white@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/img/logos/mozilla/white@2x.png -------------------------------------------------------------------------------- /src/img/logos/mozilla/white@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/img/logos/mozilla/white@3x.png -------------------------------------------------------------------------------- /src/img/placeholders/article.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/img/placeholders/article.jpg -------------------------------------------------------------------------------- /src/img/placeholders/event_16_9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/img/placeholders/event_16_9.jpg -------------------------------------------------------------------------------- /src/img/placeholders/event_3_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/img/placeholders/event_3_2.jpg -------------------------------------------------------------------------------- /src/img/placeholders/original/event_16_9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/img/placeholders/original/event_16_9.jpg -------------------------------------------------------------------------------- /src/img/placeholders/original/event_3_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/img/placeholders/original/event_3_2.jpg -------------------------------------------------------------------------------- /src/img/placeholders/original/person_16_9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/img/placeholders/original/person_16_9.jpg -------------------------------------------------------------------------------- /src/img/placeholders/original/person_3_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/img/placeholders/original/person_3_2.jpg -------------------------------------------------------------------------------- /src/img/placeholders/original/post_16_9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/img/placeholders/original/post_16_9.jpg -------------------------------------------------------------------------------- /src/img/placeholders/original/post_3_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/img/placeholders/original/post_3_2.jpg -------------------------------------------------------------------------------- /src/img/placeholders/person_16_9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/img/placeholders/person_16_9.jpg -------------------------------------------------------------------------------- /src/img/placeholders/person_3_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/img/placeholders/person_3_2.jpg -------------------------------------------------------------------------------- /src/img/placeholders/post_16_9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/img/placeholders/post_16_9.jpg -------------------------------------------------------------------------------- /src/img/placeholders/post_3_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/img/placeholders/post_3_2.jpg -------------------------------------------------------------------------------- /src/img/promo/firefox-developer@1x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/img/promo/firefox-developer@1x.jpg -------------------------------------------------------------------------------- /src/img/promo/firefox-developer@2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/img/promo/firefox-developer@2x.jpg -------------------------------------------------------------------------------- /src/img/promo/mdn-dino@1x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/img/promo/mdn-dino@1x.jpg -------------------------------------------------------------------------------- /src/img/promo/mdn-dino@2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/img/promo/mdn-dino@2x.jpg -------------------------------------------------------------------------------- /src/img/promo/mdn@1x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/img/promo/mdn@1x.jpg -------------------------------------------------------------------------------- /src/img/promo/mdn@2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/img/promo/mdn@2x.jpg -------------------------------------------------------------------------------- /src/img/promo/newsletter@1x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/img/promo/newsletter@1x.jpg -------------------------------------------------------------------------------- /src/img/promo/newsletter@2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/developer-portal/1443dcd02f2efc100fd22497206346336c0b7537/src/img/promo/newsletter@2x.jpg -------------------------------------------------------------------------------- /src/js/admin-extras.js: -------------------------------------------------------------------------------- 1 | import '../css/admin.scss'; 2 | -------------------------------------------------------------------------------- /src/js/head-includes.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-unresolved */ 2 | 3 | require('./mozilla-dnthelper'); 4 | -------------------------------------------------------------------------------- /src/js/organisms/notification-bar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Handles events related to the notification bar 3 | * 4 | * @class NotificationBar 5 | */ 6 | 7 | module.exports = class NotificationBar { 8 | static init() { 9 | const dissmissButtons = document.querySelectorAll( 10 | '.mzp-c-notification-bar-button', 11 | ); 12 | for (let i = 0; i < dissmissButtons.length; i += 1) { 13 | dissmissButtons[i].addEventListener( 14 | 'click', 15 | function handler(e) { 16 | e.currentTarget.parentNode.remove(); 17 | }, 18 | false, 19 | ); 20 | } 21 | } 22 | }; 23 | --------------------------------------------------------------------------------