We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Default to `default` distribution if client is not part of an experiment.
We want all Firefox clients to get the default bundle for the locale / channel combination, unless they are part of an experiment when using pregen bundles in https://github.com/mozmeao/snippets-service/blob/master/snippets/base/views.py#L63

This is because non-Mozilla distributors of Firefox (e.g. Linux Distributions) override the distribution field with their identification. 

We 'll only use client's distribution if it starts with `experiment-`

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `snippets/base/views.py`
Content:
```
1 import json
2 from urllib.parse import urljoin, urlparse
3 
4 from distutils.util import strtobool
5 from django.conf import settings
6 from django.contrib.auth.decorators import permission_required
7 from django.core.exceptions import ValidationError
8 from django.core.files.storage import default_storage
9 from django.http import Http404, HttpResponse, HttpResponseBadRequest, HttpResponseRedirect
10 from django.shortcuts import get_object_or_404, render
11 from django.utils.functional import lazy
12 from django.views.decorators.cache import cache_control
13 from django.views.decorators.csrf import csrf_exempt
14 from django.views.decorators.http import require_POST
15 from django.views.generic import TemplateView
16 
17 import sentry_sdk
18 from django_filters.views import FilterView
19 from django_statsd.clients import statsd
20 
21 from snippets.base import util
22 from snippets.base.bundles import ASRSnippetBundle, SnippetBundle
23 from snippets.base.decorators import access_control
24 from snippets.base.filters import JobFilter
25 from snippets.base.models import CHANNELS, ASRSnippet, Client, Snippet, SnippetTemplate
26 from snippets.base.util import get_object_or_none
27 
28 
29 def _bundle_timeout():
30     return getattr(settings, 'SNIPPET_BUNDLE_TIMEOUT')
31 SNIPPET_BUNDLE_TIMEOUT = lazy(_bundle_timeout, int)()  # noqa
32 
33 
34 class HomeView(TemplateView):
35     template_name = 'base/home.jinja'
36 
37 
38 class JobListView(FilterView):
39     filterset_class = JobFilter
40 
41     @property
42     def template_name(self):
43         if self.request.GET.get('calendar', 'false') == 'true':
44             return 'base/jobs_list_calendar.jinja'
45 
46         return 'base/jobs_list_table.jinja'
47 
48 
49 def fetch_snippets(request, **kwargs):
50     if settings.USE_PREGEN_BUNDLES and kwargs['startpage_version'] == 6:
51         return fetch_snippet_pregen_bundle(request, **kwargs)
52     return fetch_snippet_bundle(request, **kwargs)
53 
54 
55 @cache_control(public=True, max_age=settings.SNIPPET_BUNDLE_PREGEN_REDIRECT_TIMEOUT)
56 def fetch_snippet_pregen_bundle(request, **kwargs):
57     statsd.incr('serve.bundle_pregen')
58     client = Client(**kwargs)
59     product = 'Firefox'
60     channel = client.channel.lower()
61     channel = next((item for item in CHANNELS if channel.startswith(item)), None) or 'release'
62     locale = client.locale.lower()
63     distribution = client.distribution.lower()
64     filename = (
65         f'{settings.MEDIA_BUNDLES_PREGEN_ROOT}/{product}/{channel}/'
66         f'{locale}/{distribution}.json'
67     )
68 
69     full_url = urljoin(settings.CDN_URL or settings.SITE_URL,
70                        urlparse(default_storage.url(filename)).path)
71     # Remove AWS S3 parameters
72     full_url = full_url.split('?')[0]
73 
74     return HttpResponseRedirect(full_url)
75 
76 
77 @cache_control(public=True, max_age=SNIPPET_BUNDLE_TIMEOUT)
78 @access_control(max_age=SNIPPET_BUNDLE_TIMEOUT)
79 def fetch_snippet_bundle(request, **kwargs):
80     """
81     Return one of the following responses:
82     - 200 with empty body when the bundle is empty
83     - 302 to a bundle URL after generating it if not cached.
84     """
85     statsd.incr('serve.snippets')
86 
87     client = Client(**kwargs)
88     if client.startpage_version == 6:
89         bundle = ASRSnippetBundle(client)
90     else:
91         bundle = SnippetBundle(client)
92     if bundle.empty:
93         statsd.incr('bundle.empty')
94 
95         if client.startpage_version == 6:
96             # Return valid JSON for Activity Stream Router
97             return HttpResponse(status=200, content='{}', content_type='application/json')
98 
99         # This is not a 204 because Activity Stream expects content, even if
100         # it's empty.
101         return HttpResponse(status=200, content='')
102     elif bundle.cached:
103         statsd.incr('bundle.cached')
104     else:
105         statsd.incr('bundle.generate')
106         bundle.generate()
107 
108     return HttpResponseRedirect(bundle.url)
109 
110 
111 def preview_asr_snippet(request, uuid):
112     try:
113         snippet = get_object_or_404(ASRSnippet, uuid=uuid)
114     except ValidationError:
115         # Raised when UUID is a badly formed hexadecimal UUID string
116         raise Http404()
117 
118     bundle_content = json.dumps({
119         'messages': [snippet.render(preview=True)],
120     })
121     return HttpResponse(bundle_content, content_type='application/json')
122 
123 
124 @csrf_exempt
125 @permission_required('base.change_snippet')
126 def preview_snippet(request):
127     """
128     Build a snippet using info from the POST parameters, and preview that
129     snippet on a mock about:home page.
130     """
131     try:
132         template_id = int(request.POST.get('template_id', None))
133     except (TypeError, ValueError):
134         return HttpResponseBadRequest()
135 
136     template = get_object_or_none(SnippetTemplate, id=template_id)
137     data = request.POST.get('data', None)
138 
139     # Validate that data is JSON.
140     try:
141         json.loads(data)
142     except (TypeError, ValueError):
143         data = None
144 
145     # If your parameters are wrong, I have no sympathy for you.
146     if data is None or template is None:
147         return HttpResponseBadRequest()
148 
149     # Build a snippet that isn't saved so we can render it.
150     snippet = Snippet(template=template, data=data)
151 
152     if strtobool(request.POST.get('activity_stream', 'false')):
153         template_name = 'base/preview_as.jinja'
154         preview_client = Client(5, 'Firefox', '57.0', 'default', 'default', 'en-US',
155                                 'release', 'default', 'default', 'default')
156     else:
157         template_name = 'base/preview.jinja'
158         preview_client = Client(4, 'Firefox', '24.0', 'default', 'default', 'en-US',
159                                 'release', 'default', 'default', 'default')
160 
161     skip_boilerplate = request.POST.get('skip_boilerplate', 'false')
162     skip_boilerplate = strtobool(skip_boilerplate)
163     if skip_boilerplate:
164         template_name = 'base/preview_without_shell.jinja'
165 
166     return render(request, template_name, {
167         'snippets_json': json.dumps([snippet.to_dict()]),
168         'client': preview_client,
169         'preview': True,
170         'current_firefox_major_version': util.current_firefox_major_version(),
171     })
172 
173 
174 def show_snippet(request, snippet_id, uuid=False):
175     preview_client = Client(4, 'Firefox', '24.0', 'default', 'default', 'en-US',
176                             'release', 'default', 'default', 'default')
177 
178     if uuid:
179         snippet = get_object_or_404(Snippet, uuid=snippet_id)
180     else:
181         snippet = get_object_or_404(Snippet, pk=snippet_id)
182         if not snippet.published and not request.user.is_authenticated:
183             raise Http404()
184 
185     template = 'base/preview.jinja'
186     if snippet.on_startpage_5:
187         template = 'base/preview_as.jinja'
188     return render(request, template, {
189         'snippets_json': json.dumps([snippet.to_dict()]),
190         'client': preview_client,
191         'preview': True,
192         'current_firefox_major_version': util.current_firefox_major_version(),
193     })
194 
195 
196 @csrf_exempt
197 @require_POST
198 def csp_violation_capture(request):
199     try:
200         csp_data = json.loads(request.body)
201     except ValueError:
202         # Cannot decode CSP violation data, ignore
203         return HttpResponseBadRequest('Invalid CSP Report')
204 
205     try:
206         blocked_uri = csp_data['csp-report']['blocked-uri']
207     except KeyError:
208         # Incomplete CSP report
209         return HttpResponseBadRequest('Incomplete CSP Report')
210 
211     with sentry_sdk.configure_scope() as scope:
212         scope.level = 'info'
213         scope.set_tag('logger', 'csp')
214 
215         sentry_sdk.capture_message(
216             message='CSP Violation: {}'.format(blocked_uri))
217 
218     return HttpResponse('Captured CSP violation, thanks for reporting.')
219
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/snippets/base/views.py b/snippets/base/views.py
--- a/snippets/base/views.py
+++ b/snippets/base/views.py
@@ -60,7 +60,20 @@
     channel = client.channel.lower()
     channel = next((item for item in CHANNELS if channel.startswith(item)), None) or 'release'
     locale = client.locale.lower()
+
+    # Distribution populated by client's distribution if it starts with
+    # `experiment-`. Otherwise default to `default`.
+    #
+    # This is because non-Mozilla distributors of Firefox (e.g. Linux
+    # Distributions) override the distribution field with their identification.
+    # We want all Firefox clients to get the default bundle for the locale /
+    # channel combination, unless they are part of an experiment.
     distribution = client.distribution.lower()
+    if distribution.startswith('experiment-'):
+        distribution = distribution[11:]
+    else:
+        distribution = 'default'
+
     filename = (
         f'{settings.MEDIA_BUNDLES_PREGEN_ROOT}/{product}/{channel}/'
         f'{locale}/{distribution}.json'

{"golden_diff": "diff --git a/snippets/base/views.py b/snippets/base/views.py\n--- a/snippets/base/views.py\n+++ b/snippets/base/views.py\n@@ -60,7 +60,20 @@\n     channel = client.channel.lower()\n     channel = next((item for item in CHANNELS if channel.startswith(item)), None) or 'release'\n     locale = client.locale.lower()\n+\n+    # Distribution populated by client's distribution if it starts with\n+    # `experiment-`. Otherwise default to `default`.\n+    #\n+    # This is because non-Mozilla distributors of Firefox (e.g. Linux\n+    # Distributions) override the distribution field with their identification.\n+    # We want all Firefox clients to get the default bundle for the locale /\n+    # channel combination, unless they are part of an experiment.\n     distribution = client.distribution.lower()\n+    if distribution.startswith('experiment-'):\n+        distribution = distribution[11:]\n+    else:\n+        distribution = 'default'\n+\n     filename = (\n         f'{settings.MEDIA_BUNDLES_PREGEN_ROOT}/{product}/{channel}/'\n         f'{locale}/{distribution}.json'\n", "issue": "Default to `default` distribution if client is not part of an experiment.\nWe want all Firefox clients to get the default bundle for the locale / channel combination, unless they are part of an experiment when using pregen bundles in https://github.com/mozmeao/snippets-service/blob/master/snippets/base/views.py#L63\r\n\r\nThis is because non-Mozilla distributors of Firefox (e.g. Linux Distributions) override the distribution field with their identification. \r\n\r\nWe 'll only use client's distribution if it starts with `experiment-`\n", "before_files": [{"content": "import json\nfrom urllib.parse import urljoin, urlparse\n\nfrom distutils.util import strtobool\nfrom django.conf import settings\nfrom django.contrib.auth.decorators import permission_required\nfrom django.core.exceptions import ValidationError\nfrom django.core.files.storage import default_storage\nfrom django.http import Http404, HttpResponse, HttpResponseBadRequest, HttpResponseRedirect\nfrom django.shortcuts import get_object_or_404, render\nfrom django.utils.functional import lazy\nfrom django.views.decorators.cache import cache_control\nfrom django.views.decorators.csrf import csrf_exempt\nfrom django.views.decorators.http import require_POST\nfrom django.views.generic import TemplateView\n\nimport sentry_sdk\nfrom django_filters.views import FilterView\nfrom django_statsd.clients import statsd\n\nfrom snippets.base import util\nfrom snippets.base.bundles import ASRSnippetBundle, SnippetBundle\nfrom snippets.base.decorators import access_control\nfrom snippets.base.filters import JobFilter\nfrom snippets.base.models import CHANNELS, ASRSnippet, Client, Snippet, SnippetTemplate\nfrom snippets.base.util import get_object_or_none\n\n\ndef _bundle_timeout():\n    return getattr(settings, 'SNIPPET_BUNDLE_TIMEOUT')\nSNIPPET_BUNDLE_TIMEOUT = lazy(_bundle_timeout, int)()  # noqa\n\n\nclass HomeView(TemplateView):\n    template_name = 'base/home.jinja'\n\n\nclass JobListView(FilterView):\n    filterset_class = JobFilter\n\n    @property\n    def template_name(self):\n        if self.request.GET.get('calendar', 'false') == 'true':\n            return 'base/jobs_list_calendar.jinja'\n\n        return 'base/jobs_list_table.jinja'\n\n\ndef fetch_snippets(request, **kwargs):\n    if settings.USE_PREGEN_BUNDLES and kwargs['startpage_version'] == 6:\n        return fetch_snippet_pregen_bundle(request, **kwargs)\n    return fetch_snippet_bundle(request, **kwargs)\n\n\n@cache_control(public=True, max_age=settings.SNIPPET_BUNDLE_PREGEN_REDIRECT_TIMEOUT)\ndef fetch_snippet_pregen_bundle(request, **kwargs):\n    statsd.incr('serve.bundle_pregen')\n    client = Client(**kwargs)\n    product = 'Firefox'\n    channel = client.channel.lower()\n    channel = next((item for item in CHANNELS if channel.startswith(item)), None) or 'release'\n    locale = client.locale.lower()\n    distribution = client.distribution.lower()\n    filename = (\n        f'{settings.MEDIA_BUNDLES_PREGEN_ROOT}/{product}/{channel}/'\n        f'{locale}/{distribution}.json'\n    )\n\n    full_url = urljoin(settings.CDN_URL or settings.SITE_URL,\n                       urlparse(default_storage.url(filename)).path)\n    # Remove AWS S3 parameters\n    full_url = full_url.split('?')[0]\n\n    return HttpResponseRedirect(full_url)\n\n\n@cache_control(public=True, max_age=SNIPPET_BUNDLE_TIMEOUT)\n@access_control(max_age=SNIPPET_BUNDLE_TIMEOUT)\ndef fetch_snippet_bundle(request, **kwargs):\n    \"\"\"\n    Return one of the following responses:\n    - 200 with empty body when the bundle is empty\n    - 302 to a bundle URL after generating it if not cached.\n    \"\"\"\n    statsd.incr('serve.snippets')\n\n    client = Client(**kwargs)\n    if client.startpage_version == 6:\n        bundle = ASRSnippetBundle(client)\n    else:\n        bundle = SnippetBundle(client)\n    if bundle.empty:\n        statsd.incr('bundle.empty')\n\n        if client.startpage_version == 6:\n            # Return valid JSON for Activity Stream Router\n            return HttpResponse(status=200, content='{}', content_type='application/json')\n\n        # This is not a 204 because Activity Stream expects content, even if\n        # it's empty.\n        return HttpResponse(status=200, content='')\n    elif bundle.cached:\n        statsd.incr('bundle.cached')\n    else:\n        statsd.incr('bundle.generate')\n        bundle.generate()\n\n    return HttpResponseRedirect(bundle.url)\n\n\ndef preview_asr_snippet(request, uuid):\n    try:\n        snippet = get_object_or_404(ASRSnippet, uuid=uuid)\n    except ValidationError:\n        # Raised when UUID is a badly formed hexadecimal UUID string\n        raise Http404()\n\n    bundle_content = json.dumps({\n        'messages': [snippet.render(preview=True)],\n    })\n    return HttpResponse(bundle_content, content_type='application/json')\n\n\n@csrf_exempt\n@permission_required('base.change_snippet')\ndef preview_snippet(request):\n    \"\"\"\n    Build a snippet using info from the POST parameters, and preview that\n    snippet on a mock about:home page.\n    \"\"\"\n    try:\n        template_id = int(request.POST.get('template_id', None))\n    except (TypeError, ValueError):\n        return HttpResponseBadRequest()\n\n    template = get_object_or_none(SnippetTemplate, id=template_id)\n    data = request.POST.get('data', None)\n\n    # Validate that data is JSON.\n    try:\n        json.loads(data)\n    except (TypeError, ValueError):\n        data = None\n\n    # If your parameters are wrong, I have no sympathy for you.\n    if data is None or template is None:\n        return HttpResponseBadRequest()\n\n    # Build a snippet that isn't saved so we can render it.\n    snippet = Snippet(template=template, data=data)\n\n    if strtobool(request.POST.get('activity_stream', 'false')):\n        template_name = 'base/preview_as.jinja'\n        preview_client = Client(5, 'Firefox', '57.0', 'default', 'default', 'en-US',\n                                'release', 'default', 'default', 'default')\n    else:\n        template_name = 'base/preview.jinja'\n        preview_client = Client(4, 'Firefox', '24.0', 'default', 'default', 'en-US',\n                                'release', 'default', 'default', 'default')\n\n    skip_boilerplate = request.POST.get('skip_boilerplate', 'false')\n    skip_boilerplate = strtobool(skip_boilerplate)\n    if skip_boilerplate:\n        template_name = 'base/preview_without_shell.jinja'\n\n    return render(request, template_name, {\n        'snippets_json': json.dumps([snippet.to_dict()]),\n        'client': preview_client,\n        'preview': True,\n        'current_firefox_major_version': util.current_firefox_major_version(),\n    })\n\n\ndef show_snippet(request, snippet_id, uuid=False):\n    preview_client = Client(4, 'Firefox', '24.0', 'default', 'default', 'en-US',\n                            'release', 'default', 'default', 'default')\n\n    if uuid:\n        snippet = get_object_or_404(Snippet, uuid=snippet_id)\n    else:\n        snippet = get_object_or_404(Snippet, pk=snippet_id)\n        if not snippet.published and not request.user.is_authenticated:\n            raise Http404()\n\n    template = 'base/preview.jinja'\n    if snippet.on_startpage_5:\n        template = 'base/preview_as.jinja'\n    return render(request, template, {\n        'snippets_json': json.dumps([snippet.to_dict()]),\n        'client': preview_client,\n        'preview': True,\n        'current_firefox_major_version': util.current_firefox_major_version(),\n    })\n\n\n@csrf_exempt\n@require_POST\ndef csp_violation_capture(request):\n    try:\n        csp_data = json.loads(request.body)\n    except ValueError:\n        # Cannot decode CSP violation data, ignore\n        return HttpResponseBadRequest('Invalid CSP Report')\n\n    try:\n        blocked_uri = csp_data['csp-report']['blocked-uri']\n    except KeyError:\n        # Incomplete CSP report\n        return HttpResponseBadRequest('Incomplete CSP Report')\n\n    with sentry_sdk.configure_scope() as scope:\n        scope.level = 'info'\n        scope.set_tag('logger', 'csp')\n\n        sentry_sdk.capture_message(\n            message='CSP Violation: {}'.format(blocked_uri))\n\n    return HttpResponse('Captured CSP violation, thanks for reporting.')\n", "path": "snippets/base/views.py"}], "after_files": [{"content": "import json\nfrom urllib.parse import urljoin, urlparse\n\nfrom distutils.util import strtobool\nfrom django.conf import settings\nfrom django.contrib.auth.decorators import permission_required\nfrom django.core.exceptions import ValidationError\nfrom django.core.files.storage import default_storage\nfrom django.http import Http404, HttpResponse, HttpResponseBadRequest, HttpResponseRedirect\nfrom django.shortcuts import get_object_or_404, render\nfrom django.utils.functional import lazy\nfrom django.views.decorators.cache import cache_control\nfrom django.views.decorators.csrf import csrf_exempt\nfrom django.views.decorators.http import require_POST\nfrom django.views.generic import TemplateView\n\nimport sentry_sdk\nfrom django_filters.views import FilterView\nfrom django_statsd.clients import statsd\n\nfrom snippets.base import util\nfrom snippets.base.bundles import ASRSnippetBundle, SnippetBundle\nfrom snippets.base.decorators import access_control\nfrom snippets.base.filters import JobFilter\nfrom snippets.base.models import CHANNELS, ASRSnippet, Client, Snippet, SnippetTemplate\nfrom snippets.base.util import get_object_or_none\n\n\ndef _bundle_timeout():\n    return getattr(settings, 'SNIPPET_BUNDLE_TIMEOUT')\nSNIPPET_BUNDLE_TIMEOUT = lazy(_bundle_timeout, int)()  # noqa\n\n\nclass HomeView(TemplateView):\n    template_name = 'base/home.jinja'\n\n\nclass JobListView(FilterView):\n    filterset_class = JobFilter\n\n    @property\n    def template_name(self):\n        if self.request.GET.get('calendar', 'false') == 'true':\n            return 'base/jobs_list_calendar.jinja'\n\n        return 'base/jobs_list_table.jinja'\n\n\ndef fetch_snippets(request, **kwargs):\n    if settings.USE_PREGEN_BUNDLES and kwargs['startpage_version'] == 6:\n        return fetch_snippet_pregen_bundle(request, **kwargs)\n    return fetch_snippet_bundle(request, **kwargs)\n\n\n@cache_control(public=True, max_age=settings.SNIPPET_BUNDLE_PREGEN_REDIRECT_TIMEOUT)\ndef fetch_snippet_pregen_bundle(request, **kwargs):\n    statsd.incr('serve.bundle_pregen')\n    client = Client(**kwargs)\n    product = 'Firefox'\n    channel = client.channel.lower()\n    channel = next((item for item in CHANNELS if channel.startswith(item)), None) or 'release'\n    locale = client.locale.lower()\n\n    # Distribution populated by client's distribution if it starts with\n    # `experiment-`. Otherwise default to `default`.\n    #\n    # This is because non-Mozilla distributors of Firefox (e.g. Linux\n    # Distributions) override the distribution field with their identification.\n    # We want all Firefox clients to get the default bundle for the locale /\n    # channel combination, unless they are part of an experiment.\n    distribution = client.distribution.lower()\n    if distribution.startswith('experiment-'):\n        distribution = distribution[11:]\n    else:\n        distribution = 'default'\n\n    filename = (\n        f'{settings.MEDIA_BUNDLES_PREGEN_ROOT}/{product}/{channel}/'\n        f'{locale}/{distribution}.json'\n    )\n\n    full_url = urljoin(settings.CDN_URL or settings.SITE_URL,\n                       urlparse(default_storage.url(filename)).path)\n    # Remove AWS S3 parameters\n    full_url = full_url.split('?')[0]\n\n    return HttpResponseRedirect(full_url)\n\n\n@cache_control(public=True, max_age=SNIPPET_BUNDLE_TIMEOUT)\n@access_control(max_age=SNIPPET_BUNDLE_TIMEOUT)\ndef fetch_snippet_bundle(request, **kwargs):\n    \"\"\"\n    Return one of the following responses:\n    - 200 with empty body when the bundle is empty\n    - 302 to a bundle URL after generating it if not cached.\n    \"\"\"\n    statsd.incr('serve.snippets')\n\n    client = Client(**kwargs)\n    if client.startpage_version == 6:\n        bundle = ASRSnippetBundle(client)\n    else:\n        bundle = SnippetBundle(client)\n    if bundle.empty:\n        statsd.incr('bundle.empty')\n\n        if client.startpage_version == 6:\n            # Return valid JSON for Activity Stream Router\n            return HttpResponse(status=200, content='{}', content_type='application/json')\n\n        # This is not a 204 because Activity Stream expects content, even if\n        # it's empty.\n        return HttpResponse(status=200, content='')\n    elif bundle.cached:\n        statsd.incr('bundle.cached')\n    else:\n        statsd.incr('bundle.generate')\n        bundle.generate()\n\n    return HttpResponseRedirect(bundle.url)\n\n\ndef preview_asr_snippet(request, uuid):\n    try:\n        snippet = get_object_or_404(ASRSnippet, uuid=uuid)\n    except ValidationError:\n        # Raised when UUID is a badly formed hexadecimal UUID string\n        raise Http404()\n\n    bundle_content = json.dumps({\n        'messages': [snippet.render(preview=True)],\n    })\n    return HttpResponse(bundle_content, content_type='application/json')\n\n\n@csrf_exempt\n@permission_required('base.change_snippet')\ndef preview_snippet(request):\n    \"\"\"\n    Build a snippet using info from the POST parameters, and preview that\n    snippet on a mock about:home page.\n    \"\"\"\n    try:\n        template_id = int(request.POST.get('template_id', None))\n    except (TypeError, ValueError):\n        return HttpResponseBadRequest()\n\n    template = get_object_or_none(SnippetTemplate, id=template_id)\n    data = request.POST.get('data', None)\n\n    # Validate that data is JSON.\n    try:\n        json.loads(data)\n    except (TypeError, ValueError):\n        data = None\n\n    # If your parameters are wrong, I have no sympathy for you.\n    if data is None or template is None:\n        return HttpResponseBadRequest()\n\n    # Build a snippet that isn't saved so we can render it.\n    snippet = Snippet(template=template, data=data)\n\n    if strtobool(request.POST.get('activity_stream', 'false')):\n        template_name = 'base/preview_as.jinja'\n        preview_client = Client(5, 'Firefox', '57.0', 'default', 'default', 'en-US',\n                                'release', 'default', 'default', 'default')\n    else:\n        template_name = 'base/preview.jinja'\n        preview_client = Client(4, 'Firefox', '24.0', 'default', 'default', 'en-US',\n                                'release', 'default', 'default', 'default')\n\n    skip_boilerplate = request.POST.get('skip_boilerplate', 'false')\n    skip_boilerplate = strtobool(skip_boilerplate)\n    if skip_boilerplate:\n        template_name = 'base/preview_without_shell.jinja'\n\n    return render(request, template_name, {\n        'snippets_json': json.dumps([snippet.to_dict()]),\n        'client': preview_client,\n        'preview': True,\n        'current_firefox_major_version': util.current_firefox_major_version(),\n    })\n\n\ndef show_snippet(request, snippet_id, uuid=False):\n    preview_client = Client(4, 'Firefox', '24.0', 'default', 'default', 'en-US',\n                            'release', 'default', 'default', 'default')\n\n    if uuid:\n        snippet = get_object_or_404(Snippet, uuid=snippet_id)\n    else:\n        snippet = get_object_or_404(Snippet, pk=snippet_id)\n        if not snippet.published and not request.user.is_authenticated:\n            raise Http404()\n\n    template = 'base/preview.jinja'\n    if snippet.on_startpage_5:\n        template = 'base/preview_as.jinja'\n    return render(request, template, {\n        'snippets_json': json.dumps([snippet.to_dict()]),\n        'client': preview_client,\n        'preview': True,\n        'current_firefox_major_version': util.current_firefox_major_version(),\n    })\n\n\n@csrf_exempt\n@require_POST\ndef csp_violation_capture(request):\n    try:\n        csp_data = json.loads(request.body)\n    except ValueError:\n        # Cannot decode CSP violation data, ignore\n        return HttpResponseBadRequest('Invalid CSP Report')\n\n    try:\n        blocked_uri = csp_data['csp-report']['blocked-uri']\n    except KeyError:\n        # Incomplete CSP report\n        return HttpResponseBadRequest('Incomplete CSP Report')\n\n    with sentry_sdk.configure_scope() as scope:\n        scope.level = 'info'\n        scope.set_tag('logger', 'csp')\n\n        sentry_sdk.capture_message(\n            message='CSP Violation: {}'.format(blocked_uri))\n\n    return HttpResponse('Captured CSP violation, thanks for reporting.')\n", "path": "snippets/base/views.py"}]}