From 9aa732626f0c580fd5034caeeb5a3934d260c1f7 Mon Sep 17 00:00:00 2001 From: Levi <levinoelvm@gmail.com> Date: Sun, 26 Jun 2016 14:54:01 -0500 Subject: [PATCH 1/9] fixed hosting emails --- hosting/templates/emails/new_booked_vm.html | 1 + hosting/templates/emails/new_booked_vm.txt | 1 + hosting/templates/emails/vm_charged.html | 1 + hosting/templates/emails/vm_charged.txt | 1 + hosting/templates/emails/vm_status_changed.html | 1 - hosting/templates/emails/vm_status_changed.txt | 1 - 6 files changed, 4 insertions(+), 2 deletions(-) diff --git a/hosting/templates/emails/new_booked_vm.html b/hosting/templates/emails/new_booked_vm.html index 0a3d2742..0f9b4f6e 100644 --- a/hosting/templates/emails/new_booked_vm.html +++ b/hosting/templates/emails/new_booked_vm.html @@ -1,3 +1,4 @@ +{% load static from staticfiles %} <!-- Inliner Build Version 4380b7741bb759d6cb997545f3add21ad48f010b --> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;"> diff --git a/hosting/templates/emails/new_booked_vm.txt b/hosting/templates/emails/new_booked_vm.txt index 0a3d2742..0f9b4f6e 100644 --- a/hosting/templates/emails/new_booked_vm.txt +++ b/hosting/templates/emails/new_booked_vm.txt @@ -1,3 +1,4 @@ +{% load static from staticfiles %} <!-- Inliner Build Version 4380b7741bb759d6cb997545f3add21ad48f010b --> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;"> diff --git a/hosting/templates/emails/vm_charged.html b/hosting/templates/emails/vm_charged.html index b8837645..33568d05 100644 --- a/hosting/templates/emails/vm_charged.html +++ b/hosting/templates/emails/vm_charged.html @@ -1,3 +1,4 @@ +{% load static from staticfiles %} <!-- Inliner Build Version 4380b7741bb759d6cb997545f3add21ad48f010b --> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;"> diff --git a/hosting/templates/emails/vm_charged.txt b/hosting/templates/emails/vm_charged.txt index b8837645..33568d05 100644 --- a/hosting/templates/emails/vm_charged.txt +++ b/hosting/templates/emails/vm_charged.txt @@ -1,3 +1,4 @@ +{% load static from staticfiles %} <!-- Inliner Build Version 4380b7741bb759d6cb997545f3add21ad48f010b --> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;"> diff --git a/hosting/templates/emails/vm_status_changed.html b/hosting/templates/emails/vm_status_changed.html index 691c42a3..c60ba661 100644 --- a/hosting/templates/emails/vm_status_changed.html +++ b/hosting/templates/emails/vm_status_changed.html @@ -1,4 +1,3 @@ - {% load static from staticfiles %} <!-- Inliner Build Version 4380b7741bb759d6cb997545f3add21ad48f010b --> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"> diff --git a/hosting/templates/emails/vm_status_changed.txt b/hosting/templates/emails/vm_status_changed.txt index 691c42a3..c60ba661 100644 --- a/hosting/templates/emails/vm_status_changed.txt +++ b/hosting/templates/emails/vm_status_changed.txt @@ -1,4 +1,3 @@ - {% load static from staticfiles %} <!-- Inliner Build Version 4380b7741bb759d6cb997545f3add21ad48f010b --> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"> From e90c5fece035227b2202cb1585a0415e126029a0 Mon Sep 17 00:00:00 2001 From: Levi <levinoelvm@gmail.com> Date: Thu, 30 Jun 2016 01:23:14 -0500 Subject: [PATCH 2/9] Created generic view for vm pricing widget, Added VM configuration info on VM detail view, Fixed template bugs. Fixed Blog urls bugs --- dynamicweb/urls.py | 8 ++-- hosting/mixins.py | 7 ++- .../templates/hosting/hosting_pricing.html | 44 +++++++++++++++++++ .../templates/hosting/includes/_pricing.html | 26 +++++++---- .../hosting/virtual_machine_detail.html | 5 +++ hosting/urls.py | 3 +- hosting/views.py | 20 +++++++++ 7 files changed, 99 insertions(+), 14 deletions(-) create mode 100644 hosting/templates/hosting/hosting_pricing.html diff --git a/dynamicweb/urls.py b/dynamicweb/urls.py index a732934e..f0fc25bb 100644 --- a/dynamicweb/urls.py +++ b/dynamicweb/urls.py @@ -7,9 +7,10 @@ from django.conf.urls.static import static from django.conf import settings from hosting.views import RailsHostingView, DjangoHostingView, NodeJSHostingView from membership import urls as membership_urls +from ungleich_page.views import LandingView import debug_toolbar -urlpatterns = [ +urlpatterns = [ url(r'^index.html$', LandingView.as_view()), url(r'^hosting/', include('hosting.urls', namespace="hosting")), url(r'^railshosting/', RailsHostingView.as_view(), name="rails.hosting"), url(r'^nodehosting/', NodeJSHostingView.as_view(), name="node.hosting"), @@ -21,12 +22,13 @@ urlpatterns = [ # note the django CMS URLs included via i18n_patterns urlpatterns += i18n_patterns('', + url(r'^/?$', LandingView.as_view()), url(r'^admin/', include(admin.site.urls)), url(r'^digitalglarus/login/', include(membership_urls)), url(r'^digitalglarus/', include('digitalglarus.urls', namespace="digitalglarus")), - # url(r'^blog/', include('ungleich.urls', namespace='ungleich')), - url(r'^ungleich_page/', + #url(r'^blog/', include('ungleich.urls', namespace='ungleich')), + url(r'^', include('ungleich_page.urls', namespace='ungleich_page'), name='ungleich_page'), url(r'^blog/', include('ungleich.urls', namespace='ungleich')), diff --git a/hosting/mixins.py b/hosting/mixins.py index e8a2b7b4..2f8de3a5 100644 --- a/hosting/mixins.py +++ b/hosting/mixins.py @@ -1,18 +1,21 @@ from django.shortcuts import redirect from django.core.urlresolvers import reverse +from .models import VirtualMachinePlan class ProcessVMSelectionMixin(object): def post(self, request, *args, **kwargs): + hosting = request.POST.get('configuration') + configuration_detail = dict(VirtualMachinePlan.VM_CONFIGURATION).get(hosting) vm_specs = { 'cores': request.POST.get('cores'), 'memory': request.POST.get('memory'), 'disk_size': request.POST.get('disk_space'), 'hosting_company': request.POST.get('hosting_company'), 'location_code': request.POST.get('location_code'), - 'configuration': request.POST.get('configuration'), - 'configuration_detail': request.POST.get('configuration_detail'), + 'configuration': hosting, + 'configuration_detail': configuration_detail, 'final_price': request.POST.get('final_price') } request.session['vm_specs'] = vm_specs diff --git a/hosting/templates/hosting/hosting_pricing.html b/hosting/templates/hosting/hosting_pricing.html new file mode 100644 index 00000000..c6ae33d0 --- /dev/null +++ b/hosting/templates/hosting/hosting_pricing.html @@ -0,0 +1,44 @@ +{% load staticfiles %} +<!DOCTYPE html> +<html> +<head> + <link href="{% static 'hosting/css/pricing.css' %}" rel="stylesheet" /> + <title>Hosting</title> + <!-- Bootstrap Core CSS --> + <link href="{% static 'hosting/css/bootstrap.min.css' %}" rel="stylesheet"> + + <link href="{% static 'hosting/css/pricing.css' %}" rel="stylesheet"> + + + <!-- Custom CSS --> + <link href="{% static 'hosting/css/landing-page.css' %}" rel="stylesheet"> + + <!-- Custom Fonts --> + <link href='//fonts.googleapis.com/css?family=Raleway' rel='stylesheet' type='text/css'> + <link href="{% static 'hosting/font-awesome/css/font-awesome.min.css' %}" rel="stylesheet" type="text/css"> + <link href="//fonts.googleapis.com/css?family=Lato:300,400,700,300italic,400italic,700italic" rel="stylesheet" type="text/css"> + <link rel="shortcut icon" href="{% static 'hosting/img/favicon.ico' %}" type="image/x-icon" /> + +</head> +<body> + + {% include "hosting/includes/_pricing.html" with select_configuration=True%} + + + <!-- Pricing data --> + {% if vm_types %} + <script type="text/javascript"> + (function () {window.VMTypesData = "{{vm_types|safe}}";})(); + </script> + {%endif%} + + <!-- Lodash --> + <script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/4.11.1/lodash.min.js"></script> + + <!-- jQuery --> + <script src="{% static 'hosting/js/jquery.js' %}"></script> + + <!-- Pricing --> + <script src="{% static 'hosting/js/pricing.js' %}"></script> +</body> +</html> \ No newline at end of file diff --git a/hosting/templates/hosting/includes/_pricing.html b/hosting/templates/hosting/includes/_pricing.html index 3a9138b4..92033be8 100644 --- a/hosting/templates/hosting/includes/_pricing.html +++ b/hosting/templates/hosting/includes/_pricing.html @@ -24,8 +24,7 @@ {% csrf_token %} <input type="hidden" name="hosting_company" value="{{vm.hosting_company}}"> <input type="hidden" name="location_code" value="{{vm.location_code}}"> - <input type="hidden" name="configuration_detail" value="{{configuration_detail}}"> - <input type="hidden" name="configuration" value="{{hosting}}"> + <ul class="pricing {% cycle 'p-red' 'p-black' 'p-red' 'p-yel' %}"> @@ -46,13 +45,24 @@ </div> </li> <li> - <!-- Single button --> - <div class="btn-group"> - <div class="form-group"> - <label for="cores">Configuration: </label> - {{configuration_detail}} + <label for="configuration">Configuration: </label> + {% if select_configuration %} + <select class="form-control" name="configuration" id="{{vm.hosting_company}}-configuration" data-vm-type="{{vm.hosting_company}}"> + {% for key,value in configuration_options.items %} + <option value="{{key}}">{{ value }}</option> + {% endfor %} + </select> + {% else %} + <input type="hidden" name="configuration_detail" value="{{configuration_detail}}"> + <input type="hidden" name="configuration" value="{{hosting}}"> + <!-- Single button --> + <div class="btn-group"> + <div class="form-group"> + <label>Configuration: </label> + {{configuration_detail}} + </div> </div> - </div> + {% endif %} </li> <li> <!-- Single button --> diff --git a/hosting/templates/hosting/virtual_machine_detail.html b/hosting/templates/hosting/virtual_machine_detail.html index 24ee5d6c..b06bba86 100644 --- a/hosting/templates/hosting/virtual_machine_detail.html +++ b/hosting/templates/hosting/virtual_machine_detail.html @@ -89,6 +89,11 @@ </div><!--/row--> </div><!--/col-12--> </div><!--/row--> + <div class="row"> + <div class="col-md-12"> + Configuration: {{virtual_machine.get_configuration_display}} + </div> + </div> </div> diff --git a/hosting/urls.py b/hosting/urls.py index 225dd19e..5ceeba97 100644 --- a/hosting/urls.py +++ b/hosting/urls.py @@ -4,13 +4,14 @@ from .views import DjangoHostingView, RailsHostingView, PaymentVMView,\ NodeJSHostingView, LoginView, SignupView, IndexView, \ OrdersHostingListView, OrdersHostingDetailView, VirtualMachinesPlanListView,\ VirtualMachineView, GenerateVMSSHKeysView, OrdersHostingDeleteView, NotificationsView, \ - MarkAsReadNotificationView, PasswordResetView, PasswordResetConfirmView + MarkAsReadNotificationView, PasswordResetView, PasswordResetConfirmView, HostingPricingView urlpatterns = [ url(r'index/?$', IndexView.as_view(), name='index'), url(r'django/?$', DjangoHostingView.as_view(), name='djangohosting'), url(r'nodejs/?$', NodeJSHostingView.as_view(), name='nodejshosting'), url(r'rails/?$', RailsHostingView.as_view(), name='railshosting'), + url(r'pricing/?$', HostingPricingView.as_view(), name='pricing'), url(r'payment/?$', PaymentVMView.as_view(), name='payment'), url(r'orders/?$', OrdersHostingListView.as_view(), name='orders'), url(r'orders/(?P<pk>\d+)/?$', OrdersHostingDetailView.as_view(), name='orders'), diff --git a/hosting/views.py b/hosting/views.py index 09f94ac9..c66068d1 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -99,6 +99,26 @@ class NodeJSHostingView(ProcessVMSelectionMixin, View): return render(request, self.template_name, context) +class HostingPricingView(ProcessVMSelectionMixin, View): + template_name = "hosting/hosting_pricing.html" + + def get_context_data(self, **kwargs): + configuration_options = dict(VirtualMachinePlan.VM_CONFIGURATION) + context = { + 'configuration_options': configuration_options, + 'email': "info@django-hosting.ch", + 'vm_types': VirtualMachineType.get_serialized_vm_types(), + } + + return context + + def get(self, request, *args, **kwargs): + request.session['hosting_url'] = reverse('hosting:djangohosting') + context = self.get_context_data() + + return render(request, self.template_name, context) + + class IndexView(View): template_name = "hosting/index.html" From 21cd0c21a9260176c74c252e3023d20f05ad0ee9 Mon Sep 17 00:00:00 2001 From: Levi <levinoelvm@gmail.com> Date: Fri, 1 Jul 2016 00:40:39 -0500 Subject: [PATCH 3/9] fixed digitalglarus post detail view on DE --- digitalglarus/views.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/digitalglarus/views.py b/digitalglarus/views.py index 20f81cd9..b4623922 100644 --- a/digitalglarus/views.py +++ b/digitalglarus/views.py @@ -80,9 +80,8 @@ def blog(request): def blog_detail(request, slug): # post = Post.objects.filter_by_language(get_language()).filter(slug=slug).first() - language = 'en-us' # currently nothing is translated to german so we give then en - post = Post.objects.translated(language, slug=slug).first() + post = Post.objects.translated(get_language(), slug=slug).first() context = { 'post': post, } From 0dc81fff3dfb02de08509f23882de74e92927d99 Mon Sep 17 00:00:00 2001 From: Levi <levinoelvm@gmail.com> Date: Mon, 4 Jul 2016 23:44:15 -0500 Subject: [PATCH 4/9] Fixed duplicated post bug , Added reset password view test, Added confirm reset password view test --- hosting/models.py | 3 +- hosting/test_views.py | 187 ++++++++++++++++++++++++++++++++++++++++-- hosting/views.py | 30 ++++--- utils/tests.py | 21 +++++ 4 files changed, 225 insertions(+), 16 deletions(-) diff --git a/hosting/models.py b/hosting/models.py index 887f0777..099b7894 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -5,6 +5,7 @@ from django.utils.translation import ugettext_lazy as _ from django.utils.functional import cached_property + from Crypto.PublicKey import RSA from stored_messages.settings import stored_messages_settings @@ -160,7 +161,7 @@ class VirtualMachinePlan(models.Model): private_key, public_key = self.generate_RSA() self.public_key = public_key self.save(update_fields=['public_key']) - return private_key + return private_key, public_key def cancel_plan(self): self.status = self.CANCELED_STATUS diff --git a/hosting/test_views.py b/hosting/test_views.py index b2deb6d7..944cabcb 100644 --- a/hosting/test_views.py +++ b/hosting/test_views.py @@ -3,15 +3,20 @@ from django.conf import settings from django.test import TestCase, RequestFactory from django.core.urlresolvers import reverse from django.core.urlresolvers import resolve +from django.contrib.auth.tokens import default_token_generator +from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode +from django.utils.encoding import force_bytes + from model_mommy import mommy +from stored_messages.models import Inbox from membership.models import CustomUser, StripeCustomer from .models import VirtualMachineType, HostingOrder, VirtualMachinePlan from .views import DjangoHostingView, RailsHostingView, NodeJSHostingView, LoginView, SignupView, \ PaymentVMView, OrdersHostingDetailView, OrdersHostingListView, VirtualMachineView, \ - VirtualMachinesPlanListView + VirtualMachinesPlanListView, PasswordResetView, PasswordResetConfirmView from utils.tests import BaseTestCase @@ -40,9 +45,12 @@ class DjangoHostingViewTest(TestCase, ProcessVMSelectionTestMixin): self.url = reverse('django.hosting') self.view = DjangoHostingView() self.expected_template = 'hosting/django.html' + HOSTING = 'django' + configuration_detail = dict(VirtualMachinePlan.VM_CONFIGURATION).get(HOSTING) self.expected_context = { - 'hosting': "django", + 'hosting': HOSTING, 'hosting_long': "Django", + 'configuration_detail': configuration_detail, 'domain': "django-hosting.ch", 'google_analytics': "UA-62285904-6", 'email': "info@django-hosting.ch", @@ -56,9 +64,12 @@ class RailsHostingViewTest(TestCase, ProcessVMSelectionTestMixin): self.url = reverse('rails.hosting') self.view = RailsHostingView() self.expected_template = 'hosting/rails.html' + HOSTING = 'rails' + configuration_detail = dict(VirtualMachinePlan.VM_CONFIGURATION).get(HOSTING) self.expected_context = { - 'hosting': "rails", + 'hosting': HOSTING, 'hosting_long': "Ruby On Rails", + 'configuration_detail': configuration_detail, 'domain': "rails-hosting.ch", 'google_analytics': "UA-62285904-5", 'email': "info@rails-hosting.ch", @@ -72,9 +83,12 @@ class NodeJSHostingViewTest(TestCase, ProcessVMSelectionTestMixin): self.url = reverse('node.hosting') self.view = NodeJSHostingView() self.expected_template = 'hosting/nodejs.html' + HOSTING = 'nodejs' + configuration_detail = dict(VirtualMachinePlan.VM_CONFIGURATION).get(HOSTING) self.expected_context = { - 'hosting': "nodejs", + 'hosting': HOSTING, 'hosting_long': "NodeJS", + 'configuration_detail': configuration_detail, 'domain': "node-hosting.ch", 'google_analytics': "UA-62285904-7", 'email': "info@node-hosting.ch", @@ -115,6 +129,7 @@ class PaymentVMViewTest(BaseTestCase): 'memory': 10, 'disk_size': 10000, 'price': 22000, + 'configuration': dict(VirtualMachinePlan.VM_CONFIGURATION).get('django') } } @@ -153,7 +168,8 @@ class PaymentVMViewTest(BaseTestCase): 'memory': hosting_order.vm_plan.memory, 'disk_size': hosting_order.vm_plan.disk_size, 'price': hosting_order.vm_plan.price, - 'hosting_company': hosting_order.vm_plan.vm_type.hosting_company + 'hosting_company': hosting_order.vm_plan.vm_type.hosting_company, + 'configuration': hosting_order.vm_plan.configuration } self.assertEqual(vm_plan, self.session_data.get('vm_specs')) @@ -172,6 +188,92 @@ class PaymentVMViewTest(BaseTestCase): settings.STRIPE_API_PUBLIC_KEY) +class NotificationsViewTest(BaseTestCase): + + def setUp(self): + super(NotificationsViewTest, self).setUp() + + self.url = reverse('hosting:notifications') + self.expected_template = 'hosting/notifications.html' + + self.inboxes = mommy.make(Inbox, user=self.customer, _quantity=2) + self.messages = list(map(lambda x: x.message, self.inboxes)) + + def test_get(self): + + # Anonymous user should get redirect to login + response = self.client.get(self.url) + expected_url = "%s?next=%s" % (reverse('hosting:login'), reverse('hosting:notifications')) + self.assertRedirects(response, expected_url=expected_url, + status_code=302, target_status_code=200) + + # Logged user should get the page + response = self.customer_client.get(self.url, follow=True) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.context['all_notifications'], self.messages) + self.assertTemplateUsed(response, self.expected_template) + + +class MarkAsReadNotificationViewTest(BaseTestCase): + + def setUp(self): + super(MarkAsReadNotificationViewTest, self).setUp() + + self.url = reverse('hosting:notifications') + self.expected_template = 'hosting/notifications.html' + + self.inbox = mommy.make(Inbox, user=self.customer) + self.message = self.inbox.message + + self.url = reverse('hosting:read_notification', kwargs={'pk': self.message.id}) + + def test_post(self): + + # Anonymous user should get redirect to login + response = self.client.get(self.url) + expected_url = "%s?next=%s" % (reverse('hosting:login'), + reverse('hosting:read_notification', + kwargs={'pk': self.message.id})) + self.assertRedirects(response, expected_url=expected_url, + status_code=302, target_status_code=200) + + # Logged user should mark a message as read + response = self.customer_client.post(self.url, follow=True) + self.assertEqual(response.status_code, 200) + self.assertFalse(Inbox.objects.filter(user=self.customer).exists()) + self.assertTemplateUsed(response, self.expected_template) + + +class GenerateVMSSHKeysViewTest(BaseTestCase): + + def setUp(self): + super(GenerateVMSSHKeysViewTest, self).setUp() + + self.vm = mommy.make(VirtualMachinePlan) + self.expected_template = 'hosting/virtual_machine_key.html' + self.url = reverse('hosting:virtual_machine_key', kwargs={'pk': self.vm.id}) + + def test_get(self): + + # Anonymous user should get redirect to login + response = self.client.get(self.url) + expected_url = "%s?next=%s" % (reverse('hosting:login'), + reverse('hosting:virtual_machine_key', + kwargs={'pk': self.vm.id})) + self.assertRedirects(response, expected_url=expected_url, + status_code=302, target_status_code=200) + + # Logged user should get the page + response = self.customer_client.get(self.url, follow=True) + self.assertEqual(response.status_code, 200) + updated_vm = VirtualMachinePlan.objects.get(id=self.vm.id) + self.assertEqual(response.context['public_key'].decode("utf-8"), updated_vm.public_key) + self.assertTrue(response.context['private_key'] is not None) + self.assertEqual(len(response.context['public_key']), 380) + self.assertTrue(len(response.context['private_key']) is 1678 or 1674) + self.assertTemplateUsed(response, self.expected_template) + + class VirtualMachineViewTest(BaseTestCase): def setUp(self): @@ -357,3 +459,78 @@ class SignupViewTest(TestCase): self.user = CustomUser.objects.get(email=self.signup_data.get('email')) self.assertEqual(response.context['user'], self.user) self.assertEqual(response.status_code, 200) + + +class PasswordResetViewTest(BaseTestCase): + + def setUp(self): + super(PasswordResetViewTest, self).setUp() + + self.url = reverse('hosting:reset_password') + self.view = PasswordResetView + self.expected_template = 'hosting/reset_password.html' + self.user = mommy.make('membership.CustomUser') + self.password = 'fake_password' + self.user.set_password(self.password) + self.user.save() + + self.post_data = { + 'email': self.user.email + } + + def test_url_resolve_to_view_correctly(self): + found = resolve(self.url) + self.assertEqual(found.func.__name__, self.view.__name__) + + def test_get(self): + response = self.client.get(self.url) + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed(response, self.expected_template) + + def test_post(self): + response = self.client.post(self.url, data=self.post_data, follow=True) + self.assertEqual(response.status_code, 200) + + def test_test_generate_email_context(self): + context = self.setup_view(self.view()).\ + test_generate_email_context(self.user) + self.assertEqual(context.get('user'), self.user) + self.assertEqual(context.get('site_name'), 'ungleich') + self.assertEqual(len(context.get('token')), 24) + + +class PasswordResetConfirmViewTest(BaseTestCase): + + def setUp(self): + super(PasswordResetConfirmViewTest, self).setUp() + + self.view = PasswordResetConfirmView + self.expected_template = 'hosting/confirm_reset_password.html' + self.user = mommy.make('membership.CustomUser') + self.password = 'fake_password' + self.user.set_password(self.password) + self.user.save() + + self.token = default_token_generator.make_token(self.user) + self.uid = urlsafe_base64_encode(force_bytes(self.user.pk)) + self.url = reverse('hosting:reset_password_confirm', + kwargs={'token': self.token, 'uidb64': self.uid}) + + self.post_data = { + 'new_password1': 'new_password', + 'new_password2': 'new_password' + } + + def test_url_resolve_to_view_correctly(self): + found = resolve(self.url) + self.assertEqual(found.func.__name__, self.view.__name__) + + def test_get(self): + response = self.client.get(self.url) + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed(response, self.expected_template) + + def test_post(self): + response = self.client.post(self.url, data=self.post_data, follow=True) + self.assertEqual(response.status_code, 200) + self.assertTrue(not response.context['form'].errors) diff --git a/hosting/views.py b/hosting/views.py index c66068d1..18d8819e 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -195,13 +195,7 @@ class PasswordResetView(FormView): success_url = reverse_lazy('hosting:login') # form_valid_message = 'Thank you for registering' - def form_valid(self, form): - - email = form.cleaned_data.get('email') - user = CustomUser.objects.get(email=email) - - messages.add_message(self.request, messages.SUCCESS, self.success_message) - + def test_generate_email_context(self, user): context = { 'user': user, 'token': default_token_generator.make_token(user), @@ -210,6 +204,16 @@ class PasswordResetView(FormView): 'base_url': "{0}://{1}".format(self.request.scheme, self.request.get_host()) } + return context + + def form_valid(self, form): + + email = form.cleaned_data.get('email') + user = CustomUser.objects.get(email=email) + + messages.add_message(self.request, messages.SUCCESS, self.success_message) + + context = self.test_generate_email_context(user) email_data = { 'subject': 'Password Reset', 'to': email, @@ -246,15 +250,18 @@ class PasswordResetConfirmView(FormView): return self.form_valid(form) else: messages.error(request, 'Password reset has not been unsuccessful.') + form.add_error(None, 'Password reset has not been unsuccessful.') return self.form_invalid(form) else: messages.error(request, 'The reset password link is no longer valid.') + form.add_error(None, 'Password reset has not been unsuccessful.') return self.form_invalid(form) -class NotificationsView(TemplateView): +class NotificationsView(LoginRequiredMixin, TemplateView): template_name = 'hosting/notifications.html' + login_url = reverse_lazy('hosting:login') def get_context_data(self, **kwargs): context = super(NotificationsView, self).get_context_data(**kwargs) @@ -271,6 +278,7 @@ class NotificationsView(TemplateView): class MarkAsReadNotificationView(LoginRequiredMixin, UpdateView): model = Message success_url = reverse_lazy('hosting:notifications') + login_url = reverse_lazy('hosting:login') fields = '__all__' def post(self, *args, **kwargs): @@ -285,6 +293,7 @@ class GenerateVMSSHKeysView(LoginRequiredMixin, DetailView): model = VirtualMachinePlan template_name = 'hosting/virtual_machine_key.html' success_url = reverse_lazy('hosting:orders') + login_url = reverse_lazy('hosting:login') context_object_name = "virtual_machine" def get_context_data(self, **kwargs): @@ -292,9 +301,10 @@ class GenerateVMSSHKeysView(LoginRequiredMixin, DetailView): context = super(GenerateVMSSHKeysView, self).get_context_data(**kwargs) vm = self.get_object() if not vm.public_key: - private_key = vm.generate_keys() + private_key, public_key = vm.generate_keys() context.update({ - 'private_key': private_key + 'private_key': private_key, + 'public_key': public_key }) return context return context diff --git a/utils/tests.py b/utils/tests.py index 83b87a85..195fc060 100644 --- a/utils/tests.py +++ b/utils/tests.py @@ -1,8 +1,13 @@ from django.test import TestCase from django.test import Client +from django.http.request import HttpRequest + from model_mommy import mommy + + + class BaseTestCase(TestCase): """ Base class to initialize the test cases @@ -28,6 +33,11 @@ class BaseTestCase(TestCase): self.customer_client = self.get_client(self.customer) self.another_customer_client = self.get_client(self.another_customer) + # Request Object + self.request = HttpRequest() + self.request.META['SERVER_NAME'] = 'ungleich.com' + self.request.META['SERVER_PORT'] = '80' + def get_client(self, user): """ Authenticate a user and return the client @@ -64,3 +74,14 @@ class BaseTestCase(TestCase): }] } } + + def setup_view(self, view, *args, **kwargs): + """Mimic as_view() returned callable, but returns view instance. + + args and kwargs are the same you would pass to ``reverse()`` + + """ + view.request = self.request + view.args = args + view.kwargs = kwargs + return view From b34d84657e1b3bd9d34e74ad89ac6123af84fcfe Mon Sep 17 00:00:00 2001 From: Levi <levinoelvm@gmail.com> Date: Thu, 7 Jul 2016 23:35:50 -0500 Subject: [PATCH 5/9] Added HostingUserLoginForm test, Added HostingUserSignupForm test, Added PasswordResetRequestForm Test, Added SetPasswordForm test, Created custom 404 page --- dynamicweb/settings/base.py | 3 +- hosting/forms.py | 3 + hosting/test_forms.py | 116 ++++++++++++++++++ .../static/ungleich_page/css/404.css | 28 +++++ .../templates/ungleich_page/404.html | 28 +++++ utils/forms.py | 1 - utils/stripe_utils.py | 1 - utils/test_forms.py | 48 +++++++- utils/tests.py | 3 - 9 files changed, 224 insertions(+), 7 deletions(-) create mode 100644 hosting/test_forms.py create mode 100644 ungleich_page/static/ungleich_page/css/404.css create mode 100644 ungleich_page/templates/ungleich_page/404.html diff --git a/dynamicweb/settings/base.py b/dynamicweb/settings/base.py index ab2d6763..53a75f3a 100644 --- a/dynamicweb/settings/base.py +++ b/dynamicweb/settings/base.py @@ -136,7 +136,8 @@ TEMPLATES = [ 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') + os.path.join(PROJECT_DIR, 'ungleich/templates/ungleich'), + os.path.join(PROJECT_DIR, 'ungleich_page/templates/ungleich_page') ], 'APP_DIRS': True, diff --git a/hosting/forms.py b/hosting/forms.py index 8007b2f4..2a4d67e3 100644 --- a/hosting/forms.py +++ b/hosting/forms.py @@ -21,6 +21,9 @@ class HostingOrderAdminForm(forms.ModelForm): raise forms.ValidationError("""You can't make a charge over a canceled virtual machine plan""") + if not customer: + raise forms.ValidationError("""You need select a costumer""") + # Make a charge to the customer stripe_utils = StripeUtils() charge_response = stripe_utils.make_charge(customer=customer.stripe_id, diff --git a/hosting/test_forms.py b/hosting/test_forms.py new file mode 100644 index 00000000..e0f5df30 --- /dev/null +++ b/hosting/test_forms.py @@ -0,0 +1,116 @@ +from django.test import TestCase + +from unittest import mock +from model_mommy import mommy + +from .forms import HostingOrderAdminForm, HostingUserLoginForm, HostingUserSignupForm +from .models import VirtualMachinePlan + + +class HostingUserLoginFormTest(TestCase): + + def setUp(self): + password = 'user_password' + self.user = mommy.make('CustomUser') + + self.user.set_password(password) + self.user.save() + self.completed_data = { + 'email': self.user.email, + 'password': password + } + + self.incorrect_data = { + 'email': 'test', + } + + def test_valid_form(self): + form = HostingUserLoginForm(data=self.completed_data) + self.assertTrue(form.is_valid()) + + def test_invalid_form(self): + form = HostingUserLoginForm(data=self.incorrect_data) + self.assertFalse(form.is_valid()) + + +class HostingUserSignupFormTest(TestCase): + + def setUp(self): + + self.completed_data = { + 'name': 'test name', + 'email': 'test@ungleich.com', + 'password': 'test_password', + 'confirm_password': 'test_password' + } + + self.incorrect_data = { + 'email': 'test', + } + + def test_valid_form(self): + form = HostingUserSignupForm(data=self.completed_data) + self.assertTrue(form.is_valid()) + + def test_invalid_form(self): + form = HostingUserSignupForm(data=self.incorrect_data) + self.assertFalse(form.is_valid()) + + +class HostingOrderAdminFormTest(TestCase): + + def setUp(self): + + self.customer = mommy.make('StripeCustomer') + self.vm_plan = mommy.make('VirtualMachinePlan') + self.vm_canceled_plan = mommy.make('VirtualMachinePlan', + status=VirtualMachinePlan.CANCELED_STATUS) + + self.mocked_charge = { + 'amount': 5100, + 'amount_refunded': 0, + 'balance_transaction': 'txn_18U99zGjsLAXdRPzUJKkBx3Q', + 'captured': True, + 'created': 1467785123, + 'currency': 'chf', + 'customer': 'cus_8V61MvJvMd0PhM', + 'status': 'succeeded' + } + + self.completed_data = { + 'customer': self.customer.id, + 'vm_plan': self.vm_plan.id, + } + + self.incompleted_data = { + 'vm_plan': self.vm_plan.id, + 'customer': None + } + + @mock.patch('utils.stripe_utils.StripeUtils.make_charge') + def test_valid_form(self, stripe_mocked_call): + stripe_mocked_call.return_value = { + 'paid': True, + 'response_object': self.mocked_charge, + 'error': None + } + form = HostingOrderAdminForm(data=self.completed_data) + self.assertTrue(form.is_valid()) + + @mock.patch('utils.stripe_utils.StripeUtils.make_charge') + def test_invalid_form_canceled_vm(self, stripe_mocked_call): + + self.completed_data.update({ + 'vm_plan': self.vm_canceled_plan.id + }) + stripe_mocked_call.return_value = { + 'paid': True, + 'response_object': self.mocked_charge, + 'error': None + } + form = HostingOrderAdminForm(data=self.completed_data) + self.assertFalse(form.is_valid()) + + def test_invalid_form(self): + form = HostingOrderAdminForm(data=self.incompleted_data) + self.assertFalse(form.is_valid()) diff --git a/ungleich_page/static/ungleich_page/css/404.css b/ungleich_page/static/ungleich_page/css/404.css new file mode 100644 index 00000000..2528973d --- /dev/null +++ b/ungleich_page/static/ungleich_page/css/404.css @@ -0,0 +1,28 @@ +.error { + margin: 0 auto; + text-align: center; +} + +.error-code { + bottom: 60%; + color: #2d353c; + font-size: 96px; + line-height: 100px; +} + +.error-desc { + font-size: 12px; + color: #647788; +} + +.m-b-10 { + margin-bottom: 10px!important; +} + +.m-b-20 { + margin-bottom: 20px!important; +} + +.m-t-20 { + margin-top: 20px!important; +} diff --git a/ungleich_page/templates/ungleich_page/404.html b/ungleich_page/templates/ungleich_page/404.html new file mode 100644 index 00000000..6b6caf70 --- /dev/null +++ b/ungleich_page/templates/ungleich_page/404.html @@ -0,0 +1,28 @@ +{% load staticfiles bootstrap3%} + +<!DOCTYPE html> +<html> + <head> + <link href="{% static 'ungleich_page/css/404.css' %}" rel="stylesheet"> + <title>404 | ungleich</title> + </head> + <body> + + <div class="error"> + <div class="error-code m-b-10 m-t-20">404 <i class="fa fa-warning"></i></div> + <h3 class="font-bold">We couldn't find the page..</h3> + + <div class="error-desc"> + Sorry, but the page you are looking for was either not found or does not exist. <br/> + Try refreshing the page or click the button below to go back to the Homepage. + <div> + <a class=" login-detail-panel-button btn" href="http://www.vmware.com/"> + <i class="fa fa-arrow-left"></i> + Go back to Homepage + </a> + </div> + </div> + </div> + + </body> +</html> diff --git a/utils/forms.py b/utils/forms.py index 7af6f99a..e87d3579 100644 --- a/utils/forms.py +++ b/utils/forms.py @@ -48,7 +48,6 @@ class SetPasswordForm(forms.Form): return password2 - class BillingAddressForm(forms.ModelForm): token = forms.CharField(widget=forms.HiddenInput()) diff --git a/utils/stripe_utils.py b/utils/stripe_utils.py index fb0a328e..eb17ea09 100644 --- a/utils/stripe_utils.py +++ b/utils/stripe_utils.py @@ -82,7 +82,6 @@ class StripeUtils(object): ) return customer - @handleStripeError def make_charge(self, amount=None, customer=None): amount = int(amount * 100) # stripe amount unit, in cents diff --git a/utils/test_forms.py b/utils/test_forms.py index dc6d9fcc..46285fc5 100644 --- a/utils/test_forms.py +++ b/utils/test_forms.py @@ -1,5 +1,51 @@ from django.test import TestCase -from .forms import ContactUsForm, BillingAddressForm +from .forms import ContactUsForm, BillingAddressForm, PasswordResetRequestForm,\ + SetPasswordForm + +from model_mommy import mommy + + +class PasswordResetRequestFormTest(TestCase): + + def setUp(self): + self.user = mommy.make('CustomUser') + self.completed_data = { + 'email': self.user.email, + } + + self.incorrect_data = { + 'email': 'test', + } + + def test_valid_form(self): + form = PasswordResetRequestForm(data=self.completed_data) + self.assertTrue(form.is_valid()) + + def test_invalid_form(self): + form = PasswordResetRequestForm(data=self.incorrect_data) + self.assertFalse(form.is_valid()) + + +class SetPasswordFormTest(TestCase): + + def setUp(self): + # self.user = mommy.make('CustomUser') + self.completed_data = { + 'new_password1': 'new_password', + 'new_password2': 'new_password', + } + + self.incorrect_data = { + 'email': 'test', + } + + def test_valid_form(self): + form = SetPasswordForm(data=self.completed_data) + self.assertTrue(form.is_valid()) + + def test_invalid_form(self): + form = SetPasswordForm(data=self.incorrect_data) + self.assertFalse(form.is_valid()) class ContactUsFormTest(TestCase): diff --git a/utils/tests.py b/utils/tests.py index 195fc060..42831050 100644 --- a/utils/tests.py +++ b/utils/tests.py @@ -5,9 +5,6 @@ from django.http.request import HttpRequest from model_mommy import mommy - - - class BaseTestCase(TestCase): """ Base class to initialize the test cases From 228065b1d5be90bc557c0bcc4b6361279e2ac6b8 Mon Sep 17 00:00:00 2001 From: Levi <levinoelvm@gmail.com> Date: Thu, 7 Jul 2016 23:50:02 -0500 Subject: [PATCH 6/9] #hotfix fixing 404 redirect home --- ungleich_page/templates/ungleich_page/404.html | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ungleich_page/templates/ungleich_page/404.html b/ungleich_page/templates/ungleich_page/404.html index 6b6caf70..bc0dc5ba 100644 --- a/ungleich_page/templates/ungleich_page/404.html +++ b/ungleich_page/templates/ungleich_page/404.html @@ -1,5 +1,4 @@ {% load staticfiles bootstrap3%} - <!DOCTYPE html> <html> <head> @@ -16,7 +15,7 @@ Sorry, but the page you are looking for was either not found or does not exist. <br/> Try refreshing the page or click the button below to go back to the Homepage. <div> - <a class=" login-detail-panel-button btn" href="http://www.vmware.com/"> + <a class=" login-detail-panel-button btn" href="{% url 'ungleich_page:landing' %}"> <i class="fa fa-arrow-left"></i> Go back to Homepage </a> From fae5d616e6ee4040f42a1724846a677bb4c0596a Mon Sep 17 00:00:00 2001 From: Levi <levinoelvm@gmail.com> Date: Thu, 7 Jul 2016 23:52:24 -0500 Subject: [PATCH 7/9] #hotfix fixing 404 style --- ungleich_page/templates/ungleich_page/404.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ungleich_page/templates/ungleich_page/404.html b/ungleich_page/templates/ungleich_page/404.html index bc0dc5ba..2fbac545 100644 --- a/ungleich_page/templates/ungleich_page/404.html +++ b/ungleich_page/templates/ungleich_page/404.html @@ -1,4 +1,4 @@ -{% load staticfiles bootstrap3%} +{% load staticfiles%} <!DOCTYPE html> <html> <head> From 37287cf2997dd4eb499a0333f92cd6f82421792d Mon Sep 17 00:00:00 2001 From: Levi <levinoelvm@gmail.com> Date: Sat, 9 Jul 2016 16:04:50 -0500 Subject: [PATCH 8/9] Added HostingPriceView test, - [ ] Added resolving url test to few views --- hosting/test_views.py | 48 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/hosting/test_views.py b/hosting/test_views.py index 944cabcb..5e43db88 100644 --- a/hosting/test_views.py +++ b/hosting/test_views.py @@ -16,7 +16,8 @@ from membership.models import CustomUser, StripeCustomer from .models import VirtualMachineType, HostingOrder, VirtualMachinePlan from .views import DjangoHostingView, RailsHostingView, NodeJSHostingView, LoginView, SignupView, \ PaymentVMView, OrdersHostingDetailView, OrdersHostingListView, VirtualMachineView, \ - VirtualMachinesPlanListView, PasswordResetView, PasswordResetConfirmView + VirtualMachinesPlanListView, PasswordResetView, PasswordResetConfirmView, HostingPricingView, \ + NotificationsView, MarkAsReadNotificationView, GenerateVMSSHKeysView from utils.tests import BaseTestCase @@ -96,6 +97,36 @@ class NodeJSHostingViewTest(TestCase, ProcessVMSelectionTestMixin): } +class HostingPricingViewTest(TestCase): + + def setUp(self): + self.url = reverse('hosting:pricing') + self.view = HostingPricingView() + self.expected_template = 'hosting/hosting_pricing.html' + + configuration_options = dict(VirtualMachinePlan.VM_CONFIGURATION) + self.expected_context = { + 'configuration_options': configuration_options, + 'email': "info@django-hosting.ch", + 'vm_types': VirtualMachineType.get_serialized_vm_types(), + } + + def url_resolve_to_view_correctly(self): + found = resolve(self.url) + self.assertEqual(found.func.__name__, self.view.__name__) + + def get(self): + response = self.client.get(self.url) + self.assertEqual(response.status_code, 200) + self.assertEqual(self.view.get_context_data(), self.expected_context) + self.assertTemplateUsed(response, self.expected_template) + + def test_anonymous_post(self): + response = self.client.post(self.url) + self.assertRedirects(response, expected_url=reverse('hosting:login'), + status_code=302, target_status_code=200) + + class PaymentVMViewTest(BaseTestCase): def setUp(self): @@ -193,12 +224,17 @@ class NotificationsViewTest(BaseTestCase): def setUp(self): super(NotificationsViewTest, self).setUp() + self.view = NotificationsView self.url = reverse('hosting:notifications') self.expected_template = 'hosting/notifications.html' self.inboxes = mommy.make(Inbox, user=self.customer, _quantity=2) self.messages = list(map(lambda x: x.message, self.inboxes)) + def test_url_resolve_to_view_correctly(self): + found = resolve(self.url) + self.assertEqual(found.func.__name__, self.view.__name__) + def test_get(self): # Anonymous user should get redirect to login @@ -219,6 +255,7 @@ class MarkAsReadNotificationViewTest(BaseTestCase): def setUp(self): super(MarkAsReadNotificationViewTest, self).setUp() + self.view = MarkAsReadNotificationView self.url = reverse('hosting:notifications') self.expected_template = 'hosting/notifications.html' @@ -227,6 +264,10 @@ class MarkAsReadNotificationViewTest(BaseTestCase): self.url = reverse('hosting:read_notification', kwargs={'pk': self.message.id}) + def test_url_resolve_to_view_correctly(self): + found = resolve(self.url) + self.assertEqual(found.func.__name__, self.view.__name__) + def test_post(self): # Anonymous user should get redirect to login @@ -249,10 +290,15 @@ class GenerateVMSSHKeysViewTest(BaseTestCase): def setUp(self): super(GenerateVMSSHKeysViewTest, self).setUp() + self.view = GenerateVMSSHKeysView self.vm = mommy.make(VirtualMachinePlan) self.expected_template = 'hosting/virtual_machine_key.html' self.url = reverse('hosting:virtual_machine_key', kwargs={'pk': self.vm.id}) + def test_url_resolve_to_view_correctly(self): + found = resolve(self.url) + self.assertEqual(found.func.__name__, self.view.__name__) + def test_get(self): # Anonymous user should get redirect to login From 0d6e43de44e4381a29a7209c72f592514c955cb8 Mon Sep 17 00:00:00 2001 From: Levi <levinoelvm@gmail.com> Date: Sat, 9 Jul 2016 17:21:57 -0500 Subject: [PATCH 9/9] Added ungleich landing translation --- ungleich_page/locale/de/LC_MESSAGES/django.mo | Bin 2551 -> 4845 bytes ungleich_page/locale/de/LC_MESSAGES/django.po | 67 ++++++++++++------ .../ungleich_page/includes/_services.html | 3 +- 3 files changed, 47 insertions(+), 23 deletions(-) diff --git a/ungleich_page/locale/de/LC_MESSAGES/django.mo b/ungleich_page/locale/de/LC_MESSAGES/django.mo index ef04e0dae6d90fec8960faea3dc5846525199f7d..b44e0c322186db93d543c900779a04a0693b89e1 100644 GIT binary patch literal 4845 zcmbVPON<;x8Lp5JV0k7y!}E85;K%f2*CcV+wd0Jv<K4CQVRm+7g1FRl*YuR9yLwzz zz3W*7QceiSAp#*G5s4gfi8!F-5CzG(FyV&e10p9B2`)JxA+AUq`2Om7?2d&aC~fc8 zkE*}^$M^sB&pQv^_X~k>3fC8L`Fn&|0{-!R_=oYvy+V8e_&eZ9;Gco-=MVP@@p-`g zz&YSCAkV7-KL%U{eg=4L^xXhIfad}50pN4MdEn1S-`@fr;Q2Q2bHK~k<RM@T{3!4T zz?XnO1rC9=`ym_nPayupDuf>cZUK4y4d62HC%{*Lw}GtB;DeQZF92V|^RI!Q25x^y zh)024AWRg$1bzefB9QHS3&?ie2C{430e%GdSKz0B{{XU{_hRxVfDZt_415^K_BMbl zuLbPE$KMCCeeZr)hy~z15XSPq3FP+|fGqcWK=$*E(f8+pY{##F5FuUz!Zh(FkoWxq za1Ho2kk=8E`Vnvm$bK0DzXJRV5P#y|`1cfW4x7IXTm!OS>ElAcHR2}lVc>H>UjH(X z`SB+3Rp7sXp9TH~o8wjtfo}l+1mtzk!}v46-vZgM?*b#>-+^rJLl0JX9Ei}1GeDMi z7RY?s0kWQb;3t7U1hQX$280OlD)0nw2!vb1;PpJ%-@Jz9@L+!&9_(k9#RK6B`moL{ z7hwthe+2kdTqkj{9=sPQD87!1^M!5V!M^$$F6JN)_60%^xW)0|0Tl)D4F<=K2Xl8k zI0t#KpTC6bI4<@H$D3ougJaD66LhgK9i5g|dR-WjjwH2|-P5rWCrguNO2=Kwl{Ah_ z;Yr;nWk$ZB<A3|{ODAae)albt%-2QZ!q)DNxQi@&@-p_c>oBW7usZE}`u!_3*<vMc zuaa2SsrJ}lC6l&vGmq4_#dQ^XvD%>3!g*~{F@to~7D*X<M`Eo*gQBFa+*2gYfNqou zOA1q?>!tJLlu4+BxrS6ObygRb3vI$1FV>ak#g-Dar?R52tQ+4Rqic%1>?w++RaAss zEV+U6Dkqub+Gyu1ch+e|8DA7NYS_4&X;a>;(Xwi52`zTprSWAA3M7=Xf_Y(8U99sg zYE|NzCe=nM%a(2^=VYp=RmL%HJ7ty)5Gv!vHq>_$k8CTM3^+n*L79f}``ULaYqrbw zfDq+sf$aA)9f!D|XQPTvN7>*r%OWCQM2YHW#env-^&IwG#$D*+sIU}|c%v%O>L#p) znxsv~N;m*PDB+(DrXJb0E{;6HAJ?Tx;iAiOPu8GE+kt+{)@Xyx8r`#hW+eNr^aST5 zj*XqDlhkNmF;n(*f*2Kf->R-Mjy>UutYiwfIyG{6z+A90?zz5<VZNm{M>JPVz-nU` zgiT}zYru;c8|p!K>Q<B-TQq%!J~<OcBYj}mz{80t+0u~9ivq!63OLz8^jl>+*Iw*& z;W=$VQ*A&qQza+3Hcpo?!|PmuRDpz(4+tbE=5z|<kaQup6+I&J{t|l>SqPS&ZQG~l zStfWzx3!}|6B9Ock*9>f=uEaV?FVC{==f^m1e628Z6Gh3fX5cdcA#vA$2x66{!Gxc zqkgfktV#$}ykr&UW<pzig|*NJacC7Czt4flG<?QiHdVXKz>ROKybRM<GGwr_ms)jk zjak5k#CTQYoTyF?@Om)u)%lYGrV@*UhRm1{M>*k4l>+ROYn&=$SB6vw<)c%SP?9Uf zO_TzyObi)b$(#ZSr~C*tqbdb~m17z*n@~|Jla@DIHEKpoZ*WC=*_N)#sR{XAJ*XUG zO9&5zlS`Esl2Rn6$J=PXus!Bc#a>wEL7H4ANfrpv$olS>fOGkR;XV{b>?1qK$pqn4 zbo#OHoFP~C`-Sy&F^wkQ9*U$WtY8<9tZ;&Ng17A;XorHOu+_wEs|dsNDfmt$(-((S zS#3n)`aM~l$<oAK<)#aez$n%p=F^_Ex_~Fx;~){@-~=i@9}3Z05<P?PBfS^tYUARS z#!8c(X>PaHwl?Rs6`L4sxKt<6g)()~PO(UHSGRVei|kJ<iVz94NT=r)o`~ijixwWI zg>NnL{p0iVm>6xVJ<Y;L^PX)swxf-$Xc<S**5bz6sqb!WHs{t+`Jx?*!!ncb&mvvg zU0<io#zvFQHa8k;>rXe9m$#d()>D{j(h~JN+5wLiX}z(zvdb!;U0;7X)Ahop(>YLe zV{M~3wQr$5KZoS@a7(l^=tFO>Zu&?28IpU6;;yt#`E$EFm!c=9^H?o-Eoz#$NWh#$ zda|wk+|>++e->RTZ0;7x^uwb&cY2Ac+jGYa9Xm%0Czj^Km7~o9PMy{ry+T_hs`NNO z4LS!;qYh$UtC^Nz_+lH!%{cCN5sgi8-g?aerxxUt=59^IOw%y)$PL1E)qC$AqUROL zps9A$HbuLzuQD(bRSRJ=Xe`<aHP+!sjC)hfNTKB6oMcp^G)G<M;~%H?XrAMy3TB|D z2ylj9I~b<Bs8LPh_aQNZa}Zv>RhdE8p)K`>w{ny?bSYR`=GFYFRrsG956DPxuq%{- zTy^bT8jnp_V?!*J?Gzab5NnTv3~DE8w*6>n$VFroPCFcBLC5@P3qTF-JbKlk7Fv{p zl2SgU&;MV|!F83QkwIi8$5;=&2ikxTt`LW##BEzyf}1$p;ndLfROBl2P6!4XhxOs> z4&3Ajk6VT9X{(SRI2ZUp*$B-7Cjs11$?J`Bq!KB!Ore?|>3QC>Uw~GrM7oYR&lVVh zN6|5+E-RQWjW!BpM>NnaAqWO6R|FM2JE1q}81~TYO)`w9T<qGy=pG&Uz|=}Y%fRLa zDbSi%CWMn1Rfg7>nW0yRjyPHH+Cv`f76=2%aA$ZcMK{%B7T_N8@ylXV>-iL3Mu|Y? zaGwyi!7KJD(qv!hgF5XR95%B7MTd#WIQX3(-tq^Y5;P3mnJ(gvHi9e9w2MH;08NlT zEdB~CgmK(f8@#tjsj;`t3$8R3Q849B5}l_F*fySCRfCYkm<k_uBs|8gGM9IdyA!sm zu^$|Af&-dhgQL9UsLXZ;%Cx27`ckliCLMyQ+=_}Z{;eQQTPxJvpm=Ziy5T*zP=Jur z`WgHuSjT<V$RY@s3dt9X=-Q~GXoY?l$;*)skpbT_G-3iv_~O00)<ZG~XVT#<gWjKO zVrVE@RYSo(3r)qLSaV}>D9(ay5~Emyo)ClIo6f_lBTBC*D9E)(ae?s2X^r^**JdMC usf9LNl_rrH={oH-Sh4A6P%uWS9$L`1!<Ug&XgEwLjp5wkK=I816#EZhi?)UU delta 808 zcmYMy&o5k27{>8;7>3piLtE6Zi-^z&+QdY<5K@Z7LTqIcCSx=m1f2w%X)L5o(=JSi zg}=hAux?UK8eKH8C{}EwT}X&@Ro~|_oXokOGtQlJ-t!!P8?H){$Hm??qdD|Sb&uII znZ<+l4Rdf0pW;3~z%#7COAKHz+w2KCsQD23um^)Uh;2A}JO3HW&62jkAeV_Pti^3C z!{7J{FHjqFdd(hVKel2RnX)g~h^wfLcTn$tMQXEOSbzuk2v5+D|FDSt&ByVo`9m41 z(hzFF0enjuv#14+sFI594{D)Yj;BN_Q1czA1bR`0j3OV4@hHL>9KiRe-+jj#_P0|8 zd6>ha4g;tJ+VLe0Vj-^F&aY#L@7t&Z&ankAQH9j!-}oFkq;+E{_MxsUj7ne$lO+s3 zFi--Us04Pg34b5~xQ(IP9`e*F^}haJbamQ93ul_v&<2XVm|jI!VKsCm$9dfYQSXo& z4d2~Jq(dk1yQVu=8kwd$)oIjJyrR~%D2den?4RXB(fRqYSkxIwL?R3)67l$=GZLMC zmwN5{=$~A464SAG^i5>4t2sTKzn+zTQIO|J9r~TrLg8{?Y%Vc%|3>;#aNKhZ^k`5w diff --git a/ungleich_page/locale/de/LC_MESSAGES/django.po b/ungleich_page/locale/de/LC_MESSAGES/django.po index a5cb93c7..f8b938ce 100644 --- a/ungleich_page/locale/de/LC_MESSAGES/django.po +++ b/ungleich_page/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: 2016-04-15 19:16-0500\n" +"POT-Creation-Date: 2016-07-09 16:47-0500\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -70,32 +70,32 @@ msgstr "und" msgid "the story continues!" msgstr "Die Geschichte geht weiter!" -#: templates/ungleich_page/includes/_contact_us.html:14 -#: templates/ungleich_page/includes/_contact_us.html:21 views.py:35 +#: templates/ungleich_page/includes/_contact_us.html:15 +#: templates/ungleich_page/includes/_contact_us.html:28 views.py:36 msgid "Contact Us" msgstr "Kontaktieren Sie uns" -#: templates/ungleich_page/includes/_contact_us.html:16 +#: templates/ungleich_page/includes/_contact_us.html:18 msgid "Join us at" msgstr "Schliessen Sie sich uns an" -#: templates/ungleich_page/includes/_contact_us.html:16 +#: templates/ungleich_page/includes/_contact_us.html:19 msgid "Digital Glarus" msgstr "Digital Glarus" -#: templates/ungleich_page/includes/_contact_us.html:17 +#: templates/ungleich_page/includes/_contact_us.html:20 msgid "a great co-working space in the middle of Alps!" msgstr "ein wunderschöner Co-Working Space mitten in den Alpen" -#: templates/ungleich_page/includes/_contact_us.html:17 +#: templates/ungleich_page/includes/_contact_us.html:21 msgid "You can contact us at" msgstr "Sie können uns kontaktieren unter" -#: templates/ungleich_page/includes/_contact_us.html:20 +#: templates/ungleich_page/includes/_contact_us.html:26 msgid "or" msgstr "oder" -#: templates/ungleich_page/includes/_contact_us.html:50 +#: templates/ungleich_page/includes/_contact_us.html:60 msgid "Submit" msgstr "Absenden" @@ -126,21 +126,24 @@ msgid "" "infrastructure is powered by Free and Open Source Software like OpenNebula, " "Qemu and GlusterFS." msgstr "" +"Wir offerieren hohe Verfügbarkeit für das Hosting in Deutschland und in der " +"Schweiz. Unsere Infrastruktur ist unterstützt durch Free and Open Source " +"Software wie OpenNebula." #: templates/ungleich_page/includes/_portfolio.html:24 msgid "Rails Hosting" -msgstr "" +msgstr "Rails Hosting" #: templates/ungleich_page/includes/_portfolio.html:26 msgid "" "Ready to go live with your Ruby on Rails application? We offer you ready-to-" "deploy virtual machines or configure your existing infrastructure for Ruby " "on Rails." -msgstr "" +msgstr "Sind bereit mit ihrem Ruby on Rails Applikation live zu gehen?" #: templates/ungleich_page/includes/_portfolio.html:32 msgid " Configuration as a Service" -msgstr "" +msgstr "Konfiguration als Service" #: templates/ungleich_page/includes/_portfolio.html:34 msgid "" @@ -148,11 +151,23 @@ msgid "" "experienced team that configure your systems to provide service like DNS, E-" "Mail, Databases or Webservers." msgstr "" +"Sie brauchen eine Konfiguration? Mit ungleich haben sie ein erfahrenes Team " +"gefunden, dass ihnen die Konfiguration von DNS, E-Mail, Datenbanken oder " +"Webservern für ihr System anbietet" #: templates/ungleich_page/includes/_services.html:8 msgid "our services" msgstr "Unsere Dienstleistungen" +#: templates/ungleich_page/includes/_services.html:9 +msgid "We support our clients in all areas of Unix infrastructure." +msgstr "" + +#: templates/ungleich_page/includes/_services.html:10 +msgid "" +"Our top notch configuration management is refreshingly simple and reliable." +msgstr "" + #: templates/ungleich_page/includes/_services.html:18 msgid "Hosting" msgstr "Hosting" @@ -162,6 +177,8 @@ msgid "" "Ruby on Rails. Java hosting, Django hosting, we make it everything run " "smooth and safe." msgstr "" +"Ruby on Rails. Java hosting, Django hosting, wir garantieren einen " +"reibungslosen Ablauf" #: templates/ungleich_page/includes/_services.html:28 msgid "Configuration as a Service" @@ -173,16 +190,21 @@ msgid "" "needs to configured, we provide comprehensive solutions. Amazon, rackspace " "or bare metal servers, we configure for you." msgstr "" +"Ruby on Rails, Django, Java, Webserver, Mailserver, jegliche Infrastruktur " +"welche eine Konfiguration braucht, wir offerieren umfassende Lösungen, " +"Amazon, Rackspace oder Bare Metal Servers, wir konfigurieren alles." #: templates/ungleich_page/includes/_services.html:38 msgid "Linux System Engineering" -msgstr "" +msgstr "Linux System Engineering" #: templates/ungleich_page/includes/_services.html:41 msgid "" "Let your developers develop! We take care of your system administration. " "Gentoo, Archlinux, Debian, Ubuntu, and many more." msgstr "" +"Lassen sie ihre Entwickler entwickeln! Wir kümmern uns um ihre " +"Systemadministration. Gentoo, Archlinux, Debian, Ubuntu und viele mehr." #: templates/ungleich_page/includes/_team.html:8 msgid "Why ungleich?*" @@ -190,7 +212,7 @@ msgstr "Warum ungleich?" #: templates/ungleich_page/includes/_team.html:9 msgid "What our customers say" -msgstr "" +msgstr "Was unsere Kunden sagen" #: templates/ungleich_page/includes/_team.html:29 msgid "" @@ -249,16 +271,19 @@ msgstr "" #: templates/ungleich_page/includes/_team.html:95 msgid "*ungleich means not equal to (≠) U+2260." -msgstr "" +msgstr "*ungleich bedeutet nicht gleich wie (≠) U+2260." -#: urls.py:7 -msgid "contact/?$" -msgstr "" +#: urls.py:8 +#, fuzzy +#| msgid "Contact Us" +msgid "contact/$" +msgstr "Kontaktieren Sie uns" -#: views.py:25 +#: views.py:26 msgid "Message Successfully Sent" msgstr "Nachricht erfolgreich versendet" -#: views.py:36 +#: views.py:37 msgid "If you have any question, just send us an email." -msgstr "Wenn Sie irgendwelche Fragen haben, schicken Sie uns einfach eine E-Mail." +msgstr "" +"Wenn Sie irgendwelche Fragen haben, schicken Sie uns einfach eine E-Mail." diff --git a/ungleich_page/templates/ungleich_page/includes/_services.html b/ungleich_page/templates/ungleich_page/includes/_services.html index 1d35528a..d07e1336 100644 --- a/ungleich_page/templates/ungleich_page/includes/_services.html +++ b/ungleich_page/templates/ungleich_page/includes/_services.html @@ -6,8 +6,7 @@ <div class="row"> <div class="col-lg-12 text-center wow fadeInDown"> <h2 class="section-heading">{% trans "our services" %}</h2> - <h3 class="section-subheading text-muted">We support our clients in all areas of Unix infrastructure.<p></p> - Our top notch configuration management is refreshingly simple and reliable."</h3> + <h3 class="section-subheading text-muted">{% trans "" %}</h3> </div> </div> <div class="row text-center">