From 04d8c263bf57483ea60e557f6079b8adff4f9f48 Mon Sep 17 00:00:00 2001 From: Tomislav R Date: Thu, 19 May 2016 00:58:28 +0200 Subject: [PATCH 01/66] add delete hosting order --- hosting/templates/hosting/orders.html | 139 ++++++++++++++++---------- hosting/urls.py | 3 +- hosting/views.py | 12 ++- 3 files changed, 100 insertions(+), 54 deletions(-) diff --git a/hosting/templates/hosting/orders.html b/hosting/templates/hosting/orders.html index 99c6464f..2b262de3 100644 --- a/hosting/templates/hosting/orders.html +++ b/hosting/templates/hosting/orders.html @@ -1,64 +1,101 @@ {% extends "hosting/base_short.html" %} {% load staticfiles bootstrap3 %} -{% block content %} +{% block content %} -
-
-
-
- -

My Orders

-
- - - - - - - - - - - {% for order in orders %} - - - - - - - - {% endfor %} - -
#DateAmountStatus
{{order.id}}{{order.created_at}}{{order.VMPlan.price}} CHF{% if order.approved %} - Approved - {% else%} - Declined - {% endif%} - - -
+
+
+
+
+ +

My Orders

+
+ + + + + + + + + + + {% for order in orders %} + + + + + + + + +
#DateAmountStatus
{{ order.id }}{{ order.created_at }}{{ order.VMPlan.price }} CHF{% if order.approved %} + Approved + {% else %} + Declined + {% endif %} + + + +
+ + {% if is_paginated %} + - {% endif %} +
+ {% endif %} -
+
-
-
+
+
-
+ -{%endblock%} \ No newline at end of file +{% endblock %} \ No newline at end of file diff --git a/hosting/urls.py b/hosting/urls.py index bb195b79..d280c904 100644 --- a/hosting/urls.py +++ b/hosting/urls.py @@ -3,7 +3,7 @@ from django.conf.urls import url from .views import DjangoHostingView, RailsHostingView, PaymentVMView, \ NodeJSHostingView, LoginView, SignupView, IndexView, \ OrdersHostingListView, OrdersHostingDetailView, VirtualMachinesPlanListView,\ - VirtualMachineDetailListView + VirtualMachineDetailListView,OrdersHostingDeleteView urlpatterns = [ # url(r'pricing/?$', VMPricingView.as_view(), name='pricing'), @@ -14,6 +14,7 @@ urlpatterns = [ url(r'payment/?$', PaymentVMView.as_view(), name='payment'), url(r'orders/?$', OrdersHostingListView.as_view(), name='orders'), url(r'orders/(?P\d+)/?$', OrdersHostingDetailView.as_view(), name='orders'), + url(r'cancel_order/(?P\d+)/?$',OrdersHostingDeleteView.as_view(),name='delete_order'), url(r'my-virtual-machines/?$', VirtualMachinesPlanListView.as_view(), name='virtual_machines'), url(r'my-virtual-machines/(?P\d+)/?$', VirtualMachineDetailListView.as_view(), name='virtual_machines'), diff --git a/hosting/views.py b/hosting/views.py index b0e76028..4157f936 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -3,7 +3,7 @@ from django.shortcuts import get_object_or_404, render from django.core.urlresolvers import reverse_lazy, reverse from django.contrib.auth.mixins import LoginRequiredMixin -from django.views.generic import View, CreateView, FormView, ListView, DetailView +from django.views.generic import View, CreateView, FormView, ListView, DetailView,DeleteView from django.http import HttpResponseRedirect from django.contrib.auth import authenticate, login from django.conf import settings @@ -124,7 +124,7 @@ class LoginView(FormView): class SignupView(CreateView): template_name = 'hosting/signup.html' form_class = HostingUserSignupForm - moodel = CustomUser + model = CustomUser def get_success_url(self): next_url = self.request.session.get('next', reverse_lazy('hosting:signup')) @@ -235,6 +235,14 @@ class OrdersHostingListView(LoginRequiredMixin, ListView): self.queryset = HostingOrder.objects.filter(customer__user=user) return super(OrdersHostingListView, self).get_queryset() +class OrdersHostingDeleteView(LoginRequiredMixin,DeleteView): + login_url=reverse_lazy('hosting:login') + success_url = reverse_lazy('hosting:orders') + model = HostingOrder + def get_queryset(self): + user = self.request.user + self.queryset = VirtualMachinePlan.objects.active(user) + return super(VirtualMachinesPlanListView, self).get_queryset() class VirtualMachinesPlanListView(LoginRequiredMixin, ListView): template_name = "hosting/virtual_machines.html" From 418c257978d4c3909f66845f73daa2a38b89b0a6 Mon Sep 17 00:00:00 2001 From: Tom Date: Fri, 20 May 2016 15:00:06 +0000 Subject: [PATCH 02/66] delete orders --- dynamicweb/settings/base.py | 38 +++++++++++++++---------------------- hosting/views.py | 4 ---- 2 files changed, 15 insertions(+), 27 deletions(-) diff --git a/dynamicweb/settings/base.py b/dynamicweb/settings/base.py index de339f86..d681e9c9 100644 --- a/dynamicweb/settings/base.py +++ b/dynamicweb/settings/base.py @@ -84,18 +84,18 @@ INSTALLED_APPS = ( 'djangocms_file', 'djangocms_picture', 'djangocms_video', - 'djangocms_flash', - 'djangocms_googlemap', - 'djangocms_inherit', - 'djangocms_link', - 'djangocms_teaser', + # 'djangocms_flash', + # 'djangocms_googlemap', + # 'djangocms_inherit', + # 'djangocms_link', + # 'djangocms_teaser', 'djangocms_page_meta', 'djangocms_text_ckeditor', 'djangocms_admin_style', 'cmsplugin_filer_file', 'cmsplugin_filer_folder', 'cmsplugin_filer_link', - 'cmsplugin_filer_teaser', + # 'cmsplugin_filer_teaser', 'cmsplugin_filer_video', # # blog @@ -103,10 +103,9 @@ INSTALLED_APPS = ( 'reversion', # ungleich 'ungleich', -# 'ungleich_page', + 'ungleich_page', 'hosting', 'digitalglarus', - ) MIDDLEWARE_CLASSES = ( @@ -176,7 +175,7 @@ CMS_TEMPLATES = ( DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', - 'NAME': 'app_new', + 'NAME': 'app', } } @@ -280,12 +279,12 @@ CACHES = { } } -#if LOGIN_URL is None: -# LOGIN_URL = APP_ROOT_ENDPOINT + 'login/' -#if LOGOUT_URL is None: -# LOGOUT_URL = APP_ROOT_ENDPOINT + 'logout/' -#if LOGIN_REDIRECT_URL is None: -# LOGIN_REDIRECT_URL = APP_ROOT_ENDPOINT +if LOGIN_URL is None: + LOGIN_URL = APP_ROOT_ENDPOINT + 'login/' +if LOGOUT_URL is None: + LOGOUT_URL = APP_ROOT_ENDPOINT + 'logout/' +if LOGIN_REDIRECT_URL is None: + LOGIN_REDIRECT_URL = APP_ROOT_ENDPOINT # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.7/howto/static-files/ @@ -302,7 +301,7 @@ META_SITE_PROTOCOL = 'http' META_USE_SITES = True MIGRATION_MODULES = { 'cms': 'cms.migrations', - #'filer': 'filer.migrations_django', + # 'filer': 'filer.migrations_django', # 'menus': 'menus.migrations_django', 'djangocms_flash': 'djangocms_flash.migrations_django', 'djangocms_googlemap': 'djangocms_googlemap.migrations_django', @@ -337,7 +336,6 @@ STATICFILES_FINDERS = ( THUMBNAIL_PROCESSORS = ( 'easy_thumbnails.processors.colorspace', 'easy_thumbnails.processors.autocrop', - #'easy_thumbnails.processors.scale_and_crop', 'filer.thumbnail_processors.scale_and_crop_with_subject_location', 'easy_thumbnails.processors.filters', ) @@ -443,12 +441,6 @@ PARLER_LANGUAGES = {1: ({'code': 'en-us'}, {'code': 'de'},)} AUTH_USER_MODEL = 'membership.CustomUser' - -ALLOWED_HOSTS = [ - "*" - ] - - # PAYMENT STRIPE_DESCRIPTION_ON_PAYMENT = "Payment for ungleich GmbH services" diff --git a/hosting/views.py b/hosting/views.py index b4441a54..fbeb36ea 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -244,10 +244,6 @@ class OrdersHostingDeleteView(LoginRequiredMixin,DeleteView): login_url=reverse_lazy('hosting:login') success_url = reverse_lazy('hosting:orders') model = HostingOrder - def get_queryset(self): - user = self.request.user - self.queryset = VirtualMachinePlan.objects.active(user) - return super(VirtualMachinesPlanListView, self).get_queryset() class VirtualMachinesPlanListView(LoginRequiredMixin, ListView): template_name = "hosting/virtual_machines.html" From 5bdcc5d2b2e3f035dc68056b1dc0b543fe27a929 Mon Sep 17 00:00:00 2001 From: Levi Date: Fri, 20 May 2016 16:41:42 -0430 Subject: [PATCH 03/66] Fixed Login Error Style , Started view to generating SSH key for a VM --- dynamicweb/settings/base.py | 1 - .../management/commands/create_vm_types.py | 4 +- hosting/models.py | 14 +++++++ hosting/templates/hosting/base_short.html | 3 ++ hosting/templates/hosting/login.html | 4 +- .../hosting/virtual_machine_key.html | 37 +++++++++++++++++++ hosting/urls.py | 4 +- hosting/views.py | 28 +++++++++++++- requirements.txt | 1 + 9 files changed, 88 insertions(+), 8 deletions(-) create mode 100644 hosting/templates/hosting/virtual_machine_key.html diff --git a/dynamicweb/settings/base.py b/dynamicweb/settings/base.py index de339f86..5fd2d646 100644 --- a/dynamicweb/settings/base.py +++ b/dynamicweb/settings/base.py @@ -457,7 +457,6 @@ STRIPE_DESCRIPTION_ON_PAYMENT = "Payment for ungleich GmbH services" REGISTRATION_MESSAGE = {'subject': "Validation mail", 'message': 'Thank You for registering for account on Digital Glarus.\nPlease verify Your account under following link http://{host}/en-us/digitalglarus/login/validate/{slug}', } - STRIPE_API_PRIVATE_KEY = env('STRIPE_API_PRIVATE_KEY') STRIPE_API_PUBLIC_KEY = env('STRIPE_API_PUBLIC_KEY') diff --git a/hosting/management/commands/create_vm_types.py b/hosting/management/commands/create_vm_types.py index 493e9e82..6be49f19 100644 --- a/hosting/management/commands/create_vm_types.py +++ b/hosting/management/commands/create_vm_types.py @@ -13,7 +13,7 @@ class Command(BaseCommand): 'memory_price': 5, 'disk_size_price': 1, 'description': 'VM auf einzelner HW, Raid1, kein HA', - 'location':'DE' + 'location': 'DE' } return { @@ -46,7 +46,7 @@ class Command(BaseCommand): 'memory_price': 7, 'disk_size_price': 0.70, 'description': "VM in Bern, HA Setup ohne HA Garantie", - 'location':'CH', + 'location': 'CH', } } diff --git a/hosting/models.py b/hosting/models.py index e2775fc4..2d322aad 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -104,6 +104,20 @@ class VirtualMachinePlan(models.Model): instance = cls.objects.create(**data) return instance + @classmethod + def generate_RSA(bits=2048): + ''' + Generate an RSA keypair with an exponent of 65537 in PEM format + param: bits The key length in bits + Return private key and public key + ''' + from Crypto.PublicKey import RSA + import os + new_key = RSA.generate(2048, os.urandom) + public_key = new_key.publickey() + private_key = new_key.exportKey("OpenSSH") + return private_key, public_key + class HostingOrder(models.Model): diff --git a/hosting/templates/hosting/base_short.html b/hosting/templates/hosting/base_short.html index 8b3f0dbb..cc41f453 100644 --- a/hosting/templates/hosting/base_short.html +++ b/hosting/templates/hosting/base_short.html @@ -150,6 +150,9 @@ + + + diff --git a/hosting/templates/hosting/login.html b/hosting/templates/hosting/login.html index 0c772da6..d88d740c 100644 --- a/hosting/templates/hosting/login.html +++ b/hosting/templates/hosting/login.html @@ -8,7 +8,7 @@ {% block messages %} {% if request.GET.logged_out %} -
+
× You haven been logged out
@@ -23,7 +23,7 @@ {% for field in form %} {% bootstrap_field field show_label=False type='fields'%} {% endfor %} - {% bootstrap_form_errors form type='non_fields'%} +

{{form.non_field_errors|striptags}}

{% buttons %} + +
+
+ + + + + + + + +{%endblock%} + + + + + + + + + diff --git a/hosting/urls.py b/hosting/urls.py index 67bf9e5e..9801e100 100644 --- a/hosting/urls.py +++ b/hosting/urls.py @@ -3,7 +3,7 @@ from django.conf.urls import url from .views import DjangoHostingView, RailsHostingView, PaymentVMView, \ NodeJSHostingView, LoginView, SignupView, IndexView, \ OrdersHostingListView, OrdersHostingDetailView, VirtualMachinesPlanListView,\ - VirtualMachineDetailView + VirtualMachineDetailView, GenerateVMSSHKeysView urlpatterns = [ # url(r'pricing/?$', VMPricingView.as_view(), name='pricing'), @@ -17,6 +17,8 @@ urlpatterns = [ url(r'my-virtual-machines/?$', VirtualMachinesPlanListView.as_view(), name='virtual_machines'), url(r'my-virtual-machines/(?P\d+)/?$', VirtualMachineDetailView.as_view(), name='virtual_machines'), + url(r'my-virtual-machines/(?P\d+)/key/?$', GenerateVMSSHKeysView.as_view(), + name='virtual_machine_key'), url(r'login/?$', LoginView.as_view(), name='login'), url(r'signup/?$', SignupView.as_view(), name='signup'), url(r'^logout/?$', 'django.contrib.auth.views.logout', diff --git a/hosting/views.py b/hosting/views.py index 83fa16a0..8478be74 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -3,8 +3,8 @@ from django.shortcuts import get_object_or_404, render from django.core.urlresolvers import reverse_lazy, reverse from django.contrib.auth.mixins import LoginRequiredMixin -from django.views.generic import View, CreateView, FormView, ListView, DetailView -from django.http import HttpResponseRedirect +from django.views.generic import View, CreateView, FormView, ListView, DetailView, UpdateView +from django.http import HttpResponseRedirect, HttpResponse from django.contrib.auth import authenticate, login from django.conf import settings @@ -145,6 +145,30 @@ class SignupView(CreateView): return HttpResponseRedirect(self.get_success_url()) +class GenerateVMSSHKeysView(LoginRequiredMixin, UpdateView): + model = VirtualMachinePlan + template_name = 'hosting/virtual_machine_key.html' + success_url = reverse_lazy('hosting:orders') + + def get_context_data(self, **kwargs): + private_key, public_key = VirtualMachinePlan.generate_RSA() + context = { + 'private_key': private_key + } + return context + + # def get(self, *args, **kwargs): + # vm = self.get_object() + # private_key, public_key = VirtualMachinePlan.generate_RSA() + # print(private_key) + # print(public_key) + # key_name = "private_key" + # response = HttpResponse(content_type='text/plain') + # response['Content-Disposition'] = 'attachment; filename="%s.pem"' % key_name + # response.write(private_key) + # return response + # return HttpResponseRedirect(reverse('')) + class PaymentVMView(LoginRequiredMixin, FormView): template_name = 'hosting/payment.html' login_url = reverse_lazy('hosting:login') diff --git a/requirements.txt b/requirements.txt index 13ea5675..dcf2b02b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,6 +17,7 @@ django-mptt easy_thumbnails django-polymorphic model-mommy +pycryptodome #PLUGINS djangocms_flash From f6fbf130232e9130b5f2612bf224256d1f6e887b Mon Sep 17 00:00:00 2001 From: Tomislav R Date: Sat, 21 May 2016 00:03:17 +0200 Subject: [PATCH 04/66] https://redmine.ungleich.ch/issues/2290 --- hosting/views.py | 8 ++++++-- membership/models.py | 11 +++++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/hosting/views.py b/hosting/views.py index fbeb36ea..8262517d 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1,5 +1,5 @@ -from django.shortcuts import get_object_or_404, render +from django.shortcuts import get_object_or_404, render,render_to_response from django.core.urlresolvers import reverse_lazy, reverse from django.contrib.auth.mixins import LoginRequiredMixin @@ -7,6 +7,7 @@ from django.views.generic import View, CreateView, FormView, ListView, DetailVie from django.http import HttpResponseRedirect from django.contrib.auth import authenticate, login from django.conf import settings +from django.contrib import messages from membership.models import CustomUser, StripeCustomer from utils.stripe_utils import StripeUtils @@ -133,7 +134,6 @@ class SignupView(CreateView): return next_url def form_valid(self, form): - name = form.cleaned_data.get('name') email = form.cleaned_data.get('email') password = form.cleaned_data.get('password') @@ -179,6 +179,10 @@ class PaymentVMView(LoginRequiredMixin, FormView): # Get or create stripe customer customer = StripeCustomer.get_or_create(email=self.request.user.email, token=token) + if not customer: + form.add_error("__all__","Invalid credit card") + return self.render_to_response(self.get_context_data(form=form)) + # Create Virtual Machine Plan plan = VirtualMachinePlan.create(plan_data, request.user) diff --git a/membership/models.py b/membership/models.py index d9861166..97d3ee06 100644 --- a/membership/models.py +++ b/membership/models.py @@ -143,12 +143,15 @@ class StripeCustomer(models.Model): stripe_utils = StripeUtils() stripe_data = stripe_utils.create_customer(token, email) - stripe_cus_id = stripe_data.get('response_object').get('id') + if stripe_data.get('response_object'): + stripe_cus_id = stripe_data.get('response_object').get('id') - stripe_customer = StripeCustomer.objects.\ - create(user=user, stripe_id=stripe_cus_id) + stripe_customer = StripeCustomer.objects.\ + create(user=user, stripe_id=stripe_cus_id) - return stripe_customer + return stripe_customer + else: + return None class CreditCards(models.Model): From ed95a7ada411fb1b884f3514e5d80d255a6bf587 Mon Sep 17 00:00:00 2001 From: Tomislav R Date: Sun, 22 May 2016 17:36:28 +0200 Subject: [PATCH 05/66] hosting: https://redmine.ungleich.ch/issues/2292, fixed footer --- hosting/static/hosting/css/landing-page.css | 44 +++--- hosting/templates/hosting/order_detail.html | 1 + membership/static/stylesheet.css | 156 ++++++++++---------- 3 files changed, 105 insertions(+), 96 deletions(-) diff --git a/hosting/static/hosting/css/landing-page.css b/hosting/static/hosting/css/landing-page.css index cbbc5898..c79f35a7 100644 --- a/hosting/static/hosting/css/landing-page.css +++ b/hosting/static/hosting/css/landing-page.css @@ -17,12 +17,12 @@ h3, h4, h5, h6 { - font-family: 'Raleway',"Lato","Helvetica Neue",Helvetica,Arial,sans-serif; + font-family: 'Raleway', "Lato", "Helvetica Neue", Helvetica, Arial, sans-serif; font-weight: 700; } .topnav { - font-size: 14px; + font-size: 14px; } .lead { @@ -38,6 +38,7 @@ h6 { background: url(../img/intro-bg.jpg) no-repeat center center; background-size: cover; } + .intro-header-1 { padding-top: 50px; /* If you're making other pages, make sure there is 50px of padding to make sure the navbar doesn't overlap content! */ padding-bottom: 50px; @@ -46,6 +47,7 @@ h6 { background: url(../img/configure.jpg) no-repeat center center; background-size: cover; } + .intro-header-2 { padding-top: 50px; /* If you're making other pages, make sure there is 50px of padding to make sure the navbar doesn't overlap content! */ padding-bottom: 50px; @@ -54,6 +56,7 @@ h6 { background: url(../img/configure.jpg) no-repeat center center; background-size: cover; } + .intro-message { position: relative; padding-top: 20%; @@ -75,7 +78,7 @@ h6 { .intro-signup { background: url(../img/configure.jpg) no-repeat center center; - background-size: cover; + background-size: cover; } .intro-message > h1 { @@ -87,14 +90,14 @@ h6 { .intro-divider { width: 400px; border-top: 1px solid #f8f8f8; - border-bottom: 1px solid rgba(0,0,0,0.2); + border-bottom: 1px solid rgba(0, 0, 0, 0.2); } .intro-message > h3 { font-weight: 300; } -@media(max-width:767px) { +@media (max-width: 767px) { .intro-message { padding-bottom: 15%; } @@ -168,17 +171,17 @@ h6 { margin-top: 0; } -@media(max-width:1199px) { +@media (max-width: 1199px) { ul.banner-social-buttons { float: left; margin-top: 15px; } } -@media(max-width:767px) { +@media (max-width: 767px) { .banner h2 { margin: 0; - text-shadow: 2px 2px 3px rgba(0,0,0,0.6); + text-shadow: 2px 2px 3px rgba(0, 0, 0, 0.6); font-size: 3em; } @@ -194,31 +197,34 @@ h6 { } footer { - padding: 50px 0; + padding: 2%; background-color: #f8f8f8; + #position: absolute; + right: 0; + bottom: 0; + left: 0; } p.copyright { margin: 15px 0 0; } - a#forgotpassword { - color: #ffffff; - } + color: #ffffff; +} .content-404 h1 { - margin: 0 0 15px; - font-size: 200px; - line-height: 1; - font-weight: 700; - color: #6db97c; + margin: 0 0 15px; + font-size: 200px; + line-height: 1; + font-weight: 700; + color: #6db97c; } a.unlink { - color: inherit; + color: inherit; } a.unlink:hover { - color: inherit; + color: inherit; } \ No newline at end of file diff --git a/hosting/templates/hosting/order_detail.html b/hosting/templates/hosting/order_detail.html index 8e1ff691..607c3677 100644 --- a/hosting/templates/hosting/order_detail.html +++ b/hosting/templates/hosting/order_detail.html @@ -58,6 +58,7 @@

Total

{{order.vm_plan.price}} CHF

+ diff --git a/membership/static/stylesheet.css b/membership/static/stylesheet.css index 20f5931b..9357a1c0 100644 --- a/membership/static/stylesheet.css +++ b/membership/static/stylesheet.css @@ -6,161 +6,163 @@ /* ---------- http://weloveiconfonts.com/ ---------- */ *[class*="fontawesome-"]:before { - font-family: 'FontAwesome', sans-serif; + font-family: 'FontAwesome', sans-serif; } /* ---------- GENERAL ---------- */ /*body {*/ - /*background: #f9f9f9;*/ - /*color: #0e171c;*/ - /*font: 300 100%/1em 'Lato', sans-serif;*/ - /*margin: 0;*/ +/*background: #f9f9f9;*/ +/*color: #0e171c;*/ +/*font: 300 100%/1em 'Lato', sans-serif;*/ +/*margin: 0;*/ /*}*/ /**/ /*a {*/ - /*text-decoration: none;*/ +/*text-decoration: none;*/ /*}*/ /*Month size*/ #monthtitle { - font-size: 1.2em; - line-height: 1.25em; - margin: .25em 0; - font-weight: 600; + font-size: 1.2em; + line-height: 1.25em; + margin: .25em 0; + font-weight: 600; } h3 { - font-size: 1.5em; - line-height: 1em; - margin: .33em 0; + font-size: 1.5em; + line-height: 1em; + margin: .33em 0; } table { - border-collapse: collapse; - border-spacing: 0; - margin:auto; + border-collapse: collapse; + border-spacing: 0; + margin: auto; } /*.container {*/ - /*height: 358px;*/ - /*left: 50%;*/ - /*margin: -255px 0 0 -245px;*/ - /*position: absolute;*/ - /*top: 50%;*/ - /*width: 340px;*/ +/*height: 358px;*/ +/*left: 50%;*/ +/*margin: -255px 0 0 -245px;*/ +/*position: absolute;*/ +/*top: 50%;*/ +/*width: 340px;*/ /*}*/ /* ---------- CALENDAR ---------- */ .calendar { - text-align: center; + text-align: center; } .calendar header { - position: relative; + position: relative; } .calendar #monthtitle { - text-transform: uppercase; - color: #1A6687; + text-transform: uppercase; + color: #1A6687; } -#datesbooked{ - color:#1a6687; - padding-top: 2px; +#datesbooked { + color: #1a6687; + padding-top: 2px; } + /*Title*/ .calendar thead { - font-weight: 500; - /*text-transform: uppercase;*/ - color: #8BC4C9; - /*margin-bottom:1px;*/ + font-weight: 500; + /*text-transform: uppercase;*/ + color: #8BC4C9; + /*margin-bottom:1px;*/ } /*Body text*/ .calendar tbody { - color: #7c8a95; + color: #7c8a95; } /*select date*/ .calendar tbody td:hover { - background: #8BC4C9; - color: #f9f9f9; - /*border: .1px solid #8BC4C9;*/ + background: #8BC4C9; + color: #f9f9f9; + /*border: .1px solid #8BC4C9;*/ - /*border-radius: 50%;*/ + /*border-radius: 50%;*/ } -.selected{ - background: #6b9699; - color: #f9f9f9; + +.selected { + background: #6b9699; + color: #f9f9f9; } -.calendar thead>tr>td{ - border-top:hidden; - border-left:hidden; - border-right: hidden; + +.calendar thead > tr > td { + border-top: hidden; + border-left: hidden; + border-right: hidden; } .calendar td { - border: .1px solid #cbd1d2; - /*border-radius: 50%;*/ - display: inline-block; - height: 2.5em; - line-height: 2.5em; - text-align: center; - width: 2.5em; - + border: .1px solid #cbd1d2; + /*border-radius: 50%;*/ + display: inline-block; + height: 2.5em; + line-height: 2.5em; + text-align: center; + width: 2.5em; + } .calendar .prev-month, .calendar .next-month { - /*border: .1px solid #cbd1d2;*/ - color: #cbd1d2; + /*border: .1px solid #cbd1d2;*/ + color: #cbd1d2; } .calendar .prev-month:hover, .calendar .next-month:hover { - border: .5px solid #cbd1d2; - background: #cbd1d2; - color: #f9f9f9; + border: .5px solid #cbd1d2; + background: #cbd1d2; + color: #f9f9f9; } /*Today*/ .current-day { - color: #8BC4C9; - /*background-color: #8BC4C9;*/ + color: #8BC4C9; + /*background-color: #8BC4C9;*/ } - /*Next,Prev month*/ .btn-prev, .btn-next { - border: 1px solid transparent; - color: #8BC4C9; - font-size: 1.5em; - padding: 1em; - /*height: .7em;*/ - /*line-height: .3em;*/ - /*margin: auto;*/ - /*position: absolute;*/ - /*top: .1em;*/ - /*width: 25em;*/ + border: 1px solid transparent; + color: #8BC4C9; + font-size: 1.5em; + padding: 1em; + /*height: .7em;*/ + /*line-height: .3em;*/ + /*margin: auto;*/ + /*position: absolute;*/ + /*top: .1em;*/ + /*width: 25em;*/ } - .btn-prev:hover, .btn-next:hover { - background: none; - color: #1A6687; + background: none; + color: #1A6687; } .btn-prev { - left: 6em; + left: 6em; } .btn-next { - right: 6em; -} \ No newline at end of file + right: 6em; +} + From 8376d0106c5056bc3581fa68ad3921617d591f1b Mon Sep 17 00:00:00 2001 From: Levi Date: Tue, 24 May 2016 01:19:49 -0500 Subject: [PATCH 06/66] Added next button in order detail view after the user make a payment, Avoid to regenerate SSH Key if the user refresh the page, Now the key is served to download from javascript , Added public_key attribute to VirtualVMPlan in order to store keys created by the user, Now private key has PEM format and public has OpenSSH --- .../0018_virtualmachineplan_public_key.py | 21 +++++++++ hosting/models.py | 21 ++++++--- hosting/static/hosting/js/gen-ssh-key.js | 42 +++++++++++++++++ hosting/templates/hosting/base_short.html | 3 ++ hosting/templates/hosting/order_detail.html | 8 +++- .../hosting/virtual_machine_key.html | 47 ++++++++++++++++--- hosting/views.py | 31 +++++------- 7 files changed, 140 insertions(+), 33 deletions(-) create mode 100644 hosting/migrations/0018_virtualmachineplan_public_key.py create mode 100644 hosting/static/hosting/js/gen-ssh-key.js diff --git a/hosting/migrations/0018_virtualmachineplan_public_key.py b/hosting/migrations/0018_virtualmachineplan_public_key.py new file mode 100644 index 00000000..bd869a48 --- /dev/null +++ b/hosting/migrations/0018_virtualmachineplan_public_key.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2016-05-24 03:57 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('hosting', '0017_virtualmachinetype_location'), + ] + + operations = [ + migrations.AddField( + model_name='virtualmachineplan', + name='public_key', + field=models.TextField(default='sada'), + preserve_default=False, + ), + ] diff --git a/hosting/models.py b/hosting/models.py index 2d322aad..55709fca 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -1,12 +1,14 @@ -import json +import os from django.db import models from django.utils.translation import ugettext_lazy as _ from django.utils.functional import cached_property -from django.core import serializers from membership.models import StripeCustomer from utils.models import BillingAddress +from Crypto.PublicKey import RSA + + from .managers import VMPlansManager @@ -84,6 +86,7 @@ class VirtualMachinePlan(models.Model): disk_size = models.IntegerField() vm_type = models.ForeignKey(VirtualMachineType) price = models.FloatField() + public_key = models.TextField() objects = VMPlansManager() @@ -104,20 +107,24 @@ class VirtualMachinePlan(models.Model): instance = cls.objects.create(**data) return instance - @classmethod + @staticmethod def generate_RSA(bits=2048): ''' Generate an RSA keypair with an exponent of 65537 in PEM format param: bits The key length in bits Return private key and public key ''' - from Crypto.PublicKey import RSA - import os new_key = RSA.generate(2048, os.urandom) - public_key = new_key.publickey() - private_key = new_key.exportKey("OpenSSH") + public_key = new_key.publickey().exportKey("OpenSSH") + private_key = new_key.exportKey("PEM") return private_key, public_key + def generate_keys(self): + private_key, public_key = self.generate_RSA() + self.public_key = public_key + self.save(update_fields=['public_key']) + return private_key + class HostingOrder(models.Model): diff --git a/hosting/static/hosting/js/gen-ssh-key.js b/hosting/static/hosting/js/gen-ssh-key.js new file mode 100644 index 00000000..46e701d7 --- /dev/null +++ b/hosting/static/hosting/js/gen-ssh-key.js @@ -0,0 +1,42 @@ +$( document ).ready(function() { + + + + + + // Create a file with ssh private key info + function donwloadKeyFile(){ + + var key = $('#ssh_key').text(); + var a = window.document.createElement('a'); + + a.href = window.URL.createObjectURL(new Blob([key], {type: 'text'})); + a.download = 'private_key.pem'; + + // Append anchor to body. + document.body.appendChild(a); + a.click(); + + // Remove anchor from body + document.body.removeChild(a); + + } + + + // Create a file with ssh private key info + $('#download_ssh_key').on('click',donwloadKeyFile); + + + $('[data-toggle="tooltip"]').tooltip(); + + var clipboard = new Clipboard('#copy_to_clipboard'); + + clipboard.on('success', function(e) { + var selector = "#"; + var copy_button_id = selector.concat(e.trigger.id); + setTimeout(function(){ + $(copy_button_id).tooltip('hide'); + }, 1000); + }); + +}); \ No newline at end of file diff --git a/hosting/templates/hosting/base_short.html b/hosting/templates/hosting/base_short.html index cc41f453..a050a3d9 100644 --- a/hosting/templates/hosting/base_short.html +++ b/hosting/templates/hosting/base_short.html @@ -163,6 +163,9 @@ + + + diff --git a/hosting/templates/hosting/order_detail.html b/hosting/templates/hosting/order_detail.html index 607c3677..87b4a5e0 100644 --- a/hosting/templates/hosting/order_detail.html +++ b/hosting/templates/hosting/order_detail.html @@ -58,7 +58,13 @@

Total

{{order.vm_plan.price}} CHF

- +
+ {% url 'hosting:payment' as payment_url %} + {% if payment_url in request.META.HTTP_REFERER %} + + {% endif %} diff --git a/hosting/templates/hosting/virtual_machine_key.html b/hosting/templates/hosting/virtual_machine_key.html index d2c79fd4..ee2f27df 100644 --- a/hosting/templates/hosting/virtual_machine_key.html +++ b/hosting/templates/hosting/virtual_machine_key.html @@ -6,25 +6,58 @@
-

Private Key

+ +

SSH Private Key


+ {% if private_key %} +
+ Warning! You can view your SSH private key once. Copy it or if it wasn't downloaded automatically, just click on Download to start it. +
- - - + +
-
+
+ + +
+ {% else %} +
+ Warning! Your SSH private key was already generated and downloaded, if you lost it, contact us. +
+ {% endif %} + Go to my Virtual Machine Dashboard +
- + +{% if private_key %} + +{%endif%} + + {%endblock%} diff --git a/hosting/views.py b/hosting/views.py index 1f058783..ad892143 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -145,29 +145,23 @@ class SignupView(CreateView): return HttpResponseRedirect(self.get_success_url()) -class GenerateVMSSHKeysView(LoginRequiredMixin, UpdateView): +class GenerateVMSSHKeysView(LoginRequiredMixin, DetailView): model = VirtualMachinePlan template_name = 'hosting/virtual_machine_key.html' success_url = reverse_lazy('hosting:orders') + context_object_name = "virtual_machine" def get_context_data(self, **kwargs): - private_key, public_key = VirtualMachinePlan.generate_RSA() - context = { - 'private_key': private_key - } - return context - # def get(self, *args, **kwargs): - # vm = self.get_object() - # private_key, public_key = VirtualMachinePlan.generate_RSA() - # print(private_key) - # print(public_key) - # key_name = "private_key" - # response = HttpResponse(content_type='text/plain') - # response['Content-Disposition'] = 'attachment; filename="%s.pem"' % key_name - # response.write(private_key) - # return response - # return HttpResponseRedirect(reverse('')) + context = super(GenerateVMSSHKeysView, self).get_context_data(**kwargs) + vm = self.get_object() + if not vm.public_key: + private_key = vm.generate_keys() + context.update({ + 'private_key': private_key + }) + return context + return context class PaymentVMView(LoginRequiredMixin, FormView): @@ -269,7 +263,8 @@ class OrdersHostingListView(LoginRequiredMixin, ListView): self.queryset = HostingOrder.objects.filter(customer__user=user) return super(OrdersHostingListView, self).get_queryset() -class OrdersHostingDeleteView(LoginRequiredMixin,DeleteView): + +class OrdersHostingDeleteView(LoginRequiredMixin, DeleteView): login_url=reverse_lazy('hosting:login') success_url = reverse_lazy('hosting:orders') model = HostingOrder From 71f3d30549a855a9cd1c966aa79004eacf49197a Mon Sep 17 00:00:00 2001 From: Levi Date: Tue, 24 May 2016 01:27:10 -0500 Subject: [PATCH 07/66] fixed cdn fonts url --- hosting/templates/hosting/base.html | 4 ++-- hosting/templates/hosting/base_short.html | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/hosting/templates/hosting/base.html b/hosting/templates/hosting/base.html index 72ebff2e..8d7036e5 100644 --- a/hosting/templates/hosting/base.html +++ b/hosting/templates/hosting/base.html @@ -25,9 +25,9 @@ - + - + diff --git a/hosting/templates/hosting/base_short.html b/hosting/templates/hosting/base_short.html index a050a3d9..874b590f 100644 --- a/hosting/templates/hosting/base_short.html +++ b/hosting/templates/hosting/base_short.html @@ -24,9 +24,9 @@ - - - + + + @@ -148,17 +148,17 @@ - + - + - + From b443d4d21e2bf65361a6c6c0691363ef53cc3fa1 Mon Sep 17 00:00:00 2001 From: Levi Date: Wed, 25 May 2016 01:23:32 -0500 Subject: [PATCH 08/66] Created BaseEmail class , Now we are sending email to info@ungleich.com after an user book a VM, Fixed pricing issue, Now Admin can changed data about a booked VM --- dynamicweb/settings/base.py | 1 + hosting/admin.py | 3 +- hosting/static/hosting/img/DE_flag.png | Bin 376 -> 741 bytes hosting/templates/emails/new_booked_vm.html | 12 ++++++++ hosting/templates/emails/new_booked_vm.txt | 12 ++++++++ .../templates/hosting/includes/_pricing.html | 2 +- hosting/views.py | 17 +++++++++-- utils/mailer.py | 27 ++++++++++++++++++ 8 files changed, 70 insertions(+), 4 deletions(-) create mode 100644 hosting/templates/emails/new_booked_vm.html create mode 100644 hosting/templates/emails/new_booked_vm.txt diff --git a/dynamicweb/settings/base.py b/dynamicweb/settings/base.py index 5f59a319..db2912bd 100644 --- a/dynamicweb/settings/base.py +++ b/dynamicweb/settings/base.py @@ -132,6 +132,7 @@ TEMPLATES = [ 'DIRS': [os.path.join(PROJECT_DIR, 'cms_templates/'), os.path.join(PROJECT_DIR, 'cms_templates/djangocms_blog/'), os.path.join(PROJECT_DIR, 'membership'), + os.path.join(PROJECT_DIR, 'hosting/templates/'), 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') diff --git a/hosting/admin.py b/hosting/admin.py index 70392113..d8cf3012 100644 --- a/hosting/admin.py +++ b/hosting/admin.py @@ -1,5 +1,6 @@ from django.contrib import admin -from .models import VirtualMachineType +from .models import VirtualMachineType, VirtualMachinePlan admin.site.register(VirtualMachineType) +admin.site.register(VirtualMachinePlan) diff --git a/hosting/static/hosting/img/DE_flag.png b/hosting/static/hosting/img/DE_flag.png index d4f4526e9e00847d0f5bcd13b4d270453cbe2f43..33db924c79d8b468e3b4644d4e726a57f5125b75 100644 GIT binary patch literal 741 zcmeAS@N?(olHy`uVBq!ia0y~yU~~Yo4{)#n$^n{Yj3-Aqenfs%ZFh8s!^4$~NzBAGZ+ zI29(S4rR{y?Ryy#I@yQ5N8oXBt@NJdZDj_tilH1WaNj_a+26P#Hq8R(Qg9%7Z{G(6}q}Y|gW!U_%O^81FzR}` zIEGZrd3)87x5a^h<=}1ZXCI4%{FwO)4$AvnxS0~XG(m3e84LRl``*7{T;my`(&Fkw z2O=rv_Z!YN8F!8Y$rGZJ1jINKC(@kgeEWUAEh8|Pu!9G+{MUD}IjZ?`{Rb)VboFyt I=akR{0M-L+MgRZ+ diff --git a/hosting/templates/emails/new_booked_vm.html b/hosting/templates/emails/new_booked_vm.html new file mode 100644 index 00000000..8583ba1f --- /dev/null +++ b/hosting/templates/emails/new_booked_vm.html @@ -0,0 +1,12 @@ + +< + + + + + + +NEW VM BOOKED + + + \ No newline at end of file diff --git a/hosting/templates/emails/new_booked_vm.txt b/hosting/templates/emails/new_booked_vm.txt new file mode 100644 index 00000000..8583ba1f --- /dev/null +++ b/hosting/templates/emails/new_booked_vm.txt @@ -0,0 +1,12 @@ + +< + + + + + + +NEW VM BOOKED + + + \ No newline at end of file diff --git a/hosting/templates/hosting/includes/_pricing.html b/hosting/templates/hosting/includes/_pricing.html index 1f4e308e..2f8033bc 100644 --- a/hosting/templates/hosting/includes/_pricing.html +++ b/hosting/templates/hosting/includes/_pricing.html @@ -29,7 +29,7 @@