From d2beef203257436b67ed6f351735f19d6f0e4a8b Mon Sep 17 00:00:00 2001 From: Andrii Marynets Date: Thu, 3 Aug 2017 00:24:16 +0300 Subject: [PATCH 001/189] Add parameter to function for TravisCI --- membership/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/membership/models.py b/membership/models.py index a84d12ca..6b6f4413 100644 --- a/membership/models.py +++ b/membership/models.py @@ -19,7 +19,7 @@ REGISTRATION_MESSAGE = {'subject': "Validation mail", 'from': 'test@test.com'} -def get_anonymous_user_instance(): +def get_anonymous_user_instance(CustomUser): return CustomUser(name='Anonymous', email='anonymous@ungleich.ch', validation_slug=make_password(None)) From 34acf2310b484920b6dd93fca030d88030128ad8 Mon Sep 17 00:00:00 2001 From: Siarhei Puhach Date: Thu, 3 Aug 2017 12:47:34 +0300 Subject: [PATCH 002/189] Added stripe failed payment error handler --- datacenterlight/views.py | 4 ++++ hosting/templates/hosting/payment.html | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 10e2d791..7dd2a76f 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -426,6 +426,10 @@ class OrderConfirmationView(DetailView): customer = StripeCustomer.objects.filter(id=stripe_customer_id).first() stripe_utils = StripeUtils() card_details = stripe_utils.get_card_details(customer.stripe_id, request.session.get('token')) + if not card_details.get('response_object') and not card_details.get('paid'): + msg = _('Currently its not possible to make payments. Please try later.') + messages.add_message(self.request, messages.ERROR, msg, extra_tags='failed_payment') + return HttpResponseRedirect(reverse('datacenterlight:payment')) context = { 'site_url': reverse('datacenterlight:index'), 'cc_last4': card_details.get('response_object').get('last4'), diff --git a/hosting/templates/hosting/payment.html b/hosting/templates/hosting/payment.html index d03713c1..5ef7f1ce 100644 --- a/hosting/templates/hosting/payment.html +++ b/hosting/templates/hosting/payment.html @@ -49,6 +49,15 @@ +
+ {% for message in messages %} + {% if 'failed_payment' in message.tags %} +
  • +

    {{ message|safe }}

    +
+ {% endif %} + {% endfor %} +
From 60d107baeeba504a4e7a621847f1d62478c0562b Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Thu, 3 Aug 2017 15:07:36 +0200 Subject: [PATCH 003/189] Improved the condition about when to generate ssh keys --- hosting/forms.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/hosting/forms.py b/hosting/forms.py index c2b86e3e..d9812995 100644 --- a/hosting/forms.py +++ b/hosting/forms.py @@ -14,7 +14,6 @@ def generate_ssh_key_name(): class HostingUserLoginForm(forms.Form): - email = forms.CharField(widget=forms.EmailInput()) password = forms.CharField(widget=forms.PasswordInput()) @@ -45,7 +44,6 @@ class HostingUserLoginForm(forms.Form): class HostingUserSignupForm(forms.ModelForm): - confirm_password = forms.CharField(widget=forms.PasswordInput()) password = forms.CharField(widget=forms.PasswordInput()) @@ -88,9 +86,8 @@ class UserHostingKeyForm(forms.ModelForm): def clean(self): cleaned_data = self.cleaned_data - if not self.cleaned_data.get('name', ''): + if 'generate' in self.request.POST: self.cleaned_data['name'] = generate_ssh_key_name() - if not cleaned_data.get('public_key'): private_key, public_key = UserHostingKey.generate_keys() cleaned_data.update({ 'private_key': private_key, From 29fb771afb3aa787d0fec7203f4cde0a1c8f50ff Mon Sep 17 00:00:00 2001 From: Siarhei Puhach Date: Fri, 4 Aug 2017 11:01:32 +0300 Subject: [PATCH 004/189] Changed payment error place, changed message source to Stripe errors handler --- datacenterlight/views.py | 2 +- hosting/templates/hosting/payment.html | 32 ++++++++++++++------------ 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 7dd2a76f..993cb508 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -427,7 +427,7 @@ class OrderConfirmationView(DetailView): stripe_utils = StripeUtils() card_details = stripe_utils.get_card_details(customer.stripe_id, request.session.get('token')) if not card_details.get('response_object') and not card_details.get('paid'): - msg = _('Currently its not possible to make payments. Please try later.') + msg = card_details.get('error') messages.add_message(self.request, messages.ERROR, msg, extra_tags='failed_payment') return HttpResponseRedirect(reverse('datacenterlight:payment')) context = { diff --git a/hosting/templates/hosting/payment.html b/hosting/templates/hosting/payment.html index 5ef7f1ce..acb5d389 100644 --- a/hosting/templates/hosting/payment.html +++ b/hosting/templates/hosting/payment.html @@ -49,15 +49,6 @@
-
- {% for message in messages %} - {% if 'failed_payment' in message.tags %} -
  • -

    {{ message|safe }}

    -
- {% endif %} - {% endfor %} -
@@ -139,12 +130,23 @@
-

- {% blocktrans %} - You are not making any payment yet. After submitting your card - information, you will be taken to the Confirm Order Page. - {% endblocktrans %} -

+ {% if not messages %} +

+ {% blocktrans %} + You are not making any payment yet. After submitting your card + information, you will be taken to the Confirm Order Page. + {% endblocktrans %} +

+ {% endif %} +
+ {% for message in messages %} + {% if 'failed_payment' in message.tags %} +
  • +

    {{ message|safe }}

    +
+ {% endif %} + {% endfor %} +
From 8769c4cd1f6106ab7526be17e3c256bd28529374 Mon Sep 17 00:00:00 2001 From: Siarhei Puhach Date: Fri, 4 Aug 2017 12:38:24 +0300 Subject: [PATCH 005/189] Changed payment error message style --- hosting/static/hosting/css/landing-page.css | 6 +++++- hosting/templates/hosting/payment.html | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/hosting/static/hosting/css/landing-page.css b/hosting/static/hosting/css/landing-page.css index a7d69094..49471bc8 100644 --- a/hosting/static/hosting/css/landing-page.css +++ b/hosting/static/hosting/css/landing-page.css @@ -537,6 +537,10 @@ a.unlink:hover { border-radius: 3px; padding: 5px; } +.card-warning-error { + border: 1px solid #EB4D5C; + color: #EB4D5C; +} .stripe-payment-btn { outline: none; @@ -718,4 +722,4 @@ a.unlink:hover { .footer-light a:hover, .footer-light a:focus, .footer-light a:active { color: #ddd; -} \ No newline at end of file +} diff --git a/hosting/templates/hosting/payment.html b/hosting/templates/hosting/payment.html index acb5d389..ac9ab41b 100644 --- a/hosting/templates/hosting/payment.html +++ b/hosting/templates/hosting/payment.html @@ -138,11 +138,11 @@ {% endblocktrans %}

{% endif %} -
+
{% for message in messages %} {% if 'failed_payment' in message.tags %}
  • -

    {{ message|safe }}

    +

    {{ message|safe }}

{% endif %} {% endfor %} From c6b23f6aa0dcacb6e0f3b75ada4bcc12f7c25924 Mon Sep 17 00:00:00 2001 From: PCoder Date: Fri, 4 Aug 2017 20:54:28 +0530 Subject: [PATCH 006/189] Added get_object method to verify if the user is the owner of the ssh key --- hosting/views.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hosting/views.py b/hosting/views.py index 19ec5b2a..33477b50 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -342,6 +342,13 @@ class SSHKeyDeleteView(LoginRequiredMixin, DeleteView): success_url = reverse_lazy('hosting:ssh_keys') model = UserHostingKey + def get_object(self, queryset=None): + """ Hook to ensure object is owned by request.user. """ + obj = super(SSHKeyDeleteView, self).get_object() + if not obj.owner == self.request.user: + raise Http404 + return obj + def delete(self, request, *args, **kwargs): owner = self.request.user manager = OpenNebulaManager() From d85afd56e083c1da1a30001338fccb3343af5fd2 Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Fri, 4 Aug 2017 17:49:35 +0200 Subject: [PATCH 007/189] Fixed an issue - Changed owner -> user - Reformatted code --- hosting/views.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/hosting/views.py b/hosting/views.py index 33477b50..f5fbd0a7 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -210,9 +210,9 @@ class SignupValidateView(TemplateView): def get_context_data(self, **kwargs): context = super(SignupValidateView, self).get_context_data(**kwargs) login_url = '' + str(_('login')) + '' + reverse('hosting:login') + '">' + str(_('login')) + '' home_url = 'Data Center Light' + reverse('datacenterlight:index') + '">Data Center Light' message = '{signup_success_message} {lurl} \
{go_back} {hurl}.'.format( signup_success_message=_( @@ -234,7 +234,7 @@ class SignupValidatedView(SignupValidateView): context = super(SignupValidateView, self).get_context_data(**kwargs) validated = CustomUser.validate_url(self.kwargs['validate_slug']) login_url = '' + str(_('login')) + '' + reverse('hosting:login') + '">' + str(_('login')) + '' section_title = _('Account activation') if validated: message = '{account_activation_string}
{login_string} {lurl}.'.format( @@ -244,7 +244,7 @@ class SignupValidatedView(SignupValidateView): lurl=login_url) else: home_url = 'Data Center Light' + reverse('datacenterlight:index') + '">Data Center Light' message = '{sorry_message}
{go_back_to} {hurl}'.format( sorry_message=_("Sorry. Your request is invalid."), go_back_to=_('Go back to'), @@ -343,9 +343,11 @@ class SSHKeyDeleteView(LoginRequiredMixin, DeleteView): model = UserHostingKey def get_object(self, queryset=None): - """ Hook to ensure object is owned by request.user. """ + """ Hook to ensure UserHostingKey object is owned by request.user. + We reply with a Http404 if the user is not the owner of the key. + """ obj = super(SSHKeyDeleteView, self).get_object() - if not obj.owner == self.request.user: + if not obj.user == self.request.user: raise Http404 return obj From d8592fc6d8d3e1c3efba54dfe478a1f0d582b917 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 6 Aug 2017 12:30:56 +0530 Subject: [PATCH 008/189] Added celery files --- dynamicweb/__init__.py | 5 +++++ dynamicweb/celery.py | 22 ++++++++++++++++++++++ dynamicweb/settings/base.py | 9 +++++++++ requirements.txt | 2 ++ 4 files changed, 38 insertions(+) create mode 100644 dynamicweb/celery.py diff --git a/dynamicweb/__init__.py b/dynamicweb/__init__.py index e69de29b..b64e43e8 100644 --- a/dynamicweb/__init__.py +++ b/dynamicweb/__init__.py @@ -0,0 +1,5 @@ +from __future__ import absolute_import + +# This will make sure the app is always imported when +# Django starts so that shared_task will use this app. +from .celery import app as celery_app diff --git a/dynamicweb/celery.py b/dynamicweb/celery.py new file mode 100644 index 00000000..749ffdef --- /dev/null +++ b/dynamicweb/celery.py @@ -0,0 +1,22 @@ +from __future__ import absolute_import, unicode_literals +import os +from celery import Celery + +# set the default Django settings module for the 'celery' program. +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'dynamicweb.settings') + +app = Celery('dynamicweb') + +# Using a string here means the worker don't have to serialize +# the configuration object to child processes. +# - namespace='CELERY' means all celery-related configuration keys +# should have a `CELERY_` prefix. +app.config_from_object('django.conf:settings', namespace='CELERY') + +# Load task modules from all registered Django app configs. +app.autodiscover_tasks(lambda: settings.INSTALLED_APPS) + + +@app.task(bind=True) +def debug_task(self): + print('Request: {0!r}'.format(self.request)) diff --git a/dynamicweb/settings/base.py b/dynamicweb/settings/base.py index 40187f84..f4c4b68d 100644 --- a/dynamicweb/settings/base.py +++ b/dynamicweb/settings/base.py @@ -521,6 +521,15 @@ GOOGLE_ANALYTICS_PROPERTY_IDS = { 'dynamicweb-staging.ungleich.ch': 'staging' } +# CELERY Settings +#BROKER_URL = 'redis://localhost:6379' +BROKER_URL = 'redis+socket:///var/run/redis/redis.sock' +CELERY_RESULT_BACKEND = 'redis://localhost:6379' +CELERY_ACCEPT_CONTENT = ['application/json'] +CELERY_TASK_SERIALIZER = 'json' +CELERY_RESULT_SERIALIZER = 'json' +CELERY_TIMEZONE = 'Europe/Zurich' + ENABLE_DEBUG_LOGGING = bool_env('ENABLE_DEBUG_LOGGING') if ENABLE_DEBUG_LOGGING: diff --git a/requirements.txt b/requirements.txt index f392f4d9..2520d617 100644 --- a/requirements.txt +++ b/requirements.txt @@ -86,3 +86,5 @@ git+https://github.com/ungleich/python-oca.git#egg=python-oca djangorestframework flake8==3.3.0 python-memcached==1.58 +celery==4.0.2 +redis==2.10.5 From 3ad5928aa0930d7a6a41b0791f179cf847cf7a31 Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Sun, 6 Aug 2017 14:33:55 +0200 Subject: [PATCH 009/189] A working version of celery create_vm_task added --- datacenterlight/views.py | 121 +++++++-------------------------------- 1 file changed, 22 insertions(+), 99 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index f7a07c7e..b2798844 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -4,7 +4,6 @@ from .forms import BetaAccessForm from .models import BetaAccess, BetaAccessVMType, BetaAccessVM, VMTemplate from django.contrib import messages from django.core.urlresolvers import reverse -from django.core.mail import EmailMessage from utils.mailer import BaseEmail from django.shortcuts import render from django.shortcuts import redirect @@ -13,14 +12,15 @@ from django.core.exceptions import ValidationError from django.views.decorators.cache import cache_control from django.conf import settings from django.utils.translation import ugettext_lazy as _ -from utils.forms import BillingAddressForm, UserBillingAddressForm +from utils.forms import BillingAddressForm from utils.models import BillingAddress -from hosting.models import HostingOrder, HostingBill +from hosting.models import HostingOrder from utils.stripe_utils import StripeUtils -from datetime import datetime from membership.models import CustomUser, StripeCustomer + from opennebula_api.models import OpenNebulaManager from opennebula_api.serializers import VirtualMachineTemplateSerializer, VirtualMachineSerializer, VMTemplateSerializer +from datacenterlight.tasks import create_vm_task class LandingProgramView(TemplateView): @@ -33,7 +33,6 @@ class SuccessView(TemplateView): def get(self, request, *args, **kwargs): if 'specs' not in request.session or 'user' not in request.session: return HttpResponseRedirect(reverse('datacenterlight:index')) - elif 'token' not in request.session: return HttpResponseRedirect(reverse('datacenterlight:payment')) elif 'order_confirmation' not in request.session: @@ -79,8 +78,7 @@ class PricingView(TemplateView): manager = OpenNebulaManager() template = manager.get_template(template_id) - request.session['template'] = VirtualMachineTemplateSerializer( - template).data + request.session['template'] = VirtualMachineTemplateSerializer(template).data if not request.user.is_authenticated(): request.session['next'] = reverse('hosting:payment') @@ -132,8 +130,7 @@ class BetaAccessView(FormView): email = BaseEmail(**email_data) email.send() - messages.add_message( - self.request, messages.SUCCESS, self.success_message) + messages.add_message(self.request, messages.SUCCESS, self.success_message) return render(self.request, 'datacenterlight/beta_success.html', {}) @@ -185,8 +182,7 @@ class BetaProgramView(CreateView): email = BaseEmail(**email_data) email.send() - messages.add_message( - self.request, messages.SUCCESS, self.success_message) + messages.add_message(self.request, messages.SUCCESS, self.success_message) return HttpResponseRedirect(self.get_success_url()) @@ -199,15 +195,15 @@ class IndexView(CreateView): def validate_cores(self, value): if (value > 48) or (value < 1): - raise ValidationError(_('Invalid number of cores')) + raise ValidationError(_('Not a proper cores number')) def validate_memory(self, value): if (value > 200) or (value < 2): - raise ValidationError(_('Invalid RAM size')) + raise ValidationError(_('Not a proper ram number')) def validate_storage(self, value): if (value > 2000) or (value < 10): - raise ValidationError(_('Invalid storage size')) + raise ValidationError(_('Not a proper storage number')) @cache_control(no_cache=True, must_revalidate=True, no_store=True) def get(self, request, *args, **kwargs): @@ -230,8 +226,7 @@ class IndexView(CreateView): storage_field = forms.IntegerField(validators=[self.validate_storage]) price = request.POST.get('total') template_id = int(request.POST.get('config')) - template = VMTemplate.objects.filter( - opennebula_vm_template_id=template_id).first() + template = VMTemplate.objects.filter(opennebula_vm_template_id=template_id).first() template_data = VMTemplateSerializer(template).data name = request.POST.get('name') @@ -243,40 +238,35 @@ class IndexView(CreateView): cores = cores_field.clean(cores) except ValidationError as err: msg = '{} : {}.'.format(cores, str(err)) - messages.add_message( - self.request, messages.ERROR, msg, extra_tags='cores') + messages.add_message(self.request, messages.ERROR, msg, extra_tags='cores') return HttpResponseRedirect(reverse('datacenterlight:index') + "#order_form") try: memory = memory_field.clean(memory) except ValidationError as err: msg = '{} : {}.'.format(memory, str(err)) - messages.add_message( - self.request, messages.ERROR, msg, extra_tags='memory') + messages.add_message(self.request, messages.ERROR, msg, extra_tags='memory') return HttpResponseRedirect(reverse('datacenterlight:index') + "#order_form") try: storage = storage_field.clean(storage) except ValidationError as err: msg = '{} : {}.'.format(storage, str(err)) - messages.add_message( - self.request, messages.ERROR, msg, extra_tags='storage') + messages.add_message(self.request, messages.ERROR, msg, extra_tags='storage') return HttpResponseRedirect(reverse('datacenterlight:index') + "#order_form") try: name = name_field.clean(name) except ValidationError as err: msg = '{} {}.'.format(name, _('is not a proper name')) - messages.add_message( - self.request, messages.ERROR, msg, extra_tags='name') + messages.add_message(self.request, messages.ERROR, msg, extra_tags='name') return HttpResponseRedirect(reverse('datacenterlight:index') + "#order_form") try: email = email_field.clean(email) except ValidationError as err: msg = '{} {}.'.format(email, _('is not a proper email')) - messages.add_message( - self.request, messages.ERROR, msg, extra_tags='email') + messages.add_message(self.request, messages.ERROR, msg, extra_tags='email') return HttpResponseRedirect(reverse('datacenterlight:index') + "#order_form") specs = { @@ -341,8 +331,7 @@ class IndexView(CreateView): email = BaseEmail(**email_data) email.send() - messages.add_message( - self.request, messages.SUCCESS, self.success_message) + messages.add_message(self.request, messages.SUCCESS, self.success_message) return super(IndexView, self).form_valid(form) @@ -411,7 +400,6 @@ class PaymentOrderView(FormView): # Create Billing Address billing_address = form.save() - request.session['billing_address_data'] = billing_address_data request.session['billing_address'] = billing_address.id request.session['token'] = token @@ -436,8 +424,7 @@ class OrderConfirmationView(DetailView): stripe_customer_id = request.session.get('customer') customer = StripeCustomer.objects.filter(id=stripe_customer_id).first() stripe_utils = StripeUtils() - card_details = stripe_utils.get_card_details( - customer.stripe_id, request.session.get('token')) + card_details = stripe_utils.get_card_details(customer.stripe_id, request.session.get('token')) context = { 'site_url': reverse('datacenterlight:index'), 'cc_last4': card_details.get('response_object').get('last4'), @@ -453,8 +440,7 @@ class OrderConfirmationView(DetailView): customer = StripeCustomer.objects.filter(id=stripe_customer_id).first() billing_address_data = request.session.get('billing_address_data') billing_address_id = request.session.get('billing_address') - billing_address = BillingAddress.objects.filter( - id=billing_address_id).first() + billing_address = BillingAddress.objects.filter(id=billing_address_id).first() vm_template_id = template.get('id', 1) final_price = specs.get('price') @@ -473,71 +459,8 @@ class OrderConfirmationView(DetailView): return render(request, self.payment_template_name, context) charge = charge_response.get('response_object') - - # Create OpenNebulaManager - manager = OpenNebulaManager(email=settings.OPENNEBULA_USERNAME, - password=settings.OPENNEBULA_PASSWORD) - - # Create a vm using oneadmin, also specify the name - vm_id = manager.create_vm( - template_id=vm_template_id, - specs=specs, - vm_name="{email}-{template_name}-{date}".format( - email=user.get('email'), - template_name=template.get('name'), - date=int(datetime.now().strftime("%s"))) - ) - - # Create a Hosting Order - order = HostingOrder.create( - price=final_price, - vm_id=vm_id, - customer=customer, - billing_address=billing_address - ) - - # Create a Hosting Bill - HostingBill.create( - customer=customer, billing_address=billing_address) - - # Create Billing Address for User if he does not have one - if not customer.user.billing_addresses.count(): - billing_address_data.update({ - 'user': customer.user.id - }) - billing_address_user_form = UserBillingAddressForm( - billing_address_data) - billing_address_user_form.is_valid() - billing_address_user_form.save() - - # Associate an order with a stripe payment - order.set_stripe_charge(charge) - - # If the Stripe payment was successed, set order status approved - order.set_approved() - - vm = VirtualMachineSerializer(manager.get_vm(vm_id)).data - - context = { - 'name': user.get('name'), - 'email': user.get('email'), - 'cores': specs.get('cpu'), - 'memory': specs.get('memory'), - 'storage': specs.get('disk_size'), - 'price': specs.get('price'), - 'template': template.get('name'), - 'vm.name': vm['name'], - 'vm.id': vm['vm_id'], - 'order.id': order.id - } - email_data = { - 'subject': settings.DCL_TEXT + " Order from %s" % context['email'], - 'from_email': settings.DCL_SUPPORT_FROM_ADDRESS, - 'to': ['info@ungleich.ch'], - 'body': "\n".join(["%s=%s" % (k, v) for (k, v) in context.items()]), - 'reply_to': [context['email']], - } - email = EmailMessage(**email_data) - email.send() + create_vm_task.delay(vm_template_id, user, specs, template, stripe_customer_id, billing_address_data, + billing_address_id, + charge) request.session['order_confirmation'] = True return HttpResponseRedirect(reverse('datacenterlight:order_success')) From bd2edf599ae4b8e02cc43167821d9bf3adac75e3 Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Sun, 6 Aug 2017 15:08:05 +0200 Subject: [PATCH 010/189] Merged views from master --- datacenterlight/views.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index b2798844..e37b2914 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -17,7 +17,6 @@ from utils.models import BillingAddress from hosting.models import HostingOrder from utils.stripe_utils import StripeUtils from membership.models import CustomUser, StripeCustomer - from opennebula_api.models import OpenNebulaManager from opennebula_api.serializers import VirtualMachineTemplateSerializer, VirtualMachineSerializer, VMTemplateSerializer from datacenterlight.tasks import create_vm_task @@ -195,15 +194,15 @@ class IndexView(CreateView): def validate_cores(self, value): if (value > 48) or (value < 1): - raise ValidationError(_('Not a proper cores number')) + raise ValidationError(_('Invalid number of cores')) def validate_memory(self, value): if (value > 200) or (value < 2): - raise ValidationError(_('Not a proper ram number')) + raise ValidationError(_('Invalid RAM size')) def validate_storage(self, value): if (value > 2000) or (value < 10): - raise ValidationError(_('Not a proper storage number')) + raise ValidationError(_('Invalid storage size')) @cache_control(no_cache=True, must_revalidate=True, no_store=True) def get(self, request, *args, **kwargs): From d6d0b9c8c0eed4394d3610ccade37cbc9395fefc Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Sun, 6 Aug 2017 15:08:46 +0200 Subject: [PATCH 011/189] Added default init code for celery --- dynamicweb/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dynamicweb/__init__.py b/dynamicweb/__init__.py index b64e43e8..de98902b 100644 --- a/dynamicweb/__init__.py +++ b/dynamicweb/__init__.py @@ -1,5 +1,7 @@ -from __future__ import absolute_import +from __future__ import absolute_import, unicode_literals # This will make sure the app is always imported when # Django starts so that shared_task will use this app. from .celery import app as celery_app + +__all__ = ['celery_app'] \ No newline at end of file From 477c8e81a40aeef502764082c5c22b02c77e512a Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Sun, 6 Aug 2017 15:10:26 +0200 Subject: [PATCH 012/189] Added default celery.py code --- dynamicweb/celery.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dynamicweb/celery.py b/dynamicweb/celery.py index 749ffdef..28f6af3b 100644 --- a/dynamicweb/celery.py +++ b/dynamicweb/celery.py @@ -14,7 +14,7 @@ app = Celery('dynamicweb') app.config_from_object('django.conf:settings', namespace='CELERY') # Load task modules from all registered Django app configs. -app.autodiscover_tasks(lambda: settings.INSTALLED_APPS) +app.autodiscover_tasks() @app.task(bind=True) From 68505e73a8c834f3728e633c38dbf89e7b70de96 Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Sun, 6 Aug 2017 16:32:27 +0200 Subject: [PATCH 013/189] Added django-celery-results and setup redis as backend for celery tasks --- dynamicweb/settings/base.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/dynamicweb/settings/base.py b/dynamicweb/settings/base.py index f4c4b68d..4847851f 100644 --- a/dynamicweb/settings/base.py +++ b/dynamicweb/settings/base.py @@ -120,7 +120,8 @@ INSTALLED_APPS = ( 'datacenterlight.templatetags', 'alplora', 'rest_framework', - 'opennebula_api' + 'opennebula_api', + 'django_celery_results', ) MIDDLEWARE_CLASSES = ( @@ -522,9 +523,10 @@ GOOGLE_ANALYTICS_PROPERTY_IDS = { } # CELERY Settings -#BROKER_URL = 'redis://localhost:6379' -BROKER_URL = 'redis+socket:///var/run/redis/redis.sock' -CELERY_RESULT_BACKEND = 'redis://localhost:6379' +# BROKER_URL = 'redis://localhost:6379' +CELERY_BROKER_URL = 'redis+socket:///tmp/redis.sock' +# CELERY_RESULT_BACKEND = 'redis://localhost:6379' +CELERY_RESULT_BACKEND = 'redis+socket:///tmp/redis.sock' CELERY_ACCEPT_CONTENT = ['application/json'] CELERY_TASK_SERIALIZER = 'json' CELERY_RESULT_SERIALIZER = 'json' From 55177646b456be63805604eb5d2188d0c9bd5d4f Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Sun, 6 Aug 2017 16:33:37 +0200 Subject: [PATCH 014/189] Reformatted code --- hosting/models.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/hosting/models.py b/hosting/models.py index 88386913..8cdc6114 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -1,13 +1,9 @@ import os import logging - from django.db import models from django.utils.functional import cached_property - - from Crypto.PublicKey import RSA - from membership.models import StripeCustomer, CustomUser from utils.models import BillingAddress from utils.mixins import AssignPermissionsMixin @@ -42,7 +38,6 @@ class HostingPlan(models.Model): class HostingOrder(AssignPermissionsMixin, models.Model): - ORDER_APPROVED_STATUS = 'Approved' ORDER_DECLINED_STATUS = 'Declined' @@ -101,7 +96,7 @@ class HostingOrder(AssignPermissionsMixin, models.Model): class UserHostingKey(models.Model): user = models.ForeignKey(CustomUser) public_key = models.TextField() - private_key = models.FileField(upload_to='private_keys', blank=True) + private_key = models.FileField(upload_to='private_keys', blank=True) created_at = models.DateTimeField(auto_now_add=True) name = models.CharField(max_length=100) From 6d28783f9ffd526aa076289379ffdca927c693b1 Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Sun, 6 Aug 2017 16:34:23 +0200 Subject: [PATCH 015/189] Added django-celery-results to requirements --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 2520d617..abce2271 100644 --- a/requirements.txt +++ b/requirements.txt @@ -88,3 +88,4 @@ flake8==3.3.0 python-memcached==1.58 celery==4.0.2 redis==2.10.5 +django-celery-results==1.0.1 From 5a5654be018239ae192864316ceb5a4e4c54be99 Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Sun, 6 Aug 2017 16:51:07 +0200 Subject: [PATCH 016/189] Moved CELERY_BROKER_URL and CELERY_RESULT_BACKEND to .env file --- dynamicweb/settings/base.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/dynamicweb/settings/base.py b/dynamicweb/settings/base.py index 4847851f..fbe0c9ca 100644 --- a/dynamicweb/settings/base.py +++ b/dynamicweb/settings/base.py @@ -523,10 +523,8 @@ GOOGLE_ANALYTICS_PROPERTY_IDS = { } # CELERY Settings -# BROKER_URL = 'redis://localhost:6379' -CELERY_BROKER_URL = 'redis+socket:///tmp/redis.sock' -# CELERY_RESULT_BACKEND = 'redis://localhost:6379' -CELERY_RESULT_BACKEND = 'redis+socket:///tmp/redis.sock' +CELERY_BROKER_URL = env('CELERY_BROKER_URL') +CELERY_RESULT_BACKEND = env('CELERY_RESULT_BACKEND') CELERY_ACCEPT_CONTENT = ['application/json'] CELERY_TASK_SERIALIZER = 'json' CELERY_RESULT_SERIALIZER = 'json' From 26b4feb1d16296036ccb3fe75f11f96501f3e964 Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Sun, 6 Aug 2017 18:39:45 +0200 Subject: [PATCH 017/189] Introduced int_env and CELERY_MAX_RETRIES env variable --- dynamicweb/settings/base.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/dynamicweb/settings/base.py b/dynamicweb/settings/base.py index fbe0c9ca..58609234 100644 --- a/dynamicweb/settings/base.py +++ b/dynamicweb/settings/base.py @@ -10,6 +10,9 @@ from django.utils.translation import ugettext_lazy as _ # dotenv import dotenv +import logging + +logger = logging.getLogger(__name__) def gettext(s): @@ -25,6 +28,20 @@ def bool_env(val): return True if os.environ.get(val, False) == 'True' else False +def int_env(val, default_value=0): + """Replaces string based environment values with Python integers + Return default_value if val is not set or cannot be parsed, otherwise + returns the python integer equal to the passed val + """ + return_value = default_value + try: + return_value = int(os.environ.get(val)) + except Exception as e: + logger.error(str(e)) + + return return_value + + BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) PROJECT_DIR = os.path.abspath( @@ -529,6 +546,7 @@ CELERY_ACCEPT_CONTENT = ['application/json'] CELERY_TASK_SERIALIZER = 'json' CELERY_RESULT_SERIALIZER = 'json' CELERY_TIMEZONE = 'Europe/Zurich' +CELERY_MAX_RETRIES = int_env('CELERY_MAX_RETRIES', 5) ENABLE_DEBUG_LOGGING = bool_env('ENABLE_DEBUG_LOGGING') From f950c1defb23a0cba62bbbbb732a5428f2f416a5 Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Sun, 6 Aug 2017 18:42:27 +0200 Subject: [PATCH 018/189] Added datacenterlight/tasks.py --- datacenterlight/tasks.py | 162 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 datacenterlight/tasks.py diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py new file mode 100644 index 00000000..69ba08ad --- /dev/null +++ b/datacenterlight/tasks.py @@ -0,0 +1,162 @@ +from __future__ import absolute_import, unicode_literals +from dynamicweb.celery import app +from celery.utils.log import get_task_logger +from django.conf import settings +from opennebula_api.models import OpenNebulaManager +from opennebula_api.serializers import VirtualMachineSerializer +from hosting.models import HostingOrder, HostingBill +from utils.forms import UserBillingAddressForm +from datetime import datetime +from membership.models import StripeCustomer +from django.core.mail import EmailMessage +from utils.models import BillingAddress + +logger = get_task_logger(__name__) + + +def retry_task(task, exception=None): + """Retries the specified task using a "backing off countdown", + meaning that the interval between retries grows exponentially + with every retry. + + Arguments: + task: + The task to retry. + + exception: + Optionally, the exception that caused the retry. + """ + + def backoff(attempts): + return 2 ** attempts + + kwargs = { + 'countdown': backoff(task.request.retries), + } + + if exception: + kwargs['exc'] = exception + + if task.request.retries > settings.CELERY_MAX_RETRIES: + msg_text = 'Finished {} retries for create_vm_task'.format(task.request.retries) + logger.log(msg_text) + # Try sending email and stop + email_data = { + 'subject': '{} CELERY TASK ERROR: {}'.format(settings.DCL_TEXT, msg_text), + 'from_email': settings.DCL_SUPPORT_FROM_ADDRESS, + 'to': ['info@ungleich.ch'], + 'body': "\n".join(["%s=%s" % (k, v) for (k, v) in kwargs.items()]), + } + email = EmailMessage(**email_data) + email.send() + return + else: + raise task.retry(**kwargs) + + +@app.task(bind=True) +def create_vm_task(self, vm_template_id, user, specs, template, stripe_customer_id, billing_address_data, + billing_address_id, + charge): + try: + final_price = specs.get('price') + billing_address = BillingAddress.objects.filter(id=billing_address_id).first() + customer = StripeCustomer.objects.filter(id=stripe_customer_id).first() + # Create OpenNebulaManager + manager = OpenNebulaManager(email=settings.OPENNEBULA_USERNAME, + password=settings.OPENNEBULA_PASSWORD) + + # Create a vm using oneadmin, also specify the name + vm_id = manager.create_vm( + template_id=vm_template_id, + specs=specs, + vm_name="{email}-{template_name}-{date}".format( + email=user.get('email'), + template_name=template.get('name'), + date=int(datetime.now().strftime("%s"))) + ) + + # Create a Hosting Order + order = HostingOrder.create( + price=final_price, + vm_id=vm_id, + customer=customer, + billing_address=billing_address + ) + + # Create a Hosting Bill + HostingBill.create( + customer=customer, billing_address=billing_address) + + # Create Billing Address for User if he does not have one + if not customer.user.billing_addresses.count(): + billing_address_data.update({ + 'user': customer.user.id + }) + billing_address_user_form = UserBillingAddressForm( + billing_address_data) + billing_address_user_form.is_valid() + billing_address_user_form.save() + + # Associate an order with a stripe payment + charge_object = DictDotLookup(charge) + order.set_stripe_charge(charge_object) + + # If the Stripe payment succeeds, set order status approved + order.set_approved() + + vm = VirtualMachineSerializer(manager.get_vm(vm_id)).data + + context = { + 'name': user.get('name'), + 'email': user.get('email'), + 'cores': specs.get('cpu'), + 'memory': specs.get('memory'), + 'storage': specs.get('disk_size'), + 'price': specs.get('price'), + 'template': template.get('name'), + 'vm.name': vm['name'], + 'vm.id': vm['vm_id'], + 'order.id': order.id + } + email_data = { + 'subject': settings.DCL_TEXT + " Order from %s" % context['email'], + 'from_email': settings.DCL_SUPPORT_FROM_ADDRESS, + 'to': ['info@ungleich.ch'], + 'body': "\n".join(["%s=%s" % (k, v) for (k, v) in context.items()]), + 'reply_to': [context['email']], + } + email = EmailMessage(**email_data) + email.send() + except Exception as e: + logger.error(str(e)) + retry_task(self) + + +class DictDotLookup(object): + """ + Creates objects that behave much like a dictionaries, but allow nested + key access using object '.' (dot) lookups. + """ + + def __init__(self, d): + for k in d: + if isinstance(d[k], dict): + self.__dict__[k] = DictDotLookup(d[k]) + elif isinstance(d[k], (list, tuple)): + l = [] + for v in d[k]: + if isinstance(v, dict): + l.append(DictDotLookup(v)) + else: + l.append(v) + self.__dict__[k] = l + else: + self.__dict__[k] = d[k] + + def __getitem__(self, name): + if name in self.__dict__: + return self.__dict__[name] + + def __iter__(self): + return iter(self.__dict__.keys()) From e7864f5d85788a44abab26ded38e5ec1808fc7a8 Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Sun, 6 Aug 2017 18:52:23 +0200 Subject: [PATCH 019/189] Added condition to check if create_vm succeeded --- datacenterlight/tasks.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index 69ba08ad..9f0249a2 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -58,6 +58,7 @@ def retry_task(task, exception=None): def create_vm_task(self, vm_template_id, user, specs, template, stripe_customer_id, billing_address_data, billing_address_id, charge): + vm_id = None try: final_price = specs.get('price') billing_address = BillingAddress.objects.filter(id=billing_address_id).first() @@ -76,6 +77,9 @@ def create_vm_task(self, vm_template_id, user, specs, template, stripe_customer_ date=int(datetime.now().strftime("%s"))) ) + if vm_id is None: + raise Exception("Could not create VM") + # Create a Hosting Order order = HostingOrder.create( price=final_price, @@ -132,6 +136,8 @@ def create_vm_task(self, vm_template_id, user, specs, template, stripe_customer_ logger.error(str(e)) retry_task(self) + return vm_id + class DictDotLookup(object): """ From 58bb75481491589d9024c399e448f3ae8336683d Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Sun, 6 Aug 2017 21:54:12 +0200 Subject: [PATCH 020/189] Introduced MaxRetries and MaxRetriesExceededError handling code --- datacenterlight/tasks.py | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index 9f0249a2..342c87f4 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -10,6 +10,7 @@ from datetime import datetime from membership.models import StripeCustomer from django.core.mail import EmailMessage from utils.models import BillingAddress +from celery.exceptions import MaxRetriesExceededError logger = get_task_logger(__name__) @@ -37,24 +38,10 @@ def retry_task(task, exception=None): if exception: kwargs['exc'] = exception - if task.request.retries > settings.CELERY_MAX_RETRIES: - msg_text = 'Finished {} retries for create_vm_task'.format(task.request.retries) - logger.log(msg_text) - # Try sending email and stop - email_data = { - 'subject': '{} CELERY TASK ERROR: {}'.format(settings.DCL_TEXT, msg_text), - 'from_email': settings.DCL_SUPPORT_FROM_ADDRESS, - 'to': ['info@ungleich.ch'], - 'body': "\n".join(["%s=%s" % (k, v) for (k, v) in kwargs.items()]), - } - email = EmailMessage(**email_data) - email.send() - return - else: - raise task.retry(**kwargs) + raise task.retry(**kwargs) -@app.task(bind=True) +@app.task(bind=True, max_retries=settings.CELERY_MAX_RETRIES) def create_vm_task(self, vm_template_id, user, specs, template, stripe_customer_id, billing_address_data, billing_address_id, charge): @@ -134,7 +121,21 @@ def create_vm_task(self, vm_template_id, user, specs, template, stripe_customer_ email.send() except Exception as e: logger.error(str(e)) - retry_task(self) + try: + retry_task(self) + except MaxRetriesExceededError: + msg_text = 'Finished {} retries for create_vm_task'.format(self.request.retries) + logger.error(msg_text) + # Try sending email and stop + email_data = { + 'subject': '{} CELERY TASK ERROR: {}'.format(settings.DCL_TEXT, msg_text), + 'from_email': settings.DCL_SUPPORT_FROM_ADDRESS, + 'to': ['info@ungleich.ch'], + 'body':',\n'.join(str(i) for i in self.request.args) + } + email = EmailMessage(**email_data) + email.send() + return return vm_id From 29b28d551c2f3caf20929f6eb5f57187ed81c9f9 Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Sun, 6 Aug 2017 22:19:27 +0200 Subject: [PATCH 021/189] Fixed flake8 lint warnings --- datacenterlight/tasks.py | 2 +- datacenterlight/views.py | 4 +--- dynamicweb/__init__.py | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index 342c87f4..dc71fcee 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -131,7 +131,7 @@ def create_vm_task(self, vm_template_id, user, specs, template, stripe_customer_ 'subject': '{} CELERY TASK ERROR: {}'.format(settings.DCL_TEXT, msg_text), 'from_email': settings.DCL_SUPPORT_FROM_ADDRESS, 'to': ['info@ungleich.ch'], - 'body':',\n'.join(str(i) for i in self.request.args) + 'body': ',\n'.join(str(i) for i in self.request.args) } email = EmailMessage(**email_data) email.send() diff --git a/datacenterlight/views.py b/datacenterlight/views.py index e37b2914..2bae5c77 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -13,12 +13,11 @@ from django.views.decorators.cache import cache_control from django.conf import settings from django.utils.translation import ugettext_lazy as _ from utils.forms import BillingAddressForm -from utils.models import BillingAddress from hosting.models import HostingOrder from utils.stripe_utils import StripeUtils from membership.models import CustomUser, StripeCustomer from opennebula_api.models import OpenNebulaManager -from opennebula_api.serializers import VirtualMachineTemplateSerializer, VirtualMachineSerializer, VMTemplateSerializer +from opennebula_api.serializers import VirtualMachineTemplateSerializer, VMTemplateSerializer from datacenterlight.tasks import create_vm_task @@ -439,7 +438,6 @@ class OrderConfirmationView(DetailView): customer = StripeCustomer.objects.filter(id=stripe_customer_id).first() billing_address_data = request.session.get('billing_address_data') billing_address_id = request.session.get('billing_address') - billing_address = BillingAddress.objects.filter(id=billing_address_id).first() vm_template_id = template.get('id', 1) final_price = specs.get('price') diff --git a/dynamicweb/__init__.py b/dynamicweb/__init__.py index de98902b..3b91b070 100644 --- a/dynamicweb/__init__.py +++ b/dynamicweb/__init__.py @@ -4,4 +4,4 @@ from __future__ import absolute_import, unicode_literals # Django starts so that shared_task will use this app. from .celery import app as celery_app -__all__ = ['celery_app'] \ No newline at end of file +__all__ = ['celery_app'] From 4de04b2663c47d5e367e84570faf39288e49ae5e Mon Sep 17 00:00:00 2001 From: Siarhei Puhach Date: Mon, 7 Aug 2017 09:56:57 +0300 Subject: [PATCH 022/189] Changed visa card error placement --- hosting/templates/hosting/payment.html | 6 ++++-- utils/stripe_utils.py | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/hosting/templates/hosting/payment.html b/hosting/templates/hosting/payment.html index 41c6d3eb..449962a5 100644 --- a/hosting/templates/hosting/payment.html +++ b/hosting/templates/hosting/payment.html @@ -59,7 +59,6 @@ {% csrf_token %} {% bootstrap_field field show_label=False type='fields'%} {% endfor %} - {% bootstrap_form_errors form type='non_fields'%}
@@ -130,7 +129,7 @@
- {% if not messages %} + {% if not messages and not form.errors %}

{% blocktrans %} You are not making any payment yet. After submitting your card @@ -146,6 +145,9 @@ {% endif %} {% endfor %} + {% if form.errors %} + {% bootstrap_form_errors form type='non_fields'%} + {% endif %}

diff --git a/utils/stripe_utils.py b/utils/stripe_utils.py index d46cf54d..f7d52157 100644 --- a/utils/stripe_utils.py +++ b/utils/stripe_utils.py @@ -11,7 +11,7 @@ def handleStripeError(f): 'error': None } - common_message = "Currently its not possible to make payments." + common_message = "Currently it's not possible to make payments." try: response_object = f(*args, **kwargs) response = { From 4fea099b5d8cba24e6a39a35a3a38961e4db9a5b Mon Sep 17 00:00:00 2001 From: Siarhei Puhach Date: Mon, 7 Aug 2017 10:05:12 +0300 Subject: [PATCH 023/189] Changed invalid credit card error style --- hosting/templates/hosting/payment.html | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/hosting/templates/hosting/payment.html b/hosting/templates/hosting/payment.html index 449962a5..cb7a4d8c 100644 --- a/hosting/templates/hosting/payment.html +++ b/hosting/templates/hosting/payment.html @@ -145,8 +145,13 @@ {% endif %} {% endfor %} + {% if form.errors %} - {% bootstrap_form_errors form type='non_fields'%} + {% for error in form.non_field_errors %} +

+ {{ error|escape }} +

+ {% endfor %} {% endif %}
From 4cd3d6a4aada182b3d88ec7a6f6e2743278a7754 Mon Sep 17 00:00:00 2001 From: Siarhei Puhach Date: Mon, 7 Aug 2017 13:36:21 +0300 Subject: [PATCH 024/189] Added stripe make_charge error handler --- datacenterlight/views.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index dcf64814..d82522e9 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -467,15 +467,12 @@ class OrderConfirmationView(DetailView): stripe_utils = StripeUtils() charge_response = stripe_utils.make_charge(amount=final_price, customer=customer.stripe_id) - charge = charge_response.get('response_object') # Check if the payment was approved - if not charge: - context = {} - context.update({ - 'paymentError': charge_response.get('error') - }) - return render(request, self.payment_template_name, context) + if not charge_response.get('response_object') and not charge_response.get('paid'): + msg = charge_response.get('error') + messages.add_message(self.request, messages.ERROR, msg, extra_tags='make_charge_error') + return HttpResponseRedirect(reverse('datacenterlight:payment')) charge = charge_response.get('response_object') From a71ccbc56615d2f8370d912ae8e4702b261144ed Mon Sep 17 00:00:00 2001 From: Siarhei Puhach Date: Mon, 7 Aug 2017 17:02:47 +0300 Subject: [PATCH 025/189] Added error hash in url, added payment error handler to hosting/views --- datacenterlight/views.py | 4 +- hosting/templates/hosting/payment.html | 57 ++++++++++++++------------ hosting/views.py | 11 ++--- 3 files changed, 37 insertions(+), 35 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index d82522e9..b20b212f 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -441,7 +441,7 @@ class OrderConfirmationView(DetailView): if not card_details.get('response_object') and not card_details.get('paid'): msg = card_details.get('error') messages.add_message(self.request, messages.ERROR, msg, extra_tags='failed_payment') - return HttpResponseRedirect(reverse('datacenterlight:payment')) + return HttpResponseRedirect(reverse('datacenterlight:payment') + '#dcl_payment_error') context = { 'site_url': reverse('datacenterlight:index'), @@ -472,7 +472,7 @@ class OrderConfirmationView(DetailView): if not charge_response.get('response_object') and not charge_response.get('paid'): msg = charge_response.get('error') messages.add_message(self.request, messages.ERROR, msg, extra_tags='make_charge_error') - return HttpResponseRedirect(reverse('datacenterlight:payment')) + return HttpResponseRedirect(reverse('datacenterlight:payment') + '#dcl_payment_error') charge = charge_response.get('response_object') diff --git a/hosting/templates/hosting/payment.html b/hosting/templates/hosting/payment.html index cb7a4d8c..4c4ffa62 100644 --- a/hosting/templates/hosting/payment.html +++ b/hosting/templates/hosting/payment.html @@ -85,13 +85,29 @@
-

- {% blocktrans %} - You are not making any payment yet. After submitting your card - information, you will be taken to the Confirm Order Page. - {% endblocktrans %} -

-
+ {% if not messages and not form.non_field_errors %} +

+ {% blocktrans %} + You are not making any payment yet. After submitting your card + information, you will be taken to the Confirm Order Page. + {% endblocktrans %} +

+ {% endif %} +
+ {% for message in messages %} + {% if 'failed_payment' or 'make_charge_error' in message.tags %} +
  • +

    {{ message|safe }}

    +
+ {% endif %} + {% endfor %} + {% for error in form.non_field_errors %} +

+ {{ error|escape }} +

+ {% endfor %} +
+
- {% if not messages and not form.errors %} + {% if not messages and not form.non_field_errors %}

{% blocktrans %} You are not making any payment yet. After submitting your card @@ -137,22 +153,20 @@ {% endblocktrans %}

{% endif %} -
+
{% for message in messages %} - {% if 'failed_payment' in message.tags %} + {% if 'failed_payment' or 'make_charge_error' in message.tags %}
  • {{ message|safe }}

{% endif %} {% endfor %} - {% if form.errors %} - {% for error in form.non_field_errors %} -

- {{ error|escape }} -

- {% endfor %} - {% endif %} + {% for error in form.non_field_errors %} +

+ {{ error|escape }} +

+ {% endfor %}
@@ -168,15 +182,6 @@

- {% if paymentError %} -
-
-

- {% bootstrap_alert paymentError alert_type='danger' %} -

-
-
- {% endif %} {% endif %} diff --git a/hosting/views.py b/hosting/views.py index 19ec5b2a..d1666b04 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -557,15 +557,12 @@ class PaymentVMView(LoginRequiredMixin, FormView): stripe_utils = StripeUtils() charge_response = stripe_utils.make_charge(amount=final_price, customer=customer.stripe_id) - charge = charge_response.get('response_object') # Check if the payment was approved - if not charge: - context.update({ - 'paymentError': charge_response.get('error'), - 'form': form - }) - return render(request, self.template_name, context) + if not charge_response.get('response_object') and not charge_response.get('paid'): + msg = charge_response.get('error') + messages.add_message(self.request, messages.ERROR, msg, extra_tags='make_charge_error') + return HttpResponseRedirect(reverse('hosting:payment') + '#hosting_payment_error') charge = charge_response.get('response_object') From 72ddfd96ab0fad277a1471ba3c723c6c5ff4fc45 Mon Sep 17 00:00:00 2001 From: Siarhei Puhach Date: Mon, 7 Aug 2017 17:23:58 +0300 Subject: [PATCH 026/189] Payment error was unified --- datacenterlight/views.py | 4 ++-- hosting/templates/hosting/payment.html | 4 ++-- hosting/views.py | 7 ++++--- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index b20b212f..929b0833 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -441,7 +441,7 @@ class OrderConfirmationView(DetailView): if not card_details.get('response_object') and not card_details.get('paid'): msg = card_details.get('error') messages.add_message(self.request, messages.ERROR, msg, extra_tags='failed_payment') - return HttpResponseRedirect(reverse('datacenterlight:payment') + '#dcl_payment_error') + return HttpResponseRedirect(reverse('datacenterlight:payment') + '#payment_error') context = { 'site_url': reverse('datacenterlight:index'), @@ -472,7 +472,7 @@ class OrderConfirmationView(DetailView): if not charge_response.get('response_object') and not charge_response.get('paid'): msg = charge_response.get('error') messages.add_message(self.request, messages.ERROR, msg, extra_tags='make_charge_error') - return HttpResponseRedirect(reverse('datacenterlight:payment') + '#dcl_payment_error') + return HttpResponseRedirect(reverse('datacenterlight:payment') + '#payment_error') charge = charge_response.get('response_object') diff --git a/hosting/templates/hosting/payment.html b/hosting/templates/hosting/payment.html index 4c4ffa62..7bf84645 100644 --- a/hosting/templates/hosting/payment.html +++ b/hosting/templates/hosting/payment.html @@ -93,7 +93,7 @@ {% endblocktrans %}

{% endif %} -
+
{% for message in messages %} {% if 'failed_payment' or 'make_charge_error' in message.tags %}
  • @@ -153,7 +153,7 @@ {% endblocktrans %}

    {% endif %} -
    +
    {% for message in messages %} {% if 'failed_payment' or 'make_charge_error' in message.tags %}
    • diff --git a/hosting/views.py b/hosting/views.py index d1666b04..c5877ca2 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -547,8 +547,9 @@ class PaymentVMView(LoginRequiredMixin, FormView): customer = StripeCustomer.get_or_create(email=owner.email, token=token) if not customer: - form.add_error("__all__", "Invalid credit card") - return self.render_to_response(self.get_context_data(form=form)) + msg = _("Invalid credit card") + messages.add_message(self.request, messages.ERROR, msg, extra_tags='make_charge_error') + return HttpResponseRedirect(reverse('hosting:payment') + '#payment_error') # Create Billing Address billing_address = form.save() @@ -562,7 +563,7 @@ class PaymentVMView(LoginRequiredMixin, FormView): if not charge_response.get('response_object') and not charge_response.get('paid'): msg = charge_response.get('error') messages.add_message(self.request, messages.ERROR, msg, extra_tags='make_charge_error') - return HttpResponseRedirect(reverse('hosting:payment') + '#hosting_payment_error') + return HttpResponseRedirect(reverse('hosting:payment') + '#payment_error') charge = charge_response.get('response_object') From 6c4e0067ba8269f7606fdfa35cb46e36b4e9e5df Mon Sep 17 00:00:00 2001 From: ARvind Tiwari Date: Tue, 8 Aug 2017 00:35:58 +0530 Subject: [PATCH 027/189] font-family changed to 'Lato' in all css files --- .../datacenterlight/css/landing-page.css | 52 +++++++++++------- hosting/static/hosting/css/commons.css | 3 +- hosting/static/hosting/css/landing-page.css | 19 ++++--- hosting/static/hosting/css/pricing.css | 2 +- hosting/static/hosting/css/user_keys.css | 53 ++++++++++--------- 5 files changed, 77 insertions(+), 52 deletions(-) diff --git a/datacenterlight/static/datacenterlight/css/landing-page.css b/datacenterlight/static/datacenterlight/css/landing-page.css index 6c813661..c693247c 100755 --- a/datacenterlight/static/datacenterlight/css/landing-page.css +++ b/datacenterlight/static/datacenterlight/css/landing-page.css @@ -4,10 +4,10 @@ * For details, see http://www.apache.org/licenses/LICENSE-2.0. */ -@font-face { +/*@font-face { font-family: 'Lato-Light'; src: url('../fonts/Lato/Lato-Light.ttf'); -} +}*/ body, html { @@ -22,7 +22,12 @@ h3, h4, h5, h6 { - font-family: 'Lato-Light', sans-serif; + /*font-family: 'Lato-Light', sans-serif;*/ + font-family: 'Lato', sans-serif; + font-weight: 300; +} + +button, input, optgroup, select, textarea { font-weight: 300; } @@ -143,13 +148,15 @@ h6 { .navbar-default .navbar-nav>li>a { cursor: pointer; - font-family: 'Lato-Light', sans-serif; + /*font-family: 'Lato-Light', sans-serif;*/ + font-weight: 300; } .navbar-transparent .navbar-nav>li>a { color: #fff; cursor: pointer; - font-family: 'Lato-Light', sans-serif; + /*font-family: 'Lato-Light', sans-serif;*/ + font-weight: 300; } .navbar-transparent .navbar-nav>li>a:hover { @@ -202,13 +209,15 @@ h6 { .navbar-transparent .nav-language .select-language { color: #fff; - font-family: 'Lato-Light', sans-serif; + /*font-family: 'Lato-Light', sans-serif;*/ + font-weight: 300; } .nav-language .select-language span { margin-left: 5px; margin-right: 5px; - font-family: 'Lato', sans-serif; + /*font-family: 'Lato', sans-serif;*/ + font-weight: normal; } .nav-language .drop-language{ /*position: absolute;*/ @@ -237,7 +246,8 @@ h6 { .nav-language .drop-language a{ cursor: pointer; padding: 5px 10px !important; - font-family: 'Lato-Light', sans-serif; + /*font-family: 'Lato-Light', sans-serif;*/ + font-weight: 300; } /* Show the dropdown menu on hover */ @@ -260,7 +270,8 @@ h6 { .navbar-transparent .nav-language .drop-language a { color: #fff; padding: 5px 10px !important; - font-family: 'Lato-Light', sans-serif; + /*font-family: 'Lato-Light', sans-serif;*/ + font-weight: 300; } /* .nav-language:hover .drop-language{ display: block; @@ -343,7 +354,7 @@ h6 { .intro-message>h1 { margin: 0; - font-weight: 400; + font-weight: 300; font-size: 6em; } @@ -792,7 +803,8 @@ tech-sub-sec h2 { } .percent-text { - font-family: 'Lato', sans-serif; + /*font-family: 'Lato', sans-serif;*/ +/* font-weight: normal; */ font-size: 50px; color: #999; } @@ -879,7 +891,7 @@ tech-sub-sec h2 { .dropdown-menu>li>a { font-size: 13px; font-weight: 300; - font-family: 'Lato-Light', sans-serif; + /*font-family: 'Lato-Light', sans-serif;*/ } .navbar-default .navbar-nav>.active>a, @@ -898,7 +910,8 @@ tech-sub-sec h2 { background: -webkit-linear-gradient(top, #f0f4f7, #fff) no-repeat; background: linear-gradient(to bottom, #f0f4f7, #fff) no-repeat; display: flex; - font-family: 'Lato', sans-serif; + /*font-family: 'Lato', sans-serif;*/ +/* font-weight: normal; */ } .price-calc-section .text { @@ -963,7 +976,8 @@ tech-sub-sec h2 { } .price-calc-section .card .title h3 { - font-family: 'Lato', sans-serif; + /*font-family: 'Lato', sans-serif;*/ + font-weight: normal; } .price-calc-section .card .price { @@ -1050,8 +1064,9 @@ tech-sub-sec h2 { .price-calc-section .card .description.input label { font-size: 15px; - font-weight: 800; - font-family: 'Lato'; + font-weight: 700; + /*font-weight: 800;*/ + /*font-family: 'Lato';*/ margin-bottom: 0; width: 40px; } @@ -1364,7 +1379,8 @@ tech-sub-sec h2 { padding: 30px; } .percent-text { - font-family: 'Lato'; + /*font-family: 'Lato';*/ + font-weight: normal; font-size: 37px; /* text-align: center; */ } @@ -1402,7 +1418,7 @@ tech-sub-sec h2 { .network-name { text-transform: uppercase; font-size: 14px; - font-weight: 400; + font-weight: 300; letter-spacing: 2px; } diff --git a/hosting/static/hosting/css/commons.css b/hosting/static/hosting/css/commons.css index 79053e4e..2dd5637a 100644 --- a/hosting/static/hosting/css/commons.css +++ b/hosting/static/hosting/css/commons.css @@ -115,7 +115,8 @@ line-height: 1.42857143; font-size: 25px; padding: 0; - font-family: 'Lato', sans-serif; + /*font-family: 'Lato', sans-serif;*/ + font-weight: normal; } .modal-text { padding-top: 15px; diff --git a/hosting/static/hosting/css/landing-page.css b/hosting/static/hosting/css/landing-page.css index d17ef8f2..ee9fea36 100644 --- a/hosting/static/hosting/css/landing-page.css +++ b/hosting/static/hosting/css/landing-page.css @@ -4,7 +4,7 @@ * For details, see http://www.apache.org/licenses/LICENSE-2.0. */ -@font-face { +/*@font-face { font-family: 'Lato-Regular'; src: url('../fonts/Lato/Lato-Regular.ttf'); } @@ -16,7 +16,7 @@ @font-face { font-family: 'Lato-Light'; src: url('../fonts/Lato/Lato-Light.ttf'); -} +}*/ body, html { @@ -31,7 +31,8 @@ h3, h4, h5, h6 { - font-family: 'Lato-Regular', sans-serif; + /*font-family: 'Lato-Regular', sans-serif;*/ + font-family: 'Lato', sans-serif; font-weight: 300; } @@ -53,7 +54,8 @@ h6 { .navbar-transparent .navbar-nav>li>a { color: #fff; cursor: pointer; - font-family: 'Lato-Regular', sans-serif; + /*font-family: 'Lato-Regular', sans-serif;*/ + font-weight: normal; } .navbar-transparent .navbar-nav>li>a:hover { color: #fff; @@ -376,7 +378,8 @@ h6 { text-align: center; font-size: 18px; line-height: 30px; - font-family: 'Lato' !important; + /*font-family: 'Lato' !important;*/ + font-weight: normal !important; } .sign-up-message a { @@ -488,7 +491,8 @@ a.unlink:hover { /***** DCL payment page **********/ .dcl-order-container { - font-family: Lato; + /*font-family: Lato;*/ + font-weight: normal; } .dcl-order-table-header { @@ -547,7 +551,8 @@ a.unlink:hover { } .card-warning-content { - font-family: Lato; + /*font-family: Lato;*/ + font-weight: normal; border: 1px solid #a1a1a1; border-radius: 3px; padding: 5px; diff --git a/hosting/static/hosting/css/pricing.css b/hosting/static/hosting/css/pricing.css index 8eba7ffa..3c952209 100644 --- a/hosting/static/hosting/css/pricing.css +++ b/hosting/static/hosting/css/pricing.css @@ -9,7 +9,7 @@ font-size: 14px; padding-left: 0; margin-bottom: 30px; - font-family: 'Lato'; + font-family: Lato, sans-serif; } diff --git a/hosting/static/hosting/css/user_keys.css b/hosting/static/hosting/css/user_keys.css index 77a12155..a3d4526a 100644 --- a/hosting/static/hosting/css/user_keys.css +++ b/hosting/static/hosting/css/user_keys.css @@ -1,6 +1,6 @@ /* ssh_keys_choice */ .h1-thin { - font-family: Lato, sans-serif; + /*font-family: Lato, sans-serif;*/ font-weight: 300; font-size: 32px; } @@ -10,12 +10,12 @@ } .dashboard-choice-container .page-header p { font-size: 16px; - font-family: Lato, sans-serif; + /*font-family: Lato, sans-serif;*/ font-weight: 300; } .dashboard-choice-container h2 { - font-family: Lato, sans-serif; - font-weight: 400; + /*font-family: Lato, sans-serif; + font-weight: 400;*/ font-size: 22px; margin-top: 0; } @@ -26,7 +26,7 @@ } .choice-container p{ font-size: 18px; - font-family: Lato, sans-serif; + /*font-family: Lato, sans-serif;*/ font-weight: 300; } .choice-container-top { @@ -119,7 +119,7 @@ color: #717274; font-size: 16px; font-weight: 300; - font-family: 'Lato'; + /*font-family: 'Lato';*/ } .borderless tbody:before { @@ -195,7 +195,8 @@ border-bottom: 1px solid grey; box-shadow: none; border-radius: 0; - font-family: 'Lato-Light', sans-serif; + /*font-family: 'Lato-Light', sans-serif;*/ + font-weight: 300; font-size: 20px; padding-left: 0; } @@ -203,57 +204,58 @@ .form_key_name::-webkit-input-placeholder{ font-size: 20px; font-weight:100; - font-family: 'Lato-Light', sans-serif; + /*font-family: 'Lato-Light', sans-serif;*/ + font-weight: 300; } .form_key_name::-moz-input-placeholder{ font-size: 20px; - font-weight:200; - font-family: 'Lato-Light', sans-serif; + /*font-family: 'Lato-Light', sans-serif;*/ + font-weight: 300; } .form_key_name:-moz-input-placeholder{ - font-family: 'Lato-Light', sans-serif; + /*font-family: 'Lato-Light', sans-serif;*/ + font-weight: 300; font-size: 20px; - font-weight:200; } .form_key_name:-ms-input-placeholder { font-size: 20px; - font-family: 'Lato-Light', sans-serif; - font-weight:200; + /*font-family: 'Lato-Light', sans-serif;*/ + font-weight: 300; } .form_public_key::-webkit-input-placeholder{ position: relative; top: 110px; font-size: 20px; - font-weight: 200; - font-family: 'Lato-Light', sans-serif; + /*font-family: 'Lato-Light', sans-serif;*/ + font-weight: 300; } .form_public_key::-moz-input-placeholder{ position: relative; top: 110px; font-size: 20px; - font-family: 'Lato-Light', sans-serif; - font-weight:200; + /*font-family: 'Lato-Light', sans-serif;*/ + font-weight: 300; } .form_public_key:-moz-input-placeholder{ position: relative; top: 110px; font-size: 20px; - font-weight:200; - font-family: 'Lato-Light', sans-serif; + /*font-family: 'Lato-Light', sans-serif;*/ + font-weight: 300; } .form_public_key:-ms-input-placeholder { position: relative; top: 110px; font-size: 20px; - font-weight:200; - font-family: 'Lato-Light', sans-serif; + /*font-family: 'Lato-Light', sans-serif;*/ + font-weight: 300; } .underform-contaner{ margin-bottom: 20px; @@ -273,7 +275,8 @@ } } .underform-contaner h4{ - font-family: 'Lato-Light', sans-serif; + /*font-family: 'Lato-Light', sans-serif;*/ + font-weight: 300; } .underform-contaner button{ /* font-family: Lato; */ @@ -287,9 +290,9 @@ color: #fff; } .control-label{ - font-family: 'Lato-Light', sans-serif; + /*font-family: 'Lato-Light', sans-serif;*/ + font-weight: 300; font-size: 20px; - font-weight:200; } .form-ssh h3{ margin-bottom: 40px; From cc58c494573b4f49abe866257b6125cbff3cfdbb Mon Sep 17 00:00:00 2001 From: ARvind Tiwari Date: Tue, 8 Aug 2017 00:51:54 +0530 Subject: [PATCH 028/189] font-weight fix in hosting app --- hosting/static/hosting/css/landing-page.css | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hosting/static/hosting/css/landing-page.css b/hosting/static/hosting/css/landing-page.css index ee9fea36..600ee20c 100644 --- a/hosting/static/hosting/css/landing-page.css +++ b/hosting/static/hosting/css/landing-page.css @@ -33,7 +33,7 @@ h5, h6 { /*font-family: 'Lato-Regular', sans-serif;*/ font-family: 'Lato', sans-serif; - font-weight: 300; + /*font-weight: 300;*/ } .topnav { @@ -379,7 +379,7 @@ h6 { font-size: 18px; line-height: 30px; /*font-family: 'Lato' !important;*/ - font-weight: normal !important; + font-weight: 300 !important; } .sign-up-message a { @@ -492,7 +492,7 @@ a.unlink:hover { /***** DCL payment page **********/ .dcl-order-container { /*font-family: Lato;*/ - font-weight: normal; + font-weight: 300; } .dcl-order-table-header { @@ -552,7 +552,7 @@ a.unlink:hover { .card-warning-content { /*font-family: Lato;*/ - font-weight: normal; + font-weight: 300; border: 1px solid #a1a1a1; border-radius: 3px; padding: 5px; From 227d3a20a526f3e30915e99a2b0fc3d4ef0fbbe9 Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 8 Aug 2017 01:57:29 +0530 Subject: [PATCH 029/189] Removed __future__ --- dynamicweb/__init__.py | 2 -- dynamicweb/celery.py | 1 - 2 files changed, 3 deletions(-) diff --git a/dynamicweb/__init__.py b/dynamicweb/__init__.py index 3b91b070..1a6c551d 100644 --- a/dynamicweb/__init__.py +++ b/dynamicweb/__init__.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, unicode_literals - # This will make sure the app is always imported when # Django starts so that shared_task will use this app. from .celery import app as celery_app diff --git a/dynamicweb/celery.py b/dynamicweb/celery.py index 28f6af3b..609ef5c4 100644 --- a/dynamicweb/celery.py +++ b/dynamicweb/celery.py @@ -1,4 +1,3 @@ -from __future__ import absolute_import, unicode_literals import os from celery import Celery From 8029f058243c02d13f45c277d6c4f552b034c701 Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 8 Aug 2017 02:12:13 +0530 Subject: [PATCH 030/189] Added env variable to logger message --- dynamicweb/settings/base.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dynamicweb/settings/base.py b/dynamicweb/settings/base.py index 58609234..a0d31592 100644 --- a/dynamicweb/settings/base.py +++ b/dynamicweb/settings/base.py @@ -37,7 +37,8 @@ def int_env(val, default_value=0): try: return_value = int(os.environ.get(val)) except Exception as e: - logger.error(str(e)) + logger.error("Encountered exception trying to get env value for {}\nException details: {}".format( + val, str(e))) return return_value From ac0787bdd597979830bd9fbdbe27d04f3237e34f Mon Sep 17 00:00:00 2001 From: ARvind Tiwari Date: Tue, 8 Aug 2017 23:01:19 +0530 Subject: [PATCH 031/189] modal title font-weight and capitalization fix --- hosting/locale/de/LC_MESSAGES/django.po | 2 +- hosting/static/hosting/css/commons.css | 5 ++++- hosting/templates/hosting/user_keys.html | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/hosting/locale/de/LC_MESSAGES/django.po b/hosting/locale/de/LC_MESSAGES/django.po index 0e777a19..4b886cae 100644 --- a/hosting/locale/de/LC_MESSAGES/django.po +++ b/hosting/locale/de/LC_MESSAGES/django.po @@ -393,7 +393,7 @@ msgstr "" msgid "Delete SSH Key" msgstr "SSH Key löschen" -msgid "Do You want to delete this key?" +msgid "Do you want to delete this key?" msgstr "Möchtest Du den Schlüssel löschen?" msgid "Show" diff --git a/hosting/static/hosting/css/commons.css b/hosting/static/hosting/css/commons.css index 2dd5637a..2b147f8b 100644 --- a/hosting/static/hosting/css/commons.css +++ b/hosting/static/hosting/css/commons.css @@ -110,13 +110,16 @@ font-weight: 100; color: #999; } +.modal-body .modal-icon { + margin-bottom: 10px; +} .modal-title { margin: 0; line-height: 1.42857143; font-size: 25px; padding: 0; /*font-family: 'Lato', sans-serif;*/ - font-weight: normal; + font-weight: 300; } .modal-text { padding-top: 15px; diff --git a/hosting/templates/hosting/user_keys.html b/hosting/templates/hosting/user_keys.html index 2125bd9f..dcb04c83 100644 --- a/hosting/templates/hosting/user_keys.html +++ b/hosting/templates/hosting/user_keys.html @@ -50,7 +50,7 @@ From 8160e53ea71d8bc152be424002969b7048323cb8 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Wed, 16 Aug 2017 04:10:08 +0530 Subject: [PATCH 054/189] removed commented html --- .../templates/hosting/virtual_machines.html | 87 +------------------ 1 file changed, 1 insertion(+), 86 deletions(-) diff --git a/hosting/templates/hosting/virtual_machines.html b/hosting/templates/hosting/virtual_machines.html index db1909a4..2d033a8d 100644 --- a/hosting/templates/hosting/virtual_machines.html +++ b/hosting/templates/hosting/virtual_machines.html @@ -1,5 +1,6 @@ {% extends "hosting/base_short.html" %} {% load staticfiles bootstrap3 i18n %} + {% block content %}

      {% trans "Virtual Machines" %}

      @@ -45,90 +46,4 @@
      - -{% comment %} -
      -
      -
      -
      - -

      {% trans "Virtual Machines"%}

      -
      -
      - {% if messages %} -
      - {% for message in messages %} - {{ message }} - {% endfor %} -
      - {% endif %} -
      - {% if not error %} -

      - {% trans "Create VM"%} -

      -
      - - - - - - - - - - - - {% for vm in vms %} - - - {% if vm.ipv6 %} - - - - {% endif %} - - - - - {% endfor %} - - {% endif %} -
      {% trans "ID"%}{% trans "Ipv4"%}{% trans "Ipv6"%}{% trans "Status"%}
      {{vm.vm_id}}{{vm.ipv4}}{{vm.ipv6}} - - {% if vm.state == 'ACTIVE' %} - {{vm.state}} - {% elif vm.state == 'FAILED' %} - {{vm.state}} - {% else %} - {{vm.state}} - {% endif %} - - - -
      - - {% if is_paginated %} - - {% endif %} -
      - -
      -
      - -
      -{% endcomment %} {%endblock%} From 337021a470541eacc22b832e21bc163735cbeb37 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Wed, 16 Aug 2017 04:13:40 +0530 Subject: [PATCH 055/189] added other conditional components on page --- .../templates/hosting/virtual_machines.html | 192 ++++++++++++++---- 1 file changed, 151 insertions(+), 41 deletions(-) diff --git a/hosting/templates/hosting/virtual_machines.html b/hosting/templates/hosting/virtual_machines.html index 2d033a8d..c792bc08 100644 --- a/hosting/templates/hosting/virtual_machines.html +++ b/hosting/templates/hosting/virtual_machines.html @@ -1,49 +1,159 @@ {% extends "hosting/base_short.html" %} {% load staticfiles bootstrap3 i18n %} - {% block content %}

      {% trans "Virtual Machines" %}

      -
      -

      {% trans 'To create a new virtual machine, click "Create VM"' %}

      - -
      - - - - - - - - - - - - - {% for vm in vms %} - - - {% if vm.ipv6 %} - - - {% endif %} - - - + {% if messages %} +
      + {% for message in messages %} + {{ message }} {% endfor %} -
      -
      IDIpv4Ipv6{% trans "Status" %}
      {{vm.vm_id}}{{vm.ipv4}}{{vm.ipv6}} - {% if vm.state == 'ACTIVE' %} - {{vm.state}} - {% elif vm.state == 'FAILED' %} - {{vm.state}} - {% else %} - {{vm.state}} - {% endif %} - - {% trans "View Detail"%} -
      +
      + {% endif %} + + {% if not error %} +
      +

      {% trans 'To create a new virtual machine, click "Create VM"' %}

      + +
      + + + + + + + + + + + + {% for vm in vms %} + + + {% if vm.ipv6 %} + + + {% endif %} + + + + {% endfor %} + +
      IDIpv4Ipv6{% trans "Status" %}
      {{vm.vm_id}}{{vm.ipv4}}{{vm.ipv6}} + {% if vm.state == 'ACTIVE' %} + {{vm.state}} + {% elif vm.state == 'FAILED' %} + {{vm.state}} + {% else %} + {{vm.state}} + {% endif %} + + {% trans "View Detail"%} +
      + {% endif %} + + {% if is_paginated %} + + {% endif %}
    + +{% comment %} +
    +
    +
    +
    + +

    {% trans "Virtual Machines"%}

    +
    +
    + {% if messages %} +
    + {% for message in messages %} + {{ message }} + {% endfor %} +
    + {% endif %} +
    + {% if not error %} +

    + {% trans "Create VM"%} +

    +
    + + + + + + + + + + + + {% for vm in vms %} + + + {% if vm.ipv6 %} + + + + {% endif %} + + + + + {% endfor %} + + {% endif %} +
    {% trans "ID"%}{% trans "Ipv4"%}{% trans "Ipv6"%}{% trans "Status"%}
    {{vm.vm_id}}{{vm.ipv4}}{{vm.ipv6}} + + {% if vm.state == 'ACTIVE' %} + {{vm.state}} + {% elif vm.state == 'FAILED' %} + {{vm.state}} + {% else %} + {{vm.state}} + {% endif %} + + + +
    + + {% if is_paginated %} + + {% endif %} +
    + +
    +
    + +
    +{% endcomment %} {%endblock%} From df97a16a26d1ac600308dd752c9aca2ac4f55fe8 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Wed, 16 Aug 2017 04:14:46 +0530 Subject: [PATCH 056/189] removed old markup --- .../templates/hosting/virtual_machines.html | 86 ------------------- 1 file changed, 86 deletions(-) diff --git a/hosting/templates/hosting/virtual_machines.html b/hosting/templates/hosting/virtual_machines.html index c792bc08..6964fb3a 100644 --- a/hosting/templates/hosting/virtual_machines.html +++ b/hosting/templates/hosting/virtual_machines.html @@ -70,90 +70,4 @@
    {% endif %}
- -{% comment %} -
-
-
-
- -

{% trans "Virtual Machines"%}

-
-
- {% if messages %} -
- {% for message in messages %} - {{ message }} - {% endfor %} -
- {% endif %} -
- {% if not error %} -

- {% trans "Create VM"%} -

-
- - - - - - - - - - - - {% for vm in vms %} - - - {% if vm.ipv6 %} - - - - {% endif %} - - - - - {% endfor %} - - {% endif %} -
{% trans "ID"%}{% trans "Ipv4"%}{% trans "Ipv6"%}{% trans "Status"%}
{{vm.vm_id}}{{vm.ipv4}}{{vm.ipv6}} - - {% if vm.state == 'ACTIVE' %} - {{vm.state}} - {% elif vm.state == 'FAILED' %} - {{vm.state}} - {% else %} - {{vm.state}} - {% endif %} - - - -
- - {% if is_paginated %} - - {% endif %} -
- -
-
- -
-{% endcomment %} {%endblock%} From a2229d325052dd54ce785fd65659637c9d9bfdf5 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Wed, 16 Aug 2017 04:26:17 +0530 Subject: [PATCH 057/189] created translations --- hosting/locale/de/LC_MESSAGES/django.po | 24 ++++++++++++------- hosting/templates/hosting/user_keys.html | 2 +- .../templates/hosting/virtual_machines.html | 10 ++++---- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/hosting/locale/de/LC_MESSAGES/django.po b/hosting/locale/de/LC_MESSAGES/django.po index 39bc4adc..3cc30292 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: 2017-08-11 01:16+0530\n" +"POT-Creation-Date: 2017-08-16 04:19+0530\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -435,17 +435,17 @@ msgstr "Sind Sie sicher, dass Sie ihre virtuelle Maschine beenden wollen " msgid "Virtual Machines" msgstr "Virtuelle Maschinen" -msgid "Create VM" -msgstr "Neue VM" - -msgid "ID" +msgid "To create a new virtual machine, click \"Create VM\"" msgstr "" -msgid "Ipv4" -msgstr "IPv4" +msgid "CREATE VM" +msgstr "NEUE VM" -msgid "Ipv6" -msgstr "IPv6" +msgid "Page" +msgstr "" + +msgid "of" +msgstr "" msgid "login" msgstr "einloggen" @@ -482,6 +482,12 @@ msgid "" "contact Data Center Light Support." msgstr "" +#~ msgid "Ipv4" +#~ msgstr "IPv4" + +#~ msgid "Ipv6" +#~ msgstr "IPv6" + #~ msgid "Close" #~ msgstr "Schliessen" diff --git a/hosting/templates/hosting/user_keys.html b/hosting/templates/hosting/user_keys.html index b66a1f6f..fd93b66f 100644 --- a/hosting/templates/hosting/user_keys.html +++ b/hosting/templates/hosting/user_keys.html @@ -77,7 +77,7 @@
- +
+
+

{% trans "My Orders" %}

+ {% if messages %} +
+ {% for message in messages %} + {{ message }} + {% endfor %}
-
- + {% endif %} +
+ + + + + + + + + + + + + + + + + + + {% for order in orders %} + + {% endfor %} + +
{% trans "Order Nr." %}{% trans "Date" %}{% trans "Amount" %}{% trans "Status" %}
{{ order.id }}{{ order.created_at | date:"M d, Y" }}{{ order.price }} + {% if order.approved %} + Approved + {% else %} + Declined + {% endif %} + + {% trans "View Detail" %} +
+ + {% if is_paginated %} + + {% endif %} +
{% endblock %} From 29d36faef099fede9f896f573f2d9b3c837d9d6c Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 21 Aug 2017 00:41:46 +0530 Subject: [PATCH 102/189] Refactored some code --- hosting/models.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/hosting/models.py b/hosting/models.py index 3aa27fbe..a1951bc6 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -1,13 +1,12 @@ +import os import logging -import os -from Crypto.PublicKey import RSA from django.db import models from django.utils.functional import cached_property - +from Crypto.PublicKey import RSA from membership.models import StripeCustomer, CustomUser -from utils.mixins import AssignPermissionsMixin from utils.models import BillingAddress +from utils.mixins import AssignPermissionsMixin logger = logging.getLogger(__name__) From 506b5a904a00f90db72448e5afe18be441fd56e1 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 21 Aug 2017 01:54:55 +0530 Subject: [PATCH 103/189] Added missing code from the merge --- datacenterlight/tasks.py | 4 ++-- datacenterlight/views.py | 39 +++++++++++++++++++++++++++------------ 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index b897c54a..3cab0ea0 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -89,9 +89,9 @@ def create_vm_task(self, vm_template_id, user, specs, template, stripe_customer_ billing_address_user_form.is_valid() billing_address_user_form.save() - # Associate an order with a stripe payment + # Associate an order with a stripe subscription charge_object = DictDotLookup(charge) - order.set_stripe_charge(charge_object) + order.set_subscription_id(charge_object) # If the Stripe payment succeeds, set order status approved order.set_approved() diff --git a/datacenterlight/views.py b/datacenterlight/views.py index db7f2e53..9761b471 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -444,22 +444,37 @@ class OrderConfirmationView(DetailView): billing_address_data = request.session.get('billing_address_data') billing_address_id = request.session.get('billing_address') vm_template_id = template.get('id', 1) - final_price = specs.get('price') # Make stripe charge to a customer stripe_utils = StripeUtils() - charge_response = stripe_utils.make_charge(amount=final_price, - customer=customer.stripe_id) - - # Check if the payment was approved - if not charge_response.get('response_object') and not charge_response.get('paid'): - msg = charge_response.get('error') - messages.add_message(self.request, messages.ERROR, msg, extra_tags='make_charge_error') - return HttpResponseRedirect(reverse('datacenterlight:payment') + '#payment_error') - - charge = charge_response.get('response_object') + cpu = specs.get('cpu') + memory = specs.get('memory') + disk_size = specs.get('disk_size') + amount_to_be_charged = (cpu * 5) + (memory * 2) + (disk_size * 0.6) + plan_name = "{cpu} Cores, {memory} GB RAM, {disk_size} GB SSD".format(cpu=cpu, + memory=memory, + disk_size=disk_size) + stripe_plan_id = StripeUtils.get_stripe_plan_id(cpu=cpu, + ram=memory, + ssd=disk_size, + version=1, + app='dcl') + stripe_plan = stripe_utils.get_or_create_stripe_plan(amount=amount_to_be_charged, + name=plan_name, + stripe_plan_id=stripe_plan_id) + subscription_result = stripe_utils.subscribe_customer_to_plan(customer.stripe_id, + [{"plan": stripe_plan.get( + 'response_object').stripe_plan_id}]) + response_object = subscription_result.get('response_object') + # Check if the subscription was approved and is active + if response_object is None or response_object.status is not 'active': + context = {} + context.update({ + 'paymentError': response_object.get('error') + }) + return render(request, self.payment_template_name, context) create_vm_task.delay(vm_template_id, user, specs, template, stripe_customer_id, billing_address_data, billing_address_id, - charge) + response_object) request.session['order_confirmation'] = True return HttpResponseRedirect(reverse('datacenterlight:order_success')) From 090f3388783c577413c22cf32eac52971ba4673a Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Mon, 21 Aug 2017 17:51:17 +0200 Subject: [PATCH 104/189] Added cms.css and renamed faq.html to cms_page.html --- .../static/datacenterlight/css/cms.css | 47 +++++++++++++++++++ .../{faq.html => cms_page.html} | 6 ++- 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 datacenterlight/static/datacenterlight/css/cms.css rename datacenterlight/templates/datacenterlight/{faq.html => cms_page.html} (80%) diff --git a/datacenterlight/static/datacenterlight/css/cms.css b/datacenterlight/static/datacenterlight/css/cms.css new file mode 100644 index 00000000..abf06501 --- /dev/null +++ b/datacenterlight/static/datacenterlight/css/cms.css @@ -0,0 +1,47 @@ +.dcl-cms_page-full-width { + color: #fff; + text-align: center; + background-image: -ms-linear-gradient(right, #29427A 50%, #4F6699 100%); + background-image: -moz-linear-gradient(right, #29427A 50%, #4F6699 100%); + background-image: -o-linear-gradient(right, #29427A 50%, #4F6699 100%); + background-image: -webkit-gradient(linear, right top, left top, color-stop(50, #29427A), color-stop(100, #4F6699)); + background-image: -webkit-linear-gradient(right, #29427A 50%, #4F6699 100%); + background-image: linear-gradient(to left, #29427A 50%, #4F6699 100%); +} + +.dcl-cms_page-header { + padding: 150px 0 150px 0; + text-align: center; + color: #f8f8f8; + background: url(../img/pattern.jpg) no-repeat center center; + background-size: cover; + position: relative; + background-attachment: fixed; +} + +.dcl-cms_page-header::before { + content: ""; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + background: rgba(90, 116, 175, 0.85); +} + +#dcl-cms_page-text { + background: #fff; +} + +#dcl-cms_page-text h3 { + font-size: 42px; + width: 70%; +} + +@media (max-width: 767px) { + #dcl-cms_page-text h3 { + font-size: 30px; + line-height: 40px; + width: 100%; + } +} \ No newline at end of file diff --git a/datacenterlight/templates/datacenterlight/faq.html b/datacenterlight/templates/datacenterlight/cms_page.html similarity index 80% rename from datacenterlight/templates/datacenterlight/faq.html rename to datacenterlight/templates/datacenterlight/cms_page.html index 96970ada..6f377749 100644 --- a/datacenterlight/templates/datacenterlight/faq.html +++ b/datacenterlight/templates/datacenterlight/cms_page.html @@ -1,6 +1,10 @@ {% extends "datacenterlight/base.html" %} -{% load staticfiles cms_tags %} +{% load staticfiles cms_tags sekizai_tags %} {% block content %} +{% addtoblock "css" %} + +{% endaddtoblock %} +
From c8dcd65adc1885bb4a4f36ffaac9fafb9d4cc12d Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Mon, 21 Aug 2017 17:52:57 +0200 Subject: [PATCH 105/189] Updated _navbar to show menu on other pages except for index and whydcl --- .../datacenterlight/includes/_navbar.html | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/datacenterlight/templates/datacenterlight/includes/_navbar.html b/datacenterlight/templates/datacenterlight/includes/_navbar.html index 99f6a4a8..fe948861 100644 --- a/datacenterlight/templates/datacenterlight/includes/_navbar.html +++ b/datacenterlight/templates/datacenterlight/includes/_navbar.html @@ -29,6 +29,22 @@
  • {% trans "Reliable and light" %}
  • {% trans "Order VM" %}
  • + +
  • + {% trans "Why Data Center Light?" %} +
  • +
  • + {% trans "Contact" %} +
  • + {% else %} +
  • {% trans "Why Data Center Light?" %} From 7d7260ce6d4b863df885e579ffdda6268ddc4e3e Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Mon, 21 Aug 2017 17:59:56 +0200 Subject: [PATCH 106/189] Renamed faq.html to cms_page.html --- dynamicweb/settings/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dynamicweb/settings/base.py b/dynamicweb/settings/base.py index 37fc03dc..c0f84da7 100644 --- a/dynamicweb/settings/base.py +++ b/dynamicweb/settings/base.py @@ -191,7 +191,7 @@ CMS_TEMPLATES = ( ('blog_ungleich.html', gettext('Blog')), ('page.html', gettext('Page')), # dcl - ('datacenterlight/faq.html', gettext('DCL.Faq')), + ('datacenterlight/cms_page.html', gettext('Data Center Light')), ) DATABASES = { From 696af58bffe1734486dbd382e7af0bc7e0ce1535 Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Mon, 21 Aug 2017 18:00:41 +0200 Subject: [PATCH 107/189] Refactored class names to better names --- .../templates/datacenterlight/cms_page.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/datacenterlight/templates/datacenterlight/cms_page.html b/datacenterlight/templates/datacenterlight/cms_page.html index 6f377749..f42528e4 100644 --- a/datacenterlight/templates/datacenterlight/cms_page.html +++ b/datacenterlight/templates/datacenterlight/cms_page.html @@ -5,13 +5,13 @@ {% endaddtoblock %} -
    -
    +
    +
    -

    {% placeholder 'datacenterlight_faq_title' %}

    +

    {% placeholder 'datacenterlight_cms_page_title' %}

    @@ -19,12 +19,12 @@
    -
    +
    - {% placeholder 'datacenterlight_faq_text' %} + {% placeholder 'datacenterlight_cms_page_text' %}
    From 6c80f94f6769404a82d14f5fc94136462e6b0b00 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 21 Aug 2017 23:16:30 +0530 Subject: [PATCH 108/189] Reformatted dcl's _navbar.html. Attention: Some lines are over 120 char limit as multiline templatetags is not supported. --- .../datacenterlight/includes/_navbar.html | 189 ++++++++++-------- 1 file changed, 106 insertions(+), 83 deletions(-) diff --git a/datacenterlight/templates/datacenterlight/includes/_navbar.html b/datacenterlight/templates/datacenterlight/includes/_navbar.html index fe948861..9df9e227 100644 --- a/datacenterlight/templates/datacenterlight/includes/_navbar.html +++ b/datacenterlight/templates/datacenterlight/includes/_navbar.html @@ -1,90 +1,113 @@ {% load staticfiles i18n%} {% get_current_language as LANGUAGE_CODE %} {% load custom_tags %} \ No newline at end of file + From d8b76abda2e9e5c6faea07eff7462e7d1d6a1758 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Mon, 21 Aug 2017 23:41:38 +0530 Subject: [PATCH 109/189] translations fixed --- hosting/locale/de/LC_MESSAGES/django.po | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hosting/locale/de/LC_MESSAGES/django.po b/hosting/locale/de/LC_MESSAGES/django.po index b53124bb..94a6c028 100644 --- a/hosting/locale/de/LC_MESSAGES/django.po +++ b/hosting/locale/de/LC_MESSAGES/django.po @@ -435,7 +435,7 @@ msgid "CREATE VM" msgstr "NEUE VM" msgid "login" -msgstr "einloggen" +msgstr "Einloggen" msgid "" "Thank you for signing up. We have sent an email to you. Please follow the " @@ -464,7 +464,7 @@ msgstr "Entschuldigung, deine Anfrage ist ungültig." #, fuzzy #| msgid "Credit Card" msgid "Invalid credit card" -msgstr "Invalid Kreditkarte" +msgstr "Ungültige Kreditkarte" msgid "Confirm Order" msgstr "Bestellung Bestätigen" From 83df42b5dfa893ee9bbdaffbab81dd982c258aff Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 22 Aug 2017 00:07:58 +0530 Subject: [PATCH 110/189] Fixed checking active status of subscription + redirection to payment page on error --- datacenterlight/views.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 9761b471..91dbf900 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -467,12 +467,10 @@ class OrderConfirmationView(DetailView): 'response_object').stripe_plan_id}]) response_object = subscription_result.get('response_object') # Check if the subscription was approved and is active - if response_object is None or response_object.status is not 'active': - context = {} - context.update({ - 'paymentError': response_object.get('error') - }) - return render(request, self.payment_template_name, context) + if response_object is None or response_object.status != 'active': + msg = subscription_result.get('error') + messages.add_message(self.request, messages.ERROR, msg, extra_tags='failed_payment') + return HttpResponseRedirect(reverse('datacenterlight:payment') + '#payment_error') create_vm_task.delay(vm_template_id, user, specs, template, stripe_customer_id, billing_address_data, billing_address_id, response_object) From 487ab6b9278b4fac4ab8b405b79e84c8d4673312 Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 22 Aug 2017 01:55:45 +0530 Subject: [PATCH 111/189] Changed the subscription plan id field size to 256 characters --- .../migrations/0008_auto_20170821_2024.py | 20 +++++++++++++++++++ datacenterlight/models.py | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 datacenterlight/migrations/0008_auto_20170821_2024.py diff --git a/datacenterlight/migrations/0008_auto_20170821_2024.py b/datacenterlight/migrations/0008_auto_20170821_2024.py new file mode 100644 index 00000000..5357a404 --- /dev/null +++ b/datacenterlight/migrations/0008_auto_20170821_2024.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2017-08-21 20:24 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('datacenterlight', '0007_stripeplan'), + ] + + operations = [ + migrations.AlterField( + model_name='stripeplan', + name='stripe_plan_id', + field=models.CharField(max_length=256, null=True), + ), + ] diff --git a/datacenterlight/models.py b/datacenterlight/models.py index d8237ed2..f7b50a01 100644 --- a/datacenterlight/models.py +++ b/datacenterlight/models.py @@ -65,7 +65,7 @@ class StripePlan(models.Model): """ A model to store Data Center Light's created Stripe plans """ - stripe_plan_id = models.CharField(max_length=100, null=True) + stripe_plan_id = models.CharField(max_length=256, null=True) @classmethod def create(cls, stripe_plan_id): From 9eecfbda643d396b5724119798e1abcbfcfbe339 Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 22 Aug 2017 02:12:43 +0530 Subject: [PATCH 112/189] Added hdd parameter as an optional parameter to the subscription plan id --- utils/stripe_utils.py | 8 ++++++-- utils/tests.py | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/utils/stripe_utils.py b/utils/stripe_utils.py index da1594d3..92e1dcd3 100644 --- a/utils/stripe_utils.py +++ b/utils/stripe_utils.py @@ -220,16 +220,20 @@ class StripeUtils(object): return charge @staticmethod - def get_stripe_plan_id(cpu, ram, ssd, version, app): + def get_stripe_plan_id(cpu, ram, ssd, version, app='dcl', hdd=None): """ Returns the stripe plan id string of the form `dcl-v1-cpu-2-ram-5gb-ssd-10gb` based on the input parameters :param cpu: The number of cores :param ram: The size of the RAM in GB - :param ssd: The size of storage in GB + :param ssd: The size of ssd storage in GB + :param hdd: The size of hdd storage in GB :param version: The version of the Stripe plans + :param app: The application to which the stripe plan belongs to. By default it is 'dcl' :return: A string of the form `dcl-v1-cpu-2-ram-5gb-ssd-10gb` """ dcl_plan_string = 'cpu-{cpu}-ram-{ram}gb-ssd-{ssd}gb'.format(cpu=cpu, ram=ram, ssd=ssd) + if hdd is not None: + dcl_plan_string = '{dcl_plan_string}-hdd-{hdd}gb'.format(dcl_plan_string=dcl_plan_string, hdd=hdd) stripe_plan_id_string = '{app}-v{version}-{plan}'.format(app=app, version=version, plan=dcl_plan_string) return stripe_plan_id_string diff --git a/utils/tests.py b/utils/tests.py index 3921756d..2da1edf2 100644 --- a/utils/tests.py +++ b/utils/tests.py @@ -138,6 +138,8 @@ class StripePlanTestCase(TestStripeCustomerDescription): def test_get_stripe_plan_id_string(self): plan_id_string = StripeUtils.get_stripe_plan_id(cpu=2, ram=20, ssd=100, version=1, app='dcl') self.assertEqual(plan_id_string, 'dcl-v1-cpu-2-ram-20gb-ssd-100gb') + plan_id_string = StripeUtils.get_stripe_plan_id(cpu=2, ram=20, ssd=100, version=1, app='dcl', hdd=200) + self.assertEqual(plan_id_string, 'dcl-v1-cpu-2-ram-20gb-ssd-100gb-hdd-200gb') def test_get_or_create_plan(self): stripe_plan = self.stripe_utils.get_or_create_stripe_plan(2000, "test plan 1", stripe_plan_id='test-plan-1') From 6890c821220f0c3b14a46f9851d76ec7c6b1448f Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Tue, 22 Aug 2017 20:45:18 +0530 Subject: [PATCH 113/189] dashboard url, view,template and css added --- hosting/static/hosting/css/dashboard.css | 21 +++++++ .../static/hosting/img/24-hours-support.svg | 61 +++++++++++++++++++ hosting/static/hosting/img/billing.svg | 1 + .../static/hosting/img/dashboard_settings.svg | 50 +++++++++++++++ hosting/static/hosting/img/key.svg | 7 +++ hosting/static/hosting/img/plusVM.svg | 19 ++++++ hosting/templates/hosting/base_short.html | 1 + hosting/templates/hosting/dashboard.html | 10 +++ hosting/urls.py | 29 +++++---- hosting/views.py | 21 ++++++- 10 files changed, 206 insertions(+), 14 deletions(-) create mode 100644 hosting/static/hosting/css/dashboard.css create mode 100644 hosting/static/hosting/img/24-hours-support.svg create mode 100644 hosting/static/hosting/img/billing.svg create mode 100644 hosting/static/hosting/img/dashboard_settings.svg create mode 100644 hosting/static/hosting/img/key.svg create mode 100644 hosting/static/hosting/img/plusVM.svg create mode 100644 hosting/templates/hosting/dashboard.html diff --git a/hosting/static/hosting/css/dashboard.css b/hosting/static/hosting/css/dashboard.css new file mode 100644 index 00000000..82332e23 --- /dev/null +++ b/hosting/static/hosting/css/dashboard.css @@ -0,0 +1,21 @@ +.hosting-dashboard:after { + content: ''; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + background-color: rgba(41, 66, 122, 0.59); + z-index: -1; +} +.hosting-dashboard:before { + content: ''; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + background: url(../../datacenterlight/img/pattern.jpg) no-repeat center center; + background-size: cover; + z-index: -2; +} \ No newline at end of file diff --git a/hosting/static/hosting/img/24-hours-support.svg b/hosting/static/hosting/img/24-hours-support.svg new file mode 100644 index 00000000..4db05be3 --- /dev/null +++ b/hosting/static/hosting/img/24-hours-support.svg @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hosting/static/hosting/img/billing.svg b/hosting/static/hosting/img/billing.svg new file mode 100644 index 00000000..d002fa6c --- /dev/null +++ b/hosting/static/hosting/img/billing.svg @@ -0,0 +1 @@ +billing icon \ No newline at end of file diff --git a/hosting/static/hosting/img/dashboard_settings.svg b/hosting/static/hosting/img/dashboard_settings.svg new file mode 100644 index 00000000..4044e93a --- /dev/null +++ b/hosting/static/hosting/img/dashboard_settings.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hosting/static/hosting/img/key.svg b/hosting/static/hosting/img/key.svg new file mode 100644 index 00000000..b2bd7bc3 --- /dev/null +++ b/hosting/static/hosting/img/key.svg @@ -0,0 +1,7 @@ + + + + + Svg Vector Icons : http://www.onlinewebfonts.com/icon + + \ No newline at end of file diff --git a/hosting/static/hosting/img/plusVM.svg b/hosting/static/hosting/img/plusVM.svg new file mode 100644 index 00000000..c6c49316 --- /dev/null +++ b/hosting/static/hosting/img/plusVM.svg @@ -0,0 +1,19 @@ + + + + +VM + Created with Sketch. + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/hosting/templates/hosting/base_short.html b/hosting/templates/hosting/base_short.html index 847daa37..4dcf5074 100644 --- a/hosting/templates/hosting/base_short.html +++ b/hosting/templates/hosting/base_short.html @@ -24,6 +24,7 @@ + diff --git a/hosting/templates/hosting/dashboard.html b/hosting/templates/hosting/dashboard.html new file mode 100644 index 00000000..4c06f19b --- /dev/null +++ b/hosting/templates/hosting/dashboard.html @@ -0,0 +1,10 @@ +{% extends "hosting/base_short.html" %} +{% load staticfiles bootstrap3 i18n %} + +{% block content %} +
    +
    + +
    +
    +{%endblock%} diff --git a/hosting/urls.py b/hosting/urls.py index ea96af77..e6b6fee3 100644 --- a/hosting/urls.py +++ b/hosting/urls.py @@ -1,17 +1,20 @@ from django.conf.urls import url from django.contrib.auth import views as auth_views -from .views import DjangoHostingView, RailsHostingView, PaymentVMView,\ - NodeJSHostingView, LoginView, SignupView, SignupValidateView, SignupValidatedView, IndexView, \ - OrdersHostingListView, OrdersHostingDetailView, VirtualMachinesPlanListView,\ - VirtualMachineView, OrdersHostingDeleteView, NotificationsView, \ - MarkAsReadNotificationView, PasswordResetView, PasswordResetConfirmView, HostingPricingView,\ - CreateVirtualMachinesView, HostingBillListView, HostingBillDetailView, \ - SSHKeyDeleteView, SSHKeyCreateView, SSHKeyListView, SSHKeyChoiceView +from .views import ( + DjangoHostingView, RailsHostingView, PaymentVMView, NodeJSHostingView, + LoginView, SignupView, SignupValidateView, SignupValidatedView, IndexView, + NotificationsView, OrdersHostingListView, OrdersHostingDetailView, + VirtualMachinesPlanListView, VirtualMachineView, OrdersHostingDeleteView, + MarkAsReadNotificationView, PasswordResetView, PasswordResetConfirmView, + HostingPricingView, CreateVirtualMachinesView, HostingBillListView, + HostingBillDetailView, SSHKeyDeleteView, SSHKeyCreateView, SSHKeyListView, + SSHKeyChoiceView, DashboardView) urlpatterns = [ url(r'index/?$', IndexView.as_view(), name='index'), url(r'django/?$', DjangoHostingView.as_view(), name='djangohosting'), + url(r'dashboard/?$', DashboardView.as_view(), name='dashboard'), url(r'nodejs/?$', NodeJSHostingView.as_view(), name='nodejshosting'), url(r'rails/?$', RailsHostingView.as_view(), name='railshosting'), url(r'pricing/?$', HostingPricingView.as_view(), name='pricing'), @@ -20,9 +23,12 @@ urlpatterns = [ url(r'orders/(?P\d+)/?$', OrdersHostingDetailView.as_view(), name='orders'), url(r'bills/?$', HostingBillListView.as_view(), name='bills'), url(r'bills/(?P\d+)/?$', HostingBillDetailView.as_view(), name='bills'), - url(r'cancel_order/(?P\d+)/?$', OrdersHostingDeleteView.as_view(), name='delete_order'), - url(r'create_virtual_machine/?$', CreateVirtualMachinesView.as_view(), name='create_virtual_machine'), - url(r'my-virtual-machines/?$', VirtualMachinesPlanListView.as_view(), name='virtual_machines'), + url(r'cancel_order/(?P\d+)/?$', + OrdersHostingDeleteView.as_view(), name='delete_order'), + url(r'create_virtual_machine/?$', CreateVirtualMachinesView.as_view(), + name='create_virtual_machine'), + url(r'my-virtual-machines/?$', + VirtualMachinesPlanListView.as_view(), name='virtual_machines'), url(r'my-virtual-machines/(?P\d+)/?$', VirtualMachineView.as_view(), name='virtual_machines'), url(r'ssh_keys/?$', SSHKeyListView.as_view(), @@ -44,5 +50,6 @@ urlpatterns = [ PasswordResetConfirmView.as_view(), name='reset_password_confirm'), url(r'^logout/?$', auth_views.logout, {'next_page': '/hosting/login?logged_out=true'}, name='logout'), - url(r'^validate/(?P.*)/$', SignupValidatedView.as_view(), name='validate') + url(r'^validate/(?P.*)/$', + SignupValidatedView.as_view(), name='validate') ] diff --git a/hosting/views.py b/hosting/views.py index 2b4c8d21..394fd04c 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -40,6 +40,18 @@ CONNECTION_ERROR = "Your VMs cannot be displayed at the moment due to a backend connection error. please try again in a few minutes." +class DashboardView(View): + template_name = "hosting/dashboard.html" + + def get_context_data(self, **kwargs): + context = {} + return context + + def get(self, request, *args, **kwargs): + context = self.get_context_data() + return render(request, self.template_name, context) + + class DjangoHostingView(ProcessVMSelectionMixin, View): template_name = "hosting/django.html" @@ -244,7 +256,8 @@ class SignupValidatedView(SignupValidateView): lurl=login_url) else: home_url = 'Data Center Light' + reverse('datacenterlight:index') + \ + '">Data Center Light' message = '{sorry_message}
    {go_back_to} {hurl}'.format( sorry_message=_("Sorry. Your request is invalid."), go_back_to=_('Go back to'), @@ -557,7 +570,8 @@ class PaymentVMView(LoginRequiredMixin, FormView): token=token) if not customer: msg = _("Invalid credit card") - messages.add_message(self.request, messages.ERROR, msg, extra_tags='make_charge_error') + messages.add_message( + self.request, messages.ERROR, msg, extra_tags='make_charge_error') return HttpResponseRedirect(reverse('hosting:payment') + '#payment_error') # Create Billing Address @@ -571,7 +585,8 @@ class PaymentVMView(LoginRequiredMixin, FormView): # Check if the payment was approved if not charge_response.get('response_object') and not charge_response.get('paid'): msg = charge_response.get('error') - messages.add_message(self.request, messages.ERROR, msg, extra_tags='make_charge_error') + messages.add_message( + self.request, messages.ERROR, msg, extra_tags='make_charge_error') return HttpResponseRedirect(reverse('hosting:payment') + '#payment_error') charge = charge_response.get('response_object') From 12844ac0d917df6bce34a0ddfe9ee887ef25b44d Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Tue, 22 Aug 2017 21:35:30 +0530 Subject: [PATCH 114/189] dashboard page --- hosting/static/hosting/css/dashboard.css | 56 ++++++++++++++++++++++++ hosting/static/hosting/img/connected.svg | 45 +++++++++++++++++++ hosting/static/hosting/img/plusVM.svg | 8 ++-- hosting/static/hosting/img/settings.svg | 53 ++++++++++++++++++++++ hosting/static/hosting/js/initial.js | 37 ++++++++++++++++ hosting/templates/hosting/dashboard.html | 42 +++++++++++++++++- 6 files changed, 236 insertions(+), 5 deletions(-) create mode 100644 hosting/static/hosting/img/connected.svg create mode 100644 hosting/static/hosting/img/settings.svg diff --git a/hosting/static/hosting/css/dashboard.css b/hosting/static/hosting/css/dashboard.css index 82332e23..7a80e162 100644 --- a/hosting/static/hosting/css/dashboard.css +++ b/hosting/static/hosting/css/dashboard.css @@ -18,4 +18,60 @@ background: url(../../datacenterlight/img/pattern.jpg) no-repeat center center; background-size: cover; z-index: -2; +} + +.hosting-dashboard .dashboard-container-head { + color: #fff; + margin-bottom: 25px; +} + +.hosting-dashboard-item { + background: #e9ebee; + box-shadow: 1px 3px 3px rgba(0,0,0,0.5); + padding: 25px; + color: rgba(124, 139, 175, 0.7); + font-size: 19px; +} +.hosting-dashboard-item:hover, +.hosting-dashboard-item:focus, +.hosting-dashboard-item:active { + text-decoration: none; + color: #7c8baf; + background: #fff; +} + +.hosting-dashboard-item h2 { + margin: 0; + font-size: 19px; + padding-bottom: 10px; + border-bottom: 2px solid #acb5cf; + margin-bottom: 25px; +} + +.hosting-dashboard-image { + height: 100px; + fill: #8b9bb7; + display: flex; + align-items: center; +} +.hosting-dashboard-image img, +.hosting-dashboard-image svg { + width: 100%; + height: 100%; + max-height: 65px; +} +.hosting-dashboard-image img { + opacity: 0.2; +} + +@media (min-width: 768px) { + .hosting-dashboard-content { + display: flex; + justify-content: space-between; + flex-wrap: wrap; + } + .hosting-dashboard-item { + width: 32%; + margin-bottom: 15px; + } } \ No newline at end of file diff --git a/hosting/static/hosting/img/connected.svg b/hosting/static/hosting/img/connected.svg new file mode 100644 index 00000000..fa3875dc --- /dev/null +++ b/hosting/static/hosting/img/connected.svg @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hosting/static/hosting/img/plusVM.svg b/hosting/static/hosting/img/plusVM.svg index c6c49316..2c1bb8f8 100644 --- a/hosting/static/hosting/img/plusVM.svg +++ b/hosting/static/hosting/img/plusVM.svg @@ -4,10 +4,10 @@ +VM Created with Sketch. - - - - + + + + diff --git a/hosting/static/hosting/img/settings.svg b/hosting/static/hosting/img/settings.svg new file mode 100644 index 00000000..61dc8613 --- /dev/null +++ b/hosting/static/hosting/img/settings.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hosting/static/hosting/js/initial.js b/hosting/static/hosting/js/initial.js index da2887c6..de36db7c 100644 --- a/hosting/static/hosting/js/initial.js +++ b/hosting/static/hosting/js/initial.js @@ -13,4 +13,41 @@ $( document ).ready(function() { }, 1000); }); + /* + * Replace all SVG images with inline SVG + */ + $('.svg-img').each(function(){ + console.log('asa') + var $img = $(this); + var imgID = $img.attr('id'); + var imgClass = $img.attr('class'); + var imgURL = $img.attr('src'); + + jQuery.get(imgURL, function(data) { + // Get the SVG tag, ignore the rest + var $svg = jQuery(data).find('svg'); + + // Add replaced image's ID to the new SVG + if(typeof imgID !== 'undefined') { + $svg = $svg.attr('id', imgID); + } + // Add replaced image's classes to the new SVG + if(typeof imgClass !== 'undefined') { + $svg = $svg.attr('class', imgClass+' replaced-svg'); + } + + // Remove any invalid XML tags as per http://validator.w3.org + $svg = $svg.removeAttr('xmlns:a'); + + // Check if the viewport is set, if the viewport is not set the SVG wont't scale. + if(!$svg.attr('viewBox') && $svg.attr('height') && $svg.attr('width')) { + $svg.attr('viewBox', '0 0 ' + $svg.attr('height') + ' ' + $svg.attr('width')) + } + + // Replace image with new SVG + $img.replaceWith($svg); + + }, 'xml'); + + }); }); \ No newline at end of file diff --git a/hosting/templates/hosting/dashboard.html b/hosting/templates/hosting/dashboard.html index 4c06f19b..562b3671 100644 --- a/hosting/templates/hosting/dashboard.html +++ b/hosting/templates/hosting/dashboard.html @@ -4,7 +4,47 @@ {% block content %} {%endblock%} From 9bbcd1b9abe40691974eb279c049c0d978801b77 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Tue, 22 Aug 2017 21:40:40 +0530 Subject: [PATCH 115/189] translations compiled --- hosting/locale/de/LC_MESSAGES/django.po | 75 +++++++++++++++++-------- 1 file changed, 53 insertions(+), 22 deletions(-) diff --git a/hosting/locale/de/LC_MESSAGES/django.po b/hosting/locale/de/LC_MESSAGES/django.po index 3cc30292..54ddb6a8 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: 2017-08-16 04:19+0530\n" +"POT-Creation-Date: 2017-08-22 21:38+0530\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -172,6 +172,27 @@ msgstr "CHF/Monat" msgid "Start VM" msgstr "VM jetzt starten" +msgid "My Dashboard" +msgstr "" + +msgid "Create VM" +msgstr "" + +msgid "My VMs" +msgstr "" + +msgid "My SSH Keys" +msgstr "" + +msgid "My Bills" +msgstr "" + +msgid "My Settings" +msgstr "" + +msgid "Support / Contact" +msgstr "" + #, python-format msgid "" "You're receiving this email because you requested a password reset for your " @@ -300,13 +321,21 @@ msgstr "" "\"https://stripe.com\" target=\"_blank\">Stripe für die Bezahlung und " "speichern keine Informationen in unserer Datenbank." +#, fuzzy +#| msgid "" +#| "\n" +#| " You are not making any " +#| "payment yet. After submitting your card\n" +#| " information, you will be " +#| "taken to the Confirm Order Page.\n" +#| " " msgid "" "\n" -" You are not making any payment yet. " -"After submitting your card\n" -" information, you will be taken to " -"the Confirm Order Page.\n" -" " +" You are not making any " +"payment yet. After submitting your card\n" +" information, you will be " +"taken to the Confirm Order Page.\n" +" " msgstr "" "\n" "Es wird noch keine Bezahlung vorgenommen. Nach der Eingabe Deiner " @@ -328,19 +357,6 @@ msgstr "" msgid "Card Type" msgstr "Kartentyp" -msgid "" -"\n" -" You are not making any payment " -"yet. After submitting your card\n" -" information, you will be taken " -"to the Confirm Order Page.\n" -" " -msgstr "" -"\n" -"Es wird noch keine Bezahlung vorgenommen. Nach der Eingabe Deiner " -"Kreditkateninformationen wirst du auf die Bestellbestätigungsseite " -"weitergeleitet." - msgid "Processing" msgstr "Weiter" @@ -474,6 +490,11 @@ msgstr "Du kannst dich nun" msgid "Sorry. Your request is invalid." msgstr "Entschuldigung, deine Anfrage ist ungültig." +#, fuzzy +#| msgid "Credit Card" +msgid "Invalid credit card" +msgstr "Kreditkarte" + msgid "Confirm Order" msgstr "Bestellung Bestätigen" @@ -482,6 +503,19 @@ msgid "" "contact Data Center Light Support." msgstr "" +#~ msgid "" +#~ "\n" +#~ " You are not making any payment " +#~ "yet. After submitting your card\n" +#~ " information, you will be taken to " +#~ "the Confirm Order Page.\n" +#~ " " +#~ msgstr "" +#~ "\n" +#~ "Es wird noch keine Bezahlung vorgenommen. Nach der Eingabe Deiner " +#~ "Kreditkateninformationen wirst du auf die Bestellbestätigungsseite " +#~ "weitergeleitet." + #~ msgid "Ipv4" #~ msgstr "IPv4" @@ -597,9 +631,6 @@ msgstr "" #~ msgid "Generate Key Pair" #~ msgstr "Schlüsselpaar generieren" -#~ msgid "Created at" -#~ msgstr "Erstellt am" - #~ msgid "Billing Amount" #~ msgstr "Rechnungsbetrag" From 27a233ffc41e9d9882fc03b1e129dbe069bfb315 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Wed, 23 Aug 2017 18:14:33 +0530 Subject: [PATCH 116/189] translations added --- hosting/locale/de/LC_MESSAGES/django.po | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/hosting/locale/de/LC_MESSAGES/django.po b/hosting/locale/de/LC_MESSAGES/django.po index 54ddb6a8..0206e8b1 100644 --- a/hosting/locale/de/LC_MESSAGES/django.po +++ b/hosting/locale/de/LC_MESSAGES/django.po @@ -173,25 +173,25 @@ msgid "Start VM" msgstr "VM jetzt starten" msgid "My Dashboard" -msgstr "" +msgstr "Mein Dashboard" msgid "Create VM" -msgstr "" +msgstr "VM erstellen" msgid "My VMs" -msgstr "" +msgstr "Meine VMs" msgid "My SSH Keys" -msgstr "" +msgstr "Meine SSH Keys" msgid "My Bills" -msgstr "" +msgstr "Meine Rechnungen" msgid "My Settings" -msgstr "" +msgstr "Meine Einstellungen" msgid "Support / Contact" -msgstr "" +msgstr "Support / Kontakt" #, python-format msgid "" From 0cac35831af08d4fabf3d0f9c541ac84275ce71a Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Wed, 23 Aug 2017 19:22:59 +0530 Subject: [PATCH 117/189] code cleanup, translations fix --- hosting/locale/de/LC_MESSAGES/django.po | 18 +- .../hosting/virtual_machine_detail.html | 192 +----------------- 2 files changed, 4 insertions(+), 206 deletions(-) diff --git a/hosting/locale/de/LC_MESSAGES/django.po b/hosting/locale/de/LC_MESSAGES/django.po index 95963e34..7476b255 100644 --- a/hosting/locale/de/LC_MESSAGES/django.po +++ b/hosting/locale/de/LC_MESSAGES/django.po @@ -407,15 +407,11 @@ msgstr "Public SSH Key" msgid "Download" msgstr "" -#, fuzzy -#| msgid "Virtual Machines" msgid "Your Virtual Machine Detail" msgstr "Virtuelle Maschinen Detail" -#, fuzzy -#| msgid "Settings" msgid "VM Settings" -msgstr "Einstellungen" +msgstr "VM Einstellungen" msgid "Copied" msgstr "Kopiert" @@ -426,25 +422,17 @@ msgstr "Festplatte" msgid "Billing" msgstr "Abrechnungen" -#, fuzzy -#| msgid "Current pricing" msgid "Current Pricing" msgstr "Aktueller Preis" -#, fuzzy -#| msgid "CHF/Month" msgid "Month" -msgstr "CHF/Monat" +msgstr "Monat" -#, fuzzy -#| msgid "Invoice" msgid "See Invoice" msgstr "Rechnung" -#, fuzzy -#| msgid "Your SSH Keys" msgid "Your VM is" -msgstr "Deine SSH Keys" +msgstr "Deine VM ist" msgid "Pending" msgstr "In Vorbereitung" diff --git a/hosting/templates/hosting/virtual_machine_detail.html b/hosting/templates/hosting/virtual_machine_detail.html index dafb683a..ad8c3ae4 100644 --- a/hosting/templates/hosting/virtual_machine_detail.html +++ b/hosting/templates/hosting/virtual_machine_detail.html @@ -11,7 +11,7 @@
    {% endif %}
    -

    {% trans "Your Virtual Machine Detail" %}

    +

    {% trans "Your Virtual Machine Detail" %}

    {% trans "VM Settings" %}

    @@ -106,194 +106,4 @@
    -{% comment %} -
    -
    -
    -
    -
    -

    {{virtual_machine.name}}

    -
    - - -
    - -
    -
    -
    -
    -

    {{virtual_machine.hosting_company_name}}

    - - {% if virtual_machine.ipv6 %} -
    - - -
    - {% else %} - -
    - {% trans "Ip not assigned yet"%} - -
    - - {% endif %} - -
    - -
    -
    -
    -
    -
    -
    -
    - - {% trans "Cores"%} - {{virtual_machine.cores}} -
    -
    -
    -
    - {% trans "Memory"%}
    - {{virtual_machine.memory}} GB -
    -
    -
    -
    - - {% trans "Disk"%} - {{virtual_machine.disk_size|floatformat:2}} GB -
    -
    -
    -
    -
    -
    -
    - {% trans "Configuration"%}: {{virtual_machine.configuration}} -
    -
    - - -
    -
    - -
    -
    -

    {% trans "Current pricing"%}

    - {{virtual_machine.price|floatformat}} CHF/month -
    -
    -
    -
    -
    -
    -
    -

    {% trans "Current status"%}

    - -
    - {% if virtual_machine.state == 'PENDING' %} - Pending - {% elif virtual_machine.state == 'ACTIVE' %} - Online - {% elif virtual_machine.state == 'FAILED'%} - Failed - {% endif %} -
    -
    -
    - {% if not virtual_machine.status == 'canceled' %} -
    -
    -
    -
    - {% csrf_token %} -
    - - - -
    - -
    -
    -
    - {% if messages %} -
    - {% for message in messages %} - {{ message }} - {% endfor %} -
    - {% endif %} -
    - - - - -
    - {% endif %} -
    -
    -
    - -
    -
    -
    - -
    -
    - -
    -{% endcomment %} {%endblock%} From e15cf7e16460c95d534544d4f4c57a4d44a83d4c Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Wed, 23 Aug 2017 19:35:23 +0530 Subject: [PATCH 118/189] translation fix --- hosting/locale/de/LC_MESSAGES/django.po | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hosting/locale/de/LC_MESSAGES/django.po b/hosting/locale/de/LC_MESSAGES/django.po index 7476b255..2aa516d5 100644 --- a/hosting/locale/de/LC_MESSAGES/django.po +++ b/hosting/locale/de/LC_MESSAGES/django.po @@ -443,10 +443,8 @@ msgstr "" msgid "Failed" msgstr "Fehlgeschlagen" -#, fuzzy -#| msgid "Create VM" msgid "Terminate VM" -msgstr "Neue VM" +msgstr "VM Beenden" msgid "Support / Contact" msgstr "Support / Kontakt" From 4c23d2bd30c27992e3edacec05995cb8af639f37 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Wed, 23 Aug 2017 19:42:49 +0530 Subject: [PATCH 119/189] contact box, line break --- hosting/static/hosting/css/virtual-machine.css | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/hosting/static/hosting/css/virtual-machine.css b/hosting/static/hosting/css/virtual-machine.css index 0fbc0544..2d75f3bd 100644 --- a/hosting/static/hosting/css/virtual-machine.css +++ b/hosting/static/hosting/css/virtual-machine.css @@ -345,6 +345,17 @@ } .vm-contact-us div { padding: 0 15px; + position: relative; + } + .vm-contact-us div img { + position: absolute; + left: 0; + top: 3px; + } + .vm-contact-us div span { + display: block; + padding-left: 25px; + text-align: left; } } From e912acd3ca35a8453ce113b05cf796eca27ffa88 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Wed, 23 Aug 2017 21:04:14 +0530 Subject: [PATCH 120/189] increase max-width for page contents --- hosting/static/hosting/css/virtual-machine.css | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hosting/static/hosting/css/virtual-machine.css b/hosting/static/hosting/css/virtual-machine.css index 2d75f3bd..7bcc34e8 100644 --- a/hosting/static/hosting/css/virtual-machine.css +++ b/hosting/static/hosting/css/virtual-machine.css @@ -1,3 +1,6 @@ +.virtual-machine-container { + max-width: 820px; +} .virtual-machine-container .tabs-left, .virtual-machine-container .tabs-right { border-bottom: none; padding-top: 2px; @@ -233,7 +236,7 @@ .vm-detail-item, .vm-contact-us { overflow: hidden; - border: 1px solid #ddd; + border: 1px solid #ccc; padding: 15px; color: #555; font-weight: 300; From 9257f7373913d1acfadb956520015aef9e4e926f Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Wed, 23 Aug 2017 21:07:35 +0530 Subject: [PATCH 121/189] ipv6 line break --- hosting/static/hosting/css/virtual-machine.css | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/hosting/static/hosting/css/virtual-machine.css b/hosting/static/hosting/css/virtual-machine.css index 7bcc34e8..db195c1a 100644 --- a/hosting/static/hosting/css/virtual-machine.css +++ b/hosting/static/hosting/css/virtual-machine.css @@ -362,11 +362,12 @@ } } +.value-sm-block { + display: block; + padding-top: 2px; +} + @media(max-width: 767px) { - .value-sm-block { - display: block; - padding-top: 2px; - } .vm-contact-us div { margin-bottom: 30px; } From 67362b5466719dd1c374fd92034288eea2e5843d Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Wed, 23 Aug 2017 21:09:56 +0530 Subject: [PATCH 122/189] increase width --- 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 db195c1a..f20d1dc2 100644 --- a/hosting/static/hosting/css/virtual-machine.css +++ b/hosting/static/hosting/css/virtual-machine.css @@ -1,5 +1,5 @@ .virtual-machine-container { - max-width: 820px; + max-width: 850px; } .virtual-machine-container .tabs-left, .virtual-machine-container .tabs-right { border-bottom: none; From e50baf50e8ef7e4b68a8faaffda285a01b8bcdbf Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Wed, 23 Aug 2017 21:51:56 +0530 Subject: [PATCH 123/189] increased padding --- hosting/static/hosting/css/virtual-machine.css | 12 +++--------- .../templates/hosting/virtual_machine_detail.html | 5 ++++- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/hosting/static/hosting/css/virtual-machine.css b/hosting/static/hosting/css/virtual-machine.css index f20d1dc2..d1ea5d18 100644 --- a/hosting/static/hosting/css/virtual-machine.css +++ b/hosting/static/hosting/css/virtual-machine.css @@ -350,15 +350,9 @@ padding: 0 15px; position: relative; } - .vm-contact-us div img { - position: absolute; - left: 0; - top: 3px; - } - .vm-contact-us div span { - display: block; - padding-left: 25px; - text-align: left; + .vm-contact-us-text { + display: flex; + align-items: center; } } diff --git a/hosting/templates/hosting/virtual_machine_detail.html b/hosting/templates/hosting/virtual_machine_detail.html index ad8c3ae4..59a36bdc 100644 --- a/hosting/templates/hosting/virtual_machine_detail.html +++ b/hosting/templates/hosting/virtual_machine_detail.html @@ -74,7 +74,10 @@

    {% trans "Support / Contact" %}

    - {% trans "Something doesn't work?" %} {% trans "We are here to help you!" %} + +
    + {% trans "Something doesn't work?" %} {% trans "We are here to help you!" %} +
    {% trans "CONTACT" %} From 302f17a3d315638d729fc61b42ac40348b786ef9 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Wed, 23 Aug 2017 21:52:23 +0530 Subject: [PATCH 124/189] increased 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 d1ea5d18..5f008511 100644 --- a/hosting/static/hosting/css/virtual-machine.css +++ b/hosting/static/hosting/css/virtual-machine.css @@ -1,5 +1,5 @@ .virtual-machine-container { - max-width: 850px; + max-width: 900px; } .virtual-machine-container .tabs-left, .virtual-machine-container .tabs-right { border-bottom: none; From e6e0bea9b4155267e10de492c1a94bfef6b164d0 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Wed, 23 Aug 2017 21:55:29 +0530 Subject: [PATCH 125/189] increased padding --- hosting/static/hosting/css/virtual-machine.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hosting/static/hosting/css/virtual-machine.css b/hosting/static/hosting/css/virtual-machine.css index 5f008511..c6cc379b 100644 --- a/hosting/static/hosting/css/virtual-machine.css +++ b/hosting/static/hosting/css/virtual-machine.css @@ -326,12 +326,12 @@ @media(min-width: 768px) { .vm-detail-contain { display: flex; - margin-left: -10px; - margin-right: -10px; + margin-left: -15px; + margin-right: -15px; } .vm-detail-item { width: 33.333333%; - margin: 0 10px; + margin: 0 15px; } .vm-contact-us { display: flex; From 788621bd2bde8ca1dc1468b42ac18ac9c52127ca Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Wed, 23 Aug 2017 23:07:40 +0530 Subject: [PATCH 126/189] test markup removed --- .../static/hosting/css/virtual-machine.css | 2 +- hosting/templates/hosting/orders.html | 31 +++++++++---------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/hosting/static/hosting/css/virtual-machine.css b/hosting/static/hosting/css/virtual-machine.css index ee7cb2a3..d527cff4 100644 --- a/hosting/static/hosting/css/virtual-machine.css +++ b/hosting/static/hosting/css/virtual-machine.css @@ -296,7 +296,7 @@ border-radius: 3px; border: 2px solid #87B6EA; padding: 4px 20px; - width: 125px; + min-width: 155px; /* padding-bottom: 7px; */ } diff --git a/hosting/templates/hosting/orders.html b/hosting/templates/hosting/orders.html index 97164dfe..584babab 100644 --- a/hosting/templates/hosting/orders.html +++ b/hosting/templates/hosting/orders.html @@ -27,23 +27,22 @@ - - {{ order.id }} - {{ order.created_at | date:"M d, Y" }} - {{ order.price }} - - {% if order.approved %} - Approved - {% else %} - Declined - {% endif %} - - - {% trans "View Detail" %} - - {% for order in orders %} - + + {{ order.id }} + {{ order.created_at | date:"M d, Y" }} + {{ order.price }} + + {% if order.approved %} + Approved + {% else %} + Declined + {% endif %} + + + {% trans "View Detail" %} + + {% endfor %} From 78c99310022f714076df44ffef3811d2ad693d4c Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Wed, 23 Aug 2017 23:31:06 +0530 Subject: [PATCH 127/189] mobile view buttons shifted below --- 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 d527cff4..0ccdc589 100644 --- a/hosting/static/hosting/css/virtual-machine.css +++ b/hosting/static/hosting/css/virtual-machine.css @@ -395,7 +395,7 @@ } .table-switch .last-td { position: absolute; - bottom: 20px; + bottom: 18px; right: 0; } .table-switch tbody tr .xs-td-inline { From 82b73df6e73bb620970ca01b367ceea7e37f2d2c Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Wed, 23 Aug 2017 20:12:50 +0200 Subject: [PATCH 128/189] Added test case to check whether celery task executes properly on production. --- datacenterlight/tasks.py | 29 +++++---- datacenterlight/tests.py | 117 ++++++++++++++++++++++++++++++++++++ dynamicweb/settings/base.py | 12 ++-- 3 files changed, 141 insertions(+), 17 deletions(-) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index b897c54a..a09fb7c7 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -1,15 +1,17 @@ -from dynamicweb.celery import app +from datetime import datetime + +from celery.exceptions import MaxRetriesExceededError from celery.utils.log import get_task_logger from django.conf import settings +from django.core.mail import EmailMessage + +from dynamicweb.celery import app +from hosting.models import HostingOrder, HostingBill +from membership.models import StripeCustomer from opennebula_api.models import OpenNebulaManager from opennebula_api.serializers import VirtualMachineSerializer -from hosting.models import HostingOrder, HostingBill from utils.forms import UserBillingAddressForm -from datetime import datetime -from membership.models import StripeCustomer -from django.core.mail import EmailMessage from utils.models import BillingAddress -from celery.exceptions import MaxRetriesExceededError logger = get_task_logger(__name__) @@ -41,13 +43,15 @@ def retry_task(task, exception=None): @app.task(bind=True, max_retries=settings.CELERY_MAX_RETRIES) -def create_vm_task(self, vm_template_id, user, specs, template, stripe_customer_id, billing_address_data, +def create_vm_task(self, vm_template_id, user, specs, template, + stripe_customer_id, billing_address_data, billing_address_id, charge): vm_id = None try: final_price = specs.get('price') - billing_address = BillingAddress.objects.filter(id=billing_address_id).first() + billing_address = BillingAddress.objects.filter( + id=billing_address_id).first() customer = StripeCustomer.objects.filter(id=stripe_customer_id).first() # Create OpenNebulaManager manager = OpenNebulaManager(email=settings.OPENNEBULA_USERNAME, @@ -114,7 +118,8 @@ def create_vm_task(self, vm_template_id, user, specs, template, stripe_customer_ 'subject': settings.DCL_TEXT + " Order from %s" % context['email'], 'from_email': settings.DCL_SUPPORT_FROM_ADDRESS, 'to': ['info@ungleich.ch'], - 'body': "\n".join(["%s=%s" % (k, v) for (k, v) in context.items()]), + 'body': "\n".join( + ["%s=%s" % (k, v) for (k, v) in context.items()]), 'reply_to': [context['email']], } email = EmailMessage(**email_data) @@ -124,11 +129,13 @@ def create_vm_task(self, vm_template_id, user, specs, template, stripe_customer_ try: retry_task(self) except MaxRetriesExceededError: - msg_text = 'Finished {} retries for create_vm_task'.format(self.request.retries) + msg_text = 'Finished {} retries for create_vm_task'.format( + self.request.retries) logger.error(msg_text) # Try sending email and stop email_data = { - 'subject': '{} CELERY TASK ERROR: {}'.format(settings.DCL_TEXT, msg_text), + 'subject': '{} CELERY TASK ERROR: {}'.format(settings.DCL_TEXT, + msg_text), 'from_email': settings.DCL_SUPPORT_FROM_ADDRESS, 'to': ['info@ungleich.ch'], 'body': ',\n'.join(str(i) for i in self.request.args) diff --git a/datacenterlight/tests.py b/datacenterlight/tests.py index a79ca8be..0d95be79 100644 --- a/datacenterlight/tests.py +++ b/datacenterlight/tests.py @@ -1,3 +1,120 @@ # from django.test import TestCase +from time import sleep + +import stripe +from celery.result import AsyncResult +from django.conf import settings +from django.core.management import call_command # Create your tests here. +from django.test import TestCase, override_settings +from model_mommy import mommy + +from datacenterlight.models import VMTemplate +from datacenterlight.tasks import create_vm_task +from membership.models import StripeCustomer +from opennebula_api.serializers import VMTemplateSerializer +from utils.models import BillingAddress +from utils.stripe_utils import StripeUtils + + +class CeleryTaskTestCase(TestCase): + @override_settings( + task_eager_propagates=True, + task_always_eager=True, + ) + def setUp(self): + self.customer_password = 'test_password' + self.customer_email = 'celery-createvm-task-test@ungleich.ch' + self.customer_name = "Monty Python" + self.user = { + 'email': self.customer_email, + 'name': self.customer_name + } + self.customer = mommy.make('membership.CustomUser') + self.customer.set_password(self.customer_password) + self.customer.email = self.customer_email + self.customer.save() + self.stripe_utils = StripeUtils() + stripe.api_key = settings.STRIPE_API_PRIVATE_KEY_TEST + self.token = stripe.Token.create( + card={ + "number": '4111111111111111', + "exp_month": 12, + "exp_year": 2022, + "cvc": '123' + }, + ) + # Run fetchvmtemplates so that we have the VM templates from + # OpenNebula + call_command('fetchvmtemplates') + + def test_create_vm_task(self): + """Tests the create vm task.""" + + # We create a VM from the first template available to DCL + vm_template = VMTemplate.objects.all().first() + template_data = VMTemplateSerializer(vm_template).data + + # The specs of VM that we want to create + specs = { + 'cpu': 1, + 'memory': 2, + 'disk_size': 10, + 'price': 15, + } + + stripe_customer = StripeCustomer.get_or_create( + email=self.customer_email, + token=self.token) + billing_address = BillingAddress( + cardholder_name=self.customer_name, + postal_code='1232', + country='CH', + street_address='Monty\'s Street', + city='Hollywood') + billing_address.save() + billing_address_data = {'cardholder_name': self.customer_name, + 'postal_code': '1231', + 'country': 'CH', + 'token': self.token, + 'street_address': 'Monty\'s Street', + 'city': 'Hollywood'} + + billing_address_id = billing_address.id + vm_template_id = template_data.get('id', 1) + final_price = specs.get('price') + + # Make stripe charge to a customer + stripe_utils = StripeUtils() + charge_response = stripe_utils.make_charge( + amount=final_price, + customer=stripe_customer.stripe_id) + + # Check if the payment was approved + if not charge_response.get( + 'response_object') and not charge_response.get('paid'): + msg = charge_response.get('error') + raise Exception("make_charge failed: {}".format(msg)) + + charge = charge_response.get('response_object') + async_task = create_vm_task.delay(vm_template_id, self.user, + specs, + template_data, + stripe_customer.id, + billing_address_data, + billing_address_id, + charge) + new_vm_id = 0 + res = None + for i in range(0, 10): + sleep(5) + res = AsyncResult(async_task.task_id) + if res.result is not None and res.result > 0: + new_vm_id = res.result + break + + # We expect a VM to be created within 50 seconds + self.assertGreater(new_vm_id, 0, + "VM could not be created. res._get_task_meta() = {}" + .format(res._get_task_meta())) diff --git a/dynamicweb/settings/base.py b/dynamicweb/settings/base.py index a724b38e..383ff9ff 100644 --- a/dynamicweb/settings/base.py +++ b/dynamicweb/settings/base.py @@ -2,15 +2,14 @@ Copyright 2015 ungleich. """ -# -*- coding: utf-8 -*- -# Build paths inside the project like this: os.path.join(BASE_DIR, ...) -import os - -from django.utils.translation import ugettext_lazy as _ +import logging # dotenv import dotenv -import logging +# -*- coding: utf-8 -*- +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +import os +from django.utils.translation import ugettext_lazy as _ logger = logging.getLogger(__name__) @@ -493,6 +492,7 @@ REGISTRATION_MESSAGE = {'subject': "Validation mail", } STRIPE_API_PRIVATE_KEY = env('STRIPE_API_PRIVATE_KEY') STRIPE_API_PUBLIC_KEY = env('STRIPE_API_PUBLIC_KEY') +STRIPE_API_PRIVATE_KEY_TEST = env('STRIPE_API_PRIVATE_KEY_TEST') ANONYMOUS_USER_NAME = 'anonymous@ungleich.ch' GUARDIAN_GET_INIT_ANONYMOUS_USER = 'membership.models.get_anonymous_user_instance' From 0846718893de4a82a590679d42033aae51e31e10 Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Wed, 23 Aug 2017 20:16:22 +0200 Subject: [PATCH 129/189] Rearranged imports --- datacenterlight/tasks.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index a09fb7c7..f036a461 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -1,17 +1,15 @@ -from datetime import datetime - -from celery.exceptions import MaxRetriesExceededError +from dynamicweb.celery import app from celery.utils.log import get_task_logger from django.conf import settings -from django.core.mail import EmailMessage - -from dynamicweb.celery import app -from hosting.models import HostingOrder, HostingBill -from membership.models import StripeCustomer from opennebula_api.models import OpenNebulaManager from opennebula_api.serializers import VirtualMachineSerializer +from hosting.models import HostingOrder, HostingBill from utils.forms import UserBillingAddressForm +from datetime import datetime +from membership.models import StripeCustomer +from django.core.mail import EmailMessage from utils.models import BillingAddress +from celery.exceptions import MaxRetriesExceededError logger = get_task_logger(__name__) From 4179d622aa95f8a5ea707ce07364ac4934289e64 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Wed, 23 Aug 2017 23:46:59 +0530 Subject: [PATCH 130/189] changed border color for error input --- hosting/static/hosting/css/landing-page.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/static/hosting/css/landing-page.css b/hosting/static/hosting/css/landing-page.css index 7a569dc8..c5ef9489 100644 --- a/hosting/static/hosting/css/landing-page.css +++ b/hosting/static/hosting/css/landing-page.css @@ -760,7 +760,6 @@ a.unlink:hover { .has-error.checkbox label, .has-error.radio-inline label, .has-error.checkbox-inline label, -.has-error .form-control, .has-error .form-control-feedback, .alert-danger, .list-group-item-danger, @@ -770,6 +769,7 @@ a.list-group-item-danger:focus, .panel-danger > .panel-heading { color: #eb4d5c; } +.has-error .form-control, .has-error .input-group-addon { color: #eb4d5c; border-color: #eb4d5c; From 4cc2e09b0a8a063b0972d802a62383e522bc74dd Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Wed, 23 Aug 2017 20:18:46 +0200 Subject: [PATCH 131/189] Rearranged imports in base.py --- dynamicweb/settings/base.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/dynamicweb/settings/base.py b/dynamicweb/settings/base.py index 383ff9ff..7ec18a8b 100644 --- a/dynamicweb/settings/base.py +++ b/dynamicweb/settings/base.py @@ -2,15 +2,16 @@ Copyright 2015 ungleich. """ -import logging - -# dotenv -import dotenv # -*- coding: utf-8 -*- # Build paths inside the project like this: os.path.join(BASE_DIR, ...) import os + from django.utils.translation import ugettext_lazy as _ +# dotenv +import dotenv +import logging + logger = logging.getLogger(__name__) From a2dd55cf16ebd7f8ecfddc251d9424c54de6528d Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Wed, 23 Aug 2017 23:49:29 +0530 Subject: [PATCH 132/189] padding adjusted --- hosting/static/hosting/css/virtual-machine.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hosting/static/hosting/css/virtual-machine.css b/hosting/static/hosting/css/virtual-machine.css index 0ccdc589..be5addf1 100644 --- a/hosting/static/hosting/css/virtual-machine.css +++ b/hosting/static/hosting/css/virtual-machine.css @@ -373,8 +373,8 @@ position: relative; border-top: 1px solid #ddd; /* margin-top: 15px; */ - padding-top: 5px; - padding-bottom: 15px; + padding-top: 10px; + padding-bottom: 13px; } .table-switch tbody tr:last-child { border-bottom: 1px solid #ddd; @@ -395,7 +395,7 @@ } .table-switch .last-td { position: absolute; - bottom: 18px; + bottom: 13px; right: 0; } .table-switch tbody tr .xs-td-inline { From 17be01370b5b22c19685a7b9d6a61a27b213b91e Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Wed, 23 Aug 2017 23:52:09 +0530 Subject: [PATCH 133/189] removed test url --- hosting/urls.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/hosting/urls.py b/hosting/urls.py index c5189940..23709904 100644 --- a/hosting/urls.py +++ b/hosting/urls.py @@ -1,6 +1,5 @@ from django.conf.urls import url from django.contrib.auth import views as auth_views -from django.views.generic import TemplateView from .views import DjangoHostingView, RailsHostingView, PaymentVMView,\ NodeJSHostingView, LoginView, SignupView, SignupValidateView, SignupValidatedView, IndexView, \ @@ -11,7 +10,6 @@ from .views import DjangoHostingView, RailsHostingView, PaymentVMView,\ SSHKeyDeleteView, SSHKeyCreateView, SSHKeyListView, SSHKeyChoiceView urlpatterns = [ - # url(r'test/?$', TemplateView.as_view(template_name='hosting/virtual_machine_detail.html')), url(r'index/?$', IndexView.as_view(), name='index'), url(r'django/?$', DjangoHostingView.as_view(), name='djangohosting'), url(r'nodejs/?$', NodeJSHostingView.as_view(), name='nodejshosting'), From 4cf6df64a8cc2a6c23f66966536fc224a7ae8db6 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Wed, 23 Aug 2017 23:59:18 +0530 Subject: [PATCH 134/189] btn link fixed --- 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 584babab..ea4ded94 100644 --- a/hosting/templates/hosting/orders.html +++ b/hosting/templates/hosting/orders.html @@ -40,7 +40,7 @@ {% endif %} - {% trans "View Detail" %} + {% trans "View Detail" %} {% endfor %} From f1f166b7985fd9f2af7218c8aa42cd4300fc0eb9 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Thu, 24 Aug 2017 00:13:03 +0530 Subject: [PATCH 135/189] Ihre virtuelle Maschine beenden -> Deine Virtuelle Maschine beenden --- hosting/locale/de/LC_MESSAGES/django.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/locale/de/LC_MESSAGES/django.po b/hosting/locale/de/LC_MESSAGES/django.po index 2aa516d5..e7619bbc 100644 --- a/hosting/locale/de/LC_MESSAGES/django.po +++ b/hosting/locale/de/LC_MESSAGES/django.po @@ -462,7 +462,7 @@ msgid "BACK TO LIST" msgstr "ZURÜCK ZUR LISTE" msgid "Terminate your Virtual Machine" -msgstr "Ihre virtuelle Maschine beenden" +msgstr "Deine Virtuelle Maschine beenden" msgid "Do you want to cancel your Virtual Machine" msgstr "Bist Du sicher, dass Du Deine virtuelle Maschine beenden willst" From fc0ed904472ce0aeeb8e6406f8aa0d944ca87377 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Thu, 24 Aug 2017 00:39:47 +0530 Subject: [PATCH 136/189] background and margin fix --- hosting/static/hosting/css/dashboard.css | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/hosting/static/hosting/css/dashboard.css b/hosting/static/hosting/css/dashboard.css index 7a80e162..ac5634c0 100644 --- a/hosting/static/hosting/css/dashboard.css +++ b/hosting/static/hosting/css/dashboard.css @@ -1,6 +1,6 @@ .hosting-dashboard:after { content: ''; - position: absolute; + position: fixed; top: 0; bottom: 0; left: 0; @@ -10,7 +10,7 @@ } .hosting-dashboard:before { content: ''; - position: absolute; + position: fixed; top: 0; bottom: 0; left: 0; @@ -18,6 +18,7 @@ background: url(../../datacenterlight/img/pattern.jpg) no-repeat center center; background-size: cover; z-index: -2; + height: 100%; } .hosting-dashboard .dashboard-container-head { @@ -31,6 +32,8 @@ padding: 25px; color: rgba(124, 139, 175, 0.7); font-size: 19px; + display: block; + margin-bottom: 15px; } .hosting-dashboard-item:hover, .hosting-dashboard-item:focus, @@ -72,6 +75,5 @@ } .hosting-dashboard-item { width: 32%; - margin-bottom: 15px; } } \ No newline at end of file From 04d7a2b076f30866136763119463041ec254cfd8 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Thu, 24 Aug 2017 00:48:07 +0530 Subject: [PATCH 137/189] removed fuzzy --- hosting/locale/de/LC_MESSAGES/django.po | 2 -- 1 file changed, 2 deletions(-) diff --git a/hosting/locale/de/LC_MESSAGES/django.po b/hosting/locale/de/LC_MESSAGES/django.po index 94a6c028..49a05548 100644 --- a/hosting/locale/de/LC_MESSAGES/django.po +++ b/hosting/locale/de/LC_MESSAGES/django.po @@ -461,8 +461,6 @@ msgstr "Du kannst dich nun" msgid "Sorry. Your request is invalid." msgstr "Entschuldigung, deine Anfrage ist ungültig." -#, fuzzy -#| msgid "Credit Card" msgid "Invalid credit card" msgstr "Ungültige Kreditkarte" From 738e4fc93f8d91b61dd2b224071fec540532e7a7 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Thu, 24 Aug 2017 01:07:20 +0530 Subject: [PATCH 138/189] removed duplicate for "See Invoice" --- hosting/locale/de/LC_MESSAGES/django.po | 3 --- 1 file changed, 3 deletions(-) diff --git a/hosting/locale/de/LC_MESSAGES/django.po b/hosting/locale/de/LC_MESSAGES/django.po index 155a840a..6f0ec86b 100644 --- a/hosting/locale/de/LC_MESSAGES/django.po +++ b/hosting/locale/de/LC_MESSAGES/django.po @@ -254,9 +254,6 @@ msgstr "Betrag" msgid "Status" msgstr "" -msgid "See Invoice" -msgstr "Rechnung" - msgid "View Detail" msgstr "Details anzeigen" From 4d18dc6f7c70c3d263334511c20e88f6565b62e7 Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 24 Aug 2017 01:20:32 +0530 Subject: [PATCH 139/189] Corrected/added missing google analytics and reformated code --- dynamicweb/settings/base.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/dynamicweb/settings/base.py b/dynamicweb/settings/base.py index a724b38e..25950775 100644 --- a/dynamicweb/settings/base.py +++ b/dynamicweb/settings/base.py @@ -37,8 +37,10 @@ def int_env(val, default_value=0): try: return_value = int(os.environ.get(val)) except Exception as e: - logger.error("Encountered exception trying to get env value for {}\nException details: {}".format( - val, str(e))) + logger.error( + ("Encountered exception trying to get env value for {}\nException " + "details: {}").format( + val, str(e))) return return_value @@ -169,10 +171,12 @@ TEMPLATES = [ os.path.join(PROJECT_DIR, 'membership'), os.path.join(PROJECT_DIR, 'hosting/templates/'), os.path.join(PROJECT_DIR, 'nosystemd/templates/'), - os.path.join(PROJECT_DIR, 'ungleich/templates/djangocms_blog/'), + os.path.join(PROJECT_DIR, + 'ungleich/templates/djangocms_blog/'), os.path.join(PROJECT_DIR, 'ungleich/templates/cms/ungleichch'), os.path.join(PROJECT_DIR, 'ungleich/templates/ungleich'), - os.path.join(PROJECT_DIR, 'ungleich_page/templates/ungleich_page'), + os.path.join(PROJECT_DIR, + 'ungleich_page/templates/ungleich_page'), os.path.join(PROJECT_DIR, 'templates/analytics'), ], 'APP_DIRS': True, @@ -535,9 +539,12 @@ GOOGLE_ANALYTICS_PROPERTY_IDS = { 'ungleich.ch': 'UA-62285904-1', 'digitalglarus.ch': 'UA-62285904-2', 'blog.ungleich.ch': 'UA-62285904-4', - 'hosting': 'UA-62285904-5', - 'datacenterlight.ch': 'UA-62285904-9', - + 'rails-hosting.ch': 'UA-62285904-5', + 'django-hosting.ch': 'UA-62285904-6', + 'node-hosting.ch': 'UA-62285904-7', + 'datacenterlight.ch': 'UA-62285904-8', + 'devuanhosting.ch': 'UA-62285904-9', + 'ipv6onlyhosting.ch': 'UA-62285904-10', '127.0.0.1:8000': 'localhost', 'dynamicweb-development.ungleich.ch': 'development', 'dynamicweb-staging.ungleich.ch': 'staging' @@ -562,7 +569,8 @@ if ENABLE_DEBUG_LOGGING: 'file': { 'level': 'DEBUG', 'class': 'logging.FileHandler', - 'filename': "{PROJECT_DIR}/debug.log".format(PROJECT_DIR=PROJECT_DIR), + 'filename': "{PROJECT_DIR}/debug.log".format( + PROJECT_DIR=PROJECT_DIR), }, }, 'loggers': { From 6484684ac5e44321b33264d362f19a4a7f483c0f Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 24 Aug 2017 02:29:24 +0530 Subject: [PATCH 140/189] Removed unncessary check for error for make_charge --- datacenterlight/tests.py | 2 +- datacenterlight/views.py | 98 ++++++++++++++++++++++++++-------------- hosting/views.py | 2 +- 3 files changed, 67 insertions(+), 35 deletions(-) diff --git a/datacenterlight/tests.py b/datacenterlight/tests.py index 0d95be79..b0768c9a 100644 --- a/datacenterlight/tests.py +++ b/datacenterlight/tests.py @@ -93,7 +93,7 @@ class CeleryTaskTestCase(TestCase): # Check if the payment was approved if not charge_response.get( - 'response_object') and not charge_response.get('paid'): + 'response_object'): msg = charge_response.get('error') raise Exception("make_charge failed: {}".format(msg)) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index db7f2e53..399b7676 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -18,7 +18,8 @@ from hosting.models import HostingOrder from utils.stripe_utils import StripeUtils from membership.models import CustomUser, StripeCustomer from opennebula_api.models import OpenNebulaManager -from opennebula_api.serializers import VirtualMachineTemplateSerializer, VMTemplateSerializer +from opennebula_api.serializers import VirtualMachineTemplateSerializer, \ + VMTemplateSerializer from datacenterlight.tasks import create_vm_task @@ -35,9 +36,11 @@ class SuccessView(TemplateView): elif 'token' not in request.session: return HttpResponseRedirect(reverse('datacenterlight:payment')) elif 'order_confirmation' not in request.session: - return HttpResponseRedirect(reverse('datacenterlight:order_confirmation')) + return HttpResponseRedirect( + reverse('datacenterlight:order_confirmation')) else: - for session_var in ['specs', 'user', 'template', 'billing_address', 'billing_address_data', + for session_var in ['specs', 'user', 'template', 'billing_address', + 'billing_address_data', 'token', 'customer']: if session_var in request.session: del request.session[session_var] @@ -53,7 +56,8 @@ class PricingView(TemplateView): templates = manager.get_templates() context = { - 'templates': VirtualMachineTemplateSerializer(templates, many=True).data, + 'templates': VirtualMachineTemplateSerializer(templates, + many=True).data, } except: messages.error(request, @@ -77,7 +81,8 @@ class PricingView(TemplateView): manager = OpenNebulaManager() template = manager.get_template(template_id) - request.session['template'] = VirtualMachineTemplateSerializer(template).data + request.session['template'] = VirtualMachineTemplateSerializer( + template).data if not request.user.is_authenticated(): request.session['next'] = reverse('hosting:payment') @@ -99,7 +104,8 @@ class BetaAccessView(FormView): def form_valid(self, form): context = { - 'base_url': "{0}://{1}".format(self.request.scheme, self.request.get_host()) + 'base_url': "{0}://{1}".format(self.request.scheme, + self.request.get_host()) } email_data = { @@ -129,7 +135,8 @@ class BetaAccessView(FormView): email = BaseEmail(**email_data) email.send() - messages.add_message(self.request, messages.SUCCESS, self.success_message) + messages.add_message(self.request, messages.SUCCESS, + self.success_message) return render(self.request, 'datacenterlight/beta_success.html', {}) @@ -154,7 +161,8 @@ class BetaProgramView(CreateView): # data = VirtualMachineTemplateSerializer(templates, many=True).data context.update({ - 'base_url': "{0}://{1}".format(self.request.scheme, self.request.get_host()), + 'base_url': "{0}://{1}".format(self.request.scheme, + self.request.get_host()), 'vms': vms }) return context @@ -164,7 +172,8 @@ class BetaProgramView(CreateView): vms = BetaAccessVM.create(data) context = { - 'base_url': "{0}://{1}".format(self.request.scheme, self.request.get_host()), + 'base_url': "{0}://{1}".format(self.request.scheme, + self.request.get_host()), 'email': data.get('email'), 'name': data.get('name'), 'vms': vms @@ -181,7 +190,8 @@ class BetaProgramView(CreateView): email = BaseEmail(**email_data) email.send() - messages.add_message(self.request, messages.SUCCESS, self.success_message) + messages.add_message(self.request, messages.SUCCESS, + self.success_message) return HttpResponseRedirect(self.get_success_url()) @@ -225,7 +235,8 @@ class IndexView(CreateView): storage_field = forms.IntegerField(validators=[self.validate_storage]) price = request.POST.get('total') template_id = int(request.POST.get('config')) - template = VMTemplate.objects.filter(opennebula_vm_template_id=template_id).first() + template = VMTemplate.objects.filter( + opennebula_vm_template_id=template_id).first() template_data = VMTemplateSerializer(template).data name = request.POST.get('name') @@ -237,36 +248,46 @@ class IndexView(CreateView): cores = cores_field.clean(cores) except ValidationError as err: msg = '{} : {}.'.format(cores, str(err)) - messages.add_message(self.request, messages.ERROR, msg, extra_tags='cores') - return HttpResponseRedirect(reverse('datacenterlight:index') + "#order_form") + messages.add_message(self.request, messages.ERROR, msg, + extra_tags='cores') + return HttpResponseRedirect( + reverse('datacenterlight:index') + "#order_form") try: memory = memory_field.clean(memory) except ValidationError as err: msg = '{} : {}.'.format(memory, str(err)) - messages.add_message(self.request, messages.ERROR, msg, extra_tags='memory') - return HttpResponseRedirect(reverse('datacenterlight:index') + "#order_form") + messages.add_message(self.request, messages.ERROR, msg, + extra_tags='memory') + return HttpResponseRedirect( + reverse('datacenterlight:index') + "#order_form") try: storage = storage_field.clean(storage) except ValidationError as err: msg = '{} : {}.'.format(storage, str(err)) - messages.add_message(self.request, messages.ERROR, msg, extra_tags='storage') - return HttpResponseRedirect(reverse('datacenterlight:index') + "#order_form") + messages.add_message(self.request, messages.ERROR, msg, + extra_tags='storage') + return HttpResponseRedirect( + reverse('datacenterlight:index') + "#order_form") try: name = name_field.clean(name) except ValidationError as err: msg = '{} {}.'.format(name, _('is not a proper name')) - messages.add_message(self.request, messages.ERROR, msg, extra_tags='name') - return HttpResponseRedirect(reverse('datacenterlight:index') + "#order_form") + messages.add_message(self.request, messages.ERROR, msg, + extra_tags='name') + return HttpResponseRedirect( + reverse('datacenterlight:index') + "#order_form") try: email = email_field.clean(email) except ValidationError as err: msg = '{} {}.'.format(email, _('is not a proper email')) - messages.add_message(self.request, messages.ERROR, msg, extra_tags='email') - return HttpResponseRedirect(reverse('datacenterlight:index') + "#order_form") + messages.add_message(self.request, messages.ERROR, msg, + extra_tags='email') + return HttpResponseRedirect( + reverse('datacenterlight:index') + "#order_form") specs = { 'cpu': cores, @@ -293,14 +314,16 @@ class IndexView(CreateView): def get_context_data(self, **kwargs): context = super(IndexView, self).get_context_data(**kwargs) context.update({ - 'base_url': "{0}://{1}".format(self.request.scheme, self.request.get_host()) + 'base_url': "{0}://{1}".format(self.request.scheme, + self.request.get_host()) }) return context def form_valid(self, form): context = { - 'base_url': "{0}://{1}".format(self.request.scheme, self.request.get_host()) + 'base_url': "{0}://{1}".format(self.request.scheme, + self.request.get_host()) } email_data = { @@ -330,7 +353,8 @@ class IndexView(CreateView): email = BaseEmail(**email_data) email.send() - messages.add_message(self.request, messages.SUCCESS, self.success_message) + messages.add_message(self.request, messages.SUCCESS, + self.success_message) return super(IndexView, self).form_valid(form) @@ -403,7 +427,8 @@ class PaymentOrderView(FormView): request.session['billing_address'] = billing_address.id request.session['token'] = token request.session['customer'] = customer.id - return HttpResponseRedirect(reverse('datacenterlight:order_confirmation')) + return HttpResponseRedirect( + reverse('datacenterlight:order_confirmation')) else: return self.form_invalid(form) @@ -423,11 +448,15 @@ class OrderConfirmationView(DetailView): stripe_customer_id = request.session.get('customer') customer = StripeCustomer.objects.filter(id=stripe_customer_id).first() stripe_utils = StripeUtils() - card_details = stripe_utils.get_card_details(customer.stripe_id, request.session.get('token')) - if not card_details.get('response_object') and not card_details.get('paid'): + card_details = stripe_utils.get_card_details(customer.stripe_id, + request.session.get( + 'token')) + if not card_details.get('response_object'): msg = card_details.get('error') - messages.add_message(self.request, messages.ERROR, msg, extra_tags='failed_payment') - return HttpResponseRedirect(reverse('datacenterlight:payment') + '#payment_error') + messages.add_message(self.request, messages.ERROR, msg, + extra_tags='failed_payment') + return HttpResponseRedirect( + reverse('datacenterlight:payment') + '#payment_error') context = { 'site_url': reverse('datacenterlight:index'), 'cc_last4': card_details.get('response_object').get('last4'), @@ -452,13 +481,16 @@ class OrderConfirmationView(DetailView): customer=customer.stripe_id) # Check if the payment was approved - if not charge_response.get('response_object') and not charge_response.get('paid'): + if not charge_response.get('response_object'): msg = charge_response.get('error') - messages.add_message(self.request, messages.ERROR, msg, extra_tags='make_charge_error') - return HttpResponseRedirect(reverse('datacenterlight:payment') + '#payment_error') + messages.add_message(self.request, messages.ERROR, msg, + extra_tags='make_charge_error') + return HttpResponseRedirect( + reverse('datacenterlight:payment') + '#payment_error') charge = charge_response.get('response_object') - create_vm_task.delay(vm_template_id, user, specs, template, stripe_customer_id, billing_address_data, + create_vm_task.delay(vm_template_id, user, specs, template, + stripe_customer_id, billing_address_data, billing_address_id, charge) request.session['order_confirmation'] = True diff --git a/hosting/views.py b/hosting/views.py index 2b4c8d21..5218391d 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -569,7 +569,7 @@ class PaymentVMView(LoginRequiredMixin, FormView): customer=customer.stripe_id) # Check if the payment was approved - if not charge_response.get('response_object') and not charge_response.get('paid'): + if not charge_response.get('response_object'): msg = charge_response.get('error') messages.add_message(self.request, messages.ERROR, msg, extra_tags='make_charge_error') return HttpResponseRedirect(reverse('hosting:payment') + '#payment_error') From 703644ced4216da99c9bec379a2716ae66211381 Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 24 Aug 2017 03:17:39 +0530 Subject: [PATCH 141/189] Removed orphaned alt tag --- hosting/templates/hosting/base.html | 1 - 1 file changed, 1 deletion(-) diff --git a/hosting/templates/hosting/base.html b/hosting/templates/hosting/base.html index b485451f..ec57475d 100644 --- a/hosting/templates/hosting/base.html +++ b/hosting/templates/hosting/base.html @@ -38,7 +38,6 @@ {% with 'hosting/img/'|add:hosting|add:'-intro-bg.png' as image_static %} - alt="">