Merged upstream master into tiwariav-task/3756/landing_billing_page
This commit is contained in:
		
				commit
				
					
						656e7563fd
					
				
			
		
					 25 changed files with 1676 additions and 498 deletions
				
			
		
							
								
								
									
										11
									
								
								Changelog
									
										
									
									
									
								
							
							
						
						
									
										11
									
								
								Changelog
									
										
									
									
									
								
							|  | @ -1,3 +1,14 @@ | |||
| Pre-changelog: 1.2.3 2017-09-20 | ||||
|     * #3484: [dcl, hosting] Refactored account activation, password reset, VM order and cancellation email | ||||
|     * #3731: [dcl, hosting] Added cdist ssh key handler  | ||||
|     * #3628: [dcl] on hosting, VM is created at credit card info submit | ||||
|     * #3772: [dcl] Updated hosting app billing into monthly subscription and added new text and translations | ||||
|     * #3786: [hosting] Redesigned the hosting invoice and order-confirmation page | ||||
|     * #3728: [hosting] VM Termination animation added | ||||
|     * #3777: [hosting] Create new VM calculator added like dcl landing | ||||
|     * Feature: [cms, blog] Added /cms prefix for all the django-cms generated urls | ||||
|     * Bugfix: [dcl, hosting] added host to celery error mails | ||||
|     * Bugfix: [ungleich] Fixed wrong subdomain digitalglarus.ungleich.ch | ||||
| 1.2.2: 2017-09-08 | ||||
|     * #3704: [hosting] Added my settings page | ||||
|     * #3771: [datacenterlight] Fixed the inconsistency in navbar style in billing page and onward | ||||
|  |  | |||
|  | @ -147,7 +147,6 @@ | |||
| 
 | ||||
|     function _fetchPricing() { | ||||
|         Object.keys(cardPricing).map(function(element) { | ||||
|             //$('#'+cardPricing[element].id).val(cardPricing[element].value);
 | ||||
|             $('input[name=' + element + ']').val(cardPricing[element].value); | ||||
|         }); | ||||
|         _calcPricing(); | ||||
|  |  | |||
|  | @ -26,9 +26,9 @@ | |||
|             <div class="help-block with-errors"> | ||||
|                 {% for message in messages %} | ||||
|                     {% if 'cores' in message.tags %} | ||||
|                      <ul class="list-unstyled"><li> | ||||
|                         {{ message|safe }} | ||||
|                     </li></ul> | ||||
|                         <ul class="list-unstyled"> | ||||
|                             <li>{{ message|safe }}</li> | ||||
|                         </ul> | ||||
|                     {% endif %} | ||||
|                 {% endfor %} | ||||
|             </div> | ||||
|  |  | |||
|  | @ -49,9 +49,14 @@ urlpatterns += i18n_patterns( | |||
|         include('ungleich_page.urls', | ||||
|                 namespace='ungleich_page'), | ||||
|         name='ungleich_page'), | ||||
|     url(r'^blog/', include('ungleich.urls', | ||||
|                            namespace='ungleich')), | ||||
|     url(r'^', include('cms.urls')) | ||||
|     url(r'^cms/blog/', | ||||
|         include('ungleich.urls', namespace='ungleich')), | ||||
|     url( | ||||
|         r'^blog/(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<day>\d{1,2})/(?P<slug>\w[-\w]*)/$', | ||||
|         RedirectView.as_view(pattern_name='ungleich:post-detail')), | ||||
|     url(r'^blog/|cms/$', RedirectView.as_view( | ||||
|         url=reverse_lazy('ungleich:post-list')), name='blog_list_view'), | ||||
|     url(r'^cms/', include('cms.urls')), | ||||
| ) | ||||
| 
 | ||||
| urlpatterns += [ | ||||
|  |  | |||
|  | @ -8,7 +8,7 @@ msgid "" | |||
| msgstr "" | ||||
| "Project-Id-Version: PACKAGE VERSION\n" | ||||
| "Report-Msgid-Bugs-To: \n" | ||||
| "POT-Creation-Date: 2017-09-20 20:23+0000\n" | ||||
| "POT-Creation-Date: 2017-09-23 19:00+0530\n" | ||||
| "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" | ||||
| "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" | ||||
| "Language-Team: LANGUAGE <LL@li.org>\n" | ||||
|  | @ -110,6 +110,27 @@ msgstr "vorherige" | |||
| msgid "next" | ||||
| msgstr "nächste" | ||||
| 
 | ||||
| msgid "Month" | ||||
| msgstr "Monat" | ||||
| 
 | ||||
| msgid "VAT included" | ||||
| msgstr "MwSt. inklusive" | ||||
| 
 | ||||
| msgid "Please enter a value in range 1 - 48." | ||||
| msgstr "Bitte gib einen Wert von 1 bis 48 ein." | ||||
| 
 | ||||
| msgid "Please enter a value in range 2 - 200." | ||||
| msgstr "Bitte gib einen Wert von 2 bis 200 ein." | ||||
| 
 | ||||
| msgid "Please enter a value in range 10 - 2000." | ||||
| msgstr "Bitte gib einen Wert von 10 bis 200 ein." | ||||
| 
 | ||||
| msgid "GB Storage (SSD)" | ||||
| msgstr "GB Storage (SSD)" | ||||
| 
 | ||||
| msgid "Continue" | ||||
| msgstr "Weiter" | ||||
| 
 | ||||
| msgid "SSH Key" | ||||
| msgstr "SSH Key" | ||||
| 
 | ||||
|  | @ -149,30 +170,12 @@ msgstr "Hast Du bereits ein Benutzerkonto?" | |||
| msgid "Login" | ||||
| msgstr "Anmelden" | ||||
| 
 | ||||
| msgid "New Virtual Machine" | ||||
| msgstr "Neue virtuelle Maschine" | ||||
| 
 | ||||
| msgid "Step 1. Select VM Template:" | ||||
| msgstr "Wähle eine Vorlage" | ||||
| 
 | ||||
| msgid "Step2. Select VM Configuration" | ||||
| msgstr "Wähle eine Konfiguration" | ||||
| 
 | ||||
| msgid "Price " | ||||
| msgstr "Preis" | ||||
| 
 | ||||
| msgid "CHF/Month" | ||||
| msgstr "CHF/Monat" | ||||
| 
 | ||||
| msgid "Start VM" | ||||
| msgstr "VM jetzt starten" | ||||
| msgid "Create VM" | ||||
| msgstr "VM erstellen" | ||||
| 
 | ||||
| msgid "My Dashboard" | ||||
| msgstr "Mein Dashboard" | ||||
| 
 | ||||
| msgid "Create VM" | ||||
| msgstr "VM erstellen" | ||||
| 
 | ||||
| msgid "My VMs" | ||||
| msgstr "Meine VMs" | ||||
| 
 | ||||
|  | @ -321,10 +324,14 @@ msgstr "Als gelesen markieren" | |||
| msgid "All notifications" | ||||
| msgstr "Alle Benachrichtigungen" | ||||
| 
 | ||||
| msgid "Date" | ||||
| msgstr "Datum" | ||||
| #, python-format | ||||
| msgid "%(page_header_text)s" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "Status:" | ||||
| msgid "Invoice Date" | ||||
| msgstr "Rechnung Datum" | ||||
| 
 | ||||
| msgid "Status" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "Approved" | ||||
|  | @ -333,10 +340,10 @@ msgstr "Akzeptiert" | |||
| msgid "Declined" | ||||
| msgstr "Abgelehnt" | ||||
| 
 | ||||
| msgid "Billed To:" | ||||
| msgid "Billed to" | ||||
| msgstr "Rechnungsadresse" | ||||
| 
 | ||||
| msgid "Payment Method:" | ||||
| msgid "Payment method" | ||||
| msgstr "Bezahlmethode" | ||||
| 
 | ||||
| msgid "ending in" | ||||
|  | @ -345,6 +352,9 @@ msgstr "endend in" | |||
| msgid "Order summary" | ||||
| msgstr "Bestellungsübersicht" | ||||
| 
 | ||||
| msgid "Product" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "Cores" | ||||
| msgstr "Prozessorkerne" | ||||
| 
 | ||||
|  | @ -354,15 +364,9 @@ msgstr "Arbeitsspeicher" | |||
| msgid "Disk space" | ||||
| msgstr "Festplattenkapazität" | ||||
| 
 | ||||
| msgid "Configuration" | ||||
| msgstr "Konfiguration" | ||||
| 
 | ||||
| msgid "Total" | ||||
| msgstr "Gesamt" | ||||
| 
 | ||||
| msgid "Month" | ||||
| msgstr "Monat" | ||||
| 
 | ||||
| #, python-format | ||||
| msgid "" | ||||
| "By clicking \"Place order\" this plan will charge your credit card account " | ||||
|  | @ -372,8 +376,10 @@ msgstr "" | |||
| "pro Monat belastet" | ||||
| 
 | ||||
| msgid "Place order" | ||||
| msgstr "Bestelle" | ||||
| msgstr "Bestellen" | ||||
| 
 | ||||
| msgid "BACK TO LIST" | ||||
| msgstr "ZURÜCK ZUR LISTE" | ||||
| 
 | ||||
| msgid "Processing..." | ||||
| msgstr "Abarbeitung..." | ||||
|  | @ -387,14 +393,14 @@ msgstr "Ein Problem ist aufgetreten. Bitte versuche es später noch einmal." | |||
| msgid "Order Nr." | ||||
| msgstr "Bestellung Nr." | ||||
| 
 | ||||
| msgid "Date" | ||||
| msgstr "Datum" | ||||
| 
 | ||||
| msgid "Amount" | ||||
| msgstr "Betrag" | ||||
| 
 | ||||
| msgid "Status" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "See Invoice" | ||||
| msgstr "Rechnung" | ||||
| msgstr "Siehe Rechnung" | ||||
| 
 | ||||
| msgid "Page" | ||||
| msgstr "" | ||||
|  | @ -405,6 +411,9 @@ msgstr "" | |||
| msgid "Your Order" | ||||
| msgstr "Deine Bestellung" | ||||
| 
 | ||||
| msgid "Configuration" | ||||
| msgstr "Konfiguration" | ||||
| 
 | ||||
| msgid "including VAT" | ||||
| msgstr "inkl. Mehrwertsteuer" | ||||
| 
 | ||||
|  | @ -552,6 +561,9 @@ msgstr "Aktueller Preis" | |||
| msgid "Your VM is" | ||||
| msgstr "Deine VM ist" | ||||
| 
 | ||||
| msgid "Terminating" | ||||
| msgstr "Beenden" | ||||
| 
 | ||||
| msgid "Pending" | ||||
| msgstr "In Vorbereitung" | ||||
| 
 | ||||
|  | @ -564,6 +576,11 @@ msgstr "Fehlgeschlagen" | |||
| msgid "Terminate VM" | ||||
| msgstr "VM Beenden" | ||||
| 
 | ||||
| msgid "Sorry, there was an unexpected error. Kindly retry." | ||||
| msgstr "" | ||||
| "Bitte entschuldige, es scheint ein unerwarteter Fehler aufgetreten zu sein. " | ||||
| "Versuche es doch bitte noch einmal." | ||||
| 
 | ||||
| msgid "Something doesn't work?" | ||||
| msgstr "Etwas funktioniert nicht?" | ||||
| 
 | ||||
|  | @ -573,9 +590,6 @@ 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" | ||||
| msgstr "Deine Virtuelle Maschine beenden" | ||||
| 
 | ||||
|  | @ -585,6 +599,11 @@ msgstr "Bist Du sicher, dass Du Deine virtuelle Maschine beenden willst" | |||
| msgid "OK" | ||||
| msgstr "" | ||||
| 
 | ||||
| #, python-format | ||||
| msgid "" | ||||
| "Your Virtual Machine %(virtual_machine.name)s is successfully terminated!" | ||||
| msgstr "Deine Virtuelle Machine (VM) %(virtual_machine.name)s wurde erfolgreich beendet!" | ||||
| 
 | ||||
| msgid "Virtual Machines" | ||||
| msgstr "Virtuelle Maschinen" | ||||
| 
 | ||||
|  | @ -639,6 +658,15 @@ msgstr "Ungültige Kreditkarte" | |||
| msgid "Confirm Order" | ||||
| msgstr "Bestellung Bestätigen" | ||||
| 
 | ||||
| msgid "" | ||||
| "The VM you are looking for is unavailable at the moment. Please contact Data " | ||||
| "Center Light support." | ||||
| msgstr "Kontaktiere den Data Center Light Support." | ||||
| 
 | ||||
| msgid "In order to create a VM, you need to create/upload your SSH KEY first." | ||||
| msgstr "" | ||||
| "Um eine VM zu erstellen musst du zuerst einen SSH-Key erstellen / hochladen." | ||||
| 
 | ||||
| msgid "Thank you for the order." | ||||
| msgstr "Danke für Deine Bestellung." | ||||
| 
 | ||||
|  | @ -646,24 +674,58 @@ msgid "" | |||
| "Your VM will be up and running in a few moments. We will send you a " | ||||
| "confirmation email as soon as it is ready." | ||||
| msgstr "" | ||||
| "Deine VM ist gleich bereit. Wir senden Dir eine Bestätigungsemail, " | ||||
| "sobald Du auf sie zugreifen kannst." | ||||
| "Deine VM ist gleich bereit. Wir senden Dir eine Bestätigungsemail, sobald Du " | ||||
| "auf sie zugreifen kannst." | ||||
| 
 | ||||
| msgid "In order to create a VM, you need to create/upload your SSH KEY first." | ||||
| msgstr "" | ||||
| "Um eine VM zu erstellen musst du zuerst einen SSH-Key erstellen / hochladen." | ||||
| msgid "Invalid number of cores" | ||||
| msgstr "Ungültige Anzahle CPU-Kerne" | ||||
| 
 | ||||
| msgid "Invalid RAM size" | ||||
| msgstr "Ungültige RAM-Grösse" | ||||
| 
 | ||||
| msgid "Invalid storage size" | ||||
| msgstr "Ungültige Speicher-Grösse" | ||||
| 
 | ||||
| msgid "" | ||||
| "We could not find the requested VM. Please                            " | ||||
| "contact Data Center Light Support." | ||||
| msgstr "Kontaktiere den Data Center Light Support." | ||||
| 
 | ||||
| msgid "Terminated" | ||||
| msgstr "Beendet" | ||||
| 
 | ||||
| msgid "Error terminating VM" | ||||
| msgstr "Fehler beenden VM" | ||||
| 
 | ||||
| msgid "Virtual Machine Cancellation" | ||||
| msgstr "VM Kündigung" | ||||
| 
 | ||||
| #, python-format | ||||
| msgid "VM %(VM_ID)s terminated successfully" | ||||
| msgstr "VM %(VM_ID)s erfolgreich beendet" | ||||
| #~ msgid "Close" | ||||
| #~ msgstr "Schliessen" | ||||
| 
 | ||||
| #~ msgid "VM %(VM_ID)s terminated successfully" | ||||
| #~ msgstr "VM %(VM_ID)s erfolgreich beendet" | ||||
| 
 | ||||
| #~ msgid "days" | ||||
| #~ msgstr "tage" | ||||
| 
 | ||||
| #~ msgid "New Virtual Machine" | ||||
| #~ msgstr "Neue virtuelle Maschine" | ||||
| 
 | ||||
| #~ msgid "Step 1. Select VM Template:" | ||||
| #~ msgstr "Wähle eine Vorlage" | ||||
| 
 | ||||
| #~ msgid "Step2. Select VM Configuration" | ||||
| #~ msgstr "Wähle eine Konfiguration" | ||||
| 
 | ||||
| #~ msgid "Price " | ||||
| #~ msgstr "Preis" | ||||
| 
 | ||||
| #~ msgid "CHF/Month" | ||||
| #~ msgstr "CHF/Monat" | ||||
| 
 | ||||
| #~ msgid "Start VM" | ||||
| #~ msgstr "VM jetzt starten" | ||||
| 
 | ||||
| #~ msgid "Finish Configuration" | ||||
| #~ msgstr "Konfiguration beenden" | ||||
|  | @ -719,9 +781,6 @@ msgstr "VM %(VM_ID)s erfolgreich beendet" | |||
| #~ msgid "Ipv6" | ||||
| #~ msgstr "IPv6" | ||||
| 
 | ||||
| #~ msgid "Close" | ||||
| #~ msgstr "Schliessen" | ||||
| 
 | ||||
| #~ msgid "Cancel" | ||||
| #~ msgstr "Beenden" | ||||
| 
 | ||||
|  |  | |||
|  | @ -139,6 +139,10 @@ | |||
| .modal-text p:not(:last-of-type){ | ||||
| 	margin-bottom: 5px; | ||||
| } | ||||
| 
 | ||||
| .modal-title + .modal-footer { | ||||
|     margin-top: 5px; | ||||
| } | ||||
| .modal-footer { | ||||
|     border-top: 0px solid #e5e5e5; | ||||
|     width: 100%; | ||||
|  | @ -355,4 +359,20 @@ | |||
| 
 | ||||
| .no-cards a { | ||||
|   color: #7ca3d0; | ||||
| } | ||||
| 
 | ||||
| .btn-plain { | ||||
|   background: transparent; | ||||
|   border: none; | ||||
|   fill: #595959; | ||||
|   color: #595959; | ||||
|   outline: none; | ||||
| } | ||||
| .btn-plain:hover, | ||||
| .btn-plain:focus, | ||||
| .btn-plain:active, | ||||
| .btn-plain:active:focus { | ||||
|   outline: none; | ||||
|   color: #999; | ||||
|   fill: #999; | ||||
| } | ||||
|  | @ -580,9 +580,9 @@ a.unlink:hover { | |||
| } | ||||
| 
 | ||||
| .dcl-place-order-text{ | ||||
|    font-size: 13px; | ||||
|    /* font-size: 13px; */ | ||||
|    color: #808080; | ||||
|    margin-bottom: 15px; | ||||
|    /* margin-bottom: 15px; */ | ||||
| } | ||||
| 
 | ||||
| .dcl-order-table-total .tbl-total { | ||||
|  | @ -853,6 +853,9 @@ a.list-group-item-danger:focus, | |||
| .panel-danger > .panel-heading { | ||||
|     color: #eb4d5c; | ||||
| } | ||||
| .alert-danger{ | ||||
|     background: rgba(235, 204, 209, 0.2); | ||||
| } | ||||
| .has-error .form-control, | ||||
| .has-error .form-control:focus, | ||||
| .has-error .form-control:active, | ||||
|  |  | |||
|  | @ -1,4 +1,15 @@ | |||
| .order-detail-container {padding-top: 70px; padding-bottom: 70px; margin-bottom: 70px;} | ||||
| .order-detail-container { | ||||
|     max-width: 600px; | ||||
|     margin: 100px auto 40px; | ||||
|     border: 1px solid #ccc; | ||||
|     padding: 15px; | ||||
| } | ||||
| 
 | ||||
| @media(min-width: 768px) { | ||||
|     .order-detail-container { | ||||
|         padding: 30px; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| .order-detail-container .invoice-title h2, .invoice-title h3 { | ||||
|     display: inline-block; | ||||
|  | @ -15,3 +26,67 @@ | |||
| .order-detail-container .table > tbody > tr > .thick-line { | ||||
|     border-top: 2px solid; | ||||
| } | ||||
| 
 | ||||
| .order-detail-container .dashboard-title-thin { | ||||
|     margin-top: 0; | ||||
|     margin-left: -3px; | ||||
| } | ||||
| 
 | ||||
| .order-detail-container .dashboard-title-thin .un-icon { | ||||
|     margin-top: -6px; | ||||
| } | ||||
| 
 | ||||
| .order-detail-container .dashboard-container-head { | ||||
|     position: relative; | ||||
|     padding: 0; | ||||
|     margin-bottom: 38px; | ||||
| } | ||||
| 
 | ||||
| .order-detail-container .dashboard-container-options { | ||||
|     position: absolute; | ||||
|     top: 10px; | ||||
|     right: 0; | ||||
| } | ||||
| .order-detail-container .dashboard-container-options .svg-img { | ||||
|     height: 22px; | ||||
|     width: 22px; | ||||
| } | ||||
| 
 | ||||
| .order-detail-container .order-details { | ||||
|     margin-bottom: 30px; | ||||
| } | ||||
| 
 | ||||
| .order-detail-container .order-details strong { | ||||
|     color: #595959; | ||||
| } | ||||
| 
 | ||||
| .order-detail-container h4 { | ||||
|     font-size: 16px; | ||||
|     font-weight: bold; | ||||
|     margin-bottom: 10px; | ||||
| } | ||||
| 
 | ||||
| .order-detail-container p { | ||||
|     margin-bottom: 5px; | ||||
|     color: #595959; | ||||
| } | ||||
| 
 | ||||
| .order-detail-container hr { | ||||
|     margin: 15px 0; | ||||
| } | ||||
| 
 | ||||
| @media (max-width: 767px) { | ||||
|     .order-confirm-btn { | ||||
|         text-align: center; | ||||
|         margin-top: 10px; | ||||
|     } | ||||
|     .order-detail-container .dashboard-container-options { | ||||
|         position: absolute; | ||||
|         top: 4px; | ||||
|         right: -4px; | ||||
|     } | ||||
|     .order-detail-container .dashboard-container-options .svg-img { | ||||
|         height: 16px; | ||||
|         width: 16px; | ||||
|     } | ||||
| } | ||||
|  |  | |||
							
								
								
									
										237
									
								
								hosting/static/hosting/css/price_calculator.css
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										237
									
								
								hosting/static/hosting/css/price_calculator.css
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,237 @@ | |||
| /* Create VM calculator */ | ||||
| 
 | ||||
| .price-calc-section { | ||||
|     padding: 80px 40px !important; | ||||
| } | ||||
| 
 | ||||
| @media (max-width: 768px) { | ||||
|     .price-calc-section { | ||||
|         margin-top: 40px; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| .price-calc-section .text { | ||||
|     width: 50%; | ||||
| } | ||||
| 
 | ||||
| .price-calc-section .text .section-heading { | ||||
|     font-size: 48px; | ||||
|     line-height: 48px; | ||||
|     padding-bottom: 27px; | ||||
|     color: #3a3a3a; | ||||
|     letter-spacing: 1px; | ||||
|     position: relative; | ||||
|     text-align: right; | ||||
| } | ||||
| 
 | ||||
| .price-calc-section .text .description { | ||||
|     font-size: 20px; | ||||
|     text-align: right; | ||||
| } | ||||
| 
 | ||||
| .price-calc-section .text .section-heading::before { | ||||
|     content: ""; | ||||
|     position: absolute; | ||||
|     bottom: 0; | ||||
|     background: #29427A; | ||||
|     height: 7px; | ||||
|     width: 70px; | ||||
|     right: 0; | ||||
| } | ||||
| 
 | ||||
| .price-calc-section .card { | ||||
|     width: 50%; | ||||
|     margin: 0 auto; | ||||
|     background: #fff; | ||||
|     box-shadow: 1px 3px 6px 2px rgba(0, 0, 0, 0.2); | ||||
|     padding-bottom: 30px; | ||||
|     text-align: center; | ||||
|     max-width: 320px; | ||||
|     position: relative; | ||||
| } | ||||
| 
 | ||||
| @media (min-width: 768px) { | ||||
|     .price-calc-section .card { | ||||
|         margin-left: 0; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| .price-calc-section .landing { | ||||
|     width: 100% !important; | ||||
| } | ||||
| 
 | ||||
| .no-padding { | ||||
|     padding: 0 !important; | ||||
| } | ||||
| 
 | ||||
| .price-calc-section .card .img-beta { | ||||
|     position: absolute; | ||||
|     top: 5px; | ||||
|     width: 60px; | ||||
|     left: 3px; | ||||
| } | ||||
| 
 | ||||
| .price-calc-section .card .title { | ||||
|     padding: 15px 40px; | ||||
| } | ||||
| 
 | ||||
| .price-calc-section .card .title h3 { | ||||
|     /*font-family: 'Lato', sans-serif;*/ | ||||
|     font-weight: normal; | ||||
| } | ||||
| 
 | ||||
| .price-calc-section .card .price { | ||||
|     background: #5A74AF; | ||||
|     padding: 22px; | ||||
|     color: #fff; | ||||
|     font-size: 32px; | ||||
| } | ||||
| 
 | ||||
| .price-calc-section .card .price .price-text { | ||||
|     font-size: 14px; | ||||
| } | ||||
| 
 | ||||
| .price-calc-section .card .description { | ||||
|     padding: 7px 8px 2px; | ||||
|     position: relative; | ||||
|     display: flex; | ||||
|     justify-content: space-around !important; | ||||
|     align-items: center !important; | ||||
| } | ||||
| 
 | ||||
| .price-calc-section .card .description span { | ||||
|     font-size: 14px; | ||||
|     margin-left: 5px; | ||||
|     /* margin-left: 0px; */ | ||||
|     /* justify-self: start; */ | ||||
|     width: 29%; | ||||
|     text-align: left; | ||||
|     line-height: 16px; | ||||
|     /* font-weight: normal; */ | ||||
| } | ||||
| 
 | ||||
| .price-calc-section .card .description .select-number{ | ||||
|     font-size: 16px; | ||||
|     text-align: center; | ||||
|     width: 85px; | ||||
| } | ||||
| 
 | ||||
| .price-calc-section .card .description i { | ||||
|     color: #29427a; | ||||
|     cursor: pointer; | ||||
|     font-size: 20px; | ||||
|     border: 1px solid #ccc; | ||||
|     padding: 5px 6px 3px; | ||||
|     border-radius: 5px; | ||||
| } | ||||
| 
 | ||||
| .price-calc-section .card .description .left { | ||||
|     margin-right: 7px; | ||||
| } | ||||
| 
 | ||||
| .price-calc-section .card .description .right { | ||||
|     margin-left: 7px; | ||||
| } | ||||
| 
 | ||||
| .price-calc-section .card .descriptions { | ||||
|     padding: 10px; | ||||
| } | ||||
| 
 | ||||
| .price-calc-section .card .description p { | ||||
|     margin: 0; | ||||
| } | ||||
| 
 | ||||
| .price-calc-section .card .btn { | ||||
|     margin-top: 15px; | ||||
|     font-size: 20px; | ||||
|     width: 150px; | ||||
|     border: none; | ||||
| } | ||||
| 
 | ||||
| .price-calc-section .card .select-configuration select { | ||||
|     outline: none; | ||||
|     background: #fff; | ||||
|     border-color: #d0d0d0; | ||||
|     height: 32px; | ||||
|     width: 150px; | ||||
|     text-align: center; | ||||
|     font-size: 14px; | ||||
|     margin-left: 10px; | ||||
|     padding: 6px; | ||||
|     border-radius: 4px; | ||||
| } | ||||
| 
 | ||||
| .price-calc-section .card .check-ip { | ||||
|     font-size: 18px; | ||||
| } | ||||
| 
 | ||||
| .price-calc-section .card .justify-center { | ||||
|     justify-content: center !important; | ||||
| } | ||||
| 
 | ||||
| .price-calc-section .card .description.input label { | ||||
|     font-size: 15px; | ||||
|     font-weight: 700; | ||||
|     /*font-weight: 800;*/ | ||||
|     /*font-family: 'Lato';*/ | ||||
|     margin-bottom: 0; | ||||
|     width: 40px; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /*Changed class****.price-calc-section .card .description.input input*/ | ||||
| 
 | ||||
| .price-calc-section .card .description input { | ||||
|     width: 200px; | ||||
|     font-size: 14px; | ||||
|     text-align: left; | ||||
|     padding: 4px 10px; | ||||
|     border-radius: 4px; | ||||
|     border: 1px solid #d0d0d0; | ||||
|     background: #fff; | ||||
|     margin-left: 10px; | ||||
| } | ||||
| 
 | ||||
| .price-calc-section .card .check-ip input[type=checkbox] { | ||||
|     font-size: 17px; | ||||
|     margin: 0 8px; | ||||
| } | ||||
| 
 | ||||
| .price-calc-section .help-block.with-errors { | ||||
|     text-align: center; | ||||
|     margin: 0 0; | ||||
|     padding: 0 0 5px; | ||||
| } | ||||
| .price-calc-section .help-block.with-errors ul { | ||||
|     margin-bottom: 0; | ||||
| } | ||||
| 
 | ||||
| .price-calc-section .form-group { | ||||
|     margin: 0; | ||||
|     position: relative; | ||||
| } | ||||
| 
 | ||||
| .price-calc-section .form-group:after { | ||||
|     content: ' '; | ||||
|     display: block; | ||||
|     position: absolute; | ||||
|     bottom: 0; | ||||
|     left: 18%; | ||||
|     z-index: 20; | ||||
|     height: 1px; | ||||
|     width: 65%; | ||||
|     background: rgba(128, 128, 128, 0.2); | ||||
| } | ||||
| 
 | ||||
| .price-calc-section .btn-primary { | ||||
|     background: #29427A; | ||||
|     border-color: #29427A; | ||||
|     color: #fff; | ||||
|     width: auto; | ||||
| } | ||||
| 
 | ||||
| @media(min-width: 768px) { | ||||
|     .create-vm-container { | ||||
|         padding-top: 120px; | ||||
|     } | ||||
| } | ||||
|  | @ -290,6 +290,11 @@ | |||
|   text-align: center; | ||||
| } | ||||
| 
 | ||||
| .vm-vmid .alert { | ||||
|   margin-top: 15px; | ||||
|   margin-bottom: -60px; | ||||
| } | ||||
| 
 | ||||
| .vm-item-lg { | ||||
|   font-size: 22px; | ||||
|   margin-top: 5px; | ||||
|  | @ -305,6 +310,10 @@ | |||
|   color: #e47f2f; | ||||
| } | ||||
| 
 | ||||
| .vm-color-failed { | ||||
|   color: #eb4d5c; | ||||
| } | ||||
| 
 | ||||
| .vm-detail-item .value{ | ||||
|   font-weight: 400; | ||||
| } | ||||
|  | @ -512,7 +521,7 @@ | |||
|   color: #87B6EA; | ||||
| } | ||||
| 
 | ||||
| .vm-status, .vm-status-active, .vm-status-failed { | ||||
| .vm-status, .vm-status-active, .vm-status-failed, .vm-status-pending { | ||||
|   font-weight: 600; | ||||
| } | ||||
| .vm-status-active { | ||||
|  | @ -521,6 +530,9 @@ | |||
| .vm-status-failed { | ||||
|   color: #eb4d5c; | ||||
| } | ||||
| .vm-status-pending { | ||||
|   color: #e47f2f; | ||||
| } | ||||
| 
 | ||||
| @media (min-width:768px) { | ||||
|   .dashboard-subtitle { | ||||
|  | @ -627,4 +639,28 @@ | |||
|     left: auto; | ||||
|     right: 8px; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .processing > .btn { | ||||
|   position: relative; | ||||
|   border-color: #eee; | ||||
| } | ||||
| .processing > .btn:hover, | ||||
| .processing > .btn:focus, | ||||
| .processing > .btn:active { | ||||
|   border-color: #eee; | ||||
| } | ||||
| 
 | ||||
| .processing > .btn:after { | ||||
|   content: ' '; | ||||
|   display: block; | ||||
|   position: absolute; | ||||
|   background-image: url('/static/hosting/img/ajax-loader.gif'); | ||||
|   background-repeat: no-repeat; | ||||
|   background-position: center; | ||||
|   background-color: #eee; | ||||
|   width: 100%; | ||||
|   top: 0; | ||||
|   height: 100%; | ||||
|   left: 0; | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								hosting/static/hosting/img/ajax-loader.gif
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								hosting/static/hosting/img/ajax-loader.gif
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 404 B | 
							
								
								
									
										18
									
								
								hosting/static/hosting/img/icon-pdf.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								hosting/static/hosting/img/icon-pdf.svg
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <svg width="492px" height="649px" viewBox="0 0 492 649" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> | ||||
|     <!-- Generator: Sketch 46.2 (44496) - http://www.bohemiancoding.com/sketch --> | ||||
|     <title>icon-pdf</title> | ||||
|     <desc>Created with Sketch.</desc> | ||||
|     <defs></defs> | ||||
|     <g id="Page-1" stroke="none" stroke-width="1" fill-rule="evenodd"> | ||||
|         <g id="icon-pdf" fill-rule="nonzero"> | ||||
|             <g id="Group" transform="translate(93.000000, 269.000000)"> | ||||
|                 <path d="M98,47.8 C98,54.8 97,61.7 95.1,66.6 C93.1,72.5 90.1,76.5 86.2,80.5 C82.2,84.5 76.3,87.4 70.4,89.4 C64.5,91.4 55.6,92.3 46.6,92.3 L34.7,92.3 L34.7,134 L0,134 L0,0.3 L45.5,0.3 C54.4,0.3 62.4,1.3 69.3,3.2 C76.2,5.2 81.2,8.2 85.1,12.1 C89.1,16.1 92.1,21 94,27 C96.9,32 98,38.9 98,47.8 Z M61.3,46.8 C61.3,42.8 61.3,39.9 60.3,37.9 C60.3,35.9 59.3,33.9 57.4,32 C56.4,31 54.5,30 52.4,29.1 C50.4,29.1 47.4,28.1 44.5,28.1 L34.6,28.1 L34.6,63.7 L45.5,63.7 C48.4,63.7 50.4,63.7 52.5,62.7 C54.5,61.7 56.5,61.7 57.5,59.8 C58.5,57.8 59.5,56.9 60.4,54.8 C61.3,53.8 61.3,50.8 61.3,46.8 Z" id="Shape"></path> | ||||
|                 <path d="M214.8,66.6 C214.8,78.5 213.8,89.3 210.8,97.3 C208.8,106.2 204.9,113.1 199.9,118.1 C195,123.1 189,127 182.1,130 C175.2,132 167.3,134 157.3,134 L108.8,134 L108.8,0.3 L156.3,0.3 C166.2,0.3 175.1,1.3 182,4.3 C188.9,7.2 194.9,10.2 199.8,16.2 C204.8,21.2 207.7,28.1 209.7,37 C213.8,44.8 214.8,54.7 214.8,66.6 Z M178.1,65.6 C178.1,58.7 178.1,52.7 177.1,47.8 C176.1,42.8 175.1,39.9 174.2,36.9 C172.2,34 170.2,32.9 168.3,31.9 C165.4,30.9 162.4,30.9 158.4,30.9 L145.6,30.9 L145.6,103.2 L158.4,103.2 C162.4,103.2 165.4,103.2 168.3,102.2 C171.2,101.2 173.3,99.3 174.2,97.2 C176.2,94.3 177.1,91.3 178.2,86.3 C178.1,80.5 178.1,74.5 178.1,65.6 Z" id="Shape"></path> | ||||
|                 <polygon id="Shape" points="264.3 32 264.3 55.8 304.9 55.8 304.9 85.5 264.3 85.5 264.3 135 229.7 135 229.7 0.3 312.9 0.3 313.9 31 264.4 31 264.4 32"></polygon> | ||||
|             </g> | ||||
|             <path d="M491.3,649 L0.7,649 L0.7,0 L346.1,0 L491.3,145.2 L491.3,649 L491.3,649 Z M44.7,605 L448.4,605 L448.4,162.8 L327.4,42.9 L44.7,42.9 L44.7,605 Z" id="Shape"></path> | ||||
|             <polygon id="Shape" points="469.3 176 315.3 176 315.3 20.9 359.3 20.9 359.3 132 469.3 132"></polygon> | ||||
|         </g> | ||||
|     </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 2.3 KiB | 
							
								
								
									
										17
									
								
								hosting/static/hosting/img/icon-print.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								hosting/static/hosting/img/icon-print.svg
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,17 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <svg width="612px" height="612px" viewBox="0 0 612 612" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> | ||||
|     <!-- Generator: Sketch 46.2 (44496) - http://www.bohemiancoding.com/sketch --> | ||||
|     <title>54471</title> | ||||
|     <desc>Created with Sketch.</desc> | ||||
|     <defs></defs> | ||||
|     <g id="Page-1" stroke="none" stroke-width="1" fill-rule="evenodd"> | ||||
|         <g id="54471" fill-rule="nonzero"> | ||||
|             <path d="M0,225.633 L0,386.367 C0,417.272 30.6,417.272 30.6,417.272 L83.454,417.272 L83.454,389.454 L27.818,389.454 L27.818,222.545 L584.181,222.545 L584.181,389.454 L528.545,389.454 L528.545,417.272 L581.4,417.272 C581.4,417.272 612,417.272 612,386.367 L612,225.633 C612,225.633 612,194.727 581.4,194.727 L30.6,194.727 C0,194.728 0,225.633 0,225.633 Z" id="Shape"></path> | ||||
|             <polygon id="Shape" points="500.728 166.909 500.728 0 111.273 0 111.273 166.909 139.091 166.909 139.091 27.818 472.909 27.818 472.909 166.909"></polygon> | ||||
|             <path d="M528.546,292.091 C528.546,278.182 514.358,278.182 514.358,278.182 L97.642,278.182 C97.642,278.182 83.455,278.182 83.455,292.091 C83.455,306 97.642,306 97.642,306 L514.359,306 C514.358,306 528.546,306 528.546,292.091 Z" id="Shape"></path> | ||||
|             <rect id="Rectangle-path" x="166.909" y="500.728" width="278.182" height="27.818"></rect> | ||||
|             <path d="M500.728,612 L500.728,389.454 L111.273,389.454 L111.273,612 L500.728,612 Z M139.091,417.272 L472.909,417.272 L472.909,584.181 L139.091,584.181 L139.091,417.272 Z" id="Shape"></path> | ||||
|             <rect id="Rectangle-path" x="166.909" y="445.091" width="278.182" height="27.818"></rect> | ||||
|         </g> | ||||
|     </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 1.7 KiB | 
|  | @ -1,73 +1,75 @@ | |||
| (function($){ | ||||
|     "use strict"; // Start of use strict
 | ||||
|      | ||||
|      | ||||
|     $(window).load(function(){ | ||||
|      | ||||
|    | ||||
|     }); | ||||
|      | ||||
|     $(document).ready(function(){ | ||||
|       _initOs(); | ||||
|         | ||||
|     }); | ||||
|      | ||||
|     $(window).resize(function(){ | ||||
|          | ||||
|          | ||||
|     }); | ||||
|      | ||||
| 
 | ||||
| 
 | ||||
|    	function _initOs(){ | ||||
| 
 | ||||
|    		 | ||||
|    		$('.os-circle').click(function(event){ | ||||
|    			$('.os-circle').removeClass('active'); | ||||
|    			$(this).addClass('active'); | ||||
| 
 | ||||
|         var idTemplate = $(this).data('id'); | ||||
|         $('input[name=vm_template_id]').val(idTemplate); | ||||
|    		}); | ||||
|    		$('.config-box').click(function(event){ | ||||
|    			$('.config-box').removeClass('active'); | ||||
|    			$(this).addClass('active'); | ||||
|         var idConfig = $(this).data('id'); | ||||
|         var price = $(this).data('price'); | ||||
|         $('input[name=configuration]').val(idConfig); | ||||
|         $('.container-button').fadeIn(); | ||||
|         $('#priceValue').text(price); | ||||
|    		}); | ||||
| 
 | ||||
| 		$('.owl-carousel').owlCarousel({ | ||||
|         items:4, | ||||
|         nav: true, | ||||
|         margin:30, | ||||
|         responsiveClass:true, | ||||
|         navText: ['<i class="fa fa-angle-left"></i>', '<i class="fa fa-angle-right"></i>'], | ||||
|         responsive:{ | ||||
|             0:{ | ||||
|                 items:1, | ||||
|                 nav:true | ||||
|             }, | ||||
|             600:{ | ||||
|                 items:2, | ||||
|                 nav:true | ||||
|             }, | ||||
|             768:{ | ||||
|                 items:3, | ||||
|                 nav:true | ||||
|             }, | ||||
|             990:{ | ||||
|                 items:4, | ||||
|                 nav:true | ||||
|             } | ||||
|     var cardPricing = { | ||||
|         'cpu': { | ||||
|             'id': 'coreValue', | ||||
|             'value': 1, | ||||
|             'min': 1, | ||||
|             'max': 48, | ||||
|             'interval': 1 | ||||
|         }, | ||||
|         'ram': { | ||||
|             'id': 'ramValue', | ||||
|             'value': 2, | ||||
|             'min': 2, | ||||
|             'max': 200, | ||||
|             'interval': 1 | ||||
|         }, | ||||
|         'storage': { | ||||
|             'id': 'storageValue', | ||||
|             'value': 10, | ||||
|             'min': 10, | ||||
|             'max': 2000, | ||||
|             'interval': 10 | ||||
|         } | ||||
| 		}); | ||||
| 	} | ||||
|      | ||||
|      | ||||
|      | ||||
| })(jQuery);  | ||||
|     }; | ||||
| 
 | ||||
|     function _initPricing() { | ||||
|         _fetchPricing(); | ||||
| 
 | ||||
|         $('.fa-minus.left').click(function(event) { | ||||
|             var data = $(this).data('minus'); | ||||
| 
 | ||||
|             if (cardPricing[data].value > cardPricing[data].min) { | ||||
|                 cardPricing[data].value = Number(cardPricing[data].value) - cardPricing[data].interval; | ||||
|             } | ||||
|             _fetchPricing(); | ||||
|         }); | ||||
|         $('.fa-plus.right').click(function(event) { | ||||
|             var data = $(this).data('plus'); | ||||
|             if (cardPricing[data].value < cardPricing[data].max) { | ||||
|                 cardPricing[data].value = Number(cardPricing[data].value) + cardPricing[data].interval; | ||||
|             } | ||||
|             _fetchPricing(); | ||||
|         }); | ||||
| 
 | ||||
|         $('.input-price').change(function() { | ||||
|             var data = $(this).attr("name"); | ||||
|             cardPricing[data].value = $('input[name=' + data + ']').val(); | ||||
|             _fetchPricing(); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     function _fetchPricing() { | ||||
|         Object.keys(cardPricing).map(function(element) { | ||||
|             $('input[name=' + element + ']').val(cardPricing[element].value); | ||||
|         }); | ||||
|         _calcPricing(); | ||||
|     } | ||||
| 
 | ||||
|     function _calcPricing() { | ||||
|         var total = (cardPricing['cpu'].value * 5) + (2 * cardPricing['ram'].value) + (0.6 * cardPricing['storage'].value); | ||||
|         total = parseFloat(total.toFixed(2)); | ||||
| 
 | ||||
|         $("#total").text(total); | ||||
|         $('input[name=total]').val(total); | ||||
|     } | ||||
| 
 | ||||
|     $(document).ready(function() { | ||||
|         _initPricing(); | ||||
|     }); | ||||
| 
 | ||||
| })(jQuery); | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										73
									
								
								hosting/static/hosting/js/createvm_old.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								hosting/static/hosting/js/createvm_old.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,73 @@ | |||
| (function($){ | ||||
|     "use strict"; // Start of use strict
 | ||||
|      | ||||
|      | ||||
|     $(window).load(function(){ | ||||
|      | ||||
|    | ||||
|     }); | ||||
|      | ||||
|     $(document).ready(function(){ | ||||
|       _initOs(); | ||||
|         | ||||
|     }); | ||||
|      | ||||
|     $(window).resize(function(){ | ||||
|          | ||||
|          | ||||
|     }); | ||||
|      | ||||
| 
 | ||||
| 
 | ||||
|    	function _initOs(){ | ||||
| 
 | ||||
|    		 | ||||
|    		$('.os-circle').click(function(event){ | ||||
|    			$('.os-circle').removeClass('active'); | ||||
|    			$(this).addClass('active'); | ||||
| 
 | ||||
|         var idTemplate = $(this).data('id'); | ||||
|         $('input[name=vm_template_id]').val(idTemplate); | ||||
|    		}); | ||||
|    		$('.config-box').click(function(event){ | ||||
|    			$('.config-box').removeClass('active'); | ||||
|    			$(this).addClass('active'); | ||||
|         var idConfig = $(this).data('id'); | ||||
|         var price = $(this).data('price'); | ||||
|         $('input[name=configuration]').val(idConfig); | ||||
|         $('.container-button').fadeIn(); | ||||
|         $('#priceValue').text(price); | ||||
|    		}); | ||||
| 
 | ||||
| 		$('.owl-carousel').owlCarousel({ | ||||
|         items:4, | ||||
|         nav: true, | ||||
|         margin:30, | ||||
|         responsiveClass:true, | ||||
|         navText: ['<i class="fa fa-angle-left"></i>', '<i class="fa fa-angle-right"></i>'], | ||||
|         responsive:{ | ||||
|             0:{ | ||||
|                 items:1, | ||||
|                 nav:true | ||||
|             }, | ||||
|             600:{ | ||||
|                 items:2, | ||||
|                 nav:true | ||||
|             }, | ||||
|             768:{ | ||||
|                 items:3, | ||||
|                 nav:true | ||||
|             }, | ||||
|             990:{ | ||||
|                 items:4, | ||||
|                 nav:true | ||||
|             } | ||||
|         } | ||||
| 		}); | ||||
| 	} | ||||
|      | ||||
|      | ||||
|      | ||||
| })(jQuery);  | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										387
									
								
								hosting/static/hosting/js/html2pdf.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										387
									
								
								hosting/static/hosting/js/html2pdf.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,387 @@ | |||
| /** | ||||
|  * @license | ||||
|  * | ||||
|  * MIT License | ||||
|  * | ||||
|  * Copyright (c) 2017 Erik Koopmans | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all | ||||
|  * copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|  * SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| /** | ||||
|  * Generate a PDF from an HTML element or string using html2canvas and jsPDF. | ||||
|  * | ||||
|  * @param {Element|string} source The source element or HTML string. | ||||
|  * @param {Object=} opt An object of optional settings: 'margin', 'filename', | ||||
|  *    'image' ('type' and 'quality'), and 'html2canvas' / 'jspdf', which are | ||||
|  *    sent as settings to their corresponding functions. | ||||
|  */ | ||||
| var html2pdf = (function(html2canvas, jsPDF) { | ||||
| 
 | ||||
|   /* ---------- MAIN FUNCTION ---------- */ | ||||
| 
 | ||||
|   var html2pdf = function(source, opt) { | ||||
|     // Handle input.
 | ||||
|     opt = objType(opt) === 'object' ? opt : {}; | ||||
|     var source = html2pdf.parseInput(source, opt); | ||||
| 
 | ||||
|     // Determine the PDF page size.
 | ||||
|     var pageSize = jsPDF.getPageSize(opt.jsPDF); | ||||
|     pageSize.inner = { | ||||
|       width:  pageSize.width - opt.margin[1] - opt.margin[3], | ||||
|       height: pageSize.height - opt.margin[0] - opt.margin[2] | ||||
|     }; | ||||
|     pageSize.inner.ratio = pageSize.inner.height / pageSize.inner.width; | ||||
| 
 | ||||
|     // Copy the source element into a PDF-styled container div.
 | ||||
|     var container = html2pdf.makeContainer(source, pageSize); | ||||
|     var overlay = container.parentElement; | ||||
| 
 | ||||
|     // Get the locations of all hyperlinks.
 | ||||
|     if (opt.enableLinks) { | ||||
|       // Find all anchor tags and get the container's bounds for reference.
 | ||||
|       opt.links = []; | ||||
|       var links = container.querySelectorAll('a'); | ||||
|       var containerRect = unitConvert(container.getBoundingClientRect(), pageSize.k); | ||||
| 
 | ||||
|       // Treat each client rect as a separate link (for text-wrapping).
 | ||||
|       Array.prototype.forEach.call(links, function(link) { | ||||
|         var clientRects = link.getClientRects(); | ||||
|         for (var i=0; i<clientRects.length; i++) { | ||||
|           var clientRect = unitConvert(clientRects[i], pageSize.k); | ||||
|           clientRect.left -= containerRect.left; | ||||
|           clientRect.top -= containerRect.top; | ||||
|           opt.links.push({ el: link, clientRect: clientRect }); | ||||
|         } | ||||
|       }); | ||||
|     } | ||||
| 
 | ||||
|     // Render the canvas and pass the result to makePDF.
 | ||||
|     var onRendered = opt.html2canvas.onrendered || function() {}; | ||||
|     opt.html2canvas.onrendered = function(canvas) { | ||||
|       onRendered(canvas); | ||||
|       document.body.removeChild(overlay); | ||||
|       html2pdf.makePDF(canvas, pageSize, opt); | ||||
|     } | ||||
|     html2canvas(container, opt.html2canvas); | ||||
|   }; | ||||
| 
 | ||||
|   html2pdf.parseInput = function(source, opt) { | ||||
|     // Parse the opt object.
 | ||||
|     opt.jsPDF = opt.jsPDF || {}; | ||||
|     opt.html2canvas = opt.html2canvas || {}; | ||||
|     opt.filename = opt.filename && objType(opt.filename) === 'string' ? opt.filename : 'file.pdf'; | ||||
|     opt.enableLinks = opt.hasOwnProperty('enableLinks') ? opt.enableLinks : true; | ||||
|     opt.image = opt.image || {}; | ||||
|     opt.image.type = opt.image.type || 'jpeg'; | ||||
|     opt.image.quality = opt.image.quality || 0.95; | ||||
| 
 | ||||
|     // Parse the margin property of the opt object.
 | ||||
|     switch (objType(opt.margin)) { | ||||
|       case 'undefined': | ||||
|         opt.margin = 0; | ||||
|       case 'number': | ||||
|         opt.margin = [opt.margin, opt.margin, opt.margin, opt.margin]; | ||||
|         break; | ||||
|       case 'array': | ||||
|         if (opt.margin.length === 2) { | ||||
|           opt.margin = [opt.margin[0], opt.margin[1], opt.margin[0], opt.margin[1]]; | ||||
|         } | ||||
|         if (opt.margin.length === 4) { | ||||
|           break; | ||||
|         } | ||||
|       default: | ||||
|         throw 'Invalid margin array.'; | ||||
|     } | ||||
| 
 | ||||
|     // Parse the source element/string.
 | ||||
|     if (!source) { | ||||
|       throw 'Missing source element or string.'; | ||||
|     } else if (objType(source) === 'string') { | ||||
|       source = createElement('div', { innerHTML: source }); | ||||
|     } else if (objType(source) === 'element') { | ||||
|       source = cloneNode(source, opt.html2canvas.javascriptEnabled); | ||||
|     } else { | ||||
|       throw 'Invalid source - please specify an HTML Element or string.'; | ||||
|     } | ||||
| 
 | ||||
|     // Return the parsed input (opt is modified in-place, no need to return).
 | ||||
|     return source; | ||||
|   }; | ||||
| 
 | ||||
|   html2pdf.makeContainer = function(source, pageSize) { | ||||
|     // Define the CSS styles for the container and its overlay parent.
 | ||||
|     var overlayCSS = { | ||||
|       position: 'fixed', overflow: 'hidden', zIndex: 1000, | ||||
|       left: 0, right: 0, bottom: 0, top: 0, | ||||
|       backgroundColor: 'rgba(0,0,0,0.8)' | ||||
|     }; | ||||
|     var containerCSS = { | ||||
|       position: 'absolute', width: pageSize.inner.width + pageSize.unit, | ||||
|       left: 0, right: 0, top: 0, height: 'auto', margin: 'auto', | ||||
|       backgroundColor: 'white' | ||||
|     }; | ||||
| 
 | ||||
|     // Set the overlay to hidden (could be changed in the future to provide a print preview).
 | ||||
|     overlayCSS.opacity = 0; | ||||
| 
 | ||||
|     // Create and attach the elements.
 | ||||
|     var overlay = createElement('div',   { className: 'html2pdf__overlay', style: overlayCSS }); | ||||
|     var container = createElement('div', { className: 'html2pdf__container', style: containerCSS }); | ||||
|     container.appendChild(source); | ||||
|     overlay.appendChild(container); | ||||
|     document.body.appendChild(overlay); | ||||
| 
 | ||||
|     // Enable page-breaks.
 | ||||
|     var pageBreaks = source.querySelectorAll('.html2pdf__page-break'); | ||||
|     var pxPageHeight = pageSize.inner.height * pageSize.k / 72 * 96; | ||||
|     Array.prototype.forEach.call(pageBreaks, function(el) { | ||||
|       el.style.display = 'block'; | ||||
|       var clientRect = el.getBoundingClientRect(); | ||||
|       el.style.height = pxPageHeight - (clientRect.top % pxPageHeight) + 'px'; | ||||
|     }, this); | ||||
| 
 | ||||
|     // Return the container.
 | ||||
|     return container; | ||||
|   }; | ||||
| 
 | ||||
|   html2pdf.makePDF = function(canvas, pageSize, opt) { | ||||
|     // Calculate the number of pages.
 | ||||
|     var ctx = canvas.getContext('2d'); | ||||
|     var pxFullHeight = canvas.height; | ||||
|     var pxPageHeight = Math.floor(canvas.width * pageSize.inner.ratio); | ||||
|     var nPages = Math.ceil(pxFullHeight / pxPageHeight); | ||||
| 
 | ||||
|     // Create a one-page canvas to split up the full image.
 | ||||
|     var pageCanvas = document.createElement('canvas'); | ||||
|     var pageCtx = pageCanvas.getContext('2d'); | ||||
|     var pageHeight = pageSize.inner.height; | ||||
|     pageCanvas.width = canvas.width; | ||||
|     pageCanvas.height = pxPageHeight; | ||||
| 
 | ||||
|     // Initialize the PDF.
 | ||||
|     var pdf = new jsPDF(opt.jsPDF); | ||||
| 
 | ||||
|     for (var page=0; page<nPages; page++) { | ||||
|       // Trim the final page to reduce file size.
 | ||||
|       if (page === nPages-1) { | ||||
|         pageCanvas.height = pxFullHeight % pxPageHeight; | ||||
|         pageHeight = pageCanvas.height * pageSize.inner.width / pageCanvas.width; | ||||
|       } | ||||
| 
 | ||||
|       // Display the page.
 | ||||
|       var w = pageCanvas.width; | ||||
|       var h = pageCanvas.height; | ||||
|       pageCtx.fillStyle = 'white'; | ||||
|       pageCtx.fillRect(0, 0, w, h); | ||||
|       pageCtx.drawImage(canvas, 0, page*pxPageHeight, w, h, 0, 0, w, h); | ||||
| 
 | ||||
|       // Add the page to the PDF.
 | ||||
|       if (page)  pdf.addPage(); | ||||
|       var imgData = pageCanvas.toDataURL('image/' + opt.image.type, opt.image.quality); | ||||
|       pdf.addImage(imgData, opt.image.type, opt.margin[1], opt.margin[0], | ||||
|                    pageSize.inner.width, pageHeight); | ||||
| 
 | ||||
|       // Add hyperlinks.
 | ||||
|       if (opt.enableLinks) { | ||||
|         var pageTop = page * pageSize.inner.height; | ||||
|         opt.links.forEach(function(link) { | ||||
|           if (link.clientRect.top > pageTop && link.clientRect.top < pageTop + pageSize.inner.height) { | ||||
|             var left = opt.margin[1] + link.clientRect.left; | ||||
|             var top = opt.margin[0] + link.clientRect.top - pageTop; | ||||
|             pdf.link(left, top, link.clientRect.width, link.clientRect.height, { url: link.el.href }); | ||||
|           } | ||||
|         }); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     // Finish the PDF.
 | ||||
|     pdf.save( opt.filename ); | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   /* ---------- UTILS ---------- */ | ||||
| 
 | ||||
|   // Determine the type of a variable/object.
 | ||||
|   var objType = function(obj) { | ||||
|     if (typeof obj === 'undefined')                             return 'undefined'; | ||||
|     else if (typeof obj === 'string' || obj instanceof String)  return 'string'; | ||||
|     else if (typeof obj === 'number' || obj instanceof Number)  return 'number'; | ||||
|     else if (!!obj && obj.constructor === Array)                return 'array'; | ||||
|     else if (obj && obj.nodeType === 1)                         return 'element'; | ||||
|     else if (typeof obj === 'object')                           return 'object'; | ||||
|     else                                                        return 'unknown'; | ||||
|   }; | ||||
| 
 | ||||
|   // Create an HTML element with optional className, innerHTML, and style.
 | ||||
|   var createElement = function(tagName, opt) { | ||||
|     var el = document.createElement(tagName); | ||||
|     if (opt.className)  el.className = opt.className; | ||||
|     if (opt.innerHTML) { | ||||
|       el.innerHTML = opt.innerHTML; | ||||
|       var scripts = el.getElementsByTagName('script'); | ||||
|       for (var i = scripts.length; i-- > 0; null) { | ||||
|         scripts[i].parentNode.removeChild(scripts[i]); | ||||
|       } | ||||
|     } | ||||
|     for (var key in opt.style) { | ||||
|       el.style[key] = opt.style[key]; | ||||
|     } | ||||
|     return el; | ||||
|   }; | ||||
| 
 | ||||
|   // Deep-clone a node and preserve contents/properties.
 | ||||
|   var cloneNode = function(node, javascriptEnabled) { | ||||
|     // Recursively clone the node.
 | ||||
|     var clone = node.nodeType === 3 ? document.createTextNode(node.nodeValue) : node.cloneNode(false); | ||||
|     for (var child = node.firstChild; child; child = child.nextSibling) { | ||||
|       if (javascriptEnabled === true || child.nodeType !== 1 || child.nodeName !== 'SCRIPT') { | ||||
|         clone.appendChild(cloneNode(child, javascriptEnabled)); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     if (node.nodeType === 1) { | ||||
|       // Preserve contents/properties of special nodes.
 | ||||
|       if (node.nodeName === 'CANVAS') { | ||||
|         clone.width = node.width; | ||||
|         clone.height = node.height; | ||||
|         clone.getContext('2d').drawImage(node, 0, 0); | ||||
|       } else if (node.nodeName === 'TEXTAREA' || node.nodeName === 'SELECT') { | ||||
|         clone.value = node.value; | ||||
|       } | ||||
| 
 | ||||
|       // Preserve the node's scroll position when it loads.
 | ||||
|       clone.addEventListener('load', function() { | ||||
|         clone.scrollTop = node.scrollTop; | ||||
|         clone.scrollLeft = node.scrollLeft; | ||||
|       }, true); | ||||
|     } | ||||
| 
 | ||||
|     // Return the cloned node.
 | ||||
|     return clone; | ||||
|   } | ||||
| 
 | ||||
|   // Convert units using the conversion value 'k' from jsPDF.
 | ||||
|   var unitConvert = function(obj, k) { | ||||
|     var newObj = {}; | ||||
|     for (var key in obj) { | ||||
|       newObj[key] = obj[key] * 72 / 96 / k; | ||||
|     } | ||||
|     return newObj; | ||||
|   }; | ||||
| 
 | ||||
|   // Get dimensions of a PDF page, as determined by jsPDF.
 | ||||
|   jsPDF.getPageSize = function(orientation, unit, format) { | ||||
|     // Decode options object
 | ||||
|     if (typeof orientation === 'object') { | ||||
|       var options = orientation; | ||||
|       orientation = options.orientation; | ||||
|       unit = options.unit || unit; | ||||
|       format = options.format || format; | ||||
|     } | ||||
| 
 | ||||
|     // Default options
 | ||||
|     unit        = unit || 'mm'; | ||||
|     format      = format || 'a4'; | ||||
|     orientation = ('' + (orientation || 'P')).toLowerCase(); | ||||
|     var format_as_string = ('' + format).toLowerCase(); | ||||
| 
 | ||||
|     // Size in pt of various paper formats
 | ||||
|     pageFormats = { | ||||
|       'a0'  : [2383.94, 3370.39], 'a1'  : [1683.78, 2383.94], | ||||
|       'a2'  : [1190.55, 1683.78], 'a3'  : [ 841.89, 1190.55], | ||||
|       'a4'  : [ 595.28,  841.89], 'a5'  : [ 419.53,  595.28], | ||||
|       'a6'  : [ 297.64,  419.53], 'a7'  : [ 209.76,  297.64], | ||||
|       'a8'  : [ 147.40,  209.76], 'a9'  : [ 104.88,  147.40], | ||||
|       'a10' : [  73.70,  104.88], 'b0'  : [2834.65, 4008.19], | ||||
|       'b1'  : [2004.09, 2834.65], 'b2'  : [1417.32, 2004.09], | ||||
|       'b3'  : [1000.63, 1417.32], 'b4'  : [ 708.66, 1000.63], | ||||
|       'b5'  : [ 498.90,  708.66], 'b6'  : [ 354.33,  498.90], | ||||
|       'b7'  : [ 249.45,  354.33], 'b8'  : [ 175.75,  249.45], | ||||
|       'b9'  : [ 124.72,  175.75], 'b10' : [  87.87,  124.72], | ||||
|       'c0'  : [2599.37, 3676.54], 'c1'  : [1836.85, 2599.37], | ||||
|       'c2'  : [1298.27, 1836.85], 'c3'  : [ 918.43, 1298.27], | ||||
|       'c4'  : [ 649.13,  918.43], 'c5'  : [ 459.21,  649.13], | ||||
|       'c6'  : [ 323.15,  459.21], 'c7'  : [ 229.61,  323.15], | ||||
|       'c8'  : [ 161.57,  229.61], 'c9'  : [ 113.39,  161.57], | ||||
|       'c10' : [  79.37,  113.39], 'dl'  : [ 311.81,  623.62], | ||||
|       'letter'            : [612,   792], | ||||
|       'government-letter' : [576,   756], | ||||
|       'legal'             : [612,  1008], | ||||
|       'junior-legal'      : [576,   360], | ||||
|       'ledger'            : [1224,  792], | ||||
|       'tabloid'           : [792,  1224], | ||||
|       'credit-card'       : [153,   243] | ||||
|     }; | ||||
| 
 | ||||
|     // Unit conversion
 | ||||
|     switch (unit) { | ||||
|       case 'pt':  k = 1;          break; | ||||
|       case 'mm':  k = 72 / 25.4;  break; | ||||
|       case 'cm':  k = 72 / 2.54;  break; | ||||
|       case 'in':  k = 72;         break; | ||||
|       case 'px':  k = 72 / 96;    break; | ||||
|       case 'pc':  k = 12;         break; | ||||
|       case 'em':  k = 12;         break; | ||||
|       case 'ex':  k = 6;          break; | ||||
|       default: | ||||
|         throw ('Invalid unit: ' + unit); | ||||
|     } | ||||
| 
 | ||||
|     // Dimensions are stored as user units and converted to points on output
 | ||||
|     if (pageFormats.hasOwnProperty(format_as_string)) { | ||||
|       pageHeight = pageFormats[format_as_string][1] / k; | ||||
|       pageWidth = pageFormats[format_as_string][0] / k; | ||||
|     } else { | ||||
|       try { | ||||
|         pageHeight = format[1]; | ||||
|         pageWidth = format[0]; | ||||
|       } catch (err) { | ||||
|         throw new Error('Invalid format: ' + format); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     // Handle page orientation
 | ||||
|     if (orientation === 'p' || orientation === 'portrait') { | ||||
|       orientation = 'p'; | ||||
|       if (pageWidth > pageHeight) { | ||||
|         tmp = pageWidth; | ||||
|         pageWidth = pageHeight; | ||||
|         pageHeight = tmp; | ||||
|       } | ||||
|     } else if (orientation === 'l' || orientation === 'landscape') { | ||||
|       orientation = 'l'; | ||||
|       if (pageHeight > pageWidth) { | ||||
|         tmp = pageWidth; | ||||
|         pageWidth = pageHeight; | ||||
|         pageHeight = tmp; | ||||
|       } | ||||
|     } else { | ||||
|       throw('Invalid orientation: ' + orientation); | ||||
|     } | ||||
| 
 | ||||
|     // Return information (k is the unit conversion ratio from pts)
 | ||||
|     var info = { 'width': pageWidth, 'height': pageHeight, 'unit': unit, 'k': k }; | ||||
|     return info; | ||||
|   }; | ||||
| 
 | ||||
| 
 | ||||
|   // Expose the html2pdf function.
 | ||||
|   return html2pdf; | ||||
| }(html2canvas, jsPDF)); | ||||
							
								
								
									
										15
									
								
								hosting/static/hosting/js/order.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								hosting/static/hosting/js/order.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,15 @@ | |||
| $(document).ready(function() { | ||||
|     $('.btn-pdf').click(function(e) { | ||||
|         e.preventDefault(); | ||||
|         var $target = $($(this).attr('data-target')) || $('body'); | ||||
|         var fileName = $target.attr('id') + '.pdf'; | ||||
|         html2pdf($target[0], { | ||||
|             filename: fileName, | ||||
|         }); | ||||
|     }); | ||||
|     $('.btn-print').click(function(e) { | ||||
|         e.preventDefault(); | ||||
|         console.log('a'); | ||||
|         window.print(); | ||||
|     }); | ||||
| }); | ||||
|  | @ -1,4 +1,85 @@ | |||
| $(document).ready(function () { | ||||
| function VMTerminateStatus($container, url) { | ||||
|     $.ajax({ | ||||
|         url: url, | ||||
|         type: 'GET', | ||||
|         dataType: 'json', | ||||
|         success: function(data) { | ||||
|             VMTerminateSuccess($container, data); | ||||
|         }, | ||||
|         error: function() { | ||||
|             setTimeout(function(){ | ||||
|                 VMTerminateStatus($container, url); | ||||
|             }, 5000); | ||||
|         } | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| function VMTerminateActive($container, altText) { | ||||
|     $container.find('.alert-danger').addClass('hide'); | ||||
|     $container.addClass('processing') | ||||
|         .find('.vm-item-lg').attr('class', '') | ||||
|         .addClass('vm-item-lg vm-color-failed') | ||||
|         .text(altText); | ||||
|     $container.find('.btn').prop('disabled', true); | ||||
|     $('#confirm-cancel').modal('hide'); | ||||
| } | ||||
| 
 | ||||
| function VMTerminateSuccess($container, data) { | ||||
|     $container.addClass('terminate-success') | ||||
|         .find('.vm-item-lg').text(data.text); | ||||
|     $container.find('.btn').remove(); | ||||
|     $('#terminate-success').modal('show'); | ||||
| } | ||||
| 
 | ||||
| function VMTerminateFail($container, data, text) { | ||||
|     $container.addClass('terminate-fail') | ||||
|         .find('.vm-item-lg').text(text); | ||||
|     $container.find('.btn').prop('disabled', false); | ||||
|     $container.find('.alert-danger').text(data.text).removeClass('hide'); | ||||
|     $container.removeClass('processing'); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| $(document).ready(function() { | ||||
|     $('#confirm-cancel').on('click', '.btn-ok', function(e) { | ||||
|         var url = $('#virtual_machine_cancel_form').attr('action'); | ||||
|         var $container = $('#terminate-VM'); | ||||
|         var text = $container.find('.vm-item-lg').text(); | ||||
|         var altText = $container.attr('data-alt'); | ||||
|         VMTerminateActive($container, altText); | ||||
| 
 | ||||
|         $.post(url) | ||||
|             .done(function(data) { | ||||
|                 if (data.status == true) { | ||||
|                     VMTerminateSuccess($container, data); | ||||
|                 } else { | ||||
|                     if ('text' in data) { | ||||
|                         VMTerminateFail($container, data, text); | ||||
|                     } else { | ||||
|                         VMTerminateStatus($container, url); | ||||
|                     } | ||||
|                 } | ||||
|             }) | ||||
|             .fail(function(data) { | ||||
|                 if (data.status==504) { | ||||
|                     VMTerminateStatus($container, url); | ||||
|                 } else { | ||||
|                     VMTerminateFail($container, data, text); | ||||
|                 } | ||||
|             }) | ||||
|     }); | ||||
| 
 | ||||
|     var hash = window.location.hash; | ||||
|     hash && $('ul.nav a[href="' + hash + '"]').tab('show'); | ||||
| 
 | ||||
|     $('.nav-tabs a').click(function(e) { | ||||
|         $(this).tab('show'); | ||||
|         var scrollmem = $('body').scrollTop() || $('html').scrollTop(); | ||||
|         window.location.hash = this.hash; | ||||
|         $('html,body').scrollTop(scrollmem); | ||||
|     }); | ||||
| 
 | ||||
|     $('.modal-text').removeClass('hide'); | ||||
|     var create_vm_form = $('#virtual_machine_create_form'); | ||||
|     create_vm_form.submit(function () { | ||||
|         $('#btn-create-vm').prop('disabled', true); | ||||
|  | @ -22,6 +103,7 @@ $(document).ready(function () { | |||
|                     fa_icon = $('.modal-icon > .fa'); | ||||
|                     fa_icon.attr('class', 'fa fa-times'); | ||||
|                     $('.modal-header > .close').attr('class', 'close'); | ||||
|                     $('.modal-text').addClass('hide'); | ||||
|                     if (typeof(create_vm_error_message) !== 'undefined') { | ||||
|                         $('#createvm-modal-title').text(create_vm_error_message); | ||||
|                     } | ||||
|  | @ -30,19 +112,4 @@ $(document).ready(function () { | |||
|         }); | ||||
|         return false; | ||||
|     }); | ||||
| 
 | ||||
|     $('#confirm-cancel').on('click', '.btn-ok', function (e) { | ||||
|         $('#virtual_machine_cancel_form').trigger('submit'); | ||||
|     }); | ||||
| 
 | ||||
|     var hash = window.location.hash; | ||||
|     hash && $('ul.nav a[href="' + hash + '"]').tab('show'); | ||||
| 
 | ||||
|     $('.nav-tabs a').click(function (e) { | ||||
|         $(this).tab('show'); | ||||
|         var scrollmem = $('body').scrollTop() || $('html').scrollTop(); | ||||
|         window.location.hash = this.hash; | ||||
|         $('html,body').scrollTop(scrollmem); | ||||
|     }); | ||||
| 
 | ||||
| }); | ||||
|  |  | |||
|  | @ -25,6 +25,7 @@ | |||
|     <link href="{% static 'hosting/css/commons.css' %}" rel="stylesheet"> | ||||
|     <link href="{% static 'hosting/css/virtual-machine.css' %}" rel="stylesheet"> | ||||
|     <link href="{% static 'hosting/css/dashboard.css' %}" rel="stylesheet"> | ||||
|     <link href="{% static 'hosting/css/price_calculator.css' %}" rel="stylesheet"> | ||||
|     {% block css_extra %} | ||||
|     {% endblock css_extra %} | ||||
| 
 | ||||
|  | @ -33,9 +34,6 @@ | |||
|     <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css"> | ||||
|     <link href="//fonts.googleapis.com/css?family=Lato:300,400,500,700,300italic,400italic,700italic" rel="stylesheet" type="text/css"> | ||||
|     <link rel="shortcut icon" href="img/favicon.ico" type="image/x-icon" /> | ||||
|     <link rel="stylesheet" href="{% static 'hosting/css/owl.carousel.min.css' %}"> | ||||
|     <link rel="stylesheet" href="{% static 'hosting/css/owl.theme.default.min.css' %}"> | ||||
| 
 | ||||
| 
 | ||||
|     <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries --> | ||||
|     <!-- WARNING: Respond.js doesn't work if you view the page via file:// --> | ||||
|  | @ -76,8 +74,8 @@ | |||
|     {% endif %} | ||||
|     <!-- jQuery --> | ||||
|     <script src="{% static 'hosting/js/jquery.js' %}"></script> | ||||
|     <script type="text/javascript" src="//cdn.jsdelivr.net/jquery.validation/1.13.1/jquery.validate.min.js"></script> | ||||
|     <script src="{% static 'hosting/js/vendor/owl.carousel.min.js'%}"></script> | ||||
|     <script src="//cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.16.0/jquery.validate.min.js"></script> | ||||
|     <script src="//cdnjs.cloudflare.com/ajax/libs/1000hz-bootstrap-validator/0.11.9/validator.min.js"></script> | ||||
|     <!-- Copy Clipboard --> | ||||
|     <script src="//cdnjs.cloudflare.com/ajax/libs/clipboard.js/1.5.10/clipboard.min.js"></script> | ||||
| 
 | ||||
|  | @ -94,6 +92,13 @@ | |||
|     <!-- Init JavaScript --> | ||||
|     <script src="{% static 'hosting/js/initial.js' %}"></script> | ||||
| 
 | ||||
|     {% block js_extra %} | ||||
|         {% comment %} | ||||
|             this block is above some files, because on stripe error scripts below the stripe | ||||
|             script are not properly executed. | ||||
|         {% endcomment %} | ||||
|     {% endblock js_extra %} | ||||
| 
 | ||||
|     <script src="https://js.stripe.com/v3/"></script> | ||||
|     <script src="https://js.stripe.com/v2/"></script> | ||||
|     <!-- Stripe Lib --> | ||||
|  | @ -108,8 +113,6 @@ | |||
|     <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js"></script> | ||||
|     <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment-with-locales.js"></script> | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| </body> | ||||
| 
 | ||||
| </html> | ||||
|  |  | |||
							
								
								
									
										87
									
								
								hosting/templates/hosting/calculator_form.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								hosting/templates/hosting/calculator_form.html
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,87 @@ | |||
| {% load staticfiles i18n%} | ||||
| <form id="order_form" method="POST" action="" data-toggle="validator" role="form"> | ||||
|     {% csrf_token %} | ||||
|     <div class="price"> | ||||
|         <span id="total">15</span> | ||||
|         <span>CHF/{% trans "Month" %}</span> | ||||
|         <div class="price-text"> | ||||
|             <span>{% trans "VAT included" %}</span> | ||||
|         </div> | ||||
|     </div> | ||||
|     <div class="descriptions"> | ||||
|         <div class="form-group"> | ||||
|             <div class="description input"> | ||||
|                 <i class="fa fa-minus left" data-minus="cpu" aria-hidden="true"></i> | ||||
|                 <input class="input-price select-number" type="number" min="1" max="48" id="coreValue" name="cpu" data-error="{% trans 'Please enter a value in range 1 - 48.' %}" required> | ||||
|                 <span> Core</span> | ||||
|                 <i class="fa fa-plus right" data-plus="cpu" aria-hidden="true"></i> | ||||
|             </div> | ||||
|             <div class="help-block with-errors"> | ||||
|                 {% for message in messages %} | ||||
|                     {% if 'cores' in message.tags %} | ||||
|                      <ul class="list-unstyled"><li> | ||||
|                         {{ message|safe }} | ||||
|                     </li></ul> | ||||
|                     {% endif %} | ||||
|                 {% endfor %} | ||||
|             </div> | ||||
|         </div> | ||||
|         <div class="form-group"> | ||||
|             <div class="description input"> | ||||
|                 <i class="fa fa-minus left" data-minus="ram" aria-hidden="true"></i> | ||||
|                 <input id="ramValue" class="input-price select-number" type="number" min="2" max="200" name="ram" | ||||
|                        data-error="{% trans 'Please enter a value in range 2 - 200.' %}" required> | ||||
|                 <span> GB RAM</span> | ||||
|                 <i class="fa fa-plus right" data-plus="ram" aria-hidden="true"></i> | ||||
|             </div> | ||||
|             <div class="help-block with-errors"> | ||||
|                 {% for message in messages %} | ||||
|                     {% if 'memory' in message.tags %} | ||||
|                      <ul class="list-unstyled"><li> | ||||
|                         {{ message|safe }} | ||||
|                     </li></ul> | ||||
|                     {% endif %} | ||||
|                 {% endfor %} | ||||
|             </div> | ||||
|         </div> | ||||
|         <div class="form-group"> | ||||
|             <div class="description input"> | ||||
|                 <i class="fa fa-minus left" data-minus="storage" aria-hidden="true"></i> | ||||
|                 <input id="storageValue" class="input-price select-number" type="number" min="10" max="2000" step="10" | ||||
|                        name="storage" data-error="{% trans 'Please enter a value in range 10 - 2000.' %}" required> | ||||
|                 <span>{% trans "GB Storage (SSD)" %}</span> | ||||
|                 <i class="fa fa-plus right" data-plus="storage" aria-hidden="true"></i> | ||||
|             </div> | ||||
|             <div class="help-block with-errors"> | ||||
|                 {% for message in messages %} | ||||
|                     {% if 'storage' in message.tags %} | ||||
|                      <ul class="list-unstyled"><li> | ||||
|                         {{ message|safe }} | ||||
|                     </li></ul> | ||||
|                     {% endif %} | ||||
|                 {% endfor %} | ||||
|             </div> | ||||
|         </div> | ||||
|         <div class="form-group"> | ||||
|             <div class="description select-configuration input justify-center"> | ||||
|                 <label for="config">OS</label> | ||||
|                 <select name="config" id=""> | ||||
|                     {% for template in templates %} | ||||
|                     <option value="{{template.opennebula_vm_template_id}}">{{template.name}}</option> | ||||
|                     {% endfor %} | ||||
|                 </select> | ||||
|             </div> | ||||
|             <div class="help-block with-errors"> | ||||
|                 {% for message in messages %} | ||||
|                     {% if 'cores' in message.tags %} | ||||
|                      <ul class="list-unstyled"><li> | ||||
|                         {{ message|safe }} | ||||
|                     </li></ul> | ||||
|                     {% endif %} | ||||
|                 {% endfor %} | ||||
|             </div> | ||||
|         </div> | ||||
|         <input type="hidden" name="total"> | ||||
|     </div> | ||||
|     <input type="submit" class="btn btn-primary disabled" value="{% trans 'Continue' %}"></input> | ||||
| </form> | ||||
|  | @ -1,70 +1,30 @@ | |||
| {% extends "hosting/base_short.html" %} | ||||
| {% load staticfiles bootstrap3 i18n %} | ||||
| {% block content %}  | ||||
| <div> | ||||
|     <div class="dashboard-container" > | ||||
|         <div class="row"> | ||||
| 
 | ||||
|                 <div class="col-md-12"> | ||||
|                     <br/> | ||||
|                     {% if messages %} | ||||
|                         <div class="alert alert-warning"> | ||||
|                             {% for message in messages %} | ||||
| {% block content %} | ||||
| <div class="dashboard-container create-vm-container"> | ||||
|     <div class="row"> | ||||
|         <div class="col-sm-5"> | ||||
|           <div class="dashboard-container-head"> | ||||
|                 <h3 class="dashboard-title-thin"><img src="{% static 'hosting/img/plusVM.svg' %}" class="un-icon" style="margin-top: -18px;width: 42px;height: 42px;"> {% trans "Create VM" %}</h3> | ||||
|                 {% if messages %} | ||||
|                     <div class="alert alert-warning"> | ||||
|                         {% for message in messages %} | ||||
|                             <span>{{ message }}</span> | ||||
|                             {% endfor %} | ||||
|                         </div> | ||||
|                     {% endif %} | ||||
|                 </div> | ||||
|                 {% if not error %} | ||||
|                 <div class="dashboard-title"> | ||||
|                     <h3>{% trans "New Virtual Machine"%} </h3> | ||||
|                     <hr/> | ||||
|                 </div> | ||||
| 
 | ||||
|                 <form method="POST" action=""> | ||||
|                     {% csrf_token %} | ||||
|              | ||||
|                     <div class="step-title"> | ||||
|                         <h4>{% trans "Step 1. Select VM Template:" %} </h4> | ||||
|                         {% endfor %} | ||||
|                     </div> | ||||
|                     <div class="parent-container"> | ||||
|                         <div class="container-os owl-carousel owl-theme" id="containerOs"> | ||||
|                              | ||||
|                             {% for template in templates %} | ||||
|                                 <div class="os-circle" data-id="{{template.id}}"> | ||||
|                                     <span class="text" >{{template.name}}</span> | ||||
|                                 </div> | ||||
|                             {% endfor %} | ||||
|                         </div> | ||||
|                         <input type="hidden" name="vm_template_id"> | ||||
|                     </div> | ||||
|                      <div class="step-title"> | ||||
|                         <h4>{% trans "Step2. Select VM Configuration" %}</h4> | ||||
|                     </div> | ||||
|                     <div class="parent-container"> | ||||
|                         <div class="container-os config owl-carousel owl-theme"> | ||||
|                             | ||||
|                             {% for config in configuration_options %} | ||||
|                                 <div class="config-box" data-id="{{config.id}}" data-price="{{config.price|floatformat}}"> | ||||
|                                     <span>CORE: {{config.cpu|floatformat}}</span> | ||||
|                                     <span>RAM: {{config.memory|floatformat}} GB</span> | ||||
|                                     <span>SSD: {{config.disk_size|floatformat}} GB</span> | ||||
|                                 </div> | ||||
|                             {% endfor %} | ||||
|                         </div> | ||||
|                          <input type="hidden" name="configuration"> | ||||
|                     </div> | ||||
|                     <div class="container-button"> | ||||
|                           <div class="price"> | ||||
|                             <span class="label-price">{% trans "Price " %}<span id="priceValue">0</span>{% trans "CHF/Month" %}</span> | ||||
|                           </div> | ||||
|                          <button class="btn btn-success" >{% trans "Start VM"%} </button>    | ||||
|                     </div> | ||||
|                 </form> | ||||
|                 {% endif %} | ||||
|           </div> | ||||
|         </div> | ||||
|         <div class="col-sm-6"> | ||||
|             <div class="price-calc-section no-padding"> | ||||
|                 <div class="landing card"> | ||||
|                     <div class="caption"> | ||||
|                         {% include "hosting/calculator_form.html" %} | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| 
 | ||||
| </div> | ||||
| 
 | ||||
| {%endblock%} | ||||
| {%endblock%} | ||||
|  | @ -4,206 +4,189 @@ | |||
| {% load custom_tags %} | ||||
| 
 | ||||
| {% block content %} | ||||
| 
 | ||||
| <div class="order-detail-container"> | ||||
| <div id="order-detail{{order.pk}}" class="order-detail-container"> | ||||
|     {% if messages %} | ||||
|     <div class="row"> | ||||
|         <div class="col-xs-12 col-md-8 col-md-offset-2"> | ||||
|             <br/> | ||||
|             <div class="alert alert-warning"> | ||||
|                 {% for message in messages %} | ||||
|         <div class="alert alert-warning"> | ||||
|             {% for message in messages %} | ||||
|                 <span>{{ message }}</span> | ||||
|                 {% endfor %} | ||||
|             </div> | ||||
|             {% endfor %} | ||||
|         </div> | ||||
|     </div> | ||||
|     {% endif %} | ||||
|     {% if not error %} | ||||
|     <div class="row"> | ||||
|         <div class="col-xs-12 col-md-8 col-md-offset-2"> | ||||
|             <div class="invoice-title"> | ||||
|                 <h2>{{page_header_text}}</h2> | ||||
|                 <h3 class="pull-right"> | ||||
|         <div class="dashboard-container-head"> | ||||
|             <h1 class="dashboard-title-thin"> | ||||
|                 <img src="{% static 'hosting/img/billing.svg' %}" class="un-icon">{% blocktrans with page_header_text=page_header_text|default:"Invoice" %}{{page_header_text}}{% endblocktrans %} | ||||
|             </h1> | ||||
|             <div class="dashboard-container-options"> | ||||
|                 <button type="button" class="btn-plain btn-pdf" data-target="#order-detail{{order.pk}}"><img src="{% static 'hosting/img/icon-pdf.svg' %}" class="svg-img"></button> | ||||
|                 <button type="button" class="btn-plain btn-print"><img src="{% static 'hosting/img/icon-print.svg' %}" class="svg-img"></button> | ||||
|             </div> | ||||
|         </div> | ||||
|         <div class="order-details"> | ||||
|             {% if order %} | ||||
|                 <p> | ||||
|                     <strong>{% trans "Order #" %} {{order.id}}</strong> | ||||
|                 </p> | ||||
|             {% endif %} | ||||
|             <p> | ||||
|                 <strong>{% trans "Invoice Date" %}:</strong> | ||||
|                 <span id="order-created_at"> | ||||
|                     {% if order %} | ||||
|                     {% trans "Order #"%} {{order.id}} | ||||
|                         {{order.created_at|date:'Y-m-d H:i'}} | ||||
|                     {% else %} | ||||
|                         {% now "Y-m-d H:i" %} | ||||
|                     {% endif %} | ||||
|                 </h3> | ||||
|                 </span> | ||||
|             </p> | ||||
|             {% if order %} | ||||
|                 <p> | ||||
|                     <strong>{% trans "Status" %}: </strong> | ||||
|                     <strong> | ||||
|                         {% if order.status == 'Approved' %} | ||||
|                             <span class="vm-color-online">{% trans "Approved" %}</span> | ||||
|                         {% else %} | ||||
|                             <span class="vm-status-failed">{% trans "Declined" %}</span> | ||||
|                         {% endif %} | ||||
|                     </strong> | ||||
|                 </p> | ||||
|             {% endif %} | ||||
|             <hr> | ||||
|             <div> | ||||
|                 <address> | ||||
|                     <h4>{% trans "Billed to" %}:</h4> | ||||
|                     <p> | ||||
|                         {% if order %} | ||||
|                             {{user.name}}<br> | ||||
|                             {{order.billing_address.street_address}}, {{order.billing_address.postal_code}}<br> | ||||
|                             {{order.billing_address.city}}, {{order.billing_address.country}} | ||||
|                         {% else %} | ||||
|                             {% with request.session.billing_address_data as billing_address %} | ||||
|                                 {{billing_address.cardholder_name}}<br> | ||||
|                                 {{billing_address.street_address}}, {{billing_address.postal_code}}<br> | ||||
|                                 {{billing_address.city}}, {{billing_address.country}} | ||||
|                             {% endwith %} | ||||
|                         {% endif %} | ||||
|                     </p> | ||||
|                 </address> | ||||
|             </div> | ||||
|             <hr> | ||||
|             <div class="row"> | ||||
|                 <div class="col-xs-12 col-md-6 pull-right order-confirm-date"> | ||||
|                     <address> | ||||
|                         <strong>{% trans "Date"%}:</strong><br> | ||||
|                         <span id="order-created_at"> | ||||
|                                 {% if order %} | ||||
|                                     {{order.created_at|date:'Y-m-d H:i'}} | ||||
|                                 {% else %} | ||||
|                                     {% now "Y-m-d H:i" %} | ||||
|                                 {% endif %} | ||||
|                             </span><br><br> | ||||
|                         {% if order %} | ||||
|                         <strong>{% trans "Status:"%}</strong><br> | ||||
|                         {% if order.status == 'Approved' %} | ||||
|                         <strong class="text-success"> | ||||
|                             {% trans "Approved" %} | ||||
|                         </strong> | ||||
|                         {% else %} | ||||
|                         <strong class="text-danger"> | ||||
|                             {% trans "Declined" %} | ||||
|                         </strong> | ||||
|                         {% endif %} | ||||
|                         <br><br> | ||||
|                         {% endif %} | ||||
|                     </address> | ||||
| 
 | ||||
|                 </div> | ||||
|                 <div class="col-xs-12 col-md-6"> | ||||
|                     <address> | ||||
|                         <h3><b>{% trans "Billed To:"%}</b></h3> | ||||
|                         {% if order %} | ||||
|                         {{user.name}}<br> | ||||
|                         {{order.billing_address.street_address}},{{order.billing_address.postal_code}}<br> | ||||
|                         {{order.billing_address.city}}, | ||||
|                         {{order.billing_address.country}}. | ||||
|                         {% else %} | ||||
|                         {% with request.session.billing_address_data as billing_address %} | ||||
|                         {{billing_address|get_value_from_dict:'cardholder_name'}}<br> | ||||
|                         {{billing_address|get_value_from_dict:'street_address'}}, | ||||
|                         {{billing_address|get_value_from_dict:'postal_code'}}<br> | ||||
|                         {{billing_address|get_value_from_dict:'city'}}, | ||||
|                         {{billing_address|get_value_from_dict:'country'}}. | ||||
|                         {% endwith %} | ||||
|                         {% endif %} | ||||
|                     </address> | ||||
|                 </div> | ||||
| 
 | ||||
|             </div> | ||||
|             <div class="row"> | ||||
|                 <div class="col-xs-6"> | ||||
|                     <address> | ||||
|                         <strong>{% trans "Payment Method:"%}</strong><br> | ||||
|                         {% if order %} | ||||
|             <div> | ||||
|                 <h4>{% trans "Payment method" %}:</h4> | ||||
|                 <p> | ||||
|                     {% if order %} | ||||
|                         {{order.cc_brand}} {% trans "ending in" %} **** | ||||
|                         {{order.last4}}<br> | ||||
|                         {{user.email}} | ||||
|                         {% else %} | ||||
|                         {{cc_brand}} {% trans "ending in" %} **** | ||||
|                     {% else %} | ||||
|                         {{cc_brand|default:'Card'}} {% trans "ending in" %} **** | ||||
|                         {{cc_last4}}<br> | ||||
|                         {{request.session.user.email}} | ||||
|                         {% if request.user.is_authenticated %} | ||||
|                             {{request.user.email}} | ||||
|                         {% else %} | ||||
|                             {{request.session.user.email}} | ||||
|                         {% endif %} | ||||
|                     </address> | ||||
|                     {% endif %} | ||||
|                 </p> | ||||
|             </div> | ||||
|             <hr> | ||||
|             <div> | ||||
|                 <h4>{% trans "Order summary" %}</h4> | ||||
|                 <p> | ||||
|                     <strong>{% trans "Product" %}:</strong> {{vm.name}} | ||||
|                 </p> | ||||
|                 <div class="row"> | ||||
|                     <div class="col-sm-6"> | ||||
|                         {% comment %} | ||||
|                         <p> | ||||
|                             <span>{% trans "Period" %}</span> | ||||
|                             <span class="pull-right">{{}}</span> | ||||
|                         </p> | ||||
|                         {% endcomment %} | ||||
|                         <p> | ||||
|                             <span>{% trans "Cores" %}</span> | ||||
|                             {% if vm.cores %} | ||||
|                                 <span class="pull-right">{{vm.cores|floatformat}}</span> | ||||
|                             {% else %} | ||||
|                                 <span class="pull-right">{{vm.cpu|floatformat}}</span> | ||||
|                             {% endif %} | ||||
|                         </p> | ||||
|                         <p> | ||||
|                             <span>{% trans "Memory" %}</span> | ||||
|                             <span class="pull-right">{{vm.memory}} GB</span> | ||||
|                         </p> | ||||
|                         <p> | ||||
|                             <span>{% trans "Disk space" %}</span> | ||||
|                             <span class="pull-right">{{vm.disk_size}} GB</span> | ||||
|                         </p> | ||||
|                         <p> | ||||
|                             <span>{% trans "Total" %}</span> | ||||
|                             <span class="pull-right">{{vm.price}} CHF</span> | ||||
|                         </p> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| 
 | ||||
|     <div class="row"> | ||||
|         <div class="col-md-8 col-md-offset-2"> | ||||
|             <h3><b>{% trans "Order summary"%}</b></h3> | ||||
|             <hr> | ||||
|             <div class="content"> | ||||
|                 {% if request.session.specs %} | ||||
|                 {% with request.session.specs as vm %} | ||||
|                 <p><b>{% trans "Cores"%}</b> | ||||
|                     <span class="pull-right">{{vm.cpu}}</span> | ||||
|                 </p> | ||||
|                 <hr> | ||||
|                 <p><b>{% trans "Memory"%}</b> | ||||
|                     <span class="pull-right">{{vm.memory}} GB</span> | ||||
|                 </p> | ||||
|                 <hr> | ||||
|                 <p><b>{% trans "Disk space"%}</b> | ||||
|                     <span class="pull-right">{{vm.disk_size}} GB</span> | ||||
|                 </p> | ||||
|                 <hr> | ||||
|                 <p><b>{% trans "Configuration"%}</b> | ||||
|                     <span class="pull-right">{{request.session.template.name}}</span> | ||||
|                 </p> | ||||
|                 <hr> | ||||
|                 <h4>{% trans "Total"%} | ||||
|                     <p class="pull-right"> | ||||
|                         <b>{{vm.price}} CHF</b> | ||||
|                         <span class="dcl-price-month"> /{% trans "Month" %} | ||||
|                                 </span> | ||||
|                     </p> | ||||
|                 </h4> | ||||
|                 {% endwith %} | ||||
|                 {% else %} | ||||
|                 <p><b>{% trans "Cores"%}</b> | ||||
|                     <span class="pull-right">{{vm.cores}}</span> | ||||
|                 </p> | ||||
|                 <hr> | ||||
|                 <p><b>{% trans "Memory"%}</b> | ||||
|                     <span class="pull-right">{{vm.memory}} GB</span> | ||||
|                 </p> | ||||
|                 <hr> | ||||
|                 <p><b>{% trans "Disk space"%}</b> | ||||
|                     <span class="pull-right">{{vm.disk_size}} GB</span> | ||||
|                 </p> | ||||
|                 <hr> | ||||
|                 <h4>{% trans "Total"%}<p class="pull-right"><b>{{vm.price}} | ||||
|                     CHF</b><span | ||||
|                         class="dcl-price-month"> /{% trans "Month" %}</span> | ||||
|                 </p></h4> | ||||
|                 {% endif %} | ||||
|             </div> | ||||
|             <br/> | ||||
|             {% if not order %} | ||||
|                 <hr> | ||||
|             {% endif %} | ||||
|         </div> | ||||
|         {% if not order %} | ||||
|             <form method="post" id="virtual_machine_create_form"> | ||||
|                 {% csrf_token %} | ||||
|                 <div class="row"> | ||||
|                     <div class="col-sm-8"> | ||||
|                         <p class="dcl-place-order-text">{% blocktrans with vm_price=request.session.specs.price %}By clicking "Place order" this plan will charge your credit card account with the fee of {{ vm_price }}CHF/month{% endblocktrans %}.</p> | ||||
|                         <div class="dcl-place-order-text">{% blocktrans with vm_price=request.session.specs.price %}By clicking "Place order" this plan will charge your credit card account with the fee of {{ vm_price }}CHF/month{% endblocktrans %}.</div> | ||||
|                     </div> | ||||
|                     <div class="col-sm-4 content"> | ||||
|                         <button class="btn btn-info pull-right" | ||||
|                                 id="btn-create-vm" | ||||
|                                 data-href="{% url 'hosting:order-confirmation' %}" | ||||
|                                 data-toggle="modal" | ||||
|                                 data-target="#createvm-modal"> | ||||
|                             {% trans "Place order"%} | ||||
|                     <div class="col-sm-4 order-confirm-btn text-right"> | ||||
|                         <button class="btn choice-btn" id="btn-create-vm" data-href="{% url 'hosting:order-confirmation' %}" data-toggle="modal" data-target="#createvm-modal"> | ||||
|                             {% trans "Place order" %} | ||||
|                         </button> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </form> | ||||
|             {% endif %} | ||||
|         </div> | ||||
|     </div> | ||||
|         {% endif %} | ||||
|     {% endif %} | ||||
| </div> | ||||
| <!-- Create VM Modal --> | ||||
| <div class="modal fade" id="createvm-modal" tabindex="-1" role="dialog" | ||||
|      aria-hidden="true" data-backdrop="static" data-keyboard="false"> | ||||
|     <div class="modal-dialog"> | ||||
|         <div class="modal-content"> | ||||
|             <div class="modal-header"> | ||||
|                 <button type="button" class="close hidden" data-dismiss="modal" | ||||
|                         aria-label="create-vm-close"> | ||||
|                     <span aria-hidden="true">×</span> | ||||
|                 </button> | ||||
|             </div> | ||||
|             <div class="modal-body"> | ||||
|                 <div class="modal-icon"> | ||||
|                     <i class="fa fa-cog fa-spin fa-3x fa-fw"></i> | ||||
|                     <span class="sr-only">{% trans "Processing..." %}</span> | ||||
| 
 | ||||
| {% if order %} | ||||
|     <div class="text-center" style="margin-bottom: 50px;"> | ||||
|         <a class="btn btn-vm-back" href="{% url 'hosting:orders' %}">{% trans "BACK TO LIST" %}</a> | ||||
|     </div> | ||||
| {% endif %} | ||||
| 
 | ||||
| {% if not order %} | ||||
|     <!-- Create VM Modal --> | ||||
|     <div class="modal fade" id="createvm-modal" tabindex="-1" role="dialog" | ||||
|          aria-hidden="true" data-backdrop="static" data-keyboard="false"> | ||||
|         <div class="modal-dialog"> | ||||
|             <div class="modal-content"> | ||||
|                 <div class="modal-header"> | ||||
|                     <button type="button" class="close hidden" data-dismiss="modal" | ||||
|                             aria-label="create-vm-close"> | ||||
|                         <span aria-hidden="true">×</span> | ||||
|                     </button> | ||||
|                 </div> | ||||
|                 <h4 class="modal-title" id="createvm-modal-title"> | ||||
|                 </h4> | ||||
|                 <div class="modal-text" id="createvm-modal-body"> | ||||
|                     {% trans "Hold tight, we are processing your request" %} | ||||
|                 </div> | ||||
|                 <div class="modal-footer"> | ||||
|                 <div class="modal-body"> | ||||
|                     <div class="modal-icon"> | ||||
|                         <i class="fa fa-cog fa-spin fa-3x fa-fw"></i> | ||||
|                         <span class="sr-only">{% trans "Processing..." %}</span> | ||||
|                     </div> | ||||
|                     <h4 class="modal-title" id="createvm-modal-title"> | ||||
|                     </h4> | ||||
|                     <div class="modal-text" id="createvm-modal-body"> | ||||
|                         {% trans "Hold tight, we are processing your request" %} | ||||
|                     </div> | ||||
|                     <div class="modal-footer"> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| </div> | ||||
| <!-- / Create VM Modal --> | ||||
| 
 | ||||
|     <!-- / Create VM Modal --> | ||||
| {% endif %} | ||||
| 
 | ||||
| <script type="text/javascript"> | ||||
|     {% trans "Some problem encountered. Please try again later." as err_msg %} | ||||
|     var create_vm_error_message = '{{err_msg|safe}}.'; | ||||
|     var create_vm_error_message = '{{err_msg|safe}}'; | ||||
| 
 | ||||
|     window.onload = function () { | ||||
|         var locale_date = moment.utc(document.getElementById("order-created_at").textContent, 'YYYY-MM-DD HH:mm').toDate(); | ||||
|  | @ -211,7 +194,12 @@ | |||
|         document.getElementById('order-created_at').innerHTML = locale_date; | ||||
| 
 | ||||
|     }; | ||||
| 
 | ||||
| </script> | ||||
| {%endblock%} | ||||
| 
 | ||||
| {% block js_extra %} | ||||
|     <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.3.5/jspdf.min.js"></script> | ||||
|     <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.min.js"></script> | ||||
|     <script src="{% static 'hosting/js/html2pdf.js' %}"></script> | ||||
|     <script src="{% static 'hosting/js/order.js' %}"></script> | ||||
| {% endblock js_extra %} | ||||
|  | @ -53,19 +53,26 @@ | |||
| 				<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 id="terminate-VM" data-alt="{% trans 'Terminating' %}"> | ||||
| 							{% 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> | ||||
| 							{% else %} | ||||
| 								<div class="vm-item-lg"></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-toggle="modal" data-target="#confirm-cancel" class="btn btn-vm-term">{% trans "Terminate VM" %}</button> | ||||
| 								<div class="alert alert-danger hide"> | ||||
| 									{% trans "Sorry, there was an unexpected error. Kindly retry." %} | ||||
| 								</div> | ||||
| 							{% endif %} | ||||
| 						</div> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 		</div> | ||||
|  | @ -89,24 +96,41 @@ | |||
| 	</div> | ||||
| 	<!-- Cancel Modal --> | ||||
| 	<div class="modal fade" id="confirm-cancel" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> | ||||
| 	    <div class="modal-dialog"> | ||||
| 	        <div class="modal-content"> | ||||
|     <div class="modal-dialog"> | ||||
|       <div class="modal-content"> | ||||
| 				<div class="modal-header"> | ||||
| 					<button type="button" class="close" data-dismiss="modal" aria-label="Confirm"><span aria-hidden="true">×</span></button> | ||||
| 				</div> | ||||
| 	            <div class="modal-body"> | ||||
|       	<div class="modal-body"> | ||||
| 					<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> | ||||
| 					<div class="modal-text"> | ||||
| 						<p>{% trans "Do you want to cancel your Virtual Machine" %} ?</p> | ||||
| 						<p><strong>{{virtual_machine.name}}</strong></p> | ||||
| 					</div> | ||||
| 		            <div class="modal-footer"> | ||||
| 		                <a class="btn btn-danger btn-ok btn-wide">{% trans "OK" %}</a> | ||||
| 		            </div> | ||||
| 	            </div> | ||||
| 	        </div> | ||||
| 	    </div> | ||||
|           <div class="modal-footer"> | ||||
|               <a class="btn btn-danger btn-ok btn-wide">{% trans "OK" %}</a> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
| 	</div> | ||||
| 	<!-- / Cancel Modal --> | ||||
| 	<!-- Success Modal --> | ||||
| 	<div class="modal fade" id="terminate-success" tabindex="-1" role="dialog" aria-hidden="true"> | ||||
|     <div class="modal-dialog"> | ||||
|       <div class="modal-content"> | ||||
| 				<div class="modal-header"> | ||||
| 				</div> | ||||
|         <div class="modal-body"> | ||||
| 					<div class="modal-icon"><i class="fa fa-check" aria-hidden="true"></i></div> | ||||
| 					<h4 class="modal-title" id="ModalLabel">{% blocktrans with machine_name=virtual_machine.name %}Your Virtual Machine <strong>{{machine_name}}</strong> is successfully terminated!{% endblocktrans %}</h4> | ||||
|           <div class="modal-footer"> | ||||
|             <a href="{% url 'hosting:virtual_machines' %}"	class="btn btn-success btn-wide">{% trans "OK" %}</a> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
| 	</div> | ||||
| 	<!-- / Cancel Modal --> | ||||
| {%endblock%} | ||||
|  |  | |||
|  | @ -46,13 +46,15 @@ | |||
|                         <td data-header="IPv6">{{vm.ipv6}}</td> | ||||
|                     {% endif %} | ||||
|                     <td data-header="{% trans 'Status' %}"> | ||||
|                         {% if vm.state == 'ACTIVE' %} | ||||
|                             <span class="vm-status-active"><strong>{{vm.state|title}}</strong></span> | ||||
|                         {% elif  vm.state == 'FAILED' %} | ||||
|                             <span class="vm-status-failed"><strong>{{vm.state|title}}</strong></span> | ||||
|                         {% else %} | ||||
|                             <span class="vm-status"><strong>{{vm.state|title}}</strong></span> | ||||
|                         {% endif %} | ||||
|                         <strong> | ||||
|                             {% if vm.state == 'ACTIVE' %} | ||||
|                                 <span class="vm-status-active">{{vm.state|title}}</span> | ||||
|                             {% elif  vm.state == 'FAILED' %} | ||||
|                                 <span class="vm-status-failed">{{vm.state|title}}</span> | ||||
|                             {% else %} | ||||
|                                 <span class="vm-status">{{vm.state|title}}</span> | ||||
|                             {% endif %} | ||||
|                         </strong> | ||||
|                     </td> | ||||
|                     <td class="text-right last-td"> | ||||
|                         <a class="btn btn-vm-detail" href="{% url 'hosting:virtual_machines' vm.vm_id %}">{% trans "View Detail" %}</a> | ||||
|  |  | |||
							
								
								
									
										270
									
								
								hosting/views.py
									
										
									
									
									
								
							
							
						
						
									
										270
									
								
								hosting/views.py
									
										
									
									
									
								
							|  | @ -1,23 +1,27 @@ | |||
| import json | ||||
| import logging | ||||
| import uuid | ||||
| from time import sleep | ||||
| 
 | ||||
| from django import forms | ||||
| from django.conf import settings | ||||
| from django.contrib import messages | ||||
| from django.contrib.auth.mixins import LoginRequiredMixin | ||||
| from django.contrib.auth.tokens import default_token_generator | ||||
| from django.core.exceptions import ValidationError | ||||
| from django.core.files.base import ContentFile | ||||
| from django.core.urlresolvers import reverse_lazy, reverse | ||||
| from django.http import Http404 | ||||
| from django.http import HttpResponseRedirect, HttpResponse | ||||
| from django.shortcuts import redirect | ||||
| from django.shortcuts import render | ||||
| 
 | ||||
| from django.http import Http404, HttpResponseRedirect, HttpResponse | ||||
| from django.shortcuts import redirect, render | ||||
| from django.utils.http import urlsafe_base64_decode | ||||
| from django.utils.safestring import mark_safe | ||||
| from django.utils.translation import get_language, ugettext_lazy as _ | ||||
| from django.views.generic import View, CreateView, FormView, ListView, \ | ||||
|     DetailView, \ | ||||
|     DeleteView, TemplateView, UpdateView | ||||
| from django.utils.translation import ugettext | ||||
| from django.views.generic import ( | ||||
|     View, CreateView, FormView, ListView, DetailView, DeleteView, | ||||
|     TemplateView, UpdateView | ||||
| ) | ||||
| from guardian.mixins import PermissionRequiredMixin | ||||
| from oca.pool import WrongIdError | ||||
| from stored_messages.api import mark_read | ||||
|  | @ -33,12 +37,15 @@ from utils.forms import BillingAddressForm, PasswordResetRequestForm, \ | |||
|     UserBillingAddressForm | ||||
| from utils.mailer import BaseEmail | ||||
| from utils.stripe_utils import StripeUtils | ||||
| from utils.views import PasswordResetViewMixin, PasswordResetConfirmViewMixin, \ | ||||
|     LoginViewMixin | ||||
| from utils.views import ( | ||||
|     PasswordResetViewMixin, PasswordResetConfirmViewMixin, LoginViewMixin | ||||
| ) | ||||
| from .forms import HostingUserSignupForm, HostingUserLoginForm, \ | ||||
|     UserHostingKeyForm, generate_ssh_key_name | ||||
| from .mixins import ProcessVMSelectionMixin | ||||
| from .models import HostingOrder, HostingBill, HostingPlan, UserHostingKey | ||||
| from datacenterlight.models import VMTemplate | ||||
| 
 | ||||
| 
 | ||||
| logger = logging.getLogger(__name__) | ||||
| 
 | ||||
|  | @ -637,7 +644,7 @@ class OrdersHostingDetailView(LoginRequiredMixin, | |||
|     model = HostingOrder | ||||
| 
 | ||||
|     def get_object(self): | ||||
|         return HostingOrder.objects.filter( | ||||
|         return HostingOrder.objects.get( | ||||
|             pk=self.kwargs.get('pk')) if self.kwargs.get('pk') else None | ||||
| 
 | ||||
|     def get_context_data(self, **kwargs): | ||||
|  | @ -653,46 +660,76 @@ class OrdersHostingDetailView(LoginRequiredMixin, | |||
|         stripe_customer_id = self.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, | ||||
|                                                      self.request.session.get( | ||||
|                                                          'token')) | ||||
|         if not card_details.get('response_object'): | ||||
|             msg = card_details.get('error') | ||||
|             messages.add_message(self.request, messages.ERROR, msg, | ||||
|                                  extra_tags='failed_payment') | ||||
|             return HttpResponseRedirect( | ||||
|                 reverse('hosting:payment') + '#payment_error') | ||||
|         if customer: | ||||
|             card_details = stripe_utils.get_card_details( | ||||
|                 customer.stripe_id, | ||||
|                 self.request.session.get('token') | ||||
|             ) | ||||
|         else: | ||||
|             card_details = {} | ||||
| 
 | ||||
|         if self.request.GET.get('page', '') == 'payment': | ||||
|         if self.request.GET.get('page') == 'payment': | ||||
|             context['page_header_text'] = _('Confirm Order') | ||||
|         else: | ||||
|             context['page_header_text'] = _('Invoice') | ||||
| 
 | ||||
|         if obj is not None: | ||||
|             # invoice for previous order | ||||
|             try: | ||||
|                 manager = OpenNebulaManager(email=owner.email, | ||||
|                                             password=owner.password) | ||||
|                 manager = OpenNebulaManager( | ||||
|                     email=owner.email, password=owner.password | ||||
|                 ) | ||||
|                 vm = manager.get_vm(obj.vm_id) | ||||
|                 context['vm'] = VirtualMachineSerializer(vm).data | ||||
|             except WrongIdError: | ||||
|                 messages.error(self.request, | ||||
|                                'The VM you are looking for is unavailable at the moment. \ | ||||
|                                 Please contact Data Center Light support.' | ||||
|                                ) | ||||
|                 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.' | ||||
|                                ) | ||||
|                 messages.error( | ||||
|                     self.request, | ||||
|                     _('In order to create a VM, you need to create/upload ' | ||||
|                       'your SSH KEY first.') | ||||
|                 ) | ||||
|         elif not card_details.get('response_object'): | ||||
|             # new order, failed to get card details | ||||
|             context['failed_payment'] = True | ||||
|             context['card_details'] = card_details | ||||
|         else: | ||||
|             # new order, confirm payment | ||||
|             context['site_url'] = reverse('hosting:create_virtual_machine') | ||||
|             context['cc_last4'] = card_details.get('response_object').get( | ||||
|                 'last4') | ||||
|             context['cc_brand'] = card_details.get('response_object').get( | ||||
|                 'cc_brand') | ||||
|             context['vm'] = self.request.session.get('specs') | ||||
|         return context | ||||
| 
 | ||||
|     def get(self, request, *args, **kwargs): | ||||
|         if not self.kwargs.get('pk'): | ||||
|             if 'specs' not in self.request.session: | ||||
|                 return HttpResponseRedirect( | ||||
|                     reverse('hosting:create_virtual_machine') | ||||
|                 ) | ||||
|             if 'token' not in self.request.session: | ||||
|                 return HttpResponseRedirect(reverse('hosting:payment')) | ||||
|         self.object = self.get_object() | ||||
|         context = self.get_context_data(object=self.object) | ||||
|         if 'failed_payment' in context: | ||||
|             msg = context['card_details'].get('error') | ||||
|             messages.add_message( | ||||
|                 self.request, messages.ERROR, msg, | ||||
|                 extra_tags='failed_payment' | ||||
|             ) | ||||
|             return HttpResponseRedirect( | ||||
|                 reverse('hosting:payment') + '#payment_error' | ||||
|             ) | ||||
|         return self.render_to_response(context) | ||||
| 
 | ||||
|     def post(self, request): | ||||
|         template = request.session.get('template') | ||||
|         specs = request.session.get('specs') | ||||
|  | @ -838,39 +875,71 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View): | |||
|     template_name = "hosting/create_virtual_machine.html" | ||||
|     login_url = reverse_lazy('hosting:login') | ||||
| 
 | ||||
|     def validate_cores(self, value): | ||||
|         if (value > 48) or (value < 1): | ||||
|             raise ValidationError(_('Invalid number of cores')) | ||||
| 
 | ||||
|     def validate_memory(self, value): | ||||
|         if (value > 200) or (value < 2): | ||||
|             raise ValidationError(_('Invalid RAM size')) | ||||
| 
 | ||||
|     def validate_storage(self, value): | ||||
|         if (value > 2000) or (value < 10): | ||||
|             raise ValidationError(_('Invalid storage size')) | ||||
| 
 | ||||
|     def get(self, request, *args, **kwargs): | ||||
|         try: | ||||
|             manager = OpenNebulaManager() | ||||
|             templates = manager.get_templates() | ||||
|             configuration_options = HostingPlan.get_serialized_configs() | ||||
| 
 | ||||
|             context = { | ||||
|                 'templates': VirtualMachineTemplateSerializer(templates, | ||||
|                                                               many=True).data, | ||||
|                 'configuration_options': configuration_options, | ||||
|             } | ||||
|         except: | ||||
|             messages.error( | ||||
|                 request, | ||||
|                 'We could not load the VM templates due to a backend connection \ | ||||
|                 error. Please try again in a few minutes' | ||||
|             ) | ||||
|             context = { | ||||
|                 'error': 'connection' | ||||
|             } | ||||
| 
 | ||||
|         context = {'templates': VMTemplate.objects.all()} | ||||
|         return render(request, self.template_name, context) | ||||
| 
 | ||||
|     def post(self, request): | ||||
|         manager = OpenNebulaManager() | ||||
|         template_id = request.POST.get('vm_template_id') | ||||
|         template = manager.get_template(template_id) | ||||
|         configuration_id = int(request.POST.get('configuration')) | ||||
|         configuration = HostingPlan.objects.get(id=configuration_id) | ||||
|         request.session['template'] = VirtualMachineTemplateSerializer( | ||||
|             template).data | ||||
|         cores = request.POST.get('cpu') | ||||
|         cores_field = forms.IntegerField(validators=[self.validate_cores]) | ||||
|         memory = request.POST.get('ram') | ||||
|         memory_field = forms.IntegerField(validators=[self.validate_memory]) | ||||
|         storage = request.POST.get('storage') | ||||
|         storage_field = forms.IntegerField(validators=[self.validate_storage]) | ||||
|         price = request.POST.get('total') | ||||
|         template_id = int(request.POST.get('config')) | ||||
|         template = VMTemplate.objects.filter( | ||||
|             opennebula_vm_template_id=template_id).first() | ||||
|         template_data = VMTemplateSerializer(template).data | ||||
| 
 | ||||
|         request.session['specs'] = configuration.serialize() | ||||
|         try: | ||||
|             cores = cores_field.clean(cores) | ||||
|         except ValidationError as err: | ||||
|             msg = '{} : {}.'.format(cores, str(err)) | ||||
|             messages.add_message(self.request, messages.ERROR, msg, | ||||
|                                  extra_tags='cores') | ||||
|             return HttpResponseRedirect( | ||||
|                 reverse('datacenterlight:index') + "#order_form") | ||||
| 
 | ||||
|         try: | ||||
|             memory = memory_field.clean(memory) | ||||
|         except ValidationError as err: | ||||
|             msg = '{} : {}.'.format(memory, str(err)) | ||||
|             messages.add_message(self.request, messages.ERROR, msg, | ||||
|                                  extra_tags='memory') | ||||
|             return HttpResponseRedirect( | ||||
|                 reverse('datacenterlight:index') + "#order_form") | ||||
| 
 | ||||
|         try: | ||||
|             storage = storage_field.clean(storage) | ||||
|         except ValidationError as err: | ||||
|             msg = '{} : {}.'.format(storage, str(err)) | ||||
|             messages.add_message(self.request, messages.ERROR, msg, | ||||
|                                  extra_tags='storage') | ||||
|             return HttpResponseRedirect( | ||||
|                 reverse('datacenterlight:index') + "#order_form") | ||||
| 
 | ||||
|         specs = { | ||||
|             'cpu': cores, | ||||
|             'memory': memory, | ||||
|             'disk_size': storage, | ||||
|             'price': price | ||||
|         } | ||||
| 
 | ||||
|         request.session['specs'] = specs | ||||
|         request.session['template'] = template_data | ||||
|         return redirect(reverse('hosting:payment')) | ||||
| 
 | ||||
| 
 | ||||
|  | @ -912,7 +981,19 @@ class VirtualMachineView(LoginRequiredMixin, View): | |||
|     def get(self, request, *args, **kwargs): | ||||
|         vm = self.get_object() | ||||
|         if vm is None: | ||||
|             return redirect(reverse('hosting:virtual_machines')) | ||||
|             if self.request.is_ajax(): | ||||
|                 storage = messages.get_messages(request) | ||||
|                 for m in storage: | ||||
|                     pass | ||||
|                 storage.used = True | ||||
|                 return HttpResponse( | ||||
|                     json.dumps({'text': ugettext('Terminated')}), | ||||
|                     content_type="application/json" | ||||
|                 ) | ||||
|             else: | ||||
|                 return redirect(reverse('hosting:virtual_machines')) | ||||
|         elif self.request.is_ajax(): | ||||
|             return HttpResponse() | ||||
|         try: | ||||
|             serializer = VirtualMachineSerializer(vm) | ||||
|             context = { | ||||
|  | @ -927,6 +1008,7 @@ class VirtualMachineView(LoginRequiredMixin, View): | |||
|         return render(request, self.template_name, context) | ||||
| 
 | ||||
|     def post(self, request, *args, **kwargs): | ||||
|         response = {'status': False} | ||||
|         owner = self.request.user | ||||
|         vm = self.get_object() | ||||
| 
 | ||||
|  | @ -936,42 +1018,50 @@ class VirtualMachineView(LoginRequiredMixin, View): | |||
|             email=owner.email, | ||||
|             password=owner.password | ||||
|         ) | ||||
|         vm_data = VirtualMachineSerializer(manager.get_vm(vm.id)).data | ||||
|         terminated = manager.delete_vm( | ||||
|             vm.id | ||||
|         ) | ||||
| 
 | ||||
|         try: | ||||
|             vm_data = VirtualMachineSerializer(manager.get_vm(vm.id)).data | ||||
|         except WrongIdError: | ||||
|             return redirect(reverse('hosting:virtual_machines')) | ||||
| 
 | ||||
|         terminated = manager.delete_vm(vm.id) | ||||
| 
 | ||||
|         if not terminated: | ||||
|             messages.error( | ||||
|                 request, | ||||
|                 'Error terminating VM %s' % (opennebula_vm_id) | ||||
|             ) | ||||
|             return HttpResponseRedirect(self.get_success_url()) | ||||
|         context = { | ||||
|             'vm': vm_data, | ||||
|             'base_url': "{0}://{1}".format(self.request.scheme, | ||||
|                                            self.request.get_host()), | ||||
|             'page_header': _('Virtual Machine Cancellation') | ||||
|         } | ||||
|         email_data = { | ||||
|             'subject': context['page_header'], | ||||
|             'to': self.request.user.email, | ||||
|             'context': context, | ||||
|             'template_name': 'vm_canceled', | ||||
|             'template_path': 'hosting/emails/', | ||||
|             'from_address': settings.DCL_SUPPORT_FROM_ADDRESS, | ||||
|         } | ||||
|         email = BaseEmail(**email_data) | ||||
|         email.send() | ||||
| 
 | ||||
|         messages.error( | ||||
|             request, | ||||
|             _('VM %(VM_ID)s terminated successfully') % { | ||||
|                 'VM_ID': opennebula_vm_id} | ||||
|             response['text'] = ugettext( | ||||
|                 'Error terminating VM') + opennebula_vm_id | ||||
|         else: | ||||
|             for t in range(15): | ||||
|                 try: | ||||
|                     manager.get_vm(opennebula_vm_id) | ||||
|                 except WrongIdError: | ||||
|                     response['status'] = True | ||||
|                     response['text'] = ugettext('Terminated') | ||||
|                     break | ||||
|                 except BaseException: | ||||
|                     break | ||||
|                 else: | ||||
|                     sleep(2) | ||||
|             context = { | ||||
|                 'vm': vm_data, | ||||
|                 'base_url': "{0}://{1}".format(self.request.scheme, | ||||
|                                                self.request.get_host()), | ||||
|                 'page_header': _('Virtual Machine Cancellation') | ||||
|             } | ||||
|             email_data = { | ||||
|                 'subject': context['page_header'], | ||||
|                 'to': self.request.user.email, | ||||
|                 'context': context, | ||||
|                 'template_name': 'vm_canceled', | ||||
|                 'template_path': 'hosting/emails/', | ||||
|                 'from_address': settings.DCL_SUPPORT_FROM_ADDRESS, | ||||
|             } | ||||
|             email = BaseEmail(**email_data) | ||||
|             email.send() | ||||
|         return HttpResponse( | ||||
|             json.dumps(response), | ||||
|             content_type="application/json" | ||||
|         ) | ||||
| 
 | ||||
|         return HttpResponseRedirect(self.get_success_url()) | ||||
| 
 | ||||
| 
 | ||||
| class HostingBillListView(PermissionRequiredMixin, LoginRequiredMixin, | ||||
|                           ListView): | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue