mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-05-20 10:34:30 +00:00
firstboot: freedombox.me pagekite vouchers
- Show PageKite voucher only if cfg.danube_edition is enabled
This commit is contained in:
parent
9b4497212d
commit
435f980c6f
1
INSTALL
1
INSTALL
@ -29,6 +29,7 @@
|
|||||||
python3-django-stronghold \
|
python3-django-stronghold \
|
||||||
python3-gi \
|
python3-gi \
|
||||||
python3-psutil \
|
python3-psutil \
|
||||||
|
python3-requests \
|
||||||
python3-setuptools \
|
python3-setuptools \
|
||||||
python3-yaml \
|
python3-yaml \
|
||||||
xmlto
|
xmlto
|
||||||
|
|||||||
@ -28,3 +28,8 @@ class PlinthError(Exception):
|
|||||||
class ActionError(PlinthError):
|
class ActionError(PlinthError):
|
||||||
"""Use this error for exceptions when executing an action."""
|
"""Use this error for exceptions when executing an action."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class DomainRegistrationError(PlinthError):
|
||||||
|
"""Domain registration failed"""
|
||||||
|
pass
|
||||||
|
|||||||
@ -19,13 +19,23 @@
|
|||||||
Forms for first boot module.
|
Forms for first boot module.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from django import forms
|
||||||
from django.contrib import auth
|
from django.contrib import auth
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from plinth import actions
|
from plinth import actions
|
||||||
from plinth.errors import ActionError
|
from plinth import cfg
|
||||||
|
from plinth.errors import ActionError, DomainRegistrationError
|
||||||
|
from plinth.modules.pagekite.utils import PREDEFINED_SERVICES, run
|
||||||
from plinth.modules.users.forms import GROUP_CHOICES
|
from plinth.modules.users.forms import GROUP_CHOICES
|
||||||
|
from plinth.utils import format_lazy
|
||||||
|
LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class State1Form(auth.forms.UserCreationForm):
|
class State1Form(auth.forms.UserCreationForm):
|
||||||
@ -77,3 +87,99 @@ class State1Form(auth.forms.UserCreationForm):
|
|||||||
else:
|
else:
|
||||||
message = _('User account created, you are now logged in')
|
message = _('User account created, you are now logged in')
|
||||||
messages.success(self.request, message)
|
messages.success(self.request, message)
|
||||||
|
|
||||||
|
|
||||||
|
class SubdomainWidget(forms.widgets.TextInput):
|
||||||
|
"""Append the domain to the subdomain bootstrap input field"""
|
||||||
|
def render(self, *args, **kwargs):
|
||||||
|
inputfield = super(SubdomainWidget, self).render(*args, **kwargs)
|
||||||
|
domain = State5Form.DOMAIN_APPENDIX
|
||||||
|
return """<div class="input-group">
|
||||||
|
{0}
|
||||||
|
<span class="input-group-addon">{1}</span>
|
||||||
|
</div>""".format(inputfield, domain)
|
||||||
|
|
||||||
|
|
||||||
|
class State5Form(forms.Form):
|
||||||
|
"""Set up freedombox.me pagekite subdomain"""
|
||||||
|
DOMAIN_APPENDIX = ".freedombox.me"
|
||||||
|
# webservice url for domain validation and registration
|
||||||
|
service_url = "http://freedombox.me/cgi-bin/freedomkite.pl"
|
||||||
|
code_help_text = _("The voucher you received with your {box_name} Danube "
|
||||||
|
"Edition")
|
||||||
|
code = forms.CharField(help_text=format_lazy(code_help_text,
|
||||||
|
box_name=_(cfg.box_name)))
|
||||||
|
domain = forms.SlugField(label=_("Subdomain"),
|
||||||
|
widget=SubdomainWidget,
|
||||||
|
help_text=_("The subdomain you want to register"))
|
||||||
|
|
||||||
|
def clean_domain(self):
|
||||||
|
"""Append the domain to the users' subdomain"""
|
||||||
|
return self.cleaned_data['domain'] + self.DOMAIN_APPENDIX
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
"""Validate user input (subdomain and code)"""
|
||||||
|
cleaned_data = super(State5Form, self).clean()
|
||||||
|
# if the subdomain is wrong don't look if the domain is available
|
||||||
|
if self.errors:
|
||||||
|
return cleaned_data
|
||||||
|
|
||||||
|
self.domain_already_registered = False
|
||||||
|
code = cleaned_data.get("code")
|
||||||
|
domain = cleaned_data.get("domain")
|
||||||
|
|
||||||
|
response = requests.get(self.service_url, params={'code': code}).json()
|
||||||
|
# The validation response looks like:
|
||||||
|
# 1. code invalid: {}
|
||||||
|
if 'domain' not in response:
|
||||||
|
raise ValidationError(_('This code is not valid'), code='invalid')
|
||||||
|
# 2. code valid, domain registered: {'domain': 'xx.freedombox.me'}
|
||||||
|
elif response['domain']:
|
||||||
|
if response['domain'] == domain:
|
||||||
|
self.domain_already_registered = True
|
||||||
|
else:
|
||||||
|
msg = _('This code is bound to the domain %s' %
|
||||||
|
response['domain'])
|
||||||
|
raise ValidationError(msg, code='invalid')
|
||||||
|
# 3. code valid, no domain registered: {'domain': None}
|
||||||
|
elif response['domain'] is None:
|
||||||
|
# make sure that the desired domain is available
|
||||||
|
data = {'domain': domain}
|
||||||
|
domain_response = requests.get(self.service_url, params=data)
|
||||||
|
registered_domain = domain_response.json()['domain']
|
||||||
|
if registered_domain is not None:
|
||||||
|
msg = _('The requested Domain is already registered')
|
||||||
|
raise ValidationError(msg, code='invalid')
|
||||||
|
|
||||||
|
return cleaned_data
|
||||||
|
|
||||||
|
def register_domain(self):
|
||||||
|
"""Register a domain (only if it's not already registered)"""
|
||||||
|
if not self.domain_already_registered:
|
||||||
|
data = {'domain': self.cleaned_data['domain'],
|
||||||
|
'code': self.cleaned_data['code']}
|
||||||
|
response = requests.post(self.service_url, data)
|
||||||
|
if not response.ok:
|
||||||
|
msg = "Domain registration failed: %s" % response.text
|
||||||
|
LOGGER.error(msg)
|
||||||
|
raise DomainRegistrationError(msg)
|
||||||
|
|
||||||
|
def setup_pagekite(self):
|
||||||
|
"""Configure pagekite and enable the pagekite service"""
|
||||||
|
# set kite name and secret
|
||||||
|
run(['set-kite', '--kite-name', self.cleaned_data['domain']],
|
||||||
|
input=self.cleaned_data['code'].encode())
|
||||||
|
|
||||||
|
# set frontend
|
||||||
|
run(['set-frontend', '%s:80' % self.cleaned_data['domain']])
|
||||||
|
|
||||||
|
# enable pagekite http+https service
|
||||||
|
for service_name in ['http', 'https']:
|
||||||
|
service = PREDEFINED_SERVICES[service_name]['params']
|
||||||
|
try:
|
||||||
|
run(['add-service', '--service', json.dumps(service)])
|
||||||
|
except ActionError as err:
|
||||||
|
if 'already exists' not in str(err):
|
||||||
|
raise
|
||||||
|
|
||||||
|
run(['start-and-enable'])
|
||||||
|
|||||||
33
plinth/modules/first_boot/templates/base_firstboot.html
Normal file
33
plinth/modules/first_boot/templates/base_firstboot.html
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% comment %}
|
||||||
|
#
|
||||||
|
# This file is part of Plinth.
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as
|
||||||
|
# published by the Free Software Foundation, either version 3 of the
|
||||||
|
# License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
{% endcomment %}
|
||||||
|
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
|
{% block page_head %}
|
||||||
|
<style type="text/css">
|
||||||
|
a.navbar-brand {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block mainmenu_right %}
|
||||||
|
{% include "firstboot_navbar.html" %}
|
||||||
|
{% endblock %}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base_firstboot.html" %}
|
||||||
{% comment %}
|
{% comment %}
|
||||||
#
|
#
|
||||||
# This file is part of Plinth.
|
# This file is part of Plinth.
|
||||||
@ -30,10 +30,6 @@
|
|||||||
</style>
|
</style>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block mainmenu_right %}
|
|
||||||
{% include "firstboot_navbar.html" %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content_row %}
|
{% block content_row %}
|
||||||
<p class="text-center">
|
<p class="text-center">
|
||||||
<img src="{% static 'theme/img/FreedomBox-logo-standard.png' %}"
|
<img src="{% static 'theme/img/FreedomBox-logo-standard.png' %}"
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base_firstboot.html" %}
|
||||||
{% comment %}
|
{% comment %}
|
||||||
#
|
#
|
||||||
# This file is part of Plinth.
|
# This file is part of Plinth.
|
||||||
@ -22,18 +22,6 @@
|
|||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
|
|
||||||
{% block page_head %}
|
|
||||||
<style type="text/css">
|
|
||||||
a.navbar-brand {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block mainmenu_right %}
|
|
||||||
{% include "firstboot_navbar.html" %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content_row %}
|
{% block content_row %}
|
||||||
<div class="col-md-6 col-md-offset-3">
|
<div class="col-md-6 col-md-offset-3">
|
||||||
{% include 'messages.html' %}
|
{% include 'messages.html' %}
|
||||||
|
|||||||
70
plinth/modules/first_boot/templates/firstboot_state5.html
Normal file
70
plinth/modules/first_boot/templates/firstboot_state5.html
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
{% extends "base_firstboot.html" %}
|
||||||
|
{% comment %}
|
||||||
|
#
|
||||||
|
# This file is part of Plinth.
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as
|
||||||
|
# published by the Free Software Foundation, either version 3 of the
|
||||||
|
# License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
{% endcomment %}
|
||||||
|
|
||||||
|
{% load bootstrap %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<h3>{% trans "Set up a freedombox.me subdomain with your voucher" %}</h3>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
{% url 'first_boot:state10' as finish_firstboot_url %}
|
||||||
|
{% blocktrans trimmed %}
|
||||||
|
<a href="{{ finish_firstboot_url }}">Skip the setup</a> if you do not have a
|
||||||
|
voucher or want to configure pagekite without using a freedombox.me subdomain.
|
||||||
|
{% endblocktrans %}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
{% blocktrans trimmed %}
|
||||||
|
You can use a redeemed voucher but it will only work with the initially
|
||||||
|
registered subdomain.
|
||||||
|
{% endblocktrans %}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<form class='firstboot form-horizontal' role="form" action="" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form|bootstrap_horizontal:'col-lg-3' }}
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-offset-3 col-sm-9">
|
||||||
|
<button type="submit" class="btn btn-primary">
|
||||||
|
{% trans "Register" %}
|
||||||
|
</button>
|
||||||
|
<a href="{% url 'first_boot:state10' %}" class="btn btn-primary"
|
||||||
|
role="button">
|
||||||
|
{% trans "Skip Registration" %}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block page_js %}
|
||||||
|
<script>
|
||||||
|
$('#id_code').focus();
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
@ -22,7 +22,7 @@ URLs for the First Boot module
|
|||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
from stronghold.decorators import public
|
from stronghold.decorators import public
|
||||||
|
|
||||||
from .views import State0View, State1View, state10
|
from .views import State0View, State1View, State5View, state10
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
@ -30,5 +30,6 @@ urlpatterns = [
|
|||||||
url(r'^firstboot/$', public(State0View.as_view()), name='index'),
|
url(r'^firstboot/$', public(State0View.as_view()), name='index'),
|
||||||
url(r'^firstboot/state0/$', public(State0View.as_view()), name='state0'),
|
url(r'^firstboot/state0/$', public(State0View.as_view()), name='state0'),
|
||||||
url(r'^firstboot/state1/$', public(State1View.as_view()), name='state1'),
|
url(r'^firstboot/state1/$', public(State1View.as_view()), name='state1'),
|
||||||
|
url(r'^firstboot/state5/$', State5View.as_view(), name='state5'),
|
||||||
url(r'^firstboot/state10/$', state10, name='state10'),
|
url(r'^firstboot/state10/$', state10, name='state10'),
|
||||||
]
|
]
|
||||||
|
|||||||
@ -15,16 +15,20 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
from django.contrib import messages
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.core.urlresolvers import reverse_lazy
|
from django.core.urlresolvers import reverse_lazy
|
||||||
|
from django.http.response import HttpResponseRedirect
|
||||||
from django.shortcuts import render_to_response
|
from django.shortcuts import render_to_response
|
||||||
from django.template import RequestContext
|
from django.template import RequestContext
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.views.generic import CreateView, TemplateView
|
from django.views.generic import CreateView, FormView, TemplateView
|
||||||
|
|
||||||
|
from plinth import cfg
|
||||||
from plinth import kvstore
|
from plinth import kvstore
|
||||||
from plinth import network
|
from plinth import network
|
||||||
from .forms import State1Form
|
from plinth.errors import DomainRegistrationError
|
||||||
|
from .forms import State1Form, State5Form
|
||||||
|
|
||||||
|
|
||||||
class State0View(TemplateView):
|
class State0View(TemplateView):
|
||||||
@ -38,6 +42,11 @@ class State1View(CreateView):
|
|||||||
form_class = State1Form
|
form_class = State1Form
|
||||||
success_url = reverse_lazy('first_boot:state10')
|
success_url = reverse_lazy('first_boot:state10')
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
if cfg.danube_edition:
|
||||||
|
self.success_url = reverse_lazy('first_boot:state5')
|
||||||
|
return super(State1View, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def get_form_kwargs(self):
|
def get_form_kwargs(self):
|
||||||
"""Make request available to the form (to insert messages)"""
|
"""Make request available to the form (to insert messages)"""
|
||||||
kwargs = super(State1View, self).get_form_kwargs()
|
kwargs = super(State1View, self).get_form_kwargs()
|
||||||
@ -60,3 +69,29 @@ def state10(request):
|
|||||||
{'title': _('Setup Complete'),
|
{'title': _('Setup Complete'),
|
||||||
'connections': connections},
|
'connections': connections},
|
||||||
context_instance=RequestContext(request))
|
context_instance=RequestContext(request))
|
||||||
|
|
||||||
|
|
||||||
|
class State5View(FormView):
|
||||||
|
"""
|
||||||
|
State 5 is is the (optional) setup of the pagekite freedombox.me subdomain
|
||||||
|
"""
|
||||||
|
template_name = 'firstboot_state5.html'
|
||||||
|
form_class = State5Form
|
||||||
|
success_url = reverse_lazy('first_boot:state10')
|
||||||
|
|
||||||
|
def get(self, *args, **kwargs):
|
||||||
|
kvstore.set('firstboot_state', 5)
|
||||||
|
return super(State5View, self).get(*args, **kwargs)
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
try:
|
||||||
|
form.register_domain()
|
||||||
|
except DomainRegistrationError as err:
|
||||||
|
messages.error(self.request, err)
|
||||||
|
return HttpResponseRedirect(reverse_lazy('first_boot:state5'))
|
||||||
|
else:
|
||||||
|
form.setup_pagekite()
|
||||||
|
msg = _("Pagekite setup finished. The HTTP and HTTPS services \
|
||||||
|
are activated now.")
|
||||||
|
messages.success(self.request, msg)
|
||||||
|
return super(State5View, self).form_valid(form)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user