From 59b020c0d4f301cbd7c38286d1dd5acc06f7efe9 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 25 Jul 2018 21:41:05 +0200 Subject: [PATCH 001/154] Replace all occurrences of logger.info with logger.error --- hosting/views.py | 2 +- opennebula_api/models.py | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/hosting/views.py b/hosting/views.py index 6af1885b..f2c51cf5 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1394,7 +1394,7 @@ class VirtualMachineView(LoginRequiredMixin, View): terminated = manager.delete_vm(vm.id) if not terminated: - logger.debug( + logger.error( "manager.delete_vm returned False. Hence, error making " "xml-rpc call to delete vm failed." ) diff --git a/opennebula_api/models.py b/opennebula_api/models.py index 29632009..611a0712 100644 --- a/opennebula_api/models.py +++ b/opennebula_api/models.py @@ -110,7 +110,7 @@ class OpenNebulaManager(): raise UserExistsError() except OpenNebulaException as err: logger.error('OpenNebulaException error: {0}'.format(err)) - logger.debug('User exists but password is wrong') + logger.error('User exists but password is wrong') raise UserCredentialError() except WrongNameError: @@ -148,7 +148,7 @@ class OpenNebulaManager(): ) return opennebula_user except ConnectionRefusedError: - logger.info( + logger.error( 'Could not connect to host: {host} via protocol {protocol}'.format( host=settings.OPENNEBULA_DOMAIN, protocol=settings.OPENNEBULA_PROTOCOL) @@ -160,7 +160,7 @@ class OpenNebulaManager(): user_pool = oca.UserPool(self.oneadmin_client) user_pool.info() except ConnectionRefusedError: - logger.info( + logger.error( 'Could not connect to host: {host} via protocol {protocol}'.format( host=settings.OPENNEBULA_DOMAIN, protocol=settings.OPENNEBULA_PROTOCOL) @@ -174,7 +174,7 @@ class OpenNebulaManager(): vm_pool.info() return vm_pool except AttributeError: - logger.info('Could not connect via client, using oneadmin instead') + logger.error('Could not connect via client, using oneadmin instead') try: vm_pool = oca.VirtualMachinePool(self.oneadmin_client) vm_pool.info(filter=-2) @@ -183,7 +183,7 @@ class OpenNebulaManager(): raise ConnectionRefusedError except ConnectionRefusedError: - logger.info( + logger.error( 'Could not connect to host: {host} via protocol {protocol}'.format( host=settings.OPENNEBULA_DOMAIN, protocol=settings.OPENNEBULA_PROTOCOL) @@ -325,14 +325,14 @@ class OpenNebulaManager(): ) vm_terminated = True except socket.timeout as socket_err: - logger.info("Socket timeout error: {0}".format(socket_err)) + logger.error("Socket timeout error: {0}".format(socket_err)) except OpenNebulaException as opennebula_err: - logger.info( + logger.error( "OpenNebulaException error: {0}".format(opennebula_err)) except OSError as os_err: - logger.info("OSError : {0}".format(os_err)) + logger.error("OSError : {0}".format(os_err)) except ValueError as value_err: - logger.info("ValueError : {0}".format(value_err)) + logger.error("ValueError : {0}".format(value_err)) return vm_terminated @@ -342,7 +342,7 @@ class OpenNebulaManager(): template_pool.info() return template_pool except ConnectionRefusedError: - logger.info( + logger.error( """Could not connect to host: {host} via protocol {protocol}""".format( host=settings.OPENNEBULA_DOMAIN, From a5cdfab306e86f44ae3c9ba1d0f9e1eaed0051fe Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 25 Jul 2018 21:53:33 +0200 Subject: [PATCH 002/154] Send an email to admin if the VM is still there after 30 seconds of the VM terminate calll --- hosting/views.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/hosting/views.py b/hosting/views.py index f2c51cf5..476c62ff 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1404,6 +1404,9 @@ class VirtualMachineView(LoginRequiredMixin, View): try: manager.get_vm(vm.id) except WrongIdError: + logger.error( + "VM {} not found. So, its terminated.".format(vm.id) + ) response['status'] = True response['text'] = ugettext('Terminated') vm_detail_obj = VMDetail.objects.filter( @@ -1421,6 +1424,19 @@ class VirtualMachineView(LoginRequiredMixin, View): break else: sleep(2) + if 'status' not in response: + vm_msg = "VM {} has not terminated yet!".format(vm.id) + admin_email_body['status'] = vm_msg + logger.error(vm_msg) + err_email_data = { + 'subject': vm_msg, + 'from_email': settings.DCL_SUPPORT_FROM_ADDRESS, + 'to': ['info@ungleich.ch'], + 'body': "Called VM terminate xml-rpc and waited for over " + "30 seconds for the VM to disappear. But, it did " + "not happen. So, please verify!", + } + send_plain_email_task.delay(err_email_data) context = { 'vm_name': vm_name, 'base_url': "{0}://{1}".format( From df10e8441892d715e0137874e694d909d5bc5162 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 25 Jul 2018 22:04:44 +0200 Subject: [PATCH 003/154] Add owner's email to the subject --- hosting/views.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hosting/views.py b/hosting/views.py index 476c62ff..d256115d 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1425,7 +1425,9 @@ class VirtualMachineView(LoginRequiredMixin, View): else: sleep(2) if 'status' not in response: - vm_msg = "VM {} has not terminated yet!".format(vm.id) + vm_msg = "VM {} of user {} has not terminated yet!".format( + vm.id, owner.email + ) admin_email_body['status'] = vm_msg logger.error(vm_msg) err_email_data = { From 929e7ead1c0c9cdc9e9213e572a612ae03caba64 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 22 Aug 2018 22:36:17 +0200 Subject: [PATCH 004/154] Add vm-terminate-warning style --- hosting/static/hosting/css/virtual-machine.css | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hosting/static/hosting/css/virtual-machine.css b/hosting/static/hosting/css/virtual-machine.css index 1c50776d..5255061c 100644 --- a/hosting/static/hosting/css/virtual-machine.css +++ b/hosting/static/hosting/css/virtual-machine.css @@ -183,6 +183,13 @@ margin-top: 25px; } +.vm-terminate-warning { + letter-spacing: 0.6px; + font-size: 12px; + font-weight: 400; + color: #373636; +} + .vm-contact-us { margin: 25px 0 30px; /* text-align: center; */ From 7e790e702740f25ae66e6ff94316f157918b83e7 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 22 Aug 2018 22:36:29 +0200 Subject: [PATCH 005/154] Add warning div --- hosting/templates/hosting/virtual_machine_detail.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hosting/templates/hosting/virtual_machine_detail.html b/hosting/templates/hosting/virtual_machine_detail.html index 68894851..7ee26211 100644 --- a/hosting/templates/hosting/virtual_machine_detail.html +++ b/hosting/templates/hosting/virtual_machine_detail.html @@ -72,6 +72,10 @@ {% trans "Sorry, there was an unexpected error. Kindly retry." %} {% endif %} +
+

{% trans "Attention:" %}

+

{% trans "terminating VM can not be reverted." %}

+
From 1e5cf082735dfacbc72a9163c10966d4942896e6 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 22 Aug 2018 22:47:49 +0200 Subject: [PATCH 006/154] Move the warning to the bottom --- hosting/templates/hosting/virtual_machine_detail.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hosting/templates/hosting/virtual_machine_detail.html b/hosting/templates/hosting/virtual_machine_detail.html index 7ee26211..6af9fb94 100644 --- a/hosting/templates/hosting/virtual_machine_detail.html +++ b/hosting/templates/hosting/virtual_machine_detail.html @@ -72,12 +72,12 @@ {% trans "Sorry, there was an unexpected error. Kindly retry." %} {% endif %} -
-

{% trans "Attention:" %}

-

{% trans "terminating VM can not be reverted." %}

-
+
+

{% trans "Attention:" %}

+

{% trans "terminating VM can not be reverted." %}

+
From 07837c8752329ab6e7e2854511303b42eb19c84f Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 22 Aug 2018 22:51:22 +0200 Subject: [PATCH 007/154] Reduce bottom padding --- hosting/static/hosting/css/virtual-machine.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/static/hosting/css/virtual-machine.css b/hosting/static/hosting/css/virtual-machine.css index 5255061c..440b38a2 100644 --- a/hosting/static/hosting/css/virtual-machine.css +++ b/hosting/static/hosting/css/virtual-machine.css @@ -142,7 +142,7 @@ } .vm-vmid { - padding: 50px 0 70px; + padding: 50px 0 35px; text-align: center; } From b7ff51962417022339a53c327f5984f7f473880e Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 22 Aug 2018 22:54:49 +0200 Subject: [PATCH 008/154] Reduce bottom padding further by 2px --- hosting/static/hosting/css/virtual-machine.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/static/hosting/css/virtual-machine.css b/hosting/static/hosting/css/virtual-machine.css index 440b38a2..fcbaa301 100644 --- a/hosting/static/hosting/css/virtual-machine.css +++ b/hosting/static/hosting/css/virtual-machine.css @@ -142,7 +142,7 @@ } .vm-vmid { - padding: 50px 0 35px; + padding: 50px 0 33px; text-align: center; } From 4425aa7c88735a3c4b484b321225b527d849a12a Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 22 Aug 2018 22:57:32 +0200 Subject: [PATCH 009/154] Change modal text --- hosting/templates/hosting/virtual_machine_detail.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/templates/hosting/virtual_machine_detail.html b/hosting/templates/hosting/virtual_machine_detail.html index 6af9fb94..0dfc2cbf 100644 --- a/hosting/templates/hosting/virtual_machine_detail.html +++ b/hosting/templates/hosting/virtual_machine_detail.html @@ -109,7 +109,7 @@

{% trans "Status" %}

-
+
{% trans "Your VM is" %}
{% if virtual_machine.state == 'PENDING' %} From af78631ec86f1bccf4d7902e3b65d1ca2b4b072a Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 22 Aug 2018 23:13:49 +0200 Subject: [PATCH 011/154] Update hosting's django.po --- hosting/locale/de/LC_MESSAGES/django.po | 32 ++++++++++++++++--------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/hosting/locale/de/LC_MESSAGES/django.po b/hosting/locale/de/LC_MESSAGES/django.po index 95515355..ac324ccd 100644 --- a/hosting/locale/de/LC_MESSAGES/django.po +++ b/hosting/locale/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2018-07-05 23:15+0000\n" +"POT-Creation-Date: 2018-08-22 21:11+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -290,9 +290,8 @@ msgid "" "You are not making any payment yet. After placing your order, you will be " "taken to the Submit Payment Page." msgstr "" -"Es wird noch keine Bezahlung vorgenommen. Die Bezahlung wird erst " -"ausgelöst, nachdem Du die Bestellung auf der nächsten Seite bestätigt " -"hast." +"Es wird noch keine Bezahlung vorgenommen. Die Bezahlung wird erst ausgelöst, " +"nachdem Du die Bestellung auf der nächsten Seite bestätigt hast." msgid "SUBMIT" msgstr "ABSENDEN" @@ -469,9 +468,9 @@ msgid "" "database." msgstr "" "Bitte wähle eine der zuvor genutzten Kreditkarten oder gib Deine " -"Kreditkartendetails unten an. Die Bezahlung wird über " -"Stripe abgewickelt. " -"Wir speichern Deine Kreditkartendetails nicht in unserer Datenbank." +"Kreditkartendetails unten an. Die Bezahlung wird über Stripe abgewickelt. Wir speichern Deine " +"Kreditkartendetails nicht in unserer Datenbank." msgid "" "Please fill in your credit card information below. We are using Date: Wed, 22 Aug 2018 23:16:26 +0200 Subject: [PATCH 012/154] Remove an unnecessary question mark --- hosting/templates/hosting/virtual_machine_detail.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/templates/hosting/virtual_machine_detail.html b/hosting/templates/hosting/virtual_machine_detail.html index a549aebf..060a21be 100644 --- a/hosting/templates/hosting/virtual_machine_detail.html +++ b/hosting/templates/hosting/virtual_machine_detail.html @@ -109,7 +109,7 @@ {% else %} +

+ {% trans "Product" %}:  + {{ product_name }} +

{% trans "Amount" %}: {{order.price|floatformat:2|intcomma}} CHF

+ {% if order.generic_payment_description %} +

+ {% trans "Description" %}: + {{order.generic_payment_description}} +

+ {% endif %} {% if order.subscription_id %}

{% trans "Recurring" %}: @@ -179,9 +189,6 @@

{% endif %}
-
-
-
{% endif %}
diff --git a/hosting/views.py b/hosting/views.py index 8b6e061d..c9b7ab08 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -59,7 +59,8 @@ from .forms import ( ) from .mixins import ProcessVMSelectionMixin, HostingContextMixin from .models import ( - HostingOrder, HostingBill, HostingPlan, UserHostingKey, VMDetail + HostingOrder, HostingBill, HostingPlan, UserHostingKey, VMDetail, + GenericProduct ) logger = logging.getLogger(__name__) @@ -865,7 +866,9 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): if obj.generic_product_id is not None: # generic payment case logger.debug("Generic payment case") - + context['product_name'] = GenericProduct.objects.get( + id=obj.generic_product_id + ).product_name else: # invoice for previous order logger.debug("Invoice of VM order") From 48ba6a61660b1e0582bf14da69ec0179198ccc31 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 26 Sep 2018 09:13:25 +0200 Subject: [PATCH 073/154] Remove disabled on amount/recurring fields --- hosting/forms.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/hosting/forms.py b/hosting/forms.py index aae50ac5..9eb5b2d2 100644 --- a/hosting/forms.py +++ b/hosting/forms.py @@ -52,29 +52,34 @@ class HostingUserLoginForm(forms.Form): raise forms.ValidationError(_("User does not exist")) +class ProductModelChoiceField(forms.ModelChoiceField): + def label_from_instance(self, obj): + return obj.product_name + + class GenericPaymentForm(forms.Form): - product_name = forms.ModelChoiceField( + product_name = ProductModelChoiceField( queryset=GenericProduct.objects.all().order_by('product_name'), empty_label=_("Choose a product"), - to_field_name='product_name' ) amount = forms.FloatField( widget=forms.TextInput( - attrs={'placeholder': _('Amount in CHF')} + attrs={'placeholder': _('Amount in CHF'), + 'readonly': 'readonly'} ), max_value=999999, min_value=1, - disabled=True ) recurring = forms.BooleanField(required=False, label=_("Recurring monthly"), - disabled=True) + ) description = forms.CharField( widget=forms.Textarea(attrs={'style': "height: 100px;"}), required=False ) class Meta: + model = GenericProduct fields = ['product_name', 'amount', 'recurring', 'description'] From 508360472acc19366668d60a90a92942ffa94cf0 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 26 Sep 2018 09:14:11 +0200 Subject: [PATCH 074/154] Add amount/recurring form fields validation --- hosting/forms.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/hosting/forms.py b/hosting/forms.py index 9eb5b2d2..928f910e 100644 --- a/hosting/forms.py +++ b/hosting/forms.py @@ -82,6 +82,20 @@ class GenericPaymentForm(forms.Form): model = GenericProduct fields = ['product_name', 'amount', 'recurring', 'description'] + def clean_amount(self): + amount = self.cleaned_data.get('amount') + if (float(self.cleaned_data.get('product_name').get_actual_price()) != + amount): + raise forms.ValidationError(_("Amount field does not match")) + return amount + + def clean_recurring(self): + recurring = self.cleaned_data.get('recurring') + if (self.cleaned_data.get('product_name').product_is_subscription != + (True if recurring else False)): + raise forms.ValidationError(_("Recurring field does not match")) + return recurring + class HostingUserSignupForm(forms.ModelForm): confirm_password = forms.CharField(label=_("Confirm Password"), From 4575ff60ecb890547032ee3d1d4b546193b8656f Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 26 Sep 2018 09:15:24 +0200 Subject: [PATCH 075/154] Refactor validation code + Add product_id to context --- datacenterlight/views.py | 48 ++++++++-------------------------------- 1 file changed, 9 insertions(+), 39 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 736d2e63..66f8c642 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -279,8 +279,7 @@ class PaymentOrderView(FormView): product = None try: product = GenericProduct.objects.get( - product_name= - request.POST['generic_payment_form-product_name'] + id=request.POST['generic_payment_form-product_name'] ) except GenericProduct.DoesNotExist as dne: logger.error( @@ -341,37 +340,11 @@ class PaymentOrderView(FormView): ) if generic_payment_form.is_valid(): logger.debug("Generic payment form is valid.") - product = None - try: - product = GenericProduct.objects.get( - product_name= - request.POST['generic_payment_form-product_name'] - ) - except GenericProduct.DoesNotExist as dne: - err_msg = _( - "The requested product '{}' does not exist".format( - request.POST[ - 'generic_payment_form-product_name'] - ) - ) - logger.error(err_msg) - raise ValidationError(err_msg) - except GenericProduct.MultipleObjectsReturned as mpe: - logger.error( - "There seem to be more than one product with " - "the name {}".format( - request.POST[ - 'generic_payment_form-product_name'] - ) - ) - product = GenericProduct.objects.all( - product_name= - request.POST['generic_payment_form-product_name'] - ).first() + product = generic_payment_form.cleaned_data.get( + 'product_name' + ) gp_details = { - "product_name": generic_payment_form.cleaned_data.get( - 'product_name' - ), + "product_name": product.product_name, "amount": generic_payment_form.cleaned_data.get( 'amount' ), @@ -381,14 +354,10 @@ class PaymentOrderView(FormView): "description": generic_payment_form.cleaned_data.get( 'description' ), + "product_id": product.id } - if (product.get_actual_price() != gp_details['amount'] or - product.isSubscription != - (True if gp_details["recurring"] else False)): - raise ValidationError( - _("Product parameters do not match") - ) - gp_details['product_id'] = product.id + + # gp_details['product_id'] = product.id request.session["generic_payment_details"] = ( gp_details ) @@ -858,6 +827,7 @@ class OrderConfirmationView(DetailView): 'description': gp_details['description'], 'recurring': gp_details['recurring'], 'product_name': gp_details['product_name'], + 'product_id': gp_details['product_id'], 'order_id': order.id } From 41cba9daa332a9e3032b8511530d24c36629e508 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 26 Sep 2018 09:15:49 +0200 Subject: [PATCH 076/154] Show product name in dcl order detail template --- .../templates/datacenterlight/order_detail.html | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html index 82b5aaae..53a5d427 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -48,16 +48,22 @@

{% trans "Order summary" %}

{% if generic_payment_details %} +

+ {% trans "Product" %}:  + {{ generic_payment_details.product_name }} +

{% trans "Amount" %}: CHF {{generic_payment_details.amount|floatformat:2|intcomma}}

-

- {% trans "Description" %}: - {{generic_payment_details.description}} -

+ {% if generic_payment_details.description %} +

+ {% trans "Description" %}: + {{generic_payment_details.description}} +

+ {% endif %} {% if generic_payment_details.recurring %}

{% trans "Recurring" %}: From 1f990b1ab706c27beeabecc2c604d31eedcff2da Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 26 Sep 2018 09:24:47 +0200 Subject: [PATCH 077/154] Update migration --- hosting/migrations/0048_auto_20180926_0723.py | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 hosting/migrations/0048_auto_20180926_0723.py diff --git a/hosting/migrations/0048_auto_20180926_0723.py b/hosting/migrations/0048_auto_20180926_0723.py new file mode 100644 index 00000000..a5428ca5 --- /dev/null +++ b/hosting/migrations/0048_auto_20180926_0723.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2018-09-26 07:23 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import filer.fields.image +import utils.mixins + + +class Migration(migrations.Migration): + + dependencies = [ + ('filer', '0005_auto_20180217_1137'), + ('hosting', '0047_auto_20180821_1240'), + ] + + operations = [ + migrations.CreateModel( + name='GenericProduct', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('product_name', models.CharField(default='', max_length=128)), + ('product_description', models.CharField(default='', max_length=500)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('product_url', models.URLField(blank=True, max_length=1000, null=True)), + ('product_price', models.DecimalField(decimal_places=2, max_digits=6)), + ('product_vat', models.DecimalField(decimal_places=4, default=0, max_digits=6)), + ('product_is_subscription', models.BooleanField(default=True)), + ('product_image', filer.fields.image.FilerImageField(blank=True, help_text='The product image', null=True, on_delete=django.db.models.deletion.CASCADE, to='filer.Image')), + ], + bases=(utils.mixins.AssignPermissionsMixin, models.Model), + ), + migrations.AddField( + model_name='hostingorder', + name='generic_payment_description', + field=models.CharField(max_length=500, null=True), + ), + migrations.AddField( + model_name='hostingorder', + name='generic_product', + field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to='hosting.GenericProduct'), + ), + ] From 99b11f013ff4d176dec2733b9acb854a58fb947b Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 26 Sep 2018 09:25:03 +0200 Subject: [PATCH 078/154] Remove old migration --- hosting/migrations/0048_auto_20180924_2354.py | 44 ------------------- 1 file changed, 44 deletions(-) delete mode 100644 hosting/migrations/0048_auto_20180924_2354.py diff --git a/hosting/migrations/0048_auto_20180924_2354.py b/hosting/migrations/0048_auto_20180924_2354.py deleted file mode 100644 index f7fbf8ac..00000000 --- a/hosting/migrations/0048_auto_20180924_2354.py +++ /dev/null @@ -1,44 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9.4 on 2018-09-24 23:54 -from __future__ import unicode_literals - -from django.db import migrations, models -import django.db.models.deletion -import filer.fields.image -import utils.mixins - - -class Migration(migrations.Migration): - - dependencies = [ - ('filer', '0005_auto_20180217_1137'), - ('hosting', '0047_auto_20180821_1240'), - ] - - operations = [ - migrations.CreateModel( - name='GenericProduct', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('product_name', models.CharField(default='', max_length=128)), - ('product_description', models.CharField(default='', max_length=500)), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('product_url', models.URLField(blank=True, max_length=1000, null=True)), - ('product_price', models.DecimalField(decimal_places=2, max_digits=6)), - ('product_vat', models.DecimalField(decimal_places=4, default=0, max_digits=6)), - ('product_is_subscription', models.BooleanField(default=True)), - ('product_image', filer.fields.image.FilerImageField(blank=True, help_text='The product image', null=True, on_delete=django.db.models.deletion.CASCADE, to='filer.Image')), - ], - bases=(utils.mixins.AssignPermissionsMixin, models.Model), - ), - migrations.AddField( - model_name='hostingorder', - name='generic_payment_description', - field=models.CharField(max_length=500, null=True), - ), - migrations.AddField( - model_name='hostingorder', - name='generic_product', - field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to='hosting.GenericProduct'), - ), - ] From b021a8ed6ee788d3355be19ef11ade6f0e4e6b5a Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 26 Sep 2018 09:29:10 +0200 Subject: [PATCH 079/154] Remove filer dependency in migration --- hosting/migrations/0048_auto_20180926_0723.py | 1 - 1 file changed, 1 deletion(-) diff --git a/hosting/migrations/0048_auto_20180926_0723.py b/hosting/migrations/0048_auto_20180926_0723.py index a5428ca5..6c21ab03 100644 --- a/hosting/migrations/0048_auto_20180926_0723.py +++ b/hosting/migrations/0048_auto_20180926_0723.py @@ -11,7 +11,6 @@ import utils.mixins class Migration(migrations.Migration): dependencies = [ - ('filer', '0005_auto_20180217_1137'), ('hosting', '0047_auto_20180821_1240'), ] From 4feeec23d40f2119f1a531370cb7fb3503e671d5 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 26 Sep 2018 20:50:19 +0200 Subject: [PATCH 080/154] Correct spelling: virutal -> virtual --- hosting/templates/hosting/emails/vm_canceled.html | 2 +- hosting/templates/hosting/emails/vm_canceled.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hosting/templates/hosting/emails/vm_canceled.html b/hosting/templates/hosting/emails/vm_canceled.html index 9c2ec4c2..78781d5c 100644 --- a/hosting/templates/hosting/emails/vm_canceled.html +++ b/hosting/templates/hosting/emails/vm_canceled.html @@ -25,7 +25,7 @@

- {% blocktrans %}You are receiving this email because your virutal machine {{ vm_name }} has been cancelled.{% endblocktrans %} + {% blocktrans %}You are receiving this email because your virtual machine {{ vm_name }} has been cancelled.{% endblocktrans %}

{% blocktrans %}You can always order a new VM by clicking the button below.{% endblocktrans %} diff --git a/hosting/templates/hosting/emails/vm_canceled.txt b/hosting/templates/hosting/emails/vm_canceled.txt index 9149a554..43263c40 100644 --- a/hosting/templates/hosting/emails/vm_canceled.txt +++ b/hosting/templates/hosting/emails/vm_canceled.txt @@ -2,7 +2,7 @@ {% trans "Virtual Machine Cancellation" %} -{% blocktrans %}You are receiving this email because your virutal machine {{vm_name}} has been cancelled.{% endblocktrans %} +{% blocktrans %}You are receiving this email because your virtual machine {{vm_name}} has been cancelled.{% endblocktrans %} {% blocktrans %}You can always order a new VM by following the link below.{% endblocktrans %} {{ base_url }}{% url 'hosting:create_virtual_machine' %} From efae2b1d9ac0815b5c7fa6f74d21e605ead557a6 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 26 Sep 2018 21:17:35 +0200 Subject: [PATCH 081/154] Update Changelog --- Changelog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog b/Changelog index 051a0a7b..db20e920 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,5 @@ +Next: + * bugfix: Fix some typos and correct DE translations (PR #667) 2.2.1: 2018-09-25 * feature: Change DCLNavbarPlugin to show login option only if set (PR #665) * bugfix: Log opennebula errors and send proper message when vm terminate is not completed in the stipulated time (PR #648) From 737681136f451388c2017d2edd6de0bddc434be6 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 26 Sep 2018 21:23:46 +0200 Subject: [PATCH 082/154] Correct flake8 error --- datacenterlight/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 66f8c642..b9a28487 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -295,8 +295,8 @@ class PaymentOrderView(FormView): ) ) product = GenericProduct.objects.all( - product_name= - request.POST['generic_payment_form-product_name'] + product_name=request. + POST['generic_payment_form-product_name'] ).first() if product is None: return JsonResponse({}) From 104128486627b3768587caf60f5523b1be6e5df6 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 26 Sep 2018 21:51:55 +0200 Subject: [PATCH 083/154] Refactor moment.js locale date code to virutal_machine_detail.js --- .../templates/datacenterlight/order_detail.html | 11 ----------- hosting/static/hosting/js/virtual_machine_detail.js | 12 ++++++++++++ hosting/templates/hosting/order_detail.html | 11 ----------- 3 files changed, 12 insertions(+), 22 deletions(-) diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html index 53a5d427..31933e12 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -186,16 +186,5 @@ {%endblock%} \ No newline at end of file diff --git a/hosting/static/hosting/js/virtual_machine_detail.js b/hosting/static/hosting/js/virtual_machine_detail.js index 43a5a01d..8f90933b 100644 --- a/hosting/static/hosting/js/virtual_machine_detail.js +++ b/hosting/static/hosting/js/virtual_machine_detail.js @@ -134,3 +134,15 @@ $(document).ready(function() { $(this).find('.modal-footer .btn').addClass('hide'); }) }); + +window.onload = function () { + var locale_dates = document.getElementsByClassName("locale_date"); + var formats = ['YYYY-MM-DD hh:mm a']; + var i; + for (i = 0; i < locale_dates.length; i++) { + var oldDate = moment.utc(locale_dates[i].textContent, formats); + var outputFormat = locale_dates[i].getAttribute('data-format') || oldDate._f; + locale_dates[i].innerHTML = oldDate.local().format(outputFormat); + locale_dates[i].className += ' done'; + } +}; \ No newline at end of file diff --git a/hosting/templates/hosting/order_detail.html b/hosting/templates/hosting/order_detail.html index 0f92ef67..4a62e9fa 100644 --- a/hosting/templates/hosting/order_detail.html +++ b/hosting/templates/hosting/order_detail.html @@ -256,17 +256,6 @@ {%endblock%} From fcc113e9d9d00a9ed466a9d70ba6cd7c4289c8c4 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 26 Sep 2018 22:06:14 +0200 Subject: [PATCH 084/154] Add locale_date class to date fields so that we can localize --- hosting/templates/hosting/orders.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/templates/hosting/orders.html b/hosting/templates/hosting/orders.html index 140cc4c6..96d9e9e3 100644 --- a/hosting/templates/hosting/orders.html +++ b/hosting/templates/hosting/orders.html @@ -28,7 +28,7 @@ {% for order in orders %} {{ order.id }} - {{ order.created_at | date:"M d, Y H:i" }} + {{ order.created_at | date:'Y-m-d h:i a' }} {{ order.price|floatformat:2|intcomma }} {% trans 'See Invoice' %} From 84056a5b36006e2848a4a8405e37bdef718e7c0e Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 26 Sep 2018 22:21:59 +0200 Subject: [PATCH 085/154] Correct generic payment email as per Sanghee's corrections --- datacenterlight/views.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index b9a28487..18f770f0 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -847,14 +847,15 @@ class OrderConfirmationView(DetailView): "Confirmation of your payment"), 'from_email': settings.DCL_SUPPORT_FROM_ADDRESS, 'to': [user.get('email')], - 'body': ("Hi {name},\n\n" - "We just received a payment of CHF {amount} " - "from you. {recurring}.\n\n" + 'body': _("Hi {name},\n\n" + "thank you for your order!\n" + "We have just received a payment of CHF {amount:.2f} " + "from you.{recurring}\n\n" "Your DataCenterLight Team".format( name=user.get('name'), amount=gp_details['amount'], recurring=( - 'This is a monthly recurring plan.' + ' This is a monthly recurring plan.' if gp_details['recurring'] else '' ) ) From 3148dbccf8f1c6b7a34b8c1d39d6759cd68672b1 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 26 Sep 2018 22:31:06 +0200 Subject: [PATCH 086/154] Translate text + reformat --- datacenterlight/views.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 18f770f0..420f095f 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -848,15 +848,15 @@ class OrderConfirmationView(DetailView): 'from_email': settings.DCL_SUPPORT_FROM_ADDRESS, 'to': [user.get('email')], 'body': _("Hi {name},\n\n" - "thank you for your order!\n" - "We have just received a payment of CHF {amount:.2f} " - "from you.{recurring}\n\n" - "Your DataCenterLight Team".format( + "thank you for your order!\n" + "We have just received a payment of CHF {amount:.2f}" + " from you.{recurring}\n\n" + "Your DataCenterLight Team".format( name=user.get('name'), amount=gp_details['amount'], recurring=( - ' This is a monthly recurring plan.' - if gp_details['recurring'] else '' + _(' This is a monthly recurring plan.') + if gp_details['recurring'] else '' ) ) ), From 52d048a555e31aa68137f0b33eae572bbec750f9 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 26 Sep 2018 22:35:11 +0200 Subject: [PATCH 087/154] Add missing Cheers string in email --- datacenterlight/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 420f095f..d5407a85 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -851,7 +851,7 @@ class OrderConfirmationView(DetailView): "thank you for your order!\n" "We have just received a payment of CHF {amount:.2f}" " from you.{recurring}\n\n" - "Your DataCenterLight Team".format( + "Cheers\n,Your Data Center Light team".format( name=user.get('name'), amount=gp_details['amount'], recurring=( From 72c16713a78a7ccfed2d54630b24405ee8939300 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 26 Sep 2018 22:37:32 +0200 Subject: [PATCH 088/154] Update .po of datacenterlight --- .../locale/de/LC_MESSAGES/django.po | 60 +++++++++++++++++-- 1 file changed, 54 insertions(+), 6 deletions(-) diff --git a/datacenterlight/locale/de/LC_MESSAGES/django.po b/datacenterlight/locale/de/LC_MESSAGES/django.po index 1b66b640..bb92910e 100644 --- a/datacenterlight/locale/de/LC_MESSAGES/django.po +++ b/datacenterlight/locale/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2018-07-05 23:11+0000\n" +"POT-Creation-Date: 2018-09-26 20:35+0000\n" "PO-Revision-Date: 2018-03-30 23:22+0000\n" "Last-Translator: b'Anonymous User '\n" "Language-Team: LANGUAGE \n" @@ -293,6 +293,9 @@ msgstr "Registrieren" msgid "Billing Address" msgstr "Rechnungsadresse" +msgid "Make a payment" +msgstr "" + msgid "Your Order" msgstr "Deine Bestellung" @@ -336,9 +339,9 @@ msgid "" "database." msgstr "" "Bitte wähle eine der zuvor genutzten Kreditkarten oder gib Deine " -"Kreditkartendetails unten an. Die Bezahlung wird über " -"Stripe abgewickelt. " -"Wir speichern Deine Kreditkartendetails nicht in unserer Datenbank." +"Kreditkartendetails unten an. Die Bezahlung wird über Stripe abgewickelt. Wir speichern Deine " +"Kreditkartendetails nicht in unserer Datenbank." msgid "" "Please fill in your credit card information below. We are using Date: Wed, 26 Sep 2018 22:42:30 +0200 Subject: [PATCH 089/154] Change generic payment subject and correct misplaced comma Data Center LightConfirmation of your payment -> Confirmation of your payment --- datacenterlight/views.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index d5407a85..a544d41c 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -843,15 +843,14 @@ class OrderConfirmationView(DetailView): send_plain_email_task.delay(email_data) email_data = { - 'subject': (settings.DCL_TEXT + - "Confirmation of your payment"), + 'subject': _("Confirmation of your payment"), 'from_email': settings.DCL_SUPPORT_FROM_ADDRESS, 'to': [user.get('email')], 'body': _("Hi {name},\n\n" "thank you for your order!\n" "We have just received a payment of CHF {amount:.2f}" " from you.{recurring}\n\n" - "Cheers\n,Your Data Center Light team".format( + "Cheers,\nYour Data Center Light team".format( name=user.get('name'), amount=gp_details['amount'], recurring=( From b7929a16e2f56e1be327337fc370308543688631 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 26 Sep 2018 22:45:05 +0200 Subject: [PATCH 090/154] Update datacenterlight django.po --- datacenterlight/locale/de/LC_MESSAGES/django.po | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/datacenterlight/locale/de/LC_MESSAGES/django.po b/datacenterlight/locale/de/LC_MESSAGES/django.po index bb92910e..d43e91ea 100644 --- a/datacenterlight/locale/de/LC_MESSAGES/django.po +++ b/datacenterlight/locale/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2018-09-26 20:35+0000\n" +"POT-Creation-Date: 2018-09-26 20:44+0000\n" "PO-Revision-Date: 2018-03-30 23:22+0000\n" "Last-Translator: b'Anonymous User '\n" "Language-Team: LANGUAGE \n" @@ -570,6 +570,9 @@ msgid "An error occurred while associating the card. Details: {details}" msgstr "" "Beim Verbinden der Karte ist ein Fehler aufgetreten. Details: {details}" +msgid "Confirmation of your payment" +msgstr "" + msgid " This is a monthly recurring plan." msgstr "" @@ -580,8 +583,8 @@ msgid "" "thank you for your order!\n" "We have just received a payment of CHF {amount:.2f} from you.{recurring}\n" "\n" -"Cheers\n" -",Your Data Center Light team" +"Cheers,\n" +"Your Data Center Light team" msgstr "" msgid "Thank you for the payment." From 232022aaaf5679cf15524e03da3eddd979f4b7fc Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 26 Sep 2018 22:51:34 +0200 Subject: [PATCH 091/154] Fix flake8 errors --- datacenterlight/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index a544d41c..dc68dfa3 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -855,10 +855,10 @@ class OrderConfirmationView(DetailView): amount=gp_details['amount'], recurring=( _(' This is a monthly recurring plan.') - if gp_details['recurring'] else '' + if gp_details['recurring'] else '' ) ) - ), + ), 'reply_to': ['info@ungleich.ch'], } send_plain_email_task.delay(email_data) From 3075cffd771a0cd97c88e0960e78abf612c10f44 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 26 Sep 2018 23:00:57 +0200 Subject: [PATCH 092/154] Include product_id in generic payment Stripe plan name --- datacenterlight/views.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index dc68dfa3..8cacacb4 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -356,8 +356,6 @@ class PaymentOrderView(FormView): ), "product_id": product.id } - - # gp_details['product_id'] = product.id request.session["generic_payment_details"] = ( gp_details ) @@ -642,7 +640,10 @@ class OrderConfirmationView(DetailView): 2 ) ) - plan_name = "generic-{0:.2f}".format(amount_to_be_charged) + plan_name = "generic-{}-{0:.2f}".format( + request.session['generic_payment_details']['product_id'], + amount_to_be_charged + ) stripe_plan_id = plan_name else: template = request.session.get('template') From 12f139976d295138bacc3d107af5a7bedf0f3ea5 Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 27 Sep 2018 09:06:39 +0200 Subject: [PATCH 093/154] Simplify logic to set min_ram in the calculator form --- datacenterlight/cms_plugins.py | 3 +-- .../datacenterlight/includes/_calculator_form.html | 7 +++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/datacenterlight/cms_plugins.py b/datacenterlight/cms_plugins.py index 98b144b4..404f3181 100644 --- a/datacenterlight/cms_plugins.py +++ b/datacenterlight/cms_plugins.py @@ -97,8 +97,7 @@ class DCLCalculatorPlugin(CMSPluginBase): context['templates'] = VMTemplate.objects.filter( vm_type=instance.vm_type ) - if instance.enable_512mb_ram: - context['enable_512mb_ram'] = True + context['min_ram'] = 0.5 if instance.enable_512mb_ram else 1 return context diff --git a/datacenterlight/templates/datacenterlight/includes/_calculator_form.html b/datacenterlight/templates/datacenterlight/includes/_calculator_form.html index bc4299ed..eed79e27 100644 --- a/datacenterlight/templates/datacenterlight/includes/_calculator_form.html +++ b/datacenterlight/templates/datacenterlight/includes/_calculator_form.html @@ -9,7 +9,7 @@ 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}}; - window.enable_512mb = {{vm_pricing.enable_512mb|default:false}}; + window.minRam = {{min_ram}}; {% endif %} @@ -55,9 +55,8 @@

- {% firstof if enable_512mb_ram "0.5" endif "1" as myvar %} - + GB RAM
From 21084cdc9ff7486ad0af4646f9a3c7fbfd109e4e Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 27 Sep 2018 09:07:28 +0200 Subject: [PATCH 094/154] Initialize minRam js variable from what is passed from backend --- datacenterlight/static/datacenterlight/js/main.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/datacenterlight/static/datacenterlight/js/main.js b/datacenterlight/static/datacenterlight/js/main.js index 679d38ea..bc8efc1c 100644 --- a/datacenterlight/static/datacenterlight/js/main.js +++ b/datacenterlight/static/datacenterlight/js/main.js @@ -6,8 +6,8 @@ Scripts initialization --------------------------------------------- */ var minRam = 1; - if(window.enable_512mb){ - minRam = 0.5; + if(window.minRam){ + minRam = window.minRam; } var cardPricing = { 'cpu': { From 768f3532f7882d41cddb85f8d5f76fe7fb62b115 Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 27 Sep 2018 22:09:11 +0200 Subject: [PATCH 095/154] Add default_selected_template field to DCLCalculatorPluginModel --- datacenterlight/cms_models.py | 7 +++++++ ...orpluginmodel_default_selected_template.py | 20 +++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 datacenterlight/migrations/0026_dclcalculatorpluginmodel_default_selected_template.py diff --git a/datacenterlight/cms_models.py b/datacenterlight/cms_models.py index 69f45507..10ea1510 100644 --- a/datacenterlight/cms_models.py +++ b/datacenterlight/cms_models.py @@ -354,3 +354,10 @@ class DCLCalculatorPluginModel(CMSPlugin): "in the backend to be automatically listed in this " "calculator instance." ) + default_selected_template=models.CharField( + default="Devuan Jessie", + null=True, + max_length=128, + help_text="Write the name of the template that you need selected as" + " default when the calculator loads" + ) diff --git a/datacenterlight/migrations/0026_dclcalculatorpluginmodel_default_selected_template.py b/datacenterlight/migrations/0026_dclcalculatorpluginmodel_default_selected_template.py new file mode 100644 index 00000000..257bf0ce --- /dev/null +++ b/datacenterlight/migrations/0026_dclcalculatorpluginmodel_default_selected_template.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2018-09-27 20:07 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('datacenterlight', '0025_dclnavbarpluginmodel_show_login_option'), + ] + + operations = [ + migrations.AddField( + model_name='dclcalculatorpluginmodel', + name='default_selected_template', + field=models.CharField(default='Devuan Jessie', help_text='Write the name of the template that you need selected as default when the calculator loads', max_length=128, null=True), + ), + ] From a02c3c6973976ff6439081236ea0060463ff689d Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 27 Sep 2018 22:12:04 +0200 Subject: [PATCH 096/154] Sort templates alphabetically And also select the chosen template as the default one --- datacenterlight/cms_plugins.py | 7 +++++-- .../datacenterlight/includes/_calculator_form.html | 3 ++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/datacenterlight/cms_plugins.py b/datacenterlight/cms_plugins.py index 95a496d8..b7841de8 100644 --- a/datacenterlight/cms_plugins.py +++ b/datacenterlight/cms_plugins.py @@ -92,11 +92,14 @@ class DCLCalculatorPlugin(CMSPluginBase): if ids: context['templates'] = VMTemplate.objects.filter( vm_type=instance.vm_type - ).filter(opennebula_vm_template_id__in=ids) + ).filter(opennebula_vm_template_id__in=ids).order_by('name') else: context['templates'] = VMTemplate.objects.filter( vm_type=instance.vm_type - ) + ).order_by('name') + context['default_selected_template'] = ( + instance.default_selected_template + ) return context diff --git a/datacenterlight/templates/datacenterlight/includes/_calculator_form.html b/datacenterlight/templates/datacenterlight/includes/_calculator_form.html index 72ca5a05..0a35e2ad 100644 --- a/datacenterlight/templates/datacenterlight/includes/_calculator_form.html +++ b/datacenterlight/templates/datacenterlight/includes/_calculator_form.html @@ -91,7 +91,8 @@
From d93861ca328872850a282318b66ae503b22ed86d Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 27 Sep 2018 22:14:42 +0200 Subject: [PATCH 097/154] Set Devuan Ascii as default template --- datacenterlight/cms_models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datacenterlight/cms_models.py b/datacenterlight/cms_models.py index 10ea1510..60ff72c4 100644 --- a/datacenterlight/cms_models.py +++ b/datacenterlight/cms_models.py @@ -355,7 +355,7 @@ class DCLCalculatorPluginModel(CMSPlugin): "calculator instance." ) default_selected_template=models.CharField( - default="Devuan Jessie", + default="Devuan Ascii", null=True, max_length=128, help_text="Write the name of the template that you need selected as" From 56460ac8f0f962d8a2bdd3421d4c5faa8754d241 Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 27 Sep 2018 22:16:05 +0200 Subject: [PATCH 098/154] Update migration --- ...0026_dclcalculatorpluginmodel_default_selected_template.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/datacenterlight/migrations/0026_dclcalculatorpluginmodel_default_selected_template.py b/datacenterlight/migrations/0026_dclcalculatorpluginmodel_default_selected_template.py index 257bf0ce..0bca184a 100644 --- a/datacenterlight/migrations/0026_dclcalculatorpluginmodel_default_selected_template.py +++ b/datacenterlight/migrations/0026_dclcalculatorpluginmodel_default_selected_template.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.9.4 on 2018-09-27 20:07 +# Generated by Django 1.9.4 on 2018-09-27 20:15 from __future__ import unicode_literals from django.db import migrations, models @@ -15,6 +15,6 @@ class Migration(migrations.Migration): migrations.AddField( model_name='dclcalculatorpluginmodel', name='default_selected_template', - field=models.CharField(default='Devuan Jessie', help_text='Write the name of the template that you need selected as default when the calculator loads', max_length=128, null=True), + field=models.CharField(default='Devuan Ascii', help_text='Write the name of the template that you need selected as default when the calculator loads', max_length=128, null=True), ), ] From b047ccdef1445040339d7f43ebbc52789ba36cc1 Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 27 Sep 2018 22:33:24 +0200 Subject: [PATCH 099/154] Fix flake8 error --- datacenterlight/cms_models.py | 2 +- .../0026_dclcalculatorpluginmodel_default_selected_template.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datacenterlight/cms_models.py b/datacenterlight/cms_models.py index 60ff72c4..7b0e81f4 100644 --- a/datacenterlight/cms_models.py +++ b/datacenterlight/cms_models.py @@ -354,7 +354,7 @@ class DCLCalculatorPluginModel(CMSPlugin): "in the backend to be automatically listed in this " "calculator instance." ) - default_selected_template=models.CharField( + default_selected_template = models.CharField( default="Devuan Ascii", null=True, max_length=128, diff --git a/datacenterlight/migrations/0026_dclcalculatorpluginmodel_default_selected_template.py b/datacenterlight/migrations/0026_dclcalculatorpluginmodel_default_selected_template.py index 0bca184a..047d4096 100644 --- a/datacenterlight/migrations/0026_dclcalculatorpluginmodel_default_selected_template.py +++ b/datacenterlight/migrations/0026_dclcalculatorpluginmodel_default_selected_template.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.9.4 on 2018-09-27 20:15 +# Generated by Django 1.9.4 on 2018-09-27 20:32 from __future__ import unicode_literals from django.db import migrations, models From e1ce017ec8bff9047b5cc256431f279f4b83236d Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 27 Sep 2018 22:48:23 +0200 Subject: [PATCH 100/154] Add migration --- ...lcalculatorpluginmodel_enable_512mb_ram.py | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 datacenterlight/migrations/0026_dclcalculatorpluginmodel_enable_512mb_ram.py diff --git a/datacenterlight/migrations/0026_dclcalculatorpluginmodel_enable_512mb_ram.py b/datacenterlight/migrations/0026_dclcalculatorpluginmodel_enable_512mb_ram.py new file mode 100644 index 00000000..a29e76b5 --- /dev/null +++ b/datacenterlight/migrations/0026_dclcalculatorpluginmodel_enable_512mb_ram.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2018-09-27 20:46 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('datacenterlight', '0025_dclnavbarpluginmodel_show_login_option'), + ] + + operations = [ + migrations.AddField( + model_name='dclcalculatorpluginmodel', + name='enable_512mb_ram', + field=models.BooleanField(default=False), + ), + ] From db20e3cbe74f38ba0cfc7cab6e0ec86bd00ceddd Mon Sep 17 00:00:00 2001 From: PCoder Date: Fri, 28 Sep 2018 08:07:08 +0200 Subject: [PATCH 101/154] Add plus minus ram handler for 512MB option --- datacenterlight/static/datacenterlight/js/main.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/datacenterlight/static/datacenterlight/js/main.js b/datacenterlight/static/datacenterlight/js/main.js index bc8efc1c..0999caa5 100644 --- a/datacenterlight/static/datacenterlight/js/main.js +++ b/datacenterlight/static/datacenterlight/js/main.js @@ -148,14 +148,22 @@ var data = $(this).data('minus'); if (cardPricing[data].value > cardPricing[data].min) { - cardPricing[data].value = Number(cardPricing[data].value) - cardPricing[data].interval; + if(data === 'ram' && cardPricing[data].value === 1){ + cardPricing[data].value = 0.5; + } else { + 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; + if(data === 'ram' && cardPricing[data].value === 0.5){ + cardPricing[data].value = 1; + } else { + cardPricing[data].value = Number(cardPricing[data].value) + cardPricing[data].interval; + } } _fetchPricing(); }); From 737d890a7cbf6ffa60d429eca4f84df1216828c1 Mon Sep 17 00:00:00 2001 From: PCoder Date: Fri, 28 Sep 2018 08:11:36 +0200 Subject: [PATCH 102/154] Update Changelog for 2.2.2 --- Changelog | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Changelog b/Changelog index db20e920..71237d5c 100644 --- a/Changelog +++ b/Changelog @@ -1,4 +1,5 @@ -Next: +2.2.2: 2018-09-28 + * #5721: Set calculator OS list in alphabetical order and set `Devuan Ascii` as the default * bugfix: Fix some typos and correct DE translations (PR #667) 2.2.1: 2018-09-25 * feature: Change DCLNavbarPlugin to show login option only if set (PR #665) From 8743853a7b06fe2c59de5e5faceaa27f4b77b0af Mon Sep 17 00:00:00 2001 From: PCoder Date: Fri, 28 Sep 2018 08:13:37 +0200 Subject: [PATCH 103/154] Update Changelog --- Changelog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog b/Changelog index 71237d5c..190a7af8 100644 --- a/Changelog +++ b/Changelog @@ -1,5 +1,5 @@ 2.2.2: 2018-09-28 - * #5721: Set calculator OS list in alphabetical order and set `Devuan Ascii` as the default + * #5721: Set calculator OS list in alphabetical order and set `Devuan Ascii` as the default (PR #668) * bugfix: Fix some typos and correct DE translations (PR #667) 2.2.1: 2018-09-25 * feature: Change DCLNavbarPlugin to show login option only if set (PR #665) From 24d719e4f1c80f313bbbb38b5bbce744a785756f Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 29 Sep 2018 07:37:23 +0200 Subject: [PATCH 104/154] Update migration --- ...m.py => 0027_dclcalculatorpluginmodel_enable_512mb_ram.py} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename datacenterlight/migrations/{0026_dclcalculatorpluginmodel_enable_512mb_ram.py => 0027_dclcalculatorpluginmodel_enable_512mb_ram.py} (73%) diff --git a/datacenterlight/migrations/0026_dclcalculatorpluginmodel_enable_512mb_ram.py b/datacenterlight/migrations/0027_dclcalculatorpluginmodel_enable_512mb_ram.py similarity index 73% rename from datacenterlight/migrations/0026_dclcalculatorpluginmodel_enable_512mb_ram.py rename to datacenterlight/migrations/0027_dclcalculatorpluginmodel_enable_512mb_ram.py index a29e76b5..bd639c9d 100644 --- a/datacenterlight/migrations/0026_dclcalculatorpluginmodel_enable_512mb_ram.py +++ b/datacenterlight/migrations/0027_dclcalculatorpluginmodel_enable_512mb_ram.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.9.4 on 2018-09-27 20:46 +# Generated by Django 1.9.4 on 2018-09-29 05:36 from __future__ import unicode_literals from django.db import migrations, models @@ -8,7 +8,7 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('datacenterlight', '0025_dclnavbarpluginmodel_show_login_option'), + ('datacenterlight', '0026_dclcalculatorpluginmodel_default_selected_template'), ] operations = [ From f85ef714abf217fedca458537ce79d3404fcc4a5 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 1 Oct 2018 07:50:15 +0200 Subject: [PATCH 105/154] Pass instance context Also POST plugin_id as a form parameter to check enable_512mb_ram case --- datacenterlight/cms_plugins.py | 4 +--- .../templates/datacenterlight/includes/_calculator_form.html | 5 +++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/datacenterlight/cms_plugins.py b/datacenterlight/cms_plugins.py index af32e64f..ebd4fb8d 100644 --- a/datacenterlight/cms_plugins.py +++ b/datacenterlight/cms_plugins.py @@ -97,9 +97,7 @@ class DCLCalculatorPlugin(CMSPluginBase): context['templates'] = VMTemplate.objects.filter( vm_type=instance.vm_type ).order_by('name') - context['default_selected_template'] = ( - instance.default_selected_template - ) + context['instance'] = instance context['min_ram'] = 0.5 if instance.enable_512mb_ram else 1 return context diff --git a/datacenterlight/templates/datacenterlight/includes/_calculator_form.html b/datacenterlight/templates/datacenterlight/includes/_calculator_form.html index f7deb13f..0b73c83f 100644 --- a/datacenterlight/templates/datacenterlight/includes/_calculator_form.html +++ b/datacenterlight/templates/datacenterlight/includes/_calculator_form.html @@ -15,6 +15,7 @@ {% csrf_token %} +

{% trans "VM hosting" %}

@@ -93,11 +94,11 @@
- \ No newline at end of file + From c2dbbf04245cb1a869ac945cb6da967053f83bee Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 1 Oct 2018 07:52:17 +0200 Subject: [PATCH 106/154] Update views.py to include 512mb ram case RAM server side validation is as follows: - pid is a mandatory parameter for a valid RAM, otherwise a validation error is raised - check if enable_512mb_ram is enabled. In this case, validate if the input ram is either a whole number or 0.5 and in the range 0.5 <= value <= 200 - otherwise check ram is a whole number in the range 1 <= value <= 200 --- datacenterlight/views.py | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index be4e5700..5b2dc3e1 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -20,6 +20,7 @@ from utils.forms import BillingAddressForm, BillingAddressFormSignup from utils.hosting_utils import get_vm_price_with_vat from utils.stripe_utils import StripeUtils from utils.tasks import send_plain_email_task +from .cms_models import DCLCalculatorPluginModel from .forms import ContactForm from .models import VMTemplate, VMPricing from .utils import get_cms_integration, create_vm @@ -82,7 +83,29 @@ class IndexView(CreateView): raise ValidationError(_('Invalid number of cores')) def validate_memory(self, value): - if (value > 200) or (value < 1): + if 'pid' in self.request.POST: + try: + plugin = DCLCalculatorPluginModel.objects.get( + id=self.request.POST['pid'] + ) + except DCLCalculatorPluginModel.DoesNotExist as dne: + logger.error( + str(dne) + " plugin_id: " + self.request.POST['pid'] + ) + raise ValidationError(_('Invalid calculator properties')) + if plugin.enable_512mb_ram: + if value % 1 == 0 or value == 0.5: + logger.debug( + "Given ram {value} is either 0.5 or a" + " whole number".format(value=value) + ) + if (value > 200) or (value < 0.5): + raise ValidationError(_('Invalid RAM size')) + else: + raise ValidationError(_('Invalid RAM size')) + elif (value > 200) or (value < 1) or (value % 1 != 0): + raise ValidationError(_('Invalid RAM size')) + else: raise ValidationError(_('Invalid RAM size')) def validate_storage(self, value): @@ -101,7 +124,7 @@ class IndexView(CreateView): cores = request.POST.get('cpu') cores_field = forms.IntegerField(validators=[self.validate_cores]) memory = request.POST.get('ram') - memory_field = forms.IntegerField(validators=[self.validate_memory]) + memory_field = forms.FloatField(validators=[self.validate_memory]) storage = request.POST.get('storage') storage_field = forms.IntegerField(validators=[self.validate_storage]) template_id = int(request.POST.get('config')) From a93c9001099eff3d628274cce2e6b10488a8435e Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 1 Oct 2018 08:54:22 +0200 Subject: [PATCH 107/154] Pass 512 MB to memory parameter --- opennebula_api/models.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/opennebula_api/models.py b/opennebula_api/models.py index 0ce5e8a2..adc39bf0 100644 --- a/opennebula_api/models.py +++ b/opennebula_api/models.py @@ -249,8 +249,8 @@ class OpenNebulaManager(): vm_specs = vm_specs_formatter.format( vcpu=int(specs['cpu']), cpu=0.1 * int(specs['cpu']), - memory=1024 * int(specs['memory']), - + memory=(512 if specs['memory'] == 0.5 else + 1024 * int(specs['memory'])), ) vm_specs += """ fs @@ -269,8 +269,8 @@ class OpenNebulaManager(): vm_specs = vm_specs_formatter.format( vcpu=int(specs['cpu']), cpu=0.1 * int(specs['cpu']), - memory=1024 * int(specs['memory']), - + memory=(512 if specs['memory'] == 0.5 else + 1024 * int(specs['memory'])), ) vm_specs += """ fs From f2f95c85593027fdef587bad9a9f117c56ab67f3 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 1 Oct 2018 08:56:45 +0200 Subject: [PATCH 108/154] Update min attribute according to minimum ram in the plugin --- .../templates/datacenterlight/includes/_calculator_form.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/datacenterlight/templates/datacenterlight/includes/_calculator_form.html b/datacenterlight/templates/datacenterlight/includes/_calculator_form.html index 0b73c83f..6306357e 100644 --- a/datacenterlight/templates/datacenterlight/includes/_calculator_form.html +++ b/datacenterlight/templates/datacenterlight/includes/_calculator_form.html @@ -10,6 +10,7 @@ window.hddUnitPrice = {{vm_pricing.hdd_unit_price|default:0}}; window.discountAmount = {{vm_pricing.discount_amount|default:0}}; window.minRam = {{min_ram}}; + window.minRamErr = '{% blocktrans with min_ram=min_ram %}Please enter a value in range {{min_ram}} - 200.{% endblocktrans %}'; {% endif %} @@ -56,7 +57,7 @@
- GB RAM From 930333357e59db23b39a1a95832959060a0674f1 Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 2 Oct 2018 09:27:20 +0200 Subject: [PATCH 109/154] GenericProduct: Remove image field and add slug field --- ...o_20180926_0723.py => 0048_auto_20181002_0725.py} | 5 ++--- hosting/models.py | 12 +++++++----- 2 files changed, 9 insertions(+), 8 deletions(-) rename hosting/migrations/{0048_auto_20180926_0723.py => 0048_auto_20181002_0725.py} (86%) diff --git a/hosting/migrations/0048_auto_20180926_0723.py b/hosting/migrations/0048_auto_20181002_0725.py similarity index 86% rename from hosting/migrations/0048_auto_20180926_0723.py rename to hosting/migrations/0048_auto_20181002_0725.py index 6c21ab03..645067a7 100644 --- a/hosting/migrations/0048_auto_20180926_0723.py +++ b/hosting/migrations/0048_auto_20181002_0725.py @@ -1,10 +1,9 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.9.4 on 2018-09-26 07:23 +# Generated by Django 1.9.4 on 2018-10-02 07:25 from __future__ import unicode_literals from django.db import migrations, models import django.db.models.deletion -import filer.fields.image import utils.mixins @@ -26,7 +25,7 @@ class Migration(migrations.Migration): ('product_price', models.DecimalField(decimal_places=2, max_digits=6)), ('product_vat', models.DecimalField(decimal_places=4, default=0, max_digits=6)), ('product_is_subscription', models.BooleanField(default=True)), - ('product_image', filer.fields.image.FilerImageField(blank=True, help_text='The product image', null=True, on_delete=django.db.models.deletion.CASCADE, to='filer.Image')), + ('product_slug', models.SlugField(blank=True, help_text='An optional html id for the Section. Required to set as target of a link on page', null=True)), ], bases=(utils.mixins.AssignPermissionsMixin, models.Model), ), diff --git a/hosting/models.py b/hosting/models.py index ba503753..ca41452d 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -8,7 +8,6 @@ from django.utils import timezone from django.utils.functional import cached_property from datacenterlight.models import VMPricing, VMTemplate -from filer.fields.image import FilerImageField from membership.models import StripeCustomer, CustomUser from utils.mixins import AssignPermissionsMixin from utils.models import BillingAddress @@ -67,14 +66,17 @@ class GenericProduct(AssignPermissionsMixin, models.Model): product_name = models.CharField(max_length=128, default="") product_description = models.CharField(max_length=500, default="") created_at = models.DateTimeField(auto_now_add=True) - product_image = FilerImageField( - on_delete=models.CASCADE, null=True, blank=True, - help_text='The product image' - ) product_url = models.URLField(max_length=1000, null=True, blank=True) product_price = models.DecimalField(max_digits=6, decimal_places=2) product_vat = models.DecimalField(max_digits=6, decimal_places=4, default=0) product_is_subscription = models.BooleanField(default=True) + product_slug = models.SlugField( + blank=True, null=True, + help_text=( + 'An optional html id for the Section. Required to set as target ' + 'of a link on page' + ) + ) def __str__(self): return self.product_name From 3bad37c6058c8cfa9802ad67d603b46f6292d269 Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 2 Oct 2018 10:00:59 +0200 Subject: [PATCH 110/154] Make GenericProduct slug unique --- ...{0048_auto_20181002_0725.py => 0048_auto_20181002_0757.py} | 4 ++-- hosting/models.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) rename hosting/migrations/{0048_auto_20181002_0725.py => 0048_auto_20181002_0757.py} (94%) diff --git a/hosting/migrations/0048_auto_20181002_0725.py b/hosting/migrations/0048_auto_20181002_0757.py similarity index 94% rename from hosting/migrations/0048_auto_20181002_0725.py rename to hosting/migrations/0048_auto_20181002_0757.py index 645067a7..bbce4183 100644 --- a/hosting/migrations/0048_auto_20181002_0725.py +++ b/hosting/migrations/0048_auto_20181002_0757.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.9.4 on 2018-10-02 07:25 +# Generated by Django 1.9.4 on 2018-10-02 07:57 from __future__ import unicode_literals from django.db import migrations, models @@ -25,7 +25,7 @@ class Migration(migrations.Migration): ('product_price', models.DecimalField(decimal_places=2, max_digits=6)), ('product_vat', models.DecimalField(decimal_places=4, default=0, max_digits=6)), ('product_is_subscription', models.BooleanField(default=True)), - ('product_slug', models.SlugField(blank=True, help_text='An optional html id for the Section. Required to set as target of a link on page', null=True)), + ('product_slug', models.SlugField(blank=True, help_text='An optional html id for the Section. Required to set as target of a link on page', null=True, unique=True)), ], bases=(utils.mixins.AssignPermissionsMixin, models.Model), ), diff --git a/hosting/models.py b/hosting/models.py index ca41452d..1ab4c61f 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -72,6 +72,7 @@ class GenericProduct(AssignPermissionsMixin, models.Model): product_is_subscription = models.BooleanField(default=True) product_slug = models.SlugField( blank=True, null=True, + unique=True, help_text=( 'An optional html id for the Section. Required to set as target ' 'of a link on page' From e47f4f05b4cfd68683b359b6fb30d2dd033233f3 Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 2 Oct 2018 10:02:02 +0200 Subject: [PATCH 111/154] Handler if product_slug is given (wip) --- datacenterlight/views.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 8cacacb4..e9babd74 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -6,7 +6,7 @@ from django.contrib import messages from django.contrib.auth import login, authenticate from django.core.exceptions import ValidationError from django.core.urlresolvers import reverse -from django.http import HttpResponseRedirect, JsonResponse +from django.http import HttpResponseRedirect, JsonResponse, Http404 from django.shortcuts import render from django.utils.translation import get_language, ugettext_lazy as _ from django.views.decorators.cache import cache_control @@ -254,6 +254,9 @@ class PaymentOrderView(FormView): context.update({'generic_payment_form': GenericPaymentForm( prefix='generic_payment_form' ), }) + # TODO: handle if we have a product id + #if 'product_id' in self.request.session: + else: context.update({ 'vm_pricing': VMPricing.get_vm_pricing_by_name( @@ -269,6 +272,19 @@ class PaymentOrderView(FormView): request.session['generic_payment_type'] = request.GET['type'] if 'generic_payment_details' in request.session: request.session.pop('generic_payment_details') + if 'product_slug' in kwargs: + logger.debug("Product slug is " + kwargs['product_slug']) + try: + product = GenericProduct.objects.get( + product_slug=kwargs['product_slug'] + ) + except GenericProduct.DoesNotExist as dne: + logger.error( + "Product '{}' does " + "not exist".format(kwargs['product_slug']) + ) + raise Http404() + request.session['product_id'] = product.id elif 'specs' not in request.session: return HttpResponseRedirect(reverse('datacenterlight:index')) return self.render_to_response(self.get_context_data()) From e3bd963600668de1069deb4c06f0f952716ff9e8 Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 2 Oct 2018 10:02:38 +0200 Subject: [PATCH 112/154] Test product_slug url (wip) --- dynamicweb/urls.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dynamicweb/urls.py b/dynamicweb/urls.py index 7e2d58a1..0fadb91b 100644 --- a/dynamicweb/urls.py +++ b/dynamicweb/urls.py @@ -10,6 +10,7 @@ from django.conf import settings from hosting.views import ( RailsHostingView, DjangoHostingView, NodeJSHostingView ) +from datacenterlight.views import PaymentOrderView from membership import urls as membership_urls from ungleich_page.views import LandingView from django.views.generic import RedirectView @@ -29,6 +30,7 @@ urlpatterns = [ url(r'^nosystemd/', include('nosystemd.urls', namespace="nosystemd")), url(r'^taggit_autosuggest/', include('taggit_autosuggest.urls')), url(r'^jsi18n/(?P\S+?)/$', i18n.javascript_catalog), + url(r'^product/(?P[\w-]+)/$', PaymentOrderView.as_view()), ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) urlpatterns += i18n_patterns( From a4065c7e24371859711468fe83af4ae379abbe27 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Oct 2018 07:55:56 +0200 Subject: [PATCH 113/154] Handle product_slug --- datacenterlight/views.py | 7 ++++--- dynamicweb/urls.py | 4 +++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index e9babd74..606a9b61 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -268,8 +268,9 @@ class PaymentOrderView(FormView): @cache_control(no_cache=True, must_revalidate=True, no_store=True) def get(self, request, *args, **kwargs): - if 'type' in request.GET and request.GET['type'] == 'generic': - request.session['generic_payment_type'] = request.GET['type'] + if (('type' in request.GET and request.GET['type'] == 'generic') + or 'product_slug' in kwargs): + request.session['generic_payment_type'] = 'generic' if 'generic_payment_details' in request.session: request.session.pop('generic_payment_details') if 'product_slug' in kwargs: @@ -897,7 +898,7 @@ class OrderConfirmationView(DetailView): for session_var in ['specs', 'template', 'billing_address', 'billing_address_data', 'card_id', 'token', 'customer', 'generic_payment_type', - 'generic_payment_details']: + 'generic_payment_details', 'product_slug']: if session_var in request.session: del request.session[session_var] diff --git a/dynamicweb/urls.py b/dynamicweb/urls.py index 0fadb91b..37bb69a4 100644 --- a/dynamicweb/urls.py +++ b/dynamicweb/urls.py @@ -30,7 +30,9 @@ urlpatterns = [ url(r'^nosystemd/', include('nosystemd.urls', namespace="nosystemd")), url(r'^taggit_autosuggest/', include('taggit_autosuggest.urls')), url(r'^jsi18n/(?P\S+?)/$', i18n.javascript_catalog), - url(r'^product/(?P[\w-]+)/$', PaymentOrderView.as_view()), + url(r'^product/(?P[\w-]+)/$', + PaymentOrderView.as_view(), + name='show_product'), ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) urlpatterns += i18n_patterns( From 1cdc9ea6577f211382a5f906f4693121c31d64c0 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Oct 2018 08:18:19 +0200 Subject: [PATCH 114/154] Clear product_id from session --- datacenterlight/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 606a9b61..1fae0fde 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -898,7 +898,7 @@ class OrderConfirmationView(DetailView): for session_var in ['specs', 'template', 'billing_address', 'billing_address_data', 'card_id', 'token', 'customer', 'generic_payment_type', - 'generic_payment_details', 'product_slug']: + 'generic_payment_details', 'product_id']: if session_var in request.session: del request.session[session_var] From 530bbcd5f6500919f41c594fcd9e002ad3110f79 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Oct 2018 08:18:45 +0200 Subject: [PATCH 115/154] Create ProductPaymentForm from GenericPaymentForm --- hosting/forms.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/hosting/forms.py b/hosting/forms.py index 928f910e..c941e28f 100644 --- a/hosting/forms.py +++ b/hosting/forms.py @@ -97,6 +97,14 @@ class GenericPaymentForm(forms.Form): return recurring +class ProductPaymentForm(GenericPaymentForm): + def __init__(self, *args, **kwargs): + super(GenericPaymentForm, self).__init__(*args, **kwargs) + self.fields['product_name'].widget = forms.TextInput( + attrs={'placeholder': _('Product name'), 'readonly': 'readonly'} + ) + + class HostingUserSignupForm(forms.ModelForm): confirm_password = forms.CharField(label=_("Confirm Password"), widget=forms.PasswordInput()) From 495ac0c6d60901208922fbbe0c67ffbf4a3e8cbc Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Oct 2018 08:19:29 +0200 Subject: [PATCH 116/154] Use ProductPaymentForm instead of GenericPaymentForm Only if product_id is in the session, which identifies that we are coming here via product_slug --- datacenterlight/views.py | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 1fae0fde..d66461ce 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -12,7 +12,9 @@ from django.utils.translation import get_language, ugettext_lazy as _ from django.views.decorators.cache import cache_control from django.views.generic import FormView, CreateView, DetailView -from hosting.forms import HostingUserLoginForm, GenericPaymentForm +from hosting.forms import ( + HostingUserLoginForm, GenericPaymentForm, ProductPaymentForm +) from hosting.models import ( HostingBill, HostingOrder, UserCardDetail, GenericProduct ) @@ -251,12 +253,22 @@ class PaymentOrderView(FormView): if ('generic_payment_type' in self.request.session and self.request.session['generic_payment_type'] == 'generic'): - context.update({'generic_payment_form': GenericPaymentForm( - prefix='generic_payment_form' - ), }) - # TODO: handle if we have a product id - #if 'product_id' in self.request.session: - + if 'product_id' in self.request.session: + product = GenericProduct.objects.get( + id=self.request.session['product_id'] + ) + context.update({'generic_payment_form': ProductPaymentForm( + prefix='generic_payment_form', + initial={'product_name': product.product_name, + 'amount': float(product.get_actual_price()), + 'recurring': product.product_is_subscription, + 'description': product.product_description, + } + ), }) + else: + context.update({'generic_payment_form': GenericPaymentForm( + prefix='generic_payment_form', + ), }) else: context.update({ 'vm_pricing': VMPricing.get_vm_pricing_by_name( From 97693f0bb38ec9416cb4f1702b1ee45f2030aa5f Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Oct 2018 08:27:22 +0200 Subject: [PATCH 117/154] Format code --- hosting/forms.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/hosting/forms.py b/hosting/forms.py index c941e28f..873c0344 100644 --- a/hosting/forms.py +++ b/hosting/forms.py @@ -63,16 +63,15 @@ class GenericPaymentForm(forms.Form): empty_label=_("Choose a product"), ) amount = forms.FloatField( - widget=forms.TextInput( - attrs={'placeholder': _('Amount in CHF'), - 'readonly': 'readonly'} - ), - max_value=999999, - min_value=1, - ) + widget=forms.TextInput( + attrs={'placeholder': _('Amount in CHF'), + 'readonly': 'readonly'} + ), + max_value=999999, + min_value=1, + ) recurring = forms.BooleanField(required=False, - label=_("Recurring monthly"), - ) + label=_("Recurring monthly"), ) description = forms.CharField( widget=forms.Textarea(attrs={'style': "height: 100px;"}), required=False @@ -164,7 +163,7 @@ class UserHostingKeyForm(forms.ModelForm): public_key=openssh_pubkey_str).first().name KEY_EXISTS_MESSAGE = _( "This key exists already with the name \"%(name)s\"") % { - 'name': key_name} + 'name': key_name} raise forms.ValidationError(KEY_EXISTS_MESSAGE) with tempfile.NamedTemporaryFile(delete=True) as tmp_public_key_file: From 8a2734fa0ed76437212f24009ead1bef9678cdc2 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Oct 2018 08:36:21 +0200 Subject: [PATCH 118/154] Show GenericPaymentForm labels --- datacenterlight/templates/datacenterlight/landing_payment.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datacenterlight/templates/datacenterlight/landing_payment.html b/datacenterlight/templates/datacenterlight/landing_payment.html index 5cc3875f..fb6d51b0 100644 --- a/datacenterlight/templates/datacenterlight/landing_payment.html +++ b/datacenterlight/templates/datacenterlight/landing_payment.html @@ -74,7 +74,7 @@ {% csrf_token %} {% for field in generic_payment_form %} - {% bootstrap_field field show_label=False type='fields'%} + {% bootstrap_field field type='fields'%} {% endfor %}

{{generic_payment_form.non_field_errors|striptags}}

From 27a92780a6c7b934465fc9b6e51ff6c0f501b865 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Oct 2018 08:38:20 +0200 Subject: [PATCH 119/154] Add amount label + Reset textarea height --- hosting/forms.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hosting/forms.py b/hosting/forms.py index 873c0344..91454681 100644 --- a/hosting/forms.py +++ b/hosting/forms.py @@ -65,15 +65,16 @@ class GenericPaymentForm(forms.Form): amount = forms.FloatField( widget=forms.TextInput( attrs={'placeholder': _('Amount in CHF'), - 'readonly': 'readonly'} + 'readonly': 'readonly', } ), max_value=999999, min_value=1, + label=_('Amount in CHF') ) recurring = forms.BooleanField(required=False, label=_("Recurring monthly"), ) description = forms.CharField( - widget=forms.Textarea(attrs={'style': "height: 100px;"}), + widget=forms.Textarea(attrs={'style': "height: 60px;"}), required=False ) From e1c91d886bc7a41817765b2a0ff7008679eca90b Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Oct 2018 09:35:20 +0200 Subject: [PATCH 120/154] Use explicit index in plan name formatting --- datacenterlight/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index d66461ce..c0f3d2a4 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -669,7 +669,7 @@ class OrderConfirmationView(DetailView): 2 ) ) - plan_name = "generic-{}-{0:.2f}".format( + plan_name = "generic-{0}-{1:.2f}".format( request.session['generic_payment_details']['product_id'], amount_to_be_charged ) From ca180048199e27de9623bd495764f9337adbf21c Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Oct 2018 09:36:00 +0200 Subject: [PATCH 121/154] Remove stale reference to product_id --- datacenterlight/views.py | 1 + 1 file changed, 1 insertion(+) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index c0f3d2a4..20206d1b 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -285,6 +285,7 @@ class PaymentOrderView(FormView): request.session['generic_payment_type'] = 'generic' if 'generic_payment_details' in request.session: request.session.pop('generic_payment_details') + request.session.pop('product_id') if 'product_slug' in kwargs: logger.debug("Product slug is " + kwargs['product_slug']) try: From 193b87bbb5f2286343e48e1fb0a9d462f2f5a5f2 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Oct 2018 09:36:43 +0200 Subject: [PATCH 122/154] Use proper payment form --- datacenterlight/views.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 20206d1b..d66eb8c3 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -365,14 +365,23 @@ class PaymentOrderView(FormView): # payment details form before we go on to verify payment if ('generic_payment_type' in request.session and self.request.session['generic_payment_type'] == 'generic'): - generic_payment_form = GenericPaymentForm( - data=request.POST, prefix='generic_payment_form' - ) + if 'product_id' in request.session: + generic_payment_form = ProductPaymentForm( + data=request.POST, prefix='generic_payment_form', + product_id=request.session['product_id'] + ) + else: + generic_payment_form = GenericPaymentForm( + data=request.POST, prefix='generic_payment_form' + ) if generic_payment_form.is_valid(): logger.debug("Generic payment form is valid.") - product = generic_payment_form.cleaned_data.get( - 'product_name' - ) + if 'product_id' in request.session: + product = generic_payment_form.product + else: + product = generic_payment_form.cleaned_data.get( + 'product_name' + ) gp_details = { "product_name": product.product_name, "amount": generic_payment_form.cleaned_data.get( From e4bfdec0b64eb32263682e80f1657ab1b471ac8b Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Oct 2018 09:38:49 +0200 Subject: [PATCH 123/154] Update ProductPaymentForm's validation --- hosting/forms.py | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/hosting/forms.py b/hosting/forms.py index 91454681..4092f4a1 100644 --- a/hosting/forms.py +++ b/hosting/forms.py @@ -99,11 +99,31 @@ class GenericPaymentForm(forms.Form): class ProductPaymentForm(GenericPaymentForm): def __init__(self, *args, **kwargs): - super(GenericPaymentForm, self).__init__(*args, **kwargs) - self.fields['product_name'].widget = forms.TextInput( - attrs={'placeholder': _('Product name'), 'readonly': 'readonly'} + product_id = kwargs.pop('product_id', None) + if product_id is not None: + self.product = GenericProduct.objects.get(id=product_id) + super(ProductPaymentForm, self).__init__(*args, **kwargs) + self.fields['product_name'] = forms.CharField( + widget=forms.TextInput( + attrs={'placeholder': _('Product name'), + 'readonly': 'readonly' } + ) ) + def clean_amount(self): + amount = self.cleaned_data.get('amount') + if (self.product is None or + float(self.product.get_actual_price()) != amount): + raise forms.ValidationError(_("Amount field does not match")) + return amount + + def clean_recurring(self): + recurring = self.cleaned_data.get('recurring') + if (self.product.product_is_subscription != + (True if recurring else False)): + raise forms.ValidationError(_("Recurring field does not match")) + return recurring + class HostingUserSignupForm(forms.ModelForm): confirm_password = forms.CharField(label=_("Confirm Password"), From 74ec39498ef01acef82ffdabd7eee8b9290093db Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Oct 2018 09:58:40 +0200 Subject: [PATCH 124/154] Update GenericProduct Remove url field Make slug mandatory --- ...o_20181002_0757.py => 0048_auto_20181003_0757.py} | 5 ++--- hosting/models.py | 12 +++++------- 2 files changed, 7 insertions(+), 10 deletions(-) rename hosting/migrations/{0048_auto_20181002_0757.py => 0048_auto_20181003_0757.py} (82%) diff --git a/hosting/migrations/0048_auto_20181002_0757.py b/hosting/migrations/0048_auto_20181003_0757.py similarity index 82% rename from hosting/migrations/0048_auto_20181002_0757.py rename to hosting/migrations/0048_auto_20181003_0757.py index bbce4183..7b80958a 100644 --- a/hosting/migrations/0048_auto_20181002_0757.py +++ b/hosting/migrations/0048_auto_20181003_0757.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.9.4 on 2018-10-02 07:57 +# Generated by Django 1.9.4 on 2018-10-03 07:57 from __future__ import unicode_literals from django.db import migrations, models @@ -19,13 +19,12 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('product_name', models.CharField(default='', max_length=128)), + ('product_slug', models.SlugField(help_text='An optional html id for the Section. Required to set as target of a link on page', unique=True)), ('product_description', models.CharField(default='', max_length=500)), ('created_at', models.DateTimeField(auto_now_add=True)), - ('product_url', models.URLField(blank=True, max_length=1000, null=True)), ('product_price', models.DecimalField(decimal_places=2, max_digits=6)), ('product_vat', models.DecimalField(decimal_places=4, default=0, max_digits=6)), ('product_is_subscription', models.BooleanField(default=True)), - ('product_slug', models.SlugField(blank=True, help_text='An optional html id for the Section. Required to set as target of a link on page', null=True, unique=True)), ], bases=(utils.mixins.AssignPermissionsMixin, models.Model), ), diff --git a/hosting/models.py b/hosting/models.py index 1ab4c61f..b895b81d 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -64,20 +64,18 @@ class OrderDetail(AssignPermissionsMixin, models.Model): class GenericProduct(AssignPermissionsMixin, models.Model): permissions = ('view_genericproduct',) product_name = models.CharField(max_length=128, default="") - product_description = models.CharField(max_length=500, default="") - created_at = models.DateTimeField(auto_now_add=True) - product_url = models.URLField(max_length=1000, null=True, blank=True) - product_price = models.DecimalField(max_digits=6, decimal_places=2) - product_vat = models.DecimalField(max_digits=6, decimal_places=4, default=0) - product_is_subscription = models.BooleanField(default=True) product_slug = models.SlugField( - blank=True, null=True, unique=True, help_text=( 'An optional html id for the Section. Required to set as target ' 'of a link on page' ) ) + product_description = models.CharField(max_length=500, default="") + created_at = models.DateTimeField(auto_now_add=True) + product_price = models.DecimalField(max_digits=6, decimal_places=2) + product_vat = models.DecimalField(max_digits=6, decimal_places=4, default=0) + product_is_subscription = models.BooleanField(default=True) def __str__(self): return self.product_name From ec70cd1c8332f67bf18f8b864a8a97edfc5db447 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Oct 2018 22:53:24 +0200 Subject: [PATCH 125/154] Pass product_id to ProductPaymentForm --- datacenterlight/views.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index d66eb8c3..63ed94c7 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -263,7 +263,8 @@ class PaymentOrderView(FormView): 'amount': float(product.get_actual_price()), 'recurring': product.product_is_subscription, 'description': product.product_description, - } + }, + product_id=product.id ), }) else: context.update({'generic_payment_form': GenericPaymentForm( From b3e3af1c1a428a01bcfbf1abd313e6aba3013d81 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Oct 2018 22:54:45 +0200 Subject: [PATCH 126/154] Make ProductPaymentForm's recurring field hidden --- hosting/forms.py | 11 +++++++++++ hosting/static/hosting/js/payment.js | 7 ++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/hosting/forms.py b/hosting/forms.py index 4092f4a1..6bf9cdd0 100644 --- a/hosting/forms.py +++ b/hosting/forms.py @@ -109,6 +109,17 @@ class ProductPaymentForm(GenericPaymentForm): 'readonly': 'readonly' } ) ) + if self.product.product_is_subscription: + self.fields['amount'].label = "{amt} ({payment_type})".format( + amt=_('Amount in CHF'), + payment_type=_('Monthly subscription') + ) + else: + self.fields['amount'].label = "{amt} ({payment_type})".format( + amt=_('Amount in CHF'), + payment_type = _('One time payment') + ) + self.fields['recurring'].widget=forms.HiddenInput() def clean_amount(self): amount = self.cleaned_data.get('amount') diff --git a/hosting/static/hosting/js/payment.js b/hosting/static/hosting/js/payment.js index 52dd511a..fa89f218 100644 --- a/hosting/static/hosting/js/payment.js +++ b/hosting/static/hosting/js/payment.js @@ -165,9 +165,14 @@ $(document).ready(function () { function submitBillingForm() { var billing_form = $('#billing-form'); + var recurring_input = $('#id_generic_payment_form-recurring'); billing_form.append(''); billing_form.append(''); - billing_form.append(''); + if (recurring_input.attr('type') === 'hidden') { + billing_form.append(''); + } else { + billing_form.append(''); + } billing_form.append(''); billing_form.submit(); } From 1100d61b5d5860aa64039ecbc0563ccb594ca848 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Oct 2018 23:17:25 +0200 Subject: [PATCH 127/154] Fix flake8 warnings --- hosting/forms.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hosting/forms.py b/hosting/forms.py index 6bf9cdd0..50885e6a 100644 --- a/hosting/forms.py +++ b/hosting/forms.py @@ -106,7 +106,7 @@ class ProductPaymentForm(GenericPaymentForm): self.fields['product_name'] = forms.CharField( widget=forms.TextInput( attrs={'placeholder': _('Product name'), - 'readonly': 'readonly' } + 'readonly': 'readonly'} ) ) if self.product.product_is_subscription: @@ -117,9 +117,9 @@ class ProductPaymentForm(GenericPaymentForm): else: self.fields['amount'].label = "{amt} ({payment_type})".format( amt=_('Amount in CHF'), - payment_type = _('One time payment') + payment_type=_('One time payment') ) - self.fields['recurring'].widget=forms.HiddenInput() + self.fields['recurring'].widget = forms.HiddenInput() def clean_amount(self): amount = self.cleaned_data.get('amount') From 8758cd1cd862517a05dfd884fb087d6c063ac22e Mon Sep 17 00:00:00 2001 From: PCoder Date: Fri, 5 Oct 2018 08:57:20 +0200 Subject: [PATCH 128/154] Add style input-no-border --- datacenterlight/static/datacenterlight/css/common.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/datacenterlight/static/datacenterlight/css/common.css b/datacenterlight/static/datacenterlight/css/common.css index 28674b30..e1dea021 100644 --- a/datacenterlight/static/datacenterlight/css/common.css +++ b/datacenterlight/static/datacenterlight/css/common.css @@ -179,4 +179,10 @@ footer .dcl-link-separator::before { .new-card-button-margin button{ margin-top: 5px; margin-bottom: 5px; +} + +.input-no-border { + border: none !important; + background: transparent !important; + resize: none; } \ No newline at end of file From e3ec67d32c5caff2cf7d3f33abeff54c558221a3 Mon Sep 17 00:00:00 2001 From: PCoder Date: Fri, 5 Oct 2018 08:58:10 +0200 Subject: [PATCH 129/154] ProductPaymentForm: Set input fields input-no-border style --- hosting/forms.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hosting/forms.py b/hosting/forms.py index 50885e6a..16b06fe0 100644 --- a/hosting/forms.py +++ b/hosting/forms.py @@ -120,6 +120,9 @@ class ProductPaymentForm(GenericPaymentForm): payment_type=_('One time payment') ) self.fields['recurring'].widget = forms.HiddenInput() + self.fields['product_name'].widget.attrs['class'] = 'input-no-border' + self.fields['amount'].widget.attrs['class'] = 'input-no-border' + self.fields['description'].widget.attrs['class'] = 'input-no-border' def clean_amount(self): amount = self.cleaned_data.get('amount') From 12eabc5f6c56580d00e098bee17dcf028e4f5bbc Mon Sep 17 00:00:00 2001 From: PCoder Date: Fri, 5 Oct 2018 08:58:54 +0200 Subject: [PATCH 130/154] Redirect user to product page on login For the case when user is on product page and tries logging in --- datacenterlight/views.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 63ed94c7..aa404db5 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -346,6 +346,13 @@ class PaymentOrderView(FormView): auth_user = authenticate(email=email, password=password) if auth_user: login(self.request, auth_user) + if 'product_slug' in kwargs: + return HttpResponseRedirect( + reverse('show_product', + kwargs={ + 'product_slug': kwargs['product_slug']} + ) + ) return HttpResponseRedirect( reverse('datacenterlight:payment') ) From f4579595c3be72246ca609febdee2a718abe8756 Mon Sep 17 00:00:00 2001 From: PCoder Date: Fri, 5 Oct 2018 09:05:37 +0200 Subject: [PATCH 131/154] Add newline character to end of file --- datacenterlight/static/datacenterlight/css/common.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datacenterlight/static/datacenterlight/css/common.css b/datacenterlight/static/datacenterlight/css/common.css index e1dea021..00ee52cc 100644 --- a/datacenterlight/static/datacenterlight/css/common.css +++ b/datacenterlight/static/datacenterlight/css/common.css @@ -185,4 +185,4 @@ footer .dcl-link-separator::before { border: none !important; background: transparent !important; resize: none; -} \ No newline at end of file +} From 1dafa592a2dd50b94e4ed0253070abc94e001fa2 Mon Sep 17 00:00:00 2001 From: PCoder Date: Fri, 5 Oct 2018 09:23:39 +0200 Subject: [PATCH 132/154] Update Changelog --- Changelog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog b/Changelog index 190a7af8..edce80f8 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,5 @@ +Next: + * #5690: Generic payment page -- allow admin to add a onetime/monthly product and lets user pay for this product (PR #666) 2.2.2: 2018-09-28 * #5721: Set calculator OS list in alphabetical order and set `Devuan Ascii` as the default (PR #668) * bugfix: Fix some typos and correct DE translations (PR #667) From 2fbee916cceabd7983053fce33fe2badc8b9cb15 Mon Sep 17 00:00:00 2001 From: PCoder Date: Fri, 5 Oct 2018 09:37:57 +0200 Subject: [PATCH 133/154] Correct help text for product slug field --- hosting/migrations/0049_auto_20181005_0736.py | 20 +++++++++++++++++++ hosting/models.py | 3 +-- 2 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 hosting/migrations/0049_auto_20181005_0736.py diff --git a/hosting/migrations/0049_auto_20181005_0736.py b/hosting/migrations/0049_auto_20181005_0736.py new file mode 100644 index 00000000..b091ef45 --- /dev/null +++ b/hosting/migrations/0049_auto_20181005_0736.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2018-10-05 07:36 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('hosting', '0048_auto_20181003_0757'), + ] + + operations = [ + migrations.AlterField( + model_name='genericproduct', + name='product_slug', + field=models.SlugField(help_text='An mandatory unique slug for the product', unique=True), + ), + ] diff --git a/hosting/models.py b/hosting/models.py index b895b81d..707b072d 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -67,8 +67,7 @@ class GenericProduct(AssignPermissionsMixin, models.Model): product_slug = models.SlugField( unique=True, help_text=( - 'An optional html id for the Section. Required to set as target ' - 'of a link on page' + 'An mandatory unique slug for the product' ) ) product_description = models.CharField(max_length=500, default="") From 5d9b2ee41a83acc7d264197e1d64b259255bc116 Mon Sep 17 00:00:00 2001 From: PCoder Date: Fri, 5 Oct 2018 10:36:13 +0200 Subject: [PATCH 134/154] Refactor clearing all session variables --- datacenterlight/utils.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/datacenterlight/utils.py b/datacenterlight/utils.py index 8da408a0..b9edc994 100644 --- a/datacenterlight/utils.py +++ b/datacenterlight/utils.py @@ -89,8 +89,14 @@ def create_vm(billing_address_data, stripe_customer_id, specs, create_vm_task.delay(vm_template_id, user, specs, template, order.id) - for session_var in ['specs', 'template', 'billing_address', - 'billing_address_data', 'card_id', - 'token', 'customer']: - if session_var in request.session: - del request.session[session_var] + clear_all_session_vars(request) + + +def clear_all_session_vars(request): + if request.session is not None: + for session_var in ['specs', 'template', 'billing_address', + 'billing_address_data', 'card_id', + 'token', 'customer','generic_payment_type', + 'generic_payment_details', 'product_id']: + if session_var in request.session: + del request.session[session_var] \ No newline at end of file From 806726614e3d7fa1cc435f56fe1ef83b34cfb968 Mon Sep 17 00:00:00 2001 From: PCoder Date: Fri, 5 Oct 2018 10:37:23 +0200 Subject: [PATCH 135/154] Clear all session variables before loading DCLCalculatorPlugin --- datacenterlight/cms_plugins.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/datacenterlight/cms_plugins.py b/datacenterlight/cms_plugins.py index b7841de8..cac73e8a 100644 --- a/datacenterlight/cms_plugins.py +++ b/datacenterlight/cms_plugins.py @@ -9,6 +9,7 @@ from .cms_models import ( DCLSectionPromoPluginModel, DCLCalculatorPluginModel ) from .models import VMTemplate +from datacenterlight.utils import clear_all_session_vars @plugin_pool.register_plugin @@ -85,6 +86,7 @@ class DCLCalculatorPlugin(CMSPluginBase): require_parent = True def render(self, context, instance, placeholder): + clear_all_session_vars(context['request']) context = super(DCLCalculatorPlugin, self).render( context, instance, placeholder ) From 10dab1350aa08c0470e4de8bf341ff4ae863e77b Mon Sep 17 00:00:00 2001 From: PCoder Date: Fri, 5 Oct 2018 11:01:49 +0200 Subject: [PATCH 136/154] Use refactored method clear_all_session_vars --- datacenterlight/views.py | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index aa404db5..0086e880 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -29,7 +29,7 @@ from utils.stripe_utils import StripeUtils from utils.tasks import send_plain_email_task from .forms import ContactForm from .models import VMTemplate, VMPricing -from .utils import get_cms_integration, create_vm +from .utils import get_cms_integration, create_vm, clear_all_session_vars logger = logging.getLogger(__name__) @@ -98,10 +98,7 @@ class IndexView(CreateView): @cache_control(no_cache=True, must_revalidate=True, no_store=True) def get(self, request, *args, **kwargs): - for session_var in ['specs', 'user', 'billing_address_data', - 'pricing_name']: - if session_var in request.session: - del request.session[session_var] + clear_all_session_vars(request) return HttpResponseRedirect(reverse('datacenterlight:cms_index')) def post(self, request): @@ -925,12 +922,7 @@ class OrderConfirmationView(DetailView): 'info@ungleich.ch for any question that you may have.') ) } - for session_var in ['specs', 'template', 'billing_address', - 'billing_address_data', 'card_id', - 'token', 'customer', 'generic_payment_type', - 'generic_payment_details', 'product_id']: - if session_var in request.session: - del request.session[session_var] + clear_all_session_vars(request) return JsonResponse(response) From 2b7d4bbef5eb815413bf72b18038c9b0b583f0f7 Mon Sep 17 00:00:00 2001 From: PCoder Date: Fri, 5 Oct 2018 11:10:10 +0200 Subject: [PATCH 137/154] Redirect to product page on error --- datacenterlight/views.py | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 0086e880..b7c606e9 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -398,7 +398,8 @@ class PaymentOrderView(FormView): "description": generic_payment_form.cleaned_data.get( 'description' ), - "product_id": product.id + "product_id": product.id, + "product_slug": product.product_slug } request.session["generic_payment_details"] = ( gp_details @@ -570,7 +571,12 @@ class OrderConfirmationView(DetailView): response = { 'status': False, 'redirect': "{url}#{section}".format( - url=reverse('datacenterlight:payment'), + url=(reverse('show_product', kwargs={ + 'product_slug': kwargs['product_slug']} + ) if 'generic_payment_details' in + request.session else + reverse('datacenterlight:payment') + ), section='payment_error'), 'msg_title': str(_('Error.')), 'msg_body': str( @@ -609,7 +615,12 @@ class OrderConfirmationView(DetailView): response = { 'status': False, 'redirect': "{url}#{section}".format( - url=reverse('hosting:payment'), + url=(reverse('show_product', kwargs={ + 'product_slug': kwargs['product_slug']} + ) if 'generic_payment_details' in + request.session else + reverse('datacenterlight:payment') + ), section='payment_error'), 'msg_title': str(_('Error.')), 'msg_body': str( @@ -664,8 +675,12 @@ class OrderConfirmationView(DetailView): response = { 'status': False, 'redirect': "{url}#{section}".format( - url=(reverse('datacenterlight:payment') + - "?type=generic"), + url=(reverse('show_product', kwargs={ + 'product_slug': kwargs['product_slug']} + ) if 'generic_payment_details' in + request.session else + reverse('datacenterlight:payment') + ), section='payment_error'), 'msg_title': str(_('Error.')), 'msg_body': str( @@ -736,7 +751,12 @@ class OrderConfirmationView(DetailView): response = { 'status': False, 'redirect': "{url}#{section}".format( - url=reverse('datacenterlight:payment'), + url=(reverse('show_product', kwargs={ + 'product_slug': kwargs['product_slug']} + ) if 'generic_payment_details' in + request.session else + reverse('datacenterlight:payment') + ), section='payment_error'), 'msg_title': str(_('Error.')), 'msg_body': str( From 5985ded36f6f515dd043e6f3cf747f274a9e6408 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 6 Oct 2018 07:31:38 +0200 Subject: [PATCH 138/154] Obtain product_slug from session Obtaining slug from kwargs won't work in OrderConfirmation page because we do not set the kwargs for that page. To resolve this, I add the product_slug to the generic_payment_details dict in the session --- datacenterlight/views.py | 43 ++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index b7c606e9..19faa2d1 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -571,12 +571,14 @@ class OrderConfirmationView(DetailView): response = { 'status': False, 'redirect': "{url}#{section}".format( - url=(reverse('show_product', kwargs={ - 'product_slug': kwargs['product_slug']} - ) if 'generic_payment_details' in - request.session else + url=(reverse( + 'show_product', + kwargs={'product_slug': + request.session['generic_payment_details'] + ['product_slug']} + ) if 'generic_payment_details' in request.session else reverse('datacenterlight:payment') - ), + ), section='payment_error'), 'msg_title': str(_('Error.')), 'msg_body': str( @@ -615,12 +617,16 @@ class OrderConfirmationView(DetailView): response = { 'status': False, 'redirect': "{url}#{section}".format( - url=(reverse('show_product', kwargs={ - 'product_slug': kwargs['product_slug']} - ) if 'generic_payment_details' in - request.session else + url=(reverse( + 'show_product', + kwargs={'product_slug': + request.session + ['generic_payment_details'] + ['product_slug']} + ) if 'generic_payment_details' in + request.session else reverse('datacenterlight:payment') - ), + ), section='payment_error'), 'msg_title': str(_('Error.')), 'msg_body': str( @@ -676,7 +682,7 @@ class OrderConfirmationView(DetailView): 'status': False, 'redirect': "{url}#{section}".format( url=(reverse('show_product', kwargs={ - 'product_slug': kwargs['product_slug']} + 'product_slug': gp_details['product_slug']} ) if 'generic_payment_details' in request.session else reverse('datacenterlight:payment') @@ -751,13 +757,16 @@ class OrderConfirmationView(DetailView): response = { 'status': False, 'redirect': "{url}#{section}".format( - url=(reverse('show_product', kwargs={ - 'product_slug': kwargs['product_slug']} - ) if 'generic_payment_details' in - request.session else + url=(reverse( + 'show_product', + kwargs={'product_slug': + request.session['generic_payment_details'] + ['product_slug']} + ) if 'generic_payment_details' in request.session else reverse('datacenterlight:payment') - ), - section='payment_error'), + ), + section='payment_error' + ), 'msg_title': str(_('Error.')), 'msg_body': str( _('There was a payment related error.' From 5770c231ee60063aa60b93c273c03dcedb0826fb Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 6 Oct 2018 07:47:40 +0200 Subject: [PATCH 139/154] Fix flake8 warnings --- datacenterlight/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/datacenterlight/utils.py b/datacenterlight/utils.py index b9edc994..bbcb16ab 100644 --- a/datacenterlight/utils.py +++ b/datacenterlight/utils.py @@ -96,7 +96,7 @@ def clear_all_session_vars(request): if request.session is not None: for session_var in ['specs', 'template', 'billing_address', 'billing_address_data', 'card_id', - 'token', 'customer','generic_payment_type', + 'token', 'customer', 'generic_payment_type', 'generic_payment_details', 'product_id']: if session_var in request.session: - del request.session[session_var] \ No newline at end of file + del request.session[session_var] From 27ee5ca5ace4e08c49d8704a3177aeceb28c250d Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 8 Oct 2018 07:28:04 +0200 Subject: [PATCH 140/154] Update Changelog for 2.3 --- Changelog | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Changelog b/Changelog index edce80f8..6d1b57d2 100644 --- a/Changelog +++ b/Changelog @@ -1,5 +1,5 @@ -Next: - * #5690: Generic payment page -- allow admin to add a onetime/monthly product and lets user pay for this product (PR #666) +2.3: 2018-10-08 + * #5690: Generic payment page - allow admin to add a onetime/monthly product and the frontend for user to pay for this product (PR #666) 2.2.2: 2018-09-28 * #5721: Set calculator OS list in alphabetical order and set `Devuan Ascii` as the default (PR #668) * bugfix: Fix some typos and correct DE translations (PR #667) From 26970ece923fb4f45a1db7629d62e143b96f9dd6 Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 16 Oct 2018 08:55:05 +0200 Subject: [PATCH 141/154] Set default RAM step to 1 We later change this according to minRam set in the backend --- .../templates/datacenterlight/includes/_calculator_form.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datacenterlight/templates/datacenterlight/includes/_calculator_form.html b/datacenterlight/templates/datacenterlight/includes/_calculator_form.html index 6306357e..d25e9df2 100644 --- a/datacenterlight/templates/datacenterlight/includes/_calculator_form.html +++ b/datacenterlight/templates/datacenterlight/includes/_calculator_form.html @@ -58,7 +58,7 @@
+ data-error="{% blocktrans with min_ram=min_ram %}Please enter a value in range {{min_ram}} - 200.{% endblocktrans %}" required step="1"> GB RAM
From b7cc7b08ce951dc28282c1d29ceed5919d76bb96 Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 16 Oct 2018 08:58:12 +0200 Subject: [PATCH 142/154] Update calculator min RAM input js validation --- .../static/datacenterlight/js/main.js | 43 +++++++++++++++++-- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/datacenterlight/static/datacenterlight/js/main.js b/datacenterlight/static/datacenterlight/js/main.js index 0999caa5..7a02b384 100644 --- a/datacenterlight/static/datacenterlight/js/main.js +++ b/datacenterlight/static/datacenterlight/js/main.js @@ -44,6 +44,7 @@ _initNavUrl(); _initPricing(); ajaxForms(); + $('#ramValue').data('old-value', $('#ramValue').val()); }); $(window).resize(function() { @@ -148,29 +149,65 @@ var data = $(this).data('minus'); if (cardPricing[data].value > cardPricing[data].min) { - if(data === 'ram' && cardPricing[data].value === 1){ + if(data === 'ram' && String(cardPricing[data].value) === "1" && minRam === 0.5){ cardPricing[data].value = 0.5; + $('#ramValue').val('0.5'); + $("#ramValue").attr('step', 0.5); } else { cardPricing[data].value = Number(cardPricing[data].value) - cardPricing[data].interval; } } _fetchPricing(); + $('#ramValue').data('old-value', $('#ramValue').val()); }); $('.fa-plus-circle.right').click(function(event) { var data = $(this).data('plus'); if (cardPricing[data].value < cardPricing[data].max) { - if(data === 'ram' && cardPricing[data].value === 0.5){ + if(data === 'ram' && String(cardPricing[data].value) === "0.5" && minRam === 0.5){ cardPricing[data].value = 1; + $('#ramValue').val('1'); + $("#ramValue").attr('step', 1); } else { cardPricing[data].value = Number(cardPricing[data].value) + cardPricing[data].interval; } } _fetchPricing(); + $('#ramValue').data('old-value', $('#ramValue').val()); }); $('.input-price').change(function() { var data = $(this).attr("name"); - cardPricing[data].value = $('input[name=' + data + ']').val(); + var input = $('input[name=' + data + ']'); + var inputValue = input.val(); + console.log(".input-price change " + data + " => " + inputValue); + if(data === 'ram' && window.minRam === 0.5) { + if (inputValue === '1'){ + //$('input[name=' + data + ']').attr('step', 0.5); + } + } + + if(data === 'ram') { + var ramInput = $('#ramValue'); + if ($('#ramValue').data('old-value') < $('#ramValue').val()) { + console.log("$('#ramValue').val() = " + $('#ramValue').val() + ", and minRam = " + minRam); + if($('#ramValue').val() === '1' && minRam === 0.5) { + console.log("Setting step = 1"); + $("#ramValue").attr('step', 1); + $('#ramValue').val('1'); + } + console.log('Alert up'); + } else { + if($('#ramValue').val() === '0' && minRam === 0.5) { + console.log("Setting step = 0.5"); + $("#ramValue").attr('step', 0.5); + $('#ramValue').val('0.5'); + } + console.log('Alert down'); + } + inputValue = $('#ramValue').val(); + $('#ramValue').data('old-value', $('#ramValue').val()); + } + cardPricing[data].value = inputValue; _fetchPricing(); }); } From 0e9f8ce906939ed404302e17fbefd7a2646541c1 Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 16 Oct 2018 09:01:00 +0200 Subject: [PATCH 143/154] Cleanup unnecessary code --- datacenterlight/static/datacenterlight/js/main.js | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/datacenterlight/static/datacenterlight/js/main.js b/datacenterlight/static/datacenterlight/js/main.js index 7a02b384..65db1d6b 100644 --- a/datacenterlight/static/datacenterlight/js/main.js +++ b/datacenterlight/static/datacenterlight/js/main.js @@ -179,30 +179,19 @@ var data = $(this).attr("name"); var input = $('input[name=' + data + ']'); var inputValue = input.val(); - console.log(".input-price change " + data + " => " + inputValue); - if(data === 'ram' && window.minRam === 0.5) { - if (inputValue === '1'){ - //$('input[name=' + data + ']').attr('step', 0.5); - } - } if(data === 'ram') { var ramInput = $('#ramValue'); if ($('#ramValue').data('old-value') < $('#ramValue').val()) { - console.log("$('#ramValue').val() = " + $('#ramValue').val() + ", and minRam = " + minRam); if($('#ramValue').val() === '1' && minRam === 0.5) { - console.log("Setting step = 1"); $("#ramValue").attr('step', 1); $('#ramValue').val('1'); } - console.log('Alert up'); } else { if($('#ramValue').val() === '0' && minRam === 0.5) { - console.log("Setting step = 0.5"); $("#ramValue").attr('step', 0.5); $('#ramValue').val('0.5'); } - console.log('Alert down'); } inputValue = $('#ramValue').val(); $('#ramValue').data('old-value', $('#ramValue').val()); From 8929a26714130cbe84d3df057bb5a622ff9b147b Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 17 Oct 2018 07:27:28 +0200 Subject: [PATCH 144/154] Round VAT percent to 2 decimal places --- utils/hosting_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/hosting_utils.py b/utils/hosting_utils.py index 8354f485..ec97a320 100644 --- a/utils/hosting_utils.py +++ b/utils/hosting_utils.py @@ -130,7 +130,7 @@ def get_vm_price_with_vat(cpu, memory, ssd_size, hdd_size=0, 'amount': round(float(pricing.discount_amount), 2) } return (round(float(price), 2), round(float(vat), 2), - round(float(vat_percent)), discount) + round(float(vat_percent), 2), discount) def ping_ok(host_ipv6): From 5a52ae1a1d219aff9822e1ac98c100f9bb9f1cf1 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 17 Oct 2018 07:47:34 +0200 Subject: [PATCH 145/154] Update Changelog for 2.3.1 --- Changelog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog b/Changelog index 6d1b57d2..27802eb6 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,5 @@ +2.3.1: 2018-10-17 + * bugfix: [hosting, dcl] Show VAT percent rounded to 2 decimal places in the order confirmation page (PR #673) 2.3: 2018-10-08 * #5690: Generic payment page - allow admin to add a onetime/monthly product and the frontend for user to pay for this product (PR #666) 2.2.2: 2018-09-28 From 814163e58e980a5176d6dacfb64fde65f9c00451 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 17 Oct 2018 09:11:30 +0200 Subject: [PATCH 146/154] Add minRAM js code for hosting also --- hosting/static/hosting/js/initial.js | 48 +++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/hosting/static/hosting/js/initial.js b/hosting/static/hosting/js/initial.js index 9c1c226e..6b6d744d 100644 --- a/hosting/static/hosting/js/initial.js +++ b/hosting/static/hosting/js/initial.js @@ -157,6 +157,10 @@ $( document ).ready(function() { /* --------------------------------------------- Scripts initialization --------------------------------------------- */ + var minRam = 1; + if(window.minRam){ + minRam = window.minRam; + } var cardPricing = { 'cpu': { 'id': 'coreValue', @@ -168,7 +172,7 @@ $( document ).ready(function() { 'ram': { 'id': 'ramValue', 'value': 2, - 'min': 1, + 'min': minRam, 'max': 200, 'interval': 1 }, @@ -188,21 +192,54 @@ $( document ).ready(function() { var data = $(this).data('minus'); if (cardPricing[data].value > cardPricing[data].min) { - cardPricing[data].value = Number(cardPricing[data].value) - cardPricing[data].interval; + if(data === 'ram' && String(cardPricing[data].value) === "1" && minRam === 0.5){ + cardPricing[data].value = 0.5; + $('#ramValue').val('0.5'); + $("#ramValue").attr('step', 0.5); + } else { + cardPricing[data].value = Number(cardPricing[data].value) - cardPricing[data].interval; + } } _fetchPricing(); + $('#ramValue').data('old-value', $('#ramValue').val()); }); $('.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; + if(data === 'ram' && String(cardPricing[data].value) === "0.5" && minRam === 0.5){ + cardPricing[data].value = 1; + $('#ramValue').val('1'); + $("#ramValue").attr('step', 1); + } else { + cardPricing[data].value = Number(cardPricing[data].value) + cardPricing[data].interval; + } } _fetchPricing(); + $('#ramValue').data('old-value', $('#ramValue').val()); }); $('.input-price').change(function() { var data = $(this).attr("name"); - cardPricing[data].value = $('input[name=' + data + ']').val(); + var input = $('input[name=' + data + ']'); + var inputValue = input.val(); + + if(data === 'ram') { + var ramInput = $('#ramValue'); + if ($('#ramValue').data('old-value') < $('#ramValue').val()) { + if($('#ramValue').val() === '1' && minRam === 0.5) { + $("#ramValue").attr('step', 1); + $('#ramValue').val('1'); + } + } else { + if($('#ramValue').val() === '0' && minRam === 0.5) { + $("#ramValue").attr('step', 0.5); + $('#ramValue').val('0.5'); + } + } + inputValue = $('#ramValue').val(); + $('#ramValue').data('old-value', $('#ramValue').val()); + } + cardPricing[data].value = inputValue; _fetchPricing(); }); } @@ -236,4 +273,5 @@ $( document ).ready(function() { } _initPricing(); -}); \ No newline at end of file + $('#ramValue').data('old-value', $('#ramValue').val()); +}); From 3ebf932422f023c6f08321c0fcdab609f5a05a01 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 17 Oct 2018 09:29:06 +0200 Subject: [PATCH 147/154] Adjust hosting calculator form validation For 512 MB RAM offer --- hosting/views.py | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/hosting/views.py b/hosting/views.py index c9b7ab08..c2eef1bd 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -32,6 +32,7 @@ from stored_messages.api import mark_read from stored_messages.models import Message from stored_messages.settings import stored_messages_settings +from datacenterlight.cms_models import DCLCalculatorPluginModel from datacenterlight.models import VMTemplate, VMPricing from datacenterlight.utils import create_vm, get_cms_integration from hosting.models import UserCardDetail @@ -1198,7 +1199,29 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View): raise ValidationError(_('Invalid number of cores')) def validate_memory(self, value): - if (value > 200) or (value < 1): + if 'pid' in self.request.POST: + try: + plugin = DCLCalculatorPluginModel.objects.get( + id=self.request.POST['pid'] + ) + except DCLCalculatorPluginModel.DoesNotExist as dne: + logger.error( + str(dne) + " plugin_id: " + self.request.POST['pid'] + ) + raise ValidationError(_('Invalid calculator properties')) + if plugin.enable_512mb_ram: + if value % 1 == 0 or value == 0.5: + logger.debug( + "Given ram {value} is either 0.5 or a" + " whole number".format(value=value) + ) + if (value > 200) or (value < 0.5): + raise ValidationError(_('Invalid RAM size')) + else: + raise ValidationError(_('Invalid RAM size')) + elif (value > 200) or (value < 1) or (value % 1 != 0): + raise ValidationError(_('Invalid RAM size')) + else: raise ValidationError(_('Invalid RAM size')) def validate_storage(self, value): @@ -1218,7 +1241,7 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View): cores = request.POST.get('cpu') cores_field = forms.IntegerField(validators=[self.validate_cores]) memory = request.POST.get('ram') - memory_field = forms.IntegerField(validators=[self.validate_memory]) + memory_field = forms.FloatField(validators=[self.validate_memory]) storage = request.POST.get('storage') storage_field = forms.IntegerField(validators=[self.validate_storage]) template_id = int(request.POST.get('config')) From 270c6101118c798a72accdf28585d097f2142ae9 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 17 Oct 2018 09:56:39 +0200 Subject: [PATCH 148/154] Correct the min value of RAM Based on what is set by the admin in the backend --- .../templates/datacenterlight/includes/_calculator_form.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datacenterlight/templates/datacenterlight/includes/_calculator_form.html b/datacenterlight/templates/datacenterlight/includes/_calculator_form.html index d25e9df2..f9896f17 100644 --- a/datacenterlight/templates/datacenterlight/includes/_calculator_form.html +++ b/datacenterlight/templates/datacenterlight/includes/_calculator_form.html @@ -57,7 +57,7 @@
- GB RAM From c36554b4d16ecb3fb30e2d2e86c2544954f37cbf Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 18 Oct 2018 06:58:18 +0200 Subject: [PATCH 149/154] Round total_price also to 2 decimal places --- datacenterlight/views.py | 2 +- hosting/views.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index af19b244..445ff7cf 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -197,7 +197,7 @@ class IndexView(CreateView): 'vat': vat, 'vat_percent': vat_percent, 'discount': discount, - 'total_price': price + vat - discount['amount'], + 'total_price': round(price + vat - discount['amount'], 2), 'pricing_name': vm_pricing_name } request.session['specs'] = specs diff --git a/hosting/views.py b/hosting/views.py index c2eef1bd..32de4e54 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1305,7 +1305,7 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View): 'price': price, 'vat': vat, 'vat_percent': vat_percent, - 'total_price': price + vat - discount['amount'], + 'total_price': round(price + vat - discount['amount'], 2), 'pricing_name': vm_pricing_name } From 3a489f5be0aa60252330c79c379456249d8799bb Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 18 Oct 2018 07:44:07 +0200 Subject: [PATCH 150/154] Update Changelog for 2.4 --- Changelog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog b/Changelog index 27802eb6..c1a5708e 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,5 @@ +2.4: 2018-10-18 + * #5681: [hosting,dcl] Allow admin to lower minimum RAM to 512 MB (PR #672) 2.3.1: 2018-10-17 * bugfix: [hosting, dcl] Show VAT percent rounded to 2 decimal places in the order confirmation page (PR #673) 2.3: 2018-10-08 From 062e81408d144fc51e131d373e2a9dab7b172c6b Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 18 Oct 2018 09:03:02 +0200 Subject: [PATCH 151/154] Update pycryptodome From 3.4 to 3.6.6 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 85a41841..4646c6c9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -69,7 +69,7 @@ model-mommy==1.2.6 phonenumbers==7.4.0 phonenumberslite==7.4.0 psycopg2==2.7.3.2 -pycryptodome==3.4 +pycryptodome==3.6.6 pylibmc==1.5.1 python-dateutil==2.5.3 python-slugify==1.2.0 From bdf9f53648ae9821f6d04a71ebb445fd991cee58 Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 18 Oct 2018 09:21:16 +0200 Subject: [PATCH 152/154] Update Changelog for 2.4.1 --- Changelog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog b/Changelog index c1a5708e..e6621e35 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,5 @@ +2.4.1: 2018-10-18 + * bugfix: Update pycryptodome module from 3.4 to 3.6.6 (PR #674) 2.4: 2018-10-18 * #5681: [hosting,dcl] Allow admin to lower minimum RAM to 512 MB (PR #672) 2.3.1: 2018-10-17 From e9cb303b0917e0e966a070882d4aded8d554e5ec Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 22 Oct 2018 22:39:13 +0200 Subject: [PATCH 153/154] Use correct django-multisite version We had multiple versions of django-multisite in the requirements.txt. This commit removes the wrong version that caused server not to start with an error File "/usr/local/lib/python3.5/site-packages/django/contrib/sites/models.py" in get_current 65. return self._get_site_by_id(site_id) File "/usr/local/lib/python3.5/site-packages/django/contrib/sites/models.py" in _get_site_by_id 37. return SITE_CACHE[site_id] File "/usr/local/lib/python3.5/site-packages/multisite/hacks.py" in __getitem__ 124. raise KeyError(key) --- requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4646c6c9..a14617a9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -34,7 +34,6 @@ django-meta==1.2 django-meta-mixin==0.3.0 django-model-utils==2.5 django-mptt==0.8.4 -django-multisite==1.4.1 django-parler==1.6.3 django-phonenumber-field==1.1.0 django-polymorphic==0.9.2 From 2c674572c9ed96d240b51273b3dbc85c13f62422 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 22 Oct 2018 23:25:05 +0200 Subject: [PATCH 154/154] Update Changelog --- Changelog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog b/Changelog index e6621e35..b85dd4ff 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,5 @@ +Next: + * bugfix: Use correct version of django-multisite (MR #676) 2.4.1: 2018-10-18 * bugfix: Update pycryptodome module from 3.4 to 3.6.6 (PR #674) 2.4: 2018-10-18