Merge branch 'release/1.3.12'

This commit is contained in:
Nathan Chapman 2022-05-25 19:06:20 -06:00
commit 9c69ae6816
4 changed files with 356 additions and 418 deletions

View File

@ -4,6 +4,15 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [1.3.12] - 2022-05-25
### Added
- Captcha to newsletter form
### Changed
- Post only views to accept only POST requests
## [1.3.11] - 2022-05-21 ## [1.3.11] - 2022-05-21
### Changed ### Changed
- File logging handler to not propagate - File logging handler to not propagate

View File

@ -2,7 +2,7 @@ from decimal import Decimal
from django.test import TestCase from django.test import TestCase
from measurement.measures import Weight from measurement.measures import Weight
from accounts.models import User
from core.models import ( from core.models import (
Product, Product,
ProductPhoto, ProductPhoto,
@ -16,6 +16,8 @@ from core.models import (
class ProductModelTest(TestCase): class ProductModelTest(TestCase):
fixtures = ['accounts.json']
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
Product.objects.create( Product.objects.create(

View File

@ -1,12 +1,5 @@
<form method="POST" action="https://ptcoffee.activehosted.com/proc.php" id="_form_3_" class="_form _form_3 _inline-form _dark" novalidate> <form action="https://ptcoffee.activehosted.com/proc.php" class="_form _form_3 _inline-form _dark" id="_form_3_" method="post" name="_form_3_" novalidate="">
<input type="hidden" name="u" value="3" /> <input name="u" type="hidden" value="3"> <input name="f" type="hidden" value="3"> <input name="s" type="hidden"> <input name="c" type="hidden" value="0"> <input name="m" type="hidden" value="0"> <input name="act" type="hidden" value="sub"> <input name="v" type="hidden" value="2"> <input name="or" type="hidden" value="976f58eb66df9127e2ab7e5003a0ca69">
<input type="hidden" name="f" value="3" />
<input type="hidden" name="s" />
<input type="hidden" name="c" value="0" />
<input type="hidden" name="m" value="0" />
<input type="hidden" name="act" value="sub" />
<input type="hidden" name="v" value="2" />
<input type="hidden" name="or" value="4ef7da616ee5aadd87ec694f0969cb87" />
<div class="_form-content"> <div class="_form-content">
<div class="_form_element _x15986148 _full_width _clear"> <div class="_form_element _x15986148 _full_width _clear">
<div class="_form-title"> <div class="_form-title">
@ -19,39 +12,34 @@
</div> </div>
</div> </div>
<div class="_form_element _x55366865 _full_width"> <div class="_form_element _x55366865 _full_width">
<label for="fullname" class="_form-label"> <label class="_form-label" for="fullname">Full Name</label>
Full Name
</label>
<div class="_field-wrapper"> <div class="_field-wrapper">
<input type="text" id="fullname" name="fullname" placeholder="Type your name" /> <input id="fullname" name="fullname" placeholder="Type your name" type="text">
</div><!-- This STARTS the Custom Objects section -->
</div> </div>
<!-- This STARTS the Custom Objects section -->
</div><br>
<div class="_form_element _x53687886 _full_width"> <div class="_form_element _x53687886 _full_width">
<label for="email" class="_form-label">Email*</label> <label class="_form-label" for="email">Email*</label>
<div class="_field-wrapper"> <div class="_field-wrapper">
<input type="text" id="email" name="email" placeholder="Type your email" required/> <input id="email" name="email" placeholder="Type your email" type="text">
</div><!-- This STARTS the Custom Objects section -->
</div> </div>
<!-- This STARTS the Custom Objects section --> <br>
</div><br> <div class="_form_element _x71226546 _full_width">
<label class="_form-label" for="ls">Please verify your request*</label>
<div class="g-recaptcha" data-sitekey="6LcwIw8TAAAAACP1ysM08EhCgzd6q5JAOUR1a0Go"></div>
</div>
<br>
<div class="_button-wrapper _full_width"> <div class="_button-wrapper _full_width">
<p> <button class="_submit" id="_form_3_submit" type="submit">Subscribe</button>
<button id="_form_3_submit" class="_submit" type="submit">
Subscribe
</button>
</p>
</div> </div>
<div class="_clear-element"> <div class="_clear-element"></div>
</div>
</div>
<div class="_form-thank-you" style="display:none;">
</div> </div>
<div class="_form-thank-you" style="display:none;"></div>
</form> </form>
<script type="text/javascript"> <script type="text/javascript">
window.cfields = []; window.cfields = [];
window._show_thank_you = function(id, message, trackcmp_url, email) { window._show_thank_you = function(id, message, trackcmp_url, email) {
var form = document.getElementById('_form_' + id + '_'), var form = document.getElementById('_form_' + id + '_'), thank_you = form.querySelector('._form-thank-you');
thank_you = form.querySelector('._form-thank-you');
form.querySelector('._form-content').style.display = 'none'; form.querySelector('._form-content').style.display = 'none';
thank_you.innerHTML = message; thank_you.innerHTML = message;
thank_you.style.display = 'block'; thank_you.style.display = 'block';
@ -67,10 +55,7 @@
if (typeof window._form_callback !== 'undefined') window._form_callback(id); if (typeof window._form_callback !== 'undefined') window._form_callback(id);
}; };
window._show_error = function(id, message, html) { window._show_error = function(id, message, html) {
var form = document.getElementById('_form_' + id + '_'), var form = document.getElementById('_form_' + id + '_'), err = document.createElement('div'), button = form.querySelector('button'), old_error = form.querySelector('._form_error');
err = document.createElement('div'),
button = form.querySelector('button'),
old_error = form.querySelector('._form_error');
if (old_error) old_error.parentNode.removeChild(old_error); if (old_error) old_error.parentNode.removeChild(old_error);
err.innerHTML = message; err.innerHTML = message;
err.className = '_error-inner _form_error _no_arrow'; err.className = '_error-inner _form_error _no_arrow';
@ -87,9 +72,7 @@
} }
}; };
window._load_script = function(url, callback) { window._load_script = function(url, callback) {
var head = document.querySelector('head'), var head = document.querySelector('head'), script = document.createElement('script'), r = false;
script = document.createElement('script'),
r = false;
script.type = 'text/javascript'; script.type = 'text/javascript';
script.charset = 'utf-8'; script.charset = 'utf-8';
script.src = url; script.src = url;
@ -129,9 +112,7 @@
} }
var _removed = false; var _removed = false;
var form_to_submit = document.getElementById('_form_3_'); var form_to_submit = document.getElementById('_form_3_');
var allInputs = form_to_submit.querySelectorAll('input, select, textarea'), var allInputs = form_to_submit.querySelectorAll('input, select, textarea'), tooltips = [], submitted = false;
tooltips = [],
submitted = false;
var getUrlParam = function(name) { var getUrlParam = function(name) {
var params = new URLSearchParams(window.location.search); var params = new URLSearchParams(window.location.search);
@ -178,10 +159,7 @@
} }
}; };
var create_tooltip = function(elem, text) { var create_tooltip = function(elem, text) {
var tooltip = document.createElement('div'), var tooltip = document.createElement('div'), arrow = document.createElement('div'), inner = document.createElement('div'), new_tooltip = {};
arrow = document.createElement('div'),
inner = document.createElement('div'),
new_tooltip = {};
if (elem.type != 'radio' && elem.type != 'checkbox') { if (elem.type != 'radio' && elem.type != 'checkbox') {
tooltip.className = '_error'; tooltip.className = '_error';
arrow.className = '_error-arrow'; arrow.className = '_error-arrow';
@ -203,8 +181,7 @@
}; };
var resize_tooltip = function(tooltip) { var resize_tooltip = function(tooltip) {
var rect = tooltip.elem.getBoundingClientRect(); var rect = tooltip.elem.getBoundingClientRect();
var doc = document.documentElement, var doc = document.documentElement, scrollPosition = rect.top - ((window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0));
scrollPosition = rect.top - ((window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0));
if (scrollPosition < 40) { if (scrollPosition < 40) {
tooltip.tip.className = tooltip.tip.className.replace(/ ?(_above|_below) ?/g, '') + ' _below'; tooltip.tip.className = tooltip.tip.className.replace(/ ?(_above|_below) ?/g, '') + ' _below';
} else { } else {
@ -218,9 +195,7 @@
} }
}; };
var validate_field = function(elem, remove) { var validate_field = function(elem, remove) {
var tooltip = null, var tooltip = null, value = elem.value, no_error = true;
value = elem.value,
no_error = true;
remove ? remove_tooltip(elem) : false; remove ? remove_tooltip(elem) : false;
if (elem.type != 'checkbox') elem.className = elem.className.replace(/ ?_has_error ?/g, ''); if (elem.type != 'checkbox') elem.className = elem.className.replace(/ ?_has_error ?/g, '');
if (elem.getAttribute('required') !== null) { if (elem.getAttribute('required') !== null) {
@ -228,7 +203,8 @@
var elems = form_to_submit.elements[elem.name]; var elems = form_to_submit.elements[elem.name];
if (!(elems instanceof NodeList || elems instanceof HTMLCollection) || elems.length <= 1) { if (!(elems instanceof NodeList || elems instanceof HTMLCollection) || elems.length <= 1) {
no_error = elem.checked; no_error = elem.checked;
} else { }
else {
no_error = false; no_error = false;
for (var i = 0; i < elems.length; i++) { for (var i = 0; i < elems.length; i++) {
if (elems[i].checked) no_error = true; if (elems[i].checked) no_error = true;
@ -238,9 +214,7 @@
tooltip = create_tooltip(elem, "Please select an option."); tooltip = create_tooltip(elem, "Please select an option.");
} }
} else if (elem.type =='checkbox') { } else if (elem.type =='checkbox') {
var elems = form_to_submit.elements[elem.name], var elems = form_to_submit.elements[elem.name], found = false, err = [];
found = false,
err = [];
no_error = true; no_error = true;
for (var i = 0; i < elems.length; i++) { for (var i = 0; i < elems.length; i++) {
if (elems[i].getAttribute('required') === null) continue; if (elems[i].getAttribute('required') === null) continue;
@ -311,8 +285,7 @@
return false return false
}; };
var validate_form = function(e) { var validate_form = function(e) {
var err = form_to_submit.querySelector('._form_error'), var err = form_to_submit.querySelector('._form_error'), no_error = true;
no_error = true;
if (!submitted) { if (!submitted) {
submitted = true; submitted = true;
for (var i = 0, len = allInputs.length; i < len; i++) { for (var i = 0, len = allInputs.length; i < len; i++) {
@ -365,69 +338,22 @@
}; };
addEvent(window, 'resize', resize_tooltips); addEvent(window, 'resize', resize_tooltips);
addEvent(window, 'scroll', resize_tooltips); addEvent(window, 'scroll', resize_tooltips);
var _form_serialize = function(form) { window['recaptcha_callback'] = function() {
if (!form || form.nodeName !== "FORM") { // Get all recaptchas in the DOM (there may be more than one form on the page).
return var recaptchas = document.getElementsByClassName("g-recaptcha");
} for (var i in recaptchas) {
var i, j, q = []; // Set the recaptcha element ID, so the recaptcha can be applied to each element.
for (i = 0; i < form.elements.length; i++) { var recaptcha_id = "recaptcha_" + i;
if (form.elements[i].name === "") { recaptchas[i].id = recaptcha_id;
continue var el = document.getElementById(recaptcha_id);
} if (el != null) {
switch (form.elements[i].nodeName) { var sitekey = el.getAttribute("data-sitekey");
case "INPUT": var stoken = el.getAttribute("data-stoken");
switch (form.elements[i].type) { grecaptcha.render(recaptcha_id, {"sitekey":sitekey,"stoken":stoken});
case "text":
case "number":
case "date":
case "time":
case "hidden":
case "password":
case "button":
case "reset":
case "submit":
q.push(form.elements[i].name + "=" + encodeURIComponent(form.elements[i].value));
break;
case "checkbox":
case "radio":
if (form.elements[i].checked) {
q.push(form.elements[i].name + "=" + encodeURIComponent(form.elements[i].value))
}
break;
case "file":
break
}
break;
case "TEXTAREA":
q.push(form.elements[i].name + "=" + encodeURIComponent(form.elements[i].value));
break;
case "SELECT":
switch (form.elements[i].type) {
case "select-one":
q.push(form.elements[i].name + "=" + encodeURIComponent(form.elements[i].value));
break;
case "select-multiple":
for (j = 0; j < form.elements[i].options.length; j++) {
if (form.elements[i].options[j].selected) {
q.push(form.elements[i].name + "=" + encodeURIComponent(form.elements[i].options[j].value))
} }
} }
break }; _load_script("//www.google.com/recaptcha/api.js?onload=recaptcha_callback&render=explicit");
} var _form_serialize = function(form){if(!form||form.nodeName!=="FORM"){return }var i,j,q=[];for(i=0;i<form.elements.length;i++){if(form.elements[i].name===""){continue}switch(form.elements[i].nodeName){case"INPUT":switch(form.elements[i].type){case"text":case"number":case"date":case"time":case"hidden":case"password":case"button":case"reset":case"submit":q.push(form.elements[i].name+"="+encodeURIComponent(form.elements[i].value));break;case"checkbox":case"radio":if(form.elements[i].checked){q.push(form.elements[i].name+"="+encodeURIComponent(form.elements[i].value))}break;case"file":break}break;case"TEXTAREA":q.push(form.elements[i].name+"="+encodeURIComponent(form.elements[i].value));break;case"SELECT":switch(form.elements[i].type){case"select-one":q.push(form.elements[i].name+"="+encodeURIComponent(form.elements[i].value));break;case"select-multiple":for(j=0;j<form.elements[i].options.length;j++){if(form.elements[i].options[j].selected){q.push(form.elements[i].name+"="+encodeURIComponent(form.elements[i].options[j].value))}}break}break;case"BUTTON":switch(form.elements[i].type){case"reset":case"submit":case"button":q.push(form.elements[i].name+"="+encodeURIComponent(form.elements[i].value));break}break}}return q.join("&")};
break;
case "BUTTON":
switch (form.elements[i].type) {
case "reset":
case "submit":
case "button":
q.push(form.elements[i].name + "=" + encodeURIComponent(form.elements[i].value));
break
}
break
}
}
return q.join("&")
};
var form_submit = function(e) { var form_submit = function(e) {
e.preventDefault(); e.preventDefault();
if (validate_form()) { if (validate_form()) {
@ -442,5 +368,4 @@
}; };
addEvent(form_to_submit, 'submit', form_submit); addEvent(form_to_submit, 'submit', form_submit);
})(); })();
</script> </script>

View File

@ -65,6 +65,7 @@ class CartView(TemplateView):
class CartAddProductView(SingleObjectMixin, FormView): class CartAddProductView(SingleObjectMixin, FormView):
model = Product model = Product
form_class = AddToCartForm form_class = AddToCartForm
http_method_names = ['post']
def get_success_url(self): def get_success_url(self):
return reverse('storefront:cart-detail') return reverse('storefront:cart-detail')
@ -90,6 +91,7 @@ class CartAddProductView(SingleObjectMixin, FormView):
class CartUpdateProductView(SingleObjectMixin, FormView): class CartUpdateProductView(SingleObjectMixin, FormView):
model = Product model = Product
form_class = UpdateCartItemForm form_class = UpdateCartItemForm
http_method_names = ['post']
def get_success_url(self): def get_success_url(self):
return reverse('storefront:cart-detail') return reverse('storefront:cart-detail')
@ -121,9 +123,9 @@ def cart_remove_product_view(request, pk, grind):
class CouponApplyView(FormView): class CouponApplyView(FormView):
template_name = 'contact.html'
form_class = CouponApplyForm form_class = CouponApplyForm
success_url = reverse_lazy('storefront:cart-detail') success_url = reverse_lazy('storefront:cart-detail')
http_method_names = ['post']
def form_valid(self, form): def form_valid(self, form):
today = timezone.localtime(timezone.now()).date() today = timezone.localtime(timezone.now()).date()