Merge branch 'master' of gitlab.com:lilschool/site into feature/ecommerce-gtag

remotes/origin/feature/ecommerce-gtag
gzbender 6 years ago
commit 90088c8ca2
  1. 2
      apps/auth/forms.py
  2. 4
      apps/auth/views.py
  3. 24
      apps/content/views.py
  4. 30
      apps/notification/tasks.py
  5. 27
      apps/notification/templates/notification/email/certificate.html
  6. 2
      apps/school/views.py
  7. 19
      apps/user/migrations/0035_auto_20191002_1857.py
  8. 5
      apps/user/models.py
  9. 2
      project/settings.py
  10. 23
      project/templates/lilcity/index.html
  11. 88
      project/templates/lilcity/layer.html
  12. 4
      project/urls.py
  13. BIN
      resources/signed-user-certificates/1.jpg
  14. BIN
      resources/signed-user-certificates/2.jpg
  15. BIN
      resources/signed-user-certificates/3.jpg
  16. BIN
      resources/signed-user-certificates/4.jpg
  17. BIN
      resources/signed-user-certificates/5.jpg
  18. BIN
      resources/signed-user-certificates/6.jpg
  19. BIN
      resources/signed-user-certificates/7.jpg
  20. BIN
      resources/signed-user-certificates/8.jpg
  21. BIN
      resources/signed-user-certificates/9.jpg
  22. BIN
      web/src/img/signed-user-certificates/1.jpg
  23. BIN
      web/src/img/signed-user-certificates/2.jpg
  24. BIN
      web/src/img/signed-user-certificates/3.jpg
  25. BIN
      web/src/img/signed-user-certificates/4.jpg
  26. BIN
      web/src/img/signed-user-certificates/5.jpg
  27. BIN
      web/src/img/signed-user-certificates/6.jpg
  28. BIN
      web/src/img/signed-user-certificates/7.jpg
  29. BIN
      web/src/img/signed-user-certificates/8.jpg
  30. BIN
      web/src/img/signed-user-certificates/9.jpg
  31. BIN
      web/src/img/user-certificates/1.jpg
  32. BIN
      web/src/img/user-certificates/2.jpg
  33. BIN
      web/src/img/user-certificates/3.jpg
  34. BIN
      web/src/img/user-certificates/4.jpg
  35. BIN
      web/src/img/user-certificates/5.jpg
  36. BIN
      web/src/img/user-certificates/6.jpg
  37. BIN
      web/src/img/user-certificates/7.jpg
  38. BIN
      web/src/img/user-certificates/8.jpg
  39. BIN
      web/src/img/user-certificates/9.jpg
  40. 16
      web/webpack.config.js

@ -12,4 +12,4 @@ class LearnerRegistrationForm(forms.Form):
class LandingRegistrationForm(forms.Form):
name = forms.CharField(required=True)
email = forms.EmailField(required=True)
phone = PhoneNumberField(required=True)
phone = forms.CharField(required=True)

@ -255,13 +255,13 @@ class LandingRegistrationView(View):
name = form.cleaned_data['name'].split()
email = form.cleaned_data['email'].lower()
if User.objects.filter(Q(email=email) | Q(phone=phone)).count():
if User.objects.filter(email=email).count():
return redirect('/p/user-exists')
user = User(
username=email,
email=email,
phone=phone,
phone2=phone,
)
user.first_name = name[0]
if len(name) > 1:

@ -1,5 +1,8 @@
import os
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.http import JsonResponse
from django.http import JsonResponse, HttpResponse
from django.shortcuts import get_object_or_404
from django.template import loader
from django.utils.decorators import method_decorator
@ -9,6 +12,7 @@ from django.views.generic import TemplateView, DetailView
from apps.content.models import Contest, ContestWork, FAQ
from apps.course.models import ContestWorkComment
from apps.user.models import Child
@method_decorator(login_required, name='dispatch')
@ -124,3 +128,21 @@ class FAQView(TemplateView):
} for f in FAQ.objects.all()]
return self.render_to_response(context)
from apps.notification.tasks import draw_cert
@csrf_exempt
def get_certificate(request, cert):
signed_path_pattern = 'signed-user-certificates/%d.jpg'
fn = signed_path_pattern % cert
path = os.path.join(settings.RESOURCES_ROOT, fn)
child_id = request.GET.get('child')
if child_id:
child = get_object_or_404(Child, pk=child_id)
path = draw_cert(path, child.user.email, child.first_name, child.last_name)
f = open(path, 'rb')
response = HttpResponse(f, content_type='image/jpeg')
response['Content-Disposition'] = 'attachment; filename=%s' % fn
f.close()
if child_id:
os.remove(path)
return response

@ -50,10 +50,9 @@ def draw_cert(path, email, first_name, last_name, x=None, y=900, color=(29, 115,
@app.task
def send_certificates(email=None, date_end=None, dry_run=False):
path_pattern = 'img/user-certificates/%d.jpg'
signed_path_pattern = 'img/signed-user-certificates/%d.jpg'
signed_path_pattern = 'signed-user-certificates/%d.jpg'
if email:
fn = draw_cert(staticfiles_storage.path(signed_path_pattern % 1), email, 'Имя', 'Фамилия')
fn = draw_cert(os.path.join(settings.RESOURCES_ROOT, signed_path_pattern % 1), email, 'Имя', 'Фамилия')
file = open(fn, 'rb')
try:
send_email('Грамота от Lil School', email, 'notification/email/certificate.html',
@ -83,30 +82,19 @@ def send_certificates(email=None, date_end=None, dry_run=False):
if dry_run:
continue
file_names = []
un.certificate_number = un.certificate_number + 1 \
if un.certificate_number and staticfiles_storage.exists(path_pattern % (un.certificate_number + 1)) \
if un.certificate_number and os.path.isfile(os.path.join(settings.RESOURCES_ROOT,
signed_path_pattern % (un.certificate_number + 1))) \
else 1
if un.user.child_filled:
fn = staticfiles_storage.path(signed_path_pattern % un.certificate_number)
for child in un.user.childs.all():
file_names.append(draw_cert(fn, un.user.email, child.first_name, child.last_name))
else:
file_names.append(staticfiles_storage.path(path_pattern % un.certificate_number))
files = [open(fn, 'rb') for fn in file_names]
un.save()
try:
send_email('Грамота от Lil School', un.user.email, 'notification/email/certificate.html',
attachments=[(f.name, f.read(), 'image/jpeg') for f in files], user_notification=un,
many_childs=un.user.childs.all().count() > 1)
except:
childs=un.user.childs.all(), user_notification=un,
many_childs=un.user.childs.all().count() > 1, child_filled=un.user.child_filled)
except Exception as e:
print('Not OK')
print(e)
continue
finally:
for f in files:
f.close()
if un.user.child_filled:
for fn in file_names:
os.remove(fn)
un.certificate_last_email = now()
un.save()

@ -1,4 +1,5 @@
{% extends "notification/email/_base.html" %}
{% load settings %}
{% block content %}
<p style="margin: 0 0 20px">Привет!</p>
@ -6,7 +7,8 @@
{% if not user_notification or user_notification.certificate_number == 1 %}
<p>
Поздравляем! Вы прошли месяц обучения в Lil School.<br>
К письму {% if many_childs %}прикреплены грамоты. Распечатайте их{% else %}прикреплена грамота. Распечатайте её{% endif %} и вручите вашим детям.<br>
В письме {% if many_childs %}ссылки на грамоты. Распечатайте их и вручите вашим детям.
{% else %}ссылка на грамоту. Распечатайте её{% if not child_filled %}, подпишите{% endif %} и вручите вашему ребенку.{% endif %}<br>
Ждём вас в следующем месяце на наших творческих занятиях!
</p>
{% endif %}
@ -19,7 +21,7 @@
{% if user_notification and user_notification.certificate_number == 3 %}
<p>
Вам понравился наш творческий месяц?<br>
В письме вы найдёте {% if many_childs %}грамоты, они{% else %}грамоту, она{% endif %} для вашей семьи.<br>
В письме вы найдёте {% if many_childs %}ссылки на грамоты, они{% else %}ссылку на грамоту, она{% endif %} для вашей семьи.<br>
Как здорово, что у нас есть такие ученики!<br>
Ждём вас в следующем месяце.
</p>
@ -27,17 +29,17 @@
{% if user_notification and user_notification.certificate_number == 4 %}
<p>
Прошёл целый месяц обучения на платформе Lil School - месяц творчества, креатива и невероятных идей.<br>
Во вложении вас {% if many_childs %}ждут грамоты{% else %}ждёт грамота{% endif %}.<br>
Во письме вас {% if many_childs %}ждут ссылки на грамоты{% else %}ждёт ссылка на грамоту{% endif %}.<br>
До встречи на занятиях!
</p>
{% endif %}
{% if user_notification and user_notification.certificate_number == 5 %}
<p>
{% if many_childs %}
Ваши грамоты за успехи в учебе в Lil School ждут вас во вложении.<br>
Ваши грамоты за успехи в учебе в Lil School ждут вас.<br>
Скорее распечатайте их!<br>
{% else %}
Ваша грамота за успехи в учебе в Lil School ждёт вас во вложении.<br>
Ваша грамота за успехи в учебе в Lil School ждёт вас.<br>
Скорее распечатайте её!<br>
{% endif %}
Вам есть чем гордится!<br>
@ -55,7 +57,7 @@
{% if user_notification and user_notification.certificate_number == 7 %}
<p>
Какой классный месяц у нас был! Вместе мы очень здорово и креативно потрудились.<br>
Во вложении вас {% if many_childs %}ждут заслуженные грамоты{% else %}ждёт заслуженная грамота{% endif %}!<br>
В письме вас {% if many_childs %}ждут ссылки на заслуженные грамоты{% else %}ждёт ссылка на заслуженную грамоту{% endif %}!<br>
До встречи на уроках!
</p>
{% endif %}
@ -63,10 +65,21 @@
<p>
Месяц творчества и креатива пролетел как один миг! А как много работ мы вместе сделали!<br>
Вы - большие молодцы.<br>
Во вложении вас {% if many_childs %}ждут грамоты{% else %}ждёт грамота{% endif %}!<br>
В письме вас {% if many_childs %}ждут ссылки на грамоты{% else %}ждёт ссылка на грамоту{% endif %}!<br>
До встречи на занятиях.
</p>
{% endif %}
<p>
{% if many_childs %}
Ссылки на грамоты:<br>
{% for child in childs %}
<a href="{% setting 'MAIN_HOST' %}{% url 'get-certificate' cert=user_notification.certificate_number %}?child={{ child.id }}">Грамота ({{ child.full_name }})</a><br>
{% endfor %}
{% else %}
Ссылка на грамоту:<br>
<a href="{% setting 'MAIN_HOST' %}{% url 'get-certificate' cert=user_notification.certificate_number %}{% if child_filled %}?child={{ childs.0.id }}{% endif %}">Скачать грамоту</a>
{% endif %}
</p>
</div>
<div style="margin-bottom: 10px;">
<p>

@ -114,7 +114,7 @@ class LiveLessonsDetailView(DetailView):
user=request.user,
date_start__lte=now(),
date_end__gte=now() - timedelta(days=30),
package__duration__lte=9,
package__duration=12,
).exists()
if not is_purchased and request.user.role not in [User.ADMIN_ROLE, User.TEACHER_ROLE]:
raise Http404

@ -0,0 +1,19 @@
# Generated by Django 2.0.7 on 2019-10-02 18:57
from django.db import migrations, models
import project.utils.db
class Migration(migrations.Migration):
dependencies = [
('user', '0034_auto_20190612_1852'),
]
operations = [
migrations.AddField(
model_name='user',
name='phone2',
field=models.CharField(blank=True, max_length=20, null=True),
),
]

@ -59,6 +59,7 @@ class User(AbstractUser):
)
email = models.EmailField(_('email address'), unique=True)
phone = PhoneNumberField(null=True, blank=True, unique=True)
phone2 = models.CharField(max_length=20, null=True, blank=True)
role = models.PositiveSmallIntegerField(
'Роль', default=0, choices=ROLE_CHOICES)
gender = models.CharField(
@ -379,3 +380,7 @@ class Child(models.Model):
class Meta:
ordering = ('id',)
@property
def full_name(self):
return ' '.join([self.first_name, self.last_name]) if self.last_name else self.first_name

@ -329,7 +329,7 @@ CELERY_BEAT_SCHEDULE = {
},
'send_certificates': {
'task': 'apps.notification.tasks.send_certificates',
'schedule': crontab(minute=0, hour=19),
'schedule': crontab(minute=00, hour=20),
'args': (),
},
'sendgrid_update_recipients': {

@ -33,35 +33,12 @@
{% include 'templates/blocks/lil_store_js.html' %}
{% block pre_app_js %}{% endblock pre_app_js %}
<script type="text/javascript" src="{% static "app.js" %}"></script>
<script>
var schoolDiscount = parseFloat({{ config.SERVICE_DISCOUNT }});
var schoolAmountForDiscount = parseFloat({{ config.SERVICE_DISCOUNT_MIN_AMOUNT }});
</script>
{% comment %} ROISTAT {% endcomment %}
<script>
(function(w, d, s, h, id) { w.roistatProjectId = id; w.roistatHost = h; var p = d.location.protocol == "https:" ? "https://" : "http://"; var u = /^.*roistat_visit=[^;]+(.*)?$/.test(d.cookie) ? "/dist/module.js" : "/api/site/1.0/"+id+"/init"; var js = d.createElement(s); js.charset="UTF-8"; js.async = 1; js.src = p+h+u; var js2 = d.getElementsByTagName(s)[0]; js2.parentNode.insertBefore(js, js2);})(window, document, 'script', 'cloud.roistat.com', '{% setting "ROISTAT_COUNTER_ID" %}');
</script>
<script src="https://js.pusher.com/4.1/pusher.min.js"></script>
<script>
LIL_SERVER_TIME = "{% now 'U' %}";
LIL_SERVER_TIME_DIFF = Math.floor((new Date().getTime()) / 1000) - parseInt(LIL_SERVER_TIME);
USER_ID = "{{ request.user.id }}";
COURSE_ID = "{{ course.id }}";
MIXPANEL_CUSTOM_LIB_URL = "/static/mixpanel-2-latest.js";
</script>
<!-- Start of LiveChat (www.livechatinc.com) code -->
<script type="text/javascript">
window.__lc = window.__lc || {};
window.__lc.license = 9917240;
{% if request.user.is_authenticated %}
window.__lc.visitor = { name: '{{ request.user.get_full_name }}', email: '{{ request.user.email }}' };
{% endif %}
(function() {
var lc = document.createElement('script'); lc.type = 'text/javascript'; lc.async = true;
lc.src = ('https:' == document.location.protocol ? 'https://' : 'http://') + 'cdn.livechatinc.com/tracking.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(lc, s);
})();
</script>
<!-- End of LiveChat code -->
{% block foot_js %}{% endblock foot_js %}
{% endblock layer_body %}

@ -47,77 +47,39 @@
}
}
</script>
{% if settings.DEV_SERVER or settings.DEBUG %}
<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl+ '&gtm_auth=vlrscZ1PfuTiK5kaBcQ-Ag&gtm_preview=env-5&gtm_cookies_win=x';f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-WGMCNS7');</script>
<!-- End Google Tag Manager -->
{% else %}
<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl+ '&gtm_auth=Wg_pncigcHbhPKKVOVrvPw&gtm_preview=env-2&gtm_cookies_win=x';f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-WGMCNS7');</script>
<!-- End Google Tag Manager -->
{% endif %}
{% block layer_head %}{% endblock layer_head %}
</head>
<body {% block body_attr %}{% endblock body_attr %}>
<!-- Facebook Pixel Code -->
<script>
!function(f,b,e,v,n,t,s)
{if(f.fbq)return;n=f.fbq=function(){n.callMethod?
n.callMethod.apply(n,arguments):n.queue.push(arguments)};
if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
n.queue=[];t=b.createElement(e);t.async=!0;
t.src=v;s=b.getElementsByTagName(e)[0];
s.parentNode.insertBefore(t,s)}(window,document,'script',
'https://connect.facebook.net/en_US/fbevents.js');
fbq('init', '194961257900508');
fbq('track', 'PageView');
</script>
<noscript>
<img height="1" width="1"
src="https://www.facebook.com/tr?id=194961257900508&ev=PageView
&noscript=1"/>
</noscript>
<!-- End Facebook Pixel Code -->
<!-- Global site tag (gtag.js) - Google Ads: 808701460 -->
{% if settings.DEV_SERVER or settings.DEBUG %}
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-121923960-4"></script>
<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-WGMCNS7&gtm_auth=vlrscZ1PfuTiK5kaBcQ-Ag&gtm_preview=env-5&gtm_cookies_win=x"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->
{% else %}
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-121923960-1"></script>
<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-WGMCNS7&gtm_auth=Wg_pncigcHbhPKKVOVrvPw&gtm_preview=env-2&gtm_cookies_win=x"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->
{% endif %}
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
//gtag('config', 'AW-808701460');
{% if settings.DEV_SERVER or settings.DEBUG %}
gtag('config', 'UA-121923960-4');
{% else %}
gtag('config', 'UA-121923960-1');
{% endif %}
</script>
<!-- Yandex.Metrika counter -->
<script type="text/javascript" >
(function (d, w, c) {
(w[c] = w[c] || []).push(function() {
try {
w.yaCounter49354039 = new Ya.Metrika2({
id:49354039,
clickmap:true,
trackLinks:true,
accurateTrackBounce:true,
webvisor:true
});
} catch(e) { }
});
var n = d.getElementsByTagName("script")[0],
s = d.createElement("script"),
f = function () { n.parentNode.insertBefore(s, n); };
s.type = "text/javascript";
s.async = true;
s.src = "https://mc.yandex.ru/metrika/tag.js";
if (w.opera == "[object Opera]") {
d.addEventListener("DOMContentLoaded", f, false);
} else { f(); }
})(document, window, "yandex_metrika_callbacks2");
</script>
<noscript><div><img src="https://mc.yandex.ru/watch/49354039" style="position:absolute; left:-9999px;" alt="" /></div></noscript>
<!-- /Yandex.Metrika counter -->
{% include "templates/blocks/mixpanel.html" %}
{% block layer_body %}
{% endblock layer_body %}
</body>

@ -19,7 +19,8 @@ from django.views.generic import TemplateView
from django.urls import path, include
from apps.auth.views import LandingRegistrationView
from apps.content.views import ContestEditView, ContestView, ContestWorkView, contest_work_comment, FAQView
from apps.content.views import ContestEditView, ContestView, ContestWorkView, contest_work_comment, FAQView, \
get_certificate
from apps.course.views import (
CoursesView, likes, coursecomment,
CourseView, LessonView, SearchView,
@ -106,6 +107,7 @@ urlpatterns = [
path('links', LinksView.as_view(), name='links'),
path('prices', PackagesView.as_view(), name='packages'),
path('landing-registration', LandingRegistrationView.as_view(), name='landing-registration'),
path('certificate/<int:cert>/get', get_certificate, name='get-certificate'),
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 987 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 941 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

@ -21,7 +21,6 @@ module.exports = {
mixpanel: "./src/js/third_party/mixpanel-2-latest.js",
sprite: glob('./src/icons/*.svg'),
images: glob('./src/img/*.*'),
imagesCertificates: glob('./src/img/user-certificates/*'),
imagesGiftCertificates: glob('./src/img/gift-certificates/*'),
imagesReviews: glob('./src/img/reviews/*'),
fonts: glob('./src/fonts/*'),
@ -94,21 +93,10 @@ module.exports = {
},
{
test: /\.(png|gif|jpg|svg)$/,
exclude: [path.resolve(__dirname, 'src/icons'), path.resolve(__dirname, 'src/img/user-certificates'),
path.resolve(__dirname, 'src/img/gift-certificates'), path.resolve(__dirname, 'src/img/reviews'),
path.resolve(__dirname, 'src/img/signed-user-certificates')],
exclude: [path.resolve(__dirname, 'src/icons'),
path.resolve(__dirname, 'src/img/gift-certificates'), path.resolve(__dirname, 'src/img/reviews')],
loader: 'file-loader?name=[name].[ext]&outputPath=./img/'
},
{
test: /\.(png|jpg)$/,
include: path.resolve(__dirname, 'src/img/user-certificates'),
loader: 'file-loader?name=[name].[ext]&outputPath=./img/user-certificates/'
},
{
test: /\.(png|jpg)$/,
include: path.resolve(__dirname, 'src/img/signed-user-certificates'),
loader: 'file-loader?name=[name].[ext]&outputPath=./img/signed-user-certificates/'
},
{
test: /\.(png|jpg)$/,
include: path.resolve(__dirname, 'src/img/gift-certificates'),

Loading…
Cancel
Save