diff --git a/Changelog b/Changelog index fcf3c16c..95b7dab8 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,11 @@ +1.0.18: 2017-07-02 + * [datacenterlight] Introduced the new flow. Landing page -> Payment -> Order confirmation -> Success + * [datacenterlight] Fixed issue showing local time to the user in order confirmation page, vm pages (like ssh keys) + * [hosting] Fixed responsive issue while user signup + * [hosting] Fixed 500 error when user requests for a vm whose id does not belong to him + * [datacenterlight] Refactored partially dcl text and dcl support email to be obtained from env parameters + * [datacenterlight] Updated DE translations + * [hosting] Updated email text for user activation 1.0.17: 2017-06-16 * [datacenterlight] Cleanup OrderView useless code * [datacenterlight] Replaced GiB to GB @@ -5,7 +13,7 @@ * [datacenterlight] Fixed translations * [datacenterlight] Added email confirmation feature * [datacenterlight] Changed logo on datacerlight dashboard - * [datacenterlight] Credit card input disappearance fix + * [datacenterlight] Credit card input disappearance fix 1.0.16: 2017-06-15 * [datacenterlight] .po file issue with multiple definition fixed * [datacenterlight] Navbar items in dcl user area rearranged diff --git a/datacenterlight/locale/de/LC_MESSAGES/django.po b/datacenterlight/locale/de/LC_MESSAGES/django.po index f6628b9f..a0c9a470 100644 --- a/datacenterlight/locale/de/LC_MESSAGES/django.po +++ b/datacenterlight/locale/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-06-19 12:22+0530\n" +"POT-Creation-Date: 2017-07-02 23:08+0530\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,14 +18,6 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: templates/datacenterlight/base.html:72 -msgid "Please enter your name" -msgstr "Bitte gib Deinen Namen ein" - -#: templates/datacenterlight/base.html:73 -msgid "Please enter a valid email address" -msgstr "Bitte gib eine gültige E-Mail-Adresse ein" - #: templates/datacenterlight/beta_access.html:13 msgid "Enter name" msgstr "Name" @@ -99,15 +91,18 @@ msgid "" "datacenterlight account.
\n" " %(base_url)s%(activation_link)s\n" " " -msgstr "\n" -" Klicke hier um deinen %(dcl_text)s zu aktivieren.

\n" -" Oder kopiere den folgenden Link in die Adressleiste deines Browsers und folge dann dem Link um deinen %(dcl_text)s Account zu aktivieren.
\n" +msgstr "" +"\n" +" Klicke hier " +"um deinen %(dcl_text)s zu aktivieren.

\n" +" Oder kopiere den folgenden Link in die Adressleiste deines " +"Browsers und folge dann dem Link um deinen %(dcl_text)s Account zu " +"aktivieren.
\n" " %(base_url)s%(activation_link)s\n" " " #: templates/datacenterlight/emails/user_activation.html:123 #: templates/datacenterlight/emails/user_activation.txt:11 -#| msgid "Your Name" msgid "Your" msgstr "Dein" @@ -128,8 +123,9 @@ msgstr "" "\n" "Hallo,\n" "\n" -"Du kannst deinen %(dcl_text)s Account aktivieren, indem du hier klickst %(base_url)s" -"%(activation_link)s\n" +"Du kannst deinen %(dcl_text)s Account aktivieren, indem du hier klickst " +"%(base_url)s%(activation_link)s\n" + #: templates/datacenterlight/includes/_footer.html:11 #: templates/datacenterlight/includes/_footer.html:31 #: templates/datacenterlight/includes/_navbar.html:27 @@ -260,44 +256,112 @@ msgstr "MwSt. inklusive" msgid "Hosted in Switzerland" msgstr "Standort: Schweiz" -#: templates/datacenterlight/index.html:173 +#: templates/datacenterlight/index.html:161 +msgid "Please enter a value greater than or equal to 1." +msgstr "Bitte gib einen Wert größer oder gleich 1 ein." + +#: templates/datacenterlight/index.html:170 +msgid "Please enter a value greater than or equal to 2." +msgstr "Bitte gib einen Wert größer oder gleich 2 ein." + +#: templates/datacenterlight/index.html:179 +msgid "Please enter a value greater than or equal to 10." +msgstr "Bitte gib einen Wert größer oder gleich 10 ein" + +#: templates/datacenterlight/index.html:180 #: templates/datacenterlight/pricing.html:50 msgid "GB Storage (SSD)" msgstr "GB Storage (SSD)" -#: templates/datacenterlight/index.html:189 +#: templates/datacenterlight/index.html:199 msgid "Name" msgstr "" -#: templates/datacenterlight/index.html:190 +#: templates/datacenterlight/index.html:200 msgid "Your Name" msgstr "Dein Name" -#: templates/datacenterlight/index.html:193 +#: templates/datacenterlight/index.html:200 +msgid "Please enter your name" +msgstr "Bitte gib Deinen Namen ein" + +#: templates/datacenterlight/index.html:214 msgid "Email" msgstr "E-Mail-Adresse" -#: templates/datacenterlight/index.html:194 +#: templates/datacenterlight/index.html:215 msgid "Your Email" msgstr "Deine E-Mail" -#: templates/datacenterlight/index.html:197 +#: templates/datacenterlight/index.html:215 +msgid "Please enter a valid email address" +msgstr "Bitte gib eine gültige E-Mailadresse ein" + +#: templates/datacenterlight/index.html:228 #: templates/datacenterlight/pricing.html:79 msgid "Order Now!" msgstr "Bestelle jetzt!" -#: templates/datacenterlight/index.html:226 +#: templates/datacenterlight/index.html:254 msgid "Switzerland " msgstr "Schweiz" -#: templates/datacenterlight/index.html:243 +#: templates/datacenterlight/index.html:271 msgid "Questions?" msgstr "Fragen?" -#: templates/datacenterlight/index.html:243 +#: templates/datacenterlight/index.html:271 msgid "Contact us!" msgstr "Kontaktiere uns!" +#: templates/datacenterlight/order_detail.html:24 +msgid "Confirm Order" +msgstr "Bestellung Bestätigen" + +#: templates/datacenterlight/order_detail.html:30 +msgid "Billed To:" +msgstr "Rechnungsadresse" + +#: templates/datacenterlight/order_detail.html:39 +msgid "Date" +msgstr "Datum" + +#: templates/datacenterlight/order_detail.html:48 +msgid "Payment Method:" +msgstr "Bezahlmethode" + +#: templates/datacenterlight/order_detail.html:49 +msgid "ending" +msgstr "endend in" + +#: templates/datacenterlight/order_detail.html:59 +msgid "Order summary" +msgstr "Bestellungsübersicht" + +#: templates/datacenterlight/order_detail.html:63 +msgid "Cores" +msgstr "Prozessorkerne" + +#: templates/datacenterlight/order_detail.html:65 +msgid "Memory" +msgstr "Arbeitsspeicher" + +#: templates/datacenterlight/order_detail.html:67 +msgid "Disk space" +msgstr "Festplattenkapazität" + +#: templates/datacenterlight/order_detail.html:69 +msgid "Configuration" +msgstr "Konfiguration" + +#: templates/datacenterlight/order_detail.html:71 +msgid "Total" +msgstr "" + +#: templates/datacenterlight/order_detail.html:78 +msgid "Place order" +msgstr "Bestellen" + #: templates/datacenterlight/pricing.html:9 msgid "We are cutting down the costs significantly!" msgstr "Wir sorgen dafür, dass die Kosten für Dich signifikant abnehmen" @@ -328,6 +392,14 @@ msgstr "" msgid "as soon as possible!" msgstr "" +#: views.py:234 +msgid "is not a proper name" +msgstr "ist kein gültiger Name" + +#: views.py:241 +msgid "is not a proper email" +msgstr "ist keine gültige E-Mailadresse" + #~ msgid "Buy VM" #~ msgstr "VM Kaufen" diff --git a/datacenterlight/static/datacenterlight/css/landing-page.css b/datacenterlight/static/datacenterlight/css/landing-page.css index bcfa3f83..c9fd76aa 100755 --- a/datacenterlight/static/datacenterlight/css/landing-page.css +++ b/datacenterlight/static/datacenterlight/css/landing-page.css @@ -20,7 +20,7 @@ h3, h4, h5, h6 { - font-family: 'Lato-Regular', sans-serif; + font-family: 'Lato', sans-serif; font-weight: 300; } /*blue light #5A74AF*/ @@ -108,10 +108,12 @@ h6 { } .navbar-default .navbar-nav>li>a { cursor: pointer; + font-family: 'Lato-Regular', sans-serif; } .navbar-transparent .navbar-nav>li>a { color: #fff; cursor: pointer; + font-family: 'Lato-Regular', sans-serif; } .navbar-transparent .navbar-nav>li>a:hover { color: #fff; @@ -145,11 +147,13 @@ h6 { } .navbar-transparent .nav-language .select-language{ color: #fff; + font-family: 'Lato-Regular', sans-serif; } .nav-language .select-language span{ margin-left: 5px; margin-right: 5px; + font-family: 'Lato-Regular', sans-serif; } .nav-language .drop-language{ @@ -171,6 +175,7 @@ h6 { } .nav-language .drop-language a{ cursor: pointer; + font-family: 'Lato-Regular', sans-serif; } .navbar-transparent .nav-language .drop-language{ background: transparent; @@ -178,6 +183,7 @@ h6 { } .navbar-transparent .nav-language .drop-language a{ color: #fff; + font-family: 'Lato-Regular', sans-serif; } .nav-language:hover .drop-language{ display: block; @@ -430,7 +436,6 @@ h6 { } .pricing-section .card .description{ padding: 12px; - border-bottom: 1px solid rgba(128, 128, 128, 0.3); } .pricing-section .card .descriptions{ padding: 10px 30px; @@ -445,7 +450,8 @@ h6 { text-align: left; } .pricing-section .text .section-heading{ - font-size: 50px; + /*font-size: 50px;*/ + font-size: 48px; line-height: 50px; padding-bottom: 25px; color: #3a3a3a; @@ -571,6 +577,138 @@ h6 { width: 70px; right: 0; } +/*Why DCL*/ +.full-whydcl-sec { + color: #fff; + text-align: center; + background-image: -ms-linear-gradient(right, #29427A 50%, #4F6699 100%); + background-image: -moz-linear-gradient(right, #29427A 50%, #4F6699 100%); + background-image: -o-linear-gradient(right, #29427A 50%, #4F6699 100%); + background-image: -webkit-gradient(linear, right top, left top, color-stop(50, #29427A), color-stop(100, #4F6699)); + background-image: -webkit-linear-gradient(right, #29427A 50%, #4F6699 100%); + background-image: linear-gradient(to left, #29427A 50%, #4F6699 100%); +} +.whydcl-header { + padding: 150px 0 150px 0; + text-align: center; + color: #f8f8f8; + background: url(../img/pattern.jpg) no-repeat center center; + background-size: cover; + position: relative; + background-attachment: fixed; +} +.whydcl-header::before { + content: ""; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + background: rgba(90, 116, 175,0.85); + /*background: rgba(45,70,122,0.8);*/ +} +.single-heading h2 { + font-size: 65px; + margin: 0; + padding: 0; +} +#tech_stack { + background: #fff; +} +#tech_stack h3 { + font-size: 42px; + width: 70%; +} +hr.thick-divider { + border-top: 3px solid #eee !important; +} +.space { + padding: 50px 0; +} +tech-sub-sec h2 { + font-size: 45px; + line-height: 60px; + padding-bottom: 25px; + color: #3a3a3a; + letter-spacing: 1px; +} +.logo-wrap { + text-align: center; + min-height: 140px; + padding: 20px 40px 30px 40px; +} +.btm-space{ + padding-bottom: 8px; +} +.percent-text { + font-size: 50px; + color: #999; +} +.tech-sub-sec h2 { + font-size: 40px; + line-height: 55px; +} +.space-middle{ + padding: 45px 0; +} +.padding-vertical{ + padding: 35px 0; +} +.percent-text img { + margin-left:20px; +} +.space-block { + padding: 30px 0; +} +.dropdown-menu { + border: 1px solid #fff; + -webkit-box-shadow: -8px 13px 31px -8px rgba(77,77,77,1); + -moz-box-shadow: -8px 13px 31px -8px rgba(77,77,77,1); + box-shadow: -8px 13px 31px -8px rgba(77,77,77,1); + display: none; + text-align: center; + border-radius: 4px !important; + padding: 5px !important; +} +.dropdown-menu > li > a:focus, .dropdown-menu > li > a:hover { + background: transparent; + text-decoration: underline !important; +} +.logo-wrap .logo-caption { + padding-top: 20px; + display: inline-block; + color: #999 !important; +} +.lead-light { + color: #999 !important; + line-height: 32px !important; +} +.logo-wrap-1 { + padding-top: 50px; +} +.dropdown-menu > li > a { + padding: 1px 10px !important; +} +.dropdown-menu{ + left: 0 !important; + min-width: 155px; + text-align: left; + margin-left: 15px; +} +.navbar-default .navbar-nav > .open > a, .navbar-default .navbar-nav > .open > a:focus, .navbar-default .navbar-nav > .open > a:hover { + background: transparent; + color: #fff; +} +.dropdown-menu > li > a { + font-size: 13px; + font-weight: 300; + font-family: 'Lato-Regular', sans-serif; +} +.navbar-default .navbar-nav > .active > a, .navbar-default .navbar-nav > .active > a:focus, .navbar-default .navbar-nav > .active > a:hover { + background: #2D457A; + color: #fff; + border-radius: 6px; +} /*Pricing page*/ @@ -579,6 +717,7 @@ h6 { background: -webkit-linear-gradient(top, #f0f4f7, #fff) no-repeat; background: linear-gradient(to bottom, #f0f4f7, #fff) no-repeat; display: flex; + font-family: 'Lato-Regular', sans-serif; } .price-calc-section .text{ width: 50%; @@ -634,6 +773,7 @@ h6 { padding: 15px 40px; } .price-calc-section .card .title h3{ + font-family: 'Lato-Regular', sans-serif; } .price-calc-section .card .price{ background: #5A74AF; @@ -646,11 +786,10 @@ h6 { } .price-calc-section .card .description{ padding: 12px; - border-bottom: 1px solid rgba(128, 128, 128, 0.3); position: relative; display: flex; - justify-content: space-around; - align-items: center; + justify-content: space-around !important; + align-items: center !important; } .price-calc-section .card .description span { @@ -661,10 +800,10 @@ h6 { width: 30%; text-align: left; } -.price-calc-section .card .description input{ - font-size: 20px; - text-align: center; - width: 60px; +.price-calc-section .card .description .select-number{ + font-size: 20px; + text-align: center; + width: 60px; } .price-calc-section .card .description i{ color: #29427A; @@ -701,8 +840,8 @@ h6 { .price-calc-section .card .check-ip{ font-size: 18px; } -.price-calc-section .card .description.input{ - justify-content: center; +.price-calc-section .card .justify-center{ + justify-content: center !important; } .price-calc-section .card .description.input label{ font-size: 15px; @@ -711,10 +850,11 @@ h6 { margin-bottom: 0; width: 40px; } -.price-calc-section .card .description.input input{ - width: 200px; - font-size: 14px; - text-align: left; +/*Changed class****.price-calc-section .card .description.input input*/ +.price-calc-section .card .description input{ + width: 200px; + font-size: 14px; + text-align: left; padding: 5px 10px; border-radius: 4px; border: 1px solid #d0d0d0; @@ -726,7 +866,74 @@ h6 { font-size: 17px; margin: 0 8px; } +.help-block.with-errors { + text-align: center; + margin: 0; + padding: 0; +} +.has-error .checkbox, .has-error .checkbox-inline, .has-error .control-label, .has-error .help-block, .has-error .radio, .has-error .radio-inline, .has-error.checkbox label, .has-error.checkbox-inline label, .has-error.radio label, .has-error.radio-inline label{ +color: #eb4d5c; +} +.form-group { + margin: 0; + border-bottom: 1px solid rgba(128, 128, 128, 0.3); +} +@media(max-width:767px) { + .percent-text { + font-size: 50px; + } + #tech_stack h3 { + font-size: 30px; + line-height: 40px; + width: 100%; + } + .navbar-nav .open .dropdown-menu { + text-align: left; + font-size: 12px; + } + .visible-mobile { + display:block; + } + .visible-desktop { + display:none !important; + } + .navbar-default .navbar-nav > .open > a, .navbar-default .navbar-nav > .open > a:focus, .navbar-default .navbar-nav > .open > a:hover { + background: transparent; + color: #777 !important; +} +} +@media screen and (min-device-width: 480px) and (max-device-width: 767px) { +.logo-wrap { + width: 50%; + padding: 15px 30px !important; + min-height: 179px; +} +.logo-wrap-1 { + width: 50%; + padding: 15px 30px !importantx; + min-height: 179px; +} +.landscape-xs-6{ + width: 50%; +} +.landscape-xs-8{ + width: 66.66666667%; +} +.landscape-xs-4{ + width: 33.33333333%; +} + +} +@media(min-width:768px) { + .visible-mobile { + display:none !important; + } + .visible-desktop { + display:block; + } + +} @media(max-width:990px) { .pricing-section .text { text-align: center; @@ -742,6 +949,22 @@ h6 { } @media(max-width:768px) { +.percent-text { + font-size: 43px; +} + .tech-sub-sec h2 { + font-size: 30px; +line-height: 40px; +} +.single-heading h2 { + font-size: 50px; + } +.logo-wrap { + padding: 10px; +} +.navbar-transparent li a { + color: #777 !important; +} .intro-message { padding-bottom: 15%; } @@ -783,7 +1006,7 @@ h6 { display: none; } .navbar-transparent .navbar-nav>li>a { - font-size: 15px; + font-size: 14px; color: #777; } .nav-language:hover{ @@ -911,7 +1134,7 @@ h6 { text-align: center; } - .price-calc-section .card .description input { + .price-calc-section .card .description .select-number{ font-size: 17px; text-align: center; width: 60px; @@ -920,6 +1143,12 @@ h6 { } @media(max-width:540px) { +.logo-wrap { + padding: 30px; +} +.percent-text { + text-align: center; +} .pricing-section .card { width: 90%; } @@ -941,6 +1170,14 @@ h6 { font-size: 15px; margin-left: 0px; } + .pull-left.space-middle { + width: 67%; + padding: 20px 0px; + text-align: left; +} +.pull-left.ssdimg { + width: 30%; +} } @@ -984,9 +1221,11 @@ h6 { margin-top: 15px; } } - - - +@media(min-width:1200px) { +.container-small{ + width:980px; +} +} footer { padding: 50px 0; diff --git a/datacenterlight/static/datacenterlight/img/Ceph_Logo.png b/datacenterlight/static/datacenterlight/img/Ceph_Logo.png new file mode 100644 index 00000000..44046920 Binary files /dev/null and b/datacenterlight/static/datacenterlight/img/Ceph_Logo.png differ diff --git a/datacenterlight/static/datacenterlight/img/cdistbyungleich.png b/datacenterlight/static/datacenterlight/img/cdistbyungleich.png new file mode 100644 index 00000000..433db7d5 Binary files /dev/null and b/datacenterlight/static/datacenterlight/img/cdistbyungleich.png differ diff --git a/datacenterlight/static/datacenterlight/img/devuan.png b/datacenterlight/static/datacenterlight/img/devuan.png new file mode 100644 index 00000000..102dd476 Binary files /dev/null and b/datacenterlight/static/datacenterlight/img/devuan.png differ diff --git a/datacenterlight/static/datacenterlight/img/django.png b/datacenterlight/static/datacenterlight/img/django.png new file mode 100644 index 00000000..a89b6c76 Binary files /dev/null and b/datacenterlight/static/datacenterlight/img/django.png differ diff --git a/datacenterlight/static/datacenterlight/img/opennebula.png b/datacenterlight/static/datacenterlight/img/opennebula.png new file mode 100644 index 00000000..da2ce1c6 Binary files /dev/null and b/datacenterlight/static/datacenterlight/img/opennebula.png differ diff --git a/datacenterlight/static/datacenterlight/img/prometheus.png b/datacenterlight/static/datacenterlight/img/prometheus.png new file mode 100644 index 00000000..e7cca8e3 Binary files /dev/null and b/datacenterlight/static/datacenterlight/img/prometheus.png differ diff --git a/datacenterlight/static/datacenterlight/img/python-logo.png b/datacenterlight/static/datacenterlight/img/python-logo.png new file mode 100644 index 00000000..192f83f0 Binary files /dev/null and b/datacenterlight/static/datacenterlight/img/python-logo.png differ diff --git a/datacenterlight/static/datacenterlight/img/ssd.jpg b/datacenterlight/static/datacenterlight/img/ssd.jpg new file mode 100644 index 00000000..1a0cdfe9 Binary files /dev/null and b/datacenterlight/static/datacenterlight/img/ssd.jpg differ diff --git a/datacenterlight/static/datacenterlight/img/tayga.png b/datacenterlight/static/datacenterlight/img/tayga.png new file mode 100644 index 00000000..07b9e092 Binary files /dev/null and b/datacenterlight/static/datacenterlight/img/tayga.png differ diff --git a/datacenterlight/static/datacenterlight/js/main.js b/datacenterlight/static/datacenterlight/js/main.js index 26688e8e..bfe92631 100644 --- a/datacenterlight/static/datacenterlight/js/main.js +++ b/datacenterlight/static/datacenterlight/js/main.js @@ -53,6 +53,16 @@ /* --------------------------------------------- Nav panel classic --------------------------------------------- */ + if (window.matchMedia("(min-width: 767px)").matches) { + $('ul.nav li.dropdown').hover(function() { + $(this).find('.dropdown-menu').stop(true, true).delay(200).fadeIn(500); + }, function() { + $(this).find('.dropdown-menu').stop(true, true).delay(200).fadeOut(500); + }); +} else { + /* the viewport is less than 400 pixels wide */ +} + function _initScroll(){ @@ -66,9 +76,13 @@ if($(window).scrollTop() > 10 ){ $(".navbar").removeClass("navbar-transparent"); $(".navbar-default .btn-link").css("color", "#777"); + $(".dropdown-menu").removeClass("navbar-transparent"); + $(".dropdown-menu > li > a").css("color", "#777"); }else{ $(".navbar").addClass("navbar-transparent"); $(".navbar-default .btn-link").css("color", "#fff"); + $(".dropdown-menu").addClass("navbar-transparent"); + $(".dropdown-menu > li > a").css("color", "#fff"); } } function _initNavUrl(){ diff --git a/datacenterlight/templates/datacenterlight/base.html b/datacenterlight/templates/datacenterlight/base.html index c62d92f7..2c3f0e5d 100644 --- a/datacenterlight/templates/datacenterlight/base.html +++ b/datacenterlight/templates/datacenterlight/base.html @@ -33,6 +33,9 @@ + + {% include "google_analytics.html" %} + @@ -60,53 +63,5 @@ - - - - - + diff --git a/datacenterlight/templates/datacenterlight/includes/_navbar.html b/datacenterlight/templates/datacenterlight/includes/_navbar.html index 0d2f43f1..cb1a7180 100644 --- a/datacenterlight/templates/datacenterlight/includes/_navbar.html +++ b/datacenterlight/templates/datacenterlight/includes/_navbar.html @@ -1,67 +1,64 @@ {% load staticfiles i18n%} {% get_current_language as LANGUAGE_CODE %} {% load custom_tags %} - diff --git a/datacenterlight/templates/datacenterlight/index.html b/datacenterlight/templates/datacenterlight/index.html index 6d63b29b..822858ae 100755 --- a/datacenterlight/templates/datacenterlight/index.html +++ b/datacenterlight/templates/datacenterlight/index.html @@ -139,7 +139,7 @@
-
+ {% csrf_token %}

{% trans "VM hosting" %}

@@ -152,28 +152,37 @@
-
+

{% trans "Hosted in Switzerland" %}

-
+
+
- + Core +
+
-
+
+
- + GB RAM +
+
-
+
+
- + {% trans "GB Storage (SSD)" %} +
+
-
+
-
-
- - -
+
+
+ + +
+
+ {% for message in messages %} + {% if 'name' in message.tags %} +
  • + {{ message|safe }} +
+ {% endif %} + {% endfor %} +
+
+
+
+ + +
+
+ {% for message in messages %} + {% if 'email' in message.tags %} +
  • + {{ message|safe }} +
+ {% endif %} + {% endfor %} +
+
-
-
-
@@ -250,4 +278,4 @@
- {% endblock %} \ No newline at end of file + {% endblock %} diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html index cb412558..273074cf 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -1,7 +1,8 @@ {% extends "hosting/base_short.html" %} {% load staticfiles bootstrap3 %} {% load i18n %} -{% block content %} +{% load custom_tags %} +{% block content %}
{% if messages %} @@ -19,37 +20,38 @@ {% if not error %}
-
-

{% trans "Confirm Order"%}

{% trans "Order #"%} {{order.id}}

-
-
-
-
-
+
+

{% trans "Confirm Order"%}

+
+
+
+
+

{% trans "Billed To:"%}

- {{user.name}}
- {{order.billing_address.street_address}},{{order.billing_address.postal_code}}
- {{order.billing_address.city}}, {{order.billing_address.country}}. -
-
+ {% with request.session.billing_address_data as billing_address %} + {{request.session.user.name}}
{{billing_address|get_value_from_dict:'street_address'}}, {{billing_address|get_value_from_dict:'postal_code'}}
+ {{billing_address|get_value_from_dict:'city'}}, {{billing_address|get_value_from_dict:'country'}}. + {% endwith %} +
+
{% trans "Date"%}:
- {{order.created_at}}

+ {% now "Y-m-d H:i" %}

-
-
-
-
- {% trans "Payment Method:"%}
- {{order.cc_brand}} ending **** {{order.last4}}
- {{user.email}} -
-
-
-
+
+
+
+
+ {% trans "Payment Method:"%}
+ {{cc_brand}} {% trans "ending" %} **** {{cc_last4}}
+ {{request.session.user.email}} +
+
+
+
@@ -57,23 +59,39 @@

{% trans "Order summary"%}


-

{% trans "Cores"%} {{vm.cores}}

-
-

{% trans "Memory"%} {{vm.memory}} GB

-
-

{% trans "Disk space"%} {{vm.disk_size}} GB

-
-

{% trans "Total"%}

{{vm.price}} CHF

+ {% with request.session.specs as vm %} +

{% trans "Cores"%} {{vm.cpu}}

+
+

{% trans "Memory"%} {{vm.memory}} GB

+
+

{% trans "Disk space"%} {{vm.disk_size}} GB

+
+

{% trans "Configuration"%} {{request.session.template.name}}

+
+

{% trans "Total"%}

{{vm.price}} CHF

+ {% endwith %}

- {% url 'datacenterlight:payment' as payment_url %} - {% if payment_url in request.META.HTTP_REFERER %} +
+ {% csrf_token %} - {% endif %} +
{% endif %} + + + {%endblock%} diff --git a/datacenterlight/templates/datacenterlight/whydatacenterlight.html b/datacenterlight/templates/datacenterlight/whydatacenterlight.html new file mode 100644 index 00000000..fa85721c --- /dev/null +++ b/datacenterlight/templates/datacenterlight/whydatacenterlight.html @@ -0,0 +1,254 @@ +{% extends "datacenterlight/base.html" %} +{% load staticfiles i18n%} +{% get_current_language as LANGUAGE_CODE %} + +{% block content %} + + + +
+
+
+
+
+
+

{% trans "Why Data Center Light?" %}

+
+
+
+
+
+
+
+
+
+
+
+
+
+

{% trans "Tech Stack" %}

+
+
+

{% trans "We are seriously open source." %}

+

{% blocktrans %} Our full software stack is open source – We don't use anything that isn't open source.
Yes, we are that cool. {% endblocktrans %}

+
+
+
+
+
+ Devuan + {% trans "Our services run on" %} +
+
+ Prometheus + {% trans "Our monitoring" %} +
+
+ Ceph + {% trans "Our storage layer" %} +
+
+ Django + {% trans "Our web frontend" %} +
+
+ Opennebula + {% trans "Our cloud" %} +
+
+ Cdist by ungleich + {% trans "Our configuration management system" %} +
+
+ Python + {% trans "Our awesome juice" %} +
+
+ Tayga + {% trans "Our NAT64 gateway" %} +
+ +
+
+
+
+ +
+
+
+
+
+
+ Opennebula +
+
+ Cdist byu ngleich +
+
+ Prometheus +
+
+
+
+

{% trans "We believe in giving back to the FOSS community." %}

+

{% blocktrans %}Data Center Light is the child of free and open source software (FOSS).
We grew up with it, live by it, and believe in it.
The more we work on our data center,
the more we contribute back to the FOSS community.{% endblocktrans %}

+
+
+
+
+
+ +
+
+
+
+
+
+

{% trans "We bring the future to you." %}

+
+
+

{% blocktrans %} Data Center Light uses the most modern technologies out there.
+ Your VM needs only IPv6. Data Center Light provides
transparent two-way IPv6/IPv4 translation. + {% endblocktrans %}

+
+
+ 100% IPv6 +
+
+
+
+
+ 100% SSD SSD +
+
+
+

{% blocktrans %} No more spinning metal plates! Data Center Light uses only SSDs. We keep things faster and lighter. {% endblocktrans %}

+
+
+
+ + + +
+
+
+ +
+ +
+ +
+ +
+
+

{% trans "Starting from only 15CHF per month. Try now." %}

+

{% trans "Actions speak louder than words. Let's do it, try our VM now." %}

+
+ +
+
+
+ +
+
+ {% csrf_token %} +
+

{% trans "VM hosting" %}

+
+
+ 15 + CHF/{% trans "month" %} +
+

{% trans "VAT included" %}

+
+
+
+
+

{% trans "Hosted in Switzerland" %}

+
+
+
+ + + Core + +
+
+
+
+
+ + + GB RAM + +
+
+
+
+
+ + + {% trans "GB Storage (SSD)" %} + +
+
+
+
+ + +
+ + +
+
+ + +
+
+ {% for message in messages %} + {% if 'name' in message.tags %} +
  • + {{ message|safe }} +
+ {% endif %} + {% endfor %} +
+
+
+
+ + +
+
+ {% for message in messages %} + {% if 'email' in message.tags %} +
  • + {{ message|safe }} +
+ {% endif %} + {% endfor %} +
+
+
+ +
+
+
+
+ +
+
+
+ +
+ + + + + {% endblock %} diff --git a/datacenterlight/templatetags/custom_tags.py b/datacenterlight/templatetags/custom_tags.py index edbda197..ce6e6724 100644 --- a/datacenterlight/templatetags/custom_tags.py +++ b/datacenterlight/templatetags/custom_tags.py @@ -20,3 +20,14 @@ def change_lang(context, lang=None, *args, **kwargs): activate(cur_language) return "%s" % url + + +@register.filter('get_value_from_dict') +def get_value_from_dict(dict_data, key): + """ + usage example {{ your_dict|get_value_from_dict:your_key }} + """ + if key: + return dict_data.get(key) + else: + return "" diff --git a/datacenterlight/urls.py b/datacenterlight/urls.py index 8a83df71..a08bacdb 100644 --- a/datacenterlight/urls.py +++ b/datacenterlight/urls.py @@ -1,16 +1,17 @@ from django.conf.urls import url -from .views import IndexView, BetaProgramView, LandingProgramView, \ - BetaAccessView, PricingView, SuccessView, PaymentOrderView, OrderConfirmationView +from .views import IndexView, BetaProgramView, LandingProgramView, BetaAccessView, PricingView, SuccessView, \ + PaymentOrderView, OrderConfirmationView, WhyDataCenterLightView urlpatterns = [ url(r'^/?$', IndexView.as_view(), name='index'), + url(r'^/whydatacenterlight/?$', WhyDataCenterLightView.as_view(), name='whydatacenterlight'), url(r'^/beta-program/?$', BetaProgramView.as_view(), name='beta'), url(r'^/landing/?$', LandingProgramView.as_view(), name='landing'), url(r'^/pricing/?$', PricingView.as_view(), name='pricing'), url(r'^/payment/?$', PaymentOrderView.as_view(), name='payment'), - url(r'^/order-confirmation/(?P\d+)/?$', OrderConfirmationView.as_view(), name='order_confirmation'), + url(r'^/order-confirmation/?$', OrderConfirmationView.as_view(), name='order_confirmation'), url(r'^/order-success/?$', SuccessView.as_view(), name='order_success'), url(r'^/beta_access?$', BetaAccessView.as_view(), name='beta_access'), ] diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 53abf02d..ea7ca3ef 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -12,12 +12,14 @@ from django import forms from django.core.exceptions import ValidationError from django.views.decorators.cache import cache_control from django.conf import settings +from django.utils.translation import ugettext_lazy as _ from utils.forms import BillingAddressForm, UserBillingAddressForm -from hosting.models import HostingOrder +from utils.models import BillingAddress +from hosting.models import HostingOrder, HostingBill from utils.stripe_utils import StripeUtils from datetime import datetime from membership.models import CustomUser, StripeCustomer -from oca.pool import WrongIdError + from opennebula_api.models import OpenNebulaManager from opennebula_api.serializers import VirtualMachineTemplateSerializer, VirtualMachineSerializer @@ -32,9 +34,15 @@ class SuccessView(TemplateView): def get(self, request, *args, **kwargs): if 'specs' not in request.session or 'user' not in request.session: return HttpResponseRedirect(reverse('datacenterlight:index')) + elif 'token' not in request.session: + return HttpResponseRedirect(reverse('datacenterlight:payment')) + elif 'order_confirmation' not in request.session: + return HttpResponseRedirect(reverse('datacenterlight:order_confirmation')) else: - del request.session['specs'] - del request.session['user'] + for session_var in ['specs', 'user', 'template', 'billing_address', 'billing_address_data', + 'token', 'customer']: + if session_var in request.session: + del request.session[session_var] return render(request, self.template_name) @@ -68,7 +76,6 @@ class PricingView(TemplateView): price = request.POST.get('total') template_id = int(request.POST.get('config')) - manager = OpenNebulaManager() template = manager.get_template(template_id) @@ -189,15 +196,14 @@ class IndexView(CreateView): @cache_control(no_cache=True, must_revalidate=True, no_store=True) def get(self, request, *args, **kwargs): - if 'specs' in request.session: - del request.session['specs'] - if 'user' in request.session: - del request.session['user'] + for session_var in ['specs', 'user', 'billing_address_data']: + if session_var in request.session: + del request.session[session_var] try: manager = OpenNebulaManager() templates = manager.get_templates() context = { - 'templates': VirtualMachineTemplateSerializer(templates, many=True).data, + 'templates': VirtualMachineTemplateSerializer(templates, many=True).data } except: messages.error(request, @@ -226,14 +232,16 @@ class IndexView(CreateView): try: name = name_field.clean(name) except ValidationError as err: - messages.add_message(self.request, messages.ERROR, '%(value) is not a proper name.'.format(name)) - return HttpResponseRedirect(reverse('datacenterlight:index')) + msg = '{} {}.'.format(name, _('is not a proper name')) + messages.add_message(self.request, messages.ERROR, msg, extra_tags='name') + return HttpResponseRedirect(reverse('datacenterlight:index') + "#order_form") try: email = email_field.clean(email) except ValidationError as err: - messages.add_message(self.request, messages.ERROR, '%(value) is not a proper email.'.format(email)) - return HttpResponseRedirect(reverse('datacenterlight:index')) + msg = '{} {}.'.format(email, _('is not a proper email')) + messages.add_message(self.request, messages.ERROR, msg, extra_tags='email') + return HttpResponseRedirect(reverse('datacenterlight:index') + "#order_form") specs = { 'cpu': cores, @@ -301,14 +309,53 @@ class IndexView(CreateView): return super(IndexView, self).form_valid(form) +class WhyDataCenterLightView(IndexView): + template_name = "datacenterlight/whydatacenterlight.html" + model = BetaAccess + + @cache_control(no_cache=True, must_revalidate=True, no_store=True) + def get(self, request, *args, **kwargs): + try: + manager = OpenNebulaManager() + templates = manager.get_templates() + context = { + 'templates': VirtualMachineTemplateSerializer(templates, many=True).data, + } + except: + messages.error( + request, + 'We have a temporary problem to connect to our backend. \ + Please try again in a few minutes' + ) + context = { + 'error': 'connection' + } + return render(request, self.template_name, context) + + class PaymentOrderView(FormView): template_name = 'hosting/payment.html' form_class = BillingAddressForm + def get_form_kwargs(self): + form_kwargs = super(PaymentOrderView, self).get_form_kwargs() + billing_address_data = self.request.session.get('billing_address_data') + if billing_address_data: + form_kwargs.update({ + 'initial': { + 'street_address': billing_address_data['street_address'], + 'city': billing_address_data['city'], + 'postal_code': billing_address_data['postal_code'], + 'country': billing_address_data['country'], + } + }) + return form_kwargs + def get_context_data(self, **kwargs): context = super(PaymentOrderView, self).get_context_data(**kwargs) context.update({ - 'stripe_key': settings.STRIPE_API_PUBLIC_KEY + 'stripe_key': settings.STRIPE_API_PUBLIC_KEY, + 'site_url': reverse('datacenterlight:index') }) return context @@ -323,13 +370,8 @@ class PaymentOrderView(FormView): if form.is_valid(): # Get billing address data billing_address_data = form.cleaned_data - context = self.get_context_data() - template = request.session.get('template') - specs = request.session.get('specs') - user = request.session.get('user') - vm_template_id = template.get('id', 1) - final_price = specs.get('price') token = form.cleaned_data.get('token') + user = request.session.get('user') try: CustomUser.objects.get(email=user.get('email')) except CustomUser.DoesNotExist: @@ -350,118 +392,130 @@ class PaymentOrderView(FormView): # Create Billing Address billing_address = form.save() - - # Make stripe charge to a customer - stripe_utils = StripeUtils() - charge_response = stripe_utils.make_charge(amount=final_price, - customer=customer.stripe_id) - charge = charge_response.get('response_object') - - # Check if the payment was approved - if not charge: - context.update({ - 'paymentError': charge_response.get('error'), - 'form': form - }) - return render(request, self.template_name, context) - - charge = charge_response.get('response_object') - - # Create OpenNebulaManager - manager = OpenNebulaManager(email=settings.OPENNEBULA_USERNAME, - password=settings.OPENNEBULA_PASSWORD) - - # Create a vm using logged user - vm_id = manager.create_vm( - template_id=vm_template_id, - specs=specs, - vm_name="{email}-{template_name}-{date}".format( - email=user.get('email'), - template_name=template.get('name'), - date=int(datetime.now().strftime("%s"))) - ) - - # Create a Hosting Order - order = HostingOrder.create( - price=final_price, - vm_id=vm_id, - customer=customer, - billing_address=billing_address - ) - - # Create a Hosting Bill - # not used - # bill = HostingBill.create( - # customer=customer, billing_address=billing_address) - - # Create Billing Address for User if he does not have one - if not customer.user.billing_addresses.count(): - billing_address_data.update({ - 'user': customer.user.id - }) - billing_address_user_form = UserBillingAddressForm( - billing_address_data) - billing_address_user_form.is_valid() - billing_address_user_form.save() - - # Associate an order with a stripe payment - order.set_stripe_charge(charge) - - # If the Stripe payment was successed, set order status approved - order.set_approved() - - vm = VirtualMachineSerializer(manager.get_vm(vm_id)).data - - context = { - 'name': user.get('name'), - 'email': user.get('email'), - 'cores': specs.get('cpu'), - 'memory': specs.get('memory'), - 'storage': specs.get('disk_size'), - 'price': specs.get('price'), - 'template': template.get('name'), - 'vm.name': vm['name'], - 'vm.id': vm['vm_id'], - 'order.id': order.id - } - email_data = { - 'subject': "Data Center Light Order from %s" % context['email'], - 'from_email': '(Data Center Light) Data Center Light Support ', - 'to': ['info@ungleich.ch'], - 'body': "\n".join(["%s=%s" % (k, v) for (k, v) in context.items()]), - 'reply_to': [context['email']], - } - email = EmailMessage(**email_data) - email.send() - return HttpResponseRedirect(reverse('datacenterlight:order_confirmation', kwargs={'pk': order.id})) + request.session['billing_address_data'] = billing_address_data + request.session['billing_address'] = billing_address.id + request.session['token'] = token + request.session['customer'] = customer.id + return HttpResponseRedirect(reverse('datacenterlight:order_confirmation')) else: return self.form_invalid(form) class OrderConfirmationView(DetailView): template_name = "datacenterlight/order_detail.html" + payment_template_name = 'hosting/payment.html' context_object_name = "order" model = HostingOrder - def get_context_data(self, **kwargs): - # Get context - context = super(DetailView, self).get_context_data(**kwargs) - obj = self.get_object() + @cache_control(no_cache=True, must_revalidate=True, no_store=True) + def get(self, request, *args, **kwargs): + if 'specs' not in request.session or 'user' not in request.session: + return HttpResponseRedirect(reverse('datacenterlight:index')) + if 'token' not in request.session: + return HttpResponseRedirect(reverse('datacenterlight:payment')) + stripe_customer_id = request.session.get('customer') + customer = StripeCustomer.objects.filter(id=stripe_customer_id).first() + stripe_utils = StripeUtils() + card_details = stripe_utils.get_card_details(customer.stripe_id, request.session.get('token')) + context = { + 'site_url': reverse('datacenterlight:index'), + 'cc_last4': card_details.get('response_object').get('last4'), + 'cc_brand': card_details.get('response_object').get('brand') + } + return render(request, self.template_name, context) + + def post(self, request, *args, **kwargs): + template = request.session.get('template') + specs = request.session.get('specs') + user = request.session.get('user') + stripe_customer_id = request.session.get('customer') + customer = StripeCustomer.objects.filter(id=stripe_customer_id).first() + billing_address_data = request.session.get('billing_address_data') + billing_address_id = request.session.get('billing_address') + billing_address = BillingAddress.objects.filter(id=billing_address_id).first() + vm_template_id = template.get('id', 1) + final_price = specs.get('price') + + # Make stripe charge to a customer + stripe_utils = StripeUtils() + charge_response = stripe_utils.make_charge(amount=final_price, + customer=customer.stripe_id) + charge = charge_response.get('response_object') + + # Check if the payment was approved + if not charge: + context = {} + context.update({ + 'paymentError': charge_response.get('error') + }) + return render(request, self.payment_template_name, context) + + charge = charge_response.get('response_object') + + # Create OpenNebulaManager manager = OpenNebulaManager(email=settings.OPENNEBULA_USERNAME, password=settings.OPENNEBULA_PASSWORD) - try: - vm = manager.get_vm(obj.vm_id) - context['vm'] = VirtualMachineSerializer(vm).data - context['next_url'] = reverse('datacenterlight:order_success') - except WrongIdError: - messages.error(self.request, - 'The VM you are looking for is unavailable at the moment. \ - Please contact Data Center Light support.' - ) - self.kwargs['error'] = 'WrongIdError' - context['error'] = 'WrongIdError' - except ConnectionRefusedError: - messages.error(self.request, - 'In order to create a VM, you need to create/upload your SSH KEY first.' - ) - return context + + # Create a vm using oneadmin, also specify the name + vm_id = manager.create_vm( + template_id=vm_template_id, + specs=specs, + vm_name="{email}-{template_name}-{date}".format( + email=user.get('email'), + template_name=template.get('name'), + date=int(datetime.now().strftime("%s"))) + ) + + # Create a Hosting Order + order = HostingOrder.create( + price=final_price, + vm_id=vm_id, + customer=customer, + billing_address=billing_address + ) + + # Create a Hosting Bill + HostingBill.create( + customer=customer, billing_address=billing_address) + + # Create Billing Address for User if he does not have one + if not customer.user.billing_addresses.count(): + billing_address_data.update({ + 'user': customer.user.id + }) + billing_address_user_form = UserBillingAddressForm( + billing_address_data) + billing_address_user_form.is_valid() + billing_address_user_form.save() + + # Associate an order with a stripe payment + order.set_stripe_charge(charge) + + # If the Stripe payment was successed, set order status approved + order.set_approved() + + vm = VirtualMachineSerializer(manager.get_vm(vm_id)).data + + context = { + 'name': user.get('name'), + 'email': user.get('email'), + 'cores': specs.get('cpu'), + 'memory': specs.get('memory'), + 'storage': specs.get('disk_size'), + 'price': specs.get('price'), + 'template': template.get('name'), + 'vm.name': vm['name'], + 'vm.id': vm['vm_id'], + 'order.id': order.id + } + email_data = { + 'subject': settings.DCL_TEXT + " Order from %s" % context['email'], + 'from_email': settings.DCL_SUPPORT_FROM_ADDRESS, + 'to': ['info@ungleich.ch'], + 'body': "\n".join(["%s=%s" % (k, v) for (k, v) in context.items()]), + 'reply_to': [context['email']], + } + email = EmailMessage(**email_data) + email.send() + request.session['order_confirmation'] = True + return HttpResponseRedirect(reverse('datacenterlight:order_success')) diff --git a/digitalglarus/templates/base_glarus.html b/digitalglarus/templates/base_glarus.html index 424f3aac..b94c813e 100644 --- a/digitalglarus/templates/base_glarus.html +++ b/digitalglarus/templates/base_glarus.html @@ -39,27 +39,13 @@ - + + {% include 'google_analytics.html' %} + diff --git a/digitalglarus/templates/new_base_glarus.html b/digitalglarus/templates/new_base_glarus.html index c970f79b..826b49c0 100644 --- a/digitalglarus/templates/new_base_glarus.html +++ b/digitalglarus/templates/new_base_glarus.html @@ -56,7 +56,7 @@ ga('send', 'pageview'); - +