diff --git a/datacenterlight/cms_models.py b/datacenterlight/cms_models.py index dd6a165f..5a8d7ac8 100644 --- a/datacenterlight/cms_models.py +++ b/datacenterlight/cms_models.py @@ -26,6 +26,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,7 +292,7 @@ class DCLSectionPromoPluginModel(CMSPlugin): return extra_classes -class DCLCustomPricingModel(CMSPlugin): +class DCLCalculatorPluginModel(CMSPlugin): pricing = models.ForeignKey( VMPricing, related_name="dcl_custom_pricing_vm_pricing", diff --git a/datacenterlight/cms_plugins.py b/datacenterlight/cms_plugins.py index 19dc0b39..ecc0a355 100644 --- a/datacenterlight/cms_plugins.py +++ b/datacenterlight/cms_plugins.py @@ -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,18 @@ 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: + print(child.__dict__) 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 +79,20 @@ 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 - ) - else: - context['vm_pricing'] = VMPricing.get_default_pricing() - 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" diff --git a/datacenterlight/migrations/0021_cmsintegration_calculator_placeholder.py b/datacenterlight/migrations/0021_cmsintegration_calculator_placeholder.py new file mode 100644 index 00000000..3ebbb469 --- /dev/null +++ b/datacenterlight/migrations/0021_cmsintegration_calculator_placeholder.py @@ -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', + ), + ] diff --git a/datacenterlight/static/datacenterlight/css/landing-page.css b/datacenterlight/static/datacenterlight/css/landing-page.css index 8e9f2c2d..f241ed71 100755 --- a/datacenterlight/static/datacenterlight/css/landing-page.css +++ b/datacenterlight/static/datacenterlight/css/landing-page.css @@ -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; diff --git a/datacenterlight/static/datacenterlight/js/main.js b/datacenterlight/static/datacenterlight/js/main.js index 35f2b247..f6ba036b 100644 --- a/datacenterlight/static/datacenterlight/js/main.js +++ b/datacenterlight/static/datacenterlight/js/main.js @@ -175,7 +175,7 @@ window.coresUnitPrice = 5; } if(typeof window.ramUnitPrice === 'undefined'){ - window.coresUnitPrice = 2; + window.ramUnitPrice = 2; } if(typeof window.ssdUnitPrice === 'undefined'){ window.ssdUnitPrice = 0.6; diff --git a/datacenterlight/templates/datacenterlight/cms/base.html b/datacenterlight/templates/datacenterlight/cms/base.html index 942a0ad4..a614db67 100644 --- a/datacenterlight/templates/datacenterlight/cms/base.html +++ b/datacenterlight/templates/datacenterlight/cms/base.html @@ -61,6 +61,7 @@ {% endplaceholder %} + {% url 'datacenterlight:index' as calculator_form_url %} {% placeholder 'Datacenterlight Content' %} {% placeholder 'datacenterlight_footer'%} diff --git a/datacenterlight/templates/datacenterlight/cms/calculator.html b/datacenterlight/templates/datacenterlight/cms/calculator.html index 27d1f89c..7b123a72 100644 --- a/datacenterlight/templates/datacenterlight/cms/calculator.html +++ b/datacenterlight/templates/datacenterlight/cms/calculator.html @@ -1,16 +1,5 @@ -
-
-
-
- {% include "datacenterlight/cms/includes/_section_split_content.html" %} -
-
-
-
- {% include "datacenterlight/includes/_calculator_form.html" %} -
-
-
-
+
+
+ {% include "datacenterlight/includes/_calculator_form.html" with vm_pricing=instance.pricing %}
\ No newline at end of file diff --git a/datacenterlight/templates/datacenterlight/cms/section.html b/datacenterlight/templates/datacenterlight/cms/section.html index 5a420a99..4438cf7d 100644 --- a/datacenterlight/templates/datacenterlight/cms/section.html +++ b/datacenterlight/templates/datacenterlight/cms/section.html @@ -2,17 +2,24 @@
- {% if children_to_side|length %} + {% if children_to_side|length or children_calculator|length %}
{% include "datacenterlight/cms/includes/_section_split_content.html" %}
-
- {% for plugin in children_to_side %} + {% if children_calculator|length %} + {% for plugin in children_calculator %} {% render_plugin plugin %} {% endfor %} -
+ {% endif %} + {% if children_to_side %} +
+ {% for plugin in children_to_side %} + {% render_plugin plugin %} + {% endfor %} +
+ {% endif %}
{% else %} diff --git a/datacenterlight/templates/datacenterlight/includes/_calculator_form.html b/datacenterlight/templates/datacenterlight/includes/_calculator_form.html index e3fe8676..8335c7ec 100644 --- a/datacenterlight/templates/datacenterlight/includes/_calculator_form.html +++ b/datacenterlight/templates/datacenterlight/includes/_calculator_form.html @@ -11,13 +11,13 @@ {% endif %} -
+ {% csrf_token %}

{% trans "VM hosting" %}

- 15 + CHF/{% trans "month" %} {% if vm_pricing.vat_inclusive %}
@@ -94,4 +94,4 @@
- + \ No newline at end of file diff --git a/dynamicweb/settings/base.py b/dynamicweb/settings/base.py index 58c6b8e2..da3f0941 100644 --- a/dynamicweb/settings/base.py +++ b/dynamicweb/settings/base.py @@ -352,6 +352,18 @@ CMS_PLACEHOLDER_CONF = { }, ] }, + 'datacenterlight_calculator': { + 'name': _('Datacenterlight Calculator'), + 'plugins': ['DCLCalculatorPlugin'], + 'default_plugins': [ + { + 'plugin_type': 'DCLCalculatorPlugin', + 'values': { + 'pricing_id': 1 + }, + }, + ] + }, } CMS_PERMISSION = True diff --git a/hosting/static/hosting/css/price_calculator.css b/hosting/static/hosting/css/price_calculator.css index 316b12ca..68961d33 100644 --- a/hosting/static/hosting/css/price_calculator.css +++ b/hosting/static/hosting/css/price_calculator.css @@ -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); } diff --git a/hosting/static/hosting/js/initial.js b/hosting/static/hosting/js/initial.js index 1fca9735..7159da9a 100644 --- a/hosting/static/hosting/js/initial.js +++ b/hosting/static/hosting/js/initial.js @@ -153,4 +153,83 @@ $( 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; + } + var total = (cardPricing['cpu'].value * window.coresUnitPrice) + + (cardPricing['ram'].value * window.ramUnitPrice) + + (cardPricing['storage'].value * window.ssdUnitPrice); + total = parseFloat(total.toFixed(2)); + $("#total").text(total); + } + + _initPricing(); }); \ No newline at end of file diff --git a/hosting/templates/hosting/base_short.html b/hosting/templates/hosting/base_short.html index 9c1538db..63f2b499 100644 --- a/hosting/templates/hosting/base_short.html +++ b/hosting/templates/hosting/base_short.html @@ -1,5 +1,5 @@ -{% load staticfiles bootstrap3%} -{% load i18n %} +{% load staticfiles i18n cms_tags sekizai_tags %} + @@ -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" %} + @@ -48,7 +51,7 @@ - + {% cms_toolbar %} {% block navbar %} {% include "hosting/includes/_navbar_user.html" %} diff --git a/hosting/templates/hosting/create_virtual_machine.html b/hosting/templates/hosting/create_virtual_machine.html index a614dd78..5c4bc3cf 100644 --- a/hosting/templates/hosting/create_virtual_machine.html +++ b/hosting/templates/hosting/create_virtual_machine.html @@ -1,7 +1,9 @@ {% extends "hosting/base_short.html" %} -{% load staticfiles bootstrap3 i18n %} +{% load staticfiles bootstrap3 i18n cms_tags %} {% block content %} + +
@@ -17,14 +19,8 @@ {% endif %}
-
-
-
-
- {% include "hosting/calculator_form.html" %} -
-
-
+
+ {% render_placeholder cms_integration.calculator_placeholder %}
diff --git a/hosting/templates/hosting/payment.html b/hosting/templates/hosting/payment.html index 4878831e..ab6c6a65 100644 --- a/hosting/templates/hosting/payment.html +++ b/hosting/templates/hosting/payment.html @@ -42,12 +42,12 @@
- {%trans "Total" %} {%trans "including VAT" %} + {%trans "Total" %} {% if vm_pricing.vat_inclusive %}{%trans "including VAT" %}{% else %}{%trans "excluding VAT" %}{% endif %}
-
{{request.session.specs.price|intcomma}} - CHF/{% trans "Month" %} +
+ {{request.session.specs.price|intcomma}} CHF/{% trans "Month" %}
diff --git a/hosting/views.py b/hosting/views.py index ec36836a..495efd5c 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -30,8 +30,9 @@ 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.models import VMTemplate, VMPricing from datacenterlight.tasks import create_vm_task +from datacenterlight.utils import get_cms_integration from membership.models import CustomUser, StripeCustomer from opennebula_api.models import OpenNebulaManager from opennebula_api.serializers import ( @@ -42,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 @@ -854,7 +855,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) @@ -1003,7 +1004,11 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View): @method_decorator(decorators) def get(self, request, *args, **kwargs): - context = {'templates': VMTemplate.objects.all()} + print(get_cms_integration('default')) + context = { + 'templates': VMTemplate.objects.all(), + 'cms_integration': get_cms_integration('default'), + } return render(request, self.template_name, context) @method_decorator(decorators) @@ -1015,18 +1020,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) @@ -1034,8 +1055,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) @@ -1043,15 +1063,24 @@ 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 = 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 + 'price': price, + 'vat': vat, + 'vat_percent': vat_percent, + 'total_price': price + vat, + 'pricing_name': vm_pricing_name } request.session['specs'] = specs