From 2fc3a0274c3757bebc8d37fa2d000d630368fe3a Mon Sep 17 00:00:00 2001 From: Alice Date: Tue, 22 Oct 2019 21:28:31 +0200 Subject: [PATCH] Better frequent questions, tiny css improvements --- ccvpn/settings.py | 20 ++++ ccvpn/urls.py | 4 + kb/__init__.py | 0 kb/admin.py | 48 ++++++++ kb/apps.py | 5 + kb/migrations/0001_initial.py | 93 ++++++++++++++++ kb/migrations/__init__.py | 0 kb/models.py | 136 +++++++++++++++++++++++ kb/tests.py | 3 + kb/urls.py | 13 +++ kb/views.py | 42 +++++++ locale/fr/LC_MESSAGES/django.po | 126 +++++++++++---------- poetry.lock | 33 +++++- pyproject.toml | 2 + static/css/style.css | 86 +++++++++++++- templates/base_help.html | 73 ++++++++++++ templates/ccvpn/page.html | 35 +----- templates/kb/kb_category.html | 19 ++++ templates/kb/kb_index.html | 28 +++++ templates/kb/kb_item.html | 24 ++++ templates/pages/advanced-tor.md | 39 ++++--- templates/pages/faq.en.md | 139 ----------------------- templates/pages/faq.fr.md | 148 ------------------------- templates/pages/help.en.md | 11 +- templates/pages/help.fr.md | 10 +- templates/pages/install-gnulinux.en.md | 6 +- templates/pages/install-gnulinux.fr.md | 6 +- templates/pages/install-windows.en.md | 6 +- templates/pages/install-windows.fr.md | 6 +- templates/pages/privacy.en.md | 35 +++--- templates/pages/self-diagnosis.en.md | 26 ++--- templates/pages/self-diagnosis.fr.md | 26 ++--- templates/tickets/layout.html | 53 ++++++--- templates/tickets/list.html | 2 +- templates/tickets/new.html | 2 +- templates/tickets/view.html | 4 +- 36 files changed, 816 insertions(+), 493 deletions(-) create mode 100644 kb/__init__.py create mode 100644 kb/admin.py create mode 100644 kb/apps.py create mode 100644 kb/migrations/0001_initial.py create mode 100644 kb/migrations/__init__.py create mode 100644 kb/models.py create mode 100644 kb/tests.py create mode 100644 kb/urls.py create mode 100644 kb/views.py create mode 100644 templates/base_help.html create mode 100644 templates/kb/kb_category.html create mode 100644 templates/kb/kb_index.html create mode 100644 templates/kb/kb_item.html delete mode 100644 templates/pages/faq.en.md delete mode 100644 templates/pages/faq.fr.md diff --git a/ccvpn/settings.py b/ccvpn/settings.py index 6c1f353..99bd92d 100644 --- a/ccvpn/settings.py +++ b/ccvpn/settings.py @@ -40,13 +40,16 @@ INSTALLED_APPS = ( 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'django.contrib.humanize', 'django_countries', 'lambdainst', 'payments', 'tickets', 'downloads', + 'kb', 'constance', 'constance.backends.database', + 'tinymce', ) MIDDLEWARE = ( @@ -274,6 +277,7 @@ CONSTANCE_CONFIG = { 'TRIAL_PERIOD_MAX': (84, "Maximum number of trial periods to give (84*2h=1w)"), 'NOTIFY_DAYS_BEFORE': ("3, 1", "When to send account expiration notifications. In number of days before, separated y commas", 'integer_list'), + 'KB_MAX_TOP_ENTRIES': (10, "Maximum number of categories to show in the short list"), } CONSTANCE_ADDITIONAL_FIELDS = { @@ -282,6 +286,22 @@ CONSTANCE_ADDITIONAL_FIELDS = { }], } +TINYMCE_DEFAULT_CONFIG = { + 'selector': 'textarea', + 'theme': 'modern', + 'plugins': 'link image preview codesample table code lists paste', + 'toolbar1': 'bold italic underline | alignleft aligncenter alignright alignjustify ' + '| bullist numlist | outdent indent | table | link image | codesample | preview code', + 'contextmenu': 'formats | link image', + 'menubar': False, + 'inline': False, + 'statusbar': True, + 'height': 360, + 'width': 'auto', + 'paste_as_text': True, + 'browser_spellcheck': True, +} + # Local settings try: diff --git a/ccvpn/urls.py b/ccvpn/urls.py index aae0c57..ae1ab24 100644 --- a/ccvpn/urls.py +++ b/ccvpn/urls.py @@ -1,6 +1,7 @@ from django.urls import path, include from django.contrib import admin from django.contrib.auth import views as auth_views +from django.views.generic.base import RedirectView from lambdainst import views as account_views @@ -18,6 +19,7 @@ urlpatterns = [ path('ca.crt', account_views.ca_crt), path('setlang', views.set_lang, name='set_lang'), path('chat', views.chat, name='chat'), + path('page/faq', RedirectView.as_view(url='/kb/')), path('page/', views.page, name='page'), path('status', account_views.status), @@ -30,8 +32,10 @@ urlpatterns = [ path('account/reset/done/', auth_views.PasswordResetCompleteView.as_view(), name='password_reset_complete'), + path('tinymce/', include('tinymce.urls')), path('account/', include('lambdainst.urls', namespace='account')), path('payments/', include('payments.urls', namespace='payments')), path('tickets/', include('tickets.urls', namespace='tickets')), + path('kb/', include('kb.urls', namespace='kb')), ] diff --git a/kb/__init__.py b/kb/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/kb/admin.py b/kb/admin.py new file mode 100644 index 0000000..c2c41ca --- /dev/null +++ b/kb/admin.py @@ -0,0 +1,48 @@ +from django.contrib import admin +from django_admin_listfilter_dropdown.filters import DropdownFilter + +from .models import KbCategory, KbCategoryName, KbCategoryDescription +from .models import KbEntry, KbEntryAnswer, KbEntryQuestion + + +def custom_titled_filter(title, c=admin.FieldListFilter): + class Wrapper(c): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.title = title + return Wrapper + + +class KbCategoryNameInline(admin.TabularInline): + model = KbCategoryName + extra = 0 +class KbCategoryDescriptionInline(admin.TabularInline): + model = KbCategoryDescription + extra = 0 + +class KbCategoryAdmin(admin.ModelAdmin): + list_display = ('slug', 'name', 'entries') + inlines = (KbCategoryNameInline, KbCategoryDescriptionInline) + + def entries(self, instance): + return instance.entry_set.count() + +class KbEntryQuestionInline(admin.TabularInline): + model = KbEntryQuestion + extra = 0 +class KbEntryAnswerInline(admin.StackedInline): + model = KbEntryAnswer + extra = 0 + +class KbEntryAdmin(admin.ModelAdmin): + list_display = ('category', 'title', 'question', 'score', 'internal') + list_filter = ('category', 'internal') + search_fields = ('category', 'title', 'question', 'answer') + inlines = (KbEntryQuestionInline, KbEntryAnswerInline) + + def score(self, instance): + return "%d (%d votes)" % (instance.get_score(), instance.get_vote_count()) + + +admin.site.register(KbCategory, KbCategoryAdmin) +admin.site.register(KbEntry, KbEntryAdmin) diff --git a/kb/apps.py b/kb/apps.py new file mode 100644 index 0000000..93d7469 --- /dev/null +++ b/kb/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class HelpConfig(AppConfig): + name = 'help' diff --git a/kb/migrations/0001_initial.py b/kb/migrations/0001_initial.py new file mode 100644 index 0000000..35623b6 --- /dev/null +++ b/kb/migrations/0001_initial.py @@ -0,0 +1,93 @@ +# Generated by Django 2.2.1 on 2019-10-22 13:15 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import tinymce.models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='KbCategory', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('slug', models.SlugField()), + ('internal_name', models.CharField(max_length=100, verbose_name='Internal name')), + ], + options={ + 'verbose_name': 'Knowledgebase Category', + 'verbose_name_plural': 'Knowledgebase Categories', + }, + ), + migrations.CreateModel( + name='KbEntry', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('create_date', models.DateTimeField(auto_now_add=True)), + ('title', models.CharField(max_length=50)), + ('internal', models.BooleanField(default=False)), + ('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='entry_set', to='kb.KbCategory')), + ], + options={ + 'verbose_name': 'Knowledgebase Entry', + 'verbose_name_plural': 'Knowledgebase Entries', + }, + ), + migrations.CreateModel( + name='KbEntryVote', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('create_date', models.DateTimeField(auto_now_add=True)), + ('vote', models.IntegerField()), + ('entry', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='vote_set', to='kb.KbEntry')), + ('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), + ], + options={ + 'verbose_name': 'Knowledgebase Entry Vote', + }, + ), + migrations.CreateModel( + name='KbEntryQuestion', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('language', models.CharField(max_length=2)), + ('question', models.CharField(max_length=200)), + ('entry', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='question_set', to='kb.KbEntry')), + ], + ), + migrations.CreateModel( + name='KbEntryAnswer', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('language', models.CharField(max_length=2)), + ('answer', tinymce.models.HTMLField()), + ('entry', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='answer_set', to='kb.KbEntry')), + ], + ), + migrations.CreateModel( + name='KbCategoryName', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('language', models.CharField(max_length=2)), + ('name', models.CharField(max_length=100)), + ('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='name_set', to='kb.KbCategory')), + ], + ), + migrations.CreateModel( + name='KbCategoryDescription', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('language', models.CharField(max_length=2)), + ('description', models.TextField()), + ('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='description_set', to='kb.KbCategory')), + ], + ), + ] diff --git a/kb/migrations/__init__.py b/kb/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/kb/models.py b/kb/models.py new file mode 100644 index 0000000..0ed8f04 --- /dev/null +++ b/kb/models.py @@ -0,0 +1,136 @@ +from django.db import models +from django.db.models.aggregates import Sum +from django.conf import settings +from django.urls import reverse +from django.template.loader import render_to_string +from django.dispatch import receiver +from django.utils.translation import get_language +from constance import config +from tinymce import HTMLField + +def get_internationalized(obj_set, prop_name): + obj = obj_set.filter(language=get_language()).first() + if obj: + return getattr(obj, prop_name) + return "" + + +class KbCategory(models.Model): + slug = models.SlugField() + internal_name = models.CharField("Internal name", max_length=100) + + def get_absolute_url(self): + return reverse('help:kb_category', kwargs=dict(category=self.slug)) + + def get_top_entries(self, n=None): + n = n or config.KB_MAX_TOP_ENTRIES + return KbEntry.objects.get_top(n, category=self) + + def get_entry_count(self): + return self.entry_set.filter(internal=False).count() + + def __str__(self): + return self.name + + @property + def name(self): + return get_internationalized(self.name_set, 'name') + @property + def description(self): + return get_internationalized(self.description_set, 'description') + + class Meta: + verbose_name = "Knowledgebase Category" + verbose_name_plural = "Knowledgebase Categories" + +class KbCategoryName(models.Model): + category = models.ForeignKey(KbCategory, on_delete=models.CASCADE, + related_name='name_set') + language = models.CharField(max_length=2) + name = models.CharField(max_length=100) + +class KbCategoryDescription(models.Model): + category = models.ForeignKey(KbCategory, on_delete=models.CASCADE, + related_name='description_set') + language = models.CharField(max_length=2) + description = models.TextField() + + +class KbEntry(models.Model): + create_date = models.DateTimeField(auto_now_add=True, editable=False) + category = models.ForeignKey(KbCategory, on_delete=models.CASCADE, + related_name='entry_set') + title = models.CharField(max_length=50) + internal = models.BooleanField(default=False) + + def vote(self, user, v): + vote = self.vote_set.filter(user=user).first() + if vote: + if vote.vote == v: + return + vote.vote = v + vote.save() + else: + v = KbEntryVote(entry=self, user=user, vote=v) + v.save() + + def get_score(self): + return self.vote_set.aggregate(Sum('vote'))['vote__sum'] or 0 + + def get_vote_count(self): + return self.vote_set.count() + + def get_absolute_url(self): + return reverse('help:kb_entry', kwargs=dict(category=self.category.slug, entry=self.id)) + + + @property + def question(self): + return self.question_set.filter(language=get_language()).first().question + @property + def answer(self): + return self.answer_set.filter(language=get_language()).first().answer + + def __str__(self): + return self.question + + class Meta: + verbose_name = "Knowledgebase Entry" + verbose_name_plural = "Knowledgebase Entries" + + class KbEntryManager(models.Manager): + def get_top(self, n, **kwargs): + return (self.filter(internal=False, **kwargs) + .annotate(Sum('vote_set__vote')) + .order_by('-vote_set__vote__sum') + [:n]) + + objects = KbEntryManager() + + +class KbEntryQuestion(models.Model): + entry = models.ForeignKey(KbEntry, on_delete=models.CASCADE, + related_name='question_set') + language = models.CharField(max_length=2) + question = models.CharField(max_length=200) + +class KbEntryAnswer(models.Model): + entry = models.ForeignKey(KbEntry, on_delete=models.CASCADE, + related_name='answer_set') + language = models.CharField(max_length=2) + answer = HTMLField() + +class KbEntryVote(models.Model): + entry = models.ForeignKey(KbEntry, on_delete=models.CASCADE, + related_name='vote_set') + user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, + null=True) + create_date = models.DateTimeField(auto_now_add=True, editable=False) + vote = models.IntegerField() + + def __str__(self): + return 'Vote %+d on %s by %s' % (self.vote, self.entry, self.user) + + class Meta: + verbose_name = "Knowledgebase Entry Vote" + diff --git a/kb/tests.py b/kb/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/kb/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/kb/urls.py b/kb/urls.py new file mode 100644 index 0000000..f054a33 --- /dev/null +++ b/kb/urls.py @@ -0,0 +1,13 @@ +from django.urls import path + +from . import views + +app_name = 'help' + +urlpatterns = [ + path('', views.kb_index, name='kb_index'), + path('/', views.kb_category, name='kb_category'), + path('//', views.kb_entry, name='kb_entry'), + path('//feedback/', views.kb_feedback, name='kb_feedback'), +] + diff --git a/kb/views.py b/kb/views.py new file mode 100644 index 0000000..91fb9bf --- /dev/null +++ b/kb/views.py @@ -0,0 +1,42 @@ +from django.shortcuts import render, get_object_or_404, redirect +from django.contrib.auth.decorators import login_required +from django.contrib import messages +from django.http import Http404 +from django import forms +from django.forms import inlineformset_factory +from django.utils.translation import get_language + +from .models import KbCategory, KbEntry + + +def kb_index(request): + return render(request, 'kb/kb_index.html', { + 'categories': KbCategory.objects.all(), + }) + + +def kb_category(request, category): + category = get_object_or_404(KbCategory, slug=category) + return render(request, 'kb/kb_category.html', { + 'category': category, + 'items': list(KbEntry.objects.filter(category=category)), + }) + + +def kb_entry(request, category, entry): + entry = get_object_or_404(KbEntry, category__slug=category, pk=entry, internal=False) + return render(request, 'kb/kb_item.html', { + 'item': entry, + }) + + +@login_required +def kb_feedback(request, category, entry): + entry = get_object_or_404(KbEntry, category__slug=category, pk=entry, internal=False) + arg = request.GET.get('vote') + if arg == 'up': + entry.vote(request.user, 1) + elif arg == 'down': + entry.vote(request.user, -1) + return redirect(entry.get_absolute_url()) + diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 8cffafd..26dfce3 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-09-07 21:02+0000\n" +"POT-Creation-Date: 2019-10-22 19:16+0000\n" "PO-Revision-Date: 2016-04-07 01:32+0000\n" "Last-Translator: \n" "Language-Team: \n" @@ -27,7 +27,7 @@ msgstr "" msgid "hash" msgstr "" -#: ccvpn/views.py:40 templates/tickets/layout.html:11 +#: ccvpn/views.py:37 msgid "Live Chat" msgstr "Chat Live" @@ -130,11 +130,11 @@ msgstr "Utilisateur de codes" msgid "Gift Code Users" msgstr "Utilisateurs de codes" -#: lambdainst/openvpn.py:10 templates/ccvpn/page.html:17 +#: lambdainst/openvpn.py:10 templates/base_help.html:42 msgid "Windows" msgstr "Windows" -#: lambdainst/openvpn.py:11 templates/ccvpn/page.html:16 +#: lambdainst/openvpn.py:11 templates/base_help.html:38 msgid "Android" msgstr "Android" @@ -142,7 +142,7 @@ msgstr "Android" msgid "Ubuntu" msgstr "Ubuntu" -#: lambdainst/openvpn.py:13 templates/ccvpn/page.html:19 +#: lambdainst/openvpn.py:13 templates/base_help.html:50 msgid "OS X" msgstr "OS X" @@ -150,7 +150,7 @@ msgstr "OS X" msgid "iOS" msgstr "iOS" -#: lambdainst/openvpn.py:15 templates/ccvpn/page.html:20 +#: lambdainst/openvpn.py:15 templates/base_help.html:54 msgid "Chrome OS" msgstr "Chrome OS" @@ -334,21 +334,15 @@ msgid "Confirming transaction" msgstr "Confirmation de la transaction" #: payments/backends/coinpayments.py:169 -#, fuzzy -#| msgid "Payments" msgid "CoinPayments" -msgstr "Paiements" +msgstr "CoinPayments" #: payments/backends/coinpayments.py:198 -#, fuzzy -#| msgid "" -#| "Waiting for PayPal to confirm the transaction... It can take up to a few " -#| "minutes..." msgid "" "Waiting for CoinPayments to confirm the transaction... It can take up to a " "few minutes..." msgstr "" -"En attente de la confirmation par Paypal... Cette étape peut durer quelques " +"En attente de la confirmation par CoinPayments... Cette étape peut durer quelques " "minutes..." #: payments/backends/paypal.py:14 payments/backends/paypal.py:15 @@ -472,6 +466,43 @@ msgstr "" msgid "Unknown content" msgstr "" +#: templates/base_help.html:8 +msgid "Help" +msgstr "Aide" + +#: templates/base_help.html:13 templates/layout.html:55 +msgid "Guides" +msgstr "Guides" + +#: templates/base_help.html:19 templates/kb/kb_category.html:4 +#: templates/kb/kb_index.html:4 +msgid "Knowledge Base" +msgstr "F.A.Q." + +#: templates/base_help.html:25 +msgid "Self-Diagnosis" +msgstr "Auto-Diagnostic" + +#: templates/base_help.html:30 +msgid "Privacy" +msgstr "Vie Privée" + +#: templates/base_help.html:34 +msgid "Installation" +msgstr "Installation" + +#: templates/base_help.html:46 +msgid "GNU/Linux" +msgstr "GNU/Linux" + +#: templates/base_help.html:58 +msgid "Advanced" +msgstr "Avancé" + +#: templates/base_help.html:62 +msgid "Tor" +msgstr "Tor" + #: templates/ccvpn/chat.html:9 #, python-format msgid "" @@ -593,26 +624,6 @@ msgstr "" "Profitez de l'IPv6 même sur les réseaux qui ne supportent pas ou mal " "l'IPv6." -#: templates/ccvpn/page.html:8 -msgid "Help" -msgstr "Aide" - -#: templates/ccvpn/page.html:10 -msgid "Frequently Asked Questions" -msgstr "Questions fréquemment posées" - -#: templates/ccvpn/page.html:11 -msgid "Self-Diagnosis" -msgstr "Auto-Diagnostic" - -#: templates/ccvpn/page.html:14 -msgid "Installation" -msgstr "Installation" - -#: templates/ccvpn/page.html:18 -msgid "GNU/Linux" -msgstr "GNU/Linux" - #: templates/ccvpn/require_email.html:8 msgid "E-mail Confirmation" msgstr "Confirmation de l'adresse e-mail" @@ -664,6 +675,22 @@ msgstr "Mot de passe oublié ?" msgid "Need help?" msgstr "Besoin d'aide ?" +#: templates/kb/kb_index.html:7 +msgid "" +"Please check to see if any of these answers address your problem prior to " +"opening a support ticket." +msgstr "" +"Veuillez vérifier si une des questions suivantes résolvent votre problème " +"avant d'ouvrir un ticket." + +#: templates/kb/kb_index.html:20 +msgid "View all questions" +msgstr "Voir toutes les questions" + +#: templates/kb/kb_item.html:13 +msgid "Did you find this answer useful?" +msgstr "Avez-vous trouvé cette réponse utile ?" + #: templates/lambdainst/account.html:23 #, python-format msgid "" @@ -984,10 +1011,6 @@ msgstr "Se connecter" msgid "VPN" msgstr "VPN" -#: templates/layout.html:55 -msgid "Guides" -msgstr "Guides" - #: templates/layout.html:57 templates/tickets/index.html:6 #: templates/tickets/layout.html:8 tickets/models.py:13 msgid "Support" @@ -1130,19 +1153,23 @@ msgstr "Sujet" msgid "New Ticket" msgstr "Nouveau ticket" -#: templates/tickets/layout.html:14 +#: templates/tickets/layout.html:19 msgid "Open Tickets" msgstr "Tickets ouverts" -#: templates/tickets/layout.html:16 +#: templates/tickets/layout.html:26 msgid "Closed Tickets" msgstr "Tickets fermés" -#: templates/tickets/layout.html:18 +#: templates/tickets/layout.html:31 +msgid "Staff" +msgstr "" + +#: templates/tickets/layout.html:36 msgid "All Open" msgstr "Tous ouverts" -#: templates/tickets/layout.html:20 +#: templates/tickets/layout.html:43 msgid "All Closed" msgstr "Tous fermés" @@ -1266,18 +1293,3 @@ msgstr "Peut envoyer des messages privés" #: tickets/models.py:55 msgid "Waiting for staff" msgstr "En attente du support" - -#~ msgid "" -#~ "Error subscribing. It usually means you don't have enough money available." -#~ msgstr "" -#~ "Il y a une erreur à l'inscription. C'est le plus souvent un manque de " -#~ "fonds sur la carte." - -#~ msgid "Subscribed!" -#~ msgstr "Abonné!" - -#~ msgid "You can cancel it from PayPal account." -#~ msgstr "Vous pouvez annuler depuis votre compte PayPal." - -#~ msgid "Do you really want to cancel your subscription?" -#~ msgstr "Voulez-vous vraimer annuler votre abonnement ?" diff --git a/poetry.lock b/poetry.lock index 118e8ec..27fa9e8 100644 --- a/poetry.lock +++ b/poetry.lock @@ -49,6 +49,14 @@ version = "2.2.1" pytz = "*" sqlparse = "*" +[[package]] +category = "main" +description = "Use dropdowns in Django admin list filter" +name = "django-admin-list-filter-dropdown" +optional = false +python-versions = "*" +version = "1.0.3" + [[package]] category = "main" description = "Django live settings with pluggable backends, including Redis." @@ -91,6 +99,18 @@ version = "2.0" [package.dependencies] Django = ">=1.11" +[[package]] +category = "main" +description = "A Django application that provides a fully functional TinyMCE 4 editor widget for models and forms." +name = "django-tinymce4-lite" +optional = false +python-versions = "*" +version = "1.7.5" + +[package.dependencies] +Django = ">=1.8.0" +jsmin = "*" + [[package]] category = "main" description = "Internationalized Domain Names in Applications (IDNA)" @@ -107,6 +127,14 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "4.3.19" +[[package]] +category = "main" +description = "JavaScript minifier." +name = "jsmin" +optional = false +python-versions = "*" +version = "2.2.2" + [[package]] category = "dev" description = "A fast and thorough lazy object proxy." @@ -293,7 +321,7 @@ python-versions = "*" version = "1.11.1" [metadata] -content-hash = "5a877ca66b221fad6e7837be828b4474efe123d30feb664c6b11bb38edc4ce07" +content-hash = "c00450bd56aa5a669d833686086c1839e5f67dd1ed271fa7a1898d88d6a9ae10" python-versions = "^3.5" [metadata.hashes] @@ -302,12 +330,15 @@ certifi = ["59b7658e26ca9c7339e00f8f4636cdfe59d34fa37b9b04f6f9e9926b3cece1a5", " chardet = ["84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", "fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"] colorama = ["05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d", "f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48"] django = ["6fcc3cbd55b16f9a01f37de8bcbe286e0ea22e87096557f1511051780338eaea", "bb407d0bb46395ca1241f829f5bd03f7e482f97f7d1936e26e98dacb201ed4ec"] +django-admin-list-filter-dropdown = ["07cd37b6a9be1b08f11d4a92957c69b67bc70b1f87a2a7d4ae886c93ea51eb53", "bf1b48bab9772dad79db71efef17e78782d4f2421444d5e49bb10e0da71cd6bb"] django-constance = ["19ff1ae8295aeffc2917ab67da4b310bfaf8c42f34d570f89e289fd54c4217b0", "417f9866a4fcd93c198acd16d5bc22b68e491eaabb18efea4c70d183d42daa45"] django-countries = ["5307a61172eee5740720e44ea08721858b7d8bf8509ec7701ccd7a8d21120b9a", "e4eaaec9bddb9365365109f833d1fd0ecc0cfee3348bf5441c0ccefb2d6917cd"] django-jsonfield = ["af7938ebf0eb945d29648d80bad4845973f1ab1d9fea67f65a963753e913e271", "f357e196565daa503e914b6bdbeeaa104288a5e3b3f982a928a8de46f54e55ea"] django-picklefield = ["9052f2dcf4882c683ce87b4356f29b4d014c0dad645b6906baf9f09571f52bc8", "f1733a8db1b6046c0d7d738e785f9875aa3c198215de11993463a9339aa4ea24"] +django-tinymce4-lite = ["e6776bc5b2c7237705fea18668574bc1c4dff36babc90c99a2bb7b5d636eb5e8", "f0958117ddacc72596e80746729e02a727264413ab54b799f3b697a44e054e87"] idna = ["c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", "ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"] isort = ["49293e2ff590cc8d48bc1f51970548b5b102bf038439ca1af77f352164725628", "ba69a4be8474be11720636bc2f0cf66f7054d417d4c1dbc1dfe504bb8e739541"] +jsmin = ["b6df99b2cd1c75d9d342e4335b535789b8da9107ec748212706ef7bbe5c2553b"] lazy-object-proxy = ["159a745e61422217881c4de71f9eafd9d703b93af95618635849fe469a283661", "23f63c0821cc96a23332e45dfaa83266feff8adc72b9bcaef86c202af765244f", "3b11be575475db2e8a6e11215f5aa95b9ec14de658628776e10d96fa0b4dac13", "3f447aff8bc61ca8b42b73304f6a44fa0d915487de144652816f950a3f1ab821", "4ba73f6089cd9b9478bc0a4fa807b47dbdb8fad1d8f31a0f0a5dbf26a4527a71", "4f53eadd9932055eac465bd3ca1bd610e4d7141e1278012bd1f28646aebc1d0e", "64483bd7154580158ea90de5b8e5e6fc29a16a9b4db24f10193f0c1ae3f9d1ea", "6f72d42b0d04bfee2397aa1862262654b56922c20a9bb66bb76b6f0e5e4f9229", "7c7f1ec07b227bdc561299fa2328e85000f90179a2f44ea30579d38e037cb3d4", "7c8b1ba1e15c10b13cad4171cfa77f5bb5ec2580abc5a353907780805ebe158e", "8559b94b823f85342e10d3d9ca4ba5478168e1ac5658a8a2f18c991ba9c52c20", "a262c7dfb046f00e12a2bdd1bafaed2408114a89ac414b0af8755c696eb3fc16", "acce4e3267610c4fdb6632b3886fe3f2f7dd641158a843cf6b6a68e4ce81477b", "be089bb6b83fac7f29d357b2dc4cf2b8eb8d98fe9d9ff89f9ea6012970a853c7", "bfab710d859c779f273cc48fb86af38d6e9210f38287df0069a63e40b45a2f5c", "c10d29019927301d524a22ced72706380de7cfc50f767217485a912b4c8bd82a", "dd6e2b598849b3d7aee2295ac765a578879830fb8966f70be8cd472e6069932e", "e408f1eacc0a68fed0c08da45f31d0ebb38079f043328dce69ff133b95c29dc1"] lcoreapi = [] markdown = ["fc4a6f69a656b8d858d7503bda633f4dd63c2d70cf80abdc6eafa64c4ae8c250", "fe463ff51e679377e3624984c829022e2cfb3be5518726b06f608a07a3aad680"] diff --git a/pyproject.toml b/pyproject.toml index f065d8e..440aac0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,6 +20,8 @@ lcoreapi = {git = "https://git.packetimpact.net/lvpn/lcoreapi.git"} pygments = "^2.3" psycopg2-binary = "^2.8" python-frontmatter = "^0.4.5" +django-tinymce4-lite = "^1.7" +django-admin-list-filter-dropdown = "^1.0" [tool.poetry.dev-dependencies] pylint = "^2.3" diff --git a/static/css/style.css b/static/css/style.css index 2587e06..813d3fb 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -19,11 +19,10 @@ html, button, input, select, textarea, } a{ - color: #1c619a; + color: #0b2d4f; } em { - color: #9A691C; font-style: normal; font-weight: bold; } @@ -175,8 +174,8 @@ header nav a{ font-weight: 600; } .left-menu ul { - list-style-type: disc; - padding: 0.5em 1em 0.5em 30px; + list-style-type: none; + padding: 0.5em 1em 0.5em 1em; margin: 0; } .left-menu li { @@ -186,7 +185,7 @@ header nav a{ text-decoration: none; } -.left-menu + .content { +.left-menu + .content{ margin-left: 200px; } @@ -217,6 +216,83 @@ header nav a{ padding-left: 2em; } +.flex-page { + display: flex; +} +.flex-page .left-menu { + display: block; + float: none; + width: 200px; + position: relative; + margin-left: 1em; + flex-shrink: 0; +} + +.content-box { + margin: 1em; + border: 1px solid #bbb; + padding: 2em 2em; + background: #fff; + overflow: auto; + flex-grow: 2; + +-webkit-box-shadow: 1px 1px 2px 1px rgba(0,0,0,0.21); +-moz-box-shadow: 1px 1px 2px 1px rgba(0,0,0,0.21); +box-shadow: 1px 1px 2px 1px rgba(0,0,0,0.21); +} + +.content-box h2 { + margin-top: 0; + font-size: 1.5em; + font-weight: bold; + color: #1c619a; +} +.content-box h3 { + font-weight: bold; + font-size: 1.2em; + margin: 0.5em 0 0.25em 0; +} +.content-box h4 { + font-weight: bold; + font-size: 1.0em; + margin: 1em 0 0.25em 2em; +} +.content-box ul { + margin: 0.25em 0 0.5em 0; +} +.content-box ul { + list-style-type: circle; +} + +.content-box hr { + height: 0; + border: 0; + border-top: 1px solid #ccc; + padding: 0; + margin: 1.5em 0; +} + + +/*.kb-question-list { + list-style-type: circle; +} +.kb-question-list a { + font-size: 1.2em; +}*/ +.kb-question-meta { +} +.kb-question-meta .kb-vote-buttons { + padding-left: 0.5em; +} +.kb-vote-buttons button { + padding: 0 0.4em 0.2em 0.4em; + border: 1px solid #ccc; + border-radius: 3px; +} +.kb-question-meta p { + margin: 0 0; +} + footer{ padding-top: 4em; diff --git a/templates/base_help.html b/templates/base_help.html new file mode 100644 index 0000000..a82561b --- /dev/null +++ b/templates/base_help.html @@ -0,0 +1,73 @@ +{% extends 'layout.html' %} +{% load i18n %} +{% load staticfiles %} + +{% block content %} + +{% endblock %} diff --git a/templates/ccvpn/page.html b/templates/ccvpn/page.html index ae9a142..e072876 100644 --- a/templates/ccvpn/page.html +++ b/templates/ccvpn/page.html @@ -1,36 +1,7 @@ -{% extends 'layout.html' %} +{% extends 'base_help.html' %} {% load i18n %} {% load staticfiles %} -{% block content %} -
- -
- {% if title %} -

{{ title }}

- {% endif %} +{% block helpdesk_body %} {{ content|safe }} -
- -{% endblock %} - +{% endblock %} \ No newline at end of file diff --git a/templates/kb/kb_category.html b/templates/kb/kb_category.html new file mode 100644 index 0000000..995a111 --- /dev/null +++ b/templates/kb/kb_category.html @@ -0,0 +1,19 @@ +{% extends "base_help.html" %}{% load i18n %}{% load i18n humanize %} + +{% block helpdesk_body %} +

{% trans 'Knowledge Base' %}: {{ category.name }}

+ +
+

{{ category.name }}

+ {% if category.description %} +

{{ category.description }}

+ {% endif %} + + +
+{% endblock %} + diff --git a/templates/kb/kb_index.html b/templates/kb/kb_index.html new file mode 100644 index 0000000..863448c --- /dev/null +++ b/templates/kb/kb_index.html @@ -0,0 +1,28 @@ +{% extends "base_help.html" %}{% load i18n %} + +{% block helpdesk_body %} +

{% trans "Knowledge Base" %}

+ +
+

{% trans "Please check to see if any of these answers address your problem prior to opening a support ticket." %}

+ + {% for category in categories %} + {% with category.get_entry_count as entry_count %} + {% if entry_count >= 1 %} +

{{ category.name }}

+ + + {% endif %} + {% endwith %} + {% endfor %} +
+{% endblock %} diff --git a/templates/kb/kb_item.html b/templates/kb/kb_item.html new file mode 100644 index 0000000..abe2274 --- /dev/null +++ b/templates/kb/kb_item.html @@ -0,0 +1,24 @@ +{% extends "base_help.html" %}{% load i18n humanize %} + +{% block helpdesk_body %} + +

{{ item.question }}

+{{ item.answer|safe }} + + +{% if request.user.is_authenticated %} +
+
+

+ {% trans "Did you find this answer useful?" %} + + + + + ({{ item.get_score }} with {{ item.get_vote_count }} votes) +

+
+{% endif %} +{% endblock %} + + diff --git a/templates/pages/advanced-tor.md b/templates/pages/advanced-tor.md index accd529..cdc7c3d 100644 --- a/templates/pages/advanced-tor.md +++ b/templates/pages/advanced-tor.md @@ -14,7 +14,7 @@ This page will go over the methods and their uses and issues. [TOC] -## a. Tor through the VPN +### a. Tor through the VPN Much better for anonymity, but requires careful use of end-to-end encryption (HTTPS, SSH, ...) @@ -32,9 +32,10 @@ Much better for anonymity, but requires careful use of end-to-end encryption (HT 1. Install the VPN. 2. Install tor or the Tor Browser Bundle. +3. By using the Tor SOCKS proxy or the Tor Browser, you will effectively be using Tor over the VPN. -## b. VPN through Tor +### b. VPN through Tor Harder to keep anonymous but will provide a clean and secure connection even over Tor. @@ -46,24 +47,28 @@ Harder to keep anonymous but will provide a clean and secure connection even ove #### Cons: -- this anonymity is lessed depending on the payment method used for the VPN. +- your anonimity depends on the payment method used for the VPN. - the bandwidth will be limited by Tor and its network. - VPN servers aren't as anonymous as most Tor exit nodes. please don't get us into any trouble. #### Installation: -1. Install Tor. The exact method will be highly OS/distribution specific. - -2. Set up the VPN to use Tor: - Edit your VPN configuration file to append the following config (or add it in the "additional config" in CCVPNGUI): - - socks-proxy-retry - socks-proxy 127.0.0.1 9050 - - Replace 9050 by 9150 when using the Tor Browser Bundle. - -3. (Re)connect to the VPN to start using it through Tor. - **Do not** use the Tor Browser Bundle or SOCKS proxy directly if you want to go through the VPN too. - - +1. We will assume using a separate host or virtual machine for Tor. + This configuration is implemented by Whonix and Qubes, and we recommend it for strong + anonymity. It also greatly simplifies routing and avoids some simple failure modes. + +2. Set up the VPN on the "Workstation" (your host or VM behind the Tor gateway) + * You will need a TCP configuration to go over Tor + * Whonix Workstation: you will need to loosen the local firewall to be able to use a VPN: + `sudo iptables -I OUTPUT -j ACCEPT` + It shouldn't have serious security implications, providing that your physical network (or virtual network between VM) + is properly isolated. + * Whonix Workstation: change the default DNS server to use the VPN's server: + `echo "nameserver 10.99.0.20" | sudo tee /etc/resolv.conf` + * Whonix Workstation: using the default browser will always use Tor through a proxy. + The easiest workaround is to install and use a regular version of Firefox: + `sudo apt install firefox-esr` + +3. Your trafic should be sent to the VPN, over Tor. + Your current IP address as seen from a website visited on your workstation VM should be linked to the VPN. diff --git a/templates/pages/faq.en.md b/templates/pages/faq.en.md deleted file mode 100644 index 7215235..0000000 --- a/templates/pages/faq.en.md +++ /dev/null @@ -1,139 +0,0 @@ ---- -Title: Frequently Asked Questions ---- - -[TOC] - -General -------- - -### What is a VPN? How does it work? -A Virtual Private Network is a virtual network that provides a secure -and reliable connection between two points over the Internet. - -Our VPN forwards your traffic through our VPN servers to prevent it -from being analyzed, filtered, or modified between you and the server -and hides its original source. - -Instead of seeing your own IP address and finding your ISP and approximate -geolocation, websites will only see the IP address of the VPN server. -It lets you pick the country and blend in as anyone behind the VPN server. - -### How does it compare to tor? -Tor, although free and decentralized, is not designed for the same usage. -It provides better anonymity than a VPN, at the cost of much reduced performances -and in some cases security. - -While communications are very secure between two tor nodes, -to communicate to the existing web ("clearnet") it needs *exit nodes* -that will see and handle all your traffic, and some of those have been -seen to abuse that trust. Our servers don't do that. - -A VPN can be used for everyday browsing, online gaming, and you can -pick a server close to you for best performances. - -For anonymity, tor can be used over the VPN to hide your tor usage to -your ISP or school, as it could be used against you. - -### Do you propose an affiliate program? -Yes, sare your affiliate link and earn 2 weeks for each referral that pays. -Invite 24 people and you get one year of free VPN! - -### Is P2P allowed? -On some servers. You can see it in CCVPNGUI and on the configuration download page. - -### Can I have a dedicated IP address? -Not at the moment. - -### Do you limit bandwidth usage? -Every user of a server share its connection without hard limit. -We try to always have enough bandwidth (at least 20Mbps) available -for each connection. - -### Do you censor some websites or protocols? -No. BitTorrent is forbidden on some servers. - -### Which protocols are supported? -We only support OpenVPN for now. - -### Which payment methods are supported? -We support Paypal, credit cards (via Stripe) and many cryptocurrencies (BTC, XMR, LTC, ETH, ...). -Feel free to ask [the support](/page/help) if you need any other method. - -### Is it free software? -Yes! Our VPN is based on OpenVPN and we try to release most of our own work as free software. - -* [Our Windows OpenVPN GUI](https://github.com/PacketImpact/lvpngui/) -* [This website](https://github.com/CCrypto/ccvpn3/) - -### Are my data kept secure? -Yes, the VPN traffic is strongly encrypted and we do not keep any data on the -VPN servers. -The website and database are on a different server, in a -different datacenter. - -### Will there be more servers/countries available? -Yes, but we first need money to pay the servers. -If you would like to have a server somewhere, know a good provider or would -like to host one, please contact us. - - -Account -------- - -### Can I have a trial account? -Yes, you just have to [sign up](/account/signup) and click on the dedicated button -to get a repeatable free 2-hour test period. The limit is a full week. - -### Can I use my account on multiple machines? -Yes, you can! Up to 10 at the same time! - -### How can I delete my account? -Contact [the support](/page/help). - - -Technical ---------- - -### Encryption used -Authentication uses a 4096 bits RSA key. (3072 bits on oldest servers) -The current recommended key size considered safe until 2030 is 2048 bits. - -VPN trafic encryption is performed with the best cipher available to OpenVPN -(with a recent version AES 256 GCM) -using a random 256 bits key re-generated regularly, and unique to a VPN connection. - -Key Exchange uses a 3072 bits Diffie-Hellman parameters. -A 2048 bits key is considered safe until 2030. - -### Do you support IPv6? -Yes, most of our servers are dual stack - they perfectly support IPv4 and IPv6 -at the same time. -Some are IPv4 only (but we're working with our providers to fix it) and will -block all IPv6 traffic to make sure your IPv6 address is not leaked. - -### Do you support PPTP? -No, PPTP is not supported and will not be supported. -PPTP is considered insecure and should never be used. - - -Legal ------ - -### What do you log? -See our [privacy policy](/page/privacy) page. -Your traffic through the VPN is never logged, but VPN connections are. - -### Is it really anonymous? -We will not ask your name and you can pay with bitcoins. -The VPN will hide your identity from people over the Internet, -and can help you achieve anonymity by not leaking information through your IP address. -It will however not make you untraceable. - -### Will you log traffic or send user data to authorities? -We won't log your traffic under any condition. -We may give the little we know about you to authorities -only if required by the law to keep the service running. -In this case, we'll try to contact you before doing anything if possible. - - diff --git a/templates/pages/faq.fr.md b/templates/pages/faq.fr.md deleted file mode 100644 index 8ece75b..0000000 --- a/templates/pages/faq.fr.md +++ /dev/null @@ -1,148 +0,0 @@ ---- -Title: Questions fréquemment posées ---- - -[TOC] - -Géneral -------- - -### Qu'est-ce qu'un VPN ? Comment ça marche ? -Un VPN (Réseau Privé Virtuel) est un réseau virtual permettant -d'établir une connexion sécurisée entre deux points d'Internet. - -Notre VPN redirige votre traffic vers nos serveurs VPN à travers le tunnel -sécurisé, le protégeant contre la lecture et l'interception par un tiers, -et cachant son origine. - -Au lieu de voir votre adresse IP and d'en déduire une localisation approximative, -les sites web ne verront que l'adresse du VPN. -Cela vous donne le choix du pays depuis lequel apparaitre, et vous permet -de vous fondre dans la masse des utilisateurs de ce serveur. - -### Quelles sont les différences avec tor ? -Tor, bien que gratuit et décentralisé, n'est pas fait pour le même usage. -Il fournit un meilleur anonymat qu'un VPN, mais au coût de performances -très réduits et dans certains cas la sécurité. - -La communication entre deux noeuds tor est bien sécurisée, mais pour accéder -au reste du web ("clearnet") le réseau tor passe par des *exit nodes* -qui vont avoir accès à votre traffic, et certains ont déjà été vu abuser de -cette position. Nos serveurs ne font pas ça. - -Un VPN peut être utilisé pour la navigation de tous les jours, -les jeux en ligne, et vous pouvez choisir un serveur proche de vous -pour les meilleures performances. - -Pour l'anonymat, tor peut être utilisé en plus du VPN, pour cacher votre -utilisation de tor à votre FAI ou école qui pourrait utiliser ça contre vous. - -### Avez-vous un programme d'affiliation ? -Oui, vous pouvez partager un lien associé à votre compte, qui vous -fera gagner 2 semaines de VPN pour chaque client l'ayant suivi. -Inviter 24 personnes vous donne donc 1 an de VPN gratuit ! - -### Est-ce que le P2P est autorisé ? -Sur certains serveurs. C'est indiqué dans CCVPNGUI et sur la page -de téléchargement de config. - -### Puis-je avoir une adresse dédiée ? -Non, pas pour le moment. - -### Y a-t-il une limite de bande passante ? -Non, tous les utilisateurs partagent équitablement la connexion des serveurs. -Nous faisons en sorte qu'il y ait toujours un minimum de 20Mbps disponible -pour chaque client. - -### Censurez-vous certains sites ou protocoles ? -Non. Le BitTorrent est interdit sur certains serveurs. - -### Avec quels protocoles fonctionne le VPN ? -Notre VPN est fait avec OpenVPN. - -### Quelles méthodes de payement sont disponibles ? -Vous pouvez payer par Paypal, carte (via Stripe), et plusieurs cryptomonnaies. -Vous pouvez [nous contacter](/page/help) si vous avez besoin d'un autre moyen -de payement. - -### Est-ce Libre ? -Oui ! Notre VPN fonctionne avec OpenVPN, et nous essayons de distribuer -une grande partie de notre travail comme logiciel libre. - -* [Notre logiciel client pour Windows](https://github.com/PacketImpact/lvpngui/) -* [Ce site](https://github.com/CCrypto/ccvpn3/) - -### Est-ce vraiment sécurisé ? -Oui, le VPN utilise différents algorithmes de chiffrement fiables et nous ne -gardons aucune données sensible sur les serveurs du VPN. -Les comptes clients et historiques de connexions sont uniquement gardés sur des -serveurs séparés. - -### Y aura-t-il plus de serveurs ou dans d'autres pays ? -Oui, nous ajoutons des serveurs en fonction de la demande et de nos moyens. -Si vous voudriez héberger un serveur, recommander un bon hébergeur, ou -seriez simplement intéressé par une certain pays, contactez nous. - -Comptes -------- - -### Puis-je avoir un compte de test gratuit ? -Oui, vous n'avez qu'à [créer un compte](/account/signup) et cliquer sur -le bouton dédié pour recevoir une période d'essai gratuite de 2h répétable. -La limite est d'une semaine entière. - -### Puis-je utiliser mon compte sur plusieurs machines ? -Oui, vous pouvez utiliser votre compte avec un maximum de 10 connexions -simultannées. - -### Comment supprimer mon compte ? -[Contactez nous](/page/help). - - -Technique ---------- - -### Chiffrement -L'authentification utilise une clé RSA de 4096 bits. (3072 sur les serveurs plus anciens) -Les clés de 2048 bits ou plus sont considérées sûres jusqu'à 2030. - -Le traffic est chiffré avec le meilleur algorithme disponible pour OpenVPN -(AES 256 GCM dans les versions récentes), -en utilisant une clé aléatoire de 256 bits re-générée régulièrement -et unique pour chaque connexion au VPN. - -L'échange de clés (Diffie-Hellman) utilise un groupe de 3072 bits. -2048 bits ou plus est considéré suffisant jusqu'en 2030. - -### Est-ce que l'IPv6 est supporté ? -Oui, la plupart des serveurs fonctionnent en IPv4 et IPv6 (dual stack). -Quelques-uns ne fonctionnent qu'en IPv4 et bloquent entièrement l'IPv6 pour -éviter de laisser passer votre addresse IPv6. - -### Est-ce que le PPTP est supporté ? -Non, le PPTP n'est plus considéré sécurisé et ne doit plus être utilisé. - - -Légal ------ - -### Quelles informations gardez-vous ? -Voyez notre [politique de confidentialité](/page/privacy). -Nous n'enregistrons jamais votre traffic dans le VPN, -mais les connexions au VPN sont loggées. - -### Est-ce réellement anonyme ? -Nous ne vous demanderons pas votre nom et nous acceptons les paiements anonymes. -Le VPN cachera votre identité lors de communications sur Internet, -et peut vous aider à atteindre un niveau d'anonymat en évitant de laisser -filtrer informations par l'adresse IP. -Cela ne vous rendra pas pour autant intraçable et ne permet pas d'achapper à la loi. - -### Donnez vous des informations aux autorités ? -Nous ne vous espionnerons jamais. -Le peu de données enregistrées peuvent être transmises aux autorités si requis -par la loi. -Dans ce cas, nous essaierons de contacter les clients concernés avant tout, -si possible. - - diff --git a/templates/pages/help.en.md b/templates/pages/help.en.md index cbc88f9..eb2e2f0 100644 --- a/templates/pages/help.en.md +++ b/templates/pages/help.en.md @@ -3,7 +3,7 @@ Title: Guides --- -## Installation +### Installation -## Support +### Support - - [**Frequently Asked Questions**](/page/faq) - - [**Privacy**](/page/privacy) - - **[Self-Diagnosis](/page/self-diagnosis)**: Before asking for help, check here if you find the solution to your problem. + - [**Knowledge Base**](/kb/) + - [**Self-Diagnosis**](/page/self-diagnosis): Before asking for help, check here if you find the solution to your problem. + - [**Tickets**](/tickets/) + - E-mail: **support at ccrypto.org** diff --git a/templates/pages/help.fr.md b/templates/pages/help.fr.md index 37d844d..5ed03f8 100644 --- a/templates/pages/help.fr.md +++ b/templates/pages/help.fr.md @@ -2,7 +2,7 @@ Title: Guides --- -## Installation +### Installation -## Support +### Support - - [**Questions fréquemment posées**](/page/faq) - - [**Informations personnelles et vie privée**](/page/privacy) + - [**F.A.Q.**](/kb/) - [**Auto-Diagnostic**](/page/self-diagnosis) : Avant de demander de l'aide, vérifiez si vous trouvez la solution à votre problème ici. - + - [**Tickets**](/tickets/) + - E-mail: **support at ccrypto.org** diff --git a/templates/pages/install-gnulinux.en.md b/templates/pages/install-gnulinux.en.md index f64f427..5dcecdd 100644 --- a/templates/pages/install-gnulinux.en.md +++ b/templates/pages/install-gnulinux.en.md @@ -2,8 +2,7 @@ Title: Install on GNU/Linux --- -With NetworkManager -------------------- +### With NetworkManager 1. Download and install OpenVPN and the NetworkManager plugin with your package manager. @@ -35,8 +34,7 @@ With NetworkManager 3. Save the new connection and connect to it. -With systemd (Arch, Fedora 16 or later, Debian 8 or later, ...) ------------- +### With systemd (Arch, Fedora 16 or later, Debian 8 or later, ...) 1. Download and install OpenVPN with your package manager. diff --git a/templates/pages/install-gnulinux.fr.md b/templates/pages/install-gnulinux.fr.md index a0ac160..fb5efa8 100644 --- a/templates/pages/install-gnulinux.fr.md +++ b/templates/pages/install-gnulinux.fr.md @@ -2,8 +2,7 @@ Title: Installation sous GNU/Linux --- -Avec NetworkManager -------------------- +### Avec NetworkManager 1. Téléchargez et installez OpenVPN et le plugin NM avec votre gestionnaire de paquets : @@ -35,8 +34,7 @@ Avec NetworkManager 3. Enregistrez la connexion et connectez-vous. -Avec systemd (Arch, Fedora 16 ou plus, Debian 8 ou plus, ...) ------------- +### Avec systemd (Arch, Fedora 16 ou plus, Debian 8 ou plus, ...) 1. Téléchargez et installez OpenVPN avec votre gestionnaire de paquets : diff --git a/templates/pages/install-windows.en.md b/templates/pages/install-windows.en.md index 270f9d6..32051c9 100644 --- a/templates/pages/install-windows.en.md +++ b/templates/pages/install-windows.en.md @@ -3,8 +3,7 @@ Title: Install on Windows --- {% load dltags %} -With CCVPN GUI (recommended, simple) -------------- +### With CCVPN GUI (recommended, simple) CCVPN GUI is made for CCrypto VPN and contains everything it needs. {% download_button "ccvpngui" "windows" %} @@ -19,8 +18,7 @@ It's open-source and based on [LVPNGUI](https://github.com/PacketImpact/lvpngui/ --- -With OpenVPN GUI (advanced) ----------------- +### With OpenVPN GUI (advanced) 1. Download OpenVPN for Windows on [OpenVPN.net](http://openvpn.net/index.php/open-source/downloads.html) diff --git a/templates/pages/install-windows.fr.md b/templates/pages/install-windows.fr.md index cb7ff5c..1ec8fce 100644 --- a/templates/pages/install-windows.fr.md +++ b/templates/pages/install-windows.fr.md @@ -3,8 +3,7 @@ Title: Installation sous Windows --- {% load dltags %} -Avec CCVPN GUI (recommandé, simple) -------------- +### Avec CCVPN GUI (recommandé, simple) CCVPN GUI est fait pour CCrypto VPN et contient tout ce qu'il faut. {% download_button "ccvpngui" "windows" %} @@ -19,8 +18,7 @@ C'est open-source et basé sur [LVPNGUI](https://github.com/PacketImpact/lvpngui --- -Avec OpenVPN GUI (avancé) ----------------- +### Avec OpenVPN GUI (avancé) 1. Téléchargez le Windows Installer d'OpenVPN sur [OpenVPN.net](http://openvpn.net/index.php/open-source/downloads.html) diff --git a/templates/pages/privacy.en.md b/templates/pages/privacy.en.md index 16c86ef..d2fb47c 100644 --- a/templates/pages/privacy.en.md +++ b/templates/pages/privacy.en.md @@ -8,7 +8,7 @@ in what conditions, and the exact limits under which it is kept and transfered. If you have any question that is not answered by this page, please contact us. -## 1. Informations stored +### 1. Informations stored To run our VPN service, we collect and store the following informations: @@ -24,16 +24,16 @@ For each payment or subscription, we store: - Stripe: the Stripe charge or subscription ID and paid amount - Bitcoin: the transaction id, receiving address and received amount at the time the payment was confirmed. -We also use Piwik to monitor visitors on our website; it may record anonymized IP addresses, -pages viewed, referrer, and some informations about your browser. - All these informations are strictly kept by CCrypto and will not be shared to a third party, -except as described in sections 2 and 4. +except as described in section 4. + +Our websites embeds a support chat widget operated by Tawk.to. It can be blocked +without breaking the rest of the website and tickets can be used for communication. -We use no external analytics or advertisement network on our website. +We use no additional external analytics or advertisement network on our website. -## 2. VPN Logging +### 2. VPN Logging Each connection from a VPN client to our VPN server is logged for security and billing purposes. For each connection or authentication, we store for up to a year: @@ -46,25 +46,16 @@ For each connection or authentication, we store for up to a year: As opposed to many VPN service providers, no data is stored or logged in the gateway servers. Those are too exposed and would be the easiest target to raid or steal, and depend on the law of many countries. +Instead, connection logs are stored separately on a secure server. -Instead, logs are stored separately on a secure server operated by LambdaVPN in France, -who is also managing the gateway servers. -LambdaVPN has no access to any other user information -and has agreed not to share or use any information they store except on request by CCrypto. - -In case of abuse or data request from any authority, LambdaVPN does not -have access to any payment information or email address directly and can -only forward the request to CCrypto. - -These conditions are stated in the contract between CCrypto and LambdaVPN. - -**CCrypto or LambdaVPN will never, in any event, monitor, record, or use without your consent the traffic you send and receive through the VPN, including but not limited to DNS queries and browsing history.** +**CCrypto will never, in any event, monitor, record, or use without your consent the traffic you send and receive through the VPN, including but not limited to DNS queries and browsing history.** On some servers, we will however watch for known dangerous patterns to limit abuse, -as explicitly described by a dedicated page: [NO-P2P](/page/nop2p) +as explicitly described by a dedicated page: [NO-P2P](/page/nop2p) +Such detections are exclusively for use by CCrypto internally and will not be shared. -## 3. DMCA handling +### 3. DMCA handling DMCA cease & desists are usually ignored as we have very little control over it. If one user generates too many abuse, we may investigate and block their access @@ -81,7 +72,7 @@ Please do not abuse the service, as it will only lower the quality for everyone. **We will never disclose user information as a result of a DMCA notice.** -## 4. Data requests +### 4. Data requests The French government may request informations about VPN connections and payments as required by the French law. We will only comply to this kind of request if they are following the right legal procedure. diff --git a/templates/pages/self-diagnosis.en.md b/templates/pages/self-diagnosis.en.md index 0185a8f..c572aef 100644 --- a/templates/pages/self-diagnosis.en.md +++ b/templates/pages/self-diagnosis.en.md @@ -3,13 +3,12 @@ Title: Self Diagnosis --- -Windows -------- +### Windows -*Before anything, make sure you have started OpenVPN as Administrator and that your -config files exist in `C:\Program Files\OpenVPN\config\`.* +Before anything, make sure you have started OpenVPN as Administrator and that your +config files exist in `C:\Program Files\OpenVPN\config\`. -### netsh.exe error +#### netsh.exe error If you find lines like those in your OpenVPN log: @@ -24,7 +23,7 @@ for example "Local Area Network" to "lan". - [Rename a network connection](http://windows.microsoft.com/en-au/windows-vista/rename-a-network-connection) -### Multiple TAP-Windows adapters +#### Multiple TAP-Windows adapters Error: When using --tun-ipv6, if you have more than one TAP-Windows adapter, you must also specify --dev-node Exiting due to fatal error @@ -46,34 +45,33 @@ new line: Replace [name] by your TAP adapter name. -### Still doesn't work +#### Still doesn't work If you still cannot use the VPN, please go to the [Support page](/page/help) and we'll do our best to help you. Please also send us your OpenVPN logs. -GNU/Linux ---------- +### GNU/Linux -### I have a ".ovpn" file but I need a ".conf"! +#### I have a ".ovpn" file but I need a ".conf"! You just have to change the extension by renamming the file. .conf is more commonly used on GNU/Linux, but it's the same as the .ovpn file. -### I'm unable to use your VPN with Network-Manager. +#### I'm unable to use your VPN with Network-Manager. First, check that you have properly created the profile (tutorial to come). If it's the case, before anything else, let's make sure that OpenVPN itself is working with the following command: `sudo openvpn --config ccrypto.conf` (make sure to replace "ccrypto.conf" by the actual name of your configuration file) -### I'm connected but cannot ping google.com +#### I'm connected but cannot ping google.com Try to `ping 8.8.8.8`: if it works then your computer doesn't use the right DNS server. Add `nameserver 10.99.0.20` at the beginning of /etc/resolv.conf **once the connection is made**. Else, continue reading. -### It still doesn't work! +#### It still doesn't work! Using the `ip route` command, make sure you have, alongside with other lines, the following: 0.0.0.0/1 via 10.99.2.1 dev tun0 @@ -88,6 +86,6 @@ If you don't know how to do it, it would be best to come ask on IRC (we will nee please paste them into https://paste.cubox.me and just give us the link to the paste). -### I've tried everything but nothing seems to work! T_T +#### I've tried everything but nothing seems to work! T_T Ok… I guess now you can come [ask us on IRC](/chat) (but remember to stay a while, we're not payed professionnal, we might not be around at a given time but we will answer later on). diff --git a/templates/pages/self-diagnosis.fr.md b/templates/pages/self-diagnosis.fr.md index 60ae258..65c53f0 100644 --- a/templates/pages/self-diagnosis.fr.md +++ b/templates/pages/self-diagnosis.fr.md @@ -3,14 +3,13 @@ Title: Auto-Diagnostic --- -Windows -------- +### Windows -*Tout d'abord, assurez vous d'avoir bien démarré OpenVPN en tant qu'administrateur +Tout d'abord, assurez vous d'avoir bien démarré OpenVPN en tant qu'administrateur et que votre fichier de configuration est correctement placé dans -`C:\Program Files\OpenVPN\config\`.* +`C:\Program Files\OpenVPN\config\`. -### netsh.exe +#### netsh.exe Si vous trouvez ces lignes dans votre historique OpenVPN: @@ -25,7 +24,7 @@ Par exemple « Connexion au réseau local » en « lan ». - [(fr) Renommer une connexion réseau](http://windows.microsoft.com/fr-xf/windows-vista/rename-a-network-connection) -### Multiples interfaces TAP +#### Multiples interfaces TAP Error: When using --tun-ipv6, if you have more than one TAP-Windows adapter, you must also specify --dev-node Exiting due to fatal error @@ -46,7 +45,7 @@ et ajoutez ceci sur une nouvelle ligne : Remplacez [nom] par le nom de votre interface TAP. -### Ça ne fonctionne toujours pas ? +#### Ça ne fonctionne toujours pas ? Si vous ne pouvez toujours pas utiliser le VPN, n'hésitez pas à [nous contacter](/page/help). @@ -54,22 +53,21 @@ Joignez les logs d'OpenVPN à votre message, pour nous aider à trouver le problème au plus vite. -GNU/Linux ---------- +### GNU/Linux -### J'ai un fichier ".ovpn" mais il me faut un ".conf" ! +#### J'ai un fichier ".ovpn" mais il me faut un ".conf" ! Il vous suffit de changer l'extension en renommant le fichier. -### Il m'est impossible d'utiliser votre VPN avec Network-Manager. +#### Il m'est impossible d'utiliser votre VPN avec Network-Manager. Tout d'abord, vérifiez que vous avez correctement créé le profil (tutoriel à venir). Si c'est bien le cas, avant toute chose, vérifiez qu'OpenVPN lui-même est opérationnel en utilisant cette commande : `sudo openvpn --config ccrypto.conf` (assurez-vous de remplacer "ccrypto.conf" par le nom de votre fichier de configuration) -### Je suis connecté mais je ne peux pas ping google.com +#### Je suis connecté mais je ne peux pas ping google.com Essayez de `ping 8.8.8.8`, si ça marche, votre ordinateur n'utilise pas le serveur DNS. Ajoutez `nameserver 10.99.0.20` au début de /etc/resolv.con **une fois la connexion établie**. Sinon, lisez la suite. -### Ça ne marche toujours pas ! +#### Ça ne marche toujours pas ! En utilisant la commande `ip route`, vérifiez que vous avez, entre autre choses, les lignes suivantes : 0.0.0.0/1 via 10.99.2.1 dev tun0 @@ -84,6 +82,6 @@ Si vous ne savez pas comment faire, ce serait mieux de venir nous demander sur I (nous allons avoir besoin des sorties des commandes `ip addr` et `ip route`, veuillez utiliser https://paste.cubox.me et nous envoyer uniquement le lien vers le paste). -### J'ai tout essayé mais rien ne semble fonctionner ! T_T +#### J'ai tout essayé mais rien ne semble fonctionner ! T_T Ok… Je pense que vous pouvez venir [nous demander sur IRC](/chat) (mais souvenez-vous que nous ne sommes pas des professionnels payés, nous ne sommes pas toujours présent mais nous finirons toujours par répondre si vous ne partez pas trop vite). diff --git a/templates/tickets/layout.html b/templates/tickets/layout.html index 9073775..c7c714b 100644 --- a/templates/tickets/layout.html +++ b/templates/tickets/layout.html @@ -3,25 +3,50 @@ {% load staticfiles %} {% block content %} -
+
-
+
{% block tickets_content %}{% endblock %}
diff --git a/templates/tickets/list.html b/templates/tickets/list.html index f25e3b0..f790569 100644 --- a/templates/tickets/list.html +++ b/templates/tickets/list.html @@ -3,7 +3,7 @@ {% load staticfiles %} {% block tickets_content %} -

{% trans 'Tickets' %}

+

{% trans 'Tickets' %}

{% if tickets %} diff --git a/templates/tickets/new.html b/templates/tickets/new.html index bfc1568..cb090ef 100644 --- a/templates/tickets/new.html +++ b/templates/tickets/new.html @@ -4,7 +4,7 @@ {% block tickets_content %}
-

{% trans 'New Ticket' %}

+

{% trans 'New Ticket' %}

{% csrf_token %} diff --git a/templates/tickets/view.html b/templates/tickets/view.html index 7f96df2..ea79ea2 100644 --- a/templates/tickets/view.html +++ b/templates/tickets/view.html @@ -4,11 +4,11 @@ {% block tickets_content %}
-

{% trans 'Ticket:' %} {{ ticket.subject }} +

{% trans 'Ticket:' %} {{ ticket.subject }} {% if not ticket.is_open %} [{% trans 'closed' %}] {% endif %} -

+
{% for message in ticket_messages %}