Better frequent questions, tiny css improvements
parent
5cf77af233
commit
2fc3a0274c
@ -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)
|
@ -0,0 +1,5 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class HelpConfig(AppConfig):
|
||||||
|
name = 'help'
|
@ -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')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
@ -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"
|
||||||
|
|
@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
@ -0,0 +1,13 @@
|
|||||||
|
from django.urls import path
|
||||||
|
|
||||||
|
from . import views
|
||||||
|
|
||||||
|
app_name = 'help'
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('', views.kb_index, name='kb_index'),
|
||||||
|
path('<slug:category>/', views.kb_category, name='kb_category'),
|
||||||
|
path('<slug:category>/<int:entry>/', views.kb_entry, name='kb_entry'),
|
||||||
|
path('<slug:category>/<int:entry>/feedback/', views.kb_feedback, name='kb_feedback'),
|
||||||
|
]
|
||||||
|
|
@ -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())
|
||||||
|
|
@ -0,0 +1,73 @@
|
|||||||
|
{% extends 'layout.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load staticfiles %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="flex-page">
|
||||||
|
<div class="left-menu">
|
||||||
|
<p class="menu-title">{% trans 'Help' %}</p>
|
||||||
|
<ul>
|
||||||
|
<li class="pure-menu-item">
|
||||||
|
<a href="{% url 'page' 'help' %}">
|
||||||
|
<i class="fa fa-life-ring fa-fw" aria-hidden="true"></i>
|
||||||
|
{% trans 'Guides' %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="pure-menu-item">
|
||||||
|
<a href="{% url 'kb:kb_index' %}">
|
||||||
|
<i class="fa fa-book fa-fw" aria-hidden="true"></i>
|
||||||
|
{% trans 'Knowledge Base' %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="pure-menu-item">
|
||||||
|
<a href="/page/self-diagnosis">
|
||||||
|
<i class="fa fa-wrench fa-fw" aria-hidden="true"></i>
|
||||||
|
{% trans 'Self-Diagnosis' %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li><a href="/page/privacy">
|
||||||
|
<i class="fa fa-shield fa-fw" aria-hidden="true"></i>
|
||||||
|
{% trans 'Privacy' %}
|
||||||
|
</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p class="menu-title">{% trans 'Installation' %}</p>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/page/install-android">
|
||||||
|
<i class="fa fa-android fa-fw" aria-hidden="true"></i>
|
||||||
|
{% trans 'Android' %}
|
||||||
|
</a></li>
|
||||||
|
<li><a href="/page/install-windows">
|
||||||
|
<i class="fa fa-windows fa-fw" aria-hidden="true"></i>
|
||||||
|
{% trans 'Windows' %}
|
||||||
|
</a></li>
|
||||||
|
<li><a href="/page/install-gnulinux">
|
||||||
|
<i class="fa fa-linux fa-fw" aria-hidden="true"></i>
|
||||||
|
{% trans 'GNU/Linux' %}
|
||||||
|
</a></li>
|
||||||
|
<li><a href="/page/install-osx">
|
||||||
|
<i class="fa fa-apple fa-fw" aria-hidden="true"></i>
|
||||||
|
{% trans 'OS X' %}
|
||||||
|
</a></li>
|
||||||
|
<li><a href="/page/install-chromeos">
|
||||||
|
<i class="fa fa-chrome fa-fw" aria-hidden="true"></i>
|
||||||
|
{% trans 'Chrome OS' %}
|
||||||
|
</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p class="menu-title">{% trans 'Advanced' %}</p>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/page/advanced-tor">
|
||||||
|
<i class="fa fa-user-secret fa-fw" aria-hidden="true"></i>
|
||||||
|
{% trans 'Tor' %}
|
||||||
|
</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="content-box">
|
||||||
|
{% if title %}
|
||||||
|
<h2>{{ title }}</h2>
|
||||||
|
{% endif %}
|
||||||
|
{% block helpdesk_body %}{% endblock %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
@ -1,36 +1,7 @@
|
|||||||
{% extends 'layout.html' %}
|
{% extends 'base_help.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load staticfiles %}
|
{% load staticfiles %}
|
||||||
|
|
||||||
{% block content %}
|
{% block helpdesk_body %}
|
||||||
<div class="page">
|
|
||||||
<div class="left-menu">
|
|
||||||
<p class="menu-title">{% trans 'Help' %}</p>
|
|
||||||
<ul>
|
|
||||||
<li><a href="/page/faq">{% trans 'Frequently Asked Questions' %}</a></li>
|
|
||||||
<li><a href="/page/self-diagnosis">{% trans 'Self-Diagnosis' %}</a></li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<p class="menu-title">{% trans 'Installation' %}</p>
|
|
||||||
<ul>
|
|
||||||
<li><a href="/page/install-android">{% trans 'Android' %}</a></li>
|
|
||||||
<li><a href="/page/install-windows">{% trans 'Windows' %}</a></li>
|
|
||||||
<li><a href="/page/install-gnulinux">{% trans 'GNU/Linux' %}</a></li>
|
|
||||||
<li><a href="/page/install-osx">{% trans 'OS X' %}</a></li>
|
|
||||||
<li><a href="/page/install-chromeos">{% trans 'Chrome OS' %}</a></li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<p class="menu-title">{% trans 'Advanced' %}</p>
|
|
||||||
<ul>
|
|
||||||
<li><a href="/page/advanced-tor">{% trans 'Tor' %}</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
{% if title %}
|
|
||||||
<h1>{{ title }}</h1>
|
|
||||||
{% endif %}
|
|
||||||
{{ content|safe }}
|
{{ content|safe }}
|
||||||
</div>
|
{% endblock %}
|
||||||
</section>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
|||||||
|
{% extends "base_help.html" %}{% load i18n %}{% load i18n humanize %}
|
||||||
|
|
||||||
|
{% block helpdesk_body %}
|
||||||
|
<h2>{% trans 'Knowledge Base' %}: {{ category.name }}</h2>
|
||||||
|
|
||||||
|
<div class="block padded-block">
|
||||||
|
<h3>{{ category.name }}</h3>
|
||||||
|
{% if category.description %}
|
||||||
|
<p>{{ category.description }}</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<ul class="kb-question-list">
|
||||||
|
{% for item in items %}
|
||||||
|
<li><a href="{{ item.get_absolute_url }}">{{ item.question }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
@ -0,0 +1,28 @@
|
|||||||
|
{% extends "base_help.html" %}{% load i18n %}
|
||||||
|
|
||||||
|
{% block helpdesk_body %}
|
||||||
|
<h2>{% trans "Knowledge Base" %}</h2>
|
||||||
|
|
||||||
|
<div class="block padded-block">
|
||||||
|
<p>{% trans "Please check to see if any of these answers address your problem prior to opening a support ticket." %}</p>
|
||||||
|
|
||||||
|
{% for category in categories %}
|
||||||
|
{% with category.get_entry_count as entry_count %}
|
||||||
|
{% if entry_count >= 1 %}
|
||||||
|
<h3>{{ category.name }}</h3>
|
||||||
|
|
||||||
|
<ul class="kb-cat-top-questions">
|
||||||
|
{% for item in category.get_top_entries %}
|
||||||
|
<li><a href="{{ item.get_absolute_url }}">{{ item.question }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
{% if entry_count > config.KB_MAX_TOP_ENTRIES %}
|
||||||
|
<li class="more">
|
||||||
|
<a href='{{ category.get_absolute_url }}'><b>{% trans 'View all questions' %}</b></a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
{% endwith %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
@ -0,0 +1,24 @@
|
|||||||
|
{% extends "base_help.html" %}{% load i18n humanize %}
|
||||||
|
|
||||||
|
{% block helpdesk_body %}
|
||||||
|
|
||||||
|
<h2>{{ item.question }}</h2>
|
||||||
|
{{ item.answer|safe }}
|
||||||
|
|
||||||
|
|
||||||
|
{% if request.user.is_authenticated %}
|
||||||
|
<hr />
|
||||||
|
<div class="block block-note kb-question-meta">
|
||||||
|
<p>
|
||||||
|
{% trans "Did you find this answer useful?" %}
|
||||||
|
<span class="kb-vote-buttons">
|
||||||
|
<a href='feedback/?vote=up'><button type="button"><i class="fa fa-thumbs-up fa-lg"></i></button></a>
|
||||||
|
<a href='feedback/?vote=down'><button type="button"><i class="fa fa-thumbs-down fa-lg"></i></button></a>
|
||||||
|
</span>
|
||||||
|
({{ item.get_score }} with {{ item.get_vote_count }} votes)
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
@ -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.
|
|
||||||
|
|
||||||
|
|
@ -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.
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue