Merge branch 'master' into task/3530/upgrade_to_django_1.11

This commit is contained in:
PCoder 2017-12-24 20:19:05 +01:00
commit 4069199252
18 changed files with 164 additions and 109 deletions

View file

@ -2,6 +2,12 @@ Next:
* #3911: [dcl] Integrate resend activation link into dcl landing payment page * #3911: [dcl] Integrate resend activation link into dcl landing payment page
* #3972: [hosting] Add ungleich company info to invoice footer * #3972: [hosting] Add ungleich company info to invoice footer
* #3974: [hosting] Improve invoice number: Show 404 for invoice resources that do not belong to the user * #3974: [hosting] Improve invoice number: Show 404 for invoice resources that do not belong to the user
* [ungleich] Add video cover to the header on ungleich.ch landing page and add corresponding cms plugin
* #3774: [hosting] Update Stripe subscription on vm delete
* [ungleich] Update text on landing page
* #3601: [dcl, hosting] Change minimum required RAM from 2GB to 1GB
* #3973: [dcl] Update datacenterlight and glasfaser contact address to Linthal and company name to "ungleich glarus ag"
* #3993: [dg] Fix new user membership payment by setting cardholder_name field for UserBillingAddressForm
1.2.13: 2017-12-09 1.2.13: 2017-12-09
* [cms] Introduce UngleichHeaderBackgroundImageAndTextSliderPlugin that allows to have scrolling images and texts * [cms] Introduce UngleichHeaderBackgroundImageAndTextSliderPlugin that allows to have scrolling images and texts
* [cms] Remove <p> tag for ungleich cms customer item template * [cms] Remove <p> tag for ungleich cms customer item template

View file

@ -56,11 +56,11 @@ msgstr "Standort: Schweiz"
msgid "Please enter a value in range 1 - 48." msgid "Please enter a value in range 1 - 48."
msgstr "Bitte gib einen Wert von 1 bis 48 ein." msgstr "Bitte gib einen Wert von 1 bis 48 ein."
msgid "Please enter a value in range 2 - 200." msgid "Please enter a value in range 1 - 200."
msgstr "Bitte gib einen Wert von 2 bis 200 ein." msgstr "Bitte gib einen Wert von 1 bis 200 ein."
msgid "Please enter a value in range 10 - 2000." msgid "Please enter a value in range 10 - 2000."
msgstr "Bitte gib einen Wert von 10 bis 200 ein." msgstr "Bitte gib einen Wert von 10 bis 2000 ein."
msgid "GB Storage (SSD)" msgid "GB Storage (SSD)"
msgstr "GB Storage (SSD)" msgstr "GB Storage (SSD)"

View file

@ -16,7 +16,7 @@
'ram': { 'ram': {
'id': 'ramValue', 'id': 'ramValue',
'value': 2, 'value': 2,
'min': 2, 'min': 1,
'max': 200, 'max': 200,
'interval': 1 'interval': 1
}, },

View file

@ -36,8 +36,8 @@
<div class="form-group"> <div class="form-group">
<div class="description input"> <div class="description input">
<i class="fa fa-minus-circle left" data-minus="ram" aria-hidden="true"></i> <i class="fa fa-minus-circle left" data-minus="ram" aria-hidden="true"></i>
<input id="ramValue" class="input-price select-number" type="number" min="2" max="200" name="ram" <input id="ramValue" class="input-price select-number" type="number" min="1" max="200" name="ram"
data-error="{% trans 'Please enter a value in range 2 - 200.' %}" required> data-error="{% trans 'Please enter a value in range 1 - 200.' %}" required>
<span> GB RAM</span> <span> GB RAM</span>
<i class="fa fa-plus-circle right" data-plus="ram" aria-hidden="true"></i> <i class="fa fa-plus-circle right" data-plus="ram" aria-hidden="true"></i>
</div> </div>

View file

@ -160,11 +160,11 @@
</div> </div>
<div class="contact-details"> <div class="contact-details">
<div class="subtitle"> <div class="subtitle">
<h3>ungleich GmbH</h3> <h3>ungleich glarus ag</h3>
</div> </div>
<div class="description"> <div class="description">
<p>info@datacenterlight.ch</p> <p>info@datacenterlight.ch</p>
<p>In der Au 7, Schwanden 8762</p> <p>Bahnhofstrasse 1, 8783 Linthal</p>
<p>{% trans "Switzerland " %}</p> <p>{% trans "Switzerland " %}</p>
</div> </div>
</div> </div>

View file

@ -209,7 +209,7 @@ class IndexView(CreateView):
raise ValidationError(_('Invalid number of cores')) raise ValidationError(_('Invalid number of cores'))
def validate_memory(self, value): def validate_memory(self, value):
if (value > 200) or (value < 2): if (value > 200) or (value < 1):
raise ValidationError(_('Invalid RAM size')) raise ValidationError(_('Invalid RAM size'))
def validate_storage(self, value): def validate_storage(self, value):

View file

@ -376,6 +376,10 @@ class MembershipPaymentView(LoginRequiredMixin, IsNotMemberMixin, FormView):
return render(request, self.template_name, context) return render(request, self.template_name, context)
charge = charge_response.get('response_object') charge = charge_response.get('response_object')
if 'source' in charge:
cardholder_name = charge['source']['name']
else:
cardholder_name = customer.user.name
# Create Billing Address # Create Billing Address
billing_address = form.save() billing_address = form.save()
@ -383,7 +387,8 @@ class MembershipPaymentView(LoginRequiredMixin, IsNotMemberMixin, FormView):
# Create Billing Address for User if he does not have one # Create Billing Address for User if he does not have one
if not customer.user.billing_addresses.count(): if not customer.user.billing_addresses.count():
data.update({ data.update({
'user': customer.user.id 'user': customer.user.id,
'cardholder_name': cardholder_name
}) })
billing_address_user_form = UserBillingAddressForm(data) billing_address_user_form = UserBillingAddressForm(data)
billing_address_user_form.is_valid() billing_address_user_form.is_valid()

View file

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-10-26 03:21+0530\n" "POT-Creation-Date: 2017-12-21 00:23+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -128,11 +128,11 @@ msgstr "MwSt. inklusive"
msgid "Please enter a value in range 1 - 48." msgid "Please enter a value in range 1 - 48."
msgstr "Bitte gib einen Wert von 1 bis 48 ein." msgstr "Bitte gib einen Wert von 1 bis 48 ein."
msgid "Please enter a value in range 2 - 200." msgid "Please enter a value in range 1 - 200."
msgstr "Bitte gib einen Wert von 2 bis 200 ein." msgstr "Bitte gib einen Wert von 1 bis 200 ein."
msgid "Please enter a value in range 10 - 2000." msgid "Please enter a value in range 10 - 2000."
msgstr "Bitte gib einen Wert von 10 bis 200 ein." msgstr "Bitte gib einen Wert von 10 bis 2000 ein."
msgid "GB Storage (SSD)" msgid "GB Storage (SSD)"
msgstr "GB Storage (SSD)" msgstr "GB Storage (SSD)"

View file

@ -12,7 +12,7 @@
'ram': { 'ram': {
'id': 'ramValue', 'id': 'ramValue',
'value': 2, 'value': 2,
'min': 2, 'min': 1,
'max': 200, 'max': 200,
'interval': 1 'interval': 1
}, },

View file

@ -29,8 +29,8 @@
<div class="form-group"> <div class="form-group">
<div class="description input"> <div class="description input">
<i class="fa fa-minus left" data-minus="ram" aria-hidden="true"></i> <i class="fa fa-minus left" data-minus="ram" aria-hidden="true"></i>
<input id="ramValue" class="input-price select-number" type="number" min="2" max="200" name="ram" <input id="ramValue" class="input-price select-number" type="number" min="1" max="200" name="ram"
data-error="{% trans 'Please enter a value in range 2 - 200.' %}" required> data-error="{% trans 'Please enter a value in range 1 - 200.' %}" required>
<span> GB RAM</span> <span> GB RAM</span>
<i class="fa fa-plus right" data-plus="ram" aria-hidden="true"></i> <i class="fa fa-plus right" data-plus="ram" aria-hidden="true"></i>
</div> </div>

View file

@ -41,6 +41,7 @@ from utils.forms import (
from utils.hosting_utils import get_vm_price from utils.hosting_utils import get_vm_price
from utils.mailer import BaseEmail from utils.mailer import BaseEmail
from utils.stripe_utils import StripeUtils from utils.stripe_utils import StripeUtils
from utils.tasks import send_plain_email_task
from utils.views import ( from utils.views import (
PasswordResetViewMixin, PasswordResetConfirmViewMixin, LoginViewMixin, PasswordResetViewMixin, PasswordResetConfirmViewMixin, LoginViewMixin,
ResendActivationLinkViewMixin ResendActivationLinkViewMixin
@ -945,7 +946,7 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View):
raise ValidationError(_('Invalid number of cores')) raise ValidationError(_('Invalid number of cores'))
def validate_memory(self, value): def validate_memory(self, value):
if (value > 200) or (value < 2): if (value > 200) or (value < 1):
raise ValidationError(_('Invalid RAM size')) raise ValidationError(_('Invalid RAM size'))
def validate_storage(self, value): def validate_storage(self, value):
@ -1036,7 +1037,7 @@ class VirtualMachineView(LoginRequiredMixin, View):
) )
return None return None
except Exception as error: except Exception as error:
print(error) logger.error(str(error))
raise Http404() raise Http404()
def get_success_url(self): def get_success_url(self):
@ -1079,49 +1080,88 @@ class VirtualMachineView(LoginRequiredMixin, View):
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
response = {'status': False} response = {'status': False}
admin_email_body = {}
owner = self.request.user owner = self.request.user
vm = self.get_object() vm = self.get_object()
opennebula_vm_id = self.kwargs.get('pk')
manager = OpenNebulaManager( manager = OpenNebulaManager(
email=owner.email, email=owner.email,
password=owner.password password=owner.password
) )
try: try:
vm_data = VirtualMachineSerializer(manager.get_vm(vm.id)).data vm_data = VirtualMachineSerializer(manager.get_vm(vm.id)).data
vm_name = vm_data.get('name') vm_name = vm_data.get('name')
except WrongIdError: except WrongIdError as wrong_id_err:
logger.error(str(wrong_id_err))
return redirect(reverse('hosting:virtual_machines')) return redirect(reverse('hosting:virtual_machines'))
# Cancel Stripe subscription
stripe_utils = StripeUtils()
try:
hosting_order = HostingOrder.objects.get(
vm_id=vm.id
)
result = stripe_utils.unsubscribe_customer(
subscription_id=hosting_order.subscription_id
)
stripe_subscription_obj = result.get('response_object')
# Check if the subscription was canceled
if (stripe_subscription_obj is None or
stripe_subscription_obj.status != 'canceled'):
error_msg = result.get('error')
logger.error(
'Error canceling subscription for {user} and vm id '
'{vm_id}'.format(user=owner.email, vm_id=vm.id)
)
logger.error(error_msg)
admin_email_body['stripe_error_msg'] = error_msg
except HostingOrder.DoesNotExist:
error_msg = (
"HostingOrder corresponding to vm_id={vm_id} does"
"not exist. Hence, can not find subscription to "
"cancel ".format(vm_id=vm.id)
)
logger.error(error_msg)
admin_email_body['stripe_error_msg'] = error_msg
terminated = manager.delete_vm(vm.id) terminated = manager.delete_vm(vm.id)
if not terminated: if not terminated:
response['text'] = ugettext( logger.debug(
'Error terminating VM') + opennebula_vm_id "manager.delete_vm returned False. Hence, error making "
"xml-rpc call to delete vm failed."
)
response['text'] = ugettext('Error terminating VM') + vm.id
else: else:
for t in range(15): for t in range(15):
try: try:
manager.get_vm(opennebula_vm_id) manager.get_vm(vm.id)
except WrongIdError: except WrongIdError:
response['status'] = True response['status'] = True
response['text'] = ugettext('Terminated') response['text'] = ugettext('Terminated')
vm_detail_obj = VMDetail.objects.filter( vm_detail_obj = VMDetail.objects.filter(
vm_id=opennebula_vm_id).first() vm_id=vm.id
).first()
vm_detail_obj.terminated_at = datetime.utcnow() vm_detail_obj.terminated_at = datetime.utcnow()
vm_detail_obj.save() vm_detail_obj.save()
break except BaseException as base_exception:
except BaseException: logger.error(
"manager.get_vm({vm_id}) returned exception: "
"{details}.".format(
details=str(base_exception), vm_id=vm.id
)
)
break break
else: else:
sleep(2) sleep(2)
context = { context = {
'vm_name': vm_name, 'vm_name': vm_name,
'base_url': "{0}://{1}".format(self.request.scheme, 'base_url': "{0}://{1}".format(
self.request.get_host()), self.request.scheme, self.request.get_host()
),
'page_header': _('Virtual Machine %(vm_name)s Cancelled') % { 'page_header': _('Virtual Machine %(vm_name)s Cancelled') % {
'vm_name': vm_name} 'vm_name': vm_name
}
} }
email_data = { email_data = {
'subject': context['page_header'], 'subject': context['page_header'],
@ -1133,6 +1173,18 @@ class VirtualMachineView(LoginRequiredMixin, View):
} }
email = BaseEmail(**email_data) email = BaseEmail(**email_data)
email.send() email.send()
admin_email_body.update(response)
email_to_admin_data = {
'subject': "Deleted VM and Subscription for VM {vm_id} and "
"user: {user}".format(
vm_id=vm.id, user=owner.email
),
'from_email': settings.DCL_SUPPORT_FROM_ADDRESS,
'to': ['info@ungleich.ch'],
'body': "\n".join(
["%s=%s" % (k, v) for (k, v) in admin_email_body.items()]),
}
send_plain_email_task.delay(email_to_admin_data)
return HttpResponse( return HttpResponse(
json.dumps(response), json.dumps(response),
content_type="application/json" content_type="application/json"

View file

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-12-22 01:00+0530\n" "POT-Creation-Date: 2017-12-23 05:18+0530\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -71,14 +71,14 @@ msgstr "Die Chronik von ungleich"
msgid "The first incarnation of ungleich" msgid "The first incarnation of ungleich"
msgstr "Die erste Inkarnation von ungleich" msgstr "Die erste Inkarnation von ungleich"
msgid "in Germany" msgid "in Germany."
msgstr "in Deutschland" msgstr "in Deutschland."
msgid "ungleich founded" msgid "ungleich founded"
msgstr "ungleich gegründet" msgstr "ungleich gegründet"
msgid "in Switzerland" msgid "in Switzerland."
msgstr "in der Schweiz" msgstr "in der Schweiz."
msgid "ungleich present at various conferences" msgid "ungleich present at various conferences"
msgstr "ungleich präsent an mehreren Konferenzen" msgstr "ungleich präsent an mehreren Konferenzen"
@ -107,7 +107,7 @@ msgstr "ungleich bietet einen PC-Grundkurs für Flüchtlinge an."
msgid "" msgid ""
"ungleich starts computer learning club for locals, \"Digitale Building " "ungleich starts computer learning club for locals, \"Digitale Building "
"ungleich.\"" "ungleich\"."
msgstr "" msgstr ""
"ungleich gründet den Verein Digitale Bildung ungleich für Ortsansässige." "ungleich gründet den Verein Digitale Bildung ungleich für Ortsansässige."
@ -116,7 +116,7 @@ msgid ""
"startup in canton Zürich." "startup in canton Zürich."
msgstr "" msgstr ""
"ungleich verkauft das Projekt <a href=\"https://www.alplora.ch/de/" "ungleich verkauft das Projekt <a href=\"https://www.alplora.ch/de/"
"\">AlpLora</a> an ein IoT-Startup aus dem Kanton Zürich" "\">AlpLora</a> an ein IoT-Startup aus dem Kanton Zürich."
msgid "" msgid ""
"ungleich showcases the most affordable Swiss VM hosting, Data Center Light." "ungleich showcases the most affordable Swiss VM hosting, Data Center Light."
@ -157,8 +157,37 @@ msgstr "Copyright © ungleich GmbH"
msgid "ungleich Home" msgid "ungleich Home"
msgstr "ungleich Home" msgstr "ungleich Home"
msgid "We Design, Configure &amp; Maintain<br>Your Linux Infrastructure " msgid "Hosting"
msgstr "Wir designen, erstellen und warten<br>Ihre Linux-Infrastruktur" msgstr "Hosting"
msgid ""
"Ruby on Rails. Java hosting, Django hosting, we make it everything run "
"smooth and safe."
msgstr ""
"Ruby on Rails. Java hosting, Django hosting, wir garantieren einen "
"reibungslosen Ablauf."
msgid "Configuration as a Service"
msgstr "Konfiguration als Service"
msgid ""
"Ruby on Rails, Django, Java, Webserver, Mailserver, any infrastructure that "
"needs to configured, we provide comprehensive solutions. Amazon, rackspace "
"or bare metal servers, we configure for you."
msgstr ""
"Ruby on Rails, Django, Java, Webserver, Mailserver, jegliche Infrastruktur "
"welche eine Konfiguration braucht, wir offerieren umfassende Lösungen, "
"Amazon, Rackspace oder Bare Metal Servers, wir konfigurieren alles."
msgid "Linux System Engineering"
msgstr "Linux System Engineering"
msgid ""
"Let your developers develop! We take care of your system administration. "
"Gentoo, Archlinux, Debian, Ubuntu, and many more."
msgstr ""
"Lassen sie ihre Entwickler entwickeln! Wir kümmern uns um ihre "
"Systemadministration. Gentoo, Archlinux, Debian, Ubuntu und viele mehr."
msgid "Our Products" msgid "Our Products"
msgstr "Unsere Produkte" msgstr "Unsere Produkte"
@ -216,38 +245,6 @@ msgstr ""
"Unser erstklassiges Konfigurationsmanagement ist erfrischend einfach und " "Unser erstklassiges Konfigurationsmanagement ist erfrischend einfach und "
"zuverlässig." "zuverlässig."
msgid "Hosting"
msgstr "Hosting"
msgid ""
"Ruby on Rails. Java hosting, Django hosting, we make it everything run "
"smooth and safe."
msgstr ""
"Ruby on Rails. Java hosting, Django hosting, wir garantieren einen "
"reibungslosen Ablauf"
msgid "Configuration as a Service"
msgstr "Konfiguration als Service"
msgid ""
"Ruby on Rails, Django, Java, Webserver, Mailserver, any infrastructure that "
"needs to configured, we provide comprehensive solutions. Amazon, rackspace "
"or bare metal servers, we configure for you."
msgstr ""
"Ruby on Rails, Django, Java, Webserver, Mailserver, jegliche Infrastruktur "
"welche eine Konfiguration braucht, wir offerieren umfassende Lösungen, "
"Amazon, Rackspace oder Bare Metal Servers, wir konfigurieren alles."
msgid "Linux System Engineering"
msgstr "Linux System Engineering"
msgid ""
"Let your developers develop! We take care of your system administration. "
"Gentoo, Archlinux, Debian, Ubuntu, and many more."
msgstr ""
"Lassen sie ihre Entwickler entwickeln! Wir kümmern uns um ihre "
"Systemadministration. Gentoo, Archlinux, Debian, Ubuntu und viele mehr."
msgid "Why ungleich?*" msgid "Why ungleich?*"
msgstr "Warum ungleich?" msgstr "Warum ungleich?"
@ -363,6 +360,9 @@ msgid "If you have any question, just send us an email."
msgstr "" msgstr ""
"Wenn Sie irgendwelche Fragen haben, schicken Sie uns einfach eine E-Mail." "Wenn Sie irgendwelche Fragen haben, schicken Sie uns einfach eine E-Mail."
#~ msgid "We Design, Configure &amp; Maintain<br>Your Linux Infrastructure "
#~ msgstr "Wir designen, erstellen und warten<br>Ihre Linux-Infrastruktur"
#~ msgid "Hosting Products " #~ msgid "Hosting Products "
#~ msgstr "Hosting Produkte" #~ msgstr "Hosting Produkte"

View file

@ -30,7 +30,7 @@ class Migration(migrations.Migration):
blank=True, help_text='Text for the button, if a link is provided.', max_length=50, null=True)), blank=True, help_text='Text for the button, if a link is provided.', max_length=50, null=True)),
('heading', models.CharField( ('heading', models.CharField(
blank=True, help_text='An optional title for this slide.', max_length=100, null=True)), blank=True, help_text='An optional title for this slide.', max_length=100, null=True)),
('video', filer.fields.file.FilerFileField(blank=True, help_text='Leavig this blank will make the image as the background.', ('video', filer.fields.file.FilerFileField(blank=True, help_text='Leaving this blank will make the image as the background.',
null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='ungleich_header_item_video', to='filer.File')), null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='ungleich_header_item_video', to='filer.File')),
], ],
options={ options={

View file

@ -372,7 +372,7 @@ section h3.section-subheading {
@media(min-width:768px) { @media(min-width:768px) {
section { section {
padding: 125px 0; padding: 80px 0;
} }
section h2.section-heading { section h2.section-heading {
font-size: 40px; font-size: 40px;
@ -985,4 +985,4 @@ section h3.section-comment {
.carousel-author { .carousel-author {
height: 72px; height: 72px;
} }
} }

View file

@ -220,11 +220,11 @@
</div> </div>
<div class="contact-details"> <div class="contact-details">
<div class="subtitle"> <div class="subtitle">
<h3>ungleich GmbH</h3> <h3>ungleich glarus ag</h3>
</div> </div>
<div class="description"> <div class="description">
<p>glasfaser@ungleich.ch</p> <p>glasfaser@ungleich.ch</p>
<p>In der Au 7, Schwanden 8762</p> <p>Bahnhofstrasse 1, 8783 Linthal</p>
<p>Switzerland</p> <p>Switzerland</p>
</div> </div>
</div> </div>

View file

@ -17,7 +17,7 @@
</div> </div>
<div class="timeline-body"> <div class="timeline-body">
<p>{% trans "The first incarnation of ungleich" %}</p> <p>{% trans "The first incarnation of ungleich" %}</p>
<p>{% trans "in Germany" %}</p> <p>{% trans "in Germany." %}</p>
</div> </div>
</div> </div>
</li> </li>
@ -31,7 +31,7 @@
</div> </div>
<div class="timeline-body"> <div class="timeline-body">
<p>{% trans "ungleich founded" %} </p> <p>{% trans "ungleich founded" %} </p>
<p>{% trans "in Switzerland" %}</p> <p>{% trans "in Switzerland." %}</p>
</div> </div>
</div> </div>
</li> </li>
@ -44,7 +44,7 @@
<h4>2014</h4> <h4>2014</h4>
</div> </div>
<div class="timeline-body"> <div class="timeline-body">
<p>{% trans "ungleich present at various conferences" %}: <br><a href="http://www.linuxtag.org/2014/en/program/talk-details/?eventid=1238">Linuxtag</a>, <a href="https://www.usenix.org/conference/ucms14/summit-program/presentation/schottelius">UCMS</a>, Linux Erfa, <a href="https://www.ethz.ch/en.html">ETH Zurich</a><br> <p>{% trans "ungleich present at various conferences" %}: <br><a href="http://www.linuxtag.org/2014/en/program/talk-details/?eventid=1238">Linuxtag</a>, <a href="https://www.usenix.org/conference/ucms14/summit-program/presentation/schottelius">UCMS</a>, Linux Erfa, <a href="https://www.ethz.ch/en.html">ETH Zurich</a>.<br>
</p> </p>
</div> </div>
</div> </div>
@ -62,7 +62,7 @@
<p>{% trans "and introduces affordable 24X7 support." %}</p> <p>{% trans "and introduces affordable 24X7 support." %}</p>
<p> <p>
{% trans "ungleich launches" %} {% trans "ungleich launches" %}
<a href="https://digitalglarus.ch">{% trans "Digital Glarus project" %}</a> <a href="https://digitalglarus.ch">{% trans "Digital Glarus project" %}</a>.
</p> </p>
</div> </div>
</div> </div>
@ -93,19 +93,6 @@
</div> </div>
</div> </div>
</li> </li>
<li class="timeline-inverted">
<div class="timeline-image">
<img class="img-circle img-responsive" src="{% static 'ungleich_page/img/about/2017a.jpg' %}">
</div>
<div class="timeline-panel wow slideInRight">
<div class="timeline-heading">
<h4>2017</h4>
</div>
<div class="timeline-body">
<p>{% trans 'ungleich starts computer learning club for locals, "Digitale Building ungleich."' %}</p>
</div>
</div>
</li>
<li class="timeline-inverted"> <li class="timeline-inverted">
<div class="timeline-image"> <div class="timeline-image">
<img class="img-circle img-responsive" src="{% static 'ungleich_page/img/about/2017b.jpg' %}"> <img class="img-circle img-responsive" src="{% static 'ungleich_page/img/about/2017b.jpg' %}">
@ -115,6 +102,7 @@
<h4>2017</h4> <h4>2017</h4>
</div> </div>
<div class="timeline-body"> <div class="timeline-body">
<p>{% trans 'ungleich starts computer learning club for locals, "Digitale Building ungleich".' %}</p>
<p>{% blocktrans %}ungleich sells <a href="https://www.alplora.ch/de/">Alplora</a> to an IoT startup in canton Zürich.{% endblocktrans %}</p> <p>{% blocktrans %}ungleich sells <a href="https://www.alplora.ch/de/">Alplora</a> to an IoT startup in canton Zürich.{% endblocktrans %}</p>
<p>{% trans "ungleich showcases the most affordable Swiss VM hosting, Data Center Light." %}</p> <p>{% trans "ungleich showcases the most affordable Swiss VM hosting, Data Center Light." %}</p>
</div> </div>

View file

@ -18,11 +18,8 @@
</video> </video>
</div> </div>
<div class="container"> <div class="container">
<div class="intro-cap"> <div class="intro-cap">{% trans "Hosting" %}</div>
{% trans "We Design, Configure &amp; Maintain<br>Your Linux Infrastructure " %} <p class="intro_lead">{% trans "Ruby on Rails. Java hosting, Django hosting, we make it everything run smooth and safe." %}</p>
</div>
<p class="intro_lead">Ruby on Rails, Django, Java, Webserver, Mailserver, any infrastructure that needs to configured, we provide comprehensive solutions. Amazon, rackspace or bare metal servers, we configure for you.</p>
<a class="btn btn-trans" href="">Learn More</a>
</div> </div>
</div> </div>
<div class="item"> <div class="item">
@ -33,11 +30,9 @@
</video> </video>
</div> </div>
<div class="container"> <div class="container">
<div class="intro-cap"> <div class="intro-cap">{% trans "Configuration as a Service" %}</div>
{% trans "We Design, Configure &amp; Maintain<br>Your Linux Infrastructure " %} <p class="intro_lead">{% trans "Ruby on Rails, Django, Java, Webserver, Mailserver, any infrastructure that needs to configured, we provide comprehensive solutions. Amazon, rackspace or bare metal servers, we configure for you." %}</p>
</div> <!-- <a class="btn btn-trans" href="">Learn More</a> -->
<p class="intro_lead">Ruby on Rails, Django, Nothing else.</p>
<a class="btn btn-trans" href="">Learn More</a>
</div> </div>
</div> </div>
<div class="item"> <div class="item">
@ -47,10 +42,8 @@
</video> </video>
</div> </div>
<div class="container"> <div class="container">
<div class="intro-cap"> <div class="intro-cap">{% trans "Linux System Engineering" %}</div>
{% trans "We Design, Configure &amp; Maintain<br>Your Linux Infrastructure " %} <p class="intro_lead">{% trans "Let your developers develop! We take care of your system administration. Gentoo, Archlinux, Debian, Ubuntu, and many more." %}</p>
</div>
<a class="btn btn-trans" href="">Learn More</a>
</div> </div>
</div> </div>
</div> </div>

View file

@ -232,6 +232,17 @@ class StripeUtils(object):
) )
return subscription_result return subscription_result
@handleStripeError
def unsubscribe_customer(self, subscription_id):
"""
Cancels a given subscription
:param subscription_id: The Stripe subscription id string
:return:
"""
sub = stripe.Subscription.retrieve(subscription_id)
return sub.delete()
@handleStripeError @handleStripeError
def make_payment(self, customer, amount, token): def make_payment(self, customer, amount, token):
charge = self.stripe.Charge.create( charge = self.stripe.Charge.create(