Bug 1353794: Add app to consume and store wordpress data

Fetches wordpress data via the JSON API. Fetches tags and
featured media if present as well.

Also add blog post generic view and QS methods to filter by blog and/or tags easily.
This commit is contained in:
Paul McLanahan 2017-04-10 14:54:58 -04:00
Родитель 3a3636d233
Коммит cc22741d90
21 изменённых файлов: 1646 добавлений и 1 удалений

Просмотреть файл

@ -430,6 +430,7 @@ INSTALLED_APPS = (
'bedrock.thunderbird',
'bedrock.shapeoftheweb',
'bedrock.utils',
'bedrock.wordpress',
# last so that redirects here will be last
'bedrock.redirects',
@ -543,7 +544,18 @@ BLOG_FEEDS = {
},
'internetcitizen': {
'url': 'https://blog.mozilla.org/internetcitizen/',
'name': 'Internet Citizen'
'name': 'Internet Citizen',
},
}
# same as above, but uses the JSON REST API and thus gets
# more data (e.g. tags and featured image urls).
# TODO: Once this rolls out for the Firefox blog, we should
# convert the others and remove the old XML feed blogs above.
WP_BLOGS = {
'firefox': {
'url': 'https://blog.mozilla.org/firefox/',
'name': 'The Firefox Frontier',
'num_posts': 20,
},
}

Просмотреть файл

67
bedrock/wordpress/api.py Normal file
Просмотреть файл

@ -0,0 +1,67 @@
from __future__ import print_function, unicode_literals
from django.conf import settings
import requests
from raven.contrib.django.raven_compat.models import client as sentry_client
def _request(api_url, limit=None, page=1):
# 100 is max per page from WP
per_page = limit or 100
resp = requests.get(api_url, params={'per_page': per_page, 'page': page}, timeout=5)
resp.raise_for_status()
data = resp.json()
if limit is None and page == 1:
num_pages = int(resp.headers.get('x-wp-totalpages', 1))
if num_pages > 1:
for i in range(2, num_pages + 1):
data.extend(_request(api_url, page=i))
return data
def _api_url(feed_url, data_type, data_id):
api_url = '{}/wp-json/wp/v2/{}/'.format(feed_url.rstrip('/'), data_type)
if data_id:
api_url += str(data_id)
return api_url
def get_wp_data(feed_id, data_type, data_id=None, limit=None):
try:
feed_config = settings.WP_BLOGS[feed_id]
if data_type == 'posts' and limit is None:
limit = feed_config.get('num_posts', 20)
api_url = _api_url(feed_config['url'], data_type, data_id)
if data_id:
data = _request(api_url, limit=1)
else:
data = _request(api_url, limit=limit)
return data
except Exception:
sentry_client.captureException()
return None
def get_posts_data(feed_id, num_posts=None):
posts = get_wp_data(feed_id, 'posts', limit=num_posts)
tags = get_wp_data(feed_id, 'tags')
if not (posts and tags):
return None
tags = {t['id']: t['slug'] for t in tags}
for post in posts:
post['tags'] = [tags[t] for t in post['tags']]
# some blogs set featured_media to 0 when none is set
if 'featured_media' in post and post['featured_media']:
media = get_wp_data(feed_id, 'media', post['featured_media'])
if media:
post['featured_media'] = media
else:
post['featured_media'] = {}
return {
'posts': posts,
'wp_blog_slug': feed_id,
}

Просмотреть файл

Просмотреть файл

Просмотреть файл

@ -0,0 +1,28 @@
from __future__ import print_function
from django.conf import settings
from django.core.management.base import BaseCommand, CommandError
from bedrock.wordpress.models import BlogPost
class Command(BaseCommand):
def add_arguments(self, parser):
parser.add_argument('-q', '--quiet', action='store_true', dest='quiet', default=False,
help='If no error occurs, swallow all output.'),
parser.add_argument('--database', default='default',
help=('Specifies the database to use. '
'Defaults to "default".')),
def handle(self, *args, **options):
errors = []
for feed_id in settings.WP_BLOGS:
updated = BlogPost.objects.refresh(feed_id, options['database'])
if updated and not options['quiet']:
print('Refreshed %s posts from the %s blog' % (updated, feed_id))
if not updated:
errors.append('Something has gone wrong with refreshing the %s blog' % feed_id)
if errors:
raise CommandError('\n'.join(errors))

Просмотреть файл

@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import django_extensions.db.fields.json
class Migration(migrations.Migration):
dependencies = [
]
operations = [
migrations.CreateModel(
name='BlogPost',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('wp_id', models.IntegerField()),
('wp_blog_slug', models.CharField(max_length=50)),
('date', models.DateTimeField()),
('modified', models.DateTimeField()),
('title', models.CharField(max_length=255)),
('excerpt', models.TextField()),
('link', models.URLField()),
('featured_media', django_extensions.db.fields.json.JSONField()),
('tags', django_extensions.db.fields.json.JSONField()),
],
options={
'ordering': ['-date'],
'get_latest_by': 'date',
},
),
]

Просмотреть файл

118
bedrock/wordpress/models.py Normal file
Просмотреть файл

@ -0,0 +1,118 @@
from __future__ import print_function, unicode_literals
import operator
from django.conf import settings
from django.db import models, transaction
from django.db.models import Q
from django.db.utils import DatabaseError
from django.utils.html import strip_tags
import bleach
from django_extensions.db.fields.json import JSONField
from jinja2 import Markup
from raven.contrib.django.raven_compat.models import client as sentry_client
from bedrock.wordpress.api import get_posts_data
class BlogPostQuerySet(models.QuerySet):
def filter_by_blog(self, blog_slug):
return self.filter(wp_blog_slug=blog_slug)
def filter_by_tags(self, *tags):
tag_qs = [Q(tags__contains='"{}"'.format(t)) for t in tags]
return self.filter(reduce(operator.or_, tag_qs))
class BlogPostManager(models.Manager):
def get_queryset(self):
return BlogPostQuerySet(self.model, using=self._db)
def filter_by_blog(self, blog_slug):
return self.get_queryset().filter_by_blog(blog_slug)
def filter_by_tags(self, *tags):
return self.get_queryset().filter_by_tags(*tags)
def update_posts(self, data, database='default'):
with transaction.atomic(using=database):
count = 0
posts = data['posts']
self.filter_by_blog(data['wp_blog_slug']).delete()
for post in posts:
try:
self.create(
wp_id=post['id'],
wp_blog_slug=data['wp_blog_slug'],
date=post['date_gmt'],
modified=post['modified_gmt'],
title=bleach.clean(post['title']['rendered']),
excerpt=bleach.clean(post['excerpt']['rendered']),
link=post['link'],
featured_media=post['featured_media'],
tags=post['tags'],
)
count += 1
except (DatabaseError, KeyError):
sentry_client.captureException()
raise
return count
def refresh(self, feed_id, database='default', num_posts=None):
data = get_posts_data(feed_id, num_posts)
if data:
return self.update_posts(data, database)
# no data returned. something wrong.
return 0
class BlogPost(models.Model):
wp_id = models.IntegerField()
wp_blog_slug = models.CharField(max_length=50)
date = models.DateTimeField()
modified = models.DateTimeField()
title = models.CharField(max_length=255)
excerpt = models.TextField()
link = models.URLField()
featured_media = JSONField()
tags = JSONField()
objects = BlogPostManager()
class Meta:
get_latest_by = 'date'
ordering = ['-date']
def __unicode__(self):
return '%s: %s' % (self.blog_name, self.title)
def get_absolute_url(self):
return self.link
def htmlify(self):
summary = strip_tags(self.excerpt).strip()
if summary.lower().endswith('continue reading'):
summary = summary[:-16]
return Markup(summary)
@property
def blog_title(self):
title = strip_tags(self.title).strip()
return Markup(title).unescape()
@property
def blog_link(self):
return settings.WP_BLOGS[self.wp_blog_slug]['url']
@property
def blog_name(self):
return settings.WP_BLOGS[self.wp_blog_slug]['name']
def get_featured_image_url(self, size='large'):
try:
return self.featured_media['media_details']['sizes'][size]['source_url']
except KeyError:
return None

Просмотреть файл

Просмотреть файл

@ -0,0 +1,72 @@
from django.test import override_settings
from bedrock.wordpress import api
import responses
from mock import patch
TEST_WP_BLOGS = {
'firefox': {
'url': 'https://blog.mozilla.org/firefox/',
'name': 'The Firefox Frontier',
'num_posts': 10,
},
}
@patch.object(api, 'requests')
def test_limited_request(req_mock):
api._request('some_url', limit=10)
req_mock.get.assert_called_once_with('some_url',
params={'per_page': 10, 'page': 1},
timeout=5)
@responses.activate
def test_unlimited_request():
api_url = api._api_url(TEST_WP_BLOGS['firefox']['url'], 'tags', None)
responses.add(responses.GET,
api_url + '?per_page=100&page=1',
match_querystring=True,
json=[1],
adding_headers={'X-WP-TotalPages': '3'})
responses.add(responses.GET,
api_url + '?per_page=100&page=2',
json=[2, 2],
match_querystring=True,
adding_headers={'X-WP-TotalPages': '3'})
responses.add(responses.GET,
api_url + '?per_page=100&page=3',
json=[3, 3, 3],
match_querystring=True,
adding_headers={'X-WP-TotalPages': '3'})
data = api._request(api_url, limit=None)
assert data == [1, 2, 2, 3, 3, 3]
assert len(responses.calls) == 3
def test_api_url():
assert (api._api_url('https://moz.blog/', 'posts', 4) ==
'https://moz.blog/wp-json/wp/v2/posts/4')
assert (api._api_url('https://moz.blog/', 'tags', None) ==
'https://moz.blog/wp-json/wp/v2/tags/')
assert (api._api_url('https://moz.blog', 'media', 55) ==
'https://moz.blog/wp-json/wp/v2/media/55')
@override_settings(WP_BLOGS=TEST_WP_BLOGS)
@patch.object(api, '_request')
def test_get_wp_data(req_mock):
api.get_wp_data('firefox', 'posts')
api_url = api._api_url(TEST_WP_BLOGS['firefox']['url'], 'posts', None)
req_mock.assert_called_with(api_url, limit=10)
api.get_wp_data('firefox', 'media', 75)
api_url = api._api_url(TEST_WP_BLOGS['firefox']['url'], 'media', 75)
req_mock.assert_called_with(api_url, limit=1)
api.get_wp_data('firefox', 'tags')
api_url = api._api_url(TEST_WP_BLOGS['firefox']['url'], 'tags', None)
req_mock.assert_called_with(api_url, limit=None)

Просмотреть файл

@ -0,0 +1,139 @@
{
"id": 75,
"date": "2017-03-14T14:13:24",
"date_gmt": "2017-03-14T21:13:24",
"guid": {
"rendered": "https:\/\/blog.mozilla.org\/firefox\/files\/2017\/03\/Put-Your-Trust-in-Rust.png"
},
"modified": "2017-03-14T14:14:55",
"modified_gmt": "2017-03-14T21:14:55",
"slug": "put-your-trust-in-rust",
"status": "inherit",
"type": "attachment",
"link": "https:\/\/blog.mozilla.org\/firefox\/put-trust-rust-shipping-now-firefox\/put-your-trust-in-rust\/",
"title": {
"rendered": "Put-Your-Trust-in-Rust"
},
"author": 1431,
"comment_status": "closed",
"ping_status": "closed",
"template": "",
"meta": [],
"description": {
"rendered": "<p class=\"attachment\"><a href='https:\/\/blog.mozilla.org\/firefox\/files\/2017\/03\/Put-Your-Trust-in-Rust.png'><img width=\"300\" height=\"158\" src=\"https:\/\/blog.mozilla.org\/firefox\/files\/2017\/03\/Put-Your-Trust-in-Rust-300x158.png\" class=\"attachment-medium size-medium\" alt=\"Trustworthy browser\" srcset=\"https:\/\/blog.mozilla.org\/firefox\/files\/2017\/03\/Put-Your-Trust-in-Rust-300x158.png 300w, https:\/\/blog.mozilla.org\/firefox\/files\/2017\/03\/Put-Your-Trust-in-Rust-768x403.png 768w, https:\/\/blog.mozilla.org\/firefox\/files\/2017\/03\/Put-Your-Trust-in-Rust-600x315.png 600w, https:\/\/blog.mozilla.org\/firefox\/files\/2017\/03\/Put-Your-Trust-in-Rust-1000x525.png 1000w, https:\/\/blog.mozilla.org\/firefox\/files\/2017\/03\/Put-Your-Trust-in-Rust.png 1200w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p>Firefox puts user security and privacy first<\/p>\n"
},
"caption": {
"rendered": "<p>Firefox puts user security and privacy first<\/p>\n"
},
"alt_text": "Trustworthy browser",
"media_type": "image",
"mime_type": "image\/png",
"media_details": {
"width": 1200,
"height": 630,
"file": "2017\/03\/Put-Your-Trust-in-Rust.png",
"sizes": {
"thumbnail": {
"file": "Put-Your-Trust-in-Rust-150x150.png",
"width": 150,
"height": 150,
"mime_type": "image\/png",
"source_url": "https:\/\/blog.mozilla.org\/firefox\/files\/2017\/03\/Put-Your-Trust-in-Rust-150x150.png"
},
"medium": {
"file": "Put-Your-Trust-in-Rust-300x158.png",
"width": 300,
"height": 158,
"mime_type": "image\/png",
"source_url": "https:\/\/blog.mozilla.org\/firefox\/files\/2017\/03\/Put-Your-Trust-in-Rust-300x158.png"
},
"medium_large": {
"file": "Put-Your-Trust-in-Rust-768x403.png",
"width": 768,
"height": 403,
"mime_type": "image\/png",
"source_url": "https:\/\/blog.mozilla.org\/firefox\/files\/2017\/03\/Put-Your-Trust-in-Rust-768x403.png"
},
"large": {
"file": "Put-Your-Trust-in-Rust-600x315.png",
"width": 600,
"height": 315,
"mime_type": "image\/png",
"source_url": "https:\/\/blog.mozilla.org\/firefox\/files\/2017\/03\/Put-Your-Trust-in-Rust-600x315.png"
},
"post-large": {
"file": "Put-Your-Trust-in-Rust-600x330.png",
"width": 600,
"height": 330,
"mime_type": "image\/png",
"source_url": "https:\/\/blog.mozilla.org\/firefox\/files\/2017\/03\/Put-Your-Trust-in-Rust-600x330.png"
},
"post-thumbnail": {
"file": "Put-Your-Trust-in-Rust-300x165.png",
"width": 300,
"height": 165,
"mime_type": "image\/png",
"source_url": "https:\/\/blog.mozilla.org\/firefox\/files\/2017\/03\/Put-Your-Trust-in-Rust-300x165.png"
},
"extra-large": {
"file": "Put-Your-Trust-in-Rust-1000x525.png",
"width": 1000,
"height": 525,
"mime_type": "image\/png",
"source_url": "https:\/\/blog.mozilla.org\/firefox\/files\/2017\/03\/Put-Your-Trust-in-Rust-1000x525.png"
},
"full": {
"file": "Put-Your-Trust-in-Rust.png",
"width": 1200,
"height": 630,
"mime_type": "image\/png",
"source_url": "https:\/\/blog.mozilla.org\/firefox\/files\/2017\/03\/Put-Your-Trust-in-Rust.png"
}
},
"image_meta": {
"aperture": "0",
"credit": "",
"camera": "",
"caption": "",
"created_timestamp": "0",
"copyright": "",
"focal_length": "0",
"iso": "0",
"shutter_speed": "0",
"title": "",
"orientation": "0",
"keywords": []
}
},
"post": 74,
"source_url": "https:\/\/blog.mozilla.org\/firefox\/files\/2017\/03\/Put-Your-Trust-in-Rust.png",
"_links": {
"self": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/media\/75"
}
],
"collection": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/media"
}
],
"about": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/types\/attachment"
}
],
"author": [
{
"embeddable": true,
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/users\/1431"
}
],
"replies": [
{
"embeddable": true,
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/comments?post=75"
}
]
}
}

Просмотреть файл

@ -0,0 +1,319 @@
[
{
"id": 10,
"date": "2017-03-14T13:58:50",
"date_gmt": "2017-03-14T20:58:50",
"guid": {
"rendered": "https:\/\/blog.mozilla.org\/firefox\/?p=10"
},
"modified": "2017-03-15T15:14:33",
"modified_gmt": "2017-03-15T22:14:33",
"slug": "fast-responsive-reliable-browser-multi-tab-age",
"status": "publish",
"type": "post",
"link": "https:\/\/blog.mozilla.org\/firefox\/fast-responsive-reliable-browser-multi-tab-age\/",
"title": {
"rendered": "Fast, Responsive, Reliable: A Browser For the Multi-Tab Age"
},
"content": {
"rendered": "<p>Take a look at your browser window: how many tabs do you have open? It\u2019s likely your answer is somewhere between \u201cten\u201d and \u201ca ton,\u201d and you\u2019re not alone. These days, users consume web content in a smorgasbord of tabs, leaving pages open (to look at later) even as we open new ones (for more immediate tasks and browsing). Most of the time, the web offers five-star buffet service, responding to our commands and serving up satisfying content on command.<\/p>\n<p><img class=\"size-full wp-image-45 alignnone\" src=\"https:\/\/blog.mozilla.org\/firefox\/files\/2017\/03\/Multi-Process_PullQuote.png\" alt=\"\" width=\"1200\" height=\"630\" srcset=\"https:\/\/blog.mozilla.org\/firefox\/files\/2017\/03\/Multi-Process_PullQuote.png 1200w, https:\/\/blog.mozilla.org\/firefox\/files\/2017\/03\/Multi-Process_PullQuote-300x158.png 300w, https:\/\/blog.mozilla.org\/firefox\/files\/2017\/03\/Multi-Process_PullQuote-768x403.png 768w, https:\/\/blog.mozilla.org\/firefox\/files\/2017\/03\/Multi-Process_PullQuote-600x315.png 600w, https:\/\/blog.mozilla.org\/firefox\/files\/2017\/03\/Multi-Process_PullQuote-1000x525.png 1000w\" sizes=\"(max-width: 1200px) 100vw, 1200px\" \/><\/p>\n<p>But then there are times when you stand in line at the Internet buffet moving nowhere. You eye the lavish food displays while one person stalls, carefully picking over the salad and heaping spoonfuls of entr\u00e9es onto his tray. This kind of hang time is frustrating. Your browser, overloaded with processing, temporarily freezes and fails to respond to your clicks and keystrokes, no matter how insistently you tap away. This is because some pages require a lot of your computer\u2019s processing power, and the machine puts all of its energy into processing the page, rather than responding to you.<\/p>\n<p>Last August, we began the rollout of Electrolysis (e10s), our multi-process browser functionality. Basically, multi-process means that the browser runs in two separate lanes: one for the user interface, and another for your many tabs (i.e., web content). This means that Firefox can react quickly to your insistent taps (input), instead of temporarily freezing while processing a page (or, let\u2019s be honest, your dozens of open pages!) In terms of numbers, the multi-process Firefox browser hangs 86% less while web pages are loading, and 75% less after loading.<\/p>\n<p>This means that the Firefox browser is more stable than ever before. You know how a single hanging page can lead to the entire browser crashing? Because multi-process Firefox runs user interface and content processes separately, if a page ever hangs and crashes, it doesn\u2019t take the whole browser down with it.<\/p>\n<p>We know you expect a flexible and responsive browsing experience that keeps pace with your internet appetite. The Firefox user experience is now faster, smoother, and more reliable than ever before. So feel free to feast on the Internet, and save room for dessert. Or (let\u2019s be honest) \u2014 desserts.<\/p>\n<hr \/>\n<h4 style=\"text-align: center;\">Browse Fast. Browse Free<\/h4>\n<p><a href=\"https:\/\/www.mozilla.org\/en-US\/firefox\/new\/?scene=2\"><img class=\"size-medium wp-image-12 aligncenter\" src=\"https:\/\/blog.mozilla.org\/firefox\/files\/2017\/02\/Screen-Shot-2017-02-17-at-11.14.12-AM-300x64.png\" alt=\"Download Firefox Now\" width=\"300\" height=\"64\" srcset=\"https:\/\/blog.mozilla.org\/firefox\/files\/2017\/02\/Screen-Shot-2017-02-17-at-11.14.12-AM-300x64.png 300w, https:\/\/blog.mozilla.org\/firefox\/files\/2017\/02\/Screen-Shot-2017-02-17-at-11.14.12-AM.png 456w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n",
"protected": false
},
"excerpt": {
"rendered": "<p>Take a look at your browser window: how many tabs do you have open? It\u2019s likely your answer is somewhere between \u201cten\u201d and \u201ca ton,\u201d and you\u2019re not alone. These &hellip; <a class=\"go\" href=\"https:\/\/blog.mozilla.org\/firefox\/fast-responsive-reliable-browser-multi-tab-age\/\">Read more<\/a><\/p>\n",
"protected": false
},
"author": 144,
"featured_media": 0,
"comment_status": "closed",
"ping_status": "closed",
"sticky": false,
"template": "",
"format": "standard",
"meta": [],
"categories": [
1
],
"tags": [
43,
301510
],
"_links": {
"self": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/posts\/10"
}
],
"collection": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/posts"
}
],
"about": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/types\/post"
}
],
"author": [
{
"embeddable": true,
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/users\/144"
}
],
"replies": [
{
"embeddable": true,
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/comments?post=10"
}
],
"version-history": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/posts\/10\/revisions"
}
],
"wp:featuredmedia": [
{
"embeddable": true,
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/media\/45"
}
],
"wp:attachment": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/media?parent=10"
}
],
"wp:term": [
{
"taxonomy": "category",
"embeddable": true,
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/categories?post=10"
},
{
"taxonomy": "post_tag",
"embeddable": true,
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/tags?post=10"
}
],
"curies": [
{
"name": "wp",
"href": "https:\/\/api.w.org\/{rel}",
"templated": true
}
]
}
},
{
"id": 69,
"date": "2017-03-13T12:56:57",
"date_gmt": "2017-03-13T19:56:57",
"guid": {
"rendered": "https:\/\/blog.mozilla.org\/firefox\/?p=69"
},
"modified": "2017-04-04T08:36:59",
"modified_gmt": "2017-04-04T15:36:59",
"slug": "smooth-scrolling-web-browser-removes-jank",
"status": "publish",
"type": "post",
"link": "https:\/\/blog.mozilla.org\/firefox\/smooth-scrolling-web-browser-removes-jank\/",
"title": {
"rendered": "Smooth Scrolling: How a Web Browser Removes Jank"
},
"content": {
"rendered": "<p>Here\u2019s the scenario: it\u2019s about 6 AM on a weekday, and your alarm didn\u2019t go off. You\u2019re rushing to catch a flight that you\u2019re probably going to be late for. Your phone is displaying your approaching Lyft car, which you figure might help you win some time. Hurrying to get the info from the airline\u2019s site on your laptop (because you forgot to download the app, of course), you\u2019re scrolling through the obligatory travel disclosures when your browser window suddenly freezes on you, mid-scroll. Right then the Lyft arrives, the driver sends you a text. Frustration ensues as you try in vain to get the browser to respond; the Lyft honks outside when suddenly, the page responds to your scrolling efforts, and you end up at the bottom of the page. Thankfully, you learn that the flight is delayed 20 minutes, but no thanks to your slow browser!<\/p>\n<p><img class=\"alignnone size-full wp-image-72\" src=\"https:\/\/blog.mozilla.org\/firefox\/files\/2017\/03\/Smooth-Scrolling_2-01_1.gif\" alt=\"Smooth Scrolling Web Browser\" width=\"1201\" height=\"631\" \/><\/p>\n<p>This responsiveness issue is known as jank, and it\u2019s a problem we&#8217;ve addressed with the rollout of Electrolysis (e10s), our multi-process browser functionality. The solution to scrolling-induced browser jank is called asynchronous panning and zooming (APZ). APZ allows content to be pre-rendered before it enters the viewing window.<\/p>\n<p>Though we\u2019ve had variations of APZ around for a while, our new multi-process Firefox makes scrolling smoother and more responsive than ever. With the older, single-process Firefox browser, jank was the result of the browser being unable to handle the demands of the juggling required to display a web page at the same time as user input (like scrolling).<\/p>\n<p>Multiple processes means that your clicks and scrolls can be managed much more efficiently because they are immediately processed, rather than getting stalled behind other content (and making you late for your flight.)<\/p>\n<hr \/>\n<h4 style=\"text-align: center;\">Browse Fast. Browse Free<\/h4>\n<p><a href=\"https:\/\/www.mozilla.org\/en-US\/firefox\/new\/?scene=2\"><img class=\"size-medium wp-image-12 aligncenter\" src=\"https:\/\/blog.mozilla.org\/firefox\/files\/2017\/02\/Screen-Shot-2017-02-17-at-11.14.12-AM-300x64.png\" alt=\"Download Firefox Now\" width=\"300\" height=\"64\" srcset=\"https:\/\/blog.mozilla.org\/firefox\/files\/2017\/02\/Screen-Shot-2017-02-17-at-11.14.12-AM-300x64.png 300w, https:\/\/blog.mozilla.org\/firefox\/files\/2017\/02\/Screen-Shot-2017-02-17-at-11.14.12-AM.png 456w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n",
"protected": false
},
"excerpt": {
"rendered": "<p>Here\u2019s the scenario: it\u2019s about 6 AM on a weekday, and your alarm didn\u2019t go off. You\u2019re rushing to catch a flight that you\u2019re probably going to be late for. &hellip; <a class=\"go\" href=\"https:\/\/blog.mozilla.org\/firefox\/smooth-scrolling-web-browser-removes-jank\/\">Read more<\/a><\/p>\n",
"protected": false
},
"author": 144,
"featured_media": 0,
"comment_status": "closed",
"ping_status": "closed",
"sticky": false,
"template": "",
"format": "standard",
"meta": [],
"categories": [
1
],
"tags": [
301510,
302022,
302197,
301766
],
"_links": {
"self": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/posts\/69"
}
],
"collection": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/posts"
}
],
"about": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/types\/post"
}
],
"author": [
{
"embeddable": true,
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/users\/144"
}
],
"replies": [
{
"embeddable": true,
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/comments?post=69"
}
],
"version-history": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/posts\/69\/revisions"
}
],
"wp:featuredmedia": [
{
"embeddable": true,
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/media\/72"
}
],
"wp:attachment": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/media?parent=69"
}
],
"wp:term": [
{
"taxonomy": "category",
"embeddable": true,
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/categories?post=69"
},
{
"taxonomy": "post_tag",
"embeddable": true,
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/tags?post=69"
}
],
"curies": [
{
"name": "wp",
"href": "https:\/\/api.w.org\/{rel}",
"templated": true
}
]
}
},
{
"id": 74,
"date": "2017-03-12T14:15:32",
"date_gmt": "2017-03-12T21:15:32",
"guid": {
"rendered": "https:\/\/blog.mozilla.org\/firefox\/?p=74"
},
"modified": "2017-04-04T08:37:02",
"modified_gmt": "2017-04-04T15:37:02",
"slug": "put-trust-rust-shipping-now-firefox",
"status": "publish",
"type": "post",
"link": "https:\/\/blog.mozilla.org\/firefox\/put-trust-rust-shipping-now-firefox\/",
"title": {
"rendered": "Put your Trust in Rust &#8211; Shipping Now in Firefox"
},
"content": {
"rendered": "<p>At Mozilla, security is one of our highest priorities, as the openness and accessibility of the Internet are threatened when user safety and privacy is not protected. Seven years ago we began sponsoring the development of the Rust programming language, with the vision of building a systems programming language that emphasizes security, speed, and parallelism. Parallelism means that multiple CPU cores can execute different code at the same time, and it makes for computing that is more powerful and efficient.<\/p>\n<p><img class=\"alignnone size-full wp-image-75\" src=\"https:\/\/blog.mozilla.org\/firefox\/files\/2017\/03\/Put-Your-Trust-in-Rust.png\" alt=\"Trustworthy browser\" width=\"1200\" height=\"630\" srcset=\"https:\/\/blog.mozilla.org\/firefox\/files\/2017\/03\/Put-Your-Trust-in-Rust.png 1200w, https:\/\/blog.mozilla.org\/firefox\/files\/2017\/03\/Put-Your-Trust-in-Rust-300x158.png 300w, https:\/\/blog.mozilla.org\/firefox\/files\/2017\/03\/Put-Your-Trust-in-Rust-768x403.png 768w, https:\/\/blog.mozilla.org\/firefox\/files\/2017\/03\/Put-Your-Trust-in-Rust-600x315.png 600w, https:\/\/blog.mozilla.org\/firefox\/files\/2017\/03\/Put-Your-Trust-in-Rust-1000x525.png 1000w\" sizes=\"(max-width: 1200px) 100vw, 1200px\" \/><\/p>\n<p>With many languages, writing parallel programs is a challenge because developers have to go to great lengths to avoid errors. This means the code has a higher probability of containing bugs, which can lead to security vulnerabilities \u2014 and a single programming mistake can be greatly damaging to web security, like a leak in a dam.<\/p>\n<p>&nbsp;<\/p>\n<p>Rust, as a memory-safe programming language, solves this because it\u2019s able to check source code before it\u2019s converted (or \u201ccompiled\u201d) into low-level instructions, making it far easier for developers to write programs with confidence that the code is safe. This feature is akin to having a guardian angel watching over your work, pointing out errors and guiding you towards safe and secure code. This empowers developers to feel more comfortable and confident in building parallel algorithms, which with <a href=\"https:\/\/medium.com\/mozilla-tech\/a-quantum-leap-for-the-web-a3b7174b3c12\">Project Quantum<\/a> \u2014 our effort to develop Mozilla\u2019s future web engine \u2014 will greatly speed up the browser.<\/p>\n<p>&nbsp;<\/p>\n<p>Last summer, with the release of Firefox 48, we shipped<a href=\"https:\/\/hacks.mozilla.org\/2016\/07\/shipping-rust-in-firefox\/\"> the very first browser component to be written in the Rust programming language<\/a> \u2014 an MP4 parser for video files. Streaming media files in your browser can be particularly risky if you don\u2019t know or trust the source of the file, as these can maliciously take advantage of bugs in a browser\u2019s code. Rust\u2019s memory-safe capabilities prevent these vulnerabilities from being built into the code in the first place.<\/p>\n<p>&nbsp;<\/p>\n<p>We\u2019re proud to have helped create a language that will power our next-generation browser and put user security and privacy first.<\/p>\n<hr \/>\n<h4 style=\"text-align: center;\">Browse Fast. Browse Free<\/h4>\n<p><a href=\"https:\/\/www.mozilla.org\/en-US\/firefox\/new\/?scene=2\"><img class=\"size-medium wp-image-12 aligncenter\" src=\"https:\/\/blog.mozilla.org\/firefox\/files\/2017\/02\/Screen-Shot-2017-02-17-at-11.14.12-AM-300x64.png\" alt=\"Download Firefox Now\" width=\"300\" height=\"64\" srcset=\"https:\/\/blog.mozilla.org\/firefox\/files\/2017\/02\/Screen-Shot-2017-02-17-at-11.14.12-AM-300x64.png 300w, https:\/\/blog.mozilla.org\/firefox\/files\/2017\/02\/Screen-Shot-2017-02-17-at-11.14.12-AM.png 456w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n",
"protected": false
},
"excerpt": {
"rendered": "<p>At Mozilla, security is one of our highest priorities, as the openness and accessibility of the Internet are threatened when user safety and privacy is not protected. Seven years ago &hellip; <a class=\"go\" href=\"https:\/\/blog.mozilla.org\/firefox\/put-trust-rust-shipping-now-firefox\/\">Read more<\/a><\/p>\n",
"protected": false
},
"author": 144,
"featured_media": 75,
"comment_status": "closed",
"ping_status": "closed",
"sticky": false,
"template": "",
"format": "standard",
"meta": [],
"categories": [
1
],
"tags": [
301510,
847,
617,
16179,
69
],
"_links": {
"self": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/posts\/74"
}
],
"collection": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/posts"
}
],
"about": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/types\/post"
}
],
"author": [
{
"embeddable": true,
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/users\/144"
}
],
"replies": [
{
"embeddable": true,
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/comments?post=74"
}
],
"version-history": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/posts\/74\/revisions"
}
],
"wp:featuredmedia": [
{
"embeddable": true,
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/media\/75"
}
],
"wp:attachment": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/media?parent=74"
}
],
"wp:term": [
{
"taxonomy": "category",
"embeddable": true,
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/categories?post=74"
},
{
"taxonomy": "post_tag",
"embeddable": true,
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/tags?post=74"
}
],
"curies": [
{
"name": "wp",
"href": "https:\/\/api.w.org\/{rel}",
"templated": true
}
]
}
}
]

Просмотреть файл

@ -0,0 +1,353 @@
[
{
"id": 43,
"count": 1,
"description": "",
"link": "https:\/\/blog.mozilla.org\/firefox\/tag\/browser\/",
"name": "browser",
"slug": "browser",
"taxonomy": "post_tag",
"meta": [],
"_links": {
"self": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/tags\/43"
}
],
"collection": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/tags"
}
],
"about": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/taxonomies\/post_tag"
}
],
"wp:post_type": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/posts?tags=43"
}
],
"curies": [
{
"name": "wp",
"href": "https:\/\/api.w.org\/{rel}",
"templated": true
}
]
}
},
{
"id": 301510,
"count": 3,
"description": "",
"link": "https:\/\/blog.mozilla.org\/firefox\/tag\/fastest\/",
"name": "fastest",
"slug": "fastest",
"taxonomy": "post_tag",
"meta": [],
"_links": {
"self": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/tags\/301510"
}
],
"collection": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/tags"
}
],
"about": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/taxonomies\/post_tag"
}
],
"wp:post_type": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/posts?tags=301510"
}
],
"curies": [
{
"name": "wp",
"href": "https:\/\/api.w.org\/{rel}",
"templated": true
}
]
}
},
{
"id": 302022,
"count": 1,
"description": "",
"link": "https:\/\/blog.mozilla.org\/firefox\/tag\/jank\/",
"name": "jank",
"slug": "jank",
"taxonomy": "post_tag",
"meta": [],
"_links": {
"self": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/tags\/302022"
}
],
"collection": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/tags"
}
],
"about": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/taxonomies\/post_tag"
}
],
"wp:post_type": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/posts?tags=302022"
}
],
"curies": [
{
"name": "wp",
"href": "https:\/\/api.w.org\/{rel}",
"templated": true
}
]
}
},
{
"id": 847,
"count": 1,
"description": "",
"link": "https:\/\/blog.mozilla.org\/firefox\/tag\/privacy\/",
"name": "privacy",
"slug": "privacy",
"taxonomy": "post_tag",
"meta": [],
"_links": {
"self": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/tags\/847"
}
],
"collection": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/tags"
}
],
"about": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/taxonomies\/post_tag"
}
],
"wp:post_type": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/posts?tags=847"
}
],
"curies": [
{
"name": "wp",
"href": "https:\/\/api.w.org\/{rel}",
"templated": true
}
]
}
},
{
"id": 617,
"count": 1,
"description": "",
"link": "https:\/\/blog.mozilla.org\/firefox\/tag\/programming\/",
"name": "programming",
"slug": "programming",
"taxonomy": "post_tag",
"meta": [],
"_links": {
"self": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/tags\/617"
}
],
"collection": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/tags"
}
],
"about": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/taxonomies\/post_tag"
}
],
"wp:post_type": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/posts?tags=617"
}
],
"curies": [
{
"name": "wp",
"href": "https:\/\/api.w.org\/{rel}",
"templated": true
}
]
}
},
{
"id": 16179,
"count": 1,
"description": "",
"link": "https:\/\/blog.mozilla.org\/firefox\/tag\/rust\/",
"name": "rust",
"slug": "rust",
"taxonomy": "post_tag",
"meta": [],
"_links": {
"self": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/tags\/16179"
}
],
"collection": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/tags"
}
],
"about": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/taxonomies\/post_tag"
}
],
"wp:post_type": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/posts?tags=16179"
}
],
"curies": [
{
"name": "wp",
"href": "https:\/\/api.w.org\/{rel}",
"templated": true
}
]
}
},
{
"id": 302197,
"count": 1,
"description": "",
"link": "https:\/\/blog.mozilla.org\/firefox\/tag\/scrolling\/",
"name": "scrolling",
"slug": "scrolling",
"taxonomy": "post_tag",
"meta": [],
"_links": {
"self": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/tags\/302197"
}
],
"collection": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/tags"
}
],
"about": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/taxonomies\/post_tag"
}
],
"wp:post_type": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/posts?tags=302197"
}
],
"curies": [
{
"name": "wp",
"href": "https:\/\/api.w.org\/{rel}",
"templated": true
}
]
}
},
{
"id": 69,
"count": 1,
"description": "",
"link": "https:\/\/blog.mozilla.org\/firefox\/tag\/security\/",
"name": "security",
"slug": "security",
"taxonomy": "post_tag",
"meta": [],
"_links": {
"self": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/tags\/69"
}
],
"collection": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/tags"
}
],
"about": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/taxonomies\/post_tag"
}
],
"wp:post_type": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/posts?tags=69"
}
],
"curies": [
{
"name": "wp",
"href": "https:\/\/api.w.org\/{rel}",
"templated": true
}
]
}
},
{
"id": 301766,
"count": 1,
"description": "",
"link": "https:\/\/blog.mozilla.org\/firefox\/tag\/web-browser\/",
"name": "web browser",
"slug": "web-browser",
"taxonomy": "post_tag",
"meta": [],
"_links": {
"self": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/tags\/301766"
}
],
"collection": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/tags"
}
],
"about": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/taxonomies\/post_tag"
}
],
"wp:post_type": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/posts?tags=301766"
}
],
"curies": [
{
"name": "wp",
"href": "https:\/\/api.w.org\/{rel}",
"templated": true
}
]
}
}
]

Просмотреть файл

@ -0,0 +1,353 @@
[
{
"id": 43,
"count": 1,
"description": "",
"link": "https:\/\/blog.mozilla.org\/firefox\/tag\/browser\/",
"name": "browser",
"slug": "browser",
"taxonomy": "post_tag",
"meta": [],
"_links": {
"self": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/tags\/43"
}
],
"collection": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/tags"
}
],
"about": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/taxonomies\/post_tag"
}
],
"wp:post_type": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/posts?tags=43"
}
],
"curies": [
{
"name": "wp",
"href": "https:\/\/api.w.org\/{rel}",
"templated": true
}
]
}
},
{
"id": 301510,
"count": 3,
"description": "",
"link": "https:\/\/blog.mozilla.org\/firefox\/tag\/fastest\/",
"name": "fastest",
"slug": "fastest",
"taxonomy": "post_tag",
"meta": [],
"_links": {
"self": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/tags\/301510"
}
],
"collection": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/tags"
}
],
"about": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/taxonomies\/post_tag"
}
],
"wp:post_type": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/posts?tags=301510"
}
],
"curies": [
{
"name": "wp",
"href": "https:\/\/api.w.org\/{rel}",
"templated": true
}
]
}
},
{
"id": 302022,
"count": 1,
"description": "",
"link": "https:\/\/blog.mozilla.org\/firefox\/tag\/jank\/",
"name": "jank",
"slug": "jank",
"taxonomy": "post_tag",
"meta": [],
"_links": {
"self": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/tags\/302022"
}
],
"collection": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/tags"
}
],
"about": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/taxonomies\/post_tag"
}
],
"wp:post_type": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/posts?tags=302022"
}
],
"curies": [
{
"name": "wp",
"href": "https:\/\/api.w.org\/{rel}",
"templated": true
}
]
}
},
{
"id": 847,
"count": 1,
"description": "",
"link": "https:\/\/blog.mozilla.org\/firefox\/tag\/privacy\/",
"name": "privacy",
"slug": "privacy",
"taxonomy": "post_tag",
"meta": [],
"_links": {
"self": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/tags\/847"
}
],
"collection": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/tags"
}
],
"about": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/taxonomies\/post_tag"
}
],
"wp:post_type": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/posts?tags=847"
}
],
"curies": [
{
"name": "wp",
"href": "https:\/\/api.w.org\/{rel}",
"templated": true
}
]
}
},
{
"id": 617,
"count": 1,
"description": "",
"link": "https:\/\/blog.mozilla.org\/firefox\/tag\/programming\/",
"name": "programming",
"slug": "programming",
"taxonomy": "post_tag",
"meta": [],
"_links": {
"self": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/tags\/617"
}
],
"collection": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/tags"
}
],
"about": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/taxonomies\/post_tag"
}
],
"wp:post_type": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/posts?tags=617"
}
],
"curies": [
{
"name": "wp",
"href": "https:\/\/api.w.org\/{rel}",
"templated": true
}
]
}
},
{
"id": 16179,
"count": 1,
"description": "",
"link": "https:\/\/blog.mozilla.org\/firefox\/tag\/rust\/",
"name": "rust",
"slug": "rust",
"taxonomy": "post_tag",
"meta": [],
"_links": {
"self": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/tags\/16179"
}
],
"collection": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/tags"
}
],
"about": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/taxonomies\/post_tag"
}
],
"wp:post_type": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/posts?tags=16179"
}
],
"curies": [
{
"name": "wp",
"href": "https:\/\/api.w.org\/{rel}",
"templated": true
}
]
}
},
{
"id": 302197,
"count": 1,
"description": "",
"link": "https:\/\/blog.mozilla.org\/firefox\/tag\/scrolling\/",
"name": "scrolling",
"slug": "scrolling",
"taxonomy": "post_tag",
"meta": [],
"_links": {
"self": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/tags\/302197"
}
],
"collection": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/tags"
}
],
"about": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/taxonomies\/post_tag"
}
],
"wp:post_type": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/posts?tags=302197"
}
],
"curies": [
{
"name": "wp",
"href": "https:\/\/api.w.org\/{rel}",
"templated": true
}
]
}
},
{
"id": 69,
"count": 1,
"description": "",
"link": "https:\/\/blog.mozilla.org\/firefox\/tag\/security\/",
"name": "security",
"slug": "security",
"taxonomy": "post_tag",
"meta": [],
"_links": {
"self": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/tags\/69"
}
],
"collection": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/tags"
}
],
"about": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/taxonomies\/post_tag"
}
],
"wp:post_type": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/posts?tags=69"
}
],
"curies": [
{
"name": "wp",
"href": "https:\/\/api.w.org\/{rel}",
"templated": true
}
]
}
},
{
"id": 301766,
"count": 1,
"description": "",
"link": "https:\/\/blog.mozilla.org\/firefox\/tag\/web-browser\/",
"name": "web browser",
"slug": "web-browser",
"taxonomy": "post_tag",
"meta": [],
"_links": {
"self": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/tags\/301766"
}
],
"collection": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/tags"
}
],
"about": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/taxonomies\/post_tag"
}
],
"wp:post_type": [
{
"href": "https:\/\/blog.mozilla.org\/firefox\/wp-json\/wp\/v2\/posts?tags=301766"
}
],
"curies": [
{
"name": "wp",
"href": "https:\/\/api.w.org\/{rel}",
"templated": true
}
]
}
}
]

Просмотреть файл

@ -0,0 +1,74 @@
from django.test import override_settings
from bedrock.wordpress import api
from bedrock.wordpress.models import BlogPost
import pytest
import responses
from pathlib2 import Path
TEST_DATA = Path(__file__).with_name('test_data')
TEST_WP_BLOGS = {
'firefox': {
'url': 'https://blog.mozilla.org/firefox/',
'name': 'The Firefox Frontier',
'num_posts': 10,
},
}
def get_test_file_content(filename):
with TEST_DATA.joinpath(filename).open() as fh:
return fh.read()
def setup_responses():
posts_url = api._api_url(TEST_WP_BLOGS['firefox']['url'], 'posts', None)
tags_url = api._api_url(TEST_WP_BLOGS['firefox']['url'], 'tags', None)
media_url = api._api_url(TEST_WP_BLOGS['firefox']['url'], 'media', 75)
responses.add(responses.GET, posts_url, body=get_test_file_content('posts.json'))
responses.add(responses.GET, tags_url, body=get_test_file_content('tags.json'))
responses.add(responses.GET, media_url, body=get_test_file_content('media_75.json'))
@responses.activate
@override_settings(WP_BLOGS=TEST_WP_BLOGS)
def test_get_posts_data():
setup_responses()
data = api.get_posts_data('firefox')
assert data['wp_blog_slug'] == 'firefox'
assert data['posts'][0]['tags'] == ['browser', 'fastest']
assert not data['posts'][0]['featured_media']
assert not data['posts'][1]['featured_media']
assert data['posts'][2]['featured_media']['id'] == 75
assert len(responses.calls) == 3
@responses.activate
@override_settings(WP_BLOGS=TEST_WP_BLOGS)
@pytest.mark.django_db
def test_refresh_posts():
setup_responses()
BlogPost.objects.refresh('firefox')
blog = BlogPost.objects.filter_by_blog('firefox')
assert len(blog) == 3
bp = blog.get(wp_id=10)
assert bp.tags == ['browser', 'fastest']
assert bp.wp_blog_slug == 'firefox'
bp = blog.get(wp_id=74)
assert bp.tags == ['fastest', 'privacy', 'programming', 'rust', 'security']
assert bp.featured_media['id'] == 75
assert bp.get_featured_image_url('large').endswith('Put-Your-Trust-in-Rust-600x315.png')
@responses.activate
@override_settings(WP_BLOGS=TEST_WP_BLOGS)
@pytest.mark.django_db
def test_filter_by_tags():
setup_responses()
BlogPost.objects.refresh('firefox')
blog = BlogPost.objects.filter_by_blog('firefox')
assert len(blog.filter_by_tags('browser')) == 1
assert len(blog.filter_by_tags('fastest')) == 3
assert len(blog.filter_by_tags('browser', 'jank')) == 2

Просмотреть файл

@ -0,0 +1,43 @@
from django.test import override_settings, RequestFactory
import pytest
import responses
from bedrock.wordpress.models import BlogPost
from bedrock.wordpress.tests.test_models import setup_responses, TEST_WP_BLOGS
from bedrock.wordpress.views import BlogPostsView
@responses.activate
@override_settings(WP_BLOGS=TEST_WP_BLOGS)
@pytest.mark.django_db
def test_blog_posts_view():
setup_responses()
BlogPost.objects.refresh('firefox')
req = RequestFactory().get('/')
view = BlogPostsView(blog_slug='firefox', blog_tags=['browser', 'jank'],
template_name='mozorg/book.html')
view.request = req
blog_posts = view.get_context_data()['blog_posts']
assert len(blog_posts) == 2
assert blog_posts[0].wp_id == 10
assert blog_posts[1].wp_id == 69
# no tags defined
view = BlogPostsView(blog_slug='firefox', template_name='mozorg/book.html')
view.request = req
blog_posts = view.get_context_data()['blog_posts']
assert len(blog_posts) == 3
# incorrect blog slug
view = BlogPostsView(blog_slug='not-a-blog', template_name='mozorg/book.html')
view.request = req
blog_posts = view.get_context_data()['blog_posts']
assert len(blog_posts) == 0
# no blog defined
view = BlogPostsView(template_name='mozorg/book.html')
view.request = req
blog_posts = view.get_context_data()['blog_posts']
assert len(blog_posts) == 0

Просмотреть файл

@ -0,0 +1,26 @@
from django.views.generic import TemplateView
from bedrock.wordpress.models import BlogPost
from lib.l10n_utils import LangFilesMixin
class BlogPostsMixin(object):
blog_tags = None
blog_slug = None
def get_context_data(self, **kwargs):
ctx = super(BlogPostsMixin, self).get_context_data(**kwargs)
if self.blog_slug:
blog = BlogPost.objects.filter_by_blog(self.blog_slug)
if self.blog_tags:
blog = blog.filter_by_tags(*self.blog_tags)
ctx['blog_posts'] = blog
else:
ctx['blog_posts'] = []
return ctx
class BlogPostsView(BlogPostsMixin, LangFilesMixin, TemplateView):
pass

Просмотреть файл

@ -102,6 +102,7 @@ def schedule_database_jobs():
@scheduled_job('interval', hours=1)
def update_blog_feeds():
call_command('update_blog_feeds --database bedrock')
call_command('update_wordpress --database bedrock')
def schedul_l10n_jobs():

Просмотреть файл

@ -10,6 +10,7 @@ fi
./manage.py cron update_ical_feeds
./manage.py update_product_details_files --database bedrock
./manage.py update_blog_feeds --database bedrock
./manage.py update_wordpress --database bedrock
./manage.py update_externalfiles
./manage.py update_security_advisories
./manage.py l10n_update

Просмотреть файл

@ -38,3 +38,9 @@ funcsigs==1.0.2 \
pbr==1.10.0 \
--hash=sha256:f5cf7265a80636ecff66806d13494cbf9d77a3758a65fd8b4d4d4bee81b0c375 \
--hash=sha256:186428c270309e6fdfe2d5ab0949ab21ae5f7dea831eab96701b86bd666af39c
responses==0.5.1 \
--hash=sha256:3a907f7aae2fd2286d06cfdf238957786c38bbcadc451adceecc769a4ef882b7 \
--hash=sha256:8cad64c45959a651ceaf0023484bd26180c927fea64a81e63d334ddf6377ecea
cookies==2.2.1 \
--hash=sha256:15bee753002dff684987b8df8c235288eb8d45f8191ae056254812dfd42c81d3 \
--hash=sha256:d6b698788cae4cfa4e62ef8643a9ca332b79bd96cb314294b864ae8d7eb3ee8e