Compare commits

...

729 Commits

Author SHA1 Message Date
nico14571 66538f7335 Merge branch 'master' into 'master'
Task #6038: Change datacenterlight.ch message form to send email to support@ungleich.ch

See merge request ungleich-public/dynamicweb!679
2018-11-11 18:49:55 +01:00
aatish 1bfb710ee6 Change datacenterlight.ch message form to send email to support@ungleich.ch 2018-11-11 17:01:19 +01:00
PCoder 2c674572c9 Update Changelog 2018-10-22 23:25:05 +02:00
pcoder116 b1a4ba6f0e Merge branch 'bugfix/use-correct-django-multisite-version' into 'master'
Use correct django-multisite version

See merge request ungleich-public/dynamicweb!676
2018-10-22 23:13:00 +02:00
PCoder e9cb303b09 Use correct django-multisite version
We had multiple versions of django-multisite in the
requirements.txt. This commit removes the wrong version that
caused server not to start with an error

File "/usr/local/lib/python3.5/site-packages/django/contrib/sites/models.py" in get_current
  65.             return self._get_site_by_id(site_id)

File "/usr/local/lib/python3.5/site-packages/django/contrib/sites/models.py" in _get_site_by_id
  37.         return SITE_CACHE[site_id]

File "/usr/local/lib/python3.5/site-packages/multisite/hacks.py" in __getitem__
  124.             raise KeyError(key)
2018-10-22 22:39:22 +02:00
PCoder bdf9f53648 Update Changelog for 2.4.1 2018-10-18 09:21:16 +02:00
Pcoder 8d6f2ed4ae
Merge pull request #674 from pcoder/bugfix/update-pycryptodome
Update pycryptodome
2018-10-18 09:15:14 +02:00
PCoder 062e81408d Update pycryptodome
From 3.4 to 3.6.6
2018-10-18 09:03:08 +02:00
PCoder 3a489f5be0 Update Changelog for 2.4 2018-10-18 07:44:07 +02:00
Pcoder 961bf2e46f
Merge pull request #672 from pcoder/task/5681/offer-512mb-ram
Allow admin to lower the minimum RAM for a calculator instance to 512 MB
2018-10-18 07:21:40 +02:00
PCoder c36554b4d1 Round total_price also to 2 decimal places 2018-10-18 06:58:18 +02:00
PCoder 270c610111 Correct the min value of RAM
Based on what is set by the admin in the backend
2018-10-17 09:56:42 +02:00
PCoder 3ebf932422 Adjust hosting calculator form validation
For 512 MB RAM offer
2018-10-17 09:29:08 +02:00
PCoder 814163e58e Add minRAM js code for hosting also 2018-10-17 09:11:37 +02:00
PCoder 0d767e74a9 Merge branch 'master' into task/5681/offer-512mb-ram 2018-10-17 07:57:07 +02:00
PCoder 5a52ae1a1d Update Changelog for 2.3.1 2018-10-17 07:47:34 +02:00
Pcoder 6612a7debd
Merge pull request #673 from pcoder/bugfix/show_correct_vat
Round VAT percent to 2 decimal places
2018-10-17 07:40:22 +02:00
PCoder 8929a26714 Round VAT percent to 2 decimal places 2018-10-17 07:27:28 +02:00
PCoder 0e9f8ce906 Cleanup unnecessary code 2018-10-16 09:01:00 +02:00
PCoder b7cc7b08ce Update calculator min RAM input js validation 2018-10-16 08:58:12 +02:00
PCoder 26970ece92 Set default RAM step to 1
We later change this according to minRam set in the backend
2018-10-16 08:55:08 +02:00
PCoder 42d6f38f0c Merge branch 'master' into task/5681/offer-512mb-ram 2018-10-08 07:56:57 +02:00
PCoder 27ee5ca5ac Update Changelog for 2.3 2018-10-08 07:28:04 +02:00
Pcoder 733fb9fc43
Merge pull request #670 from pcoder/task/5690/redirect-on-payment-error
Task/5690/redirect on payment error
2018-10-06 07:59:40 +02:00
PCoder 5770c231ee Fix flake8 warnings 2018-10-06 07:47:40 +02:00
PCoder 5985ded36f Obtain product_slug from session
Obtaining slug from kwargs won't work in OrderConfirmation page
because we do not set the kwargs for that page. To resolve this,
I add the product_slug to the generic_payment_details dict in the
session
2018-10-06 07:31:41 +02:00
PCoder 2b7d4bbef5 Redirect to product page on error 2018-10-05 11:10:10 +02:00
PCoder 10dab1350a Use refactored method clear_all_session_vars 2018-10-05 11:01:49 +02:00
PCoder 806726614e Clear all session variables before loading DCLCalculatorPlugin 2018-10-05 10:37:23 +02:00
PCoder 5d9b2ee41a Refactor clearing all session variables 2018-10-05 10:36:20 +02:00
PCoder 2fbee916cc Correct help text for product slug field 2018-10-05 09:37:57 +02:00
PCoder 1dafa592a2 Update Changelog 2018-10-05 09:23:39 +02:00
Pcoder e557a777c3
Merge pull request #666 from pcoder/task/5690/generic-payment-page
Task/5690/generic payment page
2018-10-05 09:16:08 +02:00
PCoder f4579595c3 Add newline character to end of file 2018-10-05 09:05:47 +02:00
PCoder 12eabc5f6c Redirect user to product page on login
For the case when user is on product page and tries logging in
2018-10-05 08:58:57 +02:00
PCoder e3ec67d32c ProductPaymentForm: Set input fields input-no-border style 2018-10-05 08:58:10 +02:00
PCoder 8758cd1cd8 Add style input-no-border 2018-10-05 08:57:20 +02:00
PCoder 1100d61b5d Fix flake8 warnings 2018-10-03 23:17:25 +02:00
PCoder b3e3af1c1a Make ProductPaymentForm's recurring field hidden 2018-10-03 22:54:45 +02:00
PCoder ec70cd1c83 Pass product_id to ProductPaymentForm 2018-10-03 22:53:24 +02:00
PCoder 74ec39498e Update GenericProduct
Remove url field
Make slug mandatory
2018-10-03 09:58:49 +02:00
PCoder e4bfdec0b6 Update ProductPaymentForm's validation 2018-10-03 09:38:49 +02:00
PCoder 193b87bbb5 Use proper payment form 2018-10-03 09:36:43 +02:00
PCoder ca18004819 Remove stale reference to product_id 2018-10-03 09:36:00 +02:00
PCoder e1c91d886b Use explicit index in plan name formatting 2018-10-03 09:35:20 +02:00
PCoder 27a92780a6 Add amount label + Reset textarea height 2018-10-03 08:38:20 +02:00
PCoder 8a2734fa0e Show GenericPaymentForm labels 2018-10-03 08:36:21 +02:00
PCoder 97693f0bb3 Format code 2018-10-03 08:27:22 +02:00
PCoder 495ac0c6d6 Use ProductPaymentForm instead of GenericPaymentForm
Only if product_id is in the session, which identifies that we are
coming here via product_slug
2018-10-03 08:19:33 +02:00
PCoder 530bbcd5f6 Create ProductPaymentForm from GenericPaymentForm 2018-10-03 08:18:45 +02:00
PCoder 1cdc9ea657 Clear product_id from session 2018-10-03 08:18:19 +02:00
PCoder a4065c7e24 Handle product_slug 2018-10-03 07:55:56 +02:00
PCoder e3bd963600 Test product_slug url (wip) 2018-10-02 10:02:44 +02:00
PCoder e47f4f05b4 Handler if product_slug is given (wip) 2018-10-02 10:02:02 +02:00
PCoder 3bad37c605 Make GenericProduct slug unique 2018-10-02 10:00:59 +02:00
PCoder 930333357e GenericProduct: Remove image field and add slug field 2018-10-02 09:27:38 +02:00
PCoder dd9e7dde35 Merge branch 'master' into task/5690/generic-payment-page 2018-10-02 09:01:46 +02:00
PCoder f2f95c8559 Update min attribute according to minimum ram in the plugin 2018-10-01 08:56:45 +02:00
PCoder a93c900109 Pass 512 MB to memory parameter 2018-10-01 08:54:22 +02:00
PCoder c2dbbf0424 Update views.py to include 512mb ram case
RAM server side validation is as follows:
- pid is a mandatory parameter for a valid RAM, otherwise a
 validation error is raised
- check if enable_512mb_ram is enabled. In this case, validate
if the input ram is either a whole number or 0.5 and in the range
0.5 <= value <= 200
- otherwise check ram is a whole number in the range
1 <= value <= 200
2018-10-01 07:52:28 +02:00
PCoder f85ef714ab Pass instance context
Also POST plugin_id as a form parameter to check enable_512mb_ram
case
2018-10-01 07:50:18 +02:00
PCoder 24d719e4f1 Update migration 2018-09-29 07:37:23 +02:00
PCoder 5452c1c478 Merge master into task/5681/offer-512mb-ram 2018-09-28 08:36:35 +02:00
PCoder 8743853a7b Update Changelog 2018-09-28 08:13:37 +02:00
PCoder 737d890a7c Update Changelog for 2.2.2 2018-09-28 08:11:36 +02:00
PCoder db20e3cbe7 Add plus minus ram handler for 512MB option 2018-09-28 08:07:12 +02:00
Pcoder 32767fed68
Merge pull request #668 from pcoder/task/5721/set_calculator_OS_order
Task/5721/set calculator OS order
2018-09-28 07:32:55 +02:00
PCoder e1ce017ec8 Add migration 2018-09-27 22:48:23 +02:00
PCoder b047ccdef1 Fix flake8 error 2018-09-27 22:33:24 +02:00
PCoder 56460ac8f0 Update migration 2018-09-27 22:16:05 +02:00
PCoder d93861ca32 Set Devuan Ascii as default template 2018-09-27 22:14:42 +02:00
PCoder a02c3c6973 Sort templates alphabetically
And also select the chosen template as the default one
2018-09-27 22:12:13 +02:00
PCoder 768f3532f7 Add default_selected_template field to DCLCalculatorPluginModel 2018-09-27 22:09:11 +02:00
PCoder 21084cdc9f Initialize minRam js variable from what is passed from backend 2018-09-27 09:07:28 +02:00
PCoder 12f139976d Simplify logic to set min_ram in the calculator form 2018-09-27 09:06:39 +02:00
PCoder da21699212 Merge branch 'master' into task/5681/offer-512mb-ram 2018-09-26 23:10:17 +02:00
PCoder 3075cffd77 Include product_id in generic payment Stripe plan name 2018-09-26 23:01:04 +02:00
PCoder 232022aaaf Fix flake8 errors 2018-09-26 22:51:34 +02:00
PCoder b7929a16e2 Update datacenterlight django.po 2018-09-26 22:45:09 +02:00
PCoder 6c03e3f712 Change generic payment subject and correct misplaced comma
Data Center LightConfirmation of your payment ->
Confirmation of your payment
2018-09-26 22:42:33 +02:00
PCoder 72c16713a7 Update .po of datacenterlight 2018-09-26 22:37:32 +02:00
PCoder 52d048a555 Add missing Cheers string in email 2018-09-26 22:35:11 +02:00
PCoder 3148dbccf8 Translate text + reformat 2018-09-26 22:31:06 +02:00
PCoder 84056a5b36 Correct generic payment email as per Sanghee's corrections 2018-09-26 22:21:59 +02:00
PCoder fcc113e9d9 Add locale_date class to date fields so that we can localize 2018-09-26 22:06:14 +02:00
PCoder 1041284866 Refactor moment.js locale date code to virutal_machine_detail.js 2018-09-26 21:52:12 +02:00
PCoder 737681136f Correct flake8 error 2018-09-26 21:23:46 +02:00
PCoder efae2b1d9a Update Changelog 2018-09-26 21:17:35 +02:00
Pcoder 31e8467f20
Merge pull request #667 from pcoder/bugfix/corrections_by_malcolm
Bugfix/corrections by malcolm

- virutal -> virtual
- some DE corrections
2018-09-26 21:11:13 +02:00
PCoder 4feeec23d4 Correct spelling: virutal -> virtual 2018-09-26 20:50:19 +02:00
PCoder ca578ecf56 Merge branch 'master' of https://github.com/MalcolmA/dynamicweb into MalcolmA-master 2018-09-26 20:46:54 +02:00
PCoder b021a8ed6e Remove filer dependency in migration 2018-09-26 09:29:10 +02:00
PCoder 99b11f013f Remove old migration 2018-09-26 09:25:03 +02:00
PCoder 1f990b1ab7 Update migration 2018-09-26 09:24:47 +02:00
PCoder 41cba9daa3 Show product name in dcl order detail template 2018-09-26 09:15:49 +02:00
PCoder 4575ff60ec Refactor validation code + Add product_id to context 2018-09-26 09:15:24 +02:00
PCoder 508360472a Add amount/recurring form fields validation 2018-09-26 09:14:11 +02:00
PCoder 48ba6a6166 Remove disabled on amount/recurring fields 2018-09-26 09:13:45 +02:00
PCoder 10e8f0a820 Show product name in hosting order detail 2018-09-26 09:10:15 +02:00
PCoder 5df2080f92 Reset amount and recurring fields on error 2018-09-26 09:09:12 +02:00
PCoder 9ff20491bd Merge branch 'master' into task/5690/generic-payment-page 2018-09-25 23:25:22 +02:00
PCoder ce2ac4524c Update Changelog for release 2.2.1 2018-09-25 22:49:31 +02:00
Pcoder 5d5408eb8c
Merge pull request #665 from pcoder/manage_navbar_login
Change DCLNavbarPlugin to show login option only if set
2018-09-25 22:43:10 +02:00
PCoder c9ac959ff6 Change DCLNavbarPlugin to show login option only if set 2018-09-25 22:29:58 +02:00
PCoder d54bf84b1e Format code 2018-09-25 02:20:57 +02:00
PCoder 50e5fea339 Add GenericProduct model and associate it to HostingOrder 2018-09-25 02:20:31 +02:00
PCoder 67231275c7 Modify migration 2018-09-25 02:19:28 +02:00
PCoder 1228d3dbc6 Add product_name to GenericPaymentForm 2018-09-25 02:18:31 +02:00
PCoder 69a1d2df71 Add ajax call to fetch price/recurring
Based on selected production
2018-09-25 02:16:44 +02:00
PCoder b63a572231 Register GenericProduct to adminsite 2018-09-25 02:14:37 +02:00
PCoder 51c5fa98dd Save description and product_id 2018-09-25 02:14:11 +02:00
PCoder ed7ffb355f Server side validation of the POSTed product 2018-09-25 02:13:39 +02:00
PCoder 1988020006 Handle generic-payment-form POST
To return pricing based on user's selected product
2018-09-25 02:12:42 +02:00
PCoder b348c93fee Add product hidden input type
To distinguish generic-payment-form submit
2018-09-25 01:56:10 +02:00
PCoder 481f13d20c Check key existence 2018-09-24 22:27:25 +02:00
PCoder 114dbd8242 Make recurring monthly and amount in chf explicit 2018-09-24 08:32:33 +02:00
PCoder a90bec98ec Make invoice for generic payment showable 2018-09-24 08:22:46 +02:00
PCoder d99271f71d Redirect users to orders after generic payment is successful 2018-09-23 17:19:24 +02:00
PCoder ff993e32db Revert back to POST parameters for billing-form method 2018-09-23 17:01:29 +02:00
PCoder 2a694295ad Refactor payment.js 2018-09-23 17:00:48 +02:00
PCoder fcfc56e132 Move csrf_token out of the form loop 2018-09-23 16:15:48 +02:00
PCoder d6e4a86724 Append One time charge/Recurring only for generic payments 2018-09-23 13:34:26 +02:00
PCoder dd82bdc9da Fix a flake8 error 2018-09-23 13:27:33 +02:00
PCoder 7f3b916c58 Reformat code 2018-09-23 13:26:48 +02:00
PCoder d23624c525 Modify datacenterlight/views.py to includes various cases of generic payment 2018-09-23 13:15:26 +02:00
PCoder 9ec05e7df4 Merge billing and generic-payment forms and post values as get
parameters
2018-09-23 13:13:32 +02:00
PCoder bc3eaaa7eb Move csrf_token out of form loop 2018-09-23 13:10:26 +02:00
PCoder bce47032ab Handle generic payment separately in order_detail.html 2018-09-23 13:03:47 +02:00
PCoder e94ecfe52c Change recurring and description to non-required fields + Change
amount to Floatfield
2018-09-23 12:49:57 +02:00
PCoder 332e7d6624 Add generic_payment_id field to HostingOrder migration and
reflect generic payments in adminsite
2018-09-23 12:39:06 +02:00
PCoder c7edcdc8b1 Change description to CharField and set its height 2018-09-22 23:51:39 +02:00
PCoder 730492089b Modify PaymentOrderView to accomodate the new generic payment case (WIP) 2018-09-22 08:20:49 +02:00
PCoder 429dd10b75 Modify dcl landing_payment.html to accomodate the new generic
payment form
2018-09-22 08:17:46 +02:00
PCoder a7fa52490c Create GenericPaymentForm 2018-09-22 08:16:26 +02:00
PCoder 76efc35324 Add enable_512mb_ram option in model/plugin 2018-09-22 06:44:37 +02:00
PCoder d7be223fcb Change margin bottom for vm terminate error 2018-09-08 12:39:12 +02:00
PCoder 304feb4f7b Update text 2018-09-08 10:46:07 +02:00
PCoder e376f38baa Correct text 2018-09-08 10:33:00 +02:00
PCoder 84dae63968 Fix: ugettext_lazy doesn't like string format 2018-09-08 10:29:47 +02:00
PCoder 4914280868 Set text for vm terminate timeout 2018-09-08 10:14:57 +02:00
PCoder ce2d34350f Update Changelog 2018-09-08 00:15:44 +02:00
Pcoder 743ce4ed70
Merge pull request #648 from pcoder/bugfix/log_vm_terminate_errors
Bugfix/log vm terminate errors
2018-09-08 00:09:59 +02:00
PCoder a7afbec5b4 Fix flake8 error 2018-09-07 23:14:57 +02:00
PCoder ff6df8cd58 Simplify logic 2018-09-07 23:11:46 +02:00
PCoder 7d4cf5c3c2 Fix flake8 error 2018-09-07 22:46:18 +02:00
PCoder 8e7789462e Merge branch 'master' into bugfix/log_vm_terminate_errors 2018-09-07 22:43:29 +02:00
PCoder d601b987d2 Update Changelog 2018-09-06 01:15:21 +02:00
PCoder c6ec2c062c Update Changelog for 2.2 2018-09-06 01:11:27 +02:00
Pcoder c6612153e8
Merge pull request #663 from pcoder/bugfix/plan-with-price
Use price in stripe plan name too
2018-09-06 01:04:47 +02:00
PCoder fc8f9993af Remove whitespace in Stripe plan 2018-09-06 00:52:55 +02:00
PCoder b03cb073c2 Replace @ with a comma 2018-09-06 00:44:47 +02:00
PCoder fdffe2389b Round price 2018-09-06 00:31:17 +02:00
PCoder d8a532e7b0 Round discount to 2 decimal points also 2018-09-06 00:26:08 +02:00
PCoder 642153345c Round price, vat and discount to 2 decimal places 2018-09-06 00:18:04 +02:00
PCoder 8a30100488 Use price in stripe plan name too 2018-09-05 23:26:51 +02:00
PCoder 5f19a85a28 Update Changelog for 2.1.2 2018-08-30 10:10:31 +02:00
Pcoder 362a93a97f
Merge pull request #662 from pcoder/use_blog_feed
Set blog rss feed for all blog template
2018-08-30 09:57:36 +02:00
PCoder 4d4472f0d3 Set blog rss feed for all blog template 2018-08-30 09:47:29 +02:00
Malcolm Anyakee c891694dc0 Update Bugfix
Fixed bugs and fixed typo
2018-08-25 23:13:16 +02:00
PCoder 23eb054422 Update Changelog for release 2.1.1 2018-08-24 12:23:01 +02:00
PCoder 144a780105 Change text: 'Terminated VM can not ...' -> 'Terminated VMs can not ...' 2018-08-24 11:58:01 +02:00
PCoder 3796dabe11 Update Changelog 2018-08-24 11:30:49 +02:00
Pcoder 3de172adf2
Merge pull request #656 from pcoder/task/5487/warn_users_before_terminating_vm
Task/5487/warn users before terminating vm
2018-08-24 11:25:15 +02:00
PCoder d7f171d710 Change DE translation (beenden instead of terminieren) 2018-08-24 11:07:50 +02:00
PCoder 4f745b607d Merge branch 'master' into task/5487/warn_users_before_terminating_vm 2018-08-24 10:58:24 +02:00
PCoder 9a5b9c7af5 Update Changelog 2018-08-24 10:05:37 +02:00
PCoder 4a16251a69 Update Changelog 2018-08-24 10:00:17 +02:00
Pcoder fbfa6c8d21
Merge pull request #652 from pcoder/bugfix/send_email_on_dg_subscription
Send email to admin on dg subscription
2018-08-24 09:54:54 +02:00
PCoder c458b56f71 Add migration file 2018-08-24 09:40:15 +02:00
PCoder a45eef0409 Change cc_brand field to 128 chars 2018-08-24 09:37:56 +02:00
PCoder 83a6e53fae Merge branch 'master' into bugfix/send_email_on_dg_subscription 2018-08-24 08:42:25 +02:00
PCoder feceb14aec Update Changelog 2018-08-23 19:55:30 +02:00
Pcoder f1cc3c5892
Merge pull request #657 from pcoder/5458/make_hostingorder_readable
5458/make hostingorder readable
2018-08-23 19:50:17 +02:00
PCoder e1263ce9b3 Add DE translation fo #5487 2018-08-23 19:40:16 +02:00
PCoder 4baa3a7095 Improve HostingOrder str representation 2018-08-23 09:18:55 +02:00
PCoder 4a1434c514 Make string representation of HostingOrder more readable 2018-08-23 09:07:12 +02:00
PCoder f9584b4c82 Fix a bug when vm_template is None 2018-08-23 09:06:31 +02:00
PCoder 78e44332b5 Remove an unnecessary question mark 2018-08-22 23:16:26 +02:00
PCoder af78631ec8 Update hosting's django.po 2018-08-22 23:13:49 +02:00
PCoder 29172a9df7 Use a separate class for warning block 2018-08-22 23:06:20 +02:00
PCoder 4425aa7c88 Change modal text 2018-08-22 22:57:32 +02:00
PCoder b7ff519624 Reduce bottom padding further by 2px 2018-08-22 22:54:49 +02:00
PCoder 07837c8752 Reduce bottom padding 2018-08-22 22:51:22 +02:00
PCoder 1e5cf08273 Move the warning to the bottom 2018-08-22 22:47:49 +02:00
PCoder 7e790e7027 Add warning div 2018-08-22 22:36:29 +02:00
PCoder 929e7ead1c Add vm-terminate-warning style 2018-08-22 22:36:17 +02:00
PCoder ab074d9bc2 Update Changelog 2018-08-22 22:00:43 +02:00
Pcoder 05d48ec4ff
Merge pull request #653 from pcoder/bugfix/show_meta_description_in_cms_page
Bugfix/show meta description in cms page (ungleich template)
2018-08-22 21:54:12 +02:00
PCoder 9c5363ef55 Merge branch 'master' into bugfix/show_meta_description_in_cms_page 2018-08-22 21:42:37 +02:00
PCoder fb4591ef6a Update Changelog 2018-08-22 20:51:32 +02:00
Pcoder 438a6ab4e4
Merge pull request #655 from pcoder/task/5473/ping_before_cdist
Task/5473/ping before cdist
2018-08-22 20:45:00 +02:00
PCoder 432b54a86d Remove timeout parameter in ping6 request 2018-08-22 20:27:19 +02:00
PCoder e66e8202db Remove unused variable 2018-08-22 19:56:56 +02:00
PCoder 13876c24c1 Update doc 2018-08-22 00:42:50 +02:00
PCoder f56933021f Also send an email if manage_ssh_key does not complete in time 2018-08-22 00:37:40 +02:00
PCoder 2ff7eaea65 Sleep additional 10 seconds before running manage_public_key 2018-08-22 00:25:59 +02:00
PCoder 893c8ed08b Fix imports 2018-08-22 00:14:17 +02:00
PCoder 3b07687d3e Refactor ping_ok from datacenterlight.utils to utils.hosting_utils 2018-08-22 00:12:14 +02:00
PCoder a55587dbf3 Wait for VM to be pingable before doing a manage_public_key 2018-08-21 23:49:47 +02:00
PCoder 91021d7612 Add ping_ok utility method 2018-08-21 23:24:40 +02:00
PCoder cda6c60e02 Update Changelog for 2.1 2018-08-21 15:09:02 +02:00
Pcoder 873f44c6e5
Merge pull request #654 from pcoder/bugfix/long_cc_brand
Bugfix/long cc brand
2018-08-21 14:57:33 +02:00
PCoder b69abf3edd Make CC brand 128 characters long 2018-08-21 14:42:01 +02:00
PCoder 8bf6440110 Delete old migrations 2018-08-21 14:41:16 +02:00
PCoder 768b8ca820 Change HostingOrder cc_brand field size to 35 chars 2018-08-21 14:06:58 +02:00
PCoder 770b5e080b Make UserCardDetail brand field 35 chars long 2018-08-21 13:24:54 +02:00
M.Ravi 9381327a12 Change author of various cms pages to ungleich glarus ag
(Being consistent)
2018-08-20 11:52:27 +02:00
M.Ravi e5fc87a971 Add description page_attribute for ungleich cms page template 2018-08-20 11:51:16 +02:00
PCoder 7b8301f143 Send email to admin on dg subscriptionon dg subscription 2018-08-10 21:19:12 +02:00
PCoder 051408e134 Update Changelog for 2.0.5 2018-08-08 00:26:00 +02:00
PCoder 29c4cc4454 Correct IPv6 VM name in serializer 2018-08-08 00:10:03 +02:00
PCoder 895961868b Update Changelog for 2.0.4 2018-08-07 23:39:43 +02:00
Pcoder ec2cef7ec5
Merge pull request #651 from pcoder/add_rss_feed_link
Add rss feed link
2018-08-07 23:27:44 +02:00
PCoder e27317aeab Move rss icon from the header to the footer 2018-08-07 23:24:02 +02:00
PCoder a8bf22a5b9 Merge branch 'master' into add_rss_feed_link 2018-08-07 22:41:15 +02:00
PCoder 5366745188 Update Changelog 2018-08-07 22:39:43 +02:00
Pcoder 8bce9c82a9
Merge pull request #650 from pcoder/bugfix/5293/change_to_terminate_hard
Bugfix/5293/Change VM terminate action from terminate to terminate hard
2018-08-07 22:29:57 +02:00
Pcoder e16aab6951
Merge pull request #649 from pcoder/bug/5308/correct_ipv6_vm_name
Bug/5308/Correct IPv6 VM name
2018-08-07 22:28:37 +02:00
PCoder 48936ace51 Revert back commenting out unwanted templates
Todo in another pull request
2018-08-07 21:59:50 +02:00
PCoder 0a3f51361f Add RSS feed icon and link 2018-08-07 21:58:50 +02:00
PCoder 0ddbdb045f Remove unused CMS templates 2018-08-07 21:48:25 +02:00
PCoder 93d489334f Load cms_templates later
These templates are/were used in digitalglarus and hence are
obsolete now. Later can be removed after testing.
2018-08-07 21:44:57 +02:00
PCoder 6546814d62 Change VM terminate action from terminate to terminate hard 2018-08-04 09:52:42 +02:00
PCoder 07c7a64c5a Leave 'ipv6only' as a part of the VM name if it starts with it 2018-08-04 08:47:43 +02:00
PCoder df10e84418 Add owner's email to the subject 2018-07-25 22:04:44 +02:00
PCoder a5cdfab306 Send an email to admin if the VM is still there after 30 seconds
of the VM terminate calll
2018-07-25 21:54:15 +02:00
PCoder 59b020c0d4 Replace all occurrences of logger.info with logger.error 2018-07-25 21:41:05 +02:00
PCoder 15db1c88d7 Add release date for 2.0.3 2018-07-18 22:14:35 +02:00
PCoder be2831818d Update Changelog for 2.0.3 2018-07-18 22:08:47 +02:00
Pcoder a51064448a
Merge pull request #644 from pcoder/remove_unused_comic_url
Remove unused ^comic/$ url
2018-07-18 22:03:49 +02:00
PCoder 62d23b8a5c Update Changelog 2018-07-18 22:02:21 +02:00
Pcoder a062c0091f
Merge pull request #645 from pcoder/5126/allow_apps_to_be_iframed
5126/allow apps to be iframed
2018-07-18 21:56:57 +02:00
PCoder 1291b49ec3 Reformat base.py 2018-07-18 21:49:11 +02:00
PCoder 4a19bd1971 Set X_FRAME_OPTIONS from env 2018-07-18 21:48:39 +02:00
PCoder d6a404d49d Add missing placeholder 2018-07-16 22:38:29 +02:00
PCoder 13e33cbb7a Remove unused import 2018-07-16 20:44:09 +02:00
PCoder 15e435d220 Remove unused ^comic/$ url 2018-07-14 20:05:18 +02:00
PCoder 098a9065f1 Update Changelog for 2.0.2 2018-07-14 19:05:14 +02:00
PCoder c97e2c55f3 Add missing content block in the blog_ungleich.html template file 2018-07-14 19:03:04 +02:00
PCoder a5929b3e86 Update Changelog 2018-07-14 18:41:28 +02:00
Pcoder aec92d4c80
Merge pull request #643 from pcoder/bugfix_blog_template
Move placeholder to blog_ungleich.html
2018-07-14 18:14:18 +02:00
M.Ravi b3cdbbcae2 Move placeholder to blog_ungleich
This was the cause why the structure/content switch was not available on the blog page
2018-07-11 18:51:16 +02:00
Pcoder 5c6528aa03
Merge pull request #642 from pcoder/fix_de_translation
Fix DE translation
2018-07-07 21:50:20 +02:00
PCoder 28cac31a93 Update Changelog for 2.0 2018-07-07 21:45:32 +02:00
PCoder b580ac24f6 Change string formatting 2018-07-07 21:30:40 +02:00
PCoder 3ccefbdb74 Reorganize imports 2018-07-07 21:29:44 +02:00
PCoder d9bcdf22b7 Add missing param in DE translation 2018-07-07 03:01:37 +02:00
PCoder 2ada3ccc6f Update Changelog 2018-07-07 02:35:22 +02:00
Pcoder 0caf3da3bd
Merge pull request #530 from ungleich/task/3747/multiple_cards_support
Task/3747/multiple cards support
2018-07-07 02:28:29 +02:00
PCoder 33bd2e1760 Improve import_usercarddetails management command 2018-07-07 02:15:38 +02:00
PCoder 34ed51a643 Don't dissociate card if transaction fails 2018-07-07 02:03:42 +02:00
PCoder 0f26917f35 Fix PEP8 warning 2018-07-07 01:54:45 +02:00
PCoder 678167978c Add check to forbid user from deleting the last card 2018-07-07 01:50:46 +02:00
PCoder ebcbb26276 Add another DE translation - UPDATE 2018-07-07 01:38:03 +02:00
PCoder e18b8a527a Add missing DE translation 2018-07-07 01:35:52 +02:00
PCoder e60b93d126 Add management command import_usercarddetails 2018-07-07 01:28:25 +02:00
PCoder 8d42ca3200 Remove token parameter from stripe_utils.get_card_details
and add additional paramters to uniquely identify a card
2018-07-07 01:08:49 +02:00
PCoder 4c06a9e730 Add de translations by MalcolmA 2018-07-06 19:33:26 +02:00
PCoder 1f2743a65d Makemessages for datacenterlight, hosting and utils 2018-07-06 01:19:44 +02:00
PCoder 0db4a113e6 Add some styles and do not let user remove card if he has only one 2018-07-05 23:06:47 +02:00
PCoder 5cd62abc70 Refactor code 2018-07-05 20:36:43 +02:00
PCoder 1e214f7b21 Improve comment 2018-07-05 20:35:34 +02:00
PCoder 6b663d82a1 Remove unused variable 2018-07-05 20:35:00 +02:00
PCoder baa2817f57 Update BillingAddressFormSignup: add email validation 2018-07-05 10:00:12 +02:00
PCoder 9035f98060 Update UserCardDetail get method 2018-07-05 09:58:57 +02:00
PCoder faa0604fae Remove redundant code 2018-07-05 09:58:07 +02:00
PCoder 8d7b01d7e2 Disable dissociating card on subscription failure 2018-07-05 09:57:47 +02:00
PCoder e9ac699be8 Add card detail creation code in various landing cases 2018-07-05 09:57:18 +02:00
PCoder ec0216790f Remove redundant code 2018-07-05 09:55:45 +02:00
PCoder 9bf8992ff4 Landing flow: set/get token/card_id 2018-07-04 02:53:43 +02:00
PCoder dc28186fe9 Add styles for landing payment choices 2018-07-04 02:03:50 +02:00
PCoder 8e742852a5 Show all cards in landing and hosting payment page 2018-07-04 02:03:08 +02:00
PCoder 3d8237a34a Get and show all card details in the landing flow, if the user is logged in 2018-07-04 00:50:44 +02:00
PCoder c118e86230 Remove unused context parameters -- introduced during merge 2018-07-03 23:56:42 +02:00
PCoder d98a683b2a Do a get on UserCardDetail only when fingerprint, exp_month and exp_year are known 2018-07-03 23:23:02 +02:00
PCoder 1c5ff1f9dd Add UserCardDetail in the landing non-logged in flow 2018-07-03 23:22:10 +02:00
PCoder 9904a71d38 Rename usercarddetail migration 2018-07-03 22:34:59 +02:00
PCoder c9d01ba95f Fix some errors 2018-07-03 21:52:44 +02:00
PCoder 6d2b011925 Merge master into task/3747/multiple_cards_support 2018-07-03 21:29:04 +02:00
PCoder cb911e05c5 Update Changelog 2018-07-01 22:45:18 +02:00
Pcoder 1fa260aaf5
Merge pull request #624 from pcoder/task/3934/move_hosting_order_out_of_celery_task
Task/3934/Create hostingorder outside celery task
2018-07-01 22:39:08 +02:00
PCoder 44900f6a48 Rename OrderSpecifications to OrderDetail 2018-07-01 22:30:23 +02:00
PCoder 00cb1de75d Add type to OrderSpecification string 2018-07-01 19:46:21 +02:00
PCoder 7f57ace92d Set default and on_delete attributes 2018-07-01 19:23:05 +02:00
PCoder f48005166e Fix bug getting VMTemplate object from vm_template_id 2018-07-01 18:42:11 +02:00
PCoder 900f014d92 Save order specifications in HostingOrder also 2018-07-01 18:33:10 +02:00
PCoder 5851277d9a Reorganize imports 2018-07-01 16:36:41 +02:00
PCoder 43b3a63958 Reorganize imports 2018-07-01 16:25:02 +02:00
PCoder 081921e846 Merge master into task/3934/move_hosting_order_out_of_celery_task 2018-07-01 16:18:43 +02:00
PCoder 6593983f04 Update Changelog 2018-07-01 16:02:35 +02:00
Pcoder ba286eb053
Merge pull request #640 from pcoder/task/4890/ssh_key_manage_for_ipv6
Task/4890/ssh key manage for ipv6
2018-07-01 15:57:17 +02:00
PCoder ef8e380ab7 Fix flake8 error 2018-07-01 11:22:42 +02:00
PCoder ae0d4c0841 Refactor code 2018-07-01 11:17:45 +02:00
PCoder c13af95017 Refactor code: get_primary_ip -> get_ipv6 and remove unwanted code 2018-07-01 11:07:39 +02:00
PCoder 8993a7bde1 Reorganize imports + format code 2018-07-01 11:05:23 +02:00
PCoder 52e53d479e Rename a variable: ipv6s -> ipv6_list 2018-07-01 07:57:15 +02:00
PCoder 68a65b7bc7 Detect private IP correctly 2018-06-30 00:30:51 +02:00
M.Ravi 5eff54cffe Fix a bug -- json not imported; use JsonResponse instead of
HttpResponse
2018-06-27 12:34:57 +02:00
M.Ravi f1e021e1e9 Improve comments 2018-06-27 12:24:53 +02:00
M.Ravi 88e6d9d216 Separate two error messages with a period 2018-06-27 12:24:16 +02:00
M.Ravi 2cd73b313a Merge master into task/3934/move_hosting_order_out_of_celery_task
and resolve some conflicts
2018-06-27 12:09:05 +02:00
PCoder a3db7f2e1a Merge branch 'master' into task/4890/ssh_key_manage_for_ipv6 2018-06-27 09:23:48 +02:00
PCoder 88f0d73336 Update Changelog 2018-06-27 09:21:41 +02:00
Pcoder a56b2d02c8
Merge pull request #641 from pcoder/bugfix/flake8_error
Attempt to fix flake8 error
2018-06-27 09:17:11 +02:00
PCoder 60260ccb08 Attempt to fix flake8 error 2018-06-27 09:06:28 +02:00
PCoder 4a5c5f7942 Refactor getting primary ip of a vm 2018-06-27 01:06:27 +02:00
PCoder 1ec7cb8761 Fix silly copy/paste bug 2018-06-27 00:44:17 +02:00
PCoder 6c2eabbe6a Use the public ipv4 if it exists; otherwise use the ipv6 to do a
cdist cdist ssh configure
2018-06-27 00:01:55 +02:00
PCoder a5c42b9c44 Update Changelog for 1.9.1 2018-06-24 17:05:33 +02:00
PCoder e816f65114 Update Changelog 2018-06-24 16:00:54 +02:00
Pcoder 1522701a5e
Merge pull request #638 from pcoder/task/4799/show_selected_vms_only_in_calculator
Task/4799/show selected vms only in calculator
2018-06-24 15:55:11 +02:00
PCoder bf45bf1b4d Merge remote-tracking branch 'mainRepo/master' into task/4799/show_selected_vms_only_in_calculator 2018-06-24 15:14:11 +02:00
PCoder 549e882ebe Update Changelog 2018-06-24 14:54:50 +02:00
Pcoder 78bda89b16
Merge pull request #639 from pcoder/task/4847/add_comics_analytics_code
Add google analytics code for comic.ungleich.ch
2018-06-24 14:49:52 +02:00
PCoder fb9000de90 Update Changelog 2018-06-24 14:47:05 +02:00
PCoder 70cac38f81 Move initialization of VMTemplates out of the plugin 2018-06-24 10:29:07 +02:00
PCoder 79e83b4480 Refactor show_vm_templates to DCLCalculatorPluginModel from VMPricing 2018-06-24 09:08:22 +02:00
PCoder dcbb0c2d64 Add google analytics code for comic.ungleich.ch 2018-06-19 08:49:21 +02:00
PCoder b872777bda Filter context templates also by the ids that have been set for the calculator 2018-06-13 11:55:30 +02:00
PCoder 94f520be35 Add migration 2018-06-13 11:17:18 +02:00
PCoder 5748eecedb Create field for storing os_templates_to_show 2018-06-13 11:16:49 +02:00
PCoder fae9fce5c6 Remove unnecessary code 2018-06-12 11:15:17 +02:00
PCoder 6db38d7e29 Check card_id also for order confirmation 2018-06-12 11:14:36 +02:00
PCoder 86f0526773 Reformat code 2018-06-12 11:13:10 +02:00
PCoder 23630d4473 Move csrf_token call outside the for loop 2018-06-12 11:11:37 +02:00
PCoder 7494116468 Add missing import 2018-06-12 09:36:00 +02:00
PCoder 57eda62586 Update UserCardDetail migration 2018-06-12 08:28:46 +02:00
PCoder cf00ff6bd8 Attempt to merge master into task/3747/multiple_cards_support 2018-06-12 08:16:11 +02:00
Arvind Tiwari ae911c8c21
Merge pull request #635 from tiwariav/patch/vm_template_patch
add vm_type option to vm_template and dcl calculator
2018-05-24 04:24:26 +05:30
Arvind Tiwari 2d1805f11d update fetchvmtemplates command 2018-05-24 04:14:53 +05:30
Arvind Tiwari a4ca17e2ed vm template prefix 2018-05-24 03:57:01 +05:30
Arvind Tiwari cba53e0fe3
Update Changelog 2018-05-17 01:01:50 +05:30
Arvind Tiwari 4e971c23a0
Merge pull request #632 from tiwariav/task/4559/calculator_discount
bugfix/ discount calculator styles and translation
2018-05-12 22:09:32 +05:30
Arvind Tiwari 8fb0d9a48a order detail divider lines full width 2018-05-12 21:59:06 +05:30
Arvind Tiwari 3446dcc469 calculator translation 2018-05-12 21:48:57 +05:30
Arvind Tiwari d40977c5da
Merge pull request #629 from tiwariav/task/4559/calculator_discount
Task/4559 enable discount on calculator
2018-05-12 21:21:03 +05:30
Arvind Tiwari b09604d30f add space befor CHF 2018-05-12 03:56:04 +05:30
Arvind Tiwari f39f95e1f7 remove template filter from translation 2018-05-12 03:47:54 +05:30
Arvind Tiwari 20f1df8a70 blocktrans variable fix 2018-05-12 03:39:38 +05:30
Arvind Tiwari 39f7898259 edit order detail footer text 2018-05-12 03:15:07 +05:30
Arvind Tiwari 55889499df order detail style fix 2018-05-12 02:47:27 +05:30
Arvind Tiwari 8044e0c2a0 calculator discount text modified 2018-05-11 17:47:27 +05:30
Arvind Tiwari 30deae5a20 strong color fix 2018-05-11 17:25:44 +05:30
Arvind Tiwari a14407182f font weight for discount name 2018-05-11 17:21:02 +05:30
Arvind Tiwari 73e3dce8d4 order detail page font format 2018-05-11 17:18:19 +05:30
Arvind Tiwari f3ffbd96e5 translation fix 2018-05-10 21:26:47 +05:30
Arvind Tiwari b351cb9aa0 translation fix 2018-05-10 21:25:38 +05:30
Arvind Tiwari 55cbe3244a fix testing error 2018-05-07 09:14:31 +05:30
Arvind Tiwari 0fdb88b8aa invoice discount amount fix 2018-05-07 07:50:32 +05:30
Arvind Tiwari 3d2ce27954 fix discount amount 2018-05-07 06:29:53 +05:30
Arvind Tiwari eeed9b2e72 discount name in templates 2018-05-07 06:25:50 +05:30
Arvind Tiwari 2ff8c25034 exclude discount from total price 2018-05-07 06:11:44 +05:30
Arvind Tiwari 144d885fbd Merge branch 'master' into task/4559/calculator_discount 2018-05-07 05:23:50 +05:30
Arvind Tiwari 7a72cc02ab translations 2018-05-07 05:22:05 +05:30
Arvind Tiwari f8dc2c6bbe discount option added to calculator 2018-05-07 05:07:58 +05:30
Arvind Tiwari 25ef657c62
Update cms_plugins.py 2018-05-01 18:15:56 +05:30
Arvind Tiwari 4d2d337651
Release 1.8 2018-05-01 16:44:15 +05:30
Arvind Tiwari ccf55acbaf
Update navbar_dropdown.html 2018-05-01 04:36:29 +05:30
Arvind Tiwari 6ff03d8f48
Merge pull request #627 from tiwariav/task/dcl_calculator_on_non_cms_pages
Task/4527 dcl calculator on non-cms pages
2018-05-01 02:35:22 +05:30
Arvind Tiwari d6db984156
Update ungleich.css 2018-05-01 00:48:31 +05:30
Arvind Tiwari 35fb872dc1
Update Changelog 2018-05-01 00:00:05 +05:30
Arvind Tiwari 86188b1fb0
Merge pull request #626 from tiwariav/task/4474/slider_padding
Task/4474 cms header slider padding
2018-04-30 23:52:23 +05:30
Arvind Tiwari 6bb65d8e8d
Merge pull request #628 from tiwariav/bugfix/unglieich_cms_template_favicon
bugfix/ add favicon extension to ungleich cms template
2018-04-30 21:20:41 +05:30
Arvind Tiwari 34df86fb90 add favicon extension to ungleich_template 2018-04-28 00:56:11 +05:30
Arvind Tiwari 89ed869780 hosting calculator styles 2018-04-25 23:31:27 +05:30
Arvind Tiwari 0c4c945ec3 flake8 fix 2018-04-25 17:51:36 +05:30
Arvind Tiwari 14548b2f01 flake8 refacoring 2018-04-25 17:26:31 +05:30
Arvind Tiwari 91a65e88ec hosting vm payment price fix 2018-04-25 16:08:05 +05:30
Arvind Tiwari f66d768ecb hosting payment page 2018-04-25 15:55:58 +05:30
Arvind Tiwari 3b3b73a2ce alignment calculator plugin 2018-04-25 15:08:28 +05:30
Arvind Tiwari 3bf064a017 add calculator placeholder to cms_integration 2018-04-25 14:52:25 +05:30
Arvind Tiwari 91f1c1ef06
Update Changelog 2018-04-21 23:05:23 +05:30
Arvind Tiwari 67a6c8f2c2
Update Changelog 2018-04-21 22:59:00 +05:30
Arvind Tiwari 49c0406296
Merge pull request #625 from pcoder/bugfix/4481/fix_DE_blog_pages_500_error
Bugfix/4481/fix de blog pages 500 error
2018-04-21 22:54:18 +05:30
Arvind Tiwari 526426c6c0
Merge pull request #623 from pcoder/task/4370/ungleich_comic
Task/4370/ungleich comic
2018-04-21 22:47:08 +05:30
Arvind Tiwari 1a692bc88f merge master 2018-04-21 22:39:44 +05:30
Arvind Tiwari 3b6c2b9d4e fix vm_id default 2018-04-21 22:27:43 +05:30
Arvind Tiwari a5bd8347e8 merged master 2018-04-21 21:01:42 +05:30
Arvind Tiwari 45bd853a20
Update Changelog 2018-04-20 21:09:16 +05:30
Arvind Tiwari d21e2a8abe
Merge pull request #618 from tiwariav/task/4297/cms_favicon
Task/4297 dcl cms favicon as page attribute
2018-04-20 20:59:17 +05:30
Arvind Tiwari 3d0505330c
Merge pull request #620 from tiwariav/bugfix/blog_top_menu
bugfix/ cms blog top menu
2018-04-20 20:58:59 +05:30
Arvind Tiwari b660ac5ed4 merge master 2018-04-20 20:37:34 +05:30
Arvind Tiwari 564f7a5be8 merge migrations 2018-04-20 20:36:28 +05:30
Arvind Tiwari 275ea47e41 merge master 2018-04-20 20:30:45 +05:30
Arvind Tiwari 71a37972d3 Merge remote-tracking branch 'ungleich/master' 2018-04-20 20:29:09 +05:30
Arvind Tiwari 736253feda refactor imports 2018-04-20 20:27:26 +05:30
Arvind Tiwari 3debf34118 remove vm creation to util function 2018-04-20 20:25:24 +05:30
Arvind Tiwari 74554f1c24
Merge pull request #622 from pcoder/task/4379/price_without_vat
Task/4379/Price without VAT
2018-04-20 19:04:10 +05:30
Arvind Tiwari 8f6260b063
Update _calculator_form.html 2018-04-20 17:54:23 +05:30
Arvind Tiwari 1e97d0ba38
Update cms_plugins.py 2018-04-20 17:51:46 +05:30
Arvind Tiwari a25bcc807f change header slider to container fluid 2018-04-20 17:04:02 +05:30
Arvind Tiwari da13903ba2 remove padding 2018-04-20 16:42:50 +05:30
M.Ravi 0b97ae69f5 Cleanup and reformat some code 2018-04-19 09:23:22 +02:00
M.Ravi a7f1f14dc7 Raise Http404 when we do not have a post for a given language 2018-04-19 09:12:54 +02:00
M.Ravi fae1c7fbeb Update hosting_order in a pythonic way 2018-04-19 07:26:34 +02:00
PCoder 80c3ac5346 Improve create_vm_task test 2018-04-19 01:09:25 +02:00
PCoder a3a8227007 Check if subscription metadata update response does not have errors 2018-04-19 01:08:52 +02:00
PCoder 791f48513a Organize imports 2018-04-19 01:07:59 +02:00
PCoder 8a659c153e Set VM_ID metadata to the created subscription 2018-04-19 00:52:38 +02:00
PCoder 1a7412f8ff stripe_utils: Add set_subscription_metadata method 2018-04-19 00:51:55 +02:00
PCoder bd875ffe7d Create hostingorder outside celery task 2018-04-18 23:50:52 +02:00
PCoder 4e3211b62f Make total and subtotal texts bold 2018-04-18 21:37:12 +02:00
PCoder 36c0b9a0a6 Differentiate price and total_price in hosting order_detail 2018-04-17 22:23:46 +02:00
PCoder 2ac1ac7d97 Add subtotal and VAT to hosting order detail too 2018-04-17 22:20:36 +02:00
PCoder 4c21110c00 Remove fuzzy and python-format 2018-04-17 21:47:02 +02:00
PCoder a454cd2522 Update datacenterlight's django.po 2018-04-17 21:38:53 +02:00
PCoder c2513dc7c3 Show vat_percent and subtotal for vat exclusive case 2018-04-17 21:38:28 +02:00
PCoder a50fa77c8a Update get_vm_price_with_vat: Return vat_percentage also 2018-04-17 21:36:08 +02:00
PCoder e4e7d93275 Fix flake8 errors 2018-04-17 20:50:41 +02:00
M.Ravi b3d36c1be3 Reformat code 2018-04-17 18:12:45 +02:00
M.Ravi d15a4da840 Check if child plugin instances exist before looping over them 2018-04-17 18:03:14 +02:00
M.Ravi 8b5b353e59 Reformat code and give proper name to comic urlconf 2018-04-17 17:15:55 +02:00
M.Ravi 80a568b2b3 Remove unwanted /comic/... urlconf 2018-04-17 17:14:39 +02:00
M.Ravi 7d211b3333 Modify PostListViewUngleich: Filter posts by category if specified 2018-04-17 16:17:46 +02:00
M.Ravi ff1d4f1a6f Rename tags to category, because thats how we filter posts by 2018-04-17 16:15:49 +02:00
PCoder ee35fbd784 Attempt to filter blogs by category 2018-04-17 11:49:01 +02:00
PCoder b218ee1662 Add urlconfs for comic 2018-04-17 11:47:56 +02:00
PCoder 3fca9dbb0d Fix a bug creating hostingorder 2018-04-16 05:03:48 +02:00
PCoder 1e76864821 Get vm price and vat and pass it to context 2018-04-16 04:46:43 +02:00
PCoder 731fef8ad9 Show VAT details in hosting/order_details if it is set 2018-04-16 04:39:21 +02:00
PCoder 6cc40cb67f virtual machine detail: show price upto 2 decimal places 2018-04-16 04:34:40 +02:00
PCoder 602ad1b2c0 Reformat code 2018-04-16 04:32:27 +02:00
PCoder 5738dc8e1b Virtual machine detail: use hostingorder for obtaining the price,
rather than the serializer
2018-04-16 04:32:08 +02:00
PCoder adbb8a269e Hostingorders: limit amount to 2 decimal places 2018-04-16 04:16:57 +02:00
PCoder b71fec7e61 Fix a bug: use vm_pricing instead of pricing 2018-04-16 04:00:48 +02:00
PCoder b103772c0d Pass vm_pricing if available when creating HostingOrder 2018-04-16 03:37:19 +02:00
PCoder 25c0694b6c Use vm_pricing for creating HostingOrder 2018-04-16 03:36:56 +02:00
PCoder a8339a23a9 Use get_vm_price_with_vat in hosting invoice 2018-04-16 03:27:54 +02:00
PCoder 0b9c67166e Rename variable name: disk_size -> ssd_size 2018-04-16 03:26:51 +02:00
PCoder 85b8c50ef1 Use proper decimal arithmetic to compute price and vat 2018-04-16 03:25:36 +02:00
PCoder c92bf30514 Associate HostingOrder and VMPricing 2018-04-16 03:23:09 +02:00
PCoder c5029d8752 Update datacenterlight's django.po 2018-04-16 01:48:39 +02:00
PCoder 95daa37670 Use total_price for amout to be charged, always 2018-04-16 01:32:38 +02:00
PCoder 4148eff03e Rename variable: amount_to_be_charged -> price 2018-04-16 01:31:16 +02:00
PCoder d0276f22c1 Reorganize imports 2018-04-16 01:30:02 +02:00
PCoder 1b37eed53d Include pricing_name in the email sent to admin 2018-04-16 01:29:07 +02:00
PCoder 8e28756bd8 Check if we have total_price (indicates that we have VAT excluded
price);hence,use it as final_price. if not use the previous casei and use it as final_price. If not use the preivous case
2018-04-16 01:27:42 +02:00
PCoder 6d6a8ea597 Refactor: reuse final_price variable 2018-04-16 01:24:21 +02:00
PCoder 903336a46f Show 2 upto decimal places in order detail 2018-04-16 00:59:42 +02:00
PCoder ec753eb0d5 Improve string representation of VMPricing 2018-04-16 00:50:34 +02:00
PCoder 68032ec184 Update migration file 2018-04-16 00:49:55 +02:00
PCoder 1cb3fa80cf Improve logger error 2018-04-16 00:29:43 +02:00
PCoder 896984ef1f Set vm_pricing context from plugins value 2018-04-16 00:29:23 +02:00
PCoder 665fa7479b Add 0019_auto_20180415_2129.py 2018-04-15 23:32:14 +02:00
PCoder 09a03783b7 Merge branch 'master' into task/4379/price_without_vat 2018-04-15 23:27:36 +02:00
PCoder 465431d858 Add DCLCustomPricingPlugin 2018-04-15 23:24:15 +02:00
PCoder 45fc0dbaac Add .orig to .gitignore 2018-04-15 23:23:42 +02:00
PCoder fc0bc52ea3 Rename model
DCLCalculatorPluginModel -> DCLCustomPricingModelModel
2018-04-15 23:19:18 +02:00
PCoder 6575ff1afe Increase decimal_places for all VMPricing fields 2018-04-15 23:07:50 +02:00
PCoder 069ba34f94 Remove old migration file 2018-04-15 23:07:07 +02:00
PCoder d296ce72cb Register VMPricing with admin site 2018-04-15 21:37:51 +02:00
PCoder 40b984be15 Update order_detail landing template 2018-04-15 21:04:06 +02:00
PCoder 23bd0fa147 Pass context params to various landing templates 2018-04-15 21:03:31 +02:00
PCoder 957cec00a0 Add get_vm_price_with_vat method 2018-04-15 20:59:31 +02:00
PCoder e9a883bf2e Fix a bug: use ssd_unit_price instead of sdd_unit_price 2018-04-15 20:57:10 +02:00
PCoder 962c96067f Add get_vm_pricing_by_name VMPricing method 2018-04-15 20:55:39 +02:00
PCoder 63a12ffe06 Use updated get_vm_price method 2018-04-15 13:42:39 +02:00
PCoder 558e187e11 Update text: including/excluding VAT 2018-04-15 13:39:41 +02:00
PCoder c738888ab2 Set vm unit price parameters from the passed context 2018-04-15 13:39:01 +02:00
PCoder aa55c1e868 Update main.js to compute total from the unitprice's defined in the window context 2018-04-15 13:37:06 +02:00
PCoder 82a2014fa5 Pass vm_pricing context from default VMPricing object 2018-04-15 13:34:55 +02:00
PCoder 74393ac6ac Optimize imports 2018-04-15 13:32:53 +02:00
PCoder 283a0d25d1 Update get_vm_price method to use pricing defined in VMPricing 2018-04-15 13:31:55 +02:00
PCoder 3f4156ed8e Update Changelog 2018-04-13 20:52:58 +02:00
Pcoder 4cea97c5d6
Merge pull request #617 from pcoder/bugfix/load_blog_on_all_domains
Make /blog available on all domains
2018-04-13 20:48:58 +02:00
Arvind Tiwari 7d05ab5f5f
Update Changelog 2018-04-13 18:53:21 +05:30
Arvind Tiwari 00be159285
Merge pull request #621 from tiwariav/task/4376/cms_promo_section_link_color
Task/4376 cms promo link color
2018-04-13 18:44:51 +05:30
Arvind Tiwari 9cf89f6481
Merge pull request #619 from tiwariav/task/4367/dcl_email_logo_resolution
Task/4367 dcl email logo resolution
2018-04-13 18:44:20 +05:30
Arvind Tiwari 403f9b5a08 change logo in hosting emails 2018-04-13 17:55:08 +05:30
Arvind Tiwari 5b8b40f967 restore ungleich style 2018-04-13 17:49:27 +05:30
Arvind Tiwari 2536f94058 django parler config fix 2018-04-13 17:44:00 +05:30
Arvind Tiwari ca480ce9c8 darker shade for hover 2018-04-13 16:23:17 +05:30
Arvind Tiwari 479d6fbd4f change promo link color 2018-04-13 15:45:06 +05:30
Arvind Tiwari 5679289329 replaced cms_menu with static menu 2018-04-12 22:24:50 +05:30
PCoder 0ea9051de1 Change the name of the DCL Calculator Plugin
DCL Caclulator Plugin -> DCL Calculator Section Plugin
Note: We do not change the plugin name itself because it causes
data loss
2018-04-12 08:38:14 +02:00
PCoder 3e1d5ba0e2 Improve string representation of VMPricing object 2018-04-12 08:03:19 +02:00
PCoder d50f282057 Add create_vm_pricing management command 2018-04-12 08:01:43 +02:00
PCoder 588f513f2a Remove initial VMPricing code from migration 2018-04-12 07:59:04 +02:00
Arvind Tiwari c0c2dc5c37 larger image used in email 2018-04-12 09:26:30 +05:30
Arvind Tiwari ebba6d3795 cms page extension favicon 2018-04-12 08:56:24 +05:30
M.Ravi be72b9628c Add djangocms_blog namespace and urls 2018-04-10 13:21:58 +02:00
PCoder 1116812a99 Correct hdd price in VMPricing init 2018-04-09 21:40:03 +02:00
PCoder 76c9b20cc9 Add VMPricing init migration 2018-04-09 21:34:09 +02:00
PCoder 4d6fdf2de9 Add DCLCalculatorPluginModel and VMPricing models 2018-04-09 21:32:53 +02:00
PCoder b9d44b510f Merge branch 'master' into task/4379/price_without_vat 2018-04-09 21:20:24 +02:00
PCoder dd30542f9f Use DCLCalculatorPluginModel in DCLCalculatorPlugin 2018-04-09 21:18:50 +02:00
PCoder c7afbb32c0 Add DCLCalculatorPluginModel 2018-04-09 21:18:18 +02:00
PCoder d07cc41d0a Update VMPricing and add get_default_pricing class method 2018-04-09 21:17:48 +02:00
Arvind Tiwari d53e70be2c
Update Changelog 2018-04-09 03:30:37 +05:30
Arvind Tiwari a7db7e05b0
Merge pull request #615 from tiwariav/bugfix/4427/fix_navbar_logo_repeat
bugfix/4427 fix navbar logo repeat
2018-04-09 03:27:29 +05:30
Arvind Tiwari f30719f320
Merge pull request #613 from pcoder/bugfix/footer_links
Bugfix/footer links
2018-04-09 03:27:09 +05:30
Arvind Tiwari a306f59327
Update mixins.py 2018-04-09 03:12:41 +05:30
Arvind Tiwari d1347afaff update changelog 2018-04-09 03:10:32 +05:30
Arvind Tiwari 6e95e3d885 merge master 2018-04-09 03:09:05 +05:30
Arvind Tiwari 79b5ff6e2c
Merge pull request #616 from tiwariav/bugfix/4396/ungleich_favicon
Bugfix/4396 ungleich favicon
2018-04-09 03:08:03 +05:30
Arvind Tiwari 5dd29bb09e
Update mixins.py 2018-04-09 03:02:50 +05:30
Arvind Tiwari 4305c5cc41 fix logo link url to ungleich home 2018-04-09 02:46:09 +05:30
Arvind Tiwari 600cc7899a mobile visibility 2018-04-09 02:34:34 +05:30
Arvind Tiwari f9ce871c5d update changelog 2018-04-09 02:18:34 +05:30
Arvind Tiwari f4b6949d39 corrected favicon link on pages 2018-04-09 02:16:57 +05:30
Arvind Tiwari e6629a1357 update changelog 2018-04-09 02:00:42 +05:30
Arvind Tiwari 774b13034d
Merge pull request #614 from tiwariav/bugfix/dcl_migration
bugfix/ dcl migrations
2018-04-09 01:54:37 +05:30
Arvind Tiwari ff1908d2e6 added navbar-transparent styles to glasfaser.css 2018-04-09 01:52:49 +05:30
Arvind Tiwari fb7ccff584 remove old migrations from squashed migration 2018-04-09 01:11:45 +05:30
PCoder 2a08c1efd3 Merge branch 'master' into task/4379/price_without_vat 2018-04-08 13:15:41 +02:00
PCoder 30d7785700 Redirect to DCL home on hosting footer for all domains 2018-04-08 08:36:00 +02:00
PCoder c3dff3155e Redirect the contact link to dcl always 2018-04-08 00:29:39 +02:00
PCoder 698cf7f1ee Fix link in signup message 2018-04-07 13:16:36 +02:00
PCoder 7b1cb975df Fix Method Resolution Order 2018-04-07 13:15:44 +02:00
PCoder 178ff8da38 CMS home page can now be accessed at /cms/ for all sites 2018-04-07 13:13:55 +02:00
PCoder c6e3963303 Update footer links in dcl _footer.html 2018-04-07 12:55:32 +02:00
PCoder f911d6b403 Add HostingContextMixin to various hosting views 2018-04-07 12:53:53 +02:00
PCoder 399c91d636 Add HostingContextMixin that sets the REDIRECT_TO_CMS and
MULTISITE_CMS_FALLBACK urls
2018-04-07 12:51:49 +02:00
PCoder ffe4eb4123 Use REDIRECT_TO_CMS and MULTISITE_CMS_FALLBACK to construct
/hosting footer urls
2018-04-07 10:25:27 +02:00
Pcoder 56f6c87fab
Merge pull request #612 from pcoder/bugifix/cms_redirect_for_multitenant_urls_also
bugfix/Redirect multi-tenant urls to /cms also
2018-04-07 00:13:14 +02:00
PCoder 24a9b8ab78 Redirect multi-tenant urls to /cms also 2018-04-07 00:13:24 +02:00
PCoder c167e1ca94 Update Changelog for 1.6.4 2018-04-06 22:31:39 +02:00
PCoder 9fca25ec5a Update Changelog 2018-04-06 20:28:15 +02:00
Pcoder dbc702ebcf
Merge pull request #600 from pcoder/task/4362/fix_need_of_cms_dummy_page
Task/4362/Remove conflicting cms/ urlconf
2018-04-06 20:13:25 +02:00
PCoder 2d5927bc31 Merge branch 'master' into task/4362/fix_need_of_cms_dummy_page 2018-04-06 08:13:47 +02:00
PCoder 7fe5c3f88c Merge branch 'master' into task/4379/price_without_vat 2018-04-06 00:53:29 +02:00
PCoder 28de423a14 Add VMPricing model 2018-04-06 00:51:44 +02:00
Arvind Tiwari 1e81587551
Update Changelog 2018-04-06 03:20:44 +05:30
PCoder 5cb51db6d7 Update .travis.yml
Add code to compile .po files before beginning tests
2018-04-05 23:31:50 +02:00
Arvind Tiwari a00ce7605b
Merge pull request #608 from tiwariav/bugfix/4377/header_slider_btn_fix
Bugfix/4377 header slider btn fix
2018-04-04 18:43:46 +05:30
Arvind Tiwari 35decd208c merged master 2018-04-04 18:41:43 +05:30
Arvind Tiwari cb4391b885 Merge remote-tracking branch 'ungleich/master' 2018-04-04 18:41:14 +05:30
Arvind Tiwari 80f4ac52e8 update changelog 2018-04-04 18:36:45 +05:30
Arvind Tiwari bf2797eccd
Merge pull request #607 from tiwariav/task/4378/navbar_logo_multisite
Task/4378 navbar content on non-cms pages
2018-04-04 18:27:18 +05:30
Arvind Tiwari c5dba114df update changelog 2018-04-04 18:26:19 +05:30
Arvind Tiwari 7c13def3a2 header btn link fix 2018-04-04 17:55:19 +05:30
Arvind Tiwari 8a390d3661 Merge remote-tracking branch 'ungleich/master' 2018-04-04 17:48:18 +05:30
PCoder 1aa8319feb Change order of ungleich_page namespace 2018-04-04 01:05:05 +02:00
PCoder 035f5c39ae Format code 2018-04-04 00:35:25 +02:00
PCoder ae4a83bdb8 Remove name for cms.urls 2018-04-04 00:35:12 +02:00
PCoder 9b45b758c4 Add ungleich_page urlconf namespace again 2018-04-04 00:34:29 +02:00
PCoder 0ec29be4ec Use /cms for redirection instead of incorrect namespaces 2018-04-03 23:28:47 +02:00
Arvind Tiwari ae2dea993d remove whitespace 2018-04-04 02:27:30 +05:30
PCoder 69003c6913 Merge branch 'master' into task/4362/fix_need_of_cms_dummy_page 2018-04-03 22:51:07 +02:00
PCoder e0df641dbd Merge master into task/4362/fix_need_of_cms_dummy_page 2018-04-03 22:50:00 +02:00
Arvind Tiwari fe3fcb5100 flake8 fix 2018-04-03 22:51:04 +05:30
Arvind Tiwari 9fbdc56c41 Merge remote-tracking branch 'ungleich/master' into task/4378/navbar_logo_multisite 2018-04-03 22:45:03 +05:30
Arvind Tiwari d843c34d68 modified cms integrate to support separate content for each domain 2018-04-03 22:41:29 +05:30
Arvind Tiwari d3c7149098
Update Changelog 2018-04-01 19:11:51 +05:30
Arvind Tiwari 9511428c9d
Merge pull request #606 from tiwariav/task/4373/hosting_login_footer
Task/4373 hosting login footer links
2018-04-01 19:06:13 +05:30
Arvind Tiwari a1ac65bdee
Update Changelog 2018-04-01 19:05:48 +05:30
Arvind Tiwari 1e6fb26051 hosting login footer fix 2018-04-01 18:45:36 +05:30
PCoder 73c49aa964 Merge branch 'task/4362/fix_need_of_cms_dummy_page' of github.com:pcoder/dynamicweb into task/4362/fix_need_of_cms_dummy_page 2018-04-01 08:16:29 +02:00
PCoder 41d073da3b Remove conflicting cms/ urlconf 2018-04-01 08:14:50 +02:00
PCoder bf48dc33de Update Changelog 2018-03-31 01:46:40 +02:00
Pcoder 10f6000e9a
Merge pull request #605 from pcoder/bugfix/user_activation_email_style
Bugfix/user activation html email style + some add/correct some DE translations
2018-03-31 01:42:49 +02:00
PCoder fb29bd0128 [DCL] Add DE translations by Sanghee 2018-03-31 01:28:56 +02:00
PCoder aa9becdfe7 Reduce space between content and your dcl team 2018-03-31 00:09:49 +02:00
PCoder bf4f2f0f87 Correct a translation and add new translation 2018-03-30 23:47:27 +02:00
PCoder 07d15a8144 Include password reset link in the text 2018-03-30 23:46:29 +02:00
PCoder 74e69ff313 Add some space between account details and activate email linkpacebetween account details and activate email link 2018-03-30 23:45:11 +02:00
PCoder b64fe2cb1c Attempt to fix user activation html email template 2018-03-30 23:02:44 +02:00
Arvind Tiwari e19844d9ac
Update Changelog 2018-03-29 01:42:22 +05:30
Arvind Tiwari 141c6a7c0f
Update common.css 2018-03-29 01:24:18 +05:30
Arvind Tiwari 8708b3a513
Merge pull request #597 from tiwariav/task/4313/hosting_footer_text
Task/4313/hosting_footer_style_fix
2018-03-29 01:20:11 +05:30
Arvind Tiwari 1402c7fde1 footer padding fix 2018-03-29 01:19:10 +05:30
Arvind Tiwari 368fe0d08f merge master 2018-03-29 01:04:26 +05:30
Arvind Tiwari 8b4e0d847d
Merge pull request #601 from tiwariav/task/4315/cms_navbar_to_static
bugfix/ placeholder fields migration
2018-03-29 01:00:52 +05:30
Arvind Tiwari 29a2ee098b fix related name for placeholder fields 2018-03-29 00:58:08 +05:30
Arvind Tiwari 033e542cbc
Merge pull request #599 from tiwariav/task/4315/cms_navbar_to_static
Task/4315/cms navbar and footer integration to application pages
2018-03-29 00:45:40 +05:30
Arvind Tiwari 2b27d56e54 cmsintegrate management command 2018-03-29 00:43:38 +05:30
PCoder 8f6bae6bd6 Remove conflicting cms/ urlconf 2018-03-28 08:53:49 +02:00
Arvind Tiwari 8fc605f5b5 merge 2018-03-27 21:10:21 +05:30
Arvind Tiwari e7d7cf67b2 add footer and header placeholders 2018-03-27 21:04:44 +05:30
Arvind Tiwari 8beebbf487
Update Changelog 2018-03-27 20:57:46 +05:30
Arvind Tiwari 4de9272389
Merge pull request #598 from tiwariav/bugfix/remove_default_thousand_separator
bugfix/ remove default thousand seperator
2018-03-27 19:41:43 +05:30
Arvind Tiwari d52c061709 use humanize to add comma to numbers 2018-03-27 19:38:09 +05:30
Arvind Tiwari bc36849178 cms integration 2018-03-27 19:19:26 +05:30
Arvind Tiwari 52dafc99d3 ungleich gmbh -> ungleich glarus ag, settings page fix 2018-03-27 00:54:44 +05:30
Arvind Tiwari 251d4928e2
Update Changelog 2018-03-26 00:32:53 +05:30
Arvind Tiwari ba8719269e
Merge pull request #580 from tiwariav/task/3842/de_number_formatting_2
task/3842 de number formatting
2018-03-25 23:10:43 +05:30
Arvind Tiwari 9846cac6da
Merge pull request #596 from tiwariav/task/4266/dcl_cms_promotion_section
Task/4266/dcl cms promotion section
2018-03-25 22:52:23 +05:30
Arvind Tiwari 880ee3af4a padding fix for mobile 2018-03-25 22:16:46 +05:30
Arvind Tiwari bd23d9462b mobile font fix 2018-03-25 21:06:08 +05:30
Arvind Tiwari 412f1f1508 right section alignment fix 2018-03-25 21:01:59 +05:30
Arvind Tiwari ee65c61f95 promo heading font size reduced for mobile 2018-03-25 20:49:19 +05:30
Arvind Tiwari dfe47e43a2 lineheight fix and line break filter 2018-03-23 04:36:40 +05:30
Arvind Tiwari 3484b75d8b calculaotr plugin allows child promo and html plugin 2018-03-23 03:22:29 +05:30
Arvind Tiwari c3e83599a2 mobile side padding reduced 2018-03-23 02:54:24 +05:30
Arvind Tiwari a1d01fd1ef wide promo section text maxwidth 2018-03-23 01:47:03 +05:30
Arvind Tiwari 9a57c3341e sectioniconplugin placement fix 2018-03-23 01:27:51 +05:30
Arvind Tiwari 414b783983 fix unreferenced migrations 2018-03-23 01:18:03 +05:30
Arvind Tiwari d7fc4e686f fix migrations 2018-03-23 01:17:09 +05:30
Arvind Tiwari d74fdf9472 fix migrations 2018-03-23 01:14:57 +05:30
Arvind Tiwari 5e8f21c8e3 remove template tag 2018-03-23 01:07:53 +05:30
Arvind Tiwari ef1f19d698 migrations 2018-03-23 00:54:34 +05:30
Arvind Tiwari 8a3ddda93e promo section styling 2018-03-23 00:50:01 +05:30
Arvind Tiwari d45f9076b5 Merge branch 'master' into task/4266/dcl_cms_promotion_section 2018-03-22 23:43:39 +05:30
Arvind Tiwari a7193ee243
Update _navbar.html
removed from navbar
- why datacenterlight
- hightlights dropdown

changed contact url to absolute dcl cms landing page
2018-03-22 04:04:40 +05:30
Arvind Tiwari adee93ca12 merge master 2018-03-22 03:49:06 +05:30
Arvind Tiwari d454dd4b92
Merge pull request #595 from tiwariav/task/dcl_url_reditect_to_cms
bugfix/ dcl redirect to cms
2018-03-22 03:33:57 +05:30
Arvind Tiwari 6c27e0e6ff redirect form view instead of url 2018-03-22 03:30:43 +05:30
Arvind Tiwari 5184a25215
Merge pull request #594 from tiwariav/task/dcl_url_reditect_to_cms
redirect old dcl landing to cms dcl landing
2018-03-22 03:13:11 +05:30
Arvind Tiwari 748a74f38b redirect from static dcl landing to cms landing 2018-03-22 03:11:20 +05:30
Arvind Tiwari 7654cf55aa
Merge pull request #593 from tiwariav/bugfix/dcl_cms_plugins
Bugfix/ dcl cms plugins
2018-03-22 02:52:08 +05:30
Arvind Tiwari f1a77d65c4 link target with absolute path on same page scrolls 2018-03-22 02:43:53 +05:30
Arvind Tiwari 50312db5f8 Merge remote-tracking branch 'ungleich/master' into bugfix/dcl_cms_plugins 2018-03-22 02:29:54 +05:30
Arvind Tiwari 102978f879 contact section heading 2018-03-22 02:29:32 +05:30
Arvind Tiwari 8f895a1d5a
Update contact.html 2018-03-22 01:59:45 +05:30
Arvind Tiwari f9bedf20e0
Update navbar.html
change default url to `/` from `datacenterlight:index`
2018-03-22 01:18:21 +05:30
Arvind Tiwari 389d16931b
Update Changelog 2018-03-22 01:04:41 +05:30
Arvind Tiwari 7ef37de084
Merge pull request #592 from tiwariav/bugfix/dcl_cms_section_iamge_caption
Bugfix/ dcl cms section image caption
2018-03-22 00:59:30 +05:30
Arvind Tiwari 049cf5afa2 fix dcl navbar link navigation 2018-03-22 00:52:59 +05:30
Arvind Tiwari bbfb37dd8f new plugins section 2018-03-22 00:52:06 +05:30
Arvind Tiwari db99a40059 break long words to multiple lines 2018-03-21 22:26:43 +05:30
Arvind Tiwari 849cdec0e2 unlocalize vm id 2018-03-21 01:29:28 +05:30
Arvind Tiwari c9ee9f6ec3 merge master 2018-03-21 01:09:47 +05:30
Arvind Tiwari d65e678ef7
Merge pull request #591 from tiwariav/task/4278/dcl_cms_navbar_and_header
task/4278 dcl cms navbar and header
2018-03-21 01:00:46 +05:30
Arvind Tiwari 076aaac604 banner list item text set max-width 2018-03-21 00:57:03 +05:30
Arvind Tiwari 759b96f25c banner list item text set max-width 2018-03-21 00:55:20 +05:30
Arvind Tiwari 39d67c84a1 fix lead text size 2018-03-21 00:46:22 +05:30
Arvind Tiwari e29865b310 fix banner list image size, lead text size 2018-03-21 00:27:35 +05:30
Arvind Tiwari b252211a69 header btn target accepts id, navbar optional lang dropdown 2018-03-20 02:34:20 +05:30
Arvind Tiwari b3742ed3f7
Update Changelog 2018-03-17 21:33:24 +05:30
Arvind Tiwari c7ff4423db
Update navbar.html 2018-03-17 13:12:26 +05:30
Arvind Tiwari 944946e59e
Update cms_models.py 2018-03-17 13:11:42 +05:30
Arvind Tiwari 6ae98b525d
Merge pull request #589 from tiwariav/task/dcl_cms_navbar_logo
bugfix/ navbar plugin model fix
2018-03-17 12:54:50 +05:30
Arvind Tiwari 07e7efd377 add related name to logo fields 2018-03-17 12:51:44 +05:30
Arvind Tiwari 3d52cab02b
Merge pull request #588 from tiwariav/task/dcl_cms_navbar_logo
Task/ dcl cms navbar logo
2018-03-17 12:36:43 +05:30
Arvind Tiwari 9b3b1d547e fix header trans btn hover color 2018-03-16 23:56:45 +05:30
Arvind Tiwari 449683c72f fix flake8 error 2018-03-16 23:50:33 +05:30
Arvind Tiwari 94d291da6d model views refactor 2018-03-16 23:42:40 +05:30
Arvind Tiwari 648905421d fix dcl dark logo 2018-03-16 23:36:02 +05:30
Arvind Tiwari 14b5d90514 dcl cms navbar logo 2018-03-16 15:29:24 +05:30
Arvind Tiwari fc659cab9f number localization format for de set same as en 2018-03-07 19:22:13 +05:30
PCoder c24d81042b Merge branch 'master' into task/3747/multiple_cards_support 2017-12-04 00:03:11 +01:00
PCoder 272e14f712 Update hosting django.po 2017-11-27 08:43:34 +01:00
PCoder 4a30438704 Update datacenterlight django.po 2017-11-27 08:38:10 +01:00
PCoder c438c0d8cb Add some de translations 2017-10-31 09:38:36 +01:00
PCoder f9bd849333 Remove redundant "previous" text 2017-10-31 09:05:49 +01:00
PCoder d8ce0f95c5 Change letter-spacing to 2px for btn-vm-contact 2017-10-31 09:02:07 +01:00
PCoder 3e08760e04 Change letter-spacing to 2px for settings-container buttons 2017-10-31 08:58:43 +01:00
PCoder a2a35a9475 Fix error accessing stripecustomer for user when it doesn't exist 2017-10-30 08:26:35 +01:00
PCoder 24d904288f Clear token and card form variables explicitly 2017-10-30 00:26:18 +01:00
PCoder 16b6ecb38c Clear session variables on payment error 2017-10-29 23:48:33 +01:00
PCoder 83dbae74e6 Add missing card_id to session variables to be cleared 2017-10-29 22:54:33 +01:00
PCoder 4be2796270 Clean up some unnecessary code 2017-10-29 22:40:21 +01:00
PCoder 62f30bf03c Remove some commented and unnecessary code 2017-10-29 22:03:19 +01:00
PCoder edac806c11 Some more refactoring 2017-10-29 21:56:29 +01:00
PCoder 3d8f81339b Refactor, reorganize some code 2017-10-29 21:49:15 +01:00
PCoder 618d0004f2 Rearrange code 2017-10-29 21:41:54 +01:00
PCoder af1690b846 Fix: obtaining stripe_customer in landing flow 2017-10-29 21:36:12 +01:00
PCoder 63eb7fc0e2 Rename contains to get_user_card_details 2017-10-29 21:31:11 +01:00
PCoder abe8c9efa5 Add error details to messages datacenterlight landing flow 2017-10-29 15:05:32 +01:00
PCoder bea3477d84 Handle errors related to payment by redirecting to payment page 2017-10-29 14:45:14 +01:00
PCoder 23e7edf7c2 Return True if associate_customer_card runs ok 2017-10-29 14:40:15 +01:00
PCoder 82ad9ac337 Add selected card handling in landing flow "Order confirmation" page 2017-10-29 13:17:26 +01:00
PCoder 2ffaee2d5b Add clear_items_from_list utility function 2017-10-29 13:14:29 +01:00
PCoder 248283b369 Show user's credit cards dynamically in landing payment page 2017-10-28 23:44:06 +02:00
PCoder bafb4e7b68 Remove hasCreditcard js code - not used anymore 2017-10-28 23:42:30 +02:00
PCoder db8dd9af54 Hosting payment: select card text update 2017-10-28 23:18:10 +02:00
PCoder 7e5cab2cc4 Fix PEP8 warnings 2017-10-28 22:44:58 +02:00
PCoder 2a8f02a197 Order by preferred and id fields for cards in settings page 2017-10-28 22:32:35 +02:00
PCoder cac00d4b9e Implement select default card functionality 2017-10-28 22:19:53 +02:00
PCoder dc8ea8d253 Make save_default_card method static 2017-10-28 22:18:54 +02:00
PCoder c47b5cdc72 Add form to send selected card in settings page 2017-10-28 22:17:46 +02:00
PCoder 175180e193 Show DEFAULT text against the default card in the settings page 2017-10-28 20:45:00 +02:00
PCoder 2a59a3336b Add metadata (vm_id) to Stripe subscription 2017-10-28 20:25:18 +02:00
PCoder d2ae94327a Associate card only if it has not already been done 2017-10-28 18:29:19 +02:00
PCoder 06a5cba50e Add contains utility method 2017-10-28 18:28:14 +02:00
PCoder 4be105a0a9 Set default card for newly added card in hosting flow 2017-10-28 16:48:03 +02:00
PCoder a17a5f66bc Set preferred = False for cards that are not preferred 2017-10-28 16:47:10 +02:00
PCoder b6d1e8df6b Set preferred for the card that's used for a subscription 2017-10-28 15:45:05 +02:00
PCoder 6212c9df50 Manage token and card_id properly in hosting flow 2017-10-28 15:30:42 +02:00
PCoder d14a643171 Set preferred field as in card_details 2017-10-28 15:26:15 +02:00
PCoder 8759e2a4b5 Save default card only if Stripe's default_source is not None 2017-10-28 15:14:42 +02:00
PCoder 73169e825d Add set_as_default parameter for associate_customer_card method 2017-10-28 15:13:38 +02:00
PCoder 38168e8f8f Allow use of previous cards in hosting flow 2017-10-27 00:45:26 +02:00
PCoder bd7db30633 Add card hidden field to BillingAddressForm 2017-10-27 00:43:07 +02:00
PCoder 1374eaf1a2 Rename id to stripe_api_cus_id 2017-10-27 00:41:49 +02:00
PCoder a6d28bff86 Add set_default_card and set_default_card_from_stripe methods 2017-10-27 00:40:38 +02:00
M.Ravi 4dd407da67 Use card_id as session variable instead of token and customer 2017-10-26 17:57:11 +02:00
M.Ravi 780fa6cb60 Add get_or_create_user_card_detail method 2017-10-26 17:56:12 +02:00
M.Ravi 85d19c004b Remove redundant code in check_customer 2017-10-26 15:21:29 +02:00
M.Ravi 692f82cba4 Code rearrange 2017-10-26 15:15:27 +02:00
M.Ravi ef9dc446db Use hasattr to check if a user already has a stripecustomer object 2017-10-26 15:00:54 +02:00
M.Ravi 8d2c120b43 Associate newly added card with customer from hosting 2017-10-26 12:29:16 +02:00
PCoder de5035d12e Add id_card input element 2017-10-22 00:21:08 +02:00
PCoder 21bb336166 Add js code to submit id_card 2017-10-22 00:20:40 +02:00
PCoder 8351b1bf8b Use refactored get_all_cards_list function 2017-10-21 23:14:55 +02:00
PCoder bf91bf3822 Change text: delete card to remove card 2017-10-21 23:14:05 +02:00
PCoder 7f4993c3f0 Update hosting payment template to use added cards 2017-10-21 23:12:53 +02:00
PCoder 303eb4112d Add doc. and return empty list for get_all_cards_list function 2017-10-21 21:34:54 +02:00
PCoder 14c7d6ac0e Refactor getting all cards to UserCardDetail.get_all_cards_list() 2017-10-21 21:27:21 +02:00
PCoder 8df72620d6 Add 'billing address updated' message in that case 2017-10-21 20:58:54 +02:00
PCoder 1e08ae5426 Stripe API: Use list() instead of all() as the former is deprecated 2017-10-21 20:46:29 +02:00
PCoder fb2056bf95 Update stripe_id for a missing case 2017-10-21 20:45:00 +02:00
PCoder 600b549704 Handle case where a Stripe account may have been deleted via dashboard 2017-10-21 20:39:00 +02:00
PCoder 75e90dbacd Refactor and add messages 2017-10-21 20:37:50 +02:00
PCoder be8181ec42 Add _messages template to hosting/settings.html 2017-10-21 17:02:24 +02:00
PCoder 70b6bbdf2f Save card id as a parameter and use it to delete a source 2017-10-21 13:27:35 +02:00
PCoder 76b3785adc Merge branch 'master' into task/3747/multiple_cards_support 2017-10-21 12:04:15 +02:00
PCoder c664b44f2c Add functionality to delete user card 2017-10-21 00:02:35 +02:00
PCoder 255c8a1d80 Add UserCardDetail create method that assigns appropriate object permission 2017-10-21 00:00:21 +02:00
PCoder 51039a5cb3 Merge branch 'master' into task/3747/multiple_cards_support 2017-10-20 21:45:39 +02:00
M.Ravi 60cfc24319 Merge branch task/3766/credit_card_options from tiwariav into task/3747/multiple_cards_support 2017-10-19 20:24:28 +02:00
M.Ravi 68a34762b9 Merge branch 'master' into task/3747/multiple_cards_support 2017-10-19 20:17:39 +02:00
Arvind Tiwari 1839a1c27c translation re compilation 2017-10-17 00:35:51 +05:30
Arvind Tiwari 2a71be354b conflicts resolved 2017-10-17 00:27:50 +05:30
M.Ravi c939106a35 Prepare to handle delete card 2017-10-16 16:29:09 +02:00
M.Ravi bff37d6246 Add card id param to card details 2017-10-16 16:20:03 +02:00
M.Ravi 3d50868c6a Add url for card delete 2017-10-16 16:18:39 +02:00
M.Ravi de275c23ac Add modal for card delete 2017-10-16 16:18:17 +02:00
M.Ravi 619f37829e Now showing cards from UserCardDetails 2017-10-15 23:57:15 +02:00
M.Ravi 52791f5e48 Refactor cc_brand to brand 2017-10-15 23:56:38 +02:00
M.Ravi 3f9c1a68d1 Remove unused code 2017-10-15 23:38:18 +02:00
M.Ravi 74a1f82c30 Refactor handling post for settings card input form 2017-10-15 23:37:01 +02:00
M.Ravi a3ce43fd53 Add add_card_to_stripe_customer function 2017-10-15 23:32:16 +02:00
M.Ravi 83363f4701 Remove some lines 2017-10-15 21:46:26 +02:00
M.Ravi 7d69d8d5d4 Add get_cards_details_from_token function 2017-10-15 21:45:17 +02:00
M.Ravi 167e3589d4 Rename user to stripe_customer 2017-10-15 21:21:36 +02:00
M.Ravi a1d7b07e0c Relate UserCardDetail with StripeCustomer and add more fields to uniquely identify if a user already has a card 2017-10-15 21:19:13 +02:00
M.Ravi 9a9a764023 Add preferred card field to UserCardDetail 2017-10-15 20:06:56 +02:00
M.Ravi f3ca9110e1 Add UserCardDetail migration 2017-10-15 19:56:40 +02:00
M.Ravi 6420a9869b Add functionality to save UserCardDetail 2017-10-15 19:55:37 +02:00
M.Ravi f71c8e553d Add name to user details form button name 2017-10-15 19:54:01 +02:00
M.Ravi 1a25bbf11e Add name to card form button name 2017-10-15 19:51:58 +02:00
M.Ravi a4b63e220a Relate UserCardDetail to CustomUser and rename some fields 2017-10-15 19:49:44 +02:00
M.Ravi 707f1a8768 Add UserCardDetail model + Reorganize imports 2017-10-15 18:00:43 +02:00
M.Ravi e6f92d9ae4 Manual merge 2017-10-15 16:14:33 +02:00
Arvind Tiwari cc2e08aff5 translations 2017-10-06 02:02:22 +05:30
Arvind Tiwari ba8eaa2937 new card details option added 2017-10-06 01:59:18 +05:30
Arvind Tiwari bd3c59eb3e card_input template 2017-10-06 01:17:50 +05:30
Arvind Tiwari 6536991209 list all cards, card_input template 2017-10-06 01:17:35 +05:30
130 changed files with 5615 additions and 1975 deletions

3
.gitignore vendored
View File

@ -41,4 +41,5 @@ secret-key
/utils/optimize/
# to keep empty dirs
!.gitkeep
!.gitkeep
*.orig

View File

@ -9,6 +9,7 @@ env:
install: "pip install -r requirements.txt"
script:
- flake8
- python manage.py compilemessages
- python manage.py test -v 3
# - coverage run --source='.' manage.py test dynamicweb -v 3
# - coverage report

View File

@ -1,3 +1,98 @@
Next:
* bugfix: Use correct version of django-multisite (MR #676)
2.4.1: 2018-10-18
* bugfix: Update pycryptodome module from 3.4 to 3.6.6 (PR #674)
2.4: 2018-10-18
* #5681: [hosting,dcl] Allow admin to lower minimum RAM to 512 MB (PR #672)
2.3.1: 2018-10-17
* bugfix: [hosting, dcl] Show VAT percent rounded to 2 decimal places in the order confirmation page (PR #673)
2.3: 2018-10-08
* #5690: Generic payment page - allow admin to add a onetime/monthly product and the frontend for user to pay for this product (PR #666)
2.2.2: 2018-09-28
* #5721: Set calculator OS list in alphabetical order and set `Devuan Ascii` as the default (PR #668)
* bugfix: Fix some typos and correct DE translations (PR #667)
2.2.1: 2018-09-25
* feature: Change DCLNavbarPlugin to show login option only if set (PR #665)
* bugfix: Log opennebula errors and send proper message when vm terminate is not completed in the stipulated time (PR #648)
2.2: 2018-09-06
* bugfix: Include price in the Stripe plan name to make it distinct and to correct pricing since version 1.9
2.1.2: 2018-08-30
* bugfix: [blog, comic] Set blog rss feed for all blog templates
2.1.1: 2018-08-24
* #5487: [hosting] Add explicit warning message for teminating VM (PR #656)
* bugfix: [dg] Send email to admin on dg subscription and increase cc_brand field to 128 characters (PR #652)
* #5458: [admin] Make hostingorder more readable (PR #657)
* bugfix: [CMS templates] Set description meta field of ungleich template (was missing before) and set ungleich glarus ag uniformly as author of various CMS pages (PR #653)
* #5473: Ping a VM before saving ssh key of the user (PR #655)
2.1: 2018-08-21
* Bugfix: Increase CC brand name fields from 10 to 128 characters (PR #654)
2.0.5: 2018-08-08
* Fix IPv6 VM name in the billing invoice
2.0.4: 2018-08-07
* Add RSS feed link to the footer of the blog template (PR #651)
* #5308: [ipv6only] Fix - when creating a VM, the name begins with v6only (PR #649)
* #5293: Use `terminate-hard` action instead of `terminate` in the opennebula call to terminate a vm (PR #650)
2.0.3: 2018-07-18
* Remove unused /comic url (PR #644)
* #5126: Allow dynamicweb sites to be iframed on other by setting `X_FRAME_OPTIONS_ALLOW_FROM_URI` (PR #645)
2.0.2: 2018-07-14
* bugfix: [blog] Add missing content block in the blog_ungleich.html template file
2.0.1: 2018-07-14
* bugfix: [blog] Enable content/structure mode in blog page
2.0: 2018-07-07
* #3747: [dcl,hosting] Add multiple cards support (PR #530)
* #3934: [dcl,hosting] Create HostingOrder outside celery task and add and associate OrderDetail with HostingOrder (PR #624)
* #4890: [hosting] Manage SSH keys using IPv6 of the VM (PR #640)
* bugfix: Fix flake8 error that was ignored in release 1.9.1
1.9.1: 2018-06-24
* #4799: [dcl] Show selected vm templates only in calculator (PR #638)
* #4847: [comic] Add google analytics code for comic.ungleich.ch (PR #639)
* feature: add vm_type option to vm_template and dcl calculator to distinguish between public and ipv6only templates (PR #635)
1.9: 2018-05-16
* #4559: [cms] enable discount on cms calculator
1.8: 2018-05-01
* #4527: [hosting] cms calculator on non-cms pages for the hosting app
* bgfix: [dcl] navbar dropdown target fix
* bgfix: [hosting] login/signup pages footer link fix
1.7.2: 2018-04-30
* bgfix: [cms] add favicon extension to ungleich cms pages
* #4474: [cms] reduce heading slider side padding
1.7.1: 2018-04-21
* #4481: [blog] fix de blog pages 500 error
* #4370: [comic] new url /comic to show only comic blogs
1.7: 2018-04-20
* bgfix: [all] Make /blog available on all domains
* #4367: [dcl] email logo resolution fix
* #4376: [cms] dcl promo section plugin link color changed to brighter shade
* #4379: [dcl] pricing without VAT
* bgfix: [blog] fix top menu items to show only one item
* #4297: [cms] favicon as a page attribute for dcl template
1.6.5: 2018-04-08
* #4396: [ungleich] add favicon to ungleich blog
* #4327: [dcl] fix navbar logo repeat
* bgfix: [hosting] fix broken footer links
* bgfix: [dcl] remove ghost migrations from squashed migration
* bgfix: [cms] redirect multi-tenant urls to /cms also
1.6.4: 2018-04-06
* #4362: [cms] Fix the need of dummy home page for different CMS-based sites
1.6.3: 2018-04-05
* #4377: [cms] header btn external link fix
* #4378: [dcl cms] update CMS Integration to have different content for different domains
1.6.2: 2018-04-01
* bgfix: [dcl] Fix user activation email style; add/correct some DE text
* #4373: [dcl] update footer menu for pw reset/login/signup/activation request pages
1.6.1: 2018-03-28
* bgfix: fix header slider interval issue
* #4315: [cms] navbar consistency from cms page to static page
* #4313: [hosting] footer style fix
1.6: 2018-03-25
* #4266: [dcl cms] add promotional section plugin
* #3842: [dcl, hosting] change number formatting for all the numbers from german to english locale
1.5.5: 2018-03-22
* #4278: [dcl cms] edit options for cms navbar and header plugins
* bgfix: [dcl cms] fix link plugin issues and section image alignment
1.5.4: 2018-03-17
* bgfix: [dcl cms] update DCLNavbarPlugin to allow change of brand logo and url
1.5.3: 2018-03-16
* #4262: [dcl] Bugfix for incorrect template name
1.5.2: 2018-03-14
@ -55,7 +150,7 @@
* [cms] Introduce UngleichHeaderBackgroundImageAndTextSliderPlugin that allows to have scrolling images and texts
* [cms] Remove <p> tag for ungleich cms customer item template
1.2.12: 2017-12-09
* #3594: [digitalglarus] Remove white scroll bar on the right in mobile
* #3594: [digitalglarus] Remove white scroll bar on the right in mobile
* #3905: [ungleich] Update ungleich.ch header into a slider
* #3968: [ungleich] Fix navbar logo alignment
* [all] Enable logging custom modules

19
datacenterlight/admin.py Normal file
View File

@ -0,0 +1,19 @@
from django.contrib import admin
from cms.admin.placeholderadmin import PlaceholderAdminMixin
from cms.extensions import PageExtensionAdmin
from .cms_models import CMSIntegration, CMSFaviconExtension
from .models import VMPricing, VMTemplate
class CMSIntegrationAdmin(PlaceholderAdminMixin, admin.ModelAdmin):
list_display = ('name', 'domain')
class CMSFaviconExtensionAdmin(PageExtensionAdmin):
pass
admin.site.register(CMSIntegration, CMSIntegrationAdmin)
admin.site.register(CMSFaviconExtension, CMSFaviconExtensionAdmin)
admin.site.register(VMPricing)
admin.site.register(VMTemplate)

View File

@ -1,12 +1,56 @@
from djangocms_text_ckeditor.fields import HTMLField
from cms.extensions import PageExtension
from cms.extensions.extension_pool import extension_pool
from cms.models.fields import PlaceholderField
from cms.models.pluginmodel import CMSPlugin
from django import forms
from django.conf import settings
from django.contrib.postgres.fields import ArrayField
from django.contrib.sites.models import Site
from django.db import models
from django.utils.safestring import mark_safe
from djangocms_text_ckeditor.fields import HTMLField
from filer.fields.file import FilerFileField
from filer.fields.image import FilerImageField
# Models for CMS Plugins
from datacenterlight.models import VMPricing, VMTemplate
class CMSIntegration(models.Model):
name = models.CharField(
max_length=100, default='default',
help_text=(
'A unique name for the Integration. This name will be used to '
'fetch the Integration into pages'
)
)
footer_placeholder = PlaceholderField(
'datacenterlight_footer', related_name='dcl-footer-placeholder+'
)
navbar_placeholder = PlaceholderField(
'datacenterlight_navbar', related_name='dcl-navbar-placeholder+'
)
calculator_placeholder = PlaceholderField(
'datacenterlight_calculator',
related_name='dcl-calculator-placeholder+'
)
domain = models.ForeignKey(Site, null=True, blank=True)
class Meta:
unique_together = ('name', 'domain')
def __str__(self):
return self.name
class CMSFaviconExtension(PageExtension):
favicon = FilerFileField(related_name="cms_favicon_image")
extension_pool.register(CMSFaviconExtension)
# Models for CMS Plugins
class DCLSectionPluginModel(CMSPlugin):
heading = models.CharField(
blank=True, null=True, max_length=100,
@ -120,6 +164,36 @@ class DCLLinkPluginModel(CMSPlugin):
)
class DCLNavbarPluginModel(CMSPlugin):
logo_light = FilerImageField(
on_delete=models.CASCADE, null=True, blank=True,
help_text='Logo to be used on transparent navbar',
related_name="dcl_navbar_logo_light",
)
logo_dark = FilerImageField(
on_delete=models.CASCADE, null=True, blank=True,
help_text='Logo to be used on white navbar',
related_name="dcl_navbar_logo_dark",
)
logo_url = models.URLField(max_length=300, null=True, blank=True)
language_dropdown = models.BooleanField(
default=True,
help_text='Select to include the language selection dropdown.'
)
show_login_option = models.BooleanField(
default=True,
help_text='Uncheck this if you do not want to show login/dashboard.'
)
def get_logo_dark(self):
# used only if atleast one logo exists
return self.logo_dark.url if self.logo_dark else self.logo_light.url
def get_logo_light(self):
# used only if atleast one logo exists
return self.logo_light.url if self.logo_light else self.logo_dark.url
class DCLNavbarDropdownPluginModel(CMSPlugin):
target = models.CharField(
max_length=100, null=True, blank=True,
@ -134,7 +208,7 @@ class DCLNavbarDropdownPluginModel(CMSPlugin):
class DCLContactPluginModel(CMSPlugin):
heading = models.CharField(max_length=100, default="Contact", blank=True)
organization_name = models.CharField(
max_length=100, default="ungleich GmbH", blank=True
max_length=100, default="ungleich glarus ag", blank=True
)
email = models.EmailField(max_length=200, default="info@ungleich.ch")
address = models.CharField(
@ -150,7 +224,7 @@ class DCLContactPluginModel(CMSPlugin):
class DCLFooterPluginModel(CMSPlugin):
copyright_label = models.CharField(
max_length=100, default='ungleich GmbH', blank=True,
max_length=100, default='ungleich glarus ag', blank=True,
help_text='Name of the company alongside the copyright year'
)
@ -178,3 +252,113 @@ class DCLSectionImagePluginModel(CMSPlugin):
max_length=100, null=True, blank=True,
help_text='Optional caption for the image.'
)
class DCLSectionPromoPluginModel(CMSPlugin):
background_image = FilerImageField(
on_delete=models.CASCADE, null=True, blank=True,
help_text=('Optional background image for the Promo Section'),
related_name="dcl_section_promo_promo",
)
heading = models.CharField(
blank=True, null=True, max_length=100,
help_text='An optional heading for the Promo Section',
)
subheading = models.CharField(
blank=True, null=True, max_length=200,
help_text='An optional subheading for the Promo Section',
)
content = HTMLField()
html_id = models.SlugField(
blank=True, null=True,
help_text=(
'An optional html id for the Section. Required to set as target '
'of a link on page'
)
)
plain_heading = models.BooleanField(
default=False,
help_text='Select to keep the heading style simpler.'
)
text_center = models.BooleanField(
default=False,
help_text='Select to center align content on small screens.'
)
def __str__(self):
return '#' + self.html_id if self.html_id else str(self.pk)
def get_extra_classes(self):
extra_classes = ''
if self.text_center:
extra_classes += ' text-center'
if self.plain_heading:
extra_classes += ' promo-section-plain'
if self.background_image:
extra_classes += ' promo-with-bg'
return extra_classes
class MultipleChoiceArrayField(ArrayField):
"""
A field that allows us to store an array of choices.
Uses Django's Postgres ArrayField
and a MultipleChoiceField for its formfield.
"""
VMTemplateChoices = []
if settings.OPENNEBULA_DOMAIN != 'test_domain':
VMTemplateChoices = list(
(
str(obj.opennebula_vm_template_id),
(obj.name + ' - ' + VMTemplate.IPV6.title()
if obj.vm_type == VMTemplate.IPV6 else obj.name
)
)
for obj in VMTemplate.objects.all()
)
def formfield(self, **kwargs):
defaults = {
'form_class': forms.MultipleChoiceField,
'choices': self.VMTemplateChoices,
}
defaults.update(kwargs)
# Skip our parent's formfield implementation completely as we don't
# care for it.
# pylint:disable=bad-super-call
return super(ArrayField, self).formfield(**defaults)
class DCLCalculatorPluginModel(CMSPlugin):
pricing = models.ForeignKey(
VMPricing,
related_name="dcl_custom_pricing_vm_pricing",
help_text='Choose a pricing that will be associated with this '
'Calculator'
)
vm_type = models.CharField(
max_length=50, choices=VMTemplate.VM_TYPE_CHOICES,
default=VMTemplate.PUBLIC
)
vm_templates_to_show = MultipleChoiceArrayField(
base_field=models.CharField(
blank=True,
max_length=256,
),
default=list,
blank=True,
help_text="Recommended: If you wish to show all templates of the "
"corresponding VM Type (public/ipv6only), please do not "
"select any of the items in the above field. "
"This will allow any new template(s) added "
"in the backend to be automatically listed in this "
"calculator instance."
)
default_selected_template = models.CharField(
default="Devuan Ascii",
null=True,
max_length=128,
help_text="Write the name of the template that you need selected as"
" default when the calculator loads"
)
enable_512mb_ram = models.BooleanField(default=False)

View File

@ -1,14 +1,15 @@
from cms.plugin_base import CMSPluginBase
from cms.plugin_pool import plugin_pool
from cms.models.pluginmodel import CMSPlugin
from .cms_models import (
DCLBannerItemPluginModel, DCLBannerListPluginModel, DCLContactPluginModel,
DCLFooterPluginModel, DCLLinkPluginModel, DCLNavbarDropdownPluginModel,
DCLSectionIconPluginModel, DCLSectionImagePluginModel,
DCLSectionPluginModel,
DCLSectionPluginModel, DCLNavbarPluginModel,
DCLSectionPromoPluginModel, DCLCalculatorPluginModel
)
from .models import VMTemplate
from datacenterlight.utils import clear_all_session_vars
@plugin_pool.register_plugin
@ -19,7 +20,31 @@ class DCLSectionPlugin(CMSPluginBase):
render_template = "datacenterlight/cms/section.html"
cache = False
allow_children = True
child_classes = ['DCLSectionIconPlugin', 'DCLSectionImagePlugin']
child_classes = [
'DCLSectionIconPlugin', 'DCLSectionImagePlugin',
'DCLSectionPromoPlugin', 'UngleichHTMLPlugin', 'DCLCalculatorPlugin'
]
def render(self, context, instance, placeholder):
context = super(DCLSectionPlugin, self).render(
context, instance, placeholder
)
context['children_to_side'] = []
context['children_to_content'] = []
context['children_calculator'] = []
if instance.child_plugin_instances is not None:
right_children = [
'DCLSectionImagePluginModel',
'DCLSectionIconPluginModel',
]
for child in instance.child_plugin_instances:
if child.__class__.__name__ in right_children:
context['children_to_side'].append(child)
elif child.plugin_type == 'DCLCalculatorPlugin':
context['children_calculator'].append(child)
else:
context['children_to_content'].append(child)
return context
@plugin_pool.register_plugin
@ -42,19 +67,40 @@ class DCLSectionImagePlugin(CMSPluginBase):
require_parent = True
@plugin_pool.register_plugin
class DCLSectionPromoPlugin(CMSPluginBase):
module = "Datacenterlight"
name = "DCL Section Promo Plugin"
model = DCLSectionPromoPluginModel
render_template = "datacenterlight/cms/section_promo.html"
cache = False
@plugin_pool.register_plugin
class DCLCalculatorPlugin(CMSPluginBase):
module = "Datacenterlight"
name = "DCL Calculator Plugin"
model = DCLSectionPluginModel
model = DCLCalculatorPluginModel
render_template = "datacenterlight/cms/calculator.html"
cache = False
require_parent = True
def render(self, context, instance, placeholder):
clear_all_session_vars(context['request'])
context = super(DCLCalculatorPlugin, self).render(
context, instance, placeholder
)
context['templates'] = VMTemplate.objects.all()
ids = instance.vm_templates_to_show
if ids:
context['templates'] = VMTemplate.objects.filter(
vm_type=instance.vm_type
).filter(opennebula_vm_template_id__in=ids).order_by('name')
else:
context['templates'] = VMTemplate.objects.filter(
vm_type=instance.vm_type
).order_by('name')
context['instance'] = instance
context['min_ram'] = 0.5 if instance.enable_512mb_ram else 1
return context
@ -84,7 +130,7 @@ class DCLBannerItemPlugin(CMSPluginBase):
class DCLNavbarPlugin(CMSPluginBase):
module = "Datacenterlight"
name = "DCL Navbar Plugin"
model = CMSPlugin
model = DCLNavbarPluginModel
render_template = "datacenterlight/cms/navbar.html"
cache = False
allow_children = True

View File

@ -0,0 +1,24 @@
from cms.extensions.toolbar import ExtensionToolbar
from cms.toolbar_pool import toolbar_pool
from django.utils.translation import ugettext_lazy as _
from .cms_models import CMSFaviconExtension
@toolbar_pool.register
class CMSFaviconExtensionToolbar(ExtensionToolbar):
# defineds the model for the current toolbar
model = CMSFaviconExtension
def populate(self):
# setup the extension toolbar with permissions and sanity checks
current_page_menu = self._setup_extension_toolbar()
# if it's all ok
if current_page_menu:
# retrieves the instance of the current extension (if any) and the toolbar item url
page_extension, url = self.get_page_extension_admin()
if url:
# adds a toolbar item
current_page_menu.add_modal_item(
_('CMS Favicon'), url=url, disabled=not self.toolbar.edit_mode
)

View File

@ -8,65 +8,35 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-01-15 23:12+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2018-09-26 20:44+0000\n"
"PO-Revision-Date: 2018-03-30 23:22+0000\n"
"Last-Translator: b'Anonymous User <coder.purple+25@gmail.com>'\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Translated-Using: django-rosetta 0.8.1\n"
msgid "CMS Favicon"
msgstr ""
#, python-format
msgid "Your New VM %(vm_name)s at Data Center Light"
msgstr "Deine neue VM %(vm_name)s bei Data Center Light"
msgid "Enter name"
msgstr "Name"
msgid "All Rights Reserved"
msgstr "Alle Rechte vorbehalten"
msgid "Enter email"
msgstr "E-Mail-Adresse"
msgid "Toggle navigation"
msgstr "Umschalten"
msgid "Request Beta Access"
msgstr "Beantrage Beta-Zugang"
msgid "Login"
msgstr "Anmelden"
msgid "Request Sent"
msgstr "Anfrage verschickt"
msgid ""
"Thank you for your subscription! You will receive a confirmation mail from "
"our team"
msgid "Dashboard"
msgstr ""
"Vielen dank für Ihre Anmeldung. Sie erhalten in kürze eine Bestätigungsmail "
"von unserem Team"
msgid "VM hosting"
msgstr ""
msgid "month"
msgstr "Monat"
msgid "VAT included"
msgstr "MwSt. inklusive"
msgid "Hosted in Switzerland"
msgstr "Standort: Schweiz"
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 1 - 200."
msgstr "Bitte gib einen Wert von 1 bis 200 ein."
msgid "Please enter a value in range 10 - 2000."
msgstr "Bitte gib einen Wert von 10 bis 2000 ein."
msgid "GB Storage (SSD)"
msgstr "GB Storage (SSD)"
msgid "Continue"
msgstr "Weiter"
msgid "Thank you for contacting us."
msgstr "Nachricht gesendet."
@ -100,32 +70,6 @@ msgstr ""
msgid "SUBMIT"
msgstr "ABSENDEN"
msgid "Your Data Center Light Team"
msgstr "Dein Data Center Light Team"
msgid "Thank you for your request."
msgstr "Vielen Dank für Deine Anfrage."
msgid "You are one step away from being our beta tester!"
msgstr ""
"Sie sind nur noch einen Schritt davon entfernt, unser Beta-Tester zu werden!"
msgid ""
"Currently we are running our tests to make sure everything runs perfectly."
msgstr ""
"Momentan testen wir die Beta-Umgebung um sie für Ihren Gebrauch "
"sicherzustellen."
msgid ""
"In the meantime, we would like to ask you a little patience<br/> until our "
"team contacts you with beta access."
msgstr ""
"Wir werden dann sobald als möglich Ihren Beta-Zugang erstellen und Sie "
"daraufhin kontaktieren.Bis dahin bitten wir Sie um etwas Geduld."
msgid "Thank you!"
msgstr "Vielen Dank!"
msgid "Data Center Light Account Activation"
msgstr "Data Center Light Account Aktivierung"
@ -136,7 +80,7 @@ msgid ""
"#4382c8; font-weight: 400;\">here</a>."
msgstr ""
"Klicke <a href=\"%(base_url)s%(activation_link)s\"style=\"text-decoration: "
"none; color: #4382c8; font-weight: 400;\">here</a> um deinen Data Center "
"none; color: #4382c8; font-weight: 400;\">hier</a> um deinen Data Center "
"Light Account zu aktivieren."
msgid ""
@ -156,14 +100,26 @@ msgstr "Deine E-Mail-Adresse"
msgid "Password"
msgstr "Passwort"
msgid "You can reset your password here"
msgstr "Du kannst dein Passwort hier zurück setzen"
#, python-format
msgid ""
"You can reset your password <a href=\"%(base_url)s%(reset_password_url)s\" "
"style=\"text-decoration: none; color: #4382c8; font-weight: 400;\">here</a>."
msgstr ""
"Du kannst dein Passwort <a href=\"%(base_url)s%(reset_password_url)s\" style="
"\"text-decoration: none; color: #4382c8; font-weight: 400;\">hier</a> "
"zurücksetzen."
msgid "Your Data Center Light Team"
msgstr "Dein Data Center Light Team"
msgid ""
"You can copy and paste the following link into the address bar of your "
"browser to activate your Data Center Light account."
msgstr "Kopiere den folgenden Link in die Adressleiste deines Browsers."
msgid "You can reset your password here"
msgstr "Du kannst dein Passwort hier zurücksetzen"
msgid "Welcome to Data Center Light!"
msgstr "Willkommen beim Data Center Light!"
@ -178,23 +134,38 @@ msgstr "Unser Angebot beginnt bei 15 CHF pro Monat. Probier's jetzt aus!"
msgid "ORDER VM"
msgstr "VM BESTELLEN"
msgid "Home"
msgstr "Home"
msgid "Highlights"
msgid "VM hosting"
msgstr ""
msgid "Scale out"
msgstr "Skalierung"
msgid "month"
msgstr "Monat"
msgid "Reliable and light"
msgstr "Zuverlässig und leicht"
msgid "VAT included"
msgstr "MwSt. inklusive"
msgid "Pricing"
msgstr "Preise"
msgid "You save"
msgstr "Du sparst"
msgid "Order VM"
msgstr "VM bestellen"
msgid "Hosted in Switzerland"
msgstr "Standort: Schweiz"
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 1 - 200."
msgstr "Bitte gib einen Wert von 1 bis 200 ein."
msgid "Please enter a value in range 10 - 2000."
msgstr "Bitte gib einen Wert von 10 bis 2000 ein."
msgid "GB Storage (SSD)"
msgstr "GB Storage (SSD)"
msgid "Continue"
msgstr "Weiter"
msgid "Home"
msgstr "Home"
msgid "Contact"
msgstr "Kontakt"
@ -202,24 +173,12 @@ msgstr "Kontakt"
msgid "Terms of Service"
msgstr "Nutzungsbedingungen"
msgid "All Rights Reserved"
msgstr "Alle Rechte vorbehalten"
msgid "Toggle navigation"
msgstr "Umschalten"
msgid "Why Data Center Light?"
msgstr "Warum Data Center Light?"
msgid "Login"
msgstr "Anmelden"
msgid "Dashboard"
msgstr ""
msgid "Finally, an affordable VM hosting in Switzerland!"
msgstr "Endlich: bezahlbares VM Hosting in der Schweiz"
msgid "Highlights"
msgstr ""
msgid "I want it!"
msgstr "Das will ich haben!"
@ -250,6 +209,9 @@ msgstr ""
"mit FOSS (Free Open Source Software) arbeitet und wir daher auf "
"Lizenzgebühren verzichten können."
msgid "Scale out"
msgstr "Skalierung"
msgid ""
"We don't use special hardware. We use commodity hardware: we buy computers "
"that you buy. Just many more and put them in a cozy home for computers "
@ -259,6 +221,9 @@ msgstr ""
"erschwingliche Systeme. Bei grösserer Auslastung werden mehr "
"Standardkomponenten hinzugekauft und skalieren so das Datencenter."
msgid "Reliable and light"
msgstr "Zuverlässig und leicht"
msgid ""
"Our VMs are located in Switzerland, with reliable power supply and fast "
"internet connection. Our VM costs less thanks to our featherlight "
@ -328,6 +293,9 @@ msgstr "Registrieren"
msgid "Billing Address"
msgstr "Rechnungsadresse"
msgid "Make a payment"
msgstr ""
msgid "Your Order"
msgstr "Deine Bestellung"
@ -349,12 +317,32 @@ msgstr "Gesamt"
msgid "including VAT"
msgstr "inkl. Mehrwertsteuer"
msgid "excluding VAT"
msgstr "exkl. Mehrwertsteuer"
msgid "Month"
msgstr "Monat"
msgid "Discount"
msgstr "Rabatt"
msgid "Will be applied at checkout"
msgstr "wird an der Kasse angewendet"
msgid "Credit Card"
msgstr "Kreditkarte"
msgid ""
"Please select one of the cards that you used before or fill in your credit "
"card information below. We are using <a href=\"https://stripe.com\" target="
"\"_blank\">Stripe</a> for payment and do not store your information in our "
"database."
msgstr ""
"Bitte wähle eine der zuvor genutzten Kreditkarten oder gib Deine "
"Kreditkartendetails unten an. Die Bezahlung wird über <a href=\"https://"
"stripe.com\" target=\"_blank\">Stripe</a> abgewickelt. Wir speichern Deine "
"Kreditkartendetails nicht in unserer Datenbank."
msgid ""
"Please fill in your credit card information below. We are using <a href="
"\"https://stripe.com\" target=\"_blank\">Stripe</a> for payment and do not "
@ -364,31 +352,23 @@ msgstr ""
"\"https://stripe.com\" target=\"_blank\">Stripe</a> für die Bezahlung und "
"speichern keine Informationen in unserer Datenbank."
msgid ""
"You are not making any payment yet. After submitting your card information, "
"you will be taken to the Confirm Order Page."
msgstr ""
"Es wird noch keine Bezahlung vorgenommen. Die Bezahlung wird erst ausgelöst, "
"nachdem Du die Bestellung auf der nächsten Seite bestätigt hast."
msgid "Last"
msgstr "Letzten"
msgid "Card Number"
msgstr "Kreditkartennummer"
msgid "Type"
msgstr "Typ"
msgid "Expiry Date"
msgstr "Ablaufdatum"
msgid "SELECT"
msgstr "AUSWÄHLEN"
msgid "CVC"
msgstr ""
msgid "Add a new credit card"
msgstr "Eine neue Kreditkarte hinzufügen"
msgid "Card Type"
msgstr "Kartentyp"
msgid "NEW CARD"
msgstr "NEUE KARTE"
msgid ""
"You are not making any payment yet. After placing your order, you will be "
"taken to the Submit Payment Page."
msgstr ""
"Es wird noch keine Bezahlung vorgenommen. Die Bezahlung wird erst ausgelöst, "
"nachdem Du die Bestellung auf der nächsten Seite bestätigt hast."
msgid "New Credit Card"
msgstr "Neue Kreditkarte"
msgid "Processing"
msgstr "Weiter"
@ -397,24 +377,85 @@ msgid "Enter your credit card number"
msgstr "Deine Kreditkartennummer"
#, python-format
msgid "%(page_header_text)s"
msgstr ""
msgid "Date"
msgstr "Datum"
msgid "Billed to"
msgstr "Rechnungsadresse"
msgid "Payment method"
msgstr "Bezahlmethode"
msgid "ending in"
msgstr "endend in"
msgid "Order summary"
msgstr "Bestellungsübersicht"
msgid "Product"
msgstr "Produkt"
msgid "Amount"
msgstr ""
msgid "Description"
msgstr ""
msgid "Recurring"
msgstr ""
msgid "Subtotal"
msgstr "Zwischensumme"
msgid "VAT"
msgstr "Mehrwertsteuer"
msgid ""
"By clicking \"Place order\" this plan will charge your credit card account "
"with the fee of %(vm_price)sCHF/month"
"with %(total_price)s CHF/month"
msgstr ""
"Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit %(vm_price)sCHF "
"pro Monat belastet"
"Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit "
"%(vm_total_price)s CHF pro Monat belastet"
msgid ""
"By clicking \"Place order\" this payment will charge your credit card "
"account with a one time amount of %(total_price)s CHF"
msgstr ""
"Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit "
"%(vm_total_price)s CHF pro Monat belastet"
#, python-format
msgid ""
"By clicking \"Place order\" this plan will charge your credit card account "
"with %(vm_total_price)s CHF/month"
msgstr ""
"Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit "
"%(vm_total_price)s CHF pro Monat belastet"
msgid "Place order"
msgstr "Bestellen"
msgid "Thank you for order! Our team will contact you via email"
msgstr ""
"Vielen Dank für die Bestellung. Unser Team setzt sich sobald wie möglich mit "
"Dir via E-Mail in Verbindung."
msgid "Processing..."
msgstr "Abarbeitung..."
msgid "as soon as possible!"
msgid "Hold tight, we are processing your request"
msgstr "Bitte warten - wir verarbeiten Deine Anfrage gerade"
msgid "OK"
msgstr ""
msgid "Close"
msgstr ""
msgid "Some problem encountered. Please try again later."
msgstr "Ein Problem ist aufgetreten. Bitte versuche es später noch einmal."
msgid "Why Data Center Light?"
msgstr "Warum Data Center Light?"
msgid "Tech Stack"
msgstr "Tech Stack"
@ -500,6 +541,17 @@ msgstr "Ungültige RAM-Grösse"
msgid "Invalid storage size"
msgstr "Ungültige Speicher-Grösse"
#, python-brace-format
msgid "Incorrect pricing name. Please contact support{support_email}"
msgstr ""
#, python-brace-format
msgid "{user} does not have permission to access the card"
msgstr "{user} hat keine Erlaubnis auf diese Karte zuzugreifen"
msgid "An error occurred. Details: {}"
msgstr "Ein Fehler ist aufgetreten. Details: {}"
msgid "Confirm Order"
msgstr "Bestellung Bestätigen"
@ -513,6 +565,36 @@ msgstr ""
"Es ist ein Fehler bei der Zahlung betreten. Du wirst nach dem Schliessen vom "
"Popup zur Bezahlseite weitergeleitet."
#, python-brace-format
msgid "An error occurred while associating the card. Details: {details}"
msgstr ""
"Beim Verbinden der Karte ist ein Fehler aufgetreten. Details: {details}"
msgid "Confirmation of your payment"
msgstr ""
msgid " This is a monthly recurring plan."
msgstr ""
#, python-brace-format
msgid ""
"Hi {name},\n"
"\n"
"thank you for your order!\n"
"We have just received a payment of CHF {amount:.2f} from you.{recurring}\n"
"\n"
"Cheers,\n"
"Your Data Center Light team"
msgstr ""
msgid "Thank you for the payment."
msgstr "Danke für Deine Bestellung."
msgid ""
"You will soon receive a confirmation email of the payment. You can always "
"contact us at info@ungleich.ch for any question that you may have."
msgstr ""
msgid "Thank you for the order."
msgstr "Danke für Deine Bestellung."
@ -523,36 +605,88 @@ msgstr ""
"Deine VM ist gleich bereit. Wir senden Dir eine Bestätigungsemail, sobald Du "
"auf sie zugreifen kannst."
#~ msgid ""
#~ "You are not making any payment yet. After submitting your card "
#~ "information, you will be taken to the Confirm Order Page."
#~ msgstr ""
#~ "Es wird noch keine Bezahlung vorgenommen. Die Bezahlung wird erst "
#~ "ausgelöst, nachdem Du die Bestellung auf der nächsten Seite bestätigt "
#~ "hast."
#~ msgid "Card Number"
#~ msgstr "Kreditkartennummer"
#~ msgid "Expiry Date"
#~ msgstr "Ablaufdatum"
#~ msgid ""
#~ "You are not making any payment yet. After placing your order, you will be "
#~ "taken to the Submit Payment Page."
#~ msgstr ""
#~ "Es wird noch keine Bezahlung vorgenommen. Die Bezahlung wird erst "
#~ "ausgelöst, nachdem Du die Bestellung auf der nächsten Seite bestätigt "
#~ "hast."
#~ msgid "Pricing"
#~ msgstr "Preise"
#~ msgid "Order VM"
#~ msgstr "VM bestellen"
#~ msgid "Enter name"
#~ msgstr "Name"
#~ msgid "Enter email"
#~ msgstr "E-Mail-Adresse"
#~ msgid "Request Beta Access"
#~ msgstr "Beantrage Beta-Zugang"
#~ msgid "Request Sent"
#~ msgstr "Anfrage verschickt"
#~ msgid ""
#~ "Thank you for your subscription! You will receive a confirmation mail "
#~ "from our team"
#~ msgstr ""
#~ "Vielen dank für Ihre Anmeldung. Sie erhalten in kürze eine "
#~ "Bestätigungsmail von unserem Team"
#~ msgid "Thank you for your request."
#~ msgstr "Vielen Dank für Deine Anfrage."
#~ msgid "You are one step away from being our beta tester!"
#~ msgstr ""
#~ "Sie sind nur noch einen Schritt davon entfernt, unser Beta-Tester zu "
#~ "werden!"
#~ msgid ""
#~ "Currently we are running our tests to make sure everything runs perfectly."
#~ msgstr ""
#~ "Momentan testen wir die Beta-Umgebung um sie für Ihren Gebrauch "
#~ "sicherzustellen."
#~ msgid ""
#~ "In the meantime, we would like to ask you a little patience<br/> until "
#~ "our team contacts you with beta access."
#~ msgstr ""
#~ "Wir werden dann sobald als möglich Ihren Beta-Zugang erstellen und Sie "
#~ "daraufhin kontaktieren.Bis dahin bitten wir Sie um etwas Geduld."
#~ msgid "Thank you!"
#~ msgstr "Vielen Dank!"
#~ msgid "Thank you for order! Our team will contact you via email"
#~ msgstr ""
#~ "Vielen Dank für die Bestellung. Unser Team setzt sich sobald wie möglich "
#~ "mit Dir via E-Mail in Verbindung."
#~ msgid "Affordable VM hosting based in Switzerland"
#~ msgstr "Bezahlbares VM Hosting in der Schweiz"
#~ msgid "Processing..."
#~ msgstr "Abarbeitung..."
#~ msgid "Hold tight, we are processing your request"
#~ msgstr "Bitte warten - wir verbeiten Deine Anfrage gerade"
#~ msgid "Some problem encountered. Please try again later."
#~ msgstr "Ein Problem ist aufgetreten. Bitte versuche es später noch einmal."
#~ msgid "Submit"
#~ msgstr "Absenden"
#~ msgid "Date"
#~ msgstr "Datum"
#~ msgid "Billed To:"
#~ msgstr "Rechnungsadresse"
#~ msgid "Payment Method:"
#~ msgstr "Bezahlmethode"
#~ msgid "ending in"
#~ msgstr "endend in"
#~ msgid "Order summary"
#~ msgstr "Bestellungsübersicht"
#~ msgid "We are cutting down the costs significantly!"
#~ msgstr "Wir sorgen dafür, dass die Kosten für Dich signifikant abnehmen"

View File

@ -0,0 +1,21 @@
from django.core.management.base import BaseCommand
from datacenterlight.cms_models import CMSIntegration
class Command(BaseCommand):
help = '''Creates cms integration objects for datacenterlight'''
def handle(self, *args, **options):
self.create_cms_integration()
def create_cms_integration(self, site=None):
obj, created = CMSIntegration.objects.get_or_create(
name='default', domain=site
)
domain_name = site.domain if site else 'All Sites'
if created:
print('created the default CMSIntegration object for', domain_name)
else:
print(
'default CMSIntegration object already exists for', domain_name
)

View File

@ -0,0 +1,36 @@
from django.core.management.base import BaseCommand
from datacenterlight.models import VMPricing
class Command(BaseCommand):
help = '''Creates default VMPricing object'''
DEFAULT_VMPRICING_NAME = 'default'
def handle(self, *args, **options):
self.create_default_vm_pricing()
def create_default_vm_pricing(self):
obj, created = VMPricing.objects.get_or_create(
name=self.DEFAULT_VMPRICING_NAME,
defaults={
"vat_inclusive": True,
"cores_unit_price": 5,
"ram_unit_price": 2,
"ssd_unit_price": 0.6,
"hdd_unit_price": 0.01
}
)
if created:
print(
'Successfully created {} VMPricing object'.format(
self.DEFAULT_VMPRICING_NAME
)
)
else:
print(
'{} VMPricing exists already.'.format(
self.DEFAULT_VMPRICING_NAME
)
)

View File

@ -10,16 +10,28 @@ class Command(BaseCommand):
help = '''Fetches the VM templates from OpenNebula and populates the dcl
VMTemplate model'''
def get_templates(self, manager, prefix):
templates = manager.get_templates('%s-' % prefix)
dcl_vm_templates = []
for template in templates:
template_name = template.name.lstrip('%s-' % prefix)
template_id = template.id
dcl_vm_template = VMTemplate.create(
template_name, template_id, prefix
)
dcl_vm_templates.append(dcl_vm_template)
return dcl_vm_templates
def handle(self, *args, **options):
try:
manager = OpenNebulaManager()
templates = manager.get_templates()
dcl_vm_templates = []
for template in templates:
template_name = template.name.lstrip('public-')
template_id = template.id
dcl_vm_template = VMTemplate.create(template_name, template_id)
dcl_vm_templates.append(dcl_vm_template)
dcl_vm_templates.extend(
self.get_templates(manager, VMTemplate.PUBLIC)
)
dcl_vm_templates.extend(
self.get_templates(manager, VMTemplate.IPV6)
)
old_vm_templates = VMTemplate.objects.all()
old_vm_templates.delete()

View File

@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2018-03-17 07:19
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
import filer.fields.image
class Migration(migrations.Migration):
dependencies = [
('datacenterlight', '0012_dclcalculatorpluginmodel'),
('cms', '0014_auto_20160404_1908'),
]
operations = [
migrations.CreateModel(
name='DCLNavbarPluginModel',
fields=[
('cmsplugin_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='cms.CMSPlugin')),
('logo_url', models.URLField(blank=True, max_length=300, null=True)),
('logo_dark', filer.fields.image.FilerImageField(blank=True, help_text='Logo to be used on white navbar', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='dcl_navbar_logo_dark', to='filer.Image')),
('logo_light', filer.fields.image.FilerImageField(blank=True, help_text='Logo to be used on transparent navbar', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='dcl_navbar_logo_light', to='filer.Image')),
],
options={
'abstract': False,
},
bases=('cms.cmsplugin',),
),
]

View File

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2018-03-19 20:46
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('datacenterlight', '0013_dclnavbarpluginmodel'),
]
operations = [
migrations.AddField(
model_name='dclnavbarpluginmodel',
name='language_dropdown',
field=models.BooleanField(
default=True, help_text='Select to include the language selection dropdown.'),
),
]

View File

@ -0,0 +1,43 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2018-03-21 19:09
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
import djangocms_text_ckeditor.fields
import filer.fields.image
class Migration(migrations.Migration):
dependencies = [
('cms', '0014_auto_20160404_1908'),
('datacenterlight', '0013_dclnavbarpluginmodel'),
]
operations = [
migrations.CreateModel(
name='DCLSectionPromoPluginModel',
fields=[
('cmsplugin_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE,
parent_link=True, primary_key=True, serialize=False, to='cms.CMSPlugin')),
('heading', models.CharField(
blank=True, help_text='An optional heading for the Promo Section', max_length=100, null=True)),
('subheading', models.CharField(
blank=True, help_text='An optional subheading for the Promo Section', max_length=200, null=True)),
('content', djangocms_text_ckeditor.fields.HTMLField()),
('html_id', models.SlugField(
blank=True, help_text='An optional html id for the Section. Required to set as target of a link on page', null=True)),
('plain_heading', models.BooleanField(default=False,
help_text='Select to keep the heading style simpler.')),
('center_on_mobile', models.BooleanField(default=False,
help_text='Select to center align content on small screens.')),
('background_image', filer.fields.image.FilerImageField(blank=True, help_text='Optional background image for the Promo Section',
null=True, on_delete=django.db.models.deletion.CASCADE, related_name='dcl_section_promo_promo', to='filer.Image')),
],
options={
'abstract': False,
},
bases=('cms.cmsplugin',),
),
]

View File

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2018-03-22 19:22
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('datacenterlight', '0014_dclsectionpromopluginmodel'),
('datacenterlight', '0014_dclnavbarpluginmodel_language_dropdown'),
]
operations = [
migrations.RenameField(
model_name='dclsectionpromopluginmodel',
old_name='center_on_mobile',
new_name='text_center',
),
]

View File

@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2018-03-27 15:31
from __future__ import unicode_literals
import cms.models.fields
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('datacenterlight', '0015_auto_20180323_0011'),
('cms', '0014_auto_20160404_1908'),
]
operations = [
migrations.CreateModel(
name='CMSIntegration',
fields=[
('id', models.AutoField(auto_created=True,
primary_key=True, serialize=False, verbose_name='ID')),
('navbar_placeholder', cms.models.fields.PlaceholderField(editable=False, null=True,
on_delete=django.db.models.deletion.CASCADE, slotname='datacenterlight_navbar', to='cms.Placeholder')),
('footer_placeholder', cms.models.fields.PlaceholderField(editable=False, null=True,
on_delete=django.db.models.deletion.CASCADE, slotname='datacenterlight_footer', to='cms.Placeholder')),
('name', models.CharField(default='default',
help_text='A unique name for the Integration. This name will be used to fetch the Integration into pages', max_length=100, unique=True)),
],
),
]

View File

@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2018-03-28 19:26
from __future__ import unicode_literals
import cms.models.fields
from django.db import migrations
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('datacenterlight', '0016_cmsintegration'),
]
operations = [
migrations.AlterField(
model_name='cmsintegration',
name='footer_placeholder',
field=cms.models.fields.PlaceholderField(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='dcl-footer-placeholder+', slotname='datacenterlight_footer', to='cms.Placeholder'),
),
migrations.AlterField(
model_name='cmsintegration',
name='navbar_placeholder',
field=cms.models.fields.PlaceholderField(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='dcl-navbar-placeholder+', slotname='datacenterlight_navbar', to='cms.Placeholder'),
),
]

View File

@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2018-04-03 17:08
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('datacenterlight', '0017_auto_20180329_0056'),
('sites', '0002_alter_domain_unique'),
]
operations = [
migrations.AlterField(
model_name='dclcontactpluginmodel',
name='organization_name',
field=models.CharField(blank=True, default='ungleich glarus ag', max_length=100),
),
migrations.AlterField(
model_name='dclfooterpluginmodel',
name='copyright_label',
field=models.CharField(blank=True, default='ungleich glarus ag', help_text='Name of the company alongside the copyright year', max_length=100),
),
migrations.AddField(
model_name='cmsintegration',
name='domain',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='sites.Site'),
),
migrations.AlterField(
model_name='cmsintegration',
name='name',
field=models.CharField(default='default', help_text='A unique name for the Integration. This name will be used to fetch the Integration into pages', max_length=100),
),
migrations.AlterUniqueTogether(
name='cmsintegration',
unique_together=set([('name', 'domain')]),
),
]

View File

@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2018-04-15 22:36
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('cms', '0014_auto_20160404_1908'),
('datacenterlight', '0018_auto_20180403_1930'),
]
operations = [
migrations.CreateModel(
name='DCLCustomPricingModel',
fields=[
('cmsplugin_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='cms.CMSPlugin')),
],
options={
'abstract': False,
},
bases=('cms.cmsplugin',),
),
migrations.CreateModel(
name='VMPricing',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255, unique=True)),
('vat_inclusive', models.BooleanField(default=True)),
('vat_percentage', models.DecimalField(blank=True, decimal_places=5, default=0, max_digits=7)),
('cores_unit_price', models.DecimalField(decimal_places=5, default=0, max_digits=7)),
('ram_unit_price', models.DecimalField(decimal_places=5, default=0, max_digits=7)),
('ssd_unit_price', models.DecimalField(decimal_places=5, default=0, max_digits=7)),
('hdd_unit_price', models.DecimalField(decimal_places=6, default=0, max_digits=7)),
],
),
migrations.AddField(
model_name='dclcustompricingmodel',
name='pricing',
field=models.ForeignKey(help_text='Choose a pricing that will be associated with this Calculator', on_delete=django.db.models.deletion.CASCADE, related_name='dcl_custom_pricing_vm_pricing', to='datacenterlight.VMPricing'),
),
]

View File

@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2018-04-12 03:16
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
import filer.fields.file
class Migration(migrations.Migration):
dependencies = [
('datacenterlight', '0018_auto_20180403_1930'),
]
operations = [
migrations.CreateModel(
name='CMSFaviconExtension',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('extended_object', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, to='cms.Page')),
('favicon', filer.fields.file.FilerFileField(on_delete=django.db.models.deletion.CASCADE, related_name='cms_favicon_image', to='filer.File')),
('public_extension', models.OneToOneField(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='draft_extension', to='datacenterlight.CMSFaviconExtension')),
],
options={
'abstract': False,
},
),
]

View File

@ -0,0 +1,16 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2018-04-20 15:04
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('datacenterlight', '0019_auto_20180415_2236'),
('datacenterlight', '0019_cmsfaviconextension'),
]
operations = [
]

View File

@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2018-04-25 09:20
from __future__ import unicode_literals
import cms.models.fields
from django.db import migrations
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('datacenterlight', '0020_merge'),
('cms', '0014_auto_20160404_1908'),
]
operations = [
migrations.AddField(
model_name='cmsintegration',
name='calculator_placeholder',
field=cms.models.fields.PlaceholderField(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE,
related_name='dcl-calculator-placeholder+', slotname='datacenterlight_calculator', to='cms.Placeholder'),
),
migrations.RenameModel(
old_name='DCLCustomPricingModel',
new_name='DCLCalculatorPluginModel',
),
]

View File

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2018-05-07 02:19
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('datacenterlight', '0021_cmsintegration_calculator_placeholder'),
]
operations = [
migrations.AddField(
model_name='vmpricing',
name='discount_amount',
field=models.DecimalField(
decimal_places=2, default=0, max_digits=6),
),
migrations.AddField(
model_name='vmpricing',
name='discount_name',
field=models.CharField(blank=True, max_length=255, null=True),
),
]

View File

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2018-05-23 22:19
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('datacenterlight', '0022_auto_20180506_1950'),
]
operations = [
migrations.AddField(
model_name='dclcalculatorpluginmodel',
name='vm_type',
field=models.CharField(choices=[('public', 'Public'), ('ipv6only', 'Ipv6Only')], default='public', max_length=50),
),
migrations.AddField(
model_name='vmtemplate',
name='vm_type',
field=models.CharField(choices=[('public', 'Public'), ('ipv6only', 'Ipv6Only')], default='public', max_length=50),
),
]

View File

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2018-06-24 08:23
from __future__ import unicode_literals
import datacenterlight.cms_models
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('datacenterlight', '0023_auto_20180524_0349'),
]
operations = [
migrations.AddField(
model_name='dclcalculatorpluginmodel',
name='vm_templates_to_show',
field=datacenterlight.cms_models.MultipleChoiceArrayField(base_field=models.CharField(blank=True, max_length=256), blank=True, default=list, help_text='Recommended: If you wish to show all templates of the corresponding VM Type (public/ipv6only), please do not select any of the items in the above field. This will allow any new template(s) added in the backend to be automatically listed in this calculator instance.', size=None),
),
]

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2018-09-25 20:27
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('datacenterlight', '0024_dclcalculatorpluginmodel_vm_templates_to_show'),
]
operations = [
migrations.AddField(
model_name='dclnavbarpluginmodel',
name='show_login_option',
field=models.BooleanField(default=True, help_text='Uncheck this if you do not want to show login/dashboard.'),
),
]

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2018-09-27 20:32
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('datacenterlight', '0025_dclnavbarpluginmodel_show_login_option'),
]
operations = [
migrations.AddField(
model_name='dclcalculatorpluginmodel',
name='default_selected_template',
field=models.CharField(default='Devuan Ascii', help_text='Write the name of the template that you need selected as default when the calculator loads', max_length=128, null=True),
),
]

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2018-09-29 05:36
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('datacenterlight', '0026_dclcalculatorpluginmodel_default_selected_template'),
]
operations = [
migrations.AddField(
model_name='dclcalculatorpluginmodel',
name='enable_512mb_ram',
field=models.BooleanField(default=False),
),
]

View File

@ -1,17 +1,103 @@
import logging
from django.db import models
logger = logging.getLogger(__name__)
class VMTemplate(models.Model):
PUBLIC = 'public'
IPV6 = 'ipv6only'
VM_TYPE_CHOICES = (
(PUBLIC, PUBLIC.title()),
(IPV6, IPV6.title()),
)
name = models.CharField(max_length=50)
opennebula_vm_template_id = models.IntegerField()
vm_type = models.CharField(
max_length=50, choices=VM_TYPE_CHOICES, default=PUBLIC
)
def __str__(self):
return '%s - %s - %s' % (
self.opennebula_vm_template_id, self.vm_type, self.name
)
@classmethod
def create(cls, name, opennebula_vm_template_id):
def create(cls, name, opennebula_vm_template_id, vm_type):
vm_template = cls(
name=name, opennebula_vm_template_id=opennebula_vm_template_id)
name=name, opennebula_vm_template_id=opennebula_vm_template_id,
vm_type=vm_type
)
return vm_template
class VMPricing(models.Model):
name = models.CharField(max_length=255, unique=True)
vat_inclusive = models.BooleanField(default=True)
vat_percentage = models.DecimalField(
max_digits=7, decimal_places=5, blank=True, default=0
)
cores_unit_price = models.DecimalField(
max_digits=7, decimal_places=5, default=0
)
ram_unit_price = models.DecimalField(
max_digits=7, decimal_places=5, default=0
)
ssd_unit_price = models.DecimalField(
max_digits=7, decimal_places=5, default=0
)
hdd_unit_price = models.DecimalField(
max_digits=7, decimal_places=6, default=0
)
discount_name = models.CharField(max_length=255, null=True, blank=True)
discount_amount = models.DecimalField(
max_digits=6, decimal_places=2, default=0
)
def __str__(self):
display_str = self.name + ' => ' + ' - '.join([
'{}/Core'.format(self.cores_unit_price.normalize()),
'{}/GB RAM'.format(self.ram_unit_price.normalize()),
'{}/GB SSD'.format(self.ssd_unit_price.normalize()),
'{}/GB HDD'.format(self.hdd_unit_price.normalize()),
'{}% VAT'.format(self.vat_percentage.normalize())
if not self.vat_inclusive else 'VAT-Incl',
])
if self.discount_amount:
display_str = ' - '.join([
display_str,
'{} {}'.format(
self.discount_amount,
self.discount_name if self.discount_name else 'Discount'
)
])
return display_str
@classmethod
def get_vm_pricing_by_name(cls, name):
try:
pricing = VMPricing.objects.get(name=name)
except Exception as e:
logger.error(
"Error getting VMPricing with name {name}. "
"Details: {details}. Attempting to return default"
"pricing.".format(name=name, details=str(e))
)
pricing = VMPricing.get_default_pricing()
return pricing
@classmethod
def get_default_pricing(cls):
""" Returns the default pricing or None """
try:
default_pricing = VMPricing.objects.get(name='default')
except Exception as e:
logger.error(str(e))
default_pricing = None
return default_pricing
class StripePlan(models.Model):
"""
A model to store Data Center Light's created Stripe plans

View File

@ -1,7 +1,7 @@
body,
html {
width: 100%;
min-height: 100%;
height: 100%;
}
body,
@ -74,6 +74,20 @@ a.list-group-item-danger.active:focus {
padding: 10px;
}
.navbar-brand > img {
height: 100%;
}
#logoWhite,
.navbar-transparent #logoBlack {
display: none;
}
#logoBlack,
.navbar-transparent #logoWhite {
display: block;
}
@media (min-width: 768px) {
.navbar-right {
margin-right: 10px;
@ -84,15 +98,91 @@ a.list-group-item-danger.active:focus {
}
}
.navbar .dcl-link {
display: block;
padding: 15px;
color: #777;
}
.navbar .dcl-link:focus,
.navbar .dcl-link:active,
.navbar .dcl-link:hover {
text-decoration: none;
}
.navbar .dropdown-menu .dcl-link {
padding: 1px 10px;
}
p.copyright {
margin: 15px 0 0;
margin: 0;
}
footer {
padding: 20px 0;
font-weight: 300;
padding: 25px 0;
background-color: #f8f8f8;
}
footer .list-inline {
margin-bottom: 15px;
}
footer a {
color: #777;
}
}
footer .dcl-link-separator {
position: relative;
padding-left: 10px;
}
footer .dcl-link-separator::before {
content: "";
position: absolute;
display: inline-block;
top: 9px;
bottom: 0;
left: -2px;
right: 0;
width: 2px;
height: 2px;
border-radius: 100%;
background: #777;
}
.mb-0 {
margin-bottom: 0;
}
.thin-hr {
margin-top: 10px;
margin-bottom: 10px;
}
.payment-container .credit-card-info {
padding-bottom: 15px;
border-bottom: 1px solid #eee;
}
.credit-card-info {
display: flex;
}
.credit-card-info .align-bottom {
align-self: flex-end;
padding-right: 0 !important;
}
.new-card-head {
margin-top: 10px;
}
.new-card-button-margin button{
margin-top: 5px;
margin-bottom: 5px;
}
.input-no-border {
border: none !important;
background: transparent !important;
resize: none;
}

View File

@ -1,3 +1,18 @@
.btn-trans {
color: #fff;
border: 2px solid #fff;
padding: 4px 18px;
letter-spacing: 0.6px;
background: rgba(0,0,0,0.35);
}
.btn-trans:focus,
.btn-trans:active,
.btn-trans:hover {
background: #fff;
color: #333;
}
.header_slider > .carousel .carousel-inner {
min-height: 95vh;
display: flex;
@ -40,7 +55,7 @@
flex: 1;
}
.header_slider > .carousel .item .container {
.header_slider > .carousel .item .container-fluid {
overflow: auto;
padding: 50px 20px 60px;
height: 100%;
@ -89,9 +104,9 @@
.header_slider .carousel-control .fa {
font-size: 4em;
}
.header_slider > .carousel .item .container {
.header_slider > .carousel .item .container-fluid {
overflow: auto;
padding: 75px 50px;
padding: 75px;
}
.header_slider .btn-trans {
padding: 8px 15px;
@ -168,12 +183,4 @@
/* width: auto; */
height: 100%;
}
}
.btn-trans {
color: #fff;
border: 2px solid #fff;
padding: 4px 18px;
letter-spacing: 0.6px;
background: rgba(0,0,0,0.35);
}

View File

@ -1,3 +1,12 @@
.navbar-transparent #logoWhite {
display: none;
}
.navbar-transparent #logoBlack {
display: block;
width: 220px;
}
.topnav .navbar-fixed-top .navbar-collapse {
max-height: 740px;
}
@ -15,8 +24,8 @@
}
@media(min-width: 768px) {
.navbar-default .navbar-nav>li>a,
.navbar-right .highlights-dropdown .dropdown-menu>li>a {
.navbar-default .navbar-nav>li a,
.navbar-right .highlights-dropdown .dropdown-menu>li a {
font-weight: 300;
}
.navbar-right .highlights-dropdown .dropdown-menu {
@ -26,7 +35,7 @@
}
}
.navbar-right .highlights-dropdown .dropdown-menu>li>a {
.navbar-right .highlights-dropdown .dropdown-menu>li a {
font-size: 13px;
font-family: 'Lato', sans-serif;
padding: 1px 10px 1px 18px !important;
@ -34,9 +43,9 @@
color: #333;
}
.navbar-right .highlights-dropdown .dropdown-menu>li>a:hover,
.navbar-right .highlights-dropdown .dropdown-menu>li>a:focus,
.navbar-right .highlights-dropdown .dropdown-menu>li>a:active {
.navbar-right .highlights-dropdown .dropdown-menu>li a:hover,
.navbar-right .highlights-dropdown .dropdown-menu>li a:focus,
.navbar-right .highlights-dropdown .dropdown-menu>li a:active {
background: transparent;
text-decoration: underline !important;
}
@ -144,9 +153,9 @@
}
@media (max-width: 767px) {
.navbar-default .navbar-nav .open .dropdown-menu>.active>a,
.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,
.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover {
.navbar-default .navbar-nav .open .dropdown-menu>.active a,
.navbar-default .navbar-nav .open .dropdown-menu>.active a:focus,
.navbar-default .navbar-nav .open .dropdown-menu>.active a:hover {
background-color: transparent;
}
}
@ -163,7 +172,7 @@
}
.content-dashboard {
min-height: calc(100vh - 60px);
min-height: calc(100vh - 96px);
width: 100%;
margin: 0 auto;
max-width: 1120px;
@ -473,6 +482,7 @@
margin: 100px auto 40px;
border: 1px solid #ccc;
padding: 30px 30px 20px;
color: #595959;
}
.order-detail-container .dashboard-title-thin {
@ -494,10 +504,6 @@
margin-bottom: 15px;
}
.order-detail-container .order-details strong {
color: #595959;
}
.order-detail-container h4 {
font-size: 16px;
font-weight: bold;
@ -506,13 +512,28 @@
.order-detail-container p {
margin-bottom: 5px;
color: #595959;
}
.order-detail-container hr {
margin: 15px 0;
}
.order-detail-container .thin-hr {
margin: 10px 0;
}
.order-detail-container .subtotal-price {
font-size: 16px;
}
.order-detail-container .subtotal-price .text-primary {
font-size: 17px;
}
.order-detail-container .total-price {
font-size: 18px;
}
@media (max-width: 767px) {
.order-detail-container {
padding: 15px;

View File

@ -58,6 +58,16 @@ textarea {
min-width: 180px;
}
.lead {
font-size: 18px;
}
@media (min-width: 768px) {
.lead {
font-size: 21px;
}
}
/* Top navbar */
@ -85,15 +95,13 @@ textarea {
}
}
.navbar-transparent .navbar-nav>li a,
.navbar-transparent .navbar-nav>.open>a,
.navbar-transparent .navbar-nav>.open>a:focus,
.navbar-transparent .navbar-nav>.open>a:hover {
color: #fff;
}
.navbar-transparent .navbar-nav>li a {
color: #fff;
}
.navbar-transparent .navbar-nav>li a:focus,
.navbar-transparent .navbar-nav>li a:active,
@ -103,20 +111,10 @@ textarea {
text-decoration: none;
}
.navbar .dcl-link {
display: block;
padding: 15px;
color: #777;
}
.navbar .dcl-link:focus,
.navbar .dcl-link:active,
.navbar .dcl-link:hover {
text-decoration: none;
}
.navbar .dropdown-menu .dcl-link {
padding: 1px 10px;
.topnav .nav .open>a,
.topnav .nav .open>a:focus,
.topnav .nav .open>a:hover {
background: transparent;
}
.navbar-transparent .navbar-nav>li>.on-hover-border {
@ -139,17 +137,6 @@ textarea {
color: #fff;
}
#logoWhite,
.navbar-transparent #logoBlack {
display: none;
}
#logoBlack,
.navbar-transparent #logoWhite {
display: block;
width: 220px;
}
.nav-language {
position: relative;
}
@ -425,17 +412,20 @@ textarea {
}
.split-section.left .split-description {
/* width: 90%; */
margin-right: auto;
}
.split-section .split-description .lead {
color: #3a3a3a;
font-size: 21px;
}
.split-section .space .split-description .lead {
font-size: 20px;
@media (min-width: 768px) {
.split-section .split-description .lead {
font-size: 21px;
}
.split-section .space .split-description .lead {
font-size: 20px;
}
}
.split-section.right .split-description {
@ -513,14 +503,14 @@ textarea {
.split-section-plain .split-figure {
width: 41.66666667%;
}
.split-section-plain .split-figure.col-sm-push-6 {
left: 58.33333333%;
.split-section-plain .split-figure.col-sm-pull-6 {
right: 58.33333333%;
}
.split-section-plain .split-text {
width: 58.33333333%;
}
.split-section-plain .split-text.col-sm-pull-6 {
right: 41.66666667%;
.split-section-plain .split-text.col-sm-push-6 {
left: 41.66666667%;
}
}
@ -532,6 +522,7 @@ textarea {
padding-top: 20px;
display: inline-block;
color: #999 !important;
word-break: break-all;
}
.price-calc-section .card {
@ -760,17 +751,11 @@ textarea {
.ssdimg {
margin: 0 15px;
/* vertical-align: middle; */
/* display: inline-block; */
}
.ssdimg img {
max-width: 125px;
}
@media (max-width: 767px) {
.ssdimg img {
width: 100px;
max-height: 120px;
}
}
@ -791,7 +776,7 @@ textarea {
width: 100%;
margin: 0 auto;
background: #fff;
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1), 0 0 6px rgba(0, 0, 0, 0.15);
padding-bottom: 40px;
border-radius: 7px;
text-align: center;
@ -944,7 +929,7 @@ textarea {
}
@media(max-width:991px) {
@media(max-width:767px) {
.section-sm-center .split-text,
.section-sm-center .space {
text-align: center !important;
@ -1052,8 +1037,8 @@ textarea {
line-height: 35px;
}
.split-section .split-title h2 {
font-size: 35px;
line-height: 35px;
font-size: 32px;
line-height: 34px;
}
.contact-section .title {
margin: 0 auto;
@ -1186,7 +1171,7 @@ footer {
max-width: 430px;
}
.flex-row-rev .desc-text {
max-width: 710px;
max-width: 600px;
text-align: left;
}
.flex-row-rev .percent-text {
@ -1215,24 +1200,6 @@ footer {
transform: rotate(45deg);
}
footer .dcl-link-separator {
position: relative;
padding-left: 10px;
}
footer .dcl-link-separator::before {
content: "";
position: absolute;
display: inline-block;
top: 9px;
bottom: 0;
left: -2px;
right: 0;
width: 2px;
height: 2px;
border-radius: 100%;
background: #777;
}
/* new styles for whydcl section cms plugin (to replace older style) */
@ -1250,3 +1217,88 @@ footer .dcl-link-separator::before {
font-size: 30px;
}
}
/* cms section promo */
.promo-section {
padding: 75px 15px;
}
.promo-section.promo-with-bg {
color: #fff;
background-size: cover;
background-position: center;
}
.promo-section.promo-with-bg a {
color: #87B6EA;
}
.promo-section.promo-with-bg a:hover,
.promo-section.promo-with-bg a:focus {
color: #77a6da;
}
.promo-section h3 {
font-weight: 700;
font-size: 36px;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-top: 10px;
margin-bottom: 25px;
}
.promo-section h4 {
font-size: 24px;
margin-bottom: 20px;
}
.promo-section p {
font-size: 18px;
line-height: 1.5;
}
.promo-section.text-center p {
max-width: 720px;
margin: auto;
}
.promo-section.text-center h3,
.promo-section.text-center h4 {
margin-bottom: 35px;
}
.split-text .split-subsection {
margin-top: 25px;
margin-bottom: 25px;
}
.split-text .promo-section {
padding: 20px 15px;
margin-top: 30px;
margin-bottom: 30px;
}
.split-text .promo-section .container {
width: auto;
}
.split-text .promo-section h3,
.split-text .promo-section h4 {
margin-bottom: 15px;
}
@media (max-width: 767px) {
.split-text .split-subsection {
margin-left: -15px;
margin-right: -15px;
}
.promo-section h3 {
font-size: 29px;
}
.split-text .promo-section {
padding-left: 0;
padding-right: 0;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

@ -2,32 +2,32 @@
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="156.5px" height="40px" viewBox="0 0 156.5 40" enable-background="new 0 0 156.5 40" xml:space="preserve">
width="156.5px" height="30.5px" viewBox="0 0 156.5 30.5" enable-background="new 0 0 156.5 30.5" xml:space="preserve">
<g display="none">
<g display="inline">
<path fill="#231916" d="M32.599,25.896c0-0.429,0.15-0.845,0.453-1.25c0.303-0.408,0.734-0.773,1.296-1.097v-0.092
c-0.294-0.16-0.554-0.388-0.777-0.674c-0.22-0.289-0.332-0.662-0.332-1.119c0-0.354,0.115-0.712,0.345-1.076
<path fill="#231916" d="M32.599,25.896c0-0.43,0.15-0.846,0.453-1.25c0.303-0.408,0.734-0.773,1.296-1.098v-0.092
c-0.294-0.16-0.554-0.389-0.777-0.674c-0.22-0.289-0.332-0.662-0.332-1.119c0-0.354,0.115-0.712,0.345-1.076
c0.227-0.36,0.55-0.683,0.964-0.962v-0.09c-0.386-0.28-0.711-0.654-0.978-1.12c-0.263-0.464-0.397-1.021-0.397-1.67
c0-0.606,0.118-1.149,0.354-1.63c0.236-0.48,0.555-0.889,0.954-1.229c0.398-0.34,0.86-0.598,1.385-0.775
c0.523-0.178,1.081-0.266,1.672-0.266c0.605,0,1.152,0.088,1.64,0.266h4.452v1.662h-2.702c0.222,0.237,0.42,0.528,0.598,0.875
c0.178,0.348,0.267,0.735,0.267,1.165c0,0.59-0.111,1.117-0.332,1.572c-0.222,0.459-0.525,0.846-0.91,1.163
c-0.384,0.318-0.834,0.558-1.351,0.72c-0.517,0.162-1.071,0.246-1.663,0.246c-0.265,0-0.55-0.031-0.854-0.091
c-0.302-0.058-0.601-0.147-0.896-0.267c-0.503,0.326-0.753,0.701-0.753,1.131c0,0.398,0.185,0.686,0.553,0.865
c0.369,0.176,0.901,0.266,1.596,0.266h2.303c1.42,0,2.47,0.203,3.159,0.607c0.687,0.406,1.029,1.076,1.029,2.006
c0,0.518-0.145,1.004-0.431,1.461c-0.29,0.459-0.699,0.861-1.23,1.209c-0.531,0.348-1.174,0.621-1.929,0.82
c-0.751,0.199-1.594,0.299-2.525,0.299c-1.537,0-2.747-0.25-3.633-0.744C33.042,27.586,32.599,26.857,32.599,25.896z
c-0.384,0.318-0.834,0.558-1.351,0.72c-0.517,0.162-1.071,0.247-1.663,0.247c-0.265,0-0.55-0.032-0.854-0.092
c-0.302-0.058-0.601-0.147-0.896-0.267c-0.503,0.326-0.753,0.7-0.753,1.131c0,0.397,0.185,0.687,0.553,0.864
c0.369,0.177,0.901,0.267,1.596,0.267h2.303c1.42,0,2.47,0.203,3.159,0.606c0.687,0.406,1.029,1.076,1.029,2.007
c0,0.518-0.145,1.004-0.431,1.461c-0.29,0.459-0.699,0.86-1.23,1.209c-0.531,0.348-1.174,0.621-1.929,0.819
c-0.751,0.199-1.594,0.299-2.525,0.299c-1.537,0-2.747-0.25-3.633-0.743C33.042,27.586,32.599,26.857,32.599,25.896z
M34.458,25.633c0,0.516,0.285,0.932,0.854,1.25s1.393,0.477,2.47,0.477c0.577,0,1.097-0.055,1.562-0.166
c0.465-0.11,0.864-0.258,1.197-0.442s0.585-0.396,0.764-0.631c0.177-0.234,0.266-0.486,0.266-0.754
c0-0.474-0.196-0.785-0.587-0.941c-0.392-0.153-0.979-0.231-1.762-0.231h-1.905c-0.34,0-0.641-0.012-0.907-0.033
c-0.268-0.021-0.519-0.07-0.754-0.145c-0.444,0.25-0.753,0.51-0.932,0.776C34.549,25.057,34.458,25.334,34.458,25.633z
c0.465-0.109,0.864-0.258,1.197-0.441s0.585-0.396,0.764-0.631c0.177-0.234,0.266-0.486,0.266-0.754
c0-0.475-0.196-0.785-0.587-0.941c-0.392-0.152-0.979-0.23-1.762-0.23h-1.905c-0.34,0-0.641-0.012-0.907-0.033
c-0.268-0.021-0.519-0.07-0.754-0.145c-0.444,0.25-0.753,0.51-0.932,0.775C34.549,25.057,34.458,25.334,34.458,25.633z
M37.539,19.095c0.621,0,1.152-0.206,1.597-0.62c0.442-0.414,0.665-0.989,0.665-1.727c0-0.71-0.223-1.279-0.665-1.707
c-0.445-0.428-0.976-0.643-1.597-0.643s-1.152,0.215-1.595,0.643c-0.442,0.428-0.665,0.997-0.665,1.707
c0,0.738,0.223,1.313,0.665,1.727C36.386,18.889,36.918,19.095,37.539,19.095z"/>
</g>
<path display="inline" fill="#231916" d="M15.983,24.24h-1.857l-0.183-1.854h-0.092c-0.551,0.642-1.151,1.154-1.8,1.548
c-0.649,0.387-1.401,0.582-2.258,0.582c-1.329,0-2.3-0.384-2.91-1.156c-0.612-0.771-0.917-1.898-0.917-3.381V14.35L3,14.293
c-0.649,0.387-1.401,0.582-2.258,0.582c-1.329,0-2.3-0.384-2.91-1.156c-0.612-0.771-0.917-1.897-0.917-3.38V14.35L3,14.293
l5.258-2.023l-0.023,1.529v5.882c0,0.994,0.176,1.724,0.528,2.189c0.351,0.467,0.954,0.7,1.811,0.7
c0.581,0,1.111-0.143,1.593-0.426c0.48-0.283,0.998-0.762,1.547-1.433v-7.678h2.269V24.24z"/>
c0.581,0,1.111-0.144,1.593-0.426c0.48-0.283,0.998-0.763,1.547-1.434v-7.678h2.269V24.24z"/>
<path display="inline" fill="#231916" d="M19.742,13.033h1.856l0.184,1.812h0.091c0.565-0.582,1.176-1.075,1.834-1.479
c0.655-0.405,1.42-0.607,2.292-0.607c1.313,0,2.278,0.39,2.888,1.168c0.611,0.778,0.917,1.903,0.917,3.37v6.942h-2.271v-6.646
c0-0.976-0.174-1.7-0.525-2.165c-0.352-0.467-0.955-0.701-1.811-0.701c-0.597,0-1.131,0.151-1.605,0.447
@ -36,28 +36,28 @@
c0.533-0.511,1.146-0.902,1.834-1.17c0.687-0.267,1.398-0.4,2.13-0.4c0.826,0,1.555,0.13,2.19,0.39
c0.633,0.26,1.172,0.621,1.615,1.086c0.443,0.468,0.779,1.025,1.01,1.676c0.229,0.648,0.344,1.362,0.344,2.142
c0,0.229-0.012,0.447-0.035,0.652c-0.022,0.208-0.049,0.38-0.08,0.516h-8.436c0.077,1.1,0.485,1.96,1.229,2.58
c0.739,0.619,1.675,0.928,2.807,0.928c0.611,0,1.18-0.084,1.708-0.252c0.527-0.166,1.042-0.403,1.546-0.709l0.802,1.443
c-0.578,0.367-1.23,0.676-1.959,0.928c-0.726,0.252-1.525,0.377-2.396,0.377c-0.842,0-1.631-0.131-2.371-0.399
c-0.742-0.267-1.387-0.65-1.938-1.156c-0.551-0.504-0.982-1.119-1.296-1.846C55.286,20.386,55.129,19.566,55.129,18.65z
c0.739,0.619,1.675,0.928,2.807,0.928c0.611,0,1.18-0.084,1.708-0.252c0.527-0.166,1.042-0.402,1.546-0.709l0.802,1.443
c-0.578,0.367-1.23,0.676-1.959,0.928c-0.726,0.252-1.525,0.377-2.396,0.377c-0.842,0-1.631-0.131-2.371-0.398
c-0.742-0.268-1.387-0.65-1.938-1.156c-0.551-0.504-0.982-1.119-1.296-1.846C55.286,20.386,55.129,19.566,55.129,18.65z
M63.909,17.665c0-1.01-0.263-1.781-0.779-2.317c-0.521-0.534-1.262-0.8-2.225-0.8c-0.84,0-1.587,0.266-2.245,0.8
c-0.658,0.536-1.062,1.307-1.214,2.317H63.909z"/>
<path display="inline" fill="#231916" d="M73.475,12.679l0.008,11.793h-2.27v-8.611h-4.256L73.475,12.679z M72.152,10.368
<path display="inline" fill="#231916" d="M73.475,12.679l0.008,11.793h-2.27v-8.612h-4.256L73.475,12.679z M72.152,10.368
c-0.488,0-0.894-0.146-1.215-0.435c-0.32-0.291-0.48-0.681-0.48-1.17c0-0.489,0.16-0.882,0.48-1.18
c0.321-0.298,0.727-0.446,1.215-0.446s0.896,0.148,1.215,0.446c0.32,0.298,0.482,0.691,0.482,1.18c0,0.489-0.162,0.879-0.482,1.17
C73.049,10.221,72.641,10.368,72.152,10.368z"/>
c0.321-0.298,0.727-0.446,1.215-0.446c0.488,0,0.896,0.148,1.215,0.446c0.32,0.298,0.482,0.691,0.482,1.18
c0,0.489-0.162,0.879-0.482,1.17C73.049,10.221,72.641,10.368,72.152,10.368z"/>
<path display="inline" fill="#231916" d="M76.629,18.878c0-0.932,0.168-1.766,0.504-2.497c0.336-0.735,0.795-1.353,1.375-1.859
c0.582-0.503,1.25-0.884,2.008-1.145c0.756-0.26,1.562-0.389,2.416-0.389c0.918,0,1.719,0.156,2.406,0.469
c0.689,0.312,1.262,0.693,1.721,1.135l-1.1,1.443c-0.443-0.367-0.904-0.653-1.379-0.86c-0.471-0.206-0.984-0.309-1.535-0.309
c-0.594,0-1.143,0.095-1.65,0.287c-0.502,0.19-0.936,0.462-1.293,0.813c-0.358,0.352-0.639,0.776-0.838,1.272
c-0.594,0-1.143,0.095-1.65,0.287c-0.502,0.19-0.936,0.462-1.293,0.813c-0.357,0.352-0.639,0.776-0.838,1.272
c-0.197,0.495-0.297,1.042-0.297,1.638c0,0.595,0.096,1.144,0.287,1.64c0.189,0.496,0.465,0.92,0.824,1.273
c0.359,0.35,0.785,0.623,1.273,0.812c0.487,0.188,1.028,0.284,1.627,0.284c0.686,0,1.307-0.129,1.856-0.387
c0.549-0.263,1.043-0.574,1.488-0.94l0.965,1.467c-0.644,0.551-1.347,0.975-2.111,1.272c-0.764,0.298-1.559,0.445-2.383,0.445
c-0.871,0-1.687-0.129-2.44-0.39c-0.756-0.26-1.41-0.643-1.961-1.146c-0.55-0.506-0.98-1.121-1.293-1.847
c0.359,0.35,0.785,0.623,1.273,0.812c0.486,0.188,1.027,0.283,1.627,0.283c0.686,0,1.307-0.129,1.855-0.387
c0.549-0.264,1.043-0.574,1.488-0.939l0.965,1.467c-0.645,0.551-1.348,0.975-2.111,1.271c-0.764,0.298-1.559,0.445-2.383,0.445
c-0.871,0-1.688-0.129-2.439-0.391c-0.756-0.26-1.41-0.643-1.961-1.146c-0.55-0.506-0.98-1.121-1.293-1.847
C76.785,20.641,76.629,19.811,76.629,18.878z"/>
<path display="inline" fill="#231916" d="M87.4,8.285l4.752-2.356v6.601l-0.139,2.521c0.565-0.58,1.178-1.07,1.836-1.467
c0.655-0.396,1.42-0.595,2.293-0.595c1.312,0,2.274,0.389,2.885,1.168c0.611,0.78,0.918,1.903,0.918,3.371v6.945h-2.271v-6.648
c0-0.978-0.176-1.7-0.526-2.165c-0.353-0.466-0.953-0.7-1.812-0.7c-0.596,0-1.131,0.149-1.604,0.448
c-0.475,0.298-1.002,0.745-1.582,1.342v7.726h-2.27L89.836,8.189L87.4,8.285z"/>
<path display="inline" fill="#231916" d="M87.4,8.285l4.752-2.356v6.601l-0.139,2.521c0.564-0.58,1.178-1.07,1.836-1.467
c0.654-0.396,1.42-0.595,2.293-0.595c1.312,0,2.273,0.389,2.885,1.168c0.611,0.78,0.918,1.903,0.918,3.371v6.945h-2.271v-6.647
c0-0.978-0.176-1.7-0.525-2.165c-0.354-0.466-0.953-0.7-1.812-0.7c-0.596,0-1.131,0.149-1.604,0.448
c-0.476,0.298-1.002,0.745-1.582,1.342v7.727H89.88L89.836,8.189L87.4,8.285z"/>
<g display="inline">
<polygon fill="#010000" points="100.371,3.218 99.607,4.815 109.109,4.855 109.873,3.228 "/>
<polygon fill="#010000" points="99.619,6.703 98.83,8.378 108.346,8.397 109.109,6.74 "/>
@ -65,117 +65,120 @@
</g>
<g display="inline">
<path fill="#231916" d="M49.446,20.596c0,0.754,0.188,1.297,0.566,1.631c0.376,0.33,0.866,0.498,1.472,0.498
c0.295,0,0.599-0.037,0.909-0.111s0.658-0.193,1.042-0.355l0.532,1.643c-0.252,0.086-0.492,0.166-0.721,0.242
c-0.231,0.074-0.461,0.135-0.698,0.187c-0.237,0.055-0.483,0.098-0.741,0.135c-0.26,0.035-0.543,0.058-0.853,0.058
c-1.212,0-2.131-0.349-2.76-1.043c-0.627-0.693-0.941-1.697-0.941-3.014V5.711h-3.544l5.737-4.043"/>
c0.295,0,0.599-0.037,0.909-0.11c0.31-0.074,0.658-0.193,1.042-0.355l0.532,1.644c-0.252,0.086-0.492,0.166-0.721,0.242
c-0.231,0.073-0.461,0.135-0.698,0.187c-0.237,0.055-0.483,0.099-0.741,0.135c-0.26,0.035-0.543,0.058-0.853,0.058
c-1.212,0-2.131-0.349-2.76-1.043c-0.627-0.692-0.941-1.696-0.941-3.014V5.711h-3.544l5.737-4.043"/>
</g>
</g>
<path fill="#29427A" d="M12.927,23.013v-1.46h-0.045c-0.359,0.569-0.873,1.007-1.539,1.313s-1.374,0.461-2.122,0.461
c-0.839,0-1.587-0.165-2.246-0.494c-0.659-0.329-1.216-0.768-1.673-1.313c-0.457-0.547-0.805-1.18-1.044-1.898
c-0.24-0.718-0.359-1.467-0.359-2.245s0.124-1.523,0.371-2.234s0.599-1.337,1.055-1.875c0.457-0.539,1.011-0.966,1.662-1.28
<path fill="#29427A" d="M12.927,23.014v-1.461h-0.045c-0.359,0.569-0.873,1.008-1.539,1.313s-1.374,0.461-2.122,0.461
c-0.839,0-1.587-0.165-2.246-0.494c-0.659-0.329-1.216-0.769-1.673-1.313c-0.457-0.547-0.805-1.18-1.044-1.897
c-0.24-0.718-0.359-1.467-0.359-2.245c0-0.778,0.124-1.523,0.371-2.234s0.599-1.337,1.055-1.875c0.457-0.539,1.011-0.966,1.662-1.28
c0.651-0.314,1.381-0.472,2.189-0.472c0.823,0,1.52,0.157,2.089,0.472c0.568,0.314,1.011,0.659,1.325,1.033h0.045V6.035h3.683
v16.978H12.927z M12.792,17.398c0-0.359-0.064-0.711-0.191-1.056c-0.128-0.345-0.307-0.651-0.54-0.921
v16.979H12.927z M12.792,17.398c0-0.359-0.064-0.711-0.191-1.056c-0.128-0.345-0.307-0.651-0.54-0.921
c-0.232-0.27-0.513-0.486-0.842-0.651c-0.329-0.164-0.704-0.247-1.123-0.247c-0.434,0-0.816,0.083-1.145,0.247
c-0.33,0.165-0.606,0.379-0.831,0.64c-0.225,0.263-0.393,0.565-0.505,0.91s-0.168,0.696-0.168,1.056
c0,0.358,0.056,0.715,0.168,1.066s0.281,0.663,0.505,0.932c0.225,0.27,0.501,0.487,0.831,0.651c0.329,0.165,0.711,0.247,1.145,0.247
c0.419,0,0.793-0.082,1.123-0.247c0.33-0.164,0.61-0.382,0.842-0.651c0.232-0.269,0.412-0.575,0.54-0.921
c-0.33,0.165-0.606,0.379-0.831,0.64c-0.225,0.263-0.393,0.565-0.505,0.91c-0.112,0.345-0.168,0.696-0.168,1.056
c0,0.358,0.056,0.715,0.168,1.066c0.112,0.351,0.281,0.663,0.505,0.932c0.225,0.27,0.501,0.487,0.831,0.651
c0.329,0.165,0.711,0.247,1.145,0.247c0.419,0,0.793-0.082,1.123-0.247c0.33-0.164,0.61-0.382,0.842-0.651s0.412-0.575,0.54-0.921
C12.728,18.109,12.792,17.758,12.792,17.398z"/>
<path fill="#29427A" d="M19.08,13.355c0.659-0.628,1.426-1.1,2.302-1.415s1.771-0.472,2.684-0.472c0.944,0,1.741,0.116,2.392,0.348
c0.651,0.232,1.179,0.591,1.583,1.078c0.404,0.486,0.7,1.101,0.887,1.842c0.187,0.74,0.281,1.62,0.281,2.639v5.637H25.84v-1.19
h-0.067c-0.285,0.464-0.715,0.823-1.291,1.078c-0.577,0.254-1.202,0.382-1.875,0.382c-0.449,0-0.913-0.061-1.393-0.18
c-0.479-0.12-0.917-0.314-1.313-0.584c-0.397-0.27-0.723-0.629-0.977-1.078c-0.255-0.449-0.382-1.003-0.382-1.662
c0-0.809,0.221-1.459,0.663-1.953s1.011-0.876,1.707-1.146c0.696-0.27,1.471-0.449,2.324-0.539s1.685-0.135,2.493-0.135v-0.18
c0-0.554-0.195-0.962-0.584-1.225c-0.389-0.262-0.868-0.393-1.437-0.393c-0.524,0-1.03,0.112-1.516,0.337
c-0.487,0.225-0.902,0.494-1.247,0.809L19.08,13.355z M25.84,18.139h-0.472c-0.404,0-0.812,0.02-1.224,0.057
c-0.412,0.037-0.779,0.108-1.101,0.213c-0.322,0.105-0.588,0.259-0.797,0.461c-0.21,0.202-0.314,0.468-0.314,0.797
c0,0.21,0.049,0.39,0.146,0.539c0.097,0.15,0.221,0.27,0.371,0.359c0.149,0.09,0.322,0.153,0.517,0.191
c0.194,0.037,0.382,0.056,0.562,0.056c0.749,0,1.321-0.205,1.718-0.617c0.396-0.412,0.595-0.97,0.595-1.673V18.139z"/>
s1.179,0.591,1.583,1.078c0.404,0.486,0.7,1.101,0.887,1.842c0.187,0.74,0.281,1.62,0.281,2.639v5.637H25.84v-1.189h-0.067
c-0.285,0.464-0.715,0.822-1.291,1.078c-0.577,0.254-1.202,0.382-1.875,0.382c-0.449,0-0.913-0.062-1.393-0.181
c-0.479-0.119-0.917-0.313-1.313-0.584c-0.397-0.27-0.723-0.629-0.977-1.078c-0.255-0.448-0.382-1.002-0.382-1.662
c0-0.809,0.221-1.459,0.663-1.953c0.442-0.494,1.011-0.876,1.707-1.146c0.696-0.27,1.471-0.449,2.324-0.539
c0.853-0.09,1.685-0.135,2.493-0.135v-0.18c0-0.554-0.195-0.962-0.584-1.225c-0.389-0.262-0.868-0.393-1.437-0.393
c-0.524,0-1.03,0.112-1.516,0.337c-0.487,0.225-0.902,0.494-1.247,0.809L19.08,13.355z M25.84,18.139h-0.472
c-0.404,0-0.812,0.02-1.224,0.057c-0.412,0.037-0.779,0.108-1.101,0.213c-0.322,0.105-0.588,0.259-0.797,0.461
c-0.21,0.202-0.314,0.468-0.314,0.797c0,0.21,0.049,0.39,0.146,0.539c0.097,0.15,0.221,0.27,0.371,0.358
c0.149,0.091,0.322,0.153,0.517,0.191c0.194,0.037,0.382,0.057,0.562,0.057c0.749,0,1.321-0.205,1.718-0.618
c0.396-0.412,0.595-0.97,0.595-1.673L25.84,18.139L25.84,18.139z"/>
<path fill="#29427A" d="M36.067,14.568v4.283c0,0.526,0.101,0.921,0.303,1.184c0.202,0.264,0.565,0.395,1.089,0.395
c0.18,0,0.371-0.015,0.573-0.045c0.202-0.029,0.371-0.074,0.505-0.135l0.045,2.695c-0.255,0.09-0.577,0.169-0.966,0.235
c-0.389,0.067-0.779,0.102-1.168,0.102c-0.749,0-1.377-0.094-1.886-0.281c-0.509-0.188-0.917-0.458-1.224-0.811
c-0.307-0.354-0.528-0.773-0.662-1.261c-0.135-0.488-0.202-1.032-0.202-1.633v-4.729h-1.797v-2.74h1.774V8.887h3.616v2.942h2.627
v2.74H36.067z"/>
c0.18,0,0.371-0.015,0.573-0.045c0.202-0.029,0.371-0.074,0.505-0.135l0.045,2.695c-0.255,0.09-0.577,0.169-0.966,0.234
c-0.389,0.067-0.779,0.103-1.168,0.103c-0.749,0-1.377-0.095-1.886-0.281c-0.509-0.188-0.917-0.458-1.224-0.812
s-0.528-0.772-0.662-1.26c-0.135-0.489-0.202-1.033-0.202-1.634v-4.729h-1.797v-2.74h1.774v-2.94h3.616v2.942h2.627v2.74
L36.067,14.568L36.067,14.568z"/>
<path fill="#29427A" d="M40.957,13.355c0.659-0.628,1.426-1.1,2.302-1.415s1.771-0.472,2.684-0.472c0.943,0,1.741,0.116,2.392,0.348
c0.651,0.232,1.179,0.591,1.583,1.078c0.404,0.486,0.7,1.101,0.887,1.842c0.188,0.74,0.281,1.62,0.281,2.639v5.637h-3.369v-1.19
h-0.067c-0.285,0.464-0.715,0.823-1.291,1.078c-0.577,0.254-1.202,0.382-1.875,0.382c-0.449,0-0.913-0.061-1.392-0.18
c-0.479-0.12-0.917-0.314-1.314-0.584c-0.397-0.27-0.723-0.629-0.977-1.078s-0.382-1.003-0.382-1.662
c0-0.809,0.221-1.459,0.663-1.953s1.011-0.876,1.707-1.146s1.471-0.449,2.324-0.539s1.685-0.135,2.493-0.135v-0.18
c0-0.554-0.195-0.962-0.584-1.225c-0.389-0.262-0.868-0.393-1.437-0.393c-0.524,0-1.03,0.112-1.516,0.337s-0.902,0.494-1.247,0.809
L40.957,13.355z M47.717,18.139h-0.471c-0.404,0-0.812,0.02-1.224,0.057c-0.412,0.037-0.779,0.108-1.101,0.213
c-0.322,0.105-0.587,0.259-0.797,0.461c-0.209,0.202-0.314,0.468-0.314,0.797c0,0.21,0.049,0.39,0.146,0.539
c0.097,0.15,0.22,0.27,0.37,0.359c0.149,0.09,0.322,0.153,0.517,0.191c0.194,0.037,0.382,0.056,0.562,0.056
c0.749,0,1.321-0.205,1.718-0.617c0.396-0.412,0.595-0.97,0.595-1.673V18.139z"/>
s1.179,0.591,1.583,1.078c0.404,0.486,0.7,1.101,0.887,1.842c0.188,0.74,0.281,1.62,0.281,2.639v5.637h-3.369v-1.189H47.65
c-0.285,0.464-0.715,0.822-1.291,1.078c-0.577,0.254-1.202,0.382-1.875,0.382c-0.449,0-0.913-0.062-1.392-0.181
s-0.917-0.313-1.314-0.584c-0.397-0.27-0.723-0.629-0.977-1.078c-0.254-0.448-0.382-1.002-0.382-1.662
c0-0.809,0.221-1.459,0.663-1.953c0.442-0.494,1.011-0.876,1.707-1.146c0.696-0.27,1.471-0.449,2.324-0.539
c0.853-0.09,1.685-0.135,2.493-0.135v-0.18c0-0.554-0.195-0.962-0.584-1.225c-0.389-0.262-0.868-0.393-1.437-0.393
c-0.524,0-1.03,0.112-1.516,0.337c-0.486,0.225-0.902,0.494-1.247,0.809L40.957,13.355z M47.717,18.139h-0.471
c-0.404,0-0.812,0.02-1.224,0.057c-0.412,0.037-0.779,0.108-1.101,0.213c-0.322,0.105-0.587,0.259-0.797,0.461
c-0.209,0.202-0.314,0.468-0.314,0.797c0,0.21,0.049,0.39,0.146,0.539c0.097,0.15,0.22,0.27,0.37,0.358
c0.149,0.091,0.322,0.153,0.517,0.191c0.194,0.037,0.382,0.057,0.562,0.057c0.749,0,1.321-0.205,1.718-0.618
c0.396-0.412,0.595-0.97,0.595-1.673L47.717,18.139L47.717,18.139z"/>
<path fill="#5A74AF" d="M61.874,15.197c-0.375-0.389-0.768-0.685-1.179-0.887c-0.412-0.202-0.902-0.304-1.471-0.304
c-0.554,0-1.037,0.102-1.449,0.304c-0.412,0.202-0.756,0.479-1.033,0.831s-0.487,0.756-0.629,1.212
c-0.142,0.457-0.213,0.933-0.213,1.427s0.082,0.962,0.247,1.403c0.165,0.441,0.396,0.827,0.696,1.156
c0.3,0.33,0.659,0.588,1.078,0.775c0.419,0.188,0.891,0.28,1.415,0.28c0.568,0,1.055-0.101,1.459-0.303s0.778-0.498,1.123-0.888
l1.438,1.438c-0.524,0.584-1.134,1.003-1.831,1.258c-0.696,0.254-1.434,0.382-2.212,0.382c-0.823,0-1.576-0.135-2.257-0.404
s-1.269-0.647-1.763-1.135c-0.494-0.485-0.876-1.07-1.146-1.751c-0.27-0.682-0.404-1.434-0.404-2.258
c0-0.822,0.135-1.579,0.404-2.268s0.647-1.28,1.134-1.774c0.486-0.494,1.07-0.879,1.751-1.156s1.441-0.416,2.279-0.416
c-0.554,0-1.037,0.102-1.449,0.304s-0.756,0.479-1.033,0.831c-0.277,0.352-0.487,0.756-0.629,1.212
C55.971,16.81,55.9,17.286,55.9,17.78c0,0.494,0.082,0.962,0.247,1.403s0.396,0.827,0.696,1.156c0.3,0.33,0.659,0.588,1.078,0.775
s0.891,0.28,1.415,0.28c0.568,0,1.055-0.102,1.459-0.304c0.404-0.202,0.778-0.498,1.123-0.888l1.438,1.438
c-0.524,0.584-1.134,1.004-1.831,1.258c-0.696,0.254-1.434,0.383-2.212,0.383c-0.823,0-1.576-0.135-2.257-0.404
c-0.681-0.27-1.269-0.646-1.763-1.135c-0.494-0.485-0.876-1.07-1.146-1.751c-0.27-0.682-0.404-1.434-0.404-2.258
c0-0.822,0.135-1.579,0.404-2.268s0.647-1.28,1.134-1.774c0.486-0.494,1.07-0.879,1.751-1.156c0.681-0.277,1.441-0.416,2.279-0.416
c0.779,0,1.523,0.139,2.235,0.416c0.711,0.277,1.329,0.7,1.853,1.269L61.874,15.197z"/>
<path fill="#5A74AF" d="M66.612,18.432c0,0.464,0.101,0.887,0.304,1.269c0.201,0.382,0.467,0.707,0.797,0.977
c0.329,0.27,0.711,0.479,1.145,0.629c0.435,0.15,0.884,0.225,1.348,0.225c0.629,0,1.176-0.146,1.64-0.438
c0.464-0.292,0.891-0.678,1.28-1.157l1.527,1.168c-1.123,1.452-2.695,2.179-4.717,2.179c-0.838,0-1.598-0.143-2.279-0.427
s-1.258-0.678-1.729-1.179c-0.473-0.502-0.836-1.093-1.09-1.774s-0.382-1.418-0.382-2.212s0.139-1.531,0.416-2.213
c0.276-0.681,0.658-1.271,1.146-1.773c0.486-0.501,1.066-0.895,1.74-1.18c0.674-0.284,1.406-0.426,2.201-0.426
c0.942,0,1.74,0.165,2.391,0.494c0.652,0.33,1.187,0.76,1.605,1.292c0.42,0.531,0.723,1.13,0.91,1.796
c0.188,0.667,0.281,1.345,0.281,2.033v0.719H66.612z M72.99,16.814c-0.016-0.449-0.086-0.861-0.213-1.235
c-0.128-0.374-0.318-0.7-0.573-0.978c-0.255-0.276-0.573-0.493-0.954-0.65c-0.383-0.157-0.828-0.236-1.337-0.236
c-0.493,0-0.946,0.094-1.358,0.281s-0.76,0.43-1.044,0.729s-0.505,0.633-0.663,0.999c-0.156,0.367-0.235,0.73-0.235,1.09H72.99z"/>
c0.329,0.271,0.711,0.479,1.145,0.629c0.435,0.15,0.884,0.226,1.348,0.226c0.629,0,1.176-0.146,1.64-0.438
c0.464-0.292,0.891-0.678,1.28-1.157l1.527,1.168c-1.123,1.451-2.695,2.179-4.717,2.179c-0.838,0-1.598-0.144-2.279-0.427
c-0.681-0.284-1.258-0.678-1.729-1.18c-0.473-0.502-0.836-1.093-1.09-1.774c-0.254-0.681-0.382-1.418-0.382-2.212
s0.139-1.531,0.416-2.213c0.276-0.681,0.658-1.271,1.146-1.773c0.486-0.501,1.066-0.895,1.74-1.18
c0.674-0.284,1.406-0.426,2.201-0.426c0.942,0,1.74,0.165,2.391,0.494c0.652,0.33,1.187,0.76,1.605,1.292
c0.42,0.531,0.723,1.13,0.91,1.796c0.188,0.667,0.281,1.345,0.281,2.033v0.719h-8.534V18.432z M72.99,16.814
c-0.016-0.449-0.086-0.861-0.213-1.235c-0.128-0.374-0.318-0.7-0.573-0.978c-0.255-0.276-0.573-0.493-0.954-0.65
c-0.383-0.157-0.828-0.236-1.337-0.236c-0.493,0-0.946,0.094-1.358,0.281c-0.412,0.187-0.76,0.43-1.044,0.729
c-0.284,0.299-0.505,0.633-0.663,0.999c-0.156,0.367-0.235,0.73-0.235,1.09H72.99z"/>
<path fill="#5A74AF" d="M77.572,12.368h2.021v1.639h0.046c0.254-0.568,0.695-1.029,1.324-1.381s1.355-0.528,2.178-0.528
c0.51,0,1,0.079,1.472,0.236s0.883,0.4,1.235,0.73c0.352,0.329,0.633,0.752,0.842,1.269c0.209,0.516,0.314,1.126,0.314,1.83v6.85
h-2.021v-6.288c0-0.494-0.066-0.917-0.201-1.269c-0.135-0.353-0.314-0.637-0.539-0.854c-0.225-0.216-0.483-0.373-0.775-0.471
s-0.596-0.146-0.91-0.146c-0.418,0-0.808,0.067-1.167,0.202s-0.674,0.349-0.943,0.641c-0.27,0.291-0.479,0.662-0.629,1.111
s-0.225,0.98-0.225,1.595v5.479h-2.021V12.368z"/>
<path fill="#5A74AF" d="M95.762,14.119h-2.896v4.829c0,0.299,0.008,0.595,0.022,0.887s0.071,0.554,0.169,0.786
c0.097,0.232,0.246,0.419,0.449,0.561c0.201,0.144,0.497,0.214,0.887,0.214c0.239,0,0.486-0.022,0.741-0.067
s0.486-0.127,0.696-0.247v1.842c-0.24,0.135-0.551,0.229-0.932,0.28c-0.383,0.053-0.678,0.079-0.888,0.079
c-0.778,0-1.382-0.109-1.808-0.325c-0.427-0.218-0.741-0.498-0.943-0.843s-0.322-0.73-0.359-1.156
c-0.037-0.427-0.057-0.857-0.057-1.292v-5.547h-2.336v-1.751h2.336V9.381h2.021v2.987h2.896V14.119z"/>
<path fill="#5A74AF" d="M99.895,18.432c0,0.464,0.102,0.887,0.304,1.269s0.467,0.707,0.797,0.977
c0.329,0.27,0.711,0.479,1.146,0.629c0.434,0.15,0.883,0.225,1.348,0.225c0.628,0,1.175-0.146,1.639-0.438
c0.464-0.292,0.891-0.678,1.28-1.157l1.527,1.168c-1.123,1.452-2.695,2.179-4.716,2.179c-0.839,0-1.599-0.143-2.28-0.427
c-0.681-0.284-1.257-0.678-1.729-1.179c-0.471-0.502-0.834-1.093-1.088-1.774c-0.256-0.682-0.383-1.418-0.383-2.212
s0.139-1.531,0.416-2.213c0.276-0.681,0.658-1.271,1.145-1.773c0.487-0.501,1.067-0.895,1.741-1.18
c0.674-0.284,1.407-0.426,2.201-0.426c0.942,0,1.74,0.165,2.392,0.494c0.65,0.33,1.186,0.76,1.605,1.292
c0.419,0.531,0.723,1.13,0.909,1.796c0.188,0.667,0.281,1.345,0.281,2.033v0.719H99.895z M106.272,16.814
c-0.016-0.449-0.086-0.861-0.213-1.235c-0.128-0.374-0.318-0.7-0.573-0.978c-0.255-0.276-0.572-0.493-0.954-0.65
s-0.828-0.236-1.337-0.236c-0.493,0-0.946,0.094-1.358,0.281c-0.411,0.188-0.76,0.43-1.044,0.729
c-0.285,0.3-0.506,0.633-0.663,0.999c-0.157,0.367-0.235,0.73-0.235,1.09H106.272z"/>
<path fill="#5A74AF" d="M110.854,12.368h2.021v1.639h0.045c0.135-0.284,0.314-0.542,0.539-0.774
c0.225-0.232,0.475-0.431,0.752-0.595s0.58-0.295,0.909-0.393c0.33-0.097,0.659-0.146,0.988-0.146c0.33,0,0.629,0.045,0.899,0.135
l-0.091,2.178c-0.165-0.045-0.329-0.082-0.494-0.112c-0.165-0.029-0.329-0.045-0.493-0.045c-0.988,0-1.745,0.277-2.269,0.831
c-0.524,0.554-0.786,1.415-0.786,2.583v5.345h-2.021V12.368z"/>
<path fill="#5E6060" d="M120.156,5.513h1.368v11.493h-1.368V5.513z"/>
<path fill="#5E6060" d="M123.485,7.215c0-0.274,0.099-0.509,0.297-0.707c0.197-0.198,0.433-0.296,0.707-0.296
c0.273,0,0.509,0.099,0.707,0.296c0.197,0.198,0.296,0.433,0.296,0.707c0,0.273-0.099,0.509-0.296,0.707
c-0.198,0.198-0.434,0.297-0.707,0.297c-0.274,0-0.51-0.099-0.707-0.297C123.584,7.725,123.485,7.489,123.485,7.215z M123.805,9.799
h1.368v7.207h-1.368V9.799z"/>
<path fill="#5E6060" d="M134.567,16.944c0,0.548-0.094,1.047-0.281,1.498c-0.188,0.45-0.453,0.841-0.798,1.17
s-0.76,0.586-1.246,0.768c-0.487,0.183-1.024,0.273-1.611,0.273c-0.689,0-1.32-0.096-1.894-0.288
c-0.572-0.193-1.117-0.527-1.634-1.003l0.927-1.156c0.355,0.386,0.74,0.677,1.156,0.874c0.415,0.198,0.887,0.297,1.413,0.297
c0.507,0,0.928-0.074,1.262-0.221c0.335-0.146,0.601-0.337,0.799-0.569c0.197-0.234,0.337-0.5,0.418-0.799s0.121-0.601,0.121-0.904
v-1.064h-0.045c-0.264,0.436-0.621,0.758-1.072,0.966s-0.925,0.312-1.421,0.312c-0.527,0-1.017-0.094-1.468-0.281
s-0.838-0.446-1.163-0.775c-0.324-0.329-0.577-0.72-0.76-1.171c-0.183-0.45-0.273-0.939-0.273-1.467
c0-0.527,0.086-1.021,0.259-1.482c0.172-0.461,0.418-0.864,0.737-1.208c0.319-0.345,0.704-0.613,1.155-0.806
c0.45-0.192,0.955-0.289,1.513-0.289c0.486,0,0.96,0.106,1.421,0.319s0.823,0.512,1.087,0.896h0.03V9.799h1.368V16.944z
M130.828,10.894c-0.365,0-0.694,0.063-0.988,0.19c-0.294,0.126-0.542,0.299-0.745,0.517c-0.202,0.218-0.359,0.481-0.471,0.791
c-0.112,0.309-0.167,0.646-0.167,1.011c0,0.729,0.213,1.315,0.638,1.756c0.426,0.44,1.004,0.661,1.733,0.661
s1.307-0.221,1.733-0.661c0.426-0.44,0.638-1.026,0.638-1.756c0-0.365-0.056-0.702-0.167-1.011s-0.269-0.572-0.471-0.791
c-0.203-0.218-0.451-0.39-0.745-0.517C131.522,10.958,131.193,10.894,130.828,10.894z"/>
<path fill="#5E6060" d="M136.29,5.513h1.368v5.397h0.03c0.172-0.385,0.471-0.697,0.896-0.935s0.917-0.357,1.475-0.357
c0.345,0,0.677,0.053,0.996,0.16s0.598,0.271,0.836,0.494c0.238,0.223,0.429,0.509,0.57,0.859s0.213,0.763,0.213,1.239v4.637h-1.368
v-4.257c0-0.334-0.046-0.62-0.137-0.858c-0.092-0.238-0.213-0.431-0.365-0.578c-0.152-0.147-0.327-0.253-0.524-0.32
c-0.198-0.065-0.403-0.099-0.616-0.099c-0.283,0-0.547,0.046-0.79,0.137c-0.243,0.091-0.456,0.236-0.639,0.434
s-0.324,0.448-0.426,0.752c-0.101,0.304-0.151,0.664-0.151,1.079v3.71h-1.368V5.513z"/>
<path fill="#5E6060" d="M148.604,10.985h-1.961v3.269c0,0.203,0.005,0.402,0.015,0.601c0.01,0.197,0.048,0.375,0.114,0.532
c0.065,0.157,0.167,0.283,0.304,0.38s0.337,0.145,0.601,0.145c0.162,0,0.329-0.016,0.502-0.046c0.172-0.03,0.329-0.086,0.471-0.167
v1.246c-0.162,0.092-0.372,0.154-0.631,0.19c-0.258,0.035-0.458,0.053-0.6,0.053c-0.527,0-0.936-0.073-1.224-0.221
c-0.289-0.146-0.502-0.336-0.639-0.569s-0.219-0.494-0.244-0.783c-0.024-0.289-0.037-0.58-0.037-0.874v-3.755h-1.581V9.799h1.581
V7.778h1.368v2.021h1.961V10.985z"/>
<path fill="#95BDE5" d="M142.227,20.314c-0.039,0.215-0.129,0.432-0.129,0.432c-0.024,0.059-0.107,0.25-0.125,0.288
c-0.064,0.138-0.097,0.224-0.17,0.38c-0.084,0.178,0,0-0.136,0.268c-1.126,2.234-4.158,4.755-8.376,4.658
c-3.922-0.09-6.719-1.806-8.072-4.173c-0.103-0.18-0.262-0.42-0.383-0.684c-0.034-0.074-0.242-0.511-0.265-0.575
c-0.116-0.333-0.2-0.368-0.216-0.594c0,0,0.259,0.528,0.779,1.091c1.227,1.325,3.915,3.426,8.156,3.477
c4.143,0.049,6.907-2.123,8.163-3.477C141.972,20.849,142.227,20.314,142.227,20.314z"/>
c0.51,0,1,0.079,1.473,0.236c0.472,0.157,0.883,0.4,1.234,0.73c0.353,0.329,0.633,0.752,0.842,1.269
c0.209,0.516,0.314,1.126,0.314,1.83v6.851h-2.021v-6.289c0-0.494-0.065-0.917-0.2-1.269c-0.136-0.353-0.314-0.637-0.539-0.854
c-0.226-0.216-0.483-0.373-0.775-0.471c-0.292-0.098-0.596-0.146-0.909-0.146c-0.418,0-0.809,0.067-1.168,0.202
c-0.358,0.135-0.674,0.349-0.943,0.641c-0.27,0.291-0.479,0.662-0.629,1.111c-0.149,0.449-0.225,0.98-0.225,1.595v5.479h-2.021
L77.572,12.368L77.572,12.368z"/>
<path fill="#5A74AF" d="M95.762,14.119h-2.896v4.829c0,0.299,0.008,0.595,0.022,0.887c0.014,0.292,0.07,0.554,0.168,0.786
c0.098,0.232,0.246,0.419,0.449,0.561c0.201,0.145,0.497,0.215,0.887,0.215c0.239,0,0.486-0.022,0.741-0.067
s0.486-0.127,0.696-0.247v1.842c-0.24,0.135-0.551,0.229-0.932,0.28c-0.383,0.053-0.678,0.079-0.889,0.079
c-0.777,0-1.382-0.109-1.808-0.325c-0.427-0.218-0.741-0.498-0.943-0.843c-0.202-0.346-0.321-0.73-0.358-1.156
c-0.037-0.427-0.058-0.857-0.058-1.292V14.12h-2.336v-1.751h2.336V9.381h2.021v2.987h2.896v1.751H95.762z"/>
<path fill="#5A74AF" d="M99.895,18.432c0,0.464,0.103,0.887,0.305,1.269c0.201,0.382,0.467,0.707,0.797,0.977
c0.329,0.271,0.711,0.479,1.146,0.629c0.434,0.15,0.883,0.226,1.348,0.226c0.628,0,1.175-0.146,1.639-0.438s0.891-0.678,1.28-1.157
l1.526,1.168c-1.123,1.451-2.694,2.179-4.715,2.179c-0.84,0-1.6-0.144-2.281-0.427c-0.681-0.284-1.256-0.678-1.729-1.18
c-0.471-0.502-0.834-1.093-1.088-1.774c-0.256-0.682-0.383-1.418-0.383-2.212s0.139-1.531,0.416-2.213
c0.275-0.681,0.658-1.271,1.145-1.773c0.487-0.501,1.067-0.895,1.741-1.18c0.674-0.284,1.407-0.426,2.201-0.426
c0.942,0,1.739,0.165,2.392,0.494c0.65,0.33,1.186,0.76,1.605,1.292c0.419,0.531,0.723,1.13,0.908,1.796
c0.188,0.667,0.281,1.345,0.281,2.033v0.719h-8.535V18.432z M106.271,16.814c-0.016-0.449-0.086-0.861-0.213-1.235
s-0.317-0.7-0.572-0.978c-0.256-0.276-0.572-0.493-0.954-0.65c-0.382-0.157-0.828-0.236-1.337-0.236
c-0.493,0-0.946,0.094-1.358,0.281c-0.411,0.188-0.76,0.43-1.044,0.729c-0.285,0.3-0.506,0.633-0.663,0.999
c-0.157,0.367-0.235,0.73-0.235,1.09H106.271z"/>
<path fill="#5A74AF" d="M110.854,12.368h2.021v1.639h0.045c0.135-0.284,0.314-0.542,0.539-0.774s0.475-0.431,0.752-0.595
c0.277-0.164,0.58-0.295,0.909-0.393c0.33-0.097,0.659-0.146,0.987-0.146c0.33,0,0.629,0.045,0.899,0.135l-0.091,2.178
c-0.165-0.045-0.329-0.082-0.494-0.112c-0.165-0.029-0.329-0.045-0.492-0.045c-0.988,0-1.746,0.277-2.27,0.831
c-0.523,0.554-0.786,1.415-0.786,2.583v5.345h-2.021V12.368L110.854,12.368z"/>
<path fill="#5E6060" d="M120.156,5.513h1.367v11.493h-1.367V5.513z"/>
<path fill="#5E6060" d="M123.484,7.215c0-0.274,0.1-0.509,0.298-0.707c0.196-0.198,0.433-0.296,0.706-0.296s0.51,0.099,0.707,0.296
c0.197,0.198,0.297,0.433,0.297,0.707c0,0.273-0.1,0.509-0.297,0.707c-0.197,0.198-0.434,0.297-0.707,0.297s-0.51-0.099-0.706-0.297
C123.584,7.725,123.484,7.489,123.484,7.215z M123.805,9.799h1.368v7.207h-1.368V9.799z"/>
<path fill="#5E6060" d="M134.566,16.944c0,0.548-0.094,1.047-0.28,1.498c-0.188,0.45-0.453,0.841-0.798,1.17
c-0.346,0.329-0.76,0.586-1.246,0.768c-0.487,0.183-1.024,0.272-1.611,0.272c-0.689,0-1.32-0.096-1.895-0.287
c-0.571-0.193-1.116-0.527-1.633-1.003l0.926-1.156c0.355,0.386,0.74,0.677,1.156,0.874c0.416,0.198,0.887,0.297,1.414,0.297
c0.506,0,0.928-0.074,1.262-0.221s0.601-0.337,0.799-0.569c0.197-0.234,0.337-0.5,0.418-0.799s0.121-0.601,0.121-0.904V15.82h-0.045
c-0.264,0.436-0.621,0.758-1.072,0.966s-0.926,0.312-1.421,0.312c-0.527,0-1.017-0.094-1.468-0.281s-0.838-0.446-1.164-0.775
c-0.323-0.329-0.576-0.72-0.76-1.171c-0.183-0.45-0.272-0.939-0.272-1.467c0-0.527,0.086-1.021,0.259-1.482
c0.172-0.461,0.418-0.864,0.737-1.208c0.319-0.345,0.704-0.613,1.155-0.806c0.449-0.192,0.955-0.289,1.513-0.289
c0.485,0,0.96,0.106,1.421,0.319s0.822,0.512,1.087,0.896h0.03V9.799h1.367V16.944L134.566,16.944z M130.828,10.894
c-0.365,0-0.694,0.063-0.988,0.19c-0.294,0.126-0.542,0.299-0.744,0.517c-0.203,0.218-0.359,0.481-0.472,0.791
c-0.112,0.309-0.167,0.646-0.167,1.011c0,0.729,0.213,1.315,0.639,1.756c0.426,0.44,1.004,0.661,1.732,0.661
s1.307-0.221,1.732-0.661c0.426-0.44,0.639-1.026,0.639-1.756c0-0.365-0.057-0.702-0.167-1.011
c-0.111-0.309-0.269-0.572-0.472-0.791c-0.203-0.218-0.451-0.39-0.744-0.517C131.521,10.958,131.193,10.894,130.828,10.894z"/>
<path fill="#5E6060" d="M136.29,5.513h1.368v5.397h0.029c0.172-0.385,0.472-0.697,0.896-0.935c0.425-0.238,0.917-0.357,1.475-0.357
c0.346,0,0.678,0.053,0.996,0.16c0.319,0.107,0.598,0.271,0.836,0.494s0.43,0.509,0.57,0.859c0.141,0.35,0.213,0.763,0.213,1.239
v4.637h-1.367V12.75c0-0.334-0.047-0.62-0.138-0.858c-0.092-0.238-0.213-0.431-0.364-0.578c-0.152-0.147-0.328-0.253-0.525-0.32
c-0.197-0.065-0.402-0.099-0.615-0.099c-0.283,0-0.547,0.046-0.79,0.137c-0.243,0.091-0.456,0.236-0.64,0.434
c-0.183,0.198-0.323,0.448-0.426,0.752c-0.101,0.304-0.15,0.664-0.15,1.079v3.71h-1.368V5.513L136.29,5.513z"/>
<path fill="#5E6060" d="M148.604,10.985h-1.961v3.269c0,0.203,0.006,0.402,0.016,0.601c0.01,0.197,0.048,0.375,0.113,0.532
c0.065,0.157,0.168,0.283,0.305,0.38s0.337,0.145,0.602,0.145c0.161,0,0.328-0.016,0.502-0.046c0.172-0.03,0.328-0.086,0.471-0.167
v1.246c-0.162,0.092-0.373,0.154-0.631,0.19c-0.258,0.035-0.459,0.053-0.601,0.053c-0.526,0-0.937-0.073-1.224-0.221
c-0.289-0.146-0.502-0.336-0.639-0.569c-0.138-0.233-0.22-0.494-0.244-0.783c-0.024-0.289-0.037-0.58-0.037-0.874v-3.755h-1.582
V9.799h1.582V7.778h1.367v2.021h1.961V10.985L148.604,10.985z"/>
<path fill="#95BDE5" d="M142.227,20.314c-0.039,0.215-0.129,0.432-0.129,0.432c-0.023,0.059-0.106,0.25-0.125,0.288
c-0.063,0.138-0.097,0.224-0.17,0.38c-0.084,0.178,0,0-0.136,0.268c-1.126,2.234-4.158,4.756-8.376,4.658
c-3.922-0.09-6.719-1.806-8.072-4.173c-0.103-0.181-0.262-0.42-0.383-0.685c-0.033-0.073-0.242-0.51-0.266-0.574
c-0.115-0.333-0.199-0.368-0.215-0.594c0,0,0.258,0.528,0.778,1.09c1.228,1.326,3.915,3.427,8.156,3.478
c4.144,0.05,6.907-2.123,8.163-3.478C141.973,20.85,142.227,20.314,142.227,20.314z"/>
</svg>

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -5,6 +5,10 @@
/* ---------------------------------------------
Scripts initialization
--------------------------------------------- */
var minRam = 1;
if(window.minRam){
minRam = window.minRam;
}
var cardPricing = {
'cpu': {
'id': 'coreValue',
@ -16,7 +20,7 @@
'ram': {
'id': 'ramValue',
'value': 2,
'min': 1,
'min': minRam,
'max': 200,
'interval': 1
},
@ -40,6 +44,7 @@
_initNavUrl();
_initPricing();
ajaxForms();
$('#ramValue').data('old-value', $('#ramValue').val());
});
$(window).resize(function() {
@ -104,17 +109,33 @@
});
$('.url').click(function(event) {
event.preventDefault();
var href = $(this).attr('href');
var $this = $(this);
var href = $this.attr('href');
$('.navbar-collapse').removeClass('in');
$('.navbar-collapse').addClass('collapsing');
if ($(href).length) {
$('html, body').animate({
scrollTop: $(href).offset().top - 50
}, 1000);
if (href[0] === "#") {
scrollToElement(href);
} else if (href) {
var path = $(this).prop('href').split('#');
var currentPath = window.location.origin + window.location.pathname;
if (currentPath == path[0] && path[1]) {
scrollToElement('#' + path[1]);
} else {
window.location = href;
}
}
});
}
function scrollToElement(el) {
var $el = $(el);
if ($el.length) {
$('html, body').animate({
scrollTop: $el.offset().top - 50
}, 1000);
}
}
function verifiedUrl() {
if (window.location.href.indexOf('#success') > -1) {
form_success();
@ -128,21 +149,54 @@
var data = $(this).data('minus');
if (cardPricing[data].value > cardPricing[data].min) {
cardPricing[data].value = Number(cardPricing[data].value) - cardPricing[data].interval;
if(data === 'ram' && String(cardPricing[data].value) === "1" && minRam === 0.5){
cardPricing[data].value = 0.5;
$('#ramValue').val('0.5');
$("#ramValue").attr('step', 0.5);
} else {
cardPricing[data].value = Number(cardPricing[data].value) - cardPricing[data].interval;
}
}
_fetchPricing();
$('#ramValue').data('old-value', $('#ramValue').val());
});
$('.fa-plus-circle.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;
if(data === 'ram' && String(cardPricing[data].value) === "0.5" && minRam === 0.5){
cardPricing[data].value = 1;
$('#ramValue').val('1');
$("#ramValue").attr('step', 1);
} else {
cardPricing[data].value = Number(cardPricing[data].value) + cardPricing[data].interval;
}
}
_fetchPricing();
$('#ramValue').data('old-value', $('#ramValue').val());
});
$('.input-price').change(function() {
var data = $(this).attr("name");
cardPricing[data].value = $('input[name=' + data + ']').val();
var input = $('input[name=' + data + ']');
var inputValue = input.val();
if(data === 'ram') {
var ramInput = $('#ramValue');
if ($('#ramValue').data('old-value') < $('#ramValue').val()) {
if($('#ramValue').val() === '1' && minRam === 0.5) {
$("#ramValue").attr('step', 1);
$('#ramValue').val('1');
}
} else {
if($('#ramValue').val() === '0' && minRam === 0.5) {
$("#ramValue").attr('step', 0.5);
$('#ramValue').val('0.5');
}
}
inputValue = $('#ramValue').val();
$('#ramValue').data('old-value', $('#ramValue').val());
}
cardPricing[data].value = inputValue;
_fetchPricing();
});
}
@ -155,7 +209,22 @@
}
function _calcPricing() {
var total = (cardPricing['cpu'].value * 5) + (2 * cardPricing['ram'].value) + (0.6 * cardPricing['storage'].value);
if(typeof window.coresUnitPrice === 'undefined'){
window.coresUnitPrice = 5;
}
if(typeof window.ramUnitPrice === 'undefined'){
window.ramUnitPrice = 2;
}
if(typeof window.ssdUnitPrice === 'undefined'){
window.ssdUnitPrice = 0.6;
}
if(typeof window.discountAmount === 'undefined'){
window.discountAmount = 0;
}
var total = (cardPricing['cpu'].value * window.coresUnitPrice) +
(cardPricing['ram'].value * window.ramUnitPrice) +
(cardPricing['storage'].value * window.ssdUnitPrice) -
window.discountAmount;
total = parseFloat(total.toFixed(2));
$("#total").text(total);
}

View File

@ -1,23 +1,26 @@
from datetime import datetime
from celery import current_task
from celery.exceptions import MaxRetriesExceededError
from celery.utils.log import get_task_logger
from celery import current_task
from django.conf import settings
from django.core.mail import EmailMessage
from django.core.urlresolvers import reverse
from django.utils import translation
from django.utils.translation import ugettext_lazy as _
from time import sleep
from dynamicweb.celery import app
from hosting.models import HostingOrder, HostingBill
from membership.models import StripeCustomer, CustomUser
from hosting.models import HostingOrder
from membership.models import CustomUser
from opennebula_api.models import OpenNebulaManager
from opennebula_api.serializers import VirtualMachineSerializer
from utils.hosting_utils import get_all_public_keys, get_or_create_vm_detail
from utils.forms import UserBillingAddressForm
from utils.hosting_utils import (
get_all_public_keys, get_or_create_vm_detail, ping_ok
)
from utils.mailer import BaseEmail
from utils.models import BillingAddress
from utils.stripe_utils import StripeUtils
from .models import VMPricing
logger = get_task_logger(__name__)
@ -49,23 +52,15 @@ def retry_task(task, exception=None):
@app.task(bind=True, max_retries=settings.CELERY_MAX_RETRIES)
def create_vm_task(self, vm_template_id, user, specs, template,
stripe_customer_id, billing_address_data,
stripe_subscription_id, cc_details):
def create_vm_task(self, vm_template_id, user, specs, template, order_id):
logger.debug(
"Running create_vm_task on {}".format(current_task.request.hostname))
vm_id = None
try:
final_price = specs.get('price')
billing_address = BillingAddress(
cardholder_name=billing_address_data['cardholder_name'],
street_address=billing_address_data['street_address'],
city=billing_address_data['city'],
postal_code=billing_address_data['postal_code'],
country=billing_address_data['country']
final_price = (
specs.get('total_price') if 'total_price' in specs
else specs.get('price')
)
billing_address.save()
customer = StripeCustomer.objects.filter(id=stripe_customer_id).first()
if 'pass' in user:
on_user = user.get('email')
@ -94,33 +89,43 @@ def create_vm_task(self, vm_template_id, user, specs, template,
if vm_id is None:
raise Exception("Could not create VM")
# Create a Hosting Order
order = HostingOrder.create(
price=final_price,
vm_id=vm_id,
customer=customer,
billing_address=billing_address
# Update HostingOrder with the created vm_id
hosting_order = HostingOrder.objects.filter(id=order_id).first()
error_msg = None
try:
hosting_order.vm_id = vm_id
hosting_order.save()
logger.debug(
"Updated hosting_order {} with vm_id={}".format(
hosting_order.id, vm_id
)
)
except Exception as ex:
error_msg = (
"HostingOrder with id {order_id} not found. This means that "
"the hosting order was not created and/or it is/was not "
"associated with VM with id {vm_id}. Details {details}".format(
order_id=order_id, vm_id=vm_id, details=str(ex)
)
)
logger.error(error_msg)
stripe_utils = StripeUtils()
result = stripe_utils.set_subscription_metadata(
subscription_id=hosting_order.subscription_id,
metadata={"VM_ID": str(vm_id)}
)
# Create a Hosting Bill
HostingBill.create(
customer=customer, billing_address=billing_address)
# Create Billing Address for User if he does not have one
if not customer.user.billing_addresses.count():
billing_address_data.update({
'user': customer.user.id
})
billing_address_user_form = UserBillingAddressForm(
billing_address_data)
billing_address_user_form.is_valid()
billing_address_user_form.save()
# Associate an order with a stripe subscription
order.set_subscription_id(stripe_subscription_id, cc_details)
# If the Stripe payment succeeds, set order status approved
order.set_approved()
if result.get('error') is not None:
emsg = "Could not update subscription metadata for {sub}".format(
sub=hosting_order.subscription_id
)
logger.error(emsg)
if error_msg:
error_msg += ". " + emsg
else:
error_msg = emsg
vm = VirtualMachineSerializer(manager.get_vm(vm_id)).data
@ -130,12 +135,19 @@ def create_vm_task(self, vm_template_id, user, specs, template,
'cores': specs.get('cpu'),
'memory': specs.get('memory'),
'storage': specs.get('disk_size'),
'price': specs.get('price'),
'price': final_price,
'template': template.get('name'),
'vm_name': vm.get('name'),
'vm_id': vm['vm_id'],
'order_id': order.id
'order_id': order_id
}
if error_msg:
context['errors'] = error_msg
if 'pricing_name' in specs:
context['pricing'] = str(VMPricing.get_vm_pricing_by_name(
name=specs['pricing_name']
))
email_data = {
'subject': settings.DCL_TEXT + " Order from %s" % context['email'],
'from_email': settings.DCL_SUPPORT_FROM_ADDRESS,
@ -159,7 +171,7 @@ def create_vm_task(self, vm_template_id, user, specs, template,
'base_url': "{0}://{1}".format(user.get('request_scheme'),
user.get('request_host')),
'order_url': reverse('hosting:orders',
kwargs={'pk': order.id}),
kwargs={'pk': order_id}),
'page_header': _(
'Your New VM %(vm_name)s at Data Center Light') % {
'vm_name': vm.get('name')},
@ -176,11 +188,11 @@ def create_vm_task(self, vm_template_id, user, specs, template,
email = BaseEmail(**email_data)
email.send()
# try to see if we have the IP and that if the ssh keys can
# be configured
new_host = manager.get_primary_ipv4(vm_id)
# try to see if we have the IPv6 of the new vm and that if the ssh
# keys can be configured
vm_ipv6 = manager.get_ipv6(vm_id)
logger.debug("New VM ID is {vm_id}".format(vm_id=vm_id))
if new_host is not None:
if vm_ipv6 is not None:
custom_user = CustomUser.objects.get(email=user.get('email'))
get_or_create_vm_detail(custom_user, manager, vm_id)
if custom_user is not None:
@ -191,13 +203,48 @@ def create_vm_task(self, vm_template_id, user, specs, template,
logger.debug(
"Calling configure on {host} for "
"{num_keys} keys".format(
host=new_host, num_keys=len(keys)))
# Let's delay the task by 75 seconds to be sure
# that we run the cdist configure after the host
# is up
manager.manage_public_key(keys,
hosts=[new_host],
countdown=75)
host=vm_ipv6, num_keys=len(keys)
)
)
# Let's wait until the IP responds to ping before we
# run the cdist configure on the host
did_manage_public_key = False
for i in range(0, 15):
if ping_ok(vm_ipv6):
logger.debug(
"{} is pingable. Doing a "
"manage_public_key".format(vm_ipv6)
)
sleep(10)
manager.manage_public_key(
keys, hosts=[vm_ipv6]
)
did_manage_public_key = True
break
else:
logger.debug(
"Can't ping {}. Wait 5 secs".format(
vm_ipv6
)
)
sleep(5)
if not did_manage_public_key:
emsg = ("Waited for over 75 seconds for {} to be "
"pingable. But the VM was not reachable. "
"So, gave up manage_public_key. Please do "
"this manually".format(vm_ipv6))
logger.error(emsg)
email_data = {
'subject': '{} CELERY TASK INCOMPLETE: {} not '
'pingable for 75 seconds'.format(
settings.DCL_TEXT, vm_ipv6
),
'from_email': current_task.request.hostname,
'to': settings.DCL_ERROR_EMAILS_TO_LIST,
'body': emsg
}
email = EmailMessage(**email_data)
email.send()
except Exception as e:
logger.error(str(e))
try:

View File

@ -9,7 +9,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Data Center Light by ungleich">
<meta name="author" content="ungleich GmbH">
<meta name="author" content="ungleich glarus ag">
<title>Data Center Light - {% block title %}VM hosting made in Switzerland{% endblock %}</title>
<!-- Vendor CSS -->

View File

@ -1,4 +1,4 @@
{% load staticfiles i18n %}
{% load staticfiles i18n cms_tags sekizai_tags %}
{% get_current_language as LANGUAGE_CODE %}
<!DOCTYPE html>
@ -9,7 +9,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Data Center Light by ungleich">
<meta name="author" content="ungleich GmbH">
<meta name="author" content="ungleich glarus ag">
<title>Data Center Light - {% block title %}VM hosting made in Switzerland{% endblock %}</title>
@ -23,9 +23,15 @@
<!-- Custom CSS -->
<link href="{% static 'datacenterlight/css/common.css' %}" rel="stylesheet">
<link href="{% static 'datacenterlight/css/hosting.css' %}" rel="stylesheet">
{% if request.toolbar.edit_mode %}
<link href="{% static 'datacenterlight/css/cms.css' %}" rel="stylesheet">
{% endif %}
{% block css_extra %}
{% endblock css_extra %}
{% render_block "css" postprocessor "compressor.contrib.sekizai.compress" %}
{% render_block "js" postprocessor "compressor.contrib.sekizai.compress" %}
<!-- External Fonts -->
<link rel="shortcut icon" href="{% static 'datacenterlight/img/favicon.ico' %}" type="image/x-icon">
@ -43,28 +49,17 @@
</head>
<body>
{% cms_toolbar %}
{% block navbar %}
{% include "hosting/includes/_navbar_user.html" %}
{% endblock navbar %}
{% render_placeholder cms_integration.navbar_placeholder %}
<div class="{% if request.user.is_authenticated %}content-dashboard{% endif %}">
{% block content %}
{% endblock %}
</div>
<!-- Footer -->
{% if request.user.is_authenticated %}
<footer class="footer-vm">
<div class="container">
<p class="copyright text-muted small">Copyright &copy; ungleich GmbH {% now "Y" %}. {% trans "All Rights Reserved" %}</p>
</div>
</footer>
{% else %}
<div class="footer-vm">
{% include "datacenterlight/includes/_footer.html" %}
</div>
{% endif %}
{% render_placeholder cms_integration.footer_placeholder %}
<!-- Moment -->
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js"></script>

View File

@ -8,9 +8,9 @@
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Data Center Light by ungleich">
<meta name="author" content="ungleich GmbH">
<title>{% page_attribute page_title %}</title>
<meta name="author" content="ungleich glarus ag">
<meta name="description" content="{% page_attribute 'meta_description' %}">
<title>{% page_attribute "page_title" %}</title>
<!-- Vendor CSS -->
<!-- Bootstrap Core CSS -->
@ -30,7 +30,11 @@
<!-- External Fonts -->
<link href="//fonts.googleapis.com/css?family=Lato:300,400,600,700" rel="stylesheet" type="text/css">
<link rel="shortcut icon" href="{% static 'datacenterlight/img/favicon.ico' %}" type="image/x-icon">
{% if request.current_page.cmsfaviconextension %}
<link rel="shortcut icon" href="{% static request.current_page.cmsfaviconextension.favicon.url %}" type="image/x-icon">
{% else %}
<link rel="shortcut icon" href="{% static 'datacenterlight/img/favicon.ico' %}" type="image/x-icon">
{% endif %}
<!-- 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:// -->
@ -52,11 +56,12 @@
{% placeholder 'Datacenterlight Header' or %}
<div class="dcl-header">
<div class="container">
<h1>{% page_attribute page_title %}</h1>
<h1>{% page_attribute "page_title" %}</h1>
</div>
</div>
{% endplaceholder %}
{% url 'datacenterlight:index' as calculator_form_url %}
{% placeholder 'Datacenterlight Content' %}
{% placeholder 'datacenterlight_footer'%}

View File

@ -1,29 +1,5 @@
<div class="split-section {{ instance.get_extra_classes }}" id="{{ instance.html_id }}">
<div class="container">
<div class="row">
<div class="col-sm-6 {% if instance.text_direction == 'right' %}col-sm-push-6{% endif %}">
<div class="split-text">
{% if instance.heading %}
<div class="{% if not instance.plain_heading %}split-title{% endif %}">
<h2>{{ instance.heading }}</h2>
</div>
{% endif %}
{% if instance.content %}
<div class="split-description">
<div class="lead">
{{ instance.content }}
</div>
</div>
{% endif %}
</div>
</div>
<div class="col-sm-6 {% if instance.text_direction == 'right' %}col-sm-pull-6{% endif %}">
<div class="price-calc-section">
<div class="card">
{% include "datacenterlight/includes/_calculator_form.html" %}
</div>
</div>
</div>
</div>
<div class="price-calc-section">
<div class="card">
{% include "datacenterlight/includes/_calculator_form.html" with vm_pricing=instance.pricing %}
</div>
</div>

View File

@ -1,11 +1,11 @@
<div id="{{ instance.id }}" class="full-contact-section">
<div id="contact" class="full-contact-section">
<div class="intro-header-2 contact-section">
<div class="container">
<div class="row">
<div class="col-sm-6">
{% if instance.contact_text %}
{% if instance.heading%}
<div class="title">
<h2>{{ instance.contact_text }}</h2>
<h2>{{ instance.heading}}</h2>
</div>
{% endif %}
<div class="contact-details">
@ -40,4 +40,4 @@
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,21 @@
{% load cms_tags %}
{% if instance.heading %}
<div class="{% if not instance.plain_heading %}split-title{% else %}split-title-plain{% endif %}">
<h2>{{ instance.heading }}</h2>
</div>
{% endif %}
{% if instance.content %}
<div class="split-description">
<div class="lead">
{{ instance.content }}
</div>
</div>
{% endif %}
{% if children_to_content|length %}
<div class="split-subsection">
{% for plugin in children_to_content %}
{% render_plugin plugin %}
{% endfor %}
</div>
{% endif %}

View File

@ -10,8 +10,13 @@
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a href="{% url 'datacenterlight:index' %}" id="logoBlack" class="navbar-brand topnav"><img src="{% static 'datacenterlight/img/logo_black.svg' %}"></a>
<a href="{% url 'datacenterlight:index' %}" id="logoWhite" class="navbar-brand topnav"><img src="{% static 'datacenterlight/img/logo_white.svg' %}"></a>
{% if instance.logo_dark or instance.logo_light %}
<a href="{{ instance.logo_url|default:'/' }}" id="logoBlack" class="navbar-brand"><img src="{{ instance.get_logo_dark }}"></a>
<a href="{{ instance.logo_url|default:'/' }}" id="logoWhite" class="navbar-brand"><img src="{{ instance.get_logo_light }}"></a>
{% else %}
<a href="/" id="logoBlack" class="navbar-brand"><img src="{% static 'datacenterlight/img/logo_black.svg' %}"></a>
<a href="/" id="logoWhite" class="navbar-brand"><img src="{% static 'datacenterlight/img/logo_white.svg' %}"></a>
{% endif %}
</div>
<div class="collapse navbar-collapse" id="dcl-topnav">
<!-- Start Navbar collapse-->
@ -21,21 +26,25 @@
{% render_plugin plugin %}
</li>
{% endfor %}
<li>
{% if LANGUAGE_CODE == 'en-us'%}
<a class="on-hover-border" href="{% change_lang 'de' %}">Deutsch&nbsp;&nbsp;<i class="fa fa-globe" aria-hidden="true"></i></a>
{% if instance.language_dropdown %}
<li>
{% if LANGUAGE_CODE == 'en-us'%}
<a class="on-hover-border" href="{% change_lang 'de' %}">Deutsch&nbsp;&nbsp;<i class="fa fa-globe" aria-hidden="true"></i></a>
{% else %}
<a class="on-hover-border" href="{% change_lang 'en-us' %}">English&nbsp;&nbsp;<i class="fa fa-globe" aria-hidden="true"></i></a>
{% endif %}
</li>
{% endif %}
{% if instance.show_login_option %}
{% if not request.user.is_authenticated %}
<li>
<a href="{% url 'hosting:login' %}">{% trans "Login" %}&nbsp;&nbsp;<span class="fa fa-sign-in"></span></a>
</li>
{% else %}
<a class="on-hover-border" href="{% change_lang 'en-us' %}">English&nbsp;&nbsp;<i class="fa fa-globe" aria-hidden="true"></i></a>
<li>
<a href="{% url 'hosting:dashboard' %}">{% trans "Dashboard" %}</a>
</li>
{% endif %}
</li>
{% if not request.user.is_authenticated %}
<li>
<a href="{% url 'hosting:login' %}">{% trans "Login" %}&nbsp;&nbsp;<span class="fa fa-sign-in"></span></a>
</li>
{% else %}
<li>
<a href="{% url 'hosting:dashboard' %}">{% trans "Dashboard" %}</a>
</li>
{% endif %}
{% comment %}
<!-- to be used when more than one option for language -->
@ -61,4 +70,4 @@
{% endcomment %}
</ul>
</div>
</nav>
</nav>

View File

@ -1,10 +1,10 @@
{% load cms_tags %}
<div class="dropdown highlights-dropdown">
<a class="dropdown-toggle url-init dcl-link" href="{{ instance.url|default:'#' }}" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{{ instance.text }}&nbsp;<span class="caret"></span></a>
<a class="dropdown-toggle url-init dcl-link" href="{{ instance.target|default:'#' }}" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{{ instance.text }}&nbsp;<span class="caret"></span></a>
<ul class="dropdown-menu">
{% for plugin in instance.child_plugin_instances %}
{% render_plugin plugin %}
{% endfor %}
</ul>
</div>
</div>

View File

@ -1,46 +1,31 @@
{% load cms_tags %}
<div class="split-section {{ instance.get_extra_classes }}" id="{{ instance.html_id }}">
<section class="split-section {{ instance.get_extra_classes }}" id="{{ instance.html_id }}">
<div class="container">
{% if instance.child_plugin_instances|length %}
{% if children_to_side|length or children_calculator|length %}
<div class="row">
<div class="col-sm-6 {% if instance.text_direction == 'right' %}col-sm-push-6{% endif %} split-text">
{% if instance.heading %}
<div class="{% if not instance.plain_heading %}split-title{% else %}split-title-plain{% endif %}">
<h2>{{ instance.heading }}</h2>
</div>
{% endif %}
{% if instance.content %}
<div class="split-description">
<div class="lead">
{{ instance.content }}
</div>
</div>
{% endif %}
{% include "datacenterlight/cms/includes/_section_split_content.html" %}
</div>
<div class="col-sm-6 {% if instance.text_direction == 'right' %}col-sm-pull-6{% endif %} split-figure">
<div class="section-figure">
{% for plugin in instance.child_plugin_instances %}
{% if children_calculator|length %}
{% for plugin in children_calculator %}
{% render_plugin plugin %}
{% endfor %}
</div>
{% endif %}
{% if children_to_side %}
<div class="section-figure">
{% for plugin in children_to_side %}
{% render_plugin plugin %}
{% endfor %}
</div>
{% endif %}
</div>
</div>
{% else %}
<div class="space">
{% if instance.heading %}
<div class="{% if not instance.plain_heading %}split-title{% else %}split-title-plain{% endif %}">
<h2>{{ instance.heading }}</h2>
</div>
{% endif %}
{% if instance.content %}
<div class="split-description">
<div class="lead">
{{ instance.content }}
</div>
</div>
{% endif %}
{% include "datacenterlight/cms/includes/_section_split_content.html" %}
</div>
{% endif %}
</div>
</div>
</section>

View File

@ -0,0 +1,15 @@
{% load custom_tags %}
<section class="promo-section {{instance.get_extra_classes}}" {% if instance.background_image %}style="background-image:url({{ instance.background_image.url }})"{% endif %}>
<div class="container">
{% if instance.heading %}
<h3>{{instance.heading|escaped_line_break|linebreaksbr}}</h3>
{% endif %}
{% if instance.subheading %}
<h4>{{instance.subheading}}</h4>
{% endif %}
{% if instance.content %}
<p>{{instance.content}}</p>
{% endif %}
</div>
</section>

View File

@ -14,7 +14,7 @@
<table style="width: 100%; border-spacing: 0; border-collapse: collapse; max-width: 560px;">
<tr>
<td>
<img src="{{ base_url }}{% static 'datacenterlight/img/logo_black.png' %}" style="width: 200px; height: 50px;">
<img src="{{ base_url }}{% static 'datacenterlight/img/datacenterlight.png' %}" style="max-width: 200px;">
</td>
</tr>
<tr>
@ -33,18 +33,19 @@
<p style="color: #4382c8; line-height: 1.4; font-family: Lato, Arial, sans-serif; font-weight: 300; margin: 0;">
{{base_url}}{{activation_link}}
</p>
<p>
{% if account_details %}
{% url 'hosting:reset_password' as reset_password_url %}
<p style="line-height: 1.75; font-family: Lato, Arial, sans-serif; font-weight: 300; margin-bottom: 10px; margin-top: 10px;">
{% trans "Your account details are as follows" %}:
{% trans "Username" %} : {% trans "Your email address" %}
</p>
<p style="line-height: 1.75; font-family: Lato, Arial, sans-serif; font-weight: 300; margin-bottom: 10px; margin-top: 0;">
{% trans "Username" %} : {% trans "Your email address" %}<br/>
{% trans "Password" %} : {{account_details}}
{% trans "You can reset your password here" %}:
{{base_url}}{{reset_password_url}}
</p>
<p style="line-height: 1.75; font-family: Lato, Arial, sans-serif; font-weight: 300; margin-bottom: 0; margin-top: 0;">
{% blocktrans %}You can reset your password <a href="{{base_url}}{{reset_password_url}}" style="text-decoration: none; color: #4382c8; font-weight: 400;">here</a>.{% endblocktrans %}
</p>
{% endif %}
</p>
</td>
</tr>
<tr>

View File

@ -14,7 +14,7 @@
<table style="width: 100%; border-spacing: 0; border-collapse: collapse; max-width: 560px;">
<tr>
<td>
<img src="{{ base_url }}{% static 'datacenterlight/img/logo_black.png' %}" style="width: 200px; height: 50px;">
<img src="{{ base_url }}{% static 'datacenterlight/img/datacenterlight.png' %}" style="max-width: 200px;">
</td>
</tr>
<tr>

View File

@ -1,14 +1,35 @@
{% load staticfiles i18n%}
<form id="order_form" method="POST" action="{% url 'datacenterlight:index' %}" data-toggle="validator" role="form">
{% if vm_pricing %}
<script type="application/javascript">
window.vat_inclusive = {% if vm_pricing.vat_inclusive %}true{% else %}false{% endif%};
window.vat_percentage = {{vm_pricing.vat_percentage|default:0}};
window.coresUnitPrice = {{vm_pricing.cores_unit_price|default:0}};
window.ramUnitPrice = {{vm_pricing.ram_unit_price|default:0}};
window.ssdUnitPrice = {{vm_pricing.ssd_unit_price|default:0}};
window.hddUnitPrice = {{vm_pricing.hdd_unit_price|default:0}};
window.discountAmount = {{vm_pricing.discount_amount|default:0}};
window.minRam = {{min_ram}};
window.minRamErr = '{% blocktrans with min_ram=min_ram %}Please enter a value in range {{min_ram}} - 200.{% endblocktrans %}';
</script>
{% endif %}
<form id="order_form" method="POST" action="{{calculator_form_url}}" data-toggle="validator" role="form">
{% csrf_token %}
<input type="hidden" name="pid" value="{{instance.id}}">
<div class="title">
<h3>{% trans "VM hosting" %} </h3>
</div>
<div class="price">
<span id="total">15</span>
<span id="total"></span>
<span>CHF/{% trans "month" %}</span>
<div class="price-text">
<p>{% trans "VAT included" %}</p>
<p>
{% if vm_pricing.vat_inclusive %}{% trans "VAT included" %} <br>{% endif %}
{% if vm_pricing.discount_amount %}
{% trans "You save" %} {{ vm_pricing.discount_amount }} CHF
{% endif %}
</p>
</div>
</div>
<div class="descriptions">
@ -36,8 +57,8 @@
<div class="form-group">
<div class="description input">
<i class="fa fa-minus-circle left" data-minus="ram" aria-hidden="true"></i>
<input id="ramValue" class="input-price select-number" type="number" min="1" max="200" name="ram"
data-error="{% trans 'Please enter a value in range 1 - 200.' %}" required>
<input id="ramValue" class="input-price select-number" type="number" min="{% if min_ram == 0.5 %}0{% else %}1{% endif %}" max="200" name="ram"
data-error="{% blocktrans with min_ram=min_ram %}Please enter a value in range {{min_ram}} - 200.{% endblocktrans %}" required step="1">
<span> GB RAM</span>
<i class="fa fa-plus-circle right" data-plus="ram" aria-hidden="true"></i>
</div>
@ -73,10 +94,12 @@
<label for="config">OS</label>
<select name="config">
{% for template in templates %}
<option value="{{template.opennebula_vm_template_id}}">{{template.name}}</option>
<option value="{{template.opennebula_vm_template_id}}" {% if template.name|lower == instance.default_selected_template|lower %}selected="selected"{% endif %}>{{template.name}}</option>
{% endfor %}
</select>
</div>
</div>
<input type="hidden" name="pricing_name" value="{% if vm_pricing.name %}{{vm_pricing.name}}{% else %}unknown{% endif%}"></input>
<input type="submit" class="btn btn-primary disabled" value="{% trans 'Continue' %}"></input>
</form>

View File

@ -3,42 +3,19 @@
<footer>
<div class="container">
<ul class="list-inline">
{% if request.resolver_match.url_name != "index" %}
<li>
<a href="{% url 'datacenterlight:index' %}">{% trans "Home" %}</a>
</li>
<li class="footer-menu-divider">&sdot;</li>
{% endif %}
<li>
<a class="url-init" href="{% url 'datacenterlight:index' %}#how">{% trans "Highlights" %}</a>
<a class="url-init" href="https://{{MULTISITE_CMS_FALLBACK}}">{% trans "Home" %}</a>
</li>
<li class="footer-menu-divider">&sdot;</li>
<li>
<a class="url-init" href="{% url 'datacenterlight:index' %}#your">{% trans "Scale out" %}</a>
<a class="url-init" href="https://{{MULTISITE_CMS_FALLBACK}}#contact">{% trans "Contact" %}</a>
</li>
<li>
<a class="url-init" href="{% url 'datacenterlight:index' %}#our">{% trans "Reliable and light" %}</a>
</li>
{% if request.resolver_match.url_name != "index" %}
<li>
<a class="url-init" href="{% url 'datacenterlight:index' %}#price">{% trans "Pricing" %}</a>
</li>
<li class="footer-menu-divider">&sdot;</li>
{% else %}
<li>
<a class="url-init" href="{% url 'datacenterlight:index' %}#price">{% trans "Order VM" %}</a>
</li>
{% endif %}
<li class="footer-menu-divider">&sdot;</li>
<li>
<a class="url-init" href="{% url 'datacenterlight:index' %}#contact">{% trans "Contact" %}</a>
</li>
{% if request.resolver_match.url_name != "index" %}
<li class="footer-menu-divider">&sdot;</li>
{% endif %}
<li>
<a class="url-init" href="/cms/terms-of-service">{% trans "Terms of Service" %}</a>
<a class="url-init" href="https://{{MULTISITE_CMS_FALLBACK}}/cms/terms-of-service">{% trans "Terms of Service" %}</a>
</li>
</ul>
<p class="copyright text-muted small">Copyright &copy; ungleich GmbH {% now "Y" %}. {% trans "All Rights Reserved" %}</p>
<p class="copyright text-muted small">Copyright &copy; ungleich glarus ag {% now "Y" %}. {% trans "All Rights Reserved" %}</p>
</div>
</footer>

View File

@ -16,19 +16,8 @@
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<!-- Start Navbar collapse-->
<ul class="nav navbar-nav navbar-right">
<li class="dropdown highlights-dropdown">
<a class="dropdown-toggle url-init" href="{% url 'datacenterlight:index' %}#how" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{% trans "Highlights" %}&nbsp;<span class="caret"></span></a>
<ul class="dropdown-menu ">
<li><a class="url-init" href="{% url 'datacenterlight:index' %}#your">{% trans "Scale out" %}</a></li>
<li><a class="url-init" href="{% url 'datacenterlight:index' %}#our">{% trans "Reliable and light" %}</a></li>
<li> <a class="url-init" href="{% url 'datacenterlight:index' %}#price">{% trans "Order VM" %}</a></li>
</ul>
</li>
<li>
<a href="{% url 'datacenterlight:whydatacenterlight' %}">{% trans "Why Data Center Light?" %}</a>
</li>
<li>
<a class="url-init" href="{% url 'datacenterlight:index' %}#contact">{% trans "Contact" %}</a>
<a class="url-init" href="https://datacenterlight.ch/en-us/cms/datacenterlight/#contact">{% trans "Contact" %}</a>
</li>
<li>
{% if LANGUAGE_CODE == 'en-us'%}

View File

@ -1,5 +1,5 @@
{% extends "datacenterlight/base_hosting.html" %}
{% load staticfiles bootstrap3 i18n %}
{% load staticfiles bootstrap3 i18n cms_tags humanize %}
{% block css_extra %}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/paymentfont/1.1.2/css/paymentfont.min.css"/>
@ -67,110 +67,102 @@
</div>
<div class="dcl-payment-box">
<div class="dcl-payment-section">
<h3>{%trans "Your Order" %}</h3>
<hr class="top-hr">
<div class="dcl-payment-order">
<p>{% trans "Cores"%} <strong class="pull-right">{{request.session.specs.cpu|floatformat}}</strong></p>
<hr>
<p>{% trans "Memory"%} <strong class="pull-right">{{request.session.specs.memory|floatformat}} GB</strong></p>
<hr>
<p>{% trans "Disk space"%} <strong class="pull-right">{{request.session.specs.disk_size|floatformat}} GB</strong></p>
<hr>
<p>{% trans "Configuration"%} <strong class="pull-right">{{request.session.template.name}}</strong></p>
<hr>
<p class="last-p"><strong>{%trans "Total" %}</strong>&nbsp;&nbsp;<small>({%trans "including VAT" %})</small> <strong class="pull-right">{{request.session.specs.price}} CHF/{% trans "Month" %}</strong></p>
</div>
{% if generic_payment_form %}
<h3>{%trans "Make a payment" %}</h3>
<hr class="top-hr">
<form role="form" id="generic-payment-form" method="post" action="" novalidate>
{% csrf_token %}
<input type="hidden" name="product" value="1" />
{% for field in generic_payment_form %}
{% bootstrap_field field type='fields'%}
{% endfor %}
<p class="text-danger">{{generic_payment_form.non_field_errors|striptags}}</p>
</form>
{% else %}
<h3>{%trans "Your Order" %}</h3>
<hr class="top-hr">
<div class="dcl-payment-order">
<p>{% trans "Cores"%} <strong class="pull-right">{{request.session.specs.cpu|floatformat}}</strong></p>
<hr>
<p>{% trans "Memory"%} <strong class="pull-right">{{request.session.specs.memory|floatformat}} GB</strong></p>
<hr>
<p>{% trans "Disk space"%} <strong class="pull-right">{{request.session.specs.disk_size|floatformat}} GB</strong></p>
<hr>
<p>{% trans "Configuration"%} <strong class="pull-right">{{request.session.template.name}}</strong></p>
<hr>
<p>
<strong>{%trans "Total" %}</strong>&nbsp;&nbsp;
<small>
({% if vm_pricing.vat_inclusive %}{%trans "including VAT" %}{% else %}{%trans "excluding VAT" %}{% endif %})
</small>
<strong class="pull-right">{{request.session.specs.price|intcomma}} CHF/{% trans "Month" %}</strong>
</p>
<hr>
{% if vm_pricing.discount_amount %}
<p class="mb-0">
{%trans "Discount" as discount_name %}
<strong>{{ vm_pricing.discount_name|default:discount_name }}</strong>&nbsp;&nbsp;
<strong class="pull-right text-primary">- {{ vm_pricing.discount_amount }} CHF/{% trans "Month" %}</strong>
</p>
<p>
({% trans "Will be applied at checkout" %})
</p>
{% endif %}
</div>
{% endif %}
</div>
</div>
<div class="dcl-payment-box">
<div class="dcl-payment-section">
{% with card_list_len=cards_list|length %}
<h3><b>{%trans "Credit Card"%}</b></h3>
<hr class="top-hr">
<p>
{% blocktrans %}Please fill in your credit card information below. We are using <a href="https://stripe.com" target="_blank">Stripe</a> for payment and do not store your information in our database.{% endblocktrans %}
</p>
<div>
{% if credit_card_data.last4 %}
<form role="form" id="payment-form-with-creditcard" novalidate>
<h5 class="billing-head">Credit Card</h5>
<h5 class="membership-lead">Last 4: *****{{credit_card_data.last4}}</h5>
<h5 class="membership-lead">Type: {{credit_card_data.cc_brand}}</h5>
<input type="hidden" name="credit_card_needed" value="false"/>
</form>
{% if not messages and not form.non_field_errors %}
<p class="card-warning-content card-warning-addtional-margin">
{% trans "You are not making any payment yet. After submitting your card information, you will be taken to the Confirm Order Page." %}
</p>
{% endif %}
<div id='payment_error'>
{% for message in messages %}
{% if 'failed_payment' or 'make_charge_error' in message.tags %}
<ul class="list-unstyled">
<li>
<p class="card-warning-content card-warning-error">{{ message|safe }}</p>
</li>
</ul>
{% endif %}
{% endfor %}
{% for error in form.non_field_errors %}
<p class="card-warning-content card-warning-error">
{{ error|escape }}
</p>
{% endfor %}
</div>
<div class="text-right">
<button id="payment_button_with_creditcard" class="btn btn-vm-contact" type="submit">{%trans "SUBMIT" %}</button>
</div>
{% if card_list_len > 0 %}
{% blocktrans %}Please select one of the cards that you used before or fill in your credit card information below. We are using <a href="https://stripe.com" target="_blank">Stripe</a> for payment and do not store your information in our database.{% endblocktrans %}
{% else %}
<form action="" id="payment-form-new" method="POST">
<input type="hidden" name="token"/>
<div class="group">
<div class="credit-card-goup">
<div class="card-element card-number-element">
<label>{%trans "Card Number" %}</label>
<div id="card-number-element" class="field my-input"></div>
</div>
<div class="row">
<div class="col-xs-5 card-element card-expiry-element">
<label>{%trans "Expiry Date" %}</label>
<div id="card-expiry-element" class="field my-input"></div>
</div>
<div class="col-xs-3 col-xs-offset-4 card-element card-cvc-element">
<label>{%trans "CVC" %}</label>
<div id="card-cvc-element" class="field my-input"></div>
</div>
</div>
<div class="card-element brand">
<label>{%trans "Card Type" %}</label>
<i class="pf pf-credit-card" id="brand-icon"></i>
</div>
{% blocktrans %}Please fill in your credit card information below. We are using <a href="https://stripe.com" target="_blank">Stripe</a> for payment and do not store your information in our database.{% endblocktrans %}
{% endif %}
</p>
<div>
{% for card in cards_list %}
<div class="credit-card-info">
<div class="col-xs-6 no-padding">
<h5 class="billing-head">{% trans "Credit Card" %}</h5>
<h5 class="membership-lead">{% trans "Last" %} 4: ***** {{card.last4}}</h5>
<h5 class="membership-lead">{% trans "Type" %}: {{card.brand}}</h5>
</div>
<div class="col-xs-6 text-right align-bottom">
<a class="btn choice-btn choice-btn-faded" href="#" data-id_card="{{card.id}}">{% trans "SELECT" %}</a>
</div>
</div>
<div id="card-errors"></div>
{% if not messages and not form.non_field_errors %}
<p class="card-warning-content">
{% trans "You are not making any payment yet. After placing your order, you will be taken to the Submit Payment Page." %}
</p>
{% endif %}
<div id='payment_error'>
{% for message in messages %}
{% if 'failed_payment' in message.tags or 'make_charge_error' in message.tags or 'error' in message.tags %}
<ul class="list-unstyled">
<li><p class="card-warning-content card-warning-error">{{ message|safe }}</p></li>
</ul>
{% endif %}
{% endfor %}
{% endfor %}
{% if card_list_len > 0 %}
<div class="new-card-head">
<div class="row">
<div class="col-xs-6">
<h4>{% trans "Add a new credit card" %}</h4>
</div>
<div class="col-xs-6 text-right new-card-button-margin">
<button data-toggle="collapse" data-target="#newcard" class="btn choice-btn">
<span class="fa fa-plus"></span>&nbsp;&nbsp;{% trans "NEW CARD" %}
</button>
</div>
</div>
</div>
<div class="text-right">
<button class="btn btn-vm-contact btn-wide" type="submit">{%trans "SUBMIT" %}</button>
<div id="newcard" class="collapse">
<hr class="thick-hr">
<div class="card-details-box">
<h3>{%trans "New Credit Card" %}</h3>
<hr>
{% include "hosting/includes/_card_input.html" %}
</div>
</div>
<div style="display:none;">
<p class="payment-errors"></p>
</div>
</form>
{% endif %}
</div>
{% else%}
{% include "hosting/includes/_card_input.html" %}
{% endif %}
</div>
{% endwith %}
</div>
</div>
</div>
@ -190,13 +182,4 @@
})();
</script>
{%endif%}
{% if credit_card_data.last4 and credit_card_data.cc_brand %}
<script type="text/javascript">
(function () {
window.hasCreditcard = true;
})();
</script>
{%endif%}
{%endblock%}

View File

@ -1,5 +1,5 @@
{% extends "datacenterlight/base_hosting.html" %}
{% load staticfiles bootstrap3 i18n custom_tags %}
{% load staticfiles bootstrap3 i18n custom_tags humanize %}
{% block content %}
<div id="order-detail{{order.pk}}" class="order-detail-container">
@ -47,38 +47,104 @@
<hr>
<div>
<h4>{% trans "Order summary" %}</h4>
<p>
<strong>{% trans "Product" %}:</strong>&nbsp;
{{ request.session.template.name }}
</p>
<div class="row">
<div class="col-sm-6">
{% if generic_payment_details %}
<p>
<span>{% trans "Cores" %}: </span>
<span class="pull-right">{{vm.cpu|floatformat}}</span>
<strong>{% trans "Product" %}:</strong>&nbsp;
{{ generic_payment_details.product_name }}
</p>
<div class="row">
<div class="col-sm-6">
<p>
<span>{% trans "Amount" %}: </span>
<strong class="pull-right">CHF {{generic_payment_details.amount|floatformat:2|intcomma}}</strong>
</p>
{% if generic_payment_details.description %}
<p>
<span>{% trans "Description" %}: </span>
<strong class="pull-right">{{generic_payment_details.description}}</strong>
</p>
{% endif %}
{% if generic_payment_details.recurring %}
<p>
<span>{% trans "Recurring" %}: </span>
<strong class="pull-right">Yes</strong>
</p>
{% endif %}
</div>
</div>
{% else %}
<p>
<span>{% trans "Memory" %}: </span>
<span class="pull-right">{{vm.memory}} GB</span>
<strong>{% trans "Product" %}:</strong>&nbsp;
{{ request.session.template.name }}
</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 class="row">
<div class="col-sm-6">
<p>
<span>{% trans "Cores" %}: </span>
<strong class="pull-right">{{vm.cpu|floatformat}}</strong>
</p>
<p>
<span>{% trans "Memory" %}: </span>
<strong class="pull-right">{{vm.memory|intcomma}} GB</strong>
</p>
<p>
<span>{% trans "Disk space" %}: </span>
<strong class="pull-right">{{vm.disk_size|intcomma}} GB</strong>
</p>
</div>
<div class="col-sm-12">
<hr class="thin-hr">
</div>
{% if vm.vat > 0 or vm.discount.amount > 0 %}
<div class="col-sm-6">
<div class="subtotal-price">
{% if vm.vat > 0 %}
<p>
<strong class="text-lg">{% trans "Subtotal" %} </strong>
<strong class="pull-right">{{vm.price|floatformat:2|intcomma}} CHF</strong>
</p>
<p>
<small>{% trans "VAT" %} ({{ vm.vat_percent|floatformat:2|intcomma }}%) </small>
<strong class="pull-right">{{vm.vat|floatformat:2|intcomma}} CHF</strong>
</p>
{% endif %}
{% if vm.discount.amount > 0 %}
<p class="text-primary">
{%trans "Discount" as discount_name %}
<strong>{{ vm.discount.name|default:discount_name }} </strong>
<strong class="pull-right">- {{ vm.discount.amount }} CHF</strong>
</p>
{% endif %}
</div>
</div>
<div class="col-sm-12">
<hr class="thin-hr">
</div>
{% endif %}
<div class="col-sm-6">
<p class="total-price">
<strong>{% trans "Total" %} </strong>
<strong class="pull-right">{{vm.total_price|floatformat:2|intcomma}} CHF</strong>
</p>
</div>
</div>
{% endif %}
</div>
<hr>
<hr class="thin-hr">
</div>
<form id="virtual_machine_create_form" action="" method="POST">
{% csrf_token %}
<div class="row">
<div class="col-sm-8">
<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>
{% if generic_payment_details %}
{% if generic_payment_details.recurring %}
<div class="dcl-place-order-text">{% blocktrans with total_price=generic_payment_details.amount|floatformat:2|intcomma %}By clicking "Place order" this plan will charge your credit card account with {{total_price}} CHF/month{% endblocktrans %}.</div>
{% else %}
<div class="dcl-place-order-text">{% blocktrans with total_price=generic_payment_details.amount|floatformat:2|intcomma %}By clicking "Place order" this payment will charge your credit card account with a one time amount of {{total_price}} CHF{% endblocktrans %}.</div>
{% endif %}
{% else %}
<div class="dcl-place-order-text">{% blocktrans with vm_total_price=vm.total_price|floatformat:2|intcomma %}By clicking "Place order" this plan will charge your credit card account with {{vm_total_price}} CHF/month{% endblocktrans %}.</div>
{% endif %}
</div>
<div class="col-sm-4 order-confirm-btn text-right">
<button class="btn choice-btn" id="btn-create-vm" data-toggle="modal" data-target="#createvm-modal">
@ -120,16 +186,5 @@
<script type="text/javascript">
{% trans "Some problem encountered. Please try again later." as err_msg %}
var create_vm_error_message = '{{err_msg|safe}}';
window.onload = function () {
var locale_dates = document.getElementsByClassName("locale_date");
var formats = ['YYYY-MM-DD hh:mm a']
var i;
for (i = 0; i < locale_dates.length; i++) {
var oldDate = moment.utc(locale_dates[i].textContent, formats);
var outputFormat = locale_dates[i].getAttribute('data-format') || oldDate._f;
locale_dates[i].innerHTML = oldDate.local().format(outputFormat);
locale_dates[i].className += ' done';
}
};
</script>
{%endblock%}

View File

@ -41,4 +41,14 @@ def multiply(value, arg):
:param arg:
:return:
"""
return value*arg
return value * arg
@register.filter('escaped_line_break')
def escaped_line_break(value):
"""
usage: {{ text|escaped_line_break }}
:param value:
:return:
"""
return value.replace("\\n", "\n")

View File

@ -12,9 +12,11 @@ from unittest import skipIf
from datacenterlight.models import VMTemplate
from datacenterlight.tasks import create_vm_task
from hosting.models import HostingOrder
from membership.models import StripeCustomer
from opennebula_api.serializers import VMTemplateSerializer
from utils.hosting_utils import get_vm_price
from utils.models import BillingAddress
from utils.stripe_utils import StripeUtils
@ -81,11 +83,13 @@ class CeleryTaskTestCase(TestCase):
stripe_customer = StripeCustomer.get_or_create(
email=self.customer_email,
token=self.token)
token=self.token
)
card_details = self.stripe_utils.get_card_details(
stripe_customer.stripe_id,
self.token)
card_details_dict = card_details.get('response_object')
stripe_customer.stripe_id
)
card_details_dict = card_details.get('error')
self.assertEquals(card_details_dict, None)
billing_address_data = {'cardholder_name': self.customer_name,
'postal_code': '1231',
'country': 'CH',
@ -101,7 +105,8 @@ class CeleryTaskTestCase(TestCase):
disk_size=disk_size)
plan_name = StripeUtils.get_stripe_plan_name(cpu=cpu,
memory=memory,
disk_size=disk_size)
disk_size=disk_size,
price=amount_to_be_charged)
stripe_plan_id = StripeUtils.get_stripe_plan_id(cpu=cpu,
ram=memory,
ssd=disk_size,
@ -122,10 +127,24 @@ class CeleryTaskTestCase(TestCase):
msg = subscription_result.get('error')
raise Exception("Creating subscription failed: {}".format(msg))
billing_address = BillingAddress(
cardholder_name=billing_address_data['cardholder_name'],
street_address=billing_address_data['street_address'],
city=billing_address_data['city'],
postal_code=billing_address_data['postal_code'],
country=billing_address_data['country']
)
billing_address.save()
order = HostingOrder.create(
price=specs['price'],
vm_id=0,
customer=stripe_customer,
billing_address=billing_address
)
async_task = create_vm_task.delay(
vm_template_id, self.user, specs, template_data,
stripe_customer.id, billing_address_data,
stripe_subscription_obj.id, card_details_dict
vm_template_id, self.user, specs, template_data, order.id
)
new_vm_id = 0
res = None

View File

@ -1,17 +1,20 @@
from django.conf.urls import url
from django.views.generic import TemplateView
from django.views.generic import TemplateView, RedirectView
from .views import (
IndexView, PaymentOrderView, OrderConfirmationView,
WhyDataCenterLightView, ContactUsView
)
urlpatterns = [
url(r'^$', IndexView.as_view(), name='index'),
url(r'^t/$', IndexView.as_view(), name='index_t'),
url(r'^g/$', IndexView.as_view(), name='index_g'),
url(r'^f/$', IndexView.as_view(), name='index_f'),
url(r'^l/$', IndexView.as_view(), name='index_l'),
url(r'^new/$', RedirectView.as_view(url='/cms/'),
name='cms_index'),
url(r'^whydatacenterlight/?$', WhyDataCenterLightView.as_view(),
name='whydatacenterlight'),
url(r'^payment/?$', PaymentOrderView.as_view(), name='payment'),

102
datacenterlight/utils.py Normal file
View File

@ -0,0 +1,102 @@
import logging
from django.contrib.sites.models import Site
from datacenterlight.tasks import create_vm_task
from hosting.models import HostingOrder, HostingBill, OrderDetail
from membership.models import StripeCustomer
from utils.forms import UserBillingAddressForm
from utils.models import BillingAddress
from .cms_models import CMSIntegration
from .models import VMPricing, VMTemplate
logger = logging.getLogger(__name__)
def get_cms_integration(name):
current_site = Site.objects.get_current()
try:
cms_integration = CMSIntegration.objects.get(
name=name, domain=current_site
)
except CMSIntegration.DoesNotExist:
cms_integration = CMSIntegration.objects.get(name=name, domain=None)
return cms_integration
def create_vm(billing_address_data, stripe_customer_id, specs,
stripe_subscription_obj, card_details_dict, request,
vm_template_id, template, user):
billing_address = BillingAddress(
cardholder_name=billing_address_data['cardholder_name'],
street_address=billing_address_data['street_address'],
city=billing_address_data['city'],
postal_code=billing_address_data['postal_code'],
country=billing_address_data['country']
)
billing_address.save()
customer = StripeCustomer.objects.filter(id=stripe_customer_id).first()
vm_pricing = (
VMPricing.get_vm_pricing_by_name(name=specs['pricing_name'])
if 'pricing_name' in specs else
VMPricing.get_default_pricing()
)
final_price = (
specs.get('total_price')
if 'total_price' in specs
else specs.get('price')
)
# Create a Hosting Order with vm_id = 0, we shall set it later in
# celery task once the VM instance is up and running
order = HostingOrder.create(
price=final_price,
customer=customer,
billing_address=billing_address,
vm_pricing=vm_pricing
)
order_detail_obj, obj_created = OrderDetail.objects.get_or_create(
vm_template=VMTemplate.objects.get(
opennebula_vm_template_id=vm_template_id
),
cores=specs['cpu'], memory=specs['memory'], ssd_size=specs['disk_size']
)
order.order_detail = order_detail_obj
order.save()
# Create a Hosting Bill
HostingBill.create(customer=customer, billing_address=billing_address)
# Create Billing Address for User if he does not have one
if not customer.user.billing_addresses.count():
billing_address_data.update({
'user': customer.user.id
})
billing_address_user_form = UserBillingAddressForm(
billing_address_data
)
billing_address_user_form.is_valid()
billing_address_user_form.save()
# Associate the given stripe subscription with the order
order.set_subscription_id(
stripe_subscription_obj.id, card_details_dict
)
# Set order status approved
order.set_approved()
create_vm_task.delay(vm_template_id, user, specs, template, order.id)
clear_all_session_vars(request)
def clear_all_session_vars(request):
if request.session is not None:
for session_var in ['specs', 'template', 'billing_address',
'billing_address_data', 'card_id',
'token', 'customer', 'generic_payment_type',
'generic_payment_details', 'product_id']:
if session_var in request.session:
del request.session[session_var]

View File

@ -1,4 +1,3 @@
import json
import logging
from django import forms
@ -7,25 +6,31 @@ from django.contrib import messages
from django.contrib.auth import login, authenticate
from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect, HttpResponse
from django.http import HttpResponseRedirect, JsonResponse, Http404
from django.shortcuts import render
from django.utils.translation import get_language, ugettext_lazy as _
from django.views.decorators.cache import cache_control
from django.views.generic import FormView, CreateView, DetailView
from datacenterlight.tasks import create_vm_task
from hosting.forms import HostingUserLoginForm
from hosting.models import HostingOrder
from hosting.forms import (
HostingUserLoginForm, GenericPaymentForm, ProductPaymentForm
)
from hosting.models import (
HostingBill, HostingOrder, UserCardDetail, GenericProduct
)
from membership.models import CustomUser, StripeCustomer
from opennebula_api.serializers import VMTemplateSerializer
from utils.forms import (
BillingAddressForm, BillingAddressFormSignup
BillingAddressForm, BillingAddressFormSignup, UserBillingAddressForm,
BillingAddress
)
from utils.hosting_utils import get_vm_price
from utils.hosting_utils import get_vm_price_with_vat
from utils.stripe_utils import StripeUtils
from utils.tasks import send_plain_email_task
from .cms_models import DCLCalculatorPluginModel
from .forms import ContactForm
from .models import VMTemplate
from .models import VMTemplate, VMPricing
from .utils import get_cms_integration, create_vm, clear_all_session_vars
logger = logging.getLogger(__name__)
@ -42,9 +47,10 @@ class ContactUsView(FormView):
return self.render_to_response(
self.get_context_data(contact_form=form))
else:
return render(self.request,
'datacenterlight/index.html',
self.get_context_data(contact_form=form))
return render(
self.request, 'datacenterlight/index.html',
self.get_context_data(contact_form=form)
)
def form_valid(self, form):
form.save()
@ -58,7 +64,7 @@ class ContactUsView(FormView):
sender=form.cleaned_data.get('email')
),
'from_email': settings.DCL_SUPPORT_FROM_ADDRESS,
'to': [from_emails.get(from_page, 'info@ungleich.ch')],
'to': [from_emails.get(from_page, 'support@ungleich.ch')],
'body': "\n".join(
["%s=%s" % (k, v) for (k, v) in form.cleaned_data.items()]),
'reply_to': [form.cleaned_data.get('email')],
@ -68,10 +74,10 @@ class ContactUsView(FormView):
return self.render_to_response(
self.get_context_data(success=True, contact_form=form))
else:
return render(self.request,
'datacenterlight/index.html',
self.get_context_data(success=True,
contact_form=form))
return render(
self.request, 'datacenterlight/index.html',
self.get_context_data(success=True, contact_form=form)
)
class IndexView(CreateView):
@ -84,7 +90,29 @@ class IndexView(CreateView):
raise ValidationError(_('Invalid number of cores'))
def validate_memory(self, value):
if (value > 200) or (value < 1):
if 'pid' in self.request.POST:
try:
plugin = DCLCalculatorPluginModel.objects.get(
id=self.request.POST['pid']
)
except DCLCalculatorPluginModel.DoesNotExist as dne:
logger.error(
str(dne) + " plugin_id: " + self.request.POST['pid']
)
raise ValidationError(_('Invalid calculator properties'))
if plugin.enable_512mb_ram:
if value % 1 == 0 or value == 0.5:
logger.debug(
"Given ram {value} is either 0.5 or a"
" whole number".format(value=value)
)
if (value > 200) or (value < 0.5):
raise ValidationError(_('Invalid RAM size'))
else:
raise ValidationError(_('Invalid RAM size'))
elif (value > 200) or (value < 1) or (value % 1 != 0):
raise ValidationError(_('Invalid RAM size'))
else:
raise ValidationError(_('Invalid RAM size'))
def validate_storage(self, value):
@ -93,30 +121,41 @@ class IndexView(CreateView):
@cache_control(no_cache=True, must_revalidate=True, no_store=True)
def get(self, request, *args, **kwargs):
for session_var in ['specs', 'user', 'billing_address_data']:
if session_var in request.session:
del request.session[session_var]
vm_templates = VMTemplate.objects.all()
context = {
'templates': vm_templates
}
return render(request, self.template_name, context)
clear_all_session_vars(request)
return HttpResponseRedirect(reverse('datacenterlight:cms_index'))
def post(self, request):
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])
memory_field = forms.FloatField(validators=[self.validate_memory])
storage = request.POST.get('storage')
storage_field = forms.IntegerField(validators=[self.validate_storage])
template_id = int(request.POST.get('config'))
pricing_name = request.POST.get('pricing_name')
vm_pricing = VMPricing.get_vm_pricing_by_name(pricing_name)
template = VMTemplate.objects.filter(
opennebula_vm_template_id=template_id
).first()
template_data = VMTemplateSerializer(template).data
referer_url = request.META['HTTP_REFERER']
if vm_pricing is None:
vm_pricing_name_msg = _(
"Incorrect pricing name. Please contact support"
"{support_email}".format(
support_email=settings.DCL_SUPPORT_FROM_ADDRESS
)
)
messages.add_message(
self.request, messages.ERROR, vm_pricing_name_msg,
extra_tags='pricing'
)
return HttpResponseRedirect(referer_url + "#order_form")
else:
vm_pricing_name = vm_pricing.name
try:
cores = cores_field.clean(cores)
except ValidationError as err:
@ -144,14 +183,22 @@ class IndexView(CreateView):
)
return HttpResponseRedirect(referer_url + "#order_form")
amount_to_be_charged = get_vm_price(
cpu=cores, memory=memory, disk_size=storage
price, vat, vat_percent, discount = get_vm_price_with_vat(
cpu=cores,
memory=memory,
ssd_size=storage,
pricing_name=vm_pricing_name
)
specs = {
'cpu': cores,
'memory': memory,
'disk_size': storage,
'price': amount_to_be_charged
'price': price,
'vat': vat,
'vat_percent': vat_percent,
'discount': discount,
'total_price': round(price + vat - discount['amount'], 2),
'pricing_name': vm_pricing_name
}
request.session['specs'] = specs
request.session['template'] = template_data
@ -202,19 +249,15 @@ class PaymentOrderView(FormView):
billing_address_form = BillingAddressForm(
instance=self.request.user.billing_addresses.first()
)
# Get user last order
last_hosting_order = HostingOrder.objects.filter(
customer__user=self.request.user
).last()
# If user has already an hosting order, get the credit card
# data from it
if last_hosting_order:
credit_card_data = last_hosting_order.get_cc_data()
if credit_card_data:
context['credit_card_data'] = credit_card_data
else:
context['credit_card_data'] = None
user = self.request.user
if hasattr(user, 'stripecustomer'):
stripe_customer = user.stripecustomer
else:
stripe_customer = None
cards_list = UserCardDetail.get_all_cards_list(
stripe_customer=stripe_customer
)
context.update({'cards_list': cards_list})
else:
billing_address_form = BillingAddressFormSignup(
initial=billing_address_data
@ -224,17 +267,95 @@ class PaymentOrderView(FormView):
'stripe_key': settings.STRIPE_API_PUBLIC_KEY,
'site_url': reverse('datacenterlight:index'),
'login_form': HostingUserLoginForm(prefix='login_form'),
'billing_address_form': billing_address_form
'billing_address_form': billing_address_form,
'cms_integration': get_cms_integration('default'),
})
if ('generic_payment_type' in self.request.session and
self.request.session['generic_payment_type'] == 'generic'):
if 'product_id' in self.request.session:
product = GenericProduct.objects.get(
id=self.request.session['product_id']
)
context.update({'generic_payment_form': ProductPaymentForm(
prefix='generic_payment_form',
initial={'product_name': product.product_name,
'amount': float(product.get_actual_price()),
'recurring': product.product_is_subscription,
'description': product.product_description,
},
product_id=product.id
), })
else:
context.update({'generic_payment_form': GenericPaymentForm(
prefix='generic_payment_form',
), })
else:
context.update({
'vm_pricing': VMPricing.get_vm_pricing_by_name(
self.request.session['specs']['pricing_name']
)
})
return context
@cache_control(no_cache=True, must_revalidate=True, no_store=True)
def get(self, request, *args, **kwargs):
if 'specs' not in request.session:
if (('type' in request.GET and request.GET['type'] == 'generic')
or 'product_slug' in kwargs):
request.session['generic_payment_type'] = 'generic'
if 'generic_payment_details' in request.session:
request.session.pop('generic_payment_details')
request.session.pop('product_id')
if 'product_slug' in kwargs:
logger.debug("Product slug is " + kwargs['product_slug'])
try:
product = GenericProduct.objects.get(
product_slug=kwargs['product_slug']
)
except GenericProduct.DoesNotExist as dne:
logger.error(
"Product '{}' does "
"not exist".format(kwargs['product_slug'])
)
raise Http404()
request.session['product_id'] = product.id
elif 'specs' not in request.session:
return HttpResponseRedirect(reverse('datacenterlight:index'))
return self.render_to_response(self.get_context_data())
def post(self, request, *args, **kwargs):
if 'product' in request.POST:
# query for the supplied product
product = None
try:
product = GenericProduct.objects.get(
id=request.POST['generic_payment_form-product_name']
)
except GenericProduct.DoesNotExist as dne:
logger.error(
"The requested product '{}' does not exist".format(
request.POST['generic_payment_form-product_name']
)
)
except GenericProduct.MultipleObjectsReturned as mpe:
logger.error(
"There seem to be more than one product with "
"the name {}".format(
request.POST['generic_payment_form-product_name']
)
)
product = GenericProduct.objects.all(
product_name=request.
POST['generic_payment_form-product_name']
).first()
if product is None:
return JsonResponse({})
else:
return JsonResponse({
'amount': product.get_actual_price(),
'isSubscription': product.product_is_subscription
})
if 'login_form' in request.POST:
login_form = HostingUserLoginForm(
data=request.POST, prefix='login_form'
@ -245,6 +366,13 @@ class PaymentOrderView(FormView):
auth_user = authenticate(email=email, password=password)
if auth_user:
login(self.request, auth_user)
if 'product_slug' in kwargs:
return HttpResponseRedirect(
reverse('show_product',
kwargs={
'product_slug': kwargs['product_slug']}
)
)
return HttpResponseRedirect(
reverse('datacenterlight:payment')
)
@ -261,15 +389,87 @@ class PaymentOrderView(FormView):
data=request.POST,
)
if address_form.is_valid():
# Check if we are in a generic payment case and handle the generic
# payment details form before we go on to verify payment
if ('generic_payment_type' in request.session and
self.request.session['generic_payment_type'] == 'generic'):
if 'product_id' in request.session:
generic_payment_form = ProductPaymentForm(
data=request.POST, prefix='generic_payment_form',
product_id=request.session['product_id']
)
else:
generic_payment_form = GenericPaymentForm(
data=request.POST, prefix='generic_payment_form'
)
if generic_payment_form.is_valid():
logger.debug("Generic payment form is valid.")
if 'product_id' in request.session:
product = generic_payment_form.product
else:
product = generic_payment_form.cleaned_data.get(
'product_name'
)
gp_details = {
"product_name": product.product_name,
"amount": generic_payment_form.cleaned_data.get(
'amount'
),
"recurring": generic_payment_form.cleaned_data.get(
'recurring'
),
"description": generic_payment_form.cleaned_data.get(
'description'
),
"product_id": product.id,
"product_slug": product.product_slug
}
request.session["generic_payment_details"] = (
gp_details
)
else:
logger.debug("Generic payment form invalid")
context = self.get_context_data()
context['generic_payment_form'] = generic_payment_form
context['billing_address_form'] = address_form
return self.render_to_response(context)
token = address_form.cleaned_data.get('token')
if token is '':
card_id = address_form.cleaned_data.get('card')
try:
user_card_detail = UserCardDetail.objects.get(id=card_id)
if not request.user.has_perm(
'view_usercarddetail', user_card_detail
):
raise UserCardDetail.DoesNotExist(
_("{user} does not have permission to access the "
"card").format(user=request.user.email)
)
except UserCardDetail.DoesNotExist as e:
ex = str(e)
logger.error("Card Id: {card_id}, Exception: {ex}".format(
card_id=card_id, ex=ex
)
)
msg = _("An error occurred. Details: {}".format(ex))
messages.add_message(
self.request, messages.ERROR, msg,
extra_tags='make_charge_error'
)
return HttpResponseRedirect(
reverse('datacenterlight:payment') + '#payment_error'
)
request.session['card_id'] = user_card_detail.id
else:
request.session['token'] = token
if request.user.is_authenticated():
this_user = {
'email': request.user.email,
'name': request.user.name
}
customer = StripeCustomer.get_or_create(
email=this_user.get('email'),
token=token)
email=this_user.get('email'), token=token
)
else:
user_email = address_form.cleaned_data.get('email')
user_name = address_form.cleaned_data.get('name')
@ -317,7 +517,6 @@ class PaymentOrderView(FormView):
billing_address_form=address_form
)
)
request.session['token'] = token
if type(customer) is StripeCustomer:
request.session['customer'] = customer.stripe_id
else:
@ -338,45 +537,137 @@ class OrderConfirmationView(DetailView):
@cache_control(no_cache=True, must_revalidate=True, no_store=True)
def get(self, request, *args, **kwargs):
if 'specs' not in request.session or 'user' not in request.session:
context = {}
if (('specs' not in request.session or 'user' not in request.session)
and 'generic_payment_type' not in request.session):
return HttpResponseRedirect(reverse('datacenterlight:index'))
if 'token' not in request.session:
return HttpResponseRedirect(reverse('datacenterlight:payment'))
stripe_api_cus_id = request.session.get('customer')
stripe_utils = StripeUtils()
card_details = stripe_utils.get_card_details(stripe_api_cus_id,
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('datacenterlight:payment') + '#payment_error')
context = {
if 'token' in self.request.session:
token = self.request.session['token']
stripe_utils = StripeUtils()
card_details = stripe_utils.get_cards_details_from_token(
token
)
if not card_details.get('response_object'):
return HttpResponseRedirect(reverse('hosting:payment'))
card_details_response = card_details['response_object']
context['cc_last4'] = card_details_response['last4']
context['cc_brand'] = card_details_response['brand']
else:
card_id = self.request.session.get('card_id')
card_detail = UserCardDetail.objects.get(id=card_id)
context['cc_last4'] = card_detail.last4
context['cc_brand'] = card_detail.brand
if ('generic_payment_type' in request.session and
self.request.session['generic_payment_type'] == 'generic'):
context.update({
'generic_payment_details':
request.session['generic_payment_details'],
})
else:
context.update({
'vm': request.session.get('specs'),
})
context.update({
'site_url': reverse('datacenterlight:index'),
'cc_last4': card_details.get('response_object').get('last4'),
'cc_brand': card_details.get('response_object').get('brand'),
'vm': request.session.get('specs'),
'page_header_text': _('Confirm Order'),
'billing_address_data': request.session.get('billing_address_data')
}
'billing_address_data': (
request.session.get('billing_address_data')
),
'cms_integration': get_cms_integration('default'),
})
return render(request, self.template_name, context)
def post(self, request, *args, **kwargs):
template = request.session.get('template')
specs = request.session.get('specs')
user = request.session.get('user')
stripe_api_cus_id = request.session.get('customer')
vm_template_id = template.get('id', 1)
stripe_utils = StripeUtils()
card_details = stripe_utils.get_card_details(stripe_api_cus_id,
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')
if 'token' in request.session:
card_details = stripe_utils.get_cards_details_from_token(
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')
response = {
'status': False,
'redirect': "{url}#{section}".format(
url=(reverse(
'show_product',
kwargs={'product_slug':
request.session['generic_payment_details']
['product_slug']}
) if 'generic_payment_details' in request.session else
reverse('datacenterlight:payment')
),
section='payment_error'),
'msg_title': str(_('Error.')),
'msg_body': str(
_('There was a payment related error.'
' On close of this popup, you will be'
' redirected back to the payment page.')
)
}
return JsonResponse(response)
card_details_response = card_details['response_object']
card_details_dict = {
'last4': card_details_response['last4'],
'brand': card_details_response['brand'],
'card_id': card_details_response['card_id']
}
stripe_customer_obj = StripeCustomer.objects.filter(
stripe_id=stripe_api_cus_id).first()
if stripe_customer_obj:
ucd = UserCardDetail.get_user_card_details(
stripe_customer_obj, card_details_response
)
if not ucd:
acc_result = stripe_utils.associate_customer_card(
stripe_api_cus_id, request.session['token'],
set_as_default=True
)
if acc_result['response_object'] is None:
msg = _(
'An error occurred while associating the card.'
' Details: {details}'.format(
details=acc_result['error']
)
)
messages.add_message(self.request, messages.ERROR, msg,
extra_tags='failed_payment')
response = {
'status': False,
'redirect': "{url}#{section}".format(
url=(reverse(
'show_product',
kwargs={'product_slug':
request.session
['generic_payment_details']
['product_slug']}
) if 'generic_payment_details' in
request.session else
reverse('datacenterlight:payment')
),
section='payment_error'),
'msg_title': str(_('Error.')),
'msg_body': str(
_('There was a payment related error.'
' On close of this popup, you will be redirected'
' back to the payment page.')
)
}
return JsonResponse(response)
elif 'card_id' in request.session:
card_id = request.session.get('card_id')
user_card_detail = UserCardDetail.objects.get(id=card_id)
card_details_dict = {
'last4': user_card_detail.last4,
'brand': user_card_detail.brand,
'card_id': user_card_detail.card_id
}
else:
response = {
'status': False,
'redirect': "{url}#{section}".format(
@ -388,49 +679,124 @@ class OrderConfirmationView(DetailView):
' On close of this popup, you will be redirected back to'
' the payment page.'))
}
return HttpResponse(json.dumps(response),
content_type="application/json")
card_details_dict = card_details.get('response_object')
cpu = specs.get('cpu')
memory = specs.get('memory')
disk_size = specs.get('disk_size')
amount_to_be_charged = specs.get('price')
plan_name = StripeUtils.get_stripe_plan_name(cpu=cpu,
memory=memory,
disk_size=disk_size)
stripe_plan_id = StripeUtils.get_stripe_plan_id(cpu=cpu,
ram=memory,
ssd=disk_size,
version=1,
app='dcl')
stripe_plan = stripe_utils.get_or_create_stripe_plan(
amount=amount_to_be_charged,
name=plan_name,
stripe_plan_id=stripe_plan_id)
subscription_result = stripe_utils.subscribe_customer_to_plan(
stripe_api_cus_id,
[{"plan": stripe_plan.get(
'response_object').stripe_plan_id}])
stripe_subscription_obj = subscription_result.get('response_object')
# Check if the subscription was approved and is active
if (stripe_subscription_obj is None
or stripe_subscription_obj.status != 'active'):
msg = subscription_result.get('error')
messages.add_message(self.request, messages.ERROR, msg,
extra_tags='failed_payment')
response = {
'status': False,
'redirect': "{url}#{section}".format(
url=reverse('datacenterlight:payment'),
section='payment_error'),
'msg_title': str(_('Error.')),
'msg_body': str(
_('There was a payment related error.'
' On close of this popup, you will be redirected back to'
' the payment page.'))
}
return HttpResponse(json.dumps(response),
content_type="application/json")
return JsonResponse(response)
if ('generic_payment_type' in request.session and
self.request.session['generic_payment_type'] == 'generic'):
gp_details = self.request.session['generic_payment_details']
if gp_details['recurring']:
# generic recurring payment
logger.debug("Commencing a generic recurring payment")
else:
# generic one time payment
logger.debug("Commencing a one time payment")
charge_response = stripe_utils.make_charge(
amount=gp_details['amount'],
customer=stripe_api_cus_id
)
stripe_onetime_charge = charge_response.get('response_object')
# Check if the payment was approved
if not stripe_onetime_charge:
msg = charge_response.get('error')
messages.add_message(self.request, messages.ERROR, msg,
extra_tags='failed_payment')
response = {
'status': False,
'redirect': "{url}#{section}".format(
url=(reverse('show_product', kwargs={
'product_slug': gp_details['product_slug']}
) if 'generic_payment_details' in
request.session else
reverse('datacenterlight:payment')
),
section='payment_error'),
'msg_title': str(_('Error.')),
'msg_body': str(
_('There was a payment related error.'
' On close of this popup, you will be redirected'
' back to the payment page.'))
}
return JsonResponse(response)
if ('generic_payment_type' not in request.session or
(request.session['generic_payment_details']['recurring'])):
if 'generic_payment_details' in request.session:
amount_to_be_charged = (
round(
request.session['generic_payment_details']['amount'],
2
)
)
plan_name = "generic-{0}-{1:.2f}".format(
request.session['generic_payment_details']['product_id'],
amount_to_be_charged
)
stripe_plan_id = plan_name
else:
template = request.session.get('template')
specs = request.session.get('specs')
vm_template_id = template.get('id', 1)
cpu = specs.get('cpu')
memory = specs.get('memory')
disk_size = specs.get('disk_size')
amount_to_be_charged = specs.get('total_price')
plan_name = StripeUtils.get_stripe_plan_name(
cpu=cpu,
memory=memory,
disk_size=disk_size,
price=amount_to_be_charged
)
stripe_plan_id = StripeUtils.get_stripe_plan_id(
cpu=cpu,
ram=memory,
ssd=disk_size,
version=1,
app='dcl',
price=amount_to_be_charged
)
stripe_plan = stripe_utils.get_or_create_stripe_plan(
amount=amount_to_be_charged,
name=plan_name,
stripe_plan_id=stripe_plan_id)
subscription_result = stripe_utils.subscribe_customer_to_plan(
stripe_api_cus_id,
[{"plan": stripe_plan.get(
'response_object').stripe_plan_id}])
stripe_subscription_obj = subscription_result.get('response_object')
# Check if the subscription was approved and is active
if (stripe_subscription_obj is None
or stripe_subscription_obj.status != 'active'):
# At this point, we have created a Stripe API card and
# associated it with the customer; but the transaction failed
# due to some reason. So, we would want to dissociate this card
# here.
# ...
msg = subscription_result.get('error')
messages.add_message(self.request, messages.ERROR, msg,
extra_tags='failed_payment')
response = {
'status': False,
'redirect': "{url}#{section}".format(
url=(reverse(
'show_product',
kwargs={'product_slug':
request.session['generic_payment_details']
['product_slug']}
) if 'generic_payment_details' in request.session else
reverse('datacenterlight:payment')
),
section='payment_error'
),
'msg_title': str(_('Error.')),
'msg_body': str(
_('There was a payment related error.'
' On close of this popup, you will be redirected back to'
' the payment page.'))
}
return JsonResponse(response)
# Create user if the user is not logged in and if he is not already
# registered
@ -470,12 +836,148 @@ class OrderConfirmationView(DetailView):
stripe_customer_id = request.user.stripecustomer.id
custom_user = request.user
if 'token' in request.session:
ucd = UserCardDetail.get_or_create_user_card_detail(
stripe_customer=self.request.user.stripecustomer,
card_details=card_details_response
)
UserCardDetail.save_default_card_local(
self.request.user.stripecustomer.stripe_id,
ucd.card_id
)
else:
card_id = request.session.get('card_id')
user_card_detail = UserCardDetail.objects.get(id=card_id)
card_details_dict = {
'last4': user_card_detail.last4,
'brand': user_card_detail.brand,
'card_id': user_card_detail.card_id
}
if not user_card_detail.preferred:
UserCardDetail.set_default_card(
stripe_api_cus_id=stripe_api_cus_id,
stripe_source_id=user_card_detail.card_id
)
# Save billing address
billing_address_data = request.session.get('billing_address_data')
logger.debug('billing_address_data is {}'.format(billing_address_data))
billing_address_data.update({
'user': custom_user.id
})
if 'generic_payment_type' in request.session:
stripe_cus = StripeCustomer.objects.filter(
stripe_id=stripe_api_cus_id
).first()
billing_address = BillingAddress(
cardholder_name=billing_address_data['cardholder_name'],
street_address=billing_address_data['street_address'],
city=billing_address_data['city'],
postal_code=billing_address_data['postal_code'],
country=billing_address_data['country']
)
billing_address.save()
order = HostingOrder.create(
price=self.request
.session['generic_payment_details']['amount'],
customer=stripe_cus,
billing_address=billing_address,
vm_pricing=VMPricing.get_default_pricing()
)
# Create a Hosting Bill
HostingBill.create(customer=stripe_cus,
billing_address=billing_address)
# Create Billing Address for User if he does not have one
if not stripe_cus.user.billing_addresses.count():
billing_address_data.update({
'user': stripe_cus.user.id
})
billing_address_user_form = UserBillingAddressForm(
billing_address_data
)
billing_address_user_form.is_valid()
billing_address_user_form.save()
if self.request.session['generic_payment_details']['recurring']:
# Associate the given stripe subscription with the order
order.set_subscription_id(
stripe_subscription_obj.id, card_details_dict
)
else:
# Associate the given stripe charge id with the order
order.set_stripe_charge(stripe_onetime_charge)
# Set order status approved
order.set_approved()
order.generic_payment_description = gp_details["description"]
order.generic_product_id = gp_details["product_id"]
order.save()
# send emails
context = {
'name': user.get('name'),
'email': user.get('email'),
'amount': gp_details['amount'],
'description': gp_details['description'],
'recurring': gp_details['recurring'],
'product_name': gp_details['product_name'],
'product_id': gp_details['product_id'],
'order_id': order.id
}
email_data = {
'subject': (settings.DCL_TEXT +
" Payment received from %s" % context['email']),
'from_email': settings.DCL_SUPPORT_FROM_ADDRESS,
'to': ['info@ungleich.ch'],
'body': "\n".join(
["%s=%s" % (k, v) for (k, v) in context.items()]),
'reply_to': [context['email']],
}
send_plain_email_task.delay(email_data)
email_data = {
'subject': _("Confirmation of your payment"),
'from_email': settings.DCL_SUPPORT_FROM_ADDRESS,
'to': [user.get('email')],
'body': _("Hi {name},\n\n"
"thank you for your order!\n"
"We have just received a payment of CHF {amount:.2f}"
" from you.{recurring}\n\n"
"Cheers,\nYour Data Center Light team".format(
name=user.get('name'),
amount=gp_details['amount'],
recurring=(
_(' This is a monthly recurring plan.')
if gp_details['recurring'] else ''
)
)
),
'reply_to': ['info@ungleich.ch'],
}
send_plain_email_task.delay(email_data)
response = {
'status': True,
'redirect': (
reverse('hosting:orders')
if request.user.is_authenticated()
else reverse('datacenterlight:index')
),
'msg_title': str(_('Thank you for the payment.')),
'msg_body': str(
_('You will soon receive a confirmation email of the '
'payment. You can always contact us at '
'info@ungleich.ch for any question that you may have.')
)
}
clear_all_session_vars(request)
return JsonResponse(response)
user = {
'name': custom_user.name,
'email': custom_user.email,
@ -485,14 +987,11 @@ class OrderConfirmationView(DetailView):
'language': get_language(),
}
create_vm_task.delay(vm_template_id, user, specs, template,
stripe_customer_id, billing_address_data,
stripe_subscription_obj.id, card_details_dict)
for session_var in ['specs', 'template', 'billing_address',
'billing_address_data',
'token', 'customer']:
if session_var in request.session:
del request.session[session_var]
create_vm(
billing_address_data, stripe_customer_id, specs,
stripe_subscription_obj, card_details_dict, request,
vm_template_id, template, user
)
response = {
'status': True,
@ -508,5 +1007,4 @@ class OrderConfirmationView(DetailView):
' it is ready.'))
}
return HttpResponse(json.dumps(response),
content_type="application/json")
return JsonResponse(response)

View File

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2018-08-24 07:39
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('digitalglarus', '0025_membershiporder_stripe_subscription_id'),
]
operations = [
migrations.AlterField(
model_name='bookingorder',
name='cc_brand',
field=models.CharField(blank=True, max_length=128),
),
migrations.AlterField(
model_name='membershiporder',
name='cc_brand',
field=models.CharField(blank=True, max_length=128),
),
]

View File

@ -39,7 +39,7 @@ class Ordereable(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
approved = models.BooleanField(default=False)
last4 = models.CharField(max_length=4, blank=True)
cc_brand = models.CharField(max_length=10, blank=True)
cc_brand = models.CharField(max_length=128, blank=True)
stripe_charge_id = models.CharField(max_length=100, null=True)
class Meta:

View File

@ -70,7 +70,7 @@ hr.small {
}
.navbar-custom .navbar-brand {
color: white;
padding: 20px;
padding: 5px 20px;
}
.navbar-custom .navbar-brand:hover,
.navbar-custom .navbar-brand:focus {

View File

@ -57,7 +57,7 @@
ga('send', 'pageview');
</script>
<link rel="shortcut icon" href="img/favicon.ico" type="image/x-icon">
<link rel="shortcut icon" href="{% static 'digitalglarus/img/favicon.ico' %}" type="image/x-icon">
<style id="igtranslator-color" type="text/css"></style>
<style type="text/css">
@ -87,10 +87,10 @@
height: 100%;
margin: 0px;
padding: 0px;
overflow-x: hidden;
overflow-x: hidden;
}
@media only screen and (min-width: 769px){
@media only screen and (min-width: 769px){
.dropdown.home-dropdown-mobile {
display: none;
}
@ -99,10 +99,10 @@
}
}
@media only screen and (max-width: 768px){
@media only screen and (max-width: 768px){
.dropdown.home-dropdown-mobile {
display: block;
background-color:
background-color:
}
.dropdown.home-dropdown-mobile .dropdown-menu{
@ -164,7 +164,7 @@
<a class="page-scroll" href="#contact">Contact</a>
</li>
{% if request.user.is_authenticated %}
<li class="dropdown home-dropdown-mobile open">
<a class="dropdown-toggle" role="button" data-toggle="dropdown" href="#">
@ -177,7 +177,7 @@
</a>
</li>
<li>
<a href="{% url 'digitalglarus:membership_orders_list' %}"><i class="fa fa-heart-o" aria-hidden="true"></i> {% trans "Membership"%}
<a href="{% url 'digitalglarus:membership_orders_list' %}"><i class="fa fa-heart-o" aria-hidden="true"></i> {% trans "Membership"%}
</a>
</li>
<li>
@ -200,7 +200,7 @@
</a>
</li>
<li>
<a href="{% url 'digitalglarus:membership_orders_list' %}"><i class="fa fa-heart-o" aria-hidden="true"></i> {% trans "Membership"%}
<a href="{% url 'digitalglarus:membership_orders_list' %}"><i class="fa fa-heart-o" aria-hidden="true"></i> {% trans "Membership"%}
</a>
</li>
<li>
@ -210,11 +210,11 @@
</a>
</li>
</ul>
</li>
</li>
{% else %}
<li>
<a class="page-scroll" href="{% url 'digitalglarus:login' %}">Login</a>
</li>
</li>
{% endif %}
<!-- <li>
<a class="page-scroll" href="{% url 'digitalglarus:signup' %}">Sign Up</a>
@ -227,7 +227,7 @@
</nav>
{% block content %} {% endblock %}
<footer>
<div class="container">
<div class="row">
@ -255,7 +255,7 @@
</div>
</div>
</footer>
<script type="text/javascript" src="{% static 'digitalglarus/bower_components/jquery/dist/jquery.min.js' %}"></script>
<!-- jQuery -->
@ -295,7 +295,7 @@
<script type="text/javascript" src="//cdn.jsdelivr.net/jquery/1/jquery.min.js"></script>
<script type="text/javascript" src="//cdn.jsdelivr.net/momentjs/latest/moment.min.js"></script>
<!-- <link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/bootstrap/latest/css/bootstrap.css" />
-->
-->
<!-- Include Date Range Picker -->
<script type="text/javascript" src="//cdn.jsdelivr.net/bootstrap.daterangepicker/2/daterangepicker.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.42/js/bootstrap-datetimepicker.min.js

View File

@ -2,7 +2,7 @@ import logging
from django.conf import settings
from django.shortcuts import render
from django.http import HttpResponseRedirect
from django.http import HttpResponseRedirect, Http404
from django.core.urlresolvers import reverse_lazy, reverse
from django.utils.translation import ugettext_lazy as _
from django.views.generic import TemplateView, UpdateView
@ -492,6 +492,18 @@ class MembershipPaymentView(LoginRequiredMixin, IsNotMemberMixin, FormView):
'membership_dates': membership.type.first_month_formated_range
})
email_to_admin_data = {
'subject': "New Digital Glarus subscription: {user}".format(
user=self.request.user.email
),
'from_email': 'info@digitalglarus.ch',
'to': ['info@ungleich.ch'],
'body': "\n".join(
["%s=%s" % (k, v) for (k, v) in
order_data.items()]),
}
send_plain_email_task.delay(email_to_admin_data)
context = {
'membership': membership,
'order': membership_order,
@ -834,8 +846,9 @@ class ContactView(FormView):
def blog(request):
tags = ["digitalglarus"]
posts = Post.objects.filter(tags__name__in=tags, publish=True).translated(get_language())
# posts = Post.objects.filter_by_language(get_language()).filter(tags__name__in=tags, publish=True)
posts = (Post.objects
.filter(tags__name__in=tags, publish=True)
.translated(get_language()))
context = {
'post_list': posts,
}
@ -843,9 +856,9 @@ def blog(request):
def blog_detail(request, slug):
# post = Post.objects.filter_by_language(get_language()).filter(slug=slug).first()
post = Post.objects.translated(get_language(), slug=slug).first()
if post is None:
raise Http404()
context = {
'post': post,
}

View File

View File

View File

@ -0,0 +1,2 @@
DECIMAL_SEPARATOR = '.'
THOUSAND_SEPARATOR = ','

View File

@ -2,16 +2,15 @@
Copyright 2015 ungleich.
"""
import json
import logging
# -*- coding: utf-8 -*-
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os
import json
from django.utils.translation import ugettext_lazy as _
# dotenv
import dotenv
import logging
from django.utils.translation import ugettext_lazy as _
logger = logging.getLogger(__name__)
@ -56,6 +55,7 @@ PROJECT_DIR = os.path.abspath(
dotenv.read_dotenv("{0}/.env".format(PROJECT_DIR))
from multisite import SiteID
SITE_ID = SiteID(default=1)
APP_ROOT_ENDPOINT = "/"
@ -82,6 +82,7 @@ INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.humanize',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
@ -178,9 +179,7 @@ ROOT_URLCONF = 'dynamicweb.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(PROJECT_DIR, 'cms_templates/'),
os.path.join(PROJECT_DIR, 'cms_templates/djangocms_blog/'),
os.path.join(PROJECT_DIR, 'membership'),
'DIRS': [os.path.join(PROJECT_DIR, 'membership'),
os.path.join(PROJECT_DIR, 'hosting/templates/'),
os.path.join(PROJECT_DIR, 'nosystemd/templates/'),
os.path.join(PROJECT_DIR,
@ -191,6 +190,8 @@ TEMPLATES = [
os.path.join(PROJECT_DIR,
'ungleich_page/templates/ungleich_page'),
os.path.join(PROJECT_DIR, 'templates/analytics'),
os.path.join(PROJECT_DIR, 'cms_templates/'),
os.path.join(PROJECT_DIR, 'cms_templates/djangocms_blog/'),
],
'APP_DIRS': True,
'OPTIONS': {
@ -255,6 +256,10 @@ USE_L10N = True
USE_TZ = True
FORMAT_MODULE_PATH = [
'dynamicweb.formats'
]
LANGUAGES = (
('en-us', _('English')),
('de', _('Deutsch')),
@ -263,7 +268,6 @@ LANGUAGES = (
LANGUAGE_CODE = 'en-us'
LOCALE_PATHS = [
os.path.join(PROJECT_DIR, 'digitalglarus/locale'),
]
@ -352,6 +356,18 @@ CMS_PLACEHOLDER_CONF = {
},
]
},
'datacenterlight_calculator': {
'name': _('Datacenterlight Calculator'),
'plugins': ['DCLCalculatorPlugin'],
'default_plugins': [
{
'plugin_type': 'DCLCalculatorPlugin',
'values': {
'pricing_id': 1
},
},
]
},
}
CMS_PERMISSION = True
@ -516,7 +532,7 @@ META_INCLUDE_KEYWORDS = ["ungleich", "hosting", "switzerland",
"Schweiz", "Swiss", "cdist"]
META_USE_SITES = True
PARLER_LANGUAGES = {1: ({'code': 'en-us'}, {'code': 'de'},)}
PARLER_LANGUAGES = {SITE_ID: ({'code': 'en-us'}, {'code': 'de'},)}
AUTH_USER_MODEL = 'membership.CustomUser'
# PAYMENT
@ -564,7 +580,6 @@ MULTISITE_FALLBACK_KWARGS = {
FILER_ENABLE_PERMISSIONS = True
#############################################
# configurations for opennebula-integration #
#############################################
@ -614,6 +629,7 @@ GOOGLE_ANALYTICS_PROPERTY_IDS = {
'ipv6onlyhosting.ch': 'UA-62285904-10',
'ipv6onlyhosting.net': 'UA-62285904-10',
'ipv6onlyhosting.com': 'UA-62285904-10',
'comic.ungleich.ch': 'UA-62285904-13',
'127.0.0.1:8000': 'localhost',
'dynamicweb-development.ungleich.ch': 'development',
'dynamicweb-staging.ungleich.ch': 'staging'
@ -685,6 +701,12 @@ if ENABLE_LOGGING:
TEST_MANAGE_SSH_KEY_PUBKEY = env('TEST_MANAGE_SSH_KEY_PUBKEY')
TEST_MANAGE_SSH_KEY_HOST = env('TEST_MANAGE_SSH_KEY_HOST')
X_FRAME_OPTIONS_ALLOW_FROM_URI = env('X_FRAME_OPTIONS_ALLOW_FROM_URI')
X_FRAME_OPTIONS = ('SAMEORIGIN' if X_FRAME_OPTIONS_ALLOW_FROM_URI is None else
'ALLOW-FROM {}'.format(
X_FRAME_OPTIONS_ALLOW_FROM_URI.strip()
))
DEBUG = bool_env('DEBUG')
if DEBUG:

View File

@ -1,5 +1,7 @@
from cms.models.pagemodel import Page
from django.conf.urls import include, url
from django.contrib import admin
from django.contrib.sites.models import Site
from django.conf.urls.i18n import i18n_patterns
from django.conf.urls.static import static
from django.views import i18n, static as static_view
@ -8,6 +10,7 @@ from django.conf import settings
from hosting.views import (
RailsHostingView, DjangoHostingView, NodeJSHostingView
)
from datacenterlight.views import PaymentOrderView
from membership import urls as membership_urls
from ungleich_page.views import LandingView
from django.views.generic import RedirectView
@ -16,8 +19,8 @@ import debug_toolbar
urlpatterns = [
url(r'^index.html$', LandingView.as_view()),
url(r'^open_api/', include('opennebula_api.urls',
namespace='opennebula_api')),
url(r'^open_api/',
include('opennebula_api.urls', namespace='opennebula_api')),
url(r'^railshosting/', RailsHostingView.as_view(),
name="rails.hosting"),
url(r'^nodehosting/', NodeJSHostingView.as_view(),
@ -26,8 +29,10 @@ urlpatterns = [
name="django.hosting"),
url(r'^nosystemd/', include('nosystemd.urls', namespace="nosystemd")),
url(r'^taggit_autosuggest/', include('taggit_autosuggest.urls')),
url(r'^jsi18n/(?P<packages>\S+?)/$',
i18n.javascript_catalog),
url(r'^jsi18n/(?P<packages>\S+?)/$', i18n.javascript_catalog),
url(r'^product/(?P<product_slug>[\w-]+)/$',
PaymentOrderView.as_view(),
name='show_product'),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
urlpatterns += i18n_patterns(
@ -35,30 +40,31 @@ urlpatterns += i18n_patterns(
)
# note the django CMS URLs included via i18n_patterns
REDIRECT_TO_CMS = False
if Page.objects.filter(site_id=Site.objects.get_current().id).count():
REDIRECT_TO_CMS = True
urlpatterns += i18n_patterns(
url(r'^$', LandingView.as_view()),
url(r'^admin/', include(admin.site.urls)),
url(r'^datacenterlight/',
include('datacenterlight.urls', namespace="datacenterlight")),
url(r'^hosting/', RedirectView.as_view(
url=reverse_lazy('hosting:login')), name='redirect_hosting_login'),
url(r'^hosting/', RedirectView.as_view(url=reverse_lazy('hosting:login')),
name='redirect_hosting_login'),
url(r'^alplora/', include('alplora.urls', namespace="alplora")),
url(r'^membership/', include(membership_urls)),
url(r'^digitalglarus/', include('digitalglarus.urls',
namespace="digitalglarus")),
# url(r'^blog/', include('ungleich.urls', namespace='ungleich')),
url(r'^',
include('ungleich_page.urls',
namespace='ungleich_page'),
name='ungleich_page'),
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]*)/$',
url(r'^digitalglarus/',
include('digitalglarus.urls', namespace="digitalglarus")),
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'^blog/$',
RedirectView.as_view(url=reverse_lazy('ungleich:post-list')),
name='blog_list_view'),
url(r'^cms/', include('cms.urls')),
url(r'^blog/', include('djangocms_blog.urls', namespace='djangocms_blog')),
url(r'^$', RedirectView.as_view(url='/cms') if REDIRECT_TO_CMS
else LandingView.as_view()),
url(r'^', include('ungleich_page.urls', namespace='ungleich_page')),
)
urlpatterns += [

View File

@ -3,10 +3,12 @@ from django.conf.urls import include, url
from django.conf.urls.i18n import i18n_patterns
from django.contrib import admin
from django.views import static as static_view
from django.views.generic import RedirectView
urlpatterns = i18n_patterns(
url(r'^admin/', include(admin.site.urls)),
url(r'^cms/', include('cms.urls')),
url(r'^$', RedirectView.as_view(url='/cms')),
)
urlpatterns += [

View File

@ -1,8 +1,8 @@
from django.contrib import admin
from .models import HostingOrder, HostingBill, HostingPlan
from .models import HostingOrder, HostingBill, HostingPlan, GenericProduct
admin.site.register(HostingOrder)
admin.site.register(HostingBill)
admin.site.register(HostingPlan)
admin.site.register(GenericProduct)

View File

@ -10,7 +10,7 @@ from django.utils.translation import ugettext_lazy as _
from membership.models import CustomUser
from utils.hosting_utils import get_all_public_keys
from .models import UserHostingKey
from .models import UserHostingKey, GenericProduct
logger = logging.getLogger(__name__)
@ -52,6 +52,93 @@ class HostingUserLoginForm(forms.Form):
raise forms.ValidationError(_("User does not exist"))
class ProductModelChoiceField(forms.ModelChoiceField):
def label_from_instance(self, obj):
return obj.product_name
class GenericPaymentForm(forms.Form):
product_name = ProductModelChoiceField(
queryset=GenericProduct.objects.all().order_by('product_name'),
empty_label=_("Choose a product"),
)
amount = forms.FloatField(
widget=forms.TextInput(
attrs={'placeholder': _('Amount in CHF'),
'readonly': 'readonly', }
),
max_value=999999,
min_value=1,
label=_('Amount in CHF')
)
recurring = forms.BooleanField(required=False,
label=_("Recurring monthly"), )
description = forms.CharField(
widget=forms.Textarea(attrs={'style': "height: 60px;"}),
required=False
)
class Meta:
model = GenericProduct
fields = ['product_name', 'amount', 'recurring', 'description']
def clean_amount(self):
amount = self.cleaned_data.get('amount')
if (float(self.cleaned_data.get('product_name').get_actual_price()) !=
amount):
raise forms.ValidationError(_("Amount field does not match"))
return amount
def clean_recurring(self):
recurring = self.cleaned_data.get('recurring')
if (self.cleaned_data.get('product_name').product_is_subscription !=
(True if recurring else False)):
raise forms.ValidationError(_("Recurring field does not match"))
return recurring
class ProductPaymentForm(GenericPaymentForm):
def __init__(self, *args, **kwargs):
product_id = kwargs.pop('product_id', None)
if product_id is not None:
self.product = GenericProduct.objects.get(id=product_id)
super(ProductPaymentForm, self).__init__(*args, **kwargs)
self.fields['product_name'] = forms.CharField(
widget=forms.TextInput(
attrs={'placeholder': _('Product name'),
'readonly': 'readonly'}
)
)
if self.product.product_is_subscription:
self.fields['amount'].label = "{amt} ({payment_type})".format(
amt=_('Amount in CHF'),
payment_type=_('Monthly subscription')
)
else:
self.fields['amount'].label = "{amt} ({payment_type})".format(
amt=_('Amount in CHF'),
payment_type=_('One time payment')
)
self.fields['recurring'].widget = forms.HiddenInput()
self.fields['product_name'].widget.attrs['class'] = 'input-no-border'
self.fields['amount'].widget.attrs['class'] = 'input-no-border'
self.fields['description'].widget.attrs['class'] = 'input-no-border'
def clean_amount(self):
amount = self.cleaned_data.get('amount')
if (self.product is None or
float(self.product.get_actual_price()) != amount):
raise forms.ValidationError(_("Amount field does not match"))
return amount
def clean_recurring(self):
recurring = self.cleaned_data.get('recurring')
if (self.product.product_is_subscription !=
(True if recurring else False)):
raise forms.ValidationError(_("Recurring field does not match"))
return recurring
class HostingUserSignupForm(forms.ModelForm):
confirm_password = forms.CharField(label=_("Confirm Password"),
widget=forms.PasswordInput())
@ -111,7 +198,7 @@ class UserHostingKeyForm(forms.ModelForm):
public_key=openssh_pubkey_str).first().name
KEY_EXISTS_MESSAGE = _(
"This key exists already with the name \"%(name)s\"") % {
'name': key_name}
'name': key_name}
raise forms.ValidationError(KEY_EXISTS_MESSAGE)
with tempfile.NamedTemporaryFile(delete=True) as tmp_public_key_file:

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-12-21 00:23+0000\n"
"POT-Creation-Date: 2018-09-08 08:45+0000\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"
@ -209,7 +209,7 @@ msgstr "Du hast eine neue virtuelle Maschine bestellt!"
#, python-format
msgid "Your order of <strong>%(vm_name)s</strong> has been charged."
msgstr "Deine Bestellung von <strong>%(vm_name)s</strong> wurde erhoben."
msgstr "Deine Bestellung von <strong>%(vm_name)s</strong> wurde entgegengenommen."
msgid "You can view your VM detail by clicking the button below."
msgstr "Um die Rechnung zu sehen, klicke auf den Button unten."
@ -222,7 +222,7 @@ msgstr "Dein Data Center Light Team"
#, python-format
msgid "Your order of %(vm_name)s has been charged."
msgstr "Deine Bestellung von %(vm_name)s wurde erhoben."
msgstr "Deine Bestellung von %(vm_name)s wurde entgegengenommen."
msgid "You can view your VM detail by following the link below."
msgstr "Um die Rechnung zu sehen, klicke auf den Link unten."
@ -249,7 +249,7 @@ msgstr "VM Kündigung"
#, python-format
msgid ""
"You are receiving this email because your virutal machine <strong>"
"You are receiving this email because your virtual machine <strong>"
"%(vm_name)s</strong> has been cancelled."
msgstr ""
"Du erhälst diese E-Mail, da deine virtuelle Maschine <strong>%(vm_name)s</"
@ -265,7 +265,7 @@ msgstr "NEUE VM"
#, python-format
msgid ""
"You are receiving this email because your virutal machine %(vm_name)s has "
"You are receiving this email because your virtual machine %(vm_name)s has "
"been cancelled."
msgstr ""
"Du erhälst diese E-Mail, da deine virtuelle Maschine %(vm_name)s gekündigt "
@ -274,6 +274,28 @@ msgstr ""
msgid "You can always order a new VM by following the link below."
msgstr ""
msgid "Card Number"
msgstr "Kreditkartennummer"
msgid "Expiry Date"
msgstr "Ablaufdatum"
msgid "CVC"
msgstr ""
msgid "Card Type"
msgstr "Kartentyp"
msgid ""
"You are not making any payment yet. After placing your order, you will be "
"taken to the Submit Payment Page."
msgstr ""
"Es wird noch keine Bezahlung vorgenommen. Die Bezahlung wird erst ausgelöst, "
"nachdem Du die Bestellung auf der nächsten Seite bestätigt hast."
msgid "SUBMIT"
msgstr "ABSENDEN"
msgid "Toggle navigation"
msgstr "Umschalten"
@ -365,15 +387,24 @@ msgstr "Arbeitsspeicher"
msgid "Disk space"
msgstr "Festplattenkapazität"
msgid "Subtotal"
msgstr "Zwischensumme"
msgid "VAT"
msgstr "Mehrwertsteuer"
msgid "Discount"
msgstr "Rabatt"
msgid "Total"
msgstr "Gesamt"
#, python-format
msgid ""
"By clicking \"Place order\" this plan will charge your credit card account "
"with the fee of %(vm_price)sCHF/month"
"with %(vm_price)s CHF/month"
msgstr ""
"Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit %(vm_price)sCHF "
"Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit %(vm_price)s CHF "
"pro Monat belastet"
msgid "Place order"
@ -421,9 +452,26 @@ msgstr "Konfiguration"
msgid "including VAT"
msgstr "inkl. Mehrwertsteuer"
msgid "excluding VAT"
msgstr "exkl. Mehrwertsteuer"
msgid "Will be applied at checkout"
msgstr "wird an der Kasse angewendet"
msgid "Billing Address"
msgstr "Rechnungsadresse"
msgid ""
"Please select one of the cards that you used before or fill in your credit "
"card information below. We are using <a href=\"https://stripe.com\" target="
"\"_blank\">Stripe</a> for payment and do not store your information in our "
"database."
msgstr ""
"Bitte wähle eine der zuvor genutzten Kreditkarten oder gib Deine "
"Kreditkartendetails unten an. Die Bezahlung wird über <a href=\"https://"
"stripe.com\" target=\"_blank\">Stripe</a> abgewickelt. Wir speichern Deine "
"Kreditkartendetails nicht in unserer Datenbank."
msgid ""
"Please fill in your credit card information below. We are using <a href="
"\"https://stripe.com\" target=\"_blank\">Stripe</a> for payment and do not "
@ -433,28 +481,24 @@ msgstr ""
"\"https://stripe.com\" target=\"_blank\">Stripe</a> für die Bezahlung und "
"speichern keine Informationen in unserer Datenbank."
msgid ""
"You are not making any payment yet. After submitting your card information, "
"you will be taken to the Confirm Order Page."
msgstr ""
"Es wird noch keine Bezahlung vorgenommen. Die Bezahlung wird erst ausgelöst, "
"nachdem Du die Bestellung auf der nächsten Seite bestätigt hast."
msgid "Last"
msgstr "Letzten"
msgid "SUBMIT"
msgstr "ABSENDEN"
msgid "Card Number"
msgstr "Kreditkartennummer"
msgid "Expiry Date"
msgstr "Ablaufdatum"
msgid "CVC"
msgstr ""
msgid "Card Type"
msgid "Type"
msgstr "Kartentyp"
msgid "SELECT"
msgstr "AUSWÄHLEN"
msgid "Add a new credit card"
msgstr "Eine neue Kreditkarte hinzufügen"
msgid "NEW CARD"
msgstr "BEARBEITEN"
msgid "New Credit Card"
msgstr "Neue Kreditkarte"
msgid "Processing"
msgstr "Weiter"
@ -468,13 +512,22 @@ msgid "Password reset"
msgstr "Passwort zurücksetzen"
msgid "UPDATE"
msgstr ""
msgstr "AKTUALISIEREN"
msgid "Last"
msgstr ""
msgid "REMOVE CARD"
msgstr "KARTE ENTFERNEN"
msgid "Type"
msgstr "Kartentyp"
msgid "Remove Card"
msgstr "Karte entfernen"
msgid "Do you want to remove this associated card?"
msgstr "Möchtest Du den Schlüssel löschen?"
msgid "Delete"
msgstr "Löschen"
msgid "DEFAULT"
msgstr "STANDARD"
msgid "No Credit Cards Added"
msgstr "Es wurde keine Kreditkarte hinzugefügt"
@ -519,10 +572,7 @@ msgid "Public Key"
msgstr ""
msgid "Private Key"
msgstr ""
msgid "Delete"
msgstr "Löschen"
msgstr "Privater Schlüssel"
msgid "Delete SSH Key"
msgstr "SSH Key löschen"
@ -580,6 +630,12 @@ msgstr ""
"Bitte entschuldige, es scheint ein unerwarteter Fehler aufgetreten zu sein. "
"Versuche es doch bitte noch einmal."
msgid "Attention:"
msgstr "Achtung:"
msgid "terminating VM can not be reverted."
msgstr "Das Beenden kann nicht rückgängig gemacht werden."
msgid "Something doesn't work?"
msgstr "Etwas funktioniert nicht?"
@ -592,8 +648,12 @@ msgstr "KONTAKT"
msgid "Terminate your Virtual Machine"
msgstr "Deine Virtuelle Maschine beenden"
msgid "Do you want to cancel your Virtual Machine"
msgstr "Bist Du sicher, dass Du Deine virtuelle Maschine beenden willst"
msgid ""
"Terminated VMs can not be revived and will not be refunded. Do you want to "
"terminate your VM?"
msgstr ""
"Beendete VMs können nicht wiederhergestellt oder erstattet werden. Möchtest "
"du die VM beenden?"
#, python-format
msgid ""
@ -655,6 +715,36 @@ msgstr "Dein Passwort konnte nicht zurückgesetzt werden."
msgid "The reset password link is no longer valid."
msgstr "Der Link zum Zurücksetzen Deines Passwortes ist nicht mehr gültig."
msgid "Card deassociation successful"
msgstr "Die Verbindung mit der Karte wurde erfolgreich aufgehoben"
msgid "You are not permitted to do this operation"
msgstr "Du hast keine Erlaubnis um diese Operation durchzuführen"
msgid "The selected card does not exist"
msgstr "Die ausgewählte Karte existiert nicht"
msgid "Billing address updated successfully"
msgstr "Die Rechnungsadresse wurde erfolgreich aktualisiert"
msgid "You seem to have already added this card"
msgstr "Es scheint, als hättest du diese Karte bereits hinzugefügt"
#, python-brace-format
msgid "An error occurred while associating the card. Details: {details}"
msgstr ""
"Beim Verbinden der Karte ist ein Fehler aufgetreten. Details: {details}"
msgid "Successfully associated the card with your account"
msgstr "Die Karte wurde erfolgreich mit deinem Konto verbunden"
#, python-brace-format
msgid "{user} does not have permission to access the card"
msgstr "{user} hat keine Erlaubnis auf diese Karte zuzugreifen"
msgid "An error occurred. Details: {}"
msgstr "Ein Fehler ist aufgetreten. Details: {}"
msgid "Invalid credit card"
msgstr "Ungültige Kreditkarte"
@ -699,6 +789,10 @@ msgstr "Ungültige RAM-Grösse"
msgid "Invalid storage size"
msgstr "Ungültige Speicher-Grösse"
#, python-brace-format
msgid "Incorrect pricing name. Please contact support{support_email}"
msgstr ""
msgid ""
"We could not find the requested VM. Please "
"contact Data Center Light Support."
@ -713,6 +807,11 @@ msgstr ""
msgid "Error terminating VM"
msgstr "Fehler beenden VM"
msgid ""
"VM terminate action timed out. Please contact support@datacenterlight.ch for "
"further information."
msgstr ""
#, python-format
msgid "Virtual Machine %(vm_name)s Cancelled"
msgstr "Virtuelle Maschine %(vm_name)s Kündigung"
@ -722,6 +821,9 @@ msgstr ""
"Es gab einen Fehler bei der Bearbeitung Deine Anfrage. Bitte versuche es "
"noch einmal."
#~ msgid "Do you want to cancel your Virtual Machine"
#~ msgstr "Bist Du sicher, dass Du Deine virtuelle Maschine beenden willst"
#~ msgid "Reset your password"
#~ msgstr "Passwort zurücksetzen"
@ -788,15 +890,6 @@ msgstr ""
#~ msgid "Notifications "
#~ msgstr "Benachrichtigungen"
#~ msgid "REMOVE CARD"
#~ msgstr "KARTE ENTFERNEN"
#~ msgid "EDIT CARD"
#~ msgstr "BEARBEITEN"
#~ msgid "Add a new Card."
#~ msgstr "Neue Kreditkarte hinzufügen."
#~ msgid "You are not making any payment here."
#~ msgstr "Es wird noch keine Bezahlung vorgenommen"

View File

@ -0,0 +1,45 @@
from django.core.management.base import BaseCommand
from hosting.models import UserCardDetail
from membership.models import CustomUser
from utils.stripe_utils import StripeUtils
class Command(BaseCommand):
help = '''Imports the usercard details of all customers. Created just for
multiple card support.'''
def handle(self, *args, **options):
try:
stripe_utils = StripeUtils()
for user in CustomUser.objects.all():
if hasattr(user, 'stripecustomer'):
if user.stripecustomer:
card_details_resp = stripe_utils.get_card_details(
user.stripecustomer.stripe_id
)
card_details = card_details_resp['response_object']
if card_details:
ucd = UserCardDetail.get_or_create_user_card_detail(
stripe_customer=user.stripecustomer,
card_details=card_details
)
UserCardDetail.save_default_card_local(
user.stripecustomer.stripe_id,
ucd.card_id
)
print("Saved user card details for {}".format(
user.email
))
else:
print(" --- Could not get card details for "
"{}".format(user.email))
print(" --- Error: {}".format(
card_details_resp['error']
))
else:
print(" === {} does not have a StripeCustomer object".format(
user.email
))
except Exception as e:
print(" *** Error occurred. Details {}".format(str(e)))

View File

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2018-04-16 00:22
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('datacenterlight', '0019_auto_20180415_2236'),
('hosting', '0043_vmdetail'),
]
operations = [
migrations.AddField(
model_name='hostingorder',
name='vm_pricing',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='datacenterlight.VMPricing'),
preserve_default=False,
),
]

View File

@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2018-07-01 20:28
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
import utils.mixins
class Migration(migrations.Migration):
dependencies = [
('datacenterlight', '0024_dclcalculatorpluginmodel_vm_templates_to_show'),
('hosting', '0044_hostingorder_vm_pricing'),
]
operations = [
migrations.CreateModel(
name='OrderDetail',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('cores', models.IntegerField(default=0)),
('memory', models.IntegerField(default=0)),
('hdd_size', models.IntegerField(default=0)),
('ssd_size', models.IntegerField(default=0)),
('vm_template', models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to='datacenterlight.VMTemplate')),
],
bases=(utils.mixins.AssignPermissionsMixin, models.Model),
),
migrations.AddField(
model_name='hostingorder',
name='order_detail',
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to='hosting.OrderDetail'),
),
]

View File

@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2018-07-03 20:32
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
import utils.mixins
class Migration(migrations.Migration):
dependencies = [
('membership', '0007_auto_20180213_0128'),
('hosting', '0045_auto_20180701_2028'),
]
operations = [
migrations.CreateModel(
name='UserCardDetail',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('last4', models.CharField(max_length=4)),
('brand', models.CharField(max_length=10)),
('card_id', models.CharField(blank=True, default='', max_length=100)),
('fingerprint', models.CharField(max_length=100)),
('exp_month', models.IntegerField()),
('exp_year', models.IntegerField()),
('preferred', models.BooleanField(default=False)),
('stripe_customer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='membership.StripeCustomer')),
],
options={
'permissions': (('view_usercarddetail', 'View User Card'),),
},
bases=(utils.mixins.AssignPermissionsMixin, models.Model),
),
]

View File

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2018-08-21 12:40
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('hosting', '0046_usercarddetail'),
]
operations = [
migrations.AlterField(
model_name='hostingorder',
name='cc_brand',
field=models.CharField(max_length=128),
),
migrations.AlterField(
model_name='usercarddetail',
name='brand',
field=models.CharField(max_length=128),
),
]

View File

@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2018-10-03 07:57
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
import utils.mixins
class Migration(migrations.Migration):
dependencies = [
('hosting', '0047_auto_20180821_1240'),
]
operations = [
migrations.CreateModel(
name='GenericProduct',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('product_name', models.CharField(default='', max_length=128)),
('product_slug', models.SlugField(help_text='An optional html id for the Section. Required to set as target of a link on page', unique=True)),
('product_description', models.CharField(default='', max_length=500)),
('created_at', models.DateTimeField(auto_now_add=True)),
('product_price', models.DecimalField(decimal_places=2, max_digits=6)),
('product_vat', models.DecimalField(decimal_places=4, default=0, max_digits=6)),
('product_is_subscription', models.BooleanField(default=True)),
],
bases=(utils.mixins.AssignPermissionsMixin, models.Model),
),
migrations.AddField(
model_name='hostingorder',
name='generic_payment_description',
field=models.CharField(max_length=500, null=True),
),
migrations.AddField(
model_name='hostingorder',
name='generic_product',
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to='hosting.GenericProduct'),
),
]

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2018-10-05 07:36
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('hosting', '0048_auto_20181003_0757'),
]
operations = [
migrations.AlterField(
model_name='genericproduct',
name='product_slug',
field=models.SlugField(help_text='An mandatory unique slug for the product', unique=True),
),
]

View File

@ -1,4 +1,5 @@
from django.shortcuts import redirect
from django.conf import settings
from django.core.urlresolvers import reverse
from opennebula_api.serializers import VirtualMachineTemplateSerializer
@ -24,3 +25,10 @@ class ProcessVMSelectionMixin(object):
request.session['next'] = reverse('hosting:payment')
return redirect(reverse('hosting:login'))
return redirect(reverse('hosting:payment'))
class HostingContextMixin(object):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['MULTISITE_CMS_FALLBACK'] = settings.MULTISITE_CMS_FALLBACK
return context

View File

@ -1,14 +1,17 @@
import os
import logging
from dateutil.relativedelta import relativedelta
import os
from Crypto.PublicKey import RSA
from dateutil.relativedelta import relativedelta
from django.db import models
from django.utils import timezone
from django.utils.functional import cached_property
from Crypto.PublicKey import RSA
from datacenterlight.models import VMPricing, VMTemplate
from membership.models import StripeCustomer, CustomUser
from utils.models import BillingAddress
from utils.mixins import AssignPermissionsMixin
from utils.models import BillingAddress
from utils.stripe_utils import StripeUtils
logger = logging.getLogger(__name__)
@ -39,6 +42,49 @@ class HostingPlan(models.Model):
return price
class OrderDetail(AssignPermissionsMixin, models.Model):
vm_template = models.ForeignKey(
VMTemplate, blank=True, null=True, default=None,
on_delete=models.SET_NULL
)
cores = models.IntegerField(default=0)
memory = models.IntegerField(default=0)
hdd_size = models.IntegerField(default=0)
ssd_size = models.IntegerField(default=0)
def __str__(self):
return "Not available" if self.vm_template is None else (
"%s - %s, %s cores, %s GB RAM, %s GB SSD" % (
self.vm_template.name, self.vm_template.vm_type, self.cores,
self.memory, self.ssd_size
)
)
class GenericProduct(AssignPermissionsMixin, models.Model):
permissions = ('view_genericproduct',)
product_name = models.CharField(max_length=128, default="")
product_slug = models.SlugField(
unique=True,
help_text=(
'An mandatory unique slug for the product'
)
)
product_description = models.CharField(max_length=500, default="")
created_at = models.DateTimeField(auto_now_add=True)
product_price = models.DecimalField(max_digits=6, decimal_places=2)
product_vat = models.DecimalField(max_digits=6, decimal_places=4, default=0)
product_is_subscription = models.BooleanField(default=True)
def __str__(self):
return self.product_name
def get_actual_price(self):
return round(
self.product_price + (self.product_price * self.product_vat), 2
)
class HostingOrder(AssignPermissionsMixin, models.Model):
ORDER_APPROVED_STATUS = 'Approved'
ORDER_DECLINED_STATUS = 'Declined'
@ -49,11 +95,22 @@ class HostingOrder(AssignPermissionsMixin, models.Model):
created_at = models.DateTimeField(auto_now_add=True)
approved = models.BooleanField(default=False)
last4 = models.CharField(max_length=4)
cc_brand = models.CharField(max_length=10)
cc_brand = models.CharField(max_length=128)
stripe_charge_id = models.CharField(max_length=100, null=True)
price = models.FloatField()
subscription_id = models.CharField(max_length=100, null=True)
vm_pricing = models.ForeignKey(VMPricing)
order_detail = models.ForeignKey(
OrderDetail, null=True, blank=True, default=None,
on_delete=models.SET_NULL
)
generic_product = models.ForeignKey(
GenericProduct, null=True, blank=True, default=None,
on_delete=models.SET_NULL
)
generic_payment_description = models.CharField(
max_length=500, null=True
)
permissions = ('view_hostingorder',)
class Meta:
@ -62,20 +119,32 @@ class HostingOrder(AssignPermissionsMixin, models.Model):
)
def __str__(self):
return "%s" % (self.id)
hosting_order_str = ("Order Nr: #{} - VM_ID: {} - {} - {} - "
"Specs: {} - Price: {}").format(
self.id, self.vm_id, self.customer.user.email, self.created_at,
self.order_detail, self.price
)
if self.generic_product_id is not None:
hosting_order_str += " - Generic Payment"
if self.stripe_charge_id is not None:
hosting_order_str += " - One time charge"
else:
hosting_order_str += " - Recurring"
return hosting_order_str
@cached_property
def status(self):
return self.ORDER_APPROVED_STATUS if self.approved else self.ORDER_DECLINED_STATUS
@classmethod
def create(cls, price=None, vm_id=None, customer=None,
billing_address=None):
def create(cls, price=None, vm_id=0, customer=None,
billing_address=None, vm_pricing=None):
instance = cls.objects.create(
price=price,
vm_id=vm_id,
customer=customer,
billing_address=billing_address
billing_address=billing_address,
vm_pricing=vm_pricing
)
instance.assign_permissions(customer.user)
return instance
@ -180,3 +249,156 @@ class VMDetail(models.Model):
months = relativedelta(end_date, self.created_at).months or 1
end_date = self.created_at + relativedelta(months=months, days=-1)
return end_date
class UserCardDetail(AssignPermissionsMixin, models.Model):
permissions = ('view_usercarddetail',)
stripe_customer = models.ForeignKey(StripeCustomer)
last4 = models.CharField(max_length=4)
brand = models.CharField(max_length=128)
card_id = models.CharField(max_length=100, blank=True, default='')
fingerprint = models.CharField(max_length=100)
exp_month = models.IntegerField(null=False)
exp_year = models.IntegerField(null=False)
preferred = models.BooleanField(default=False)
class Meta:
permissions = (
('view_usercarddetail', 'View User Card'),
)
@classmethod
def create(cls, stripe_customer=None, last4=None, brand=None,
fingerprint=None, exp_month=None, exp_year=None, card_id=None,
preferred=False):
instance = cls.objects.create(
stripe_customer=stripe_customer, last4=last4, brand=brand,
fingerprint=fingerprint, exp_month=exp_month, exp_year=exp_year,
card_id=card_id, preferred=preferred
)
instance.assign_permissions(stripe_customer.user)
return instance
@classmethod
def get_all_cards_list(cls, stripe_customer):
"""
Get all the cards of the given customer as a list
:param stripe_customer: The StripeCustomer object
:return: A list of all cards; an empty list if the customer object is
None
"""
cards_list = []
if stripe_customer is None:
return cards_list
user_card_details = UserCardDetail.objects.filter(
stripe_customer_id=stripe_customer.id
).order_by('-preferred', 'id')
for card in user_card_details:
cards_list.append({
'last4': card.last4, 'brand': card.brand, 'id': card.id,
'preferred': card.preferred
})
return cards_list
@classmethod
def get_or_create_user_card_detail(cls, stripe_customer, card_details):
"""
A method that checks if a UserCardDetail object exists already
based upon the given card_details and creates it for the given
customer if it does not exist. It returns the UserCardDetail object
matching the given card_details if it exists.
:param stripe_customer: The given StripeCustomer object to whom the
card object should belong to
:param card_details: A dictionary identifying a given card
:return: UserCardDetail object
"""
try:
if ('fingerprint' in card_details and 'exp_month' in card_details
and 'exp_year' in card_details):
card_detail = UserCardDetail.objects.get(
stripe_customer=stripe_customer,
fingerprint=card_details['fingerprint'],
exp_month=card_details['exp_month'],
exp_year=card_details['exp_year']
)
else:
raise UserCardDetail.DoesNotExist()
except UserCardDetail.DoesNotExist:
preferred = False
if 'preferred' in card_details:
preferred = card_details['preferred']
card_detail = UserCardDetail.create(
stripe_customer=stripe_customer,
last4=card_details['last4'],
brand=card_details['brand'],
fingerprint=card_details['fingerprint'],
exp_month=card_details['exp_month'],
exp_year=card_details['exp_year'],
card_id=card_details['card_id'],
preferred=preferred
)
return card_detail
@staticmethod
def set_default_card(stripe_api_cus_id, stripe_source_id):
"""
Sets the given stripe source as the default source for the given
Stripe customer
:param stripe_api_cus_id: Stripe customer id
:param stripe_source_id: The Stripe source id
:return:
"""
stripe_utils = StripeUtils()
cus_response = stripe_utils.get_customer(stripe_api_cus_id)
cu = cus_response['response_object']
cu.default_source = stripe_source_id
cu.save()
UserCardDetail.save_default_card_local(
stripe_api_cus_id, stripe_source_id
)
@staticmethod
def set_default_card_from_stripe(stripe_api_cus_id):
stripe_utils = StripeUtils()
cus_response = stripe_utils.get_customer(stripe_api_cus_id)
cu = cus_response['response_object']
default_source = cu.default_source
if default_source is not None:
UserCardDetail.save_default_card_local(
stripe_api_cus_id, default_source
)
@staticmethod
def save_default_card_local(stripe_api_cus_id, card_id):
stripe_cust = StripeCustomer.objects.get(stripe_id=stripe_api_cus_id)
user_card_detail = UserCardDetail.objects.get(
stripe_customer=stripe_cust, card_id=card_id
)
for card in stripe_cust.usercarddetail_set.all():
card.preferred = False
card.save()
user_card_detail.preferred = True
user_card_detail.save()
@staticmethod
def get_user_card_details(stripe_customer, card_details):
"""
A utility function to check whether a StripeCustomer is already
associated with the card having given details
:param stripe_customer:
:param card_details:
:return: The UserCardDetails object if it exists, None otherwise
"""
try:
ucd = UserCardDetail.objects.get(
stripe_customer=stripe_customer,
fingerprint=card_details['fingerprint'],
exp_month=card_details['exp_month'],
exp_year=card_details['exp_year']
)
return ucd
except UserCardDetail.DoesNotExist:
return None

View File

@ -23,6 +23,13 @@
margin: 0 auto;
max-width: 1120px;
}
.container-table{
margin-top: 35px;
overflow-y: hidden;
}
.container-table table{
overflow-y: auto;
}
.borderless td {
border: none !important;
}
@ -35,6 +42,19 @@
color: transparent;
}
.inline-headers h3, .inline-headers h4 {
display: inline-block;
vertical-align: baseline;
}
.space-above {
margin-top: 4%;
}
.space-above-big {
margin-top: 20%;
}
.table>tbody>tr>td{
vertical-align: middle;
}
@ -274,6 +294,26 @@
font-size: 16px;
}
.payment-container .credit-card-info {
padding-bottom: 15px;
border-bottom: 1px solid #eee;
}
.credit-card-info {
display: flex;
}
.credit-card-info .align-bottom{
align-self: flex-end;
padding-right: 0 !important;
}
.another-card-text {
padding: 20px 0;
font-size: 18px;
font-weight: 700;
}
.credit-card-form {
max-width: 360px;
}
@ -293,8 +333,15 @@
text-decoration: none;
}
.settings-container .credit-card-details-opt {
padding-top: 15px;
.settings-container .new-card-head {
margin-top: 40px;
margin-bottom: 30px;
}
.settings-container .new-card-head h4 {
font-size: 15px;
margin-top: 8px;
font-weight: 600;
}
.caps-link .svg-img {
@ -313,7 +360,11 @@
.settings-container .btn-vm-contact {
font-weight: 600;
font-size: 13px;
/* padding: 4px 15px; */
}
.settings-container .choice-btn {
letter-spacing: 2px;
min-width: 127px;
}
.btn-wide {
@ -355,10 +406,37 @@
fill: #999;
}
.card-details-box {
border: 1px solid #eee;
padding: 5px 25px 25px;
}
.thick-hr {
border-top: 5px solid #eee;
}
.locale_date {
opacity: 0;
}
.locale_date.done{
opacity: 1;
}
.mb-0 {
margin-bottom: 0;
}
.thin-hr {
margin-top: 10px;
margin-bottom: 10px;
}
.new-card-head {
margin-top: 10px;
}
.new-card-button-margin button{
margin-top: 5px;
margin-bottom: 5px;
}

View File

@ -426,6 +426,7 @@ footer {
right: 0;
bottom: 0;
left: 0;
font-weight: 300;
}
p.copyright {
@ -448,230 +449,6 @@ a.unlink:hover {
color: inherit;
}
/***** DCL payment page **********/
.dcl-order-container {
font-weight: 300;
}
.dcl-order-table-header {
border-bottom: 1px solid #eee;
padding-top: 15px;
padding-bottom: 15px;
font-size: 16px;
color: #333;
text-align: center;
font-weight: 300;
}
.dcl-order-table-content {
border-bottom: 1px solid #eee;
padding-top: 15px;
padding-bottom: 15px;
font-size: 18px;
font-weight: 600;
text-align: center;
}
.tbl-content {
}
.dcl-order-table-total {
border-bottom: 4px solid #eee;
padding-top: 15px;
padding-bottom: 20px;
font-size: 20px;
font-weight: 600;
color: #999;
}
.dcl-order-table-total span {
font-size: 13px;
color: #999;
font-weight: 400;
padding-left: 5px;
}
.dcl-place-order-text{
color: #808080;
}
.dcl-order-table-total .tbl-total {
text-align: center;
color: #000;
padding-left: 44px;
}
.tbl-total .dcl-price-month {
font-size: 16px;
text-transform: capitalize;
color: #000;
}
.tbl-no-padding {
padding: 0px;
}
.dcl-billing-sec {
margin-top: 50px;
}
.dcl-order-sec {
padding: 0 30px;
}
.card-warning-content {
font-weight: 300;
border: 1px solid #a1a1a1;
border-radius: 3px;
padding: 5px;
margin-bottom: 15px;
}
.card-warning-error {
border: 1px solid #EB4D5C;
color: #EB4D5C;
}
.card-warning-addtional-margin {
margin-top: 15px;
}
.stripe-payment-btn {
outline: none;
width: auto;
float: right;
font-style: normal;
font-weight: 300;
position: absolute;
padding-left: 30px;
padding-right: 30px;
right: 0;
}
.card-cvc-element label {
padding-left: 10px;
}
.card-element {
margin-bottom: 10px;
}
.card-element label{
width:100%;
margin-bottom:0px;
}
.my-input {
border-bottom: 1px solid #ccc;
}
.card-cvc-element .my-input {
padding-left: 10px;
}
#card-errors {
clear: both;
padding: 0 0 10px;
color: #eb4d5c;
}
.credit-card-goup{
padding: 0;
}
@media (max-width: 767px) {
.dcl-order-table-total span {
padding-left: 3px;
}
.dcl-order-sec {
padding: 10px 20px 30px 20px;
border-bottom: 4px solid #eee;
}
.tbl-header {
border-bottom: 1px solid #eee;
padding: 10px 0;
}
.tbl-content {
border-bottom: 1px solid #eee;
padding: 10px 0;
}
.dcl-order-table-header {
border-bottom: 0px solid #eee;
padding: 10px 0;
text-align: left;
}
.dcl-order-table-content {
border-bottom: 0px solid #eee;
padding: 10px 0;
text-align: right;
font-size: 16px;
}
.dcl-order-table-total {
font-size: 18px;
color: #000;
padding: 10px 0;
border-bottom: 0px solid #eee;
}
.dcl-order-table-total .tbl-total {
padding: 0px;
text-align: right;
}
.dcl-billing-sec {
margin-top: 30px;
margin-bottom: 30px;
}
.card-expiry-element {
padding-right: 10px;
}
.card-cvc-element {
padding-left: 10px;
}
#billing-form .form-control {
box-shadow: none !important;
font-weight: 400;
}
}
@media (min-width: 1200px) {
.dcl-order-container {
width: 990px;
padding-right: 15px;
padding-left: 15px;
margin-right: auto;
margin-left: auto;
}
}
@media (min-width: 768px) {
.dcl-billing {
padding-right: 65px;
border-right: 1px solid #eee;
}
.dcl-creditcard {
padding-left: 65px;
}
.tbl-tot {
padding-left: 17px;
}
.content-dashboard {
/*width: auto !important;*/
}
}
@media only screen and (max-width: 1040px) and (min-width: 768px) {
.content-dashboard {
width: 96% !important;

View File

@ -3,6 +3,7 @@
margin: 100px auto 40px;
border: 1px solid #ccc;
padding: 15px;
color: #595959;
}
@media(min-width: 768px) {
@ -48,10 +49,6 @@
margin-bottom: 15px;
}
.order-detail-container .order-details strong {
color: #595959;
}
.order-detail-container h4 {
font-size: 16px;
font-weight: bold;
@ -60,13 +57,28 @@
.order-detail-container p {
margin-bottom: 5px;
color: #595959;
}
.order-detail-container hr {
margin: 15px 0;
}
.order-detail-container .thin-hr {
margin: 10px 0;
}
.order-detail-container .subtotal-price {
font-size: 16px;
}
.order-detail-container .subtotal-price .text-primary {
font-size: 17px;
}
.order-detail-container .total-price {
font-size: 18px;
}
@media (max-width: 767px) {
.order-confirm-btn {
text-align: center;
@ -96,4 +108,8 @@
#virtual_machine_create_form {
padding: 15px 0;
}
.dcl-place-order-text {
color: #808080;
}

View File

@ -1,19 +1,35 @@
.payment-container {padding-top:70px; padding-bottom: 11%;}
.creditcard-box .panel-title {display: inline;font-weight: bold; font-size:17px;}
.creditcard-box .checkbox.pull-right { margin: 0; }
.creditcard-box .pl-ziro { padding-left: 0px; }
.creditcard-box .form-control.error {
border-color: red;
outline: 0;
box-shadow: inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(255,0,0,0.6);
.payment-container {
padding-top: 70px;
padding-bottom: 11%;
}
.creditcard-box .panel-title {
display: inline;
font-weight: bold;
font-size: 17px;
}
.creditcard-box .checkbox.pull-right {
margin: 0;
}
.creditcard-box .pl-ziro {
padding-left: 0px;
}
.creditcard-box .form-control.error {
border-color: red;
outline: 0;
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(255, 0, 0, 0.6);
}
.creditcard-box label.error {
font-weight: bold;
color: red;
padding: 2px 8px;
margin-top: 2px;
}
.creditcard-box .payment-errors {
font-weight: bold;
color: red;
@ -21,96 +37,221 @@
margin-top: 2px;
}
/* landing page payment new style */
.last-p {
margin-bottom: 0;
}
.dcl-payment-section {
max-width: 391px;
margin: 0 auto 30px;
padding: 0 10px 30px;
border-bottom: 1px solid #edebeb;
height: 100%;
}
.dcl-payment-section hr{
margin-top: 15px;
margin-bottom: 15px;
}
.dcl-payment-section .top-hr {
margin-left: -10px;
}
.dcl-payment-section h3 {
font-weight: 600;
}
.dcl-payment-section p {
/*padding: 0 5px;*/
font-weight: 400;
}
.dcl-payment-section .card-warning-content {
padding: 8px 10px;
font-weight: 300;
}
.dcl-payment-order strong{
font-size: 17px;
}
.dcl-payment-order p {
font-weight: 300;
}
.dcl-payment-section .form-group {
margin-bottom: 10px;
}
.dcl-payment-section .form-control {
box-shadow: none;
padding: 6px 12px;
height: 32px;
}
.dcl-payment-user {
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
.dcl-order-sec {
padding: 0 30px;
}
.dcl-payment-user h4 {
font-weight: 600;
font-size: 17px;
.dcl-billing-sec {
margin-top: 50px;
}
.dcl-order-container {
font-weight: 300;
}
.dcl-order-table-header {
border-bottom: 1px solid #eee;
padding: 15px 10px;
font-size: 16px;
color: #333;
font-weight: 300;
}
.dcl-order-table-content {
border-bottom: 1px solid #eee;
padding: 15px 10px;
font-size: 18px;
font-weight: 600;
}
.dcl-order-table-total {
border-bottom: 4px solid #eee;
padding-top: 15px;
padding-bottom: 20px;
font-size: 20px;
font-weight: 600;
}
.dcl-order-table-total span {
font-size: 13px;
color: #999;
font-weight: 400;
}
.dcl-order-table-total .tbl-total {
text-align: right;
color: #000;
}
.tbl-no-padding {
padding: 0px;
}
.card-warning-content {
font-weight: 300;
border: 1px solid #a1a1a1;
border-radius: 3px;
padding: 5px;
margin-bottom: 15px;
}
.card-warning-error {
border: 1px solid #EB4D5C;
color: #EB4D5C;
}
.card-warning-addtional-margin {
margin-top: 15px;
}
.stripe-payment-btn {
outline: none;
width: auto;
float: right;
font-style: normal;
font-weight: 300;
position: absolute;
padding-left: 30px;
padding-right: 30px;
right: 0;
}
.card-cvc-element label {
padding-left: 10px;
}
.card-element {
margin-bottom: 10px;
}
.card-element label {
width: 100%;
margin-bottom: 0px;
}
.my-input {
border-bottom: 1px solid #ccc;
}
.card-cvc-element .my-input {
padding-left: 10px;
}
#card-errors {
clear: both;
padding: 0 0 10px;
color: #eb4d5c;
}
.credit-card-goup {
padding: 0;
}
@media (max-width: 767px) {
.dcl-order-sec {
padding: 10px 5px 30px;
border-bottom: 4px solid #eee;
}
.dcl-billing-sec {
margin-top: 30px;
margin-bottom: 30px;
padding: 5px;
}
.dcl-billing {
margin-top: 20px;
margin-bottom: 40px;
}
.tbl-header {
border-bottom: 1px solid #eee;
padding-top: 10px;
padding-bottom: 10px;
margin-right: -15px;
}
.dcl-order-table-total .tbl-total {
margin-left: -15px;
}
.dcl-order-table-total .tbl-tot {
margin-right: -15px;
}
.tbl-content {
border-bottom: 1px solid #eee;
padding-top: 10px;
padding-bottom: 10px;
margin-left: -15px;
}
.dcl-order-table-header {
border-bottom: 0px solid #eee;
padding: 10px 0;
text-align: left;
}
.dcl-order-table-content {
border-bottom: 0px solid #eee;
padding: 10px 0;
text-align: right;
font-size: 16px;
}
.dcl-order-table-total {
font-size: 18px;
color: #000;
padding: 10px 0;
border-bottom: 0px solid #eee;
}
.card-expiry-element {
padding-right: 10px;
}
.card-cvc-element {
padding-left: 10px;
}
#billing-form .form-control {
box-shadow: none !important;
font-weight: 400;
}
}
@media (min-width: 768px) {
.dcl-payment-grid {
display: flex;
align-items: stretch;
flex-wrap: wrap;
}
.dcl-payment-box {
width: 50%;
position: relative;
padding: 0 30px;
}
.dcl-payment-box:nth-child(2) {
order: 1;
}
.dcl-payment-box:nth-child(4) {
order: 2;
}
.dcl-payment-section {
padding: 15px 10px;
margin-bottom: 0;
border-bottom-width: 5px;
}
.dcl-payment-box:nth-child(2n) .dcl-payment-section {
border-bottom: none;
}
.dcl-payment-box:nth-child(1):after,
.dcl-payment-box:nth-child(2):after {
content: ' ';
display: block;
background: #eee;
width: 1px;
position: absolute;
right: 0;
z-index: 2;
top: 20px;
bottom: 20px;
}
.dcl-billing {
padding-right: 65px;
border-right: 1px solid #eee;
}
.dcl-creditcard {
padding-left: 65px;
}
.dcl-order-table-total .tbl-total,
.dcl-order-table-total .tbl-tot {
padding: 0 10px;
}
.tbl-header-center,
.tbl-content-center {
text-align: center;
}
.tbl-header-right,
.tbl-content-right {
text-align: right;
}
}
@media (min-width: 1200px) {
.dcl-order-container {
width: 990px;
padding-right: 15px;
padding-left: 15px;
margin-right: auto;
margin-left: auto;
}
}

View File

@ -1,7 +1,9 @@
/* Create VM calculator */
.price-calc-section {
padding: 80px 40px !important;
padding: 20px 0 !important;
font-weight: 300;
font-size: 18px;
}
@media (max-width: 768px) {
@ -40,19 +42,19 @@
}
.price-calc-section .card {
width: 50%;
border-radius: 7px;
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;
max-width: 4000px;
position: relative;
}
@media (min-width: 768px) {
.price-calc-section .card {
margin-left: 0;
/* margin-left: 0; */
}
}
@ -85,7 +87,7 @@
}
.price-calc-section .card .description {
padding: 7px 8px 2px;
padding: 12px;
position: relative;
display: flex;
justify-content: space-around !important;
@ -93,7 +95,7 @@
}
.price-calc-section .card .description span {
font-size: 14px;
font-size: 16px;
margin-left: 5px;
/* margin-left: 0px; */
/* justify-self: start; */
@ -104,17 +106,18 @@
}
.price-calc-section .card .description .select-number{
font-size: 16px;
font-size: 18px;
text-align: center;
width: 85px;
padding: 5px 10px;
}
.price-calc-section .card .description i {
color: #29427a;
cursor: pointer;
font-size: 20px;
border: 1px solid #ccc;
padding: 5px 6px 3px;
/* border: 1px solid #ccc; */
/* padding: 5px 6px 3px; */
border-radius: 5px;
}
@ -193,7 +196,7 @@
.price-calc-section .help-block.with-errors {
text-align: center;
margin: 0 0;
padding: 0 0 5px;
padding: 0 0;
}
.price-calc-section .help-block.with-errors ul {
margin-bottom: 0;
@ -209,10 +212,10 @@
display: block;
position: absolute;
bottom: 0;
left: 18%;
left: 0;
z-index: 20;
height: 1px;
width: 65%;
width: 100%;
background: rgba(128, 128, 128, 0.2);
}

View File

@ -146,9 +146,13 @@
text-align: center;
}
.vm-vmid-with-warning {
padding: 50px 0 33px !important;
}
.vm-vmid .alert {
margin-top: 15px;
margin-bottom: -60px;
margin-bottom: -25px;
}
.vm-item-lg {
@ -183,6 +187,13 @@
margin-top: 25px;
}
.vm-terminate-warning {
letter-spacing: 0.6px;
font-size: 12px;
font-weight: 400;
color: #373636;
}
.vm-contact-us {
margin: 25px 0 30px;
/* text-align: center; */
@ -269,7 +280,7 @@
border: 2px solid #A3C0E2;
padding: 5px 25px;
font-size: 12px;
letter-spacing: 1.3px;
letter-spacing: 2px;
}
.btn-vm-contact:hover, .btn-vm-contact:focus {
background: #fff;

View File

@ -153,4 +153,125 @@ $( document ).ready(function() {
$('.navbar-fixed-top.topnav').css('padding-right', topnavPadding-scrollbarWidth);
}
});
});
/* ---------------------------------------------
Scripts initialization
--------------------------------------------- */
var minRam = 1;
if(window.minRam){
minRam = window.minRam;
}
var cardPricing = {
'cpu': {
'id': 'coreValue',
'value': 1,
'min': 1,
'max': 48,
'interval': 1
},
'ram': {
'id': 'ramValue',
'value': 2,
'min': minRam,
'max': 200,
'interval': 1
},
'storage': {
'id': 'storageValue',
'value': 10,
'min': 10,
'max': 2000,
'interval': 10
}
};
function _initPricing() {
_fetchPricing();
$('.fa-minus-circle.left').click(function(event) {
var data = $(this).data('minus');
if (cardPricing[data].value > cardPricing[data].min) {
if(data === 'ram' && String(cardPricing[data].value) === "1" && minRam === 0.5){
cardPricing[data].value = 0.5;
$('#ramValue').val('0.5');
$("#ramValue").attr('step', 0.5);
} else {
cardPricing[data].value = Number(cardPricing[data].value) - cardPricing[data].interval;
}
}
_fetchPricing();
$('#ramValue').data('old-value', $('#ramValue').val());
});
$('.fa-plus-circle.right').click(function(event) {
var data = $(this).data('plus');
if (cardPricing[data].value < cardPricing[data].max) {
if(data === 'ram' && String(cardPricing[data].value) === "0.5" && minRam === 0.5){
cardPricing[data].value = 1;
$('#ramValue').val('1');
$("#ramValue").attr('step', 1);
} else {
cardPricing[data].value = Number(cardPricing[data].value) + cardPricing[data].interval;
}
}
_fetchPricing();
$('#ramValue').data('old-value', $('#ramValue').val());
});
$('.input-price').change(function() {
var data = $(this).attr("name");
var input = $('input[name=' + data + ']');
var inputValue = input.val();
if(data === 'ram') {
var ramInput = $('#ramValue');
if ($('#ramValue').data('old-value') < $('#ramValue').val()) {
if($('#ramValue').val() === '1' && minRam === 0.5) {
$("#ramValue").attr('step', 1);
$('#ramValue').val('1');
}
} else {
if($('#ramValue').val() === '0' && minRam === 0.5) {
$("#ramValue").attr('step', 0.5);
$('#ramValue').val('0.5');
}
}
inputValue = $('#ramValue').val();
$('#ramValue').data('old-value', $('#ramValue').val());
}
cardPricing[data].value = inputValue;
_fetchPricing();
});
}
function _fetchPricing() {
Object.keys(cardPricing).map(function(element) {
$('input[name=' + element + ']').val(cardPricing[element].value);
});
_calcPricing();
}
function _calcPricing() {
if(typeof window.coresUnitPrice === 'undefined'){
window.coresUnitPrice = 5;
}
if(typeof window.ramUnitPrice === 'undefined'){
window.ramUnitPrice = 2;
}
if(typeof window.ssdUnitPrice === 'undefined'){
window.ssdUnitPrice = 0.6;
}
if(typeof window.discountAmount === 'undefined'){
window.discountAmount = 0;
}
var total = (cardPricing['cpu'].value * window.coresUnitPrice) +
(cardPricing['ram'].value * window.ramUnitPrice) +
(cardPricing['storage'].value * window.ssdUnitPrice) -
window.discountAmount;
total = parseFloat(total.toFixed(2));
$("#total").text(total);
}
_initPricing();
$('#ramValue').data('old-value', $('#ramValue').val());
});

View File

@ -22,6 +22,39 @@ function setBrandIcon(brand) {
$(document).ready(function () {
$(function () {
$("select#id_generic_payment_form-product_name").change(function () {
var gp_form = $('#generic-payment-form');
$.ajax({
url: gp_form.attr('action'),
type: 'POST',
data: gp_form.serialize(),
init: function () {
console.log("init")
},
success: function (data) {
if (data.amount !== undefined) {
$("#id_generic_payment_form-amount").val(data.amount);
if (data.isSubscription) {
$('#id_generic_payment_form-recurring').prop('checked', true);
} else {
$('#id_generic_payment_form-recurring').prop('checked', false);
}
} else {
$("#id_generic_payment_form-amount").val('');
$('#id_generic_payment_form-recurring').prop('checked', false);
console.log("No product found")
}
},
error: function (xmlhttprequest, textstatus, message) {
$("#id_generic_payment_form-amount").val('');
$('#id_generic_payment_form-recurring').prop('checked', false);
console.log("Error fetching product")
}
});
})
});
$.ajaxSetup({
beforeSend: function (xhr, settings) {
function getCookie(name) {
@ -124,17 +157,35 @@ $(document).ready(function () {
$('#billing-form').submit();
}
function getCookie(name) {
var value = "; " + document.cookie;
var parts = value.split("; " + name + "=");
if (parts.length === 2) return parts.pop().split(";").shift();
}
function submitBillingForm() {
var billing_form = $('#billing-form');
var recurring_input = $('#id_generic_payment_form-recurring');
billing_form.append('<input type="hidden" name="generic_payment_form-product_name" value="' + $('#id_generic_payment_form-product_name').val() + '" />');
billing_form.append('<input type="hidden" name="generic_payment_form-amount" value="' + $('#id_generic_payment_form-amount').val() + '" />');
if (recurring_input.attr('type') === 'hidden') {
billing_form.append('<input type="hidden" name="generic_payment_form-recurring" value="' + (recurring_input.val() === 'True' ? 'on' : '') + '" />');
} else {
billing_form.append('<input type="hidden" name="generic_payment_form-recurring" value="' + (recurring_input.prop('checked') ? 'on' : '') + '" />');
}
billing_form.append('<input type="hidden" name="generic_payment_form-description" value="' + $('#id_generic_payment_form-description').val() + '" />');
billing_form.submit();
}
var $form_new = $('#payment-form-new');
$form_new.submit(payWithStripe_new);
function payWithStripe_new(e) {
e.preventDefault();
function stripeTokenHandler(token) {
// Insert the token ID into the form so it gets submitted to the server
$('#id_token').val(token.id);
$('#billing-form').submit();
submitBillingForm();
}
@ -195,5 +246,11 @@ $(document).ready(function () {
$(element).closest('.form-group').append(error);
}
});
$('.credit-card-info .btn.choice-btn').click(function () {
var id = this.dataset['id_card'];
$('#id_card').val(id);
submitBillingForm();
});
});

View File

@ -134,3 +134,15 @@ $(document).ready(function() {
$(this).find('.modal-footer .btn').addClass('hide');
})
});
window.onload = function () {
var locale_dates = document.getElementsByClassName("locale_date");
var formats = ['YYYY-MM-DD hh:mm a'];
var i;
for (i = 0; i < locale_dates.length; i++) {
var oldDate = moment.utc(locale_dates[i].textContent, formats);
var outputFormat = locale_dates[i].getAttribute('data-format') || oldDate._f;
locale_dates[i].innerHTML = oldDate.local().format(outputFormat);
locale_dates[i].className += ' done';
}
};

View File

@ -9,7 +9,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<meta name="author" content="ungleich glarus ag">
<title>{{ domain }} - {{ hosting }} hosting as easy as possible</title>

View File

@ -1,5 +1,5 @@
{% load staticfiles bootstrap3%}
{% load i18n %}
{% load staticfiles i18n cms_tags sekizai_tags %}
<!DOCTYPE html>
<html lang="en">
@ -29,6 +29,9 @@
{% block css_extra %}
{% endblock css_extra %}
{% render_block "css" postprocessor "compressor.contrib.sekizai.compress" %}
{% render_block "js" postprocessor "compressor.contrib.sekizai.compress" %}
<!-- Custom Fonts -->
<link href='//fonts.googleapis.com/css?family=Raleway' rel='stylesheet' type='text/css'>
<link href="{% static 'datacenterlight/font-awesome/css/font-awesome.min.css' %}" rel="stylesheet" type="text/css">
@ -48,7 +51,7 @@
</head>
<body>
{% cms_toolbar %}
{% block navbar %}
{% include "hosting/includes/_navbar_user.html" %}
@ -63,7 +66,7 @@
{% if request.user.is_authenticated %}
<footer class="footer-vm">
<div class="container">
<p class="copyright text-muted small">Copyright &copy; ungleich GmbH {% now "Y" %}. {% trans "All Rights Reserved" %}</p>
<p class="copyright text-muted small">Copyright &copy; ungleich glarus ag {% now "Y" %}. {% trans "All Rights Reserved" %}</p>
</div>
</footer>
{% else %}

View File

@ -1,7 +1,9 @@
{% extends "hosting/base_short.html" %}
{% load staticfiles bootstrap3 i18n %}
{% load staticfiles bootstrap3 i18n cms_tags %}
{% block content %}
<div class="dashboard-container create-vm-container">
<div class="row">
<div class="col-sm-5">
@ -17,14 +19,8 @@
{% 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 class="col-sm-6 hosting-calculator">
{% render_placeholder cms_integration.calculator_placeholder %}
</div>
</div>
</div>

View File

@ -14,7 +14,7 @@
<table style="width: 100%; border-spacing: 0; border-collapse: collapse; max-width: 560px;">
<tr>
<td>
<img src="{{ base_url }}{% static 'datacenterlight/img/logo_black.png' %}" style="width: 200px; height: 50px;">
<img src="{{ base_url }}{% static 'datacenterlight/img/datacenterlight.png' %}" style="max-width: 200px;">
</td>
</tr>
<tr>

View File

@ -14,7 +14,7 @@
<table style="width: 100%; border-spacing: 0; border-collapse: collapse; max-width: 560px;">
<tr>
<td>
<img src="{{base_url}}{% static 'datacenterlight/img/logo_black.png' %}" style="width: 200px; height: 50px;">
<img src="{{ base_url }}{% static 'datacenterlight/img/datacenterlight.png' %}" style="max-width: 200px;">
</td>
</tr>
<tr>

View File

@ -14,7 +14,7 @@
<table style="width: 100%; border-spacing: 0; border-collapse: collapse; max-width: 560px;">
<tr>
<td>
<img src="{{ base_url }}{% static 'datacenterlight/img/logo_black.png' %}" style="width: 200px; height: 50px;">
<img src="{{ base_url }}{% static 'datacenterlight/img/datacenterlight.png' %}" style="max-width: 200px;">
</td>
</tr>
<tr>
@ -25,7 +25,7 @@
<tr>
<td style="padding-top: 25px; font-size: 16px;">
<p style="line-height: 1.75; font-family: Lato, Arial, sans-serif; font-weight: 300; margin: 0;">
{% blocktrans %}You are receiving this email because your virutal machine <strong>{{ vm_name }}</strong> has been cancelled.{% endblocktrans %}
{% blocktrans %}You are receiving this email because your virtual machine <strong>{{ vm_name }}</strong> has been cancelled.{% endblocktrans %}
</p>
<p style="line-height: 1.75; font-family: Lato, Arial, sans-serif; font-weight: 300; margin: 0;">
{% blocktrans %}You can always order a new VM by clicking the button below.{% endblocktrans %}

View File

@ -2,7 +2,7 @@
{% trans "Virtual Machine Cancellation" %}
{% blocktrans %}You are receiving this email because your virutal machine {{vm_name}} has been cancelled.{% endblocktrans %}
{% blocktrans %}You are receiving this email because your virtual machine {{vm_name}} has been cancelled.{% endblocktrans %}
{% blocktrans %}You can always order a new VM by following the link below.{% endblocktrans %}
{{ base_url }}{% url 'hosting:create_virtual_machine' %}

View File

@ -74,7 +74,7 @@
<center style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
<table cellpadding="0" cellspacing="0" width="600" class="w320" style="border-collapse: collapse !important; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;"><tr style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
<td class="pull-left mobile-header-padding-left" style="vertical-align: middle; border-collapse: collapse; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; color: #777777; text-align: left; line-height: 21px; width: 290px; padding-left: 10px;" align="left" valign="middle">
<a href="{{base_url}}" style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; color: #676767; text-decoration: none !important;"><img width="137" src="{{base_url}}{% static "hosting/img/logo_black.png" %}" alt="logo" style="max-width: 600px; outline: none; text-decoration: none; -ms-interpolation-mode: bicubic; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; border: none;"></a>
<a href="{{base_url}}" style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; color: #676767; text-decoration: none !important;"><img width="137" src="{{base_url}}{% static 'hosting/img/datacenterlight.png' %}" alt="logo" style="max-width: 600px; outline: none; text-decoration: none; -ms-interpolation-mode: bicubic; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; border: none;"></a>
</td>
<td class="pull-right mobile-header-padding-right" style="color: #4d4d4d; border-collapse: collapse; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; text-align: right; line-height: 21px; width: 290px; padding-left: 10px;" align="right">
</td>
@ -100,7 +100,7 @@
</tr>
<tr style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
<td class="free-text" style="border-collapse: collapse; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; color: #777777; text-align: center; line-height: 21px; width: 100% !important; padding: 10px 60px 0px;" align="center">
Your virtual machine {{vm.name}} subscription has been charged, <br/> you can view your invoice clicking on the button below.
Your virtual machine {{vm.name}} subscription has been charged, <br/> you can view your invoice clicking on the button below.
</td>
</tr>
<tr style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">

View File

@ -0,0 +1,50 @@
{% load i18n %}
<form action="" id="payment-form-new" method="POST">
<input type="hidden" name="token"/>
<input type="hidden" name="id_card" id="id_card" value=""/>
<div class="group">
<div class="credit-card-goup">
<div class="card-element card-number-element">
<label>{%trans "Card Number" %}</label>
<div id="card-number-element" class="field my-input"></div>
</div>
<div class="row">
<div class="col-xs-5 card-element card-expiry-element">
<label>{%trans "Expiry Date" %}</label>
<div id="card-expiry-element" class="field my-input"></div>
</div>
<div class="col-xs-3 col-xs-offset-4 card-element card-cvc-element">
<label>{%trans "CVC" %}</label>
<div id="card-cvc-element" class="field my-input"></div>
</div>
</div>
<div class="card-element brand">
<label>{%trans "Card Type" %}</label>
<i class="pf pf-credit-card" id="brand-icon"></i>
</div>
</div>
</div>
<div id="card-errors"></div>
{% if not messages and not form.non_field_errors %}
<p class="card-warning-content">
{% trans "You are not making any payment yet. After placing your order, you will be taken to the Submit Payment Page." %}
</p>
{% endif %}
<div id='payment_error'>
{% for message in messages %}
{% if 'failed_payment' in message.tags or 'make_charge_error' in message.tags or 'error' in message.tags %}
<ul class="list-unstyled">
<li><p class="card-warning-content card-warning-error">{{ message|safe }}</p></li>
</ul>
{% endif %}
{% endfor %}
</div>
<div class="text-right">
<button class="btn btn-vm-contact btn-wide" type="submit" name="payment-form">{%trans "SUBMIT" %}</button>
</div>
<div style="display:none;">
<p class="payment-errors"></p>
</div>
</form>

Some files were not shown because too many files have changed in this diff Show More