Merge master into task/3934/move_hosting_order_out_of_celery_task

and resolve some conflicts
This commit is contained in:
M.Ravi 2018-06-27 12:08:48 +02:00
commit 2cd73b313a
45 changed files with 1148 additions and 674 deletions

View file

@ -1,3 +1,21 @@
Next:
* bugfix: Fix flake8 error that was ignored in release 1.9.1
1.9.1: 2018-06-24
* #4799: [dcl] Show selected vm templates only in calculator (PR #638)
* #4847: [comic] Add google analytics code for comic.ungleich.ch (PR #639)
* feature: add vm_type option to vm_template and dcl calculator to distinguish between public and ipv6only templates (PR #635)
1.9: 2018-05-16
* #4559: [cms] enable discount on cms calculator
1.8: 2018-05-01
* #4527: [hosting] cms calculator on non-cms pages for the hosting app
* bgfix: [dcl] navbar dropdown target fix
* bgfix: [hosting] login/signup pages footer link fix
1.7.2: 2018-04-30
* bgfix: [cms] add favicon extension to ungleich cms pages
* #4474: [cms] reduce heading slider side padding
1.7.1: 2018-04-21
* #4481: [blog] fix de blog pages 500 error
* #4370: [comic] new url /comic to show only comic blogs
1.7: 2018-04-20
* bgfix: [all] Make /blog available on all domains
* #4367: [dcl] email logo resolution fix

View file

@ -2,7 +2,7 @@ from django.contrib import admin
from cms.admin.placeholderadmin import PlaceholderAdminMixin
from cms.extensions import PageExtensionAdmin
from .cms_models import CMSIntegration, CMSFaviconExtension
from .models import VMPricing
from .models import VMPricing, VMTemplate
class CMSIntegrationAdmin(PlaceholderAdminMixin, admin.ModelAdmin):
@ -16,3 +16,4 @@ class CMSFaviconExtensionAdmin(PageExtensionAdmin):
admin.site.register(CMSIntegration, CMSIntegrationAdmin)
admin.site.register(CMSFaviconExtension, CMSFaviconExtensionAdmin)
admin.site.register(VMPricing)
admin.site.register(VMTemplate)

View file

@ -2,6 +2,9 @@ from cms.extensions import PageExtension
from cms.extensions.extension_pool import extension_pool
from cms.models.fields import PlaceholderField
from cms.models.pluginmodel import CMSPlugin
from django import forms
from django.conf import settings
from django.contrib.postgres.fields import ArrayField
from django.contrib.sites.models import Site
from django.db import models
from django.utils.safestring import mark_safe
@ -9,7 +12,7 @@ from djangocms_text_ckeditor.fields import HTMLField
from filer.fields.file import FilerFileField
from filer.fields.image import FilerImageField
from datacenterlight.models import VMPricing
from datacenterlight.models import VMPricing, VMTemplate
class CMSIntegration(models.Model):
@ -26,6 +29,10 @@ class CMSIntegration(models.Model):
navbar_placeholder = PlaceholderField(
'datacenterlight_navbar', related_name='dcl-navbar-placeholder+'
)
calculator_placeholder = PlaceholderField(
'datacenterlight_calculator',
related_name='dcl-calculator-placeholder+'
)
domain = models.ForeignKey(Site, null=True, blank=True)
class Meta:
@ -288,10 +295,58 @@ class DCLSectionPromoPluginModel(CMSPlugin):
return extra_classes
class DCLCustomPricingModel(CMSPlugin):
class MultipleChoiceArrayField(ArrayField):
"""
A field that allows us to store an array of choices.
Uses Django's Postgres ArrayField
and a MultipleChoiceField for its formfield.
"""
VMTemplateChoices = []
if settings.OPENNEBULA_DOMAIN != 'test_domain':
VMTemplateChoices = list(
(
str(obj.opennebula_vm_template_id),
(obj.name + ' - ' + VMTemplate.IPV6.title()
if obj.vm_type == VMTemplate.IPV6 else obj.name
)
)
for obj in VMTemplate.objects.all()
)
def formfield(self, **kwargs):
defaults = {
'form_class': forms.MultipleChoiceField,
'choices': self.VMTemplateChoices,
}
defaults.update(kwargs)
# Skip our parent's formfield implementation completely as we don't
# care for it.
# pylint:disable=bad-super-call
return super(ArrayField, self).formfield(**defaults)
class DCLCalculatorPluginModel(CMSPlugin):
pricing = models.ForeignKey(
VMPricing,
related_name="dcl_custom_pricing_vm_pricing",
help_text='Choose a pricing that will be associated with this '
'Calculator'
)
vm_type = models.CharField(
max_length=50, choices=VMTemplate.VM_TYPE_CHOICES,
default=VMTemplate.PUBLIC
)
vm_templates_to_show = MultipleChoiceArrayField(
base_field=models.CharField(
blank=True,
max_length=256,
),
default=list,
blank=True,
help_text="Recommended: If you wish to show all templates of the "
"corresponding VM Type (public/ipv6only), please do not "
"select any of the items in the above field. "
"This will allow any new template(s) added "
"in the backend to be automatically listed in this "
"calculator instance."
)

View file

@ -6,9 +6,9 @@ from .cms_models import (
DCLFooterPluginModel, DCLLinkPluginModel, DCLNavbarDropdownPluginModel,
DCLSectionIconPluginModel, DCLSectionImagePluginModel,
DCLSectionPluginModel, DCLNavbarPluginModel,
DCLSectionPromoPluginModel, DCLCustomPricingModel
DCLSectionPromoPluginModel, DCLCalculatorPluginModel
)
from .models import VMTemplate, VMPricing
from .models import VMTemplate
@plugin_pool.register_plugin
@ -21,7 +21,7 @@ class DCLSectionPlugin(CMSPluginBase):
allow_children = True
child_classes = [
'DCLSectionIconPlugin', 'DCLSectionImagePlugin',
'DCLSectionPromoPlugin', 'UngleichHTMLPlugin'
'DCLSectionPromoPlugin', 'UngleichHTMLPlugin', 'DCLCalculatorPlugin'
]
def render(self, context, instance, placeholder):
@ -30,14 +30,17 @@ class DCLSectionPlugin(CMSPluginBase):
)
context['children_to_side'] = []
context['children_to_content'] = []
context['children_calculator'] = []
if instance.child_plugin_instances is not None:
right_children = [
'DCLSectionImagePluginModel',
'DCLSectionIconPluginModel'
'DCLSectionIconPluginModel',
]
for child in instance.child_plugin_instances:
if child.__class__.__name__ in right_children:
context['children_to_side'].append(child)
elif child.plugin_type == 'DCLCalculatorPlugin':
context['children_calculator'].append(child)
else:
context['children_to_content'].append(child)
return context
@ -75,52 +78,28 @@ class DCLSectionPromoPlugin(CMSPluginBase):
@plugin_pool.register_plugin
class DCLCalculatorPlugin(CMSPluginBase):
module = "Datacenterlight"
name = "DCL Calculator Section Plugin"
model = DCLSectionPluginModel
name = "DCL Calculator Plugin"
model = DCLCalculatorPluginModel
render_template = "datacenterlight/cms/calculator.html"
cache = False
allow_children = True
child_classes = [
'DCLSectionPromoPlugin', 'UngleichHTMLPlugin', 'DCLCustomPricingPlugin'
]
require_parent = True
def render(self, context, instance, placeholder):
context = super(DCLCalculatorPlugin, self).render(
context, instance, placeholder
)
context['templates'] = VMTemplate.objects.all()
context['children_to_content'] = []
pricing_plugin_model = None
if instance.child_plugin_instances is not None:
context['children_to_content'].extend(
instance.child_plugin_instances
)
for child in instance.child_plugin_instances:
if child.__class__.__name__ == 'DCLCustomPricingModel':
# The second clause is just to make sure we pick up the
# most recent CustomPricing, if more than one is present
if (pricing_plugin_model is None or child.pricing_id >
pricing_plugin_model.model.pricing_id):
pricing_plugin_model = child
if pricing_plugin_model:
context['vm_pricing'] = VMPricing.get_vm_pricing_by_name(
name=pricing_plugin_model.pricing.name
)
ids = instance.vm_templates_to_show
if ids:
context['templates'] = VMTemplate.objects.filter(
vm_type=instance.vm_type
).filter(opennebula_vm_template_id__in=ids)
else:
context['vm_pricing'] = VMPricing.get_default_pricing()
context['templates'] = VMTemplate.objects.filter(
vm_type=instance.vm_type
)
return context
@plugin_pool.register_plugin
class DCLCustomPricingPlugin(CMSPluginBase):
module = "Datacenterlight"
name = "DCL Custom Pricing Plugin"
model = DCLCustomPricingModel
render_plugin = False
@plugin_pool.register_plugin
class DCLBannerListPlugin(CMSPluginBase):
module = "Datacenterlight"

View file

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-04-17 19:26+0000\n"
"POT-Creation-Date: 2018-05-12 21:43+0530\n"
"PO-Revision-Date: 2018-03-30 23:22+0000\n"
"Last-Translator: b'Anonymous User <coder.purple+25@gmail.com>'\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -19,6 +19,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Translated-Using: django-rosetta 0.8.1\n"
msgid "CMS Favicon"
msgstr ""
#, python-format
msgid "Your New VM %(vm_name)s at Data Center Light"
msgstr "Deine neue VM %(vm_name)s bei Data Center Light"
@ -140,6 +143,9 @@ msgstr "Monat"
msgid "VAT included"
msgstr "MwSt. inklusive"
msgid "You save"
msgstr "Du sparst"
msgid "Hosted in Switzerland"
msgstr "Standort: Schweiz"
@ -314,6 +320,12 @@ msgstr "exkl. Mehrwertsteuer"
msgid "Month"
msgstr "Monat"
msgid "Discount"
msgstr "Rabatt"
msgid "Will be applied at checkout"
msgstr "wird an der Kasse angewendet"
msgid "Credit Card"
msgstr "Kreditkarte"
@ -386,9 +398,10 @@ msgstr "Zwischensumme"
msgid "VAT"
msgstr "Mehrwertsteuer"
#, python-format
msgid ""
"By clicking \"Place order\" this plan will charge your credit card account "
"with the fee of %(vm_total_price)s CHF/month"
"with %(vm_total_price)s CHF/month"
msgstr ""
"Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit "
"%(vm_total_price)s CHF pro Monat belastet"

View file

@ -10,16 +10,28 @@ class Command(BaseCommand):
help = '''Fetches the VM templates from OpenNebula and populates the dcl
VMTemplate model'''
def get_templates(self, manager, prefix):
templates = manager.get_templates('%s-' % prefix)
dcl_vm_templates = []
for template in templates:
template_name = template.name.lstrip('%s-' % prefix)
template_id = template.id
dcl_vm_template = VMTemplate.create(
template_name, template_id, prefix
)
dcl_vm_templates.append(dcl_vm_template)
return dcl_vm_templates
def handle(self, *args, **options):
try:
manager = OpenNebulaManager()
templates = manager.get_templates()
dcl_vm_templates = []
for template in templates:
template_name = template.name.lstrip('public-')
template_id = template.id
dcl_vm_template = VMTemplate.create(template_name, template_id)
dcl_vm_templates.append(dcl_vm_template)
dcl_vm_templates.extend(
self.get_templates(manager, VMTemplate.PUBLIC)
)
dcl_vm_templates.extend(
self.get_templates(manager, VMTemplate.IPV6)
)
old_vm_templates = VMTemplate.objects.all()
old_vm_templates.delete()

View file

@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2018-04-25 09:20
from __future__ import unicode_literals
import cms.models.fields
from django.db import migrations
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('datacenterlight', '0020_merge'),
('cms', '0014_auto_20160404_1908'),
]
operations = [
migrations.AddField(
model_name='cmsintegration',
name='calculator_placeholder',
field=cms.models.fields.PlaceholderField(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE,
related_name='dcl-calculator-placeholder+', slotname='datacenterlight_calculator', to='cms.Placeholder'),
),
migrations.RenameModel(
old_name='DCLCustomPricingModel',
new_name='DCLCalculatorPluginModel',
),
]

View file

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2018-05-07 02:19
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('datacenterlight', '0021_cmsintegration_calculator_placeholder'),
]
operations = [
migrations.AddField(
model_name='vmpricing',
name='discount_amount',
field=models.DecimalField(
decimal_places=2, default=0, max_digits=6),
),
migrations.AddField(
model_name='vmpricing',
name='discount_name',
field=models.CharField(blank=True, max_length=255, null=True),
),
]

View file

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2018-05-23 22:19
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('datacenterlight', '0022_auto_20180506_1950'),
]
operations = [
migrations.AddField(
model_name='dclcalculatorpluginmodel',
name='vm_type',
field=models.CharField(choices=[('public', 'Public'), ('ipv6only', 'Ipv6Only')], default='public', max_length=50),
),
migrations.AddField(
model_name='vmtemplate',
name='vm_type',
field=models.CharField(choices=[('public', 'Public'), ('ipv6only', 'Ipv6Only')], default='public', max_length=50),
),
]

View file

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2018-06-24 08:23
from __future__ import unicode_literals
import datacenterlight.cms_models
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('datacenterlight', '0023_auto_20180524_0349'),
]
operations = [
migrations.AddField(
model_name='dclcalculatorpluginmodel',
name='vm_templates_to_show',
field=datacenterlight.cms_models.MultipleChoiceArrayField(base_field=models.CharField(blank=True, max_length=256), blank=True, default=list, help_text='Recommended: If you wish to show all templates of the corresponding VM Type (public/ipv6only), please do not select any of the items in the above field. This will allow any new template(s) added in the backend to be automatically listed in this calculator instance.', size=None),
),
]

View file

@ -6,13 +6,29 @@ logger = logging.getLogger(__name__)
class VMTemplate(models.Model):
PUBLIC = 'public'
IPV6 = 'ipv6only'
VM_TYPE_CHOICES = (
(PUBLIC, PUBLIC.title()),
(IPV6, IPV6.title()),
)
name = models.CharField(max_length=50)
opennebula_vm_template_id = models.IntegerField()
vm_type = models.CharField(
max_length=50, choices=VM_TYPE_CHOICES, default=PUBLIC
)
def __str__(self):
return '%s - %s - %s' % (
self.opennebula_vm_template_id, self.vm_type, self.name
)
@classmethod
def create(cls, name, opennebula_vm_template_id):
def create(cls, name, opennebula_vm_template_id, vm_type):
vm_template = cls(
name=name, opennebula_vm_template_id=opennebula_vm_template_id)
name=name, opennebula_vm_template_id=opennebula_vm_template_id,
vm_type=vm_type
)
return vm_template
@ -34,16 +50,29 @@ class VMPricing(models.Model):
hdd_unit_price = models.DecimalField(
max_digits=7, decimal_places=6, default=0
)
discount_name = models.CharField(max_length=255, null=True, blank=True)
discount_amount = models.DecimalField(
max_digits=6, decimal_places=2, default=0
)
def __str__(self):
return self.name + ' => ' + ' - '.join([
display_str = self.name + ' => ' + ' - '.join([
'{}/Core'.format(self.cores_unit_price.normalize()),
'{}/GB RAM'.format(self.ram_unit_price.normalize()),
'{}/GB SSD'.format(self.ssd_unit_price.normalize()),
'{}/GB HDD'.format(self.hdd_unit_price.normalize()),
'{}% VAT'.format(self.vat_percentage.normalize())
if not self.vat_inclusive else 'VAT-Incl', ]
if not self.vat_inclusive else 'VAT-Incl',
])
if self.discount_amount:
display_str = ' - '.join([
display_str,
'{} {}'.format(
self.discount_amount,
self.discount_name if self.discount_name else 'Discount'
)
])
return display_str
@classmethod
def get_vm_pricing_by_name(cls, name):

View file

@ -150,3 +150,12 @@ footer .dcl-link-separator::before {
border-radius: 100%;
background: #777;
}
.mb-0 {
margin-bottom: 0;
}
.thin-hr {
margin-top: 10px;
margin-bottom: 10px;
}

View file

@ -55,7 +55,7 @@
flex: 1;
}
.header_slider > .carousel .item .container {
.header_slider > .carousel .item .container-fluid {
overflow: auto;
padding: 50px 20px 60px;
height: 100%;
@ -104,9 +104,9 @@
.header_slider .carousel-control .fa {
font-size: 4em;
}
.header_slider > .carousel .item .container {
.header_slider > .carousel .item .container-fluid {
overflow: auto;
padding: 75px 50px;
padding: 75px;
}
.header_slider .btn-trans {
padding: 8px 15px;
@ -120,11 +120,6 @@
.header_slider .intro-cap {
font-size: 3.25em;
}
.header_slider > .carousel .item .container {
padding-left: 0;
padding-right: 0;
}
}
.header_slider .intro_lead {

View file

@ -482,6 +482,7 @@
margin: 100px auto 40px;
border: 1px solid #ccc;
padding: 30px 30px 20px;
color: #595959;
}
.order-detail-container .dashboard-title-thin {
@ -503,10 +504,6 @@
margin-bottom: 15px;
}
.order-detail-container .order-details strong {
color: #595959;
}
.order-detail-container h4 {
font-size: 16px;
font-weight: bold;
@ -515,13 +512,28 @@
.order-detail-container p {
margin-bottom: 5px;
color: #595959;
}
.order-detail-container hr {
margin: 15px 0;
}
.order-detail-container .thin-hr {
margin: 10px 0;
}
.order-detail-container .subtotal-price {
font-size: 16px;
}
.order-detail-container .subtotal-price .text-primary {
font-size: 17px;
}
.order-detail-container .total-price {
font-size: 18px;
}
@media (max-width: 767px) {
.order-detail-container {
padding: 15px;

View file

@ -776,7 +776,7 @@ textarea {
width: 100%;
margin: 0 auto;
background: #fff;
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1), 0 0 6px rgba(0, 0, 0, 0.15);
padding-bottom: 40px;
border-radius: 7px;
text-align: center;
@ -929,7 +929,7 @@ textarea {
}
@media(max-width:991px) {
@media(max-width:767px) {
.section-sm-center .split-text,
.section-sm-center .space {
text-align: center !important;

View file

@ -175,14 +175,18 @@
window.coresUnitPrice = 5;
}
if(typeof window.ramUnitPrice === 'undefined'){
window.coresUnitPrice = 2;
window.ramUnitPrice = 2;
}
if(typeof window.ssdUnitPrice === 'undefined'){
window.ssdUnitPrice = 0.6;
}
if(typeof window.discountAmount === 'undefined'){
window.discountAmount = 0;
}
var total = (cardPricing['cpu'].value * window.coresUnitPrice) +
(cardPricing['ram'].value * window.ramUnitPrice) +
(cardPricing['storage'].value * window.ssdUnitPrice);
(cardPricing['storage'].value * window.ssdUnitPrice) -
window.discountAmount;
total = parseFloat(total.toFixed(2));
$("#total").text(total);
}

View file

@ -61,6 +61,7 @@
</div>
{% endplaceholder %}
{% url 'datacenterlight:index' as calculator_form_url %}
{% placeholder 'Datacenterlight Content' %}
{% placeholder 'datacenterlight_footer'%}

View file

@ -1,16 +1,5 @@
<div class="split-section {{ instance.get_extra_classes }}" id="{{ instance.html_id }}">
<div class="container">
<div class="row">
<div class="col-sm-6 {% if instance.text_direction == 'right' %}col-sm-push-6{% endif %} split-text">
{% include "datacenterlight/cms/includes/_section_split_content.html" %}
</div>
<div class="col-sm-6 {% if instance.text_direction == 'right' %}col-sm-pull-6{% endif %}">
<div class="price-calc-section">
<div class="card">
{% include "datacenterlight/includes/_calculator_form.html" %}
</div>
</div>
</div>
</div>
{% include "datacenterlight/includes/_calculator_form.html" with vm_pricing=instance.pricing %}
</div>
</div>

View file

@ -1,7 +1,7 @@
{% load cms_tags %}
<div class="dropdown highlights-dropdown">
<a class="dropdown-toggle url-init dcl-link" href="{{ instance.url|default:'#' }}" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{{ instance.text }}&nbsp;<span class="caret"></span></a>
<a class="dropdown-toggle url-init dcl-link" href="{{ instance.target|default:'#' }}" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{{ instance.text }}&nbsp;<span class="caret"></span></a>
<ul class="dropdown-menu">
{% for plugin in instance.child_plugin_instances %}
{% render_plugin plugin %}

View file

@ -2,17 +2,24 @@
<section class="split-section {{ instance.get_extra_classes }}" id="{{ instance.html_id }}">
<div class="container">
{% if children_to_side|length %}
{% if children_to_side|length or children_calculator|length %}
<div class="row">
<div class="col-sm-6 {% if instance.text_direction == 'right' %}col-sm-push-6{% endif %} split-text">
{% include "datacenterlight/cms/includes/_section_split_content.html" %}
</div>
<div class="col-sm-6 {% if instance.text_direction == 'right' %}col-sm-pull-6{% endif %} split-figure">
{% if children_calculator|length %}
{% for plugin in children_calculator %}
{% render_plugin plugin %}
{% endfor %}
{% endif %}
{% if children_to_side %}
<div class="section-figure">
{% for plugin in children_to_side %}
{% render_plugin plugin %}
{% endfor %}
</div>
{% endif %}
</div>
</div>
{% else %}

View file

@ -8,22 +8,26 @@
window.ramUnitPrice = {{vm_pricing.ram_unit_price|default:0}};
window.ssdUnitPrice = {{vm_pricing.ssd_unit_price|default:0}};
window.hddUnitPrice = {{vm_pricing.hdd_unit_price|default:0}};
window.discountAmount = {{vm_pricing.discount_amount|default:0}};
</script>
{% endif %}
<form id="order_form" method="POST" action="{% url 'datacenterlight:index' %}" data-toggle="validator" role="form">
<form id="order_form" method="POST" action="{{calculator_form_url}}" data-toggle="validator" role="form">
{% csrf_token %}
<div class="title">
<h3>{% trans "VM hosting" %} </h3>
</div>
<div class="price">
<span id="total">15</span>
<span id="total"></span>
<span>CHF/{% trans "month" %}</span>
{% if vm_pricing.vat_inclusive %}
<div class="price-text">
<p>{% trans "VAT included" %}</p>
</div>
<p>
{% if vm_pricing.vat_inclusive %}{% trans "VAT included" %} <br>{% endif %}
{% if vm_pricing.discount_amount %}
{% trans "You save" %} {{ vm_pricing.discount_amount }} CHF
{% endif %}
</p>
</div>
</div>
<div class="descriptions">
<div class="description form-group">

View file

@ -78,7 +78,24 @@
<hr>
<p>{% trans "Configuration"%} <strong class="pull-right">{{request.session.template.name}}</strong></p>
<hr>
<p class="last-p"><strong>{%trans "Total" %}</strong>&nbsp;&nbsp;<small>({% if vm_pricing.vat_inclusive %}{%trans "including VAT" %}{% else %}{%trans "excluding VAT" %}{% endif %})</small> <strong class="pull-right">{{request.session.specs.price|intcomma}} CHF/{% trans "Month" %}</strong></p>
<p>
<strong>{%trans "Total" %}</strong>&nbsp;&nbsp;
<small>
({% if vm_pricing.vat_inclusive %}{%trans "including VAT" %}{% else %}{%trans "excluding VAT" %}{% endif %})
</small>
<strong class="pull-right">{{request.session.specs.price|intcomma}} CHF/{% trans "Month" %}</strong>
</p>
<hr>
{% if vm_pricing.discount_amount %}
<p class="mb-0">
{%trans "Discount" as discount_name %}
<strong>{{ vm_pricing.discount_name|default:discount_name }}</strong>&nbsp;&nbsp;
<strong class="pull-right text-primary">- {{ vm_pricing.discount_amount }} CHF/{% trans "Month" %}</strong>
</p>
<p>
({% trans "Will be applied at checkout" %})
</p>
{% endif %}
</div>
</div>
</div>

View file

@ -55,40 +55,61 @@
<div class="col-sm-6">
<p>
<span>{% trans "Cores" %}: </span>
<span class="pull-right">{{vm.cpu|floatformat}}</span>
<strong class="pull-right">{{vm.cpu|floatformat}}</strong>
</p>
<p>
<span>{% trans "Memory" %}: </span>
<span class="pull-right">{{vm.memory|intcomma}} GB</span>
<strong class="pull-right">{{vm.memory|intcomma}} GB</strong>
</p>
<p>
<span>{% trans "Disk space" %}: </span>
<span class="pull-right">{{vm.disk_size|intcomma}} GB</span>
<strong class="pull-right">{{vm.disk_size|intcomma}} GB</strong>
</p>
</div>
<div class="col-sm-12">
<hr class="thin-hr">
</div>
{% if vm.vat > 0 or vm.discount.amount > 0 %}
<div class="col-sm-6">
<div class="subtotal-price">
{% if vm.vat > 0 %}
<p>
<strong>{% trans "Subtotal" %}: </strong>
<span class="pull-right">{{vm.price|floatformat:2|intcomma}} CHF</span>
<strong class="text-lg">{% trans "Subtotal" %} </strong>
<strong class="pull-right">{{vm.price|floatformat:2|intcomma}} CHF</strong>
</p>
<p>
<span>{% trans "VAT" %} ({{ vm.vat_percent|floatformat:2|intcomma }}%): </span>
<span class="pull-right">{{vm.vat|floatformat:2|intcomma}} CHF</span>
<small>{% trans "VAT" %} ({{ vm.vat_percent|floatformat:2|intcomma }}%) </small>
<strong class="pull-right">{{vm.vat|floatformat:2|intcomma}} CHF</strong>
</p>
{% endif %}
<p>
{% if vm.discount.amount > 0 %}
<p class="text-primary">
{%trans "Discount" as discount_name %}
<strong>{{ vm.discount.name|default:discount_name }} </strong>
<strong class="pull-right">- {{ vm.discount.amount }} CHF</strong>
</p>
{% endif %}
</div>
</div>
<div class="col-sm-12">
<hr class="thin-hr">
</div>
{% endif %}
<div class="col-sm-6">
<p class="total-price">
<strong>{% trans "Total" %} </strong>
<span class="pull-right">{{vm.total_price|floatformat:2|intcomma}} CHF</span>
<strong class="pull-right">{{vm.total_price|floatformat:2|intcomma}} CHF</strong>
</p>
</div>
</div>
</div>
<hr>
<hr class="thin-hr">
</div>
<form id="virtual_machine_create_form" action="" method="POST">
{% csrf_token %}
<div class="row">
<div class="col-sm-8">
<div class="dcl-place-order-text">{% blocktrans with vm_total_price=vm.total_price|floatformat:2|intcomma %}By clicking "Place order" this plan will charge your credit card account with the fee of {{vm_total_price}} CHF/month{% endblocktrans %}.</div>
<div class="dcl-place-order-text">{% blocktrans with vm_total_price=vm.total_price|floatformat:2|intcomma %}By clicking "Place order" this plan will charge your credit card account with {{vm_total_price}} CHF/month{% endblocktrans %}.</div>
</div>
<div class="col-sm-4 order-confirm-btn text-right">
<button class="btn choice-btn" id="btn-create-vm" data-toggle="modal" data-target="#createvm-modal">

View file

@ -156,7 +156,7 @@ class IndexView(CreateView):
)
return HttpResponseRedirect(referer_url + "#order_form")
price, vat, vat_percent = get_vm_price_with_vat(
price, vat, vat_percent, discount = get_vm_price_with_vat(
cpu=cores,
memory=memory,
ssd_size=storage,
@ -169,7 +169,8 @@ class IndexView(CreateView):
'price': price,
'vat': vat,
'vat_percent': vat_percent,
'total_price': price + vat,
'discount': discount,
'total_price': price + vat - discount['amount'],
'pricing_name': vm_pricing_name
}
request.session['specs'] = specs
@ -385,7 +386,7 @@ class OrderConfirmationView(DetailView):
'billing_address_data': (
request.session.get('billing_address_data')
),
'cms_integration': get_cms_integration('default')
'cms_integration': get_cms_integration('default'),
}
return render(request, self.template_name, context)

View file

@ -2,7 +2,7 @@ import logging
from django.conf import settings
from django.shortcuts import render
from django.http import HttpResponseRedirect
from django.http import HttpResponseRedirect, Http404
from django.core.urlresolvers import reverse_lazy, reverse
from django.utils.translation import ugettext_lazy as _
from django.views.generic import TemplateView, UpdateView
@ -834,8 +834,9 @@ class ContactView(FormView):
def blog(request):
tags = ["digitalglarus"]
posts = Post.objects.filter(tags__name__in=tags, publish=True).translated(get_language())
# posts = Post.objects.filter_by_language(get_language()).filter(tags__name__in=tags, publish=True)
posts = (Post.objects
.filter(tags__name__in=tags, publish=True)
.translated(get_language()))
context = {
'post_list': posts,
}
@ -843,9 +844,9 @@ def blog(request):
def blog_detail(request, slug):
# post = Post.objects.filter_by_language(get_language()).filter(slug=slug).first()
post = Post.objects.translated(get_language(), slug=slug).first()
if post is None:
raise Http404()
context = {
'post': post,
}

View file

@ -267,6 +267,10 @@ LANGUAGES = (
LANGUAGE_CODE = 'en-us'
LOCALE_PATHS = [
os.path.join(PROJECT_DIR, 'digitalglarus/locale'),
]
CMS_PLACEHOLDER_CONF = {
'logo_image': {
'name': 'Logo Image',
@ -352,6 +356,18 @@ CMS_PLACEHOLDER_CONF = {
},
]
},
'datacenterlight_calculator': {
'name': _('Datacenterlight Calculator'),
'plugins': ['DCLCalculatorPlugin'],
'default_plugins': [
{
'plugin_type': 'DCLCalculatorPlugin',
'values': {
'pricing_id': 1
},
},
]
},
}
CMS_PERMISSION = True
@ -614,6 +630,7 @@ GOOGLE_ANALYTICS_PROPERTY_IDS = {
'ipv6onlyhosting.ch': 'UA-62285904-10',
'ipv6onlyhosting.net': 'UA-62285904-10',
'ipv6onlyhosting.com': 'UA-62285904-10',
'comic.ungleich.ch': 'UA-62285904-13',
'127.0.0.1:8000': 'localhost',
'dynamicweb-development.ungleich.ch': 'development',
'dynamicweb-staging.ungleich.ch': 'staging'

View file

@ -11,6 +11,7 @@ from hosting.views import (
RailsHostingView, DjangoHostingView, NodeJSHostingView
)
from membership import urls as membership_urls
from ungleich import views as ungleich_views
from ungleich_page.views import LandingView
from django.views.generic import RedirectView
from django.core.urlresolvers import reverse_lazy
@ -54,7 +55,11 @@ urlpatterns += i18n_patterns(
url(r'^blog/(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<day>\d{1,2})/(?P<slug>\w[-\w]*)/$',
RedirectView.as_view(pattern_name='ungleich:post-detail')),
url(r'^blog/$',
RedirectView.as_view(url=reverse_lazy('ungleich:post-list')), name='blog_list_view'),
RedirectView.as_view(url=reverse_lazy('ungleich:post-list')),
name='blog_list_view'),
url(r'^comic/$',
ungleich_views.PostListViewUngleich.as_view(category='comic'),
name='comic_post_list_view'),
url(r'^cms/', include('cms.urls')),
url(r'^blog/', include('djangocms_blog.urls', namespace='djangocms_blog')),
url(r'^$', RedirectView.as_view(url='/cms') if REDIRECT_TO_CMS

View file

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-12-21 00:23+0000\n"
"POT-Creation-Date: 2018-05-12 03:53+0530\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -365,13 +365,22 @@ msgstr "Arbeitsspeicher"
msgid "Disk space"
msgstr "Festplattenkapazität"
msgid "Subtotal"
msgstr "Zwischensumme"
msgid "VAT"
msgstr "Mehrwertsteuer"
msgid "Discount"
msgstr "Rabatt"
msgid "Total"
msgstr "Gesamt"
#, python-format
msgid ""
"By clicking \"Place order\" this plan will charge your credit card account "
"with the fee of %(vm_price)sCHF/month"
"with %(vm_price)s CHF/month"
msgstr ""
"Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit %(vm_price)s CHF "
"pro Monat belastet"
@ -421,6 +430,12 @@ msgstr "Konfiguration"
msgid "including VAT"
msgstr "inkl. Mehrwertsteuer"
msgid "excluding VAT"
msgstr "exkl. Mehrwertsteuer"
msgid "Will be applied at checkout"
msgstr "wird an der Kasse angewendet"
msgid "Billing Address"
msgstr "Rechnungsadresse"
@ -699,6 +714,10 @@ msgstr "Ungültige RAM-Grösse"
msgid "Invalid storage size"
msgstr "Ungültige Speicher-Grösse"
#, python-brace-format
msgid "Incorrect pricing name. Please contact support{support_email}"
msgstr ""
msgid ""
"We could not find the requested VM. Please "
"contact Data Center Light Support."

View file

@ -362,3 +362,12 @@
.locale_date.done{
opacity: 1;
}
.mb-0 {
margin-bottom: 0;
}
.thin-hr {
margin-top: 10px;
margin-bottom: 10px;
}

View file

@ -449,230 +449,6 @@ a.unlink:hover {
color: inherit;
}
/***** DCL payment page **********/
.dcl-order-container {
font-weight: 300;
}
.dcl-order-table-header {
border-bottom: 1px solid #eee;
padding-top: 15px;
padding-bottom: 15px;
font-size: 16px;
color: #333;
text-align: center;
font-weight: 300;
}
.dcl-order-table-content {
border-bottom: 1px solid #eee;
padding-top: 15px;
padding-bottom: 15px;
font-size: 18px;
font-weight: 600;
text-align: center;
}
.tbl-content {
}
.dcl-order-table-total {
border-bottom: 4px solid #eee;
padding-top: 15px;
padding-bottom: 20px;
font-size: 20px;
font-weight: 600;
color: #999;
}
.dcl-order-table-total span {
font-size: 13px;
color: #999;
font-weight: 400;
padding-left: 5px;
}
.dcl-place-order-text{
color: #808080;
}
.dcl-order-table-total .tbl-total {
text-align: center;
color: #000;
padding-left: 44px;
}
.tbl-total .dcl-price-month {
font-size: 16px;
text-transform: capitalize;
color: #000;
}
.tbl-no-padding {
padding: 0px;
}
.dcl-billing-sec {
margin-top: 50px;
}
.dcl-order-sec {
padding: 0 30px;
}
.card-warning-content {
font-weight: 300;
border: 1px solid #a1a1a1;
border-radius: 3px;
padding: 5px;
margin-bottom: 15px;
}
.card-warning-error {
border: 1px solid #EB4D5C;
color: #EB4D5C;
}
.card-warning-addtional-margin {
margin-top: 15px;
}
.stripe-payment-btn {
outline: none;
width: auto;
float: right;
font-style: normal;
font-weight: 300;
position: absolute;
padding-left: 30px;
padding-right: 30px;
right: 0;
}
.card-cvc-element label {
padding-left: 10px;
}
.card-element {
margin-bottom: 10px;
}
.card-element label{
width:100%;
margin-bottom:0px;
}
.my-input {
border-bottom: 1px solid #ccc;
}
.card-cvc-element .my-input {
padding-left: 10px;
}
#card-errors {
clear: both;
padding: 0 0 10px;
color: #eb4d5c;
}
.credit-card-goup{
padding: 0;
}
@media (max-width: 767px) {
.dcl-order-table-total span {
padding-left: 3px;
}
.dcl-order-sec {
padding: 10px 20px 30px 20px;
border-bottom: 4px solid #eee;
}
.tbl-header {
border-bottom: 1px solid #eee;
padding: 10px 0;
}
.tbl-content {
border-bottom: 1px solid #eee;
padding: 10px 0;
}
.dcl-order-table-header {
border-bottom: 0px solid #eee;
padding: 10px 0;
text-align: left;
}
.dcl-order-table-content {
border-bottom: 0px solid #eee;
padding: 10px 0;
text-align: right;
font-size: 16px;
}
.dcl-order-table-total {
font-size: 18px;
color: #000;
padding: 10px 0;
border-bottom: 0px solid #eee;
}
.dcl-order-table-total .tbl-total {
padding: 0px;
text-align: right;
}
.dcl-billing-sec {
margin-top: 30px;
margin-bottom: 30px;
}
.card-expiry-element {
padding-right: 10px;
}
.card-cvc-element {
padding-left: 10px;
}
#billing-form .form-control {
box-shadow: none !important;
font-weight: 400;
}
}
@media (min-width: 1200px) {
.dcl-order-container {
width: 990px;
padding-right: 15px;
padding-left: 15px;
margin-right: auto;
margin-left: auto;
}
}
@media (min-width: 768px) {
.dcl-billing {
padding-right: 65px;
border-right: 1px solid #eee;
}
.dcl-creditcard {
padding-left: 65px;
}
.tbl-tot {
padding-left: 17px;
}
.content-dashboard {
/*width: auto !important;*/
}
}
@media only screen and (max-width: 1040px) and (min-width: 768px) {
.content-dashboard {
width: 96% !important;

View file

@ -3,6 +3,7 @@
margin: 100px auto 40px;
border: 1px solid #ccc;
padding: 15px;
color: #595959;
}
@media(min-width: 768px) {
@ -48,10 +49,6 @@
margin-bottom: 15px;
}
.order-detail-container .order-details strong {
color: #595959;
}
.order-detail-container h4 {
font-size: 16px;
font-weight: bold;
@ -60,13 +57,28 @@
.order-detail-container p {
margin-bottom: 5px;
color: #595959;
}
.order-detail-container hr {
margin: 15px 0;
}
.order-detail-container .thin-hr {
margin: 10px 0;
}
.order-detail-container .subtotal-price {
font-size: 16px;
}
.order-detail-container .subtotal-price .text-primary {
font-size: 17px;
}
.order-detail-container .total-price {
font-size: 18px;
}
@media (max-width: 767px) {
.order-confirm-btn {
text-align: center;
@ -97,3 +109,7 @@
#virtual_machine_create_form {
padding: 15px 0;
}
.dcl-place-order-text {
color: #808080;
}

View file

@ -1,19 +1,35 @@
.payment-container {
padding-top: 70px;
padding-bottom: 11%;
}
.creditcard-box .panel-title {
display: inline;
font-weight: bold;
font-size: 17px;
}
.creditcard-box .checkbox.pull-right {
margin: 0;
}
.creditcard-box .pl-ziro {
padding-left: 0px;
}
.payment-container {padding-top:70px; padding-bottom: 11%;}
.creditcard-box .panel-title {display: inline;font-weight: bold; font-size:17px;}
.creditcard-box .checkbox.pull-right { margin: 0; }
.creditcard-box .pl-ziro { padding-left: 0px; }
.creditcard-box .form-control.error {
border-color: red;
outline: 0;
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(255, 0, 0, 0.6);
}
.creditcard-box label.error {
font-weight: bold;
color: red;
padding: 2px 8px;
margin-top: 2px;
}
.creditcard-box .payment-errors {
font-weight: bold;
color: red;
@ -21,96 +37,221 @@
margin-top: 2px;
}
/* landing page payment new style */
.last-p {
margin-bottom: 0;
}
.dcl-payment-section {
max-width: 391px;
margin: 0 auto 30px;
padding: 0 10px 30px;
border-bottom: 1px solid #edebeb;
height: 100%;
}
.dcl-payment-section hr{
margin-top: 15px;
margin-bottom: 15px;
}
.dcl-payment-section .top-hr {
margin-left: -10px;
}
.dcl-payment-section h3 {
font-weight: 600;
}
.dcl-payment-section p {
/*padding: 0 5px;*/
font-weight: 400;
}
.dcl-payment-section .card-warning-content {
padding: 8px 10px;
font-weight: 300;
}
.dcl-payment-order strong{
font-size: 17px;
}
.dcl-payment-order p {
font-weight: 300;
}
.dcl-payment-section .form-group {
margin-bottom: 10px;
}
.dcl-payment-section .form-control {
box-shadow: none;
padding: 6px 12px;
height: 32px;
}
.dcl-payment-user {
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
.dcl-order-sec {
padding: 0 30px;
}
.dcl-payment-user h4 {
.dcl-billing-sec {
margin-top: 50px;
}
.dcl-order-container {
font-weight: 300;
}
.dcl-order-table-header {
border-bottom: 1px solid #eee;
padding: 15px 10px;
font-size: 16px;
color: #333;
font-weight: 300;
}
.dcl-order-table-content {
border-bottom: 1px solid #eee;
padding: 15px 10px;
font-size: 18px;
font-weight: 600;
font-size: 17px;
}
.dcl-order-table-total {
border-bottom: 4px solid #eee;
padding-top: 15px;
padding-bottom: 20px;
font-size: 20px;
font-weight: 600;
}
.dcl-order-table-total span {
font-size: 13px;
color: #999;
font-weight: 400;
}
.dcl-order-table-total .tbl-total {
text-align: right;
color: #000;
}
.tbl-no-padding {
padding: 0px;
}
.card-warning-content {
font-weight: 300;
border: 1px solid #a1a1a1;
border-radius: 3px;
padding: 5px;
margin-bottom: 15px;
}
.card-warning-error {
border: 1px solid #EB4D5C;
color: #EB4D5C;
}
.card-warning-addtional-margin {
margin-top: 15px;
}
.stripe-payment-btn {
outline: none;
width: auto;
float: right;
font-style: normal;
font-weight: 300;
position: absolute;
padding-left: 30px;
padding-right: 30px;
right: 0;
}
.card-cvc-element label {
padding-left: 10px;
}
.card-element {
margin-bottom: 10px;
}
.card-element label {
width: 100%;
margin-bottom: 0px;
}
.my-input {
border-bottom: 1px solid #ccc;
}
.card-cvc-element .my-input {
padding-left: 10px;
}
#card-errors {
clear: both;
padding: 0 0 10px;
color: #eb4d5c;
}
.credit-card-goup {
padding: 0;
}
@media (max-width: 767px) {
.dcl-order-sec {
padding: 10px 5px 30px;
border-bottom: 4px solid #eee;
}
.dcl-billing-sec {
margin-top: 30px;
margin-bottom: 30px;
padding: 5px;
}
.dcl-billing {
margin-top: 20px;
margin-bottom: 40px;
}
.tbl-header {
border-bottom: 1px solid #eee;
padding-top: 10px;
padding-bottom: 10px;
margin-right: -15px;
}
.dcl-order-table-total .tbl-total {
margin-left: -15px;
}
.dcl-order-table-total .tbl-tot {
margin-right: -15px;
}
.tbl-content {
border-bottom: 1px solid #eee;
padding-top: 10px;
padding-bottom: 10px;
margin-left: -15px;
}
.dcl-order-table-header {
border-bottom: 0px solid #eee;
padding: 10px 0;
text-align: left;
}
.dcl-order-table-content {
border-bottom: 0px solid #eee;
padding: 10px 0;
text-align: right;
font-size: 16px;
}
.dcl-order-table-total {
font-size: 18px;
color: #000;
padding: 10px 0;
border-bottom: 0px solid #eee;
}
.card-expiry-element {
padding-right: 10px;
}
.card-cvc-element {
padding-left: 10px;
}
#billing-form .form-control {
box-shadow: none !important;
font-weight: 400;
}
}
@media (min-width: 768px) {
.dcl-payment-grid {
display: flex;
align-items: stretch;
flex-wrap: wrap;
.dcl-billing {
padding-right: 65px;
border-right: 1px solid #eee;
}
.dcl-payment-box {
width: 50%;
position: relative;
padding: 0 30px;
.dcl-creditcard {
padding-left: 65px;
}
.dcl-payment-box:nth-child(2) {
order: 1;
.dcl-order-table-total .tbl-total,
.dcl-order-table-total .tbl-tot {
padding: 0 10px;
}
.dcl-payment-box:nth-child(4) {
order: 2;
.tbl-header-center,
.tbl-content-center {
text-align: center;
}
.dcl-payment-section {
padding: 15px 10px;
margin-bottom: 0;
border-bottom-width: 5px;
}
.dcl-payment-box:nth-child(2n) .dcl-payment-section {
border-bottom: none;
}
.dcl-payment-box:nth-child(1):after,
.dcl-payment-box:nth-child(2):after {
content: ' ';
display: block;
background: #eee;
width: 1px;
position: absolute;
right: 0;
z-index: 2;
top: 20px;
bottom: 20px;
.tbl-header-right,
.tbl-content-right {
text-align: right;
}
}
@media (min-width: 1200px) {
.dcl-order-container {
width: 990px;
padding-right: 15px;
padding-left: 15px;
margin-right: auto;
margin-left: auto;
}
}

View file

@ -1,7 +1,9 @@
/* Create VM calculator */
.price-calc-section {
padding: 80px 40px !important;
padding: 20px 0 !important;
font-weight: 300;
font-size: 18px;
}
@media (max-width: 768px) {
@ -40,19 +42,19 @@
}
.price-calc-section .card {
width: 50%;
border-radius: 7px;
margin: 0 auto;
background: #fff;
box-shadow: 1px 3px 6px 2px rgba(0, 0, 0, 0.2);
padding-bottom: 30px;
text-align: center;
max-width: 320px;
max-width: 4000px;
position: relative;
}
@media (min-width: 768px) {
.price-calc-section .card {
margin-left: 0;
/* margin-left: 0; */
}
}
@ -85,7 +87,7 @@
}
.price-calc-section .card .description {
padding: 7px 8px 2px;
padding: 12px;
position: relative;
display: flex;
justify-content: space-around !important;
@ -93,7 +95,7 @@
}
.price-calc-section .card .description span {
font-size: 14px;
font-size: 16px;
margin-left: 5px;
/* margin-left: 0px; */
/* justify-self: start; */
@ -104,17 +106,18 @@
}
.price-calc-section .card .description .select-number{
font-size: 16px;
font-size: 18px;
text-align: center;
width: 85px;
padding: 5px 10px;
}
.price-calc-section .card .description i {
color: #29427a;
cursor: pointer;
font-size: 20px;
border: 1px solid #ccc;
padding: 5px 6px 3px;
/* border: 1px solid #ccc; */
/* padding: 5px 6px 3px; */
border-radius: 5px;
}
@ -193,7 +196,7 @@
.price-calc-section .help-block.with-errors {
text-align: center;
margin: 0 0;
padding: 0 0 5px;
padding: 0 0;
}
.price-calc-section .help-block.with-errors ul {
margin-bottom: 0;
@ -209,10 +212,10 @@
display: block;
position: absolute;
bottom: 0;
left: 18%;
left: 0;
z-index: 20;
height: 1px;
width: 65%;
width: 100%;
background: rgba(128, 128, 128, 0.2);
}

View file

@ -153,4 +153,87 @@ $( document ).ready(function() {
$('.navbar-fixed-top.topnav').css('padding-right', topnavPadding-scrollbarWidth);
}
});
/* ---------------------------------------------
Scripts initialization
--------------------------------------------- */
var cardPricing = {
'cpu': {
'id': 'coreValue',
'value': 1,
'min': 1,
'max': 48,
'interval': 1
},
'ram': {
'id': 'ramValue',
'value': 2,
'min': 1,
'max': 200,
'interval': 1
},
'storage': {
'id': 'storageValue',
'value': 10,
'min': 10,
'max': 2000,
'interval': 10
}
};
function _initPricing() {
_fetchPricing();
$('.fa-minus-circle.left').click(function(event) {
var data = $(this).data('minus');
if (cardPricing[data].value > cardPricing[data].min) {
cardPricing[data].value = Number(cardPricing[data].value) - cardPricing[data].interval;
}
_fetchPricing();
});
$('.fa-plus-circle.right').click(function(event) {
var data = $(this).data('plus');
if (cardPricing[data].value < cardPricing[data].max) {
cardPricing[data].value = Number(cardPricing[data].value) + cardPricing[data].interval;
}
_fetchPricing();
});
$('.input-price').change(function() {
var data = $(this).attr("name");
cardPricing[data].value = $('input[name=' + data + ']').val();
_fetchPricing();
});
}
function _fetchPricing() {
Object.keys(cardPricing).map(function(element) {
$('input[name=' + element + ']').val(cardPricing[element].value);
});
_calcPricing();
}
function _calcPricing() {
if(typeof window.coresUnitPrice === 'undefined'){
window.coresUnitPrice = 5;
}
if(typeof window.ramUnitPrice === 'undefined'){
window.ramUnitPrice = 2;
}
if(typeof window.ssdUnitPrice === 'undefined'){
window.ssdUnitPrice = 0.6;
}
if(typeof window.discountAmount === 'undefined'){
window.discountAmount = 0;
}
var total = (cardPricing['cpu'].value * window.coresUnitPrice) +
(cardPricing['ram'].value * window.ramUnitPrice) +
(cardPricing['storage'].value * window.ssdUnitPrice) -
window.discountAmount;
total = parseFloat(total.toFixed(2));
$("#total").text(total);
}
_initPricing();
});

View file

@ -1,5 +1,5 @@
{% load staticfiles bootstrap3%}
{% load i18n %}
{% load staticfiles i18n cms_tags sekizai_tags %}
<!DOCTYPE html>
<html lang="en">
@ -29,6 +29,9 @@
{% block css_extra %}
{% endblock css_extra %}
{% render_block "css" postprocessor "compressor.contrib.sekizai.compress" %}
{% render_block "js" postprocessor "compressor.contrib.sekizai.compress" %}
<!-- Custom Fonts -->
<link href='//fonts.googleapis.com/css?family=Raleway' rel='stylesheet' type='text/css'>
<link href="{% static 'datacenterlight/font-awesome/css/font-awesome.min.css' %}" rel="stylesheet" type="text/css">
@ -48,7 +51,7 @@
</head>
<body>
{% cms_toolbar %}
{% block navbar %}
{% include "hosting/includes/_navbar_user.html" %}

View file

@ -1,7 +1,9 @@
{% extends "hosting/base_short.html" %}
{% load staticfiles bootstrap3 i18n %}
{% load staticfiles bootstrap3 i18n cms_tags %}
{% block content %}
<div class="dashboard-container create-vm-container">
<div class="row">
<div class="col-sm-5">
@ -17,14 +19,8 @@
{% endif %}
</div>
</div>
<div class="col-sm-6">
<div class="price-calc-section no-padding">
<div class="landing card">
<div class="caption">
{% include "hosting/calculator_form.html" %}
</div>
</div>
</div>
<div class="col-sm-6 hosting-calculator">
{% render_placeholder cms_integration.calculator_placeholder %}
</div>
</div>
</div>

View file

@ -114,37 +114,58 @@
<p>
<span>{% trans "Cores" %}: </span>
{% if vm.cores %}
<span class="pull-right">{{vm.cores|floatformat}}</span>
<strong class="pull-right">{{vm.cores|floatformat}}</strong>
{% else %}
<span class="pull-right">{{vm.cpu|floatformat}}</span>
<strong class="pull-right">{{vm.cpu|floatformat}}</strong>
{% endif %}
</p>
<p>
<span>{% trans "Memory" %}: </span>
<span class="pull-right">{{vm.memory}} GB</span>
<strong class="pull-right">{{vm.memory}} GB</strong>
</p>
<p>
<span>{% trans "Disk space" %}: </span>
<span class="pull-right">{{vm.disk_size}} GB</span>
<strong class="pull-right">{{vm.disk_size}} GB</strong>
</p>
</div>
<div class="col-sm-12">
<hr class="thin-hr">
</div>
{% if vm.vat > 0 or vm.discount.amount > 0 %}
<div class="col-sm-6">
<div class="subtotal-price">
{% if vm.vat > 0 %}
<p>
<strong>{% trans "Subtotal" %}: </strong>
<span class="pull-right">{{vm.price|floatformat:2|intcomma}} CHF</span>
<strong>{% trans "Subtotal" %} </strong>
<strong class="pull-right">{{vm.price|floatformat:2|intcomma}} CHF</strong>
</p>
<p>
<span>{% trans "VAT" %} ({{ vm.vat_percent|floatformat:2|intcomma }}%): </span>
<span class="pull-right">{{vm.vat|floatformat:2|intcomma}} CHF</span>
<small>{% trans "VAT" %} ({{ vm.vat_percent|floatformat:2|intcomma }}%) </small>
<strong class="pull-right">{{vm.vat|floatformat:2|intcomma}} CHF</strong>
</p>
{% endif %}
<p>
{% if vm.discount.amount > 0 %}
<p class="text-primary">
{%trans "Discount" as discount_name %}
<strong>{{ vm.discount.name|default:discount_name }} </strong>
<strong class="pull-right">- {{ vm.discount.amount }} CHF</strong>
</p>
{% endif %}
</div>
</div>
<div class="col-sm-12">
<hr class="thin-hr">
</div>
{% endif %}
<div class="col-sm-6">
<p class="total-price">
<strong>{% trans "Total" %} </strong>
<span class="pull-right">{% if vm.total_price %}{{vm.total_price|floatformat:2|intcomma}}{% else %}{{vm.price|floatformat:2|intcomma}}{% endif %} CHF</span>
<strong class="pull-right">{% if vm.total_price %}{{vm.total_price|floatformat:2|intcomma}}{% else %}{{vm.price|floatformat:2|intcomma}}{% endif %} CHF</strong>
</p>
</div>
</div>
</div>
<hr>
<hr class="thin-hr">
</div>
{% if not order %}
{% block submit_btn %}
@ -152,7 +173,7 @@
{% csrf_token %}
<div class="row">
<div class="col-sm-8">
<div class="dcl-place-order-text">{% blocktrans with vm_price=request.session.specs.price %}By clicking "Place order" this plan will charge your credit card account with the fee of {{ vm_price|intcomma }}CHF/month{% endblocktrans %}.</div>
<div class="dcl-place-order-text">{% blocktrans with vm_price=vm.total_price|floatformat:2|intcomma %}By clicking "Place order" this plan will charge your credit card account with {{ vm_price }} CHF/month{% endblocktrans %}.</div>
</div>
<div class="col-sm-4 order-confirm-btn text-right">
<button class="btn choice-btn" id="btn-create-vm" data-href="{% url 'hosting:order-confirmation' %}" data-toggle="modal" data-target="#createvm-modal">

View file

@ -9,53 +9,99 @@
<!-- Credit card form -->
<div class="dcl-order-container">
<div class="payment-container">
<div class="row">
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 dcl-order-sec">
<div class="dcl-order-sec">
<h3><strong>{%trans "Your Order" %}</strong></h3>
<div class="col-xs-6 col-sm-12 col-md-12 col-lg-12 dcl-order-table-header">
<div class="col-xs-12 col-sm-2 col-md-1 col-lg-1 tbl-header">
<div class="row">
<div class="col-xs-6 col-sm-12">
<div class="dcl-order-table-header">
<div class="row">
<div class="col-sm-2">
<div class="tbl-header">
{%trans "Cores" %}
</div>
<div class="col-xs-12 col-sm-3 col-md-4 col-lg-4 tbl-header">
</div>
<div class="col-sm-4">
<div class="tbl-header tbl-header-center">
{%trans "Memory" %}
</div>
<div class="col-xs-12 col-sm-3 col-md-3 col-lg-3 tbl-header">
</div>
<div class="col-sm-3">
<div class="tbl-header tbl-header-center">
{%trans "Disk space" %}
</div>
<div class="col-xs-12 col-sm-4 col-md-4 col-lg-4 tbl-header">
</div>
<div class="col-sm-3">
<div class="tbl-header tbl-header-right">
{%trans "Configuration" %}
</div>
</div>
<div class="col-xs-6 col-sm-12 col-md-12 col-lg-12 dcl-order-table-content">
<div class="col-xs-12 col-sm-2 col-md-1 col-lg-1 tbl-content">
</div>
</div>
</div>
<div class="col-xs-6 col-sm-12">
<div class="dcl-order-table-content">
<div class="row">
<div class="col-sm-2">
<div class="tbl-content">
{{request.session.specs.cpu|floatformat}}
</div>
<div class="col-xs-12 col-sm-3 col-md-4 col-lg-4 tbl-content">
</div>
<div class="col-sm-4">
<div class="tbl-content tbl-content-center">
{{request.session.specs.memory|floatformat}} GB
</div>
<div class="col-xs-12 col-sm-3 col-md-3 col-lg-3 tbl-content">
</div>
<div class="col-sm-3">
<div class="tbl-content tbl-content-center">
{{request.session.specs.disk_size|floatformat|intcomma}} GB
</div>
<div class="col-xs-12 col-sm-4 col-md-4 col-lg-4 tbl-content">
</div>
<div class="col-sm-3">
<div class="tbl-content tbl-content-right">
{{request.session.template.name}}
</div>
</div>
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 dcl-order-table-total">
<div class="col-xs-6 col-sm-6 col-md-6 col-lg-6 tbl-tot tbl-no-padding">
{%trans "Total" %} <span>{%trans "including VAT" %}</span>
</div>
<div class="col-xs-6 col-sm-6 col-md-6 col-lg-6 tbl-no-padding">
<div class="col-xs-12 col-sm-4 col-md-4 col-lg-4"></div>
<div class="col-xs-12 col-sm-6 col-md-6 col-lg-6 tbl-total">{{request.session.specs.price|intcomma}}
CHF<span class="dcl-price-month">/{% trans "Month" %}</span>
</div>
</div>
</div>
</div>
</div>
<div class="dcl-order-table-total">
<div class="row">
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 dcl-billing-sec">
<div class="col-xs-12 col-sm-5 col-md-6 billing dcl-billing">
<div class="col-xs-6">
<div class="tbl-tot">
{%trans "Total" %}&nbsp;
<span>{% if vm_pricing.vat_inclusive %}{%trans "including VAT" %}{% else %}{%trans "excluding VAT" %}{% endif %}</span>
</div>
</div>
<div class="col-xs-6">
<div class="tbl-total">
{{request.session.specs.price|intcomma}} CHF/{% trans "Month" %}
</div>
</div>
</div>
{% if vm_pricing.discount_amount %}
<hr class="thin-hr">
<div class="row">
<div class="col-xs-6">
<div class="tbl-tot">
{%trans "Discount" as discount_name %}
{{ vm_pricing.discount_name|default:discount_name }}&nbsp;&nbsp;<br>
<span>({% trans "Will be applied at checkout" %})</span>
</div>
</div>
<div class="col-xs-6 text-right">
<div class="tbl-total">
<div class="text-primary">- {{ vm_pricing.discount_amount }} CHF/{% trans "Month" %}</div>
</div>
</div>
</div>
{% endif %}
</div>
</div>
<div class="dcl-billing-sec">
<div class="row">
<div class="col-sm-5 col-md-6">
<div class="billing dcl-billing">
<h3><b>{%trans "Billing Address"%}</b></h3>
<hr>
<form role="form" id="billing-form" method="post" action="" novalidate>
@ -65,7 +111,9 @@
{% endfor %}
</form>
</div>
<div class="col-xs-12 col-sm-7 col-md-6 creditcard-box dcl-creditcard">
</div>
<div class="col-sm-7 col-md-6">
<div class="creditcard-box dcl-creditcard">
<h3><b>{%trans "Credit Card"%}</b></h3>
<hr>
<div>
@ -169,6 +217,7 @@
</div>
</div>
</div>
</div>
<!-- stripe key data -->
{% if stripe_key %}

View file

@ -31,8 +31,8 @@ from stored_messages.api import mark_read
from stored_messages.models import Message
from stored_messages.settings import stored_messages_settings
from datacenterlight.models import VMTemplate
from datacenterlight.utils import create_vm
from datacenterlight.models import VMTemplate, VMPricing
from datacenterlight.utils import create_vm, get_cms_integration
from membership.models import CustomUser, StripeCustomer
from opennebula_api.models import OpenNebulaManager
from opennebula_api.serializers import (
@ -43,7 +43,7 @@ from utils.forms import (
BillingAddressForm, PasswordResetRequestForm, UserBillingAddressForm,
ResendActivationEmailForm
)
from utils.hosting_utils import get_vm_price, get_vm_price_with_vat
from utils.hosting_utils import get_vm_price_with_vat
from utils.mailer import BaseEmail
from utils.stripe_utils import StripeUtils
from utils.tasks import send_plain_email_task
@ -652,7 +652,10 @@ class PaymentVMView(LoginRequiredMixin, FormView):
})
context.update({
'stripe_key': settings.STRIPE_API_PUBLIC_KEY
'stripe_key': settings.STRIPE_API_PUBLIC_KEY,
'vm_pricing': VMPricing.get_vm_pricing_by_name(
self.request.session.get('specs', {}).get('pricing_name')
),
})
return context
@ -750,7 +753,7 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView):
context['vm'] = vm_detail.__dict__
context['vm']['name'] = '{}-{}'.format(
context['vm']['configuration'], context['vm']['vm_id'])
price, vat, vat_percent = get_vm_price_with_vat(
price, vat, vat_percent, discount = get_vm_price_with_vat(
cpu=context['vm']['cores'],
ssd_size=context['vm']['disk_size'],
memory=context['vm']['memory'],
@ -759,8 +762,9 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView):
)
context['vm']['vat'] = vat
context['vm']['price'] = price
context['vm']['discount'] = discount
context['vm']['vat_percent'] = vat_percent
context['vm']['total_price'] = price + vat
context['vm']['total_price'] = price + vat - discount['amount']
context['subscription_end_date'] = vm_detail.end_date()
except VMDetail.DoesNotExist:
try:
@ -769,7 +773,7 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView):
)
vm = manager.get_vm(obj.vm_id)
context['vm'] = VirtualMachineSerializer(vm).data
price, vat, vat_percent = get_vm_price_with_vat(
price, vat, vat_percent, discount = get_vm_price_with_vat(
cpu=context['vm']['cores'],
ssd_size=context['vm']['disk_size'],
memory=context['vm']['memory'],
@ -778,8 +782,10 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView):
)
context['vm']['vat'] = vat
context['vm']['price'] = price
context['vm']['discount'] = discount
context['vm']['vat_percent'] = vat_percent
context['vm']['total_price'] = price + vat
context['vm']['total_price'] = price + \
vat - discount['amount']
except WrongIdError:
messages.error(
self.request,
@ -855,7 +861,7 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView):
cpu = specs.get('cpu')
memory = specs.get('memory')
disk_size = specs.get('disk_size')
amount_to_be_charged = specs.get('price')
amount_to_be_charged = specs.get('total_price')
plan_name = StripeUtils.get_stripe_plan_name(cpu=cpu,
memory=memory,
disk_size=disk_size)
@ -1000,7 +1006,10 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View):
@method_decorator(decorators)
def get(self, request, *args, **kwargs):
context = {'templates': VMTemplate.objects.all()}
context = {
'templates': VMTemplate.objects.all(),
'cms_integration': get_cms_integration('default'),
}
return render(request, self.template_name, context)
@method_decorator(decorators)
@ -1012,18 +1021,34 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View):
storage = request.POST.get('storage')
storage_field = forms.IntegerField(validators=[self.validate_storage])
template_id = int(request.POST.get('config'))
pricing_name = request.POST.get('pricing_name')
vm_pricing = VMPricing.get_vm_pricing_by_name(pricing_name)
template = VMTemplate.objects.filter(
opennebula_vm_template_id=template_id).first()
template_data = VMTemplateSerializer(template).data
if vm_pricing is None:
vm_pricing_name_msg = _(
"Incorrect pricing name. Please contact support"
"{support_email}".format(
support_email=settings.DCL_SUPPORT_FROM_ADDRESS
)
)
messages.add_message(
self.request, messages.ERROR, vm_pricing_name_msg,
extra_tags='pricing'
)
return redirect(CreateVirtualMachinesView.as_view())
else:
vm_pricing_name = vm_pricing.name
try:
cores = cores_field.clean(cores)
except ValidationError as err:
msg = '{} : {}.'.format(cores, str(err))
messages.add_message(self.request, messages.ERROR, msg,
extra_tags='cores')
return HttpResponseRedirect(
reverse('datacenterlight:index') + "#order_form")
return redirect(CreateVirtualMachinesView.as_view())
try:
memory = memory_field.clean(memory)
@ -1031,8 +1056,7 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View):
msg = '{} : {}.'.format(memory, str(err))
messages.add_message(self.request, messages.ERROR, msg,
extra_tags='memory')
return HttpResponseRedirect(
reverse('datacenterlight:index') + "#order_form")
return redirect(CreateVirtualMachinesView.as_view())
try:
storage = storage_field.clean(storage)
@ -1040,15 +1064,25 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View):
msg = '{} : {}.'.format(storage, str(err))
messages.add_message(self.request, messages.ERROR, msg,
extra_tags='storage')
return HttpResponseRedirect(
reverse('datacenterlight:index') + "#order_form")
price = get_vm_price(cpu=cores, memory=memory,
disk_size=storage)
return redirect(CreateVirtualMachinesView.as_view())
price, vat, vat_percent, discount = get_vm_price_with_vat(
cpu=cores,
memory=memory,
ssd_size=storage,
pricing_name=vm_pricing_name
)
specs = {
'cpu': cores,
'memory': memory,
'disk_size': storage,
'price': price
'discount': discount,
'price': price,
'vat': vat,
'vat_percent': vat_percent,
'total_price': price + vat - discount['amount'],
'pricing_name': vm_pricing_name
}
request.session['specs'] = specs
@ -1232,7 +1266,10 @@ class VirtualMachineView(LoginRequiredMixin, View):
["%s=%s" % (k, v) for (k, v) in admin_email_body.items()]),
}
send_plain_email_task.delay(email_to_admin_data)
return JsonResponse(response)
return HttpResponse(
json.dumps(response),
content_type="application/json"
)
class HostingBillListView(PermissionRequiredMixin, LoginRequiredMixin,

View file

@ -362,12 +362,12 @@ class OpenNebulaManager():
except:
raise ConnectionRefusedError
def get_templates(self):
def get_templates(self, prefix='public-'):
try:
public_templates = [
template
for template in self._get_template_pool()
if template.name.startswith('public-')
if template.name.startswith(prefix)
]
return public_templates
except ConnectionRefusedError:

View file

@ -7,6 +7,7 @@ from djangocms_blog.models import Post
from djangocms_blog.views import PostListView
from djangocms_blog.settings import get_setting
from django.utils.translation import ugettext_lazy as _
from djangocms_blog.models import BlogCategory
def blog(request):
@ -20,6 +21,7 @@ def blog(request):
class PostListViewUngleich(PostListView):
category = None
model = Post
context_object_name = 'post_list'
base_template_name = 'post_list_ungleich.html'
@ -38,7 +40,26 @@ class PostListViewUngleich(PostListView):
def get_queryset(self):
language = get_language()
queryset = self.model.objects.filter(publish=True).translated(language)
if self.category:
blog_category = (
BlogCategory
._default_manager
.language(language)
.filter(
translations__language_code=language,
translations__slug=self.category
)
)
queryset = (self.model
.objects
.filter(categories=blog_category, publish=True)
.translated(language))
else:
queryset = (self.model
.objects
.filter(publish=True)
.translated(language))
setattr(self.request, get_setting('CURRENT_NAMESPACE'), self.config)
return queryset

View file

@ -195,7 +195,7 @@
flex: 1;
}
.header_slider > .carousel .item .container {
.header_slider > .carousel .item .container-fluid {
overflow: auto;
padding: 50px 20px 60px;
height: 100%;
@ -236,7 +236,7 @@
.header_slider .carousel-control .fa {
font-size: 4em;
}
.header_slider > .carousel .item .container {
.header_slider > .carousel .item .container-fluid {
overflow: auto;
padding: 75px 50px;
}

View file

@ -13,7 +13,7 @@
</video>
</div>
{% endif %}
<div class="container">
<div class="container-fluid">
{% if instance.heading %}
<div class="intro-cap">{{ instance.heading }}</div>
{% endif %}

View file

@ -33,7 +33,11 @@
{% include "google_analytics.html" %}
<!-- End Google Analytics -->
{% if request.current_page.cmsfaviconextension %}
<link rel="shortcut icon" href="{% static request.current_page.cmsfaviconextension.favicon.url %}" type="image/x-icon">
{% else %}
<link rel="shortcut icon" href="{% static 'ungleich_page/img/favicon.ico' %}" type="image/x-icon">
{% endif %}
</head>
<body id="page-top" class="index">

View file

@ -107,10 +107,12 @@ def get_vm_price_with_vat(cpu, memory, ssd_size, hdd_size=0,
)
return None
price = ((decimal.Decimal(cpu) * pricing.cores_unit_price) +
price = (
(decimal.Decimal(cpu) * pricing.cores_unit_price) +
(decimal.Decimal(memory) * pricing.ram_unit_price) +
(decimal.Decimal(ssd_size) * pricing.ssd_unit_price) +
(decimal.Decimal(hdd_size) * pricing.hdd_unit_price))
(decimal.Decimal(hdd_size) * pricing.hdd_unit_price)
)
if pricing.vat_inclusive:
vat = decimal.Decimal(0)
vat_percent = decimal.Decimal(0)
@ -121,4 +123,8 @@ def get_vm_price_with_vat(cpu, memory, ssd_size, hdd_size=0,
cents = decimal.Decimal('.01')
price = price.quantize(cents, decimal.ROUND_HALF_UP)
vat = vat.quantize(cents, decimal.ROUND_HALF_UP)
return float(price), float(vat), float(vat_percent)
discount = {
'name': pricing.discount_name,
'amount': float(pricing.discount_amount),
}
return float(price), float(vat), float(vat_percent), discount