Merged master into task/3701/enable_monthly_payments

This commit is contained in:
PCoder 2017-08-24 04:06:48 +05:30
commit a964f2fb5b
17 changed files with 968 additions and 384 deletions

View file

@ -41,13 +41,15 @@ def retry_task(task, exception=None):
@app.task(bind=True, max_retries=settings.CELERY_MAX_RETRIES) @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, billing_address_id,
charge): charge):
vm_id = None vm_id = None
try: try:
final_price = specs.get('price') 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() customer = StripeCustomer.objects.filter(id=stripe_customer_id).first()
# Create OpenNebulaManager # Create OpenNebulaManager
manager = OpenNebulaManager(email=settings.OPENNEBULA_USERNAME, manager = OpenNebulaManager(email=settings.OPENNEBULA_USERNAME,
@ -114,7 +116,8 @@ def create_vm_task(self, vm_template_id, user, specs, template, stripe_customer_
'subject': settings.DCL_TEXT + " Order from %s" % context['email'], 'subject': settings.DCL_TEXT + " Order from %s" % context['email'],
'from_email': settings.DCL_SUPPORT_FROM_ADDRESS, 'from_email': settings.DCL_SUPPORT_FROM_ADDRESS,
'to': ['info@ungleich.ch'], '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']], 'reply_to': [context['email']],
} }
email = EmailMessage(**email_data) email = EmailMessage(**email_data)
@ -124,11 +127,13 @@ def create_vm_task(self, vm_template_id, user, specs, template, stripe_customer_
try: try:
retry_task(self) retry_task(self)
except MaxRetriesExceededError: 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) logger.error(msg_text)
# Try sending email and stop # Try sending email and stop
email_data = { 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, 'from_email': settings.DCL_SUPPORT_FROM_ADDRESS,
'to': ['info@ungleich.ch'], '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)

View file

@ -1,3 +1,120 @@
# from django.test import TestCase # 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. # 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'):
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()))

View file

@ -18,7 +18,8 @@ from hosting.models import HostingOrder
from utils.stripe_utils import StripeUtils from utils.stripe_utils import StripeUtils
from membership.models import CustomUser, StripeCustomer from membership.models import CustomUser, StripeCustomer
from opennebula_api.models import OpenNebulaManager 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 from datacenterlight.tasks import create_vm_task
@ -35,9 +36,11 @@ class SuccessView(TemplateView):
elif 'token' not in request.session: elif 'token' not in request.session:
return HttpResponseRedirect(reverse('datacenterlight:payment')) return HttpResponseRedirect(reverse('datacenterlight:payment'))
elif 'order_confirmation' not in request.session: elif 'order_confirmation' not in request.session:
return HttpResponseRedirect(reverse('datacenterlight:order_confirmation')) return HttpResponseRedirect(
reverse('datacenterlight:order_confirmation'))
else: 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']: 'token', 'customer']:
if session_var in request.session: if session_var in request.session:
del request.session[session_var] del request.session[session_var]
@ -53,7 +56,8 @@ class PricingView(TemplateView):
templates = manager.get_templates() templates = manager.get_templates()
context = { context = {
'templates': VirtualMachineTemplateSerializer(templates, many=True).data, 'templates': VirtualMachineTemplateSerializer(templates,
many=True).data,
} }
except: except:
messages.error(request, messages.error(request,
@ -77,7 +81,8 @@ class PricingView(TemplateView):
manager = OpenNebulaManager() manager = OpenNebulaManager()
template = manager.get_template(template_id) template = manager.get_template(template_id)
request.session['template'] = VirtualMachineTemplateSerializer(template).data request.session['template'] = VirtualMachineTemplateSerializer(
template).data
if not request.user.is_authenticated(): if not request.user.is_authenticated():
request.session['next'] = reverse('hosting:payment') request.session['next'] = reverse('hosting:payment')
@ -99,7 +104,8 @@ class BetaAccessView(FormView):
def form_valid(self, form): def form_valid(self, form):
context = { 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 = { email_data = {
@ -129,7 +135,8 @@ class BetaAccessView(FormView):
email = BaseEmail(**email_data) email = BaseEmail(**email_data)
email.send() 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', {}) return render(self.request, 'datacenterlight/beta_success.html', {})
@ -154,7 +161,8 @@ class BetaProgramView(CreateView):
# data = VirtualMachineTemplateSerializer(templates, many=True).data # data = VirtualMachineTemplateSerializer(templates, many=True).data
context.update({ 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 'vms': vms
}) })
return context return context
@ -164,7 +172,8 @@ class BetaProgramView(CreateView):
vms = BetaAccessVM.create(data) vms = BetaAccessVM.create(data)
context = { 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'), 'email': data.get('email'),
'name': data.get('name'), 'name': data.get('name'),
'vms': vms 'vms': vms
@ -181,7 +190,8 @@ class BetaProgramView(CreateView):
email = BaseEmail(**email_data) email = BaseEmail(**email_data)
email.send() 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()) return HttpResponseRedirect(self.get_success_url())
@ -225,7 +235,8 @@ class IndexView(CreateView):
storage_field = forms.IntegerField(validators=[self.validate_storage]) storage_field = forms.IntegerField(validators=[self.validate_storage])
price = request.POST.get('total') price = request.POST.get('total')
template_id = int(request.POST.get('config')) 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 template_data = VMTemplateSerializer(template).data
name = request.POST.get('name') name = request.POST.get('name')
@ -237,36 +248,46 @@ class IndexView(CreateView):
cores = cores_field.clean(cores) cores = cores_field.clean(cores)
except ValidationError as err: except ValidationError as err:
msg = '{} : {}.'.format(cores, str(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,
return HttpResponseRedirect(reverse('datacenterlight:index') + "#order_form") extra_tags='cores')
return HttpResponseRedirect(
reverse('datacenterlight:index') + "#order_form")
try: try:
memory = memory_field.clean(memory) memory = memory_field.clean(memory)
except ValidationError as err: except ValidationError as err:
msg = '{} : {}.'.format(memory, str(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,
return HttpResponseRedirect(reverse('datacenterlight:index') + "#order_form") extra_tags='memory')
return HttpResponseRedirect(
reverse('datacenterlight:index') + "#order_form")
try: try:
storage = storage_field.clean(storage) storage = storage_field.clean(storage)
except ValidationError as err: except ValidationError as err:
msg = '{} : {}.'.format(storage, str(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,
return HttpResponseRedirect(reverse('datacenterlight:index') + "#order_form") extra_tags='storage')
return HttpResponseRedirect(
reverse('datacenterlight:index') + "#order_form")
try: try:
name = name_field.clean(name) name = name_field.clean(name)
except ValidationError as err: except ValidationError as err:
msg = '{} {}.'.format(name, _('is not a proper name')) 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,
return HttpResponseRedirect(reverse('datacenterlight:index') + "#order_form") extra_tags='name')
return HttpResponseRedirect(
reverse('datacenterlight:index') + "#order_form")
try: try:
email = email_field.clean(email) email = email_field.clean(email)
except ValidationError as err: except ValidationError as err:
msg = '{} {}.'.format(email, _('is not a proper email')) 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,
return HttpResponseRedirect(reverse('datacenterlight:index') + "#order_form") extra_tags='email')
return HttpResponseRedirect(
reverse('datacenterlight:index') + "#order_form")
specs = { specs = {
'cpu': cores, 'cpu': cores,
@ -293,14 +314,16 @@ class IndexView(CreateView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(IndexView, self).get_context_data(**kwargs) context = super(IndexView, self).get_context_data(**kwargs)
context.update({ 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 return context
def form_valid(self, form): def form_valid(self, form):
context = { 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 = { email_data = {
@ -330,7 +353,8 @@ class IndexView(CreateView):
email = BaseEmail(**email_data) email = BaseEmail(**email_data)
email.send() 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) return super(IndexView, self).form_valid(form)
@ -403,7 +427,8 @@ class PaymentOrderView(FormView):
request.session['billing_address'] = billing_address.id request.session['billing_address'] = billing_address.id
request.session['token'] = token request.session['token'] = token
request.session['customer'] = customer.id request.session['customer'] = customer.id
return HttpResponseRedirect(reverse('datacenterlight:order_confirmation')) return HttpResponseRedirect(
reverse('datacenterlight:order_confirmation'))
else: else:
return self.form_invalid(form) return self.form_invalid(form)
@ -423,11 +448,15 @@ class OrderConfirmationView(DetailView):
stripe_customer_id = request.session.get('customer') stripe_customer_id = request.session.get('customer')
customer = StripeCustomer.objects.filter(id=stripe_customer_id).first() customer = StripeCustomer.objects.filter(id=stripe_customer_id).first()
stripe_utils = StripeUtils() 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,
if not card_details.get('response_object') and not card_details.get('paid'): request.session.get(
'token'))
if not card_details.get('response_object'):
msg = card_details.get('error') msg = card_details.get('error')
messages.add_message(self.request, messages.ERROR, msg, extra_tags='failed_payment') messages.add_message(self.request, messages.ERROR, msg,
return HttpResponseRedirect(reverse('datacenterlight:payment') + '#payment_error') extra_tags='failed_payment')
return HttpResponseRedirect(
reverse('datacenterlight:payment') + '#payment_error')
context = { context = {
'site_url': reverse('datacenterlight:index'), 'site_url': reverse('datacenterlight:index'),
'cc_last4': card_details.get('response_object').get('last4'), 'cc_last4': card_details.get('response_object').get('last4'),

View file

@ -37,7 +37,9 @@ def int_env(val, default_value=0):
try: try:
return_value = int(os.environ.get(val)) return_value = int(os.environ.get(val))
except Exception as e: except Exception as e:
logger.error("Encountered exception trying to get env value for {}\nException details: {}".format( logger.error(
("Encountered exception trying to get env value for {}\nException "
"details: {}").format(
val, str(e))) val, str(e)))
return return_value return return_value
@ -169,10 +171,12 @@ TEMPLATES = [
os.path.join(PROJECT_DIR, 'membership'), os.path.join(PROJECT_DIR, 'membership'),
os.path.join(PROJECT_DIR, 'hosting/templates/'), os.path.join(PROJECT_DIR, 'hosting/templates/'),
os.path.join(PROJECT_DIR, 'nosystemd/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/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'), os.path.join(PROJECT_DIR,
'ungleich_page/templates/ungleich_page'),
os.path.join(PROJECT_DIR, 'templates/analytics'), os.path.join(PROJECT_DIR, 'templates/analytics'),
], ],
'APP_DIRS': True, 'APP_DIRS': True,
@ -493,6 +497,7 @@ REGISTRATION_MESSAGE = {'subject': "Validation mail",
} }
STRIPE_API_PRIVATE_KEY = env('STRIPE_API_PRIVATE_KEY') STRIPE_API_PRIVATE_KEY = env('STRIPE_API_PRIVATE_KEY')
STRIPE_API_PUBLIC_KEY = env('STRIPE_API_PUBLIC_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' ANONYMOUS_USER_NAME = 'anonymous@ungleich.ch'
GUARDIAN_GET_INIT_ANONYMOUS_USER = 'membership.models.get_anonymous_user_instance' GUARDIAN_GET_INIT_ANONYMOUS_USER = 'membership.models.get_anonymous_user_instance'
@ -535,9 +540,12 @@ GOOGLE_ANALYTICS_PROPERTY_IDS = {
'ungleich.ch': 'UA-62285904-1', 'ungleich.ch': 'UA-62285904-1',
'digitalglarus.ch': 'UA-62285904-2', 'digitalglarus.ch': 'UA-62285904-2',
'blog.ungleich.ch': 'UA-62285904-4', 'blog.ungleich.ch': 'UA-62285904-4',
'hosting': 'UA-62285904-5', 'rails-hosting.ch': 'UA-62285904-5',
'datacenterlight.ch': 'UA-62285904-9', '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', '127.0.0.1:8000': 'localhost',
'dynamicweb-development.ungleich.ch': 'development', 'dynamicweb-development.ungleich.ch': 'development',
'dynamicweb-staging.ungleich.ch': 'staging' 'dynamicweb-staging.ungleich.ch': 'staging'
@ -562,7 +570,8 @@ if ENABLE_DEBUG_LOGGING:
'file': { 'file': {
'level': 'DEBUG', 'level': 'DEBUG',
'class': 'logging.FileHandler', 'class': 'logging.FileHandler',
'filename': "{PROJECT_DIR}/debug.log".format(PROJECT_DIR=PROJECT_DIR), 'filename': "{PROJECT_DIR}/debug.log".format(
PROJECT_DIR=PROJECT_DIR),
}, },
}, },
'loggers': { 'loggers': {

View file

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-08-16 04:19+0530\n" "POT-Creation-Date: 2017-08-20 21:37+0530\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -245,31 +245,23 @@ msgstr "Gesamt"
msgid "Finish Configuration" msgid "Finish Configuration"
msgstr "Konfiguration beenden" msgstr "Konfiguration beenden"
msgid "Order Nr."
msgstr "Bestellung Nr."
msgid "Amount" msgid "Amount"
msgstr "Betrag" msgstr "Betrag"
msgid "Status" msgid "Status"
msgstr "" msgstr ""
msgid "Approved"
msgstr "Akzeptiert"
msgid "Declined"
msgstr "Abgelehnt"
msgid "View Detail" msgid "View Detail"
msgstr "Details anzeigen" msgstr "Details anzeigen"
msgid "Cancel Order" msgid "Page"
msgstr "Bestellung stornieren" msgstr ""
#, fuzzy msgid "of"
#| msgid "Do You want to delete your order?" msgstr ""
msgid "Do you want to delete your order?"
msgstr "Willst du deine Bestellung löschen?"
msgid "Delete"
msgstr "Löschen"
msgid "Your Order" msgid "Your Order"
msgstr "Deine Bestellung" msgstr "Deine Bestellung"
@ -300,12 +292,20 @@ msgstr ""
"\"https://stripe.com\" target=\"_blank\">Stripe</a> für die Bezahlung und " "\"https://stripe.com\" target=\"_blank\">Stripe</a> für die Bezahlung und "
"speichern keine Informationen in unserer Datenbank." "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 "" msgid ""
"\n" "\n"
" You are not making any payment yet. " " You are not making any "
"After submitting your card\n" "payment yet. After submitting your card\n"
" information, you will be taken to " " information, you will be "
"the Confirm Order Page.\n" "taken to the Confirm Order Page.\n"
" " " "
msgstr "" msgstr ""
"\n" "\n"
@ -328,19 +328,6 @@ msgstr ""
msgid "Card Type" msgid "Card Type"
msgstr "Kartentyp" 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" msgid "Processing"
msgstr "Weiter" msgstr "Weiter"
@ -390,6 +377,9 @@ msgstr ""
msgid "Private Key" msgid "Private Key"
msgstr "" msgstr ""
msgid "Delete"
msgstr "Löschen"
msgid "Delete SSH Key" msgid "Delete SSH Key"
msgstr "SSH Key löschen" msgstr "SSH Key löschen"
@ -399,38 +389,76 @@ msgstr "Möchtest Du den Schlüssel löschen?"
msgid "Show" msgid "Show"
msgstr "Anzeigen" msgstr "Anzeigen"
msgid "Public SSH Key" #, fuzzy
#| msgid "Public SSH Key"
msgid "Public SSH key"
msgstr "Public SSH Key" msgstr "Public SSH Key"
msgid "Download" msgid "Download"
msgstr "" msgstr ""
msgid "Settings" msgid "Your Virtual Machine Detail"
msgstr "Einstellungen" msgstr "Virtuelle Maschinen Detail"
msgid "Billing" msgid "VM Settings"
msgstr "Abrechnungen" msgstr "VM Einstellungen"
msgid "Ip not assigned yet" msgid "Copied"
msgstr "Ip nicht zugewiesen" msgstr "Kopiert"
msgid "Disk" msgid "Disk"
msgstr "Festplatte" msgstr "Festplatte"
msgid "Current pricing" msgid "Billing"
msgstr "Abrechnungen"
msgid "Current Pricing"
msgstr "Aktueller Preis" msgstr "Aktueller Preis"
msgid "Current status" msgid "Month"
msgstr "Aktueller Status" msgstr "Monat"
msgid "Terminate Virtual Machine" msgid "See Invoice"
msgstr "Virtuelle Maschine beenden" msgstr "Rechnung"
msgid "Your VM is"
msgstr "Deine VM ist"
msgid "Pending"
msgstr "In Vorbereitung"
msgid "Online"
msgstr ""
msgid "Failed"
msgstr "Fehlgeschlagen"
msgid "Terminate VM"
msgstr "VM Beenden"
msgid "Support / Contact"
msgstr "Support / Kontakt"
msgid "Something doesn't work?"
msgstr "Etwas funktioniert nicht?"
msgid "We are here to help you!"
msgstr "Wir sind hier, um Dir zu helfen!"
msgid "CONTACT"
msgstr "KONTACT"
msgid "BACK TO LIST"
msgstr "ZURÜCK ZUR LISTE"
msgid "Terminate your Virtual Machine" msgid "Terminate your Virtual Machine"
msgstr "Ihre virtuelle Maschine beenden" msgstr "Deine Virtuelle Maschine beenden"
msgid "Are you sure do you want to cancel your Virtual Machine " msgid "Do you want to cancel your Virtual Machine"
msgstr "Sind Sie sicher, dass Sie ihre virtuelle Maschine beenden wollen " msgstr "Bist Du sicher, dass Du Deine virtuelle Maschine beenden willst"
msgid "OK"
msgstr ""
msgid "Virtual Machines" msgid "Virtual Machines"
msgstr "Virtuelle Maschinen" msgstr "Virtuelle Maschinen"
@ -441,14 +469,8 @@ msgstr ""
msgid "CREATE VM" msgid "CREATE VM"
msgstr "NEUE VM" msgstr "NEUE VM"
msgid "Page"
msgstr ""
msgid "of"
msgstr ""
msgid "login" msgid "login"
msgstr "einloggen" msgstr "Einloggen"
msgid "" msgid ""
"Thank you for signing up. We have sent an email to you. Please follow the " "Thank you for signing up. We have sent an email to you. Please follow the "
@ -474,6 +496,9 @@ msgstr "Du kannst dich nun"
msgid "Sorry. Your request is invalid." msgid "Sorry. Your request is invalid."
msgstr "Entschuldigung, deine Anfrage ist ungültig." msgstr "Entschuldigung, deine Anfrage ist ungültig."
msgid "Invalid credit card"
msgstr "Ungültige Kreditkarte"
msgid "Confirm Order" msgid "Confirm Order"
msgstr "Bestellung Bestätigen" msgstr "Bestellung Bestätigen"
@ -482,6 +507,42 @@ msgid ""
"contact Data Center Light Support." "contact Data Center Light Support."
msgstr "" msgstr ""
#~ msgid "Approved"
#~ msgstr "Akzeptiert"
#~ msgid "Declined"
#~ msgstr "Abgelehnt"
#~ msgid "Cancel Order"
#~ msgstr "Bestellung stornieren"
#, fuzzy
#~| msgid "Do You want to delete your order?"
#~ msgid "Do you want to delete your order?"
#~ msgstr "Willst du deine Bestellung löschen?"
#~ 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 "Ip not assigned yet"
#~ msgstr "Ip nicht zugewiesen"
#~ msgid "Current status"
#~ msgstr "Aktueller Status"
#~ msgid "Terminate Virtual Machine"
#~ msgstr "Virtuelle Maschine beenden"
#~ msgid "Ipv4" #~ msgid "Ipv4"
#~ msgstr "IPv4" #~ msgstr "IPv4"

View file

@ -1,3 +1,6 @@
.virtual-machine-container {
max-width: 900px;
}
.virtual-machine-container .tabs-left, .virtual-machine-container .tabs-right { .virtual-machine-container .tabs-left, .virtual-machine-container .tabs-right {
border-bottom: none; border-bottom: none;
padding-top: 2px; padding-top: 2px;
@ -229,6 +232,204 @@
} }
} }
/* Vm Details */
.vm-detail-item, .vm-contact-us {
overflow: hidden;
border: 1px solid #ccc;
padding: 15px;
color: #555;
font-weight: 300;
margin-bottom: 15px;
}
.vm-detail-title {
margin-top: 0;
font-size: 20px;
font-weight: 300;
}
.vm-detail-title .un-icon {
float: right;
height: 24px;
width: 21px;
margin-top: 0;
}
.vm-detail-item .vm-name {
font-size: 16px;
margin-bottom: 15px;
}
.vm-detail-item p {
margin-bottom: 5px;
position: relative;
}
.vm-detail-ip {
padding-bottom: 5px;
border-bottom: 1px solid #ddd;
margin-bottom: 10px;
}
.vm-detail-ip .un-icon {
height: 14px;
width: 14px;
}
.vm-detail-ip .to_copy {
position: absolute;
right: 0;
top: 1px;
padding: 0;
line-height: 1;
}
.vm-vmid {
padding: 50px 0 70px;
text-align: center;
}
.vm-item-lg {
font-size: 22px;
margin-top: 5px;
margin-bottom: 15px;
letter-spacing: 0.6px;
}
.vm-color-online {
color: #37B07B;
}
.vm-color-pending {
color: #e47f2f;
}
.vm-detail-item .value{
font-weight: 400;
}
.vm-detail-config .value {
float: right;
font-weight: 600;
}
.vm-detail-contain {
margin-top: 25px;
}
.vm-contact-us {
margin: 25px 0 30px;
/* text-align: center; */
}
@media(min-width: 768px) {
.vm-detail-contain {
display: flex;
margin-left: -15px;
margin-right: -15px;
}
.vm-detail-item {
width: 33.333333%;
margin: 0 15px;
}
.vm-contact-us {
display: flex;
align-items: center;
justify-content: space-between;
}
.vm-contact-us .vm-detail-title {
margin-bottom: 0;
}
.vm-contact-us .un-icon {
width: 22px;
height: 22px;
margin-right: 5px;
}
.vm-contact-us div {
padding: 0 15px;
position: relative;
}
.vm-contact-us-text {
display: flex;
align-items: center;
}
}
.value-sm-block {
display: block;
padding-top: 2px;
}
@media(max-width: 767px) {
.vm-contact-us div {
margin-bottom: 30px;
}
.vm-contact-us div span {
display: block;
margin-bottom: 3px;
}
.dashboard-title-thin {
font-size: 22px;
}
}
.btn-vm-invoice {
color: #87B6EA;
border: 2px solid #87B6EA;
padding: 4px 18px;
letter-spacing: 0.6px;
}
.btn-vm-invoice:hover, .btn-vm-invoice:focus {
color : #fff;
background: #87B6EA;
}
.btn-vm-term {
color: #aaa;
border: 2px solid #ccc;
background: #fff;
padding: 4px 18px;
letter-spacing: 0.6px;
}
.btn-vm-term:hover, .btn-vm-term:focus, .btn-vm-term:active {
color: #eb4d5c;
border-color: #eb4d5c;
}
.btn-vm-contact {
color: #fff;
background: #A3C0E2;
border: 2px solid #A3C0E2;
padding: 5px 25px;
font-size: 12px;
letter-spacing: 1.3px;
}
.btn-vm-contact:hover, .btn-vm-contact:focus {
background: #fff;
color: #a3c0e2;
}
.btn-vm-back {
color: #fff;
background: #C4CEDA;
border: 2px solid #C4CEDA;
padding: 5px 25px;
font-size: 12px;
letter-spacing: 1.3px;
}
.btn-vm-back:hover, .btn-vm-back:focus {
color: #fff;
background: #8da4c0;
border-color: #8da4c0;
}
.vm-contact-us-text {
letter-spacing: 0.4px;
}
/* New styles */ /* New styles */
.dashboard-container-head { .dashboard-container-head {
padding: 0 8px; padding: 0 8px;
@ -239,10 +440,10 @@
} }
.dashboard-title-thin .un-icon { .dashboard-title-thin .un-icon {
height: 34px; height: 30px;
margin-right: 5px; margin-right: 5px;
margin-top: -1px; margin-top: -1px;
width: 20px; width: 30px;
} }
.dashboard-subtitle { .dashboard-subtitle {
@ -287,6 +488,24 @@
color: #3770CC; color: #3770CC;
} }
.btn-order-detail {
background: #87B6EA;
color: #fff;
font-weight: 400;
letter-spacing: 0.6px;
font-size: 14px;
border-radius: 3px;
border: 2px solid #87B6EA;
padding: 4px 20px;
min-width: 155px;
/* padding-bottom: 7px; */
}
.btn-order-detail:hover, .btn-order-detail:focus, .btn-order-detail:active {
background: #fff;
color: #87B6EA;
}
.vm-status, .vm-status-active, .vm-status-failed { .vm-status, .vm-status-active, .vm-status-failed {
font-weight: 600; font-weight: 600;
} }
@ -355,8 +574,8 @@
position: relative; position: relative;
border-top: 1px solid #ddd; border-top: 1px solid #ddd;
/* margin-top: 15px; */ /* margin-top: 15px; */
padding-top: 5px; padding-top: 10px;
padding-bottom: 15px; padding-bottom: 13px;
} }
.table-switch tbody tr:last-child { .table-switch tbody tr:last-child {
border-bottom: 1px solid #ddd; border-bottom: 1px solid #ddd;
@ -373,11 +592,28 @@
font-weight: 600; font-weight: 600;
position: absolute; position: absolute;
top: 5px; top: 5px;
left: 8px;
} }
.table-switch .last-td { .table-switch .last-td {
position: absolute; position: absolute;
bottom: 20px; bottom: 13px;
right: 0; right: 0;
} }
.table-switch tbody tr .xs-td-inline {
text-align: right;
padding-top: 6px;
}
.table-switch tbody tr .xs-td-bighalf {
width: 52%;
display: inline-block;
}
.table-switch tbody tr .xs-td-smallhalf {
width: 47%;
text-align: right;
display: inline-block;
}
.table-switch tbody tr .xs-td-smallhalf:before {
left: auto;
right: 8px;
}
} }

View file

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 279.525 279.525" style="enable-background:new 0 0 279.525 279.525;" xml:space="preserve">
<g>
<path d="M165.066,1.544c-29.272,0-56.007,11.05-76.268,29.191c4.494,7.146,7.047,15.46,7.287,24.042l0.001,0.025l0.001,0.025
c0.102,3.867,0.333,7.735,0.664,11.597c15.368-21.117,40.258-34.88,68.315-34.88c46.571,0,84.459,37.888,84.459,84.459
c0,46.08-37.098,83.634-82.994,84.422c4.191,3.502,8.518,6.84,12.976,9.974l0.02,0.015l0.021,0.014
c6.07,4.282,11.014,9.896,14.483,16.317c49.133-12.861,85.493-57.633,85.493-110.742C279.525,52.89,228.18,1.544,165.066,1.544z"/>
<path d="M162.256,234.942c-13.076-10.438-21.234-17.389-32.909-28.204c-3.435-3.182-7.633-5.164-11.944-5.164
c-3.299,0-6.557,1.051-9.239,3.252c-2.768,2.33-5.536,4.66-8.305,6.989c-22.499-26.738-39.206-57.895-49.027-91.431
c3.472-1.016,6.945-2.033,10.417-3.049c7.652-2.343,11.252-10.512,10.129-18.701c-2.443-17.824-3.77-26.679-5.282-43.018
c-0.775-8.375-6.349-15.65-14.338-16.085c-1.246-0.121-2.491-0.181-3.726-0.181c-29.71,0-55.578,34.436-46.009,76.564
c11.907,52.172,37.684,100.243,74.551,139.031c15.102,15.856,33.603,23.036,50.312,23.036c17.627,0,33.261-7.984,40.833-22.195
C171.778,248.891,168.83,240.19,162.256,234.942z"/>
<path d="M130.645,118.121c-7.912,7.341-13.089,13.113-15.823,17.643c-1.93,3.195-3.338,6.573-4.187,10.04
c-0.399,1.632-0.032,3.326,1.007,4.649c1.038,1.321,2.596,2.079,4.276,2.079h37.758c4.626,0,8.39-3.764,8.39-8.39
c0-4.626-3.764-8.39-8.39-8.39h-17.051c0.139-0.164,0.282-0.328,0.428-0.493c1.114-1.254,3.842-3.874,8.107-7.785
c4.473-4.105,7.493-7.179,9.232-9.398c2.621-3.336,4.571-6.593,5.794-9.679c1.247-3.145,1.88-6.498,1.88-9.967
c0-6.224-2.254-11.507-6.699-15.705c-4.416-4.164-10.495-6.274-18.071-6.274c-6.884,0-12.731,1.802-17.377,5.356
c-2.803,2.146-4.961,5.119-6.415,8.839c-0.982,2.513-0.728,5.388,0.68,7.689c1.408,2.302,3.852,3.837,6.537,4.105
c0.299,0.03,0.597,0.045,0.891,0.045c3.779,0,7.149-2.403,8.387-5.979c0.388-1.121,0.901-2.012,1.527-2.65
c1.318-1.343,3.093-1.997,5.428-1.997c2.373,0,4.146,0.618,5.418,1.889c1.269,1.269,1.886,3.12,1.886,5.66
c0,2.359-0.843,4.819-2.505,7.314C140.862,108.028,138.199,111.083,130.645,118.121z"/>
<path d="M206.235,76.451h-6.307c-1.797,0-3.475,0.886-4.489,2.37l-29.168,42.698c-0.851,1.246-1.301,2.703-1.301,4.212v6.919
c0,2.997,2.439,5.436,5.436,5.436h23.945v5.787c0,4.775,3.885,8.66,8.66,8.66c4.775,0,8.66-3.885,8.66-8.66v-5.787h0.865
c4.437,0,8.047-3.61,8.047-8.047c0-4.437-3.61-8.047-8.047-8.047h-0.865V81.887C211.671,78.89,209.232,76.451,206.235,76.451z
M194.352,121.992h-10.748l10.748-15.978V121.992z"/>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3 KiB

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="symbol symbol-billing" aria-labelledby="title" role="img"><title id="title">billing icon</title><g data-name="Layer 1"><path class="cls-1" d="M.37.023v15.954l2.775-1.387 2.775 1.387L8 14.59l2.775 1.387 2.081-1.387 2.775 1.387V.023zm13.873 13.709l-1.487-.744-2.081 1.387L7.9 12.989l-2.08 1.387-2.675-1.337-1.387.694V1.41h12.485z" role="presentation"/><path class="cls-1" d="M4.206 3.617h7.741v1.348H4.206zm0 2.697h7.741v1.349H4.206zm0 2.697h7.741v1.349H4.206z" role="presentation"/></g></svg>

After

Width:  |  Height:  |  Size: 558 B

View file

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 278.898 278.898" style="enable-background:new 0 0 278.898 278.898;" xml:space="preserve">
<g>
<path d="M269.898,175.773h-20.373V64.751c0-4.971-4.029-9-9-9h-62.702V35.377c0-4.971-4.029-9-9-9h-58.748c-4.971,0-9,4.029-9,9
v20.374H38.373c-4.971,0-9,4.029-9,9v111.022H9c-4.971,0-9,4.029-9,9v58.748c0,4.971,4.029,9,9,9h58.747c4.971,0,9-4.029,9-9
v-58.748c0-4.971-4.029-9-9-9H47.373V73.751h53.702v20.374c0,4.971,4.029,9,9,9h20.374v72.648h-20.374c-4.971,0-9,4.029-9,9v58.748
c0,4.971,4.029,9,9,9h58.748c4.971,0,9-4.029,9-9v-58.748c0-4.971-4.029-9-9-9h-20.374v-72.648h20.374c4.971,0,9-4.029,9-9V73.751
h53.702v102.022h-20.374c-4.971,0-9,4.029-9,9v58.748c0,4.971,4.029,9,9,9h58.747c4.971,0,9-4.029,9-9v-58.748
C278.898,179.803,274.869,175.773,269.898,175.773z M58.747,234.521H18v-40.748h40.747V234.521z M159.823,234.521h-40.748v-40.748
h40.748V234.521z M159.823,85.125h-40.748V44.377h40.748V85.125z M260.898,234.521h-40.747v-40.748h40.747V234.521z"/>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="340.274px" height="340.274px" viewBox="0 0 340.274 340.274" style="enable-background:new 0 0 340.274 340.274;"
xml:space="preserve">
<g>
<g>
<g>
<path d="M293.629,127.806l-5.795-13.739c19.846-44.856,18.53-46.189,14.676-50.08l-25.353-24.77l-2.516-2.12h-2.937
c-1.549,0-6.173,0-44.712,17.48l-14.184-5.719c-18.332-45.444-20.212-45.444-25.58-45.444h-35.765
c-5.362,0-7.446-0.006-24.448,45.606l-14.123,5.734C86.848,43.757,71.574,38.19,67.452,38.19l-3.381,0.105L36.801,65.032
c-4.138,3.891-5.582,5.263,15.402,49.425l-5.774,13.691C0,146.097,0,147.838,0,153.33v35.068c0,5.501,0,7.44,46.585,24.127
l5.773,13.667c-19.843,44.832-18.51,46.178-14.655,50.032l25.353,24.8l2.522,2.168h2.951c1.525,0,6.092,0,44.685-17.516
l14.159,5.758c18.335,45.438,20.218,45.427,25.598,45.427h35.771c5.47,0,7.41,0,24.463-45.589l14.195-5.74
c26.014,11,41.253,16.585,45.349,16.585l3.404-0.096l27.479-26.901c3.909-3.945,5.278-5.309-15.589-49.288l5.734-13.702
c46.496-17.967,46.496-19.853,46.496-25.221v-35.029C340.268,146.361,340.268,144.434,293.629,127.806z M170.128,228.474
c-32.798,0-59.504-26.187-59.504-58.364c0-32.153,26.707-58.315,59.504-58.315c32.78,0,59.43,26.168,59.43,58.315
C229.552,202.287,202.902,228.474,170.128,228.474z"/>
</g>
</g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
<g>
<g>
<polygon points="447.992,336 181.555,336 69.539,80 0.008,80 0.008,48 90.477,48 202.492,304 447.992,304 "/>
</g>
<path d="M287.992,416c0,26.5-21.5,48-48,48s-48-21.5-48-48s21.5-48,48-48S287.992,389.5,287.992,416z"/>
<path d="M447.992,416c0,26.5-21.5,48-48,48s-48-21.5-48-48s21.5-48,48-48S447.992,389.5,447.992,416z"/>
<g>
<polygon points="499.18,144 511.992,112 160.008,112 172.805,144 "/>
<polygon points="211.195,240 223.992,272 447.992,272 460.805,240 "/>
<polygon points="486.398,176 185.602,176 198.398,208 473.586,208 "/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -13,4 +13,62 @@ $( document ).ready(function() {
}, 1000); }, 1000);
}); });
$('.alt-text').on('mouseenter mouseleave', function(e){
var $this = $(this);
var txt = $this.text();
var alt = $this.attr('data-alt');
$this.text(alt);
$this.attr('data-alt', txt);
});
});
function getScrollbarWidth() {
var outer = document.createElement("div");
outer.style.visibility = "hidden";
outer.style.width = "100px";
outer.style.msOverflowStyle = "scrollbar"; // needed for WinJS apps
document.body.appendChild(outer);
var widthNoScroll = outer.offsetWidth;
// force scrollbars
outer.style.overflow = "scroll";
// add innerdiv
var inner = document.createElement("div");
inner.style.width = "100%";
outer.appendChild(inner);
var widthWithScroll = inner.offsetWidth;
// remove divs
outer.parentNode.removeChild(outer);
return widthNoScroll - widthWithScroll;
}
// globally stores the width of scrollbar
var scrollbarWidth = getScrollbarWidth();
var paddingAdjusted = false;
$( document ).ready(function() {
// add proper padding to fixed topnav on modal show
$('body').on('click', '[data-toggle=modal]', function(){
var $body = $('body');
if ($body[0].scrollHeight > $body.height()) {
scrollbarWidth = getScrollbarWidth();
var topnavPadding = parseInt($('.navbar-fixed-top.topnav').css('padding-right'));
$('.navbar-fixed-top.topnav').css('padding-right', topnavPadding+scrollbarWidth);
paddingAdjusted = true;
}
});
// remove added padding on modal hide
$('body').on('hidden.bs.modal', function(){
if (paddingAdjusted) {
var topnavPadding = parseInt($('.navbar-fixed-top.topnav').css('padding-right'));
$('.navbar-fixed-top.topnav').css('padding-right', topnavPadding-scrollbarWidth);
}
});
}); });

View file

@ -38,7 +38,6 @@
<![endif]--> <![endif]-->
{% with 'hosting/img/'|add:hosting|add:'-intro-bg.png' as image_static %} {% with 'hosting/img/'|add:hosting|add:'-intro-bg.png' as image_static %}
alt="">
<style media="screen" type="text/css"> <style media="screen" type="text/css">
.intro-header { .intro-header {
background: url("{% static image_static %}") no-repeat center center; background: url("{% static image_static %}") no-repeat center center;

View file

@ -3,17 +3,23 @@
{% load i18n %} {% load i18n %}
{% block content %} {% block content %}
<div class="dashboard-container">
<div class="dashboard-container-head">
<h3 class="dashboard-title-thin"><img src="{% static 'hosting/img/shopping-cart.svg' %}" class="un-icon" style="margin-top: -4px; width: 30px;"> {% trans "My Orders" %}</h3>
{% if messages %}
<div class="alert alert-warning">
{% for message in messages %}
<span>{{ message }}</span>
{% endfor %}
</div>
{% endif %}
<div class="dashboard-subtitle"></div>
</div>
<div> <table class="table table-switch">
<div class="orders-container">
<div class="row">
<div class="container-table col-md-8 col-md-offset-2">
<table class="table borderless table-hover">
<h3><i class="fa fa-credit-card fa-separate"></i>{% trans "My Orders"%}</h3>
<br/>
<thead> <thead>
<tr> <tr>
<th>#</th> <th>{% trans "Order Nr." %}</th>
<th>{% trans "Date" %}</th> <th>{% trans "Date" %}</th>
<th>{% trans "Amount" %}</th> <th>{% trans "Amount" %}</th>
<th>{% trans "Status" %}</th> <th>{% trans "Status" %}</th>
@ -23,50 +29,20 @@
<tbody> <tbody>
{% for order in orders %} {% for order in orders %}
<tr> <tr>
<td scope="row">{{ order.id }}</td> <td class="xs-td-inline" data-header="{% trans 'Order Nr.' %}">{{ order.id }}</td>
<td>{{ order.created_at | date:"M d, Y" }}</td> <td class="xs-td-bighalf" data-header="{% trans 'Date' %}">{{ order.created_at | date:"M d, Y" }}</td>
<td>{{ order.price }} CHF</td> <td class="xs-td-smallhalf" data-header="{% trans 'Amount' %}">{{ order.price }}</td>
<td>{% if order.approved %} <td data-header="{% trans 'Status' %}">
<span class="text-success strong">{% trans "Approved"%}</span> {% if order.approved %}
<span class="vm-status-active"><strong>Approved</strong></span>
{% else %} {% else %}
<span class="text-danger strong">{% trans "Declined"%}</span> <span class="vm-status-failed"><strong>Declined</strong></span>
{% endif %} {% endif %}
</td> </td>
<td> <td class="text-right last-td">
<a class="btn btn-default" <a class="btn btn-order-detail alt-text" href="{% url 'hosting:orders' order.pk %}" data-alt="{% trans 'See Invoice' %}">{% trans "View Detail" %}</a>
href="{% url 'hosting:orders' order.id %}">{% trans "View Detail"%}</a>
<button type="button" class="btn btn-default" data-toggle="modal"
data-target="#Modal{{ order.id }}"><a
href="#">{% trans "Cancel Order"%}</a>
</button>
</td> </td>
</tr> </tr>
<div class="modal fade" id="Modal{{ order.id }}" tabindex="-1" role="dialog"
aria-labelledby="exampleModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"
aria-label="Confirm"><span
aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="modal-icon"><i class="fa fa-trash" aria-hidden="true"></i></div>
<h4 class="modal-title" id="ModalLabel">{% trans "Do you want to delete your order?"%}</h4>
<form method="post"
action="{% url 'hosting:delete_order' order.id %}">
{% csrf_token %}
<div class="modal-footer">
<button type="submit" class="btn btn-danger">{% trans "Delete"%}
</button>
</div>
</form>
</div>
</div>
</div>
</div>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
@ -78,7 +54,7 @@
<a href="{{request.path}}?page={{ page_obj.previous_page_number }}">{% trans "previous" %}</a> <a href="{{request.path}}?page={{ page_obj.previous_page_number }}">{% trans "previous" %}</a>
{% endif %} {% endif %}
<span class="page-current"> <span class="page-current">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}. {% trans "Page" %} {{ page_obj.number }} {% trans "of" %} {{ page_obj.paginator.num_pages }}.
</span> </span>
{% if page_obj.has_next %} {% if page_obj.has_next %}
<a href="{{request.path}}?page={{ page_obj.next_page_number }}">{% trans "next" %}</a> <a href="{{request.path}}?page={{ page_obj.next_page_number }}">{% trans "next" %}</a>
@ -86,12 +62,5 @@
</span> </span>
</div> </div>
{% endif %} {% endif %}
</div> </div>
</div>
</div>
</div>
{% endblock %} {% endblock %}

View file

@ -3,149 +3,6 @@
{% load i18n %} {% load i18n %}
{% block content %} {% block content %}
<div>
<div class="virtual-machine-container dashboard-container ">
<div class="row">
<div class="col-md-9 col-md-offset-2">
<div class="col-sm-12">
<h3><i class="fa fa-cloud fa-separate" aria-hidden="true"></i> {{virtual_machine.name}}</h3>
<hr/>
<div class="col-md-3"> <!-- required for floating -->
<!-- Nav tabs -->
<ul class="nav nav-tabs tabs-left sideways">
<li class="active">
<a href="#settings-v" data-toggle="tab">
<i class="fa fa-cogs" aria-hidden="true"></i>
{% trans "Settings"%}
</a>
</li>
<li>
<a href="#billing-v" data-toggle="tab">
<i class="fa fa-money" aria-hidden="true"></i>
{% trans "Billing"%}
</a>
</li>
<li>
<a href="#status-v" data-toggle="tab">
<i class="fa fa-signal" aria-hidden="true"></i> {% trans "Status"%}
</a>
</li>
</ul>
</div>
<div class="col-md-9">
<!-- Tab panes -->
<div class="tab-content">
<div class="tab-pane active" id="settings-v">
<div class="row">
<div class="col-md-12 inline-headers">
<h3>{{virtual_machine.hosting_company_name}}</h3>
{% if virtual_machine.ipv6 %}
<div class="pull-right right-place">
<button type="link"
data-clipboard-text="{{virtual_machine.ipv4}}" id="copy_vm_id" class="to_copy btn btn-link"
data-toggle="tooltip" data-placement="bottom" title="Copied" data-trigger="click">
Ipv4: {{virtual_machine.ipv4}} <i class="fa fa-files-o" aria-hidden="true"></i>
</button>
<button type="link"
data-clipboard-text="{{virtual_machine.ipv6}}" id="copy_vm_id" class="to_copy btn btn-link"
data-toggle="tooltip" data-placement="bottom" title="Copied" data-trigger="click">
Ipv6: {{virtual_machine.ipv6}} <i class="fa fa-files-o" aria-hidden="true"></i>
</button>
</div>
{% else %}
<div class="pull-right right-place">
<span class="label label-warning"><strong>{% trans "Ip not assigned yet"%}</strong></span>
<i data-toggle="tooltip" title="Your ip will be assigned soon" class="fa fa-info-circle" aria-hidden="true"></i>
</div>
{% endif %}
<hr>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="row">
<div class="col-md-3">
<div class="well text-center box-setting">
<i class="fa fa-cubes" aria-hidden="true"></i>
<span>{% trans "Cores"%}</span>
<span class="label label-success">{{virtual_machine.cores}}</span>
</div>
</div>
<div class="col-md-3">
<div class="well text-center box-setting">
<i class="fa fa-tachometer" aria-hidden="true"></i> {% trans "Memory"%} <br/>
<span class="label label-success">{{virtual_machine.memory}} GB</span>
</div>
</div>
<div class="col-md-3">
<div class="well text-center box-setting">
<i class="fa fa-hdd-o" aria-hidden="true"></i>
<span>{% trans "Disk"%}</span>
<span class="label label-success">{{virtual_machine.disk_size|floatformat:2}} GB</span>
</div>
</div>
</div><!--/row-->
</div><!--/col-12-->
</div><!--/row-->
<div class="row">
<div class="col-md-12">
{% trans "Configuration"%}: {{virtual_machine.configuration}}
</div>
</div>
</div>
<div class="tab-pane" id="billing-v">
<div class="row ">
<div class="col-md-12 inline-headers">
<h3>{% trans "Current pricing"%}</h3>
<span class="h3 pull-right"><strong>{{virtual_machine.price|floatformat}} CHF</strong>/month</span>
<hr>
</div>
</div>
</div>
<div class="tab-pane" id="status-v">
<div class="row ">
<div class="col-md-12 inline-headers">
<h3>{% trans "Current status"%}</h3>
<div class="pull-right space-above">
{% if virtual_machine.state == 'PENDING' %}
<span class="label
label-warning"><strong>Pending</strong></span>
{% elif virtual_machine.state == 'ACTIVE' %}
<span class="label
label-success"><strong>Online</strong></span>
{% elif virtual_machine.state == 'FAILED'%}
<span class="label
label-danger"><strong>Failed</strong></span>
{% endif %}
</div>
</div>
</div>
{% if not virtual_machine.status == 'canceled' %}
<div class="row">
<div class="col-md-12 separate-md">
<div class="pull-right">
<form method="POST"
id="virtual_machine_cancel_form" class="cancel-form" action="{% url 'hosting:virtual_machines' virtual_machine.vm_id %}">
{% csrf_token %}
</form>
<button type="text" data-href="{% url 'hosting:virtual_machines' virtual_machine.vm_id %}" data-toggle="modal" data-target="#confirm-cancel" class="btn btn-danger">{% trans "Terminate Virtual Machine"%}</button>
</div>
</div>
<div class="col-md-12">
<br/>
{% if messages %} {% if messages %}
<div class="alert alert-warning"> <div class="alert alert-warning">
{% for message in messages %} {% for message in messages %}
@ -153,43 +10,103 @@
{% endfor %} {% endfor %}
</div> </div>
{% endif %} {% endif %}
<div class="virtual-machine-container dashboard-container">
<h1 class="dashboard-title-thin">{% trans "Your Virtual Machine Detail" %}</h1>
<div class="vm-detail-contain">
<div class="vm-detail-item">
<h2 class="vm-detail-title">{% trans "VM Settings" %} <img src="{% static 'hosting/img/settings.svg' %}" class="un-icon"></h2>
<h3 class="vm-name">{{virtual_machine.name}}</h3>
{% if virtual_machine.ipv6 %}
<div class="vm-detail-ip">
<p>
<span>IPv4:</span>
<span class="value">{{virtual_machine.ipv4}}</span>
<button data-clipboard-text="{{virtual_machine.ipv4}}" class="to_copy btn btn-link" data-toggle="tooltip" data-placement="left" title="{% trans 'Copied' %}" data-trigger="click">
<img class="un-icon" src="{% static 'hosting/img/copy.svg' %}">
</button>
</p>
<p>
<span>IPv6:</span>
<span class="value value-sm-block">{{virtual_machine.ipv6}}</span>
<button data-clipboard-text="{{virtual_machine.ipv6}}" class="to_copy btn btn-link" data-toggle="tooltip" data-placement="left" title="{% trans 'Copied' %}" data-trigger="click">
<img class="un-icon" src="{% static 'hosting/img/copy.svg' %}">
</button>
</p>
</div>
{% endif %}
<div class="vm-detail-config">
<p><span>{% trans "Cores" %}:</span><span class="value">{{virtual_machine.cores}}</span></p>
<p><span>{% trans "Memory" %}:</span><span class="value">{{virtual_machine.memory}} GB</span></p>
<p><span>{% trans "Disk" %}:</span><span class="value">{{virtual_machine.disk_size|floatformat:2}} GB</span></p>
<p><span>{% trans "Configuration" %}:</span><span class="value">{{virtual_machine.configuration}}</span></p>
</div>
</div>
<div class="vm-detail-item">
<h2 class="vm-detail-title">{% trans "Billing" %} <img src="{% static 'hosting/img/billing.svg' %}" class="un-icon"></h2>
<div class="vm-vmid">
<div class="vm-item-subtitle">{% trans "Current Pricing" %}</div>
<div class="vm-item-lg">{{virtual_machine.price|floatformat}} CHF/{% trans "Month" %}</div>
<a class="btn btn-vm-invoice" href="{% url 'hosting:orders' order.pk %}">{% trans "See Invoice" %}</a>
</div>
</div>
<div class="vm-detail-item">
<h2 class="vm-detail-title">{% trans "Status" %} <img src="{% static 'hosting/img/connected.svg' %}" class="un-icon"></h2>
<div class="vm-vmid">
<div class="vm-item-subtitle">{% trans "Your VM is" %}</div>
{% if virtual_machine.state == 'PENDING' %}
<div class="vm-item-lg vm-color-pending">{% trans "Pending" %}</div>
{% elif virtual_machine.state == 'ACTIVE' %}
<div class="vm-item-lg vm-color-online">{% trans "Online" %}</div>
{% elif virtual_machine.state == 'FAILED'%}
<div class="vm-item-lg vm-color-failed">{% trans "Failed" %}</div>
{% endif %}
{% if not virtual_machine.status == 'canceled' %}
<form method="POST" id="virtual_machine_cancel_form" class="cancel-form" action="{% url 'hosting:virtual_machines' virtual_machine.vm_id %}">
{% csrf_token %}
</form>
<button data-href="{% url 'hosting:virtual_machines' virtual_machine.vm_id %}" data-toggle="modal" data-target="#confirm-cancel" class="btn btn-vm-term">{% trans "Terminate VM" %}</button>
{% endif %}
</div>
</div>
</div>
<div class="vm-contact-us">
<div>
<h2 class="vm-detail-title">{% trans "Support / Contact" %} <img class="un-icon visible-xs" src="{% static 'hosting/img/24-hours-support.svg' %}"></h2>
</div>
<div class="vm-contact-us-text text-center">
<img class="un-icon hidden-xs" src="{% static 'hosting/img/24-hours-support.svg' %}">
<div>
<span>{% trans "Something doesn't work?" %}</span> <span>{% trans "We are here to help you!" %}</span>
</div>
</div>
<div class="text-center">
<a class="btn btn-vm-contact" href="mailto:support@datacenterlight.ch">{% trans "CONTACT" %}</a>
</div>
</div>
<div class="text-center">
<a class="btn btn-vm-back" href="{% url 'hosting:virtual_machines' %}">{% trans "BACK TO LIST" %}</a>
</div>
</div> </div>
<!-- Cancel Modal --> <!-- Cancel Modal -->
<div class="modal fade" id="confirm-cancel" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> <div class="modal fade" id="confirm-cancel" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" data-dismiss="modal" <button type="button" class="close" data-dismiss="modal" aria-label="Confirm"><span aria-hidden="true">&times;</span></button>
aria-label="Confirm"><span
aria-hidden="true">&times;</span>
</button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div class="modal-icon"><i class="fa fa-ban" aria-hidden="true"></i></div> <div class="modal-icon"><i class="fa fa-ban" aria-hidden="true"></i></div>
<h4 class="modal-title" id="ModalLabel">{% trans "Terminate your Virtual Machine"%}</h4> <h4 class="modal-title" id="ModalLabel">{% trans "Terminate your Virtual Machine"%}</h4>
<p class="modal-text">{% trans "Are you sure do you want to cancel your Virtual Machine "%} {{virtual_machine.name}} ?</p> <div class="modal-text">
<p>{% trans "Do you want to cancel your Virtual Machine" %} ?</p>
<p><strong>{{virtual_machine.name}}</strong></p>
</div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<a class="btn btn-danger btn-ok">OK</a> <a class="btn btn-danger btn-ok">{% trans "OK" %}</a>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<!-- / Cancel Modal --> <!-- / Cancel Modal -->
</div>
{% endif %}
</div>
</div>
</div>
<div class="clearfix"></div>
</div>
</div>
</div>
</div>
</div>
{%endblock%} {%endblock%}

View file

@ -20,9 +20,12 @@ urlpatterns = [
url(r'orders/(?P<pk>\d+)/?$', OrdersHostingDetailView.as_view(), name='orders'), url(r'orders/(?P<pk>\d+)/?$', OrdersHostingDetailView.as_view(), name='orders'),
url(r'bills/?$', HostingBillListView.as_view(), name='bills'), url(r'bills/?$', HostingBillListView.as_view(), name='bills'),
url(r'bills/(?P<pk>\d+)/?$', HostingBillDetailView.as_view(), name='bills'), url(r'bills/(?P<pk>\d+)/?$', HostingBillDetailView.as_view(), name='bills'),
url(r'cancel_order/(?P<pk>\d+)/?$', OrdersHostingDeleteView.as_view(), name='delete_order'), url(r'cancel_order/(?P<pk>\d+)/?$',
url(r'create_virtual_machine/?$', CreateVirtualMachinesView.as_view(), name='create_virtual_machine'), OrdersHostingDeleteView.as_view(), name='delete_order'),
url(r'my-virtual-machines/?$', VirtualMachinesPlanListView.as_view(), name='virtual_machines'), 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<pk>\d+)/?$', VirtualMachineView.as_view(), url(r'my-virtual-machines/(?P<pk>\d+)/?$', VirtualMachineView.as_view(),
name='virtual_machines'), name='virtual_machines'),
url(r'ssh_keys/?$', SSHKeyListView.as_view(), url(r'ssh_keys/?$', SSHKeyListView.as_view(),
@ -44,5 +47,6 @@ urlpatterns = [
PasswordResetConfirmView.as_view(), name='reset_password_confirm'), PasswordResetConfirmView.as_view(), name='reset_password_confirm'),
url(r'^logout/?$', auth_views.logout, url(r'^logout/?$', auth_views.logout,
{'next_page': '/hosting/login?logged_out=true'}, name='logout'), {'next_page': '/hosting/login?logged_out=true'}, name='logout'),
url(r'^validate/(?P<validate_slug>.*)/$', SignupValidatedView.as_view(), name='validate') url(r'^validate/(?P<validate_slug>.*)/$',
SignupValidatedView.as_view(), name='validate')
] ]

View file

@ -244,7 +244,8 @@ class SignupValidatedView(SignupValidateView):
lurl=login_url) lurl=login_url)
else: else:
home_url = '<a href="' + \ home_url = '<a href="' + \
reverse('datacenterlight:index') + '">Data Center Light</a>' reverse('datacenterlight:index') + \
'">Data Center Light</a>'
message = '{sorry_message} <br />{go_back_to} {hurl}'.format( message = '{sorry_message} <br />{go_back_to} {hurl}'.format(
sorry_message=_("Sorry. Your request is invalid."), sorry_message=_("Sorry. Your request is invalid."),
go_back_to=_('Go back to'), go_back_to=_('Go back to'),
@ -569,7 +570,7 @@ class PaymentVMView(LoginRequiredMixin, FormView):
customer=customer.stripe_id) customer=customer.stripe_id)
# Check if the payment was approved # 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') 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') return HttpResponseRedirect(reverse('hosting:payment') + '#payment_error')
@ -831,6 +832,7 @@ class VirtualMachineView(LoginRequiredMixin, View):
serializer = VirtualMachineSerializer(vm) serializer = VirtualMachineSerializer(vm)
context = { context = {
'virtual_machine': serializer.data, 'virtual_machine': serializer.data,
'order': HostingOrder.objects.get(vm_id=serializer.data['vm_id'])
} }
except: except:
pass pass