def send(self, http_form, referrer): ''' Sends form to user's email. Assumes sender's email has been verified. ''' data, keys = http_form_to_dict(http_form) subject = data.get('_subject', 'New submission from %s' % referrer_to_path(referrer)) reply_to = data.get('_replyto', data.get('email', data.get('Email', None))) cc = data.get('_cc', None) next = data.get('_next', url_for('thanks', next=referrer)) spam = data.get('_gotcha', None) # prevent submitting empty form if not any(data.values()): return { 'code': Form.STATUS_EMAIL_EMPTY } # return a fake success for spam if spam: return { 'code': Form.STATUS_EMAIL_SENT, 'next': next } # increment the forms counter self.counter = Form.counter + 1 DB.session.add(self) DB.session.commit() # increase the monthly counter request_date = datetime.datetime.now() self.increase_monthly_counter(basedate=request_date) # check if the forms are over the counter and the user is not upgraded overlimit = False if self.get_monthly_counter(basedate=request_date) > settings.MONTHLY_SUBMISSIONS_LIMIT: if not self.owner or not self.owner.upgraded: overlimit = True now = datetime.datetime.utcnow().strftime('%I:%M %p UTC - %d %B %Y') if not overlimit: text = render_template('email/form.txt', data=data, host=self.host, keys=keys, now=now) html = render_template('email/form.html', data=data, host=self.host, keys=keys, now=now) else: text = render_template('email/overlimit-notification.txt', host=self.host) html = render_template('email/overlimit-notification.html', host=self.host) result = send_email(to=self.email, subject=subject, text=text, html=html, sender=settings.DEFAULT_SENDER, reply_to=reply_to, cc=cc) if not result[0]: return{ 'code': Form.STATUS_EMAIL_FAILED } return { 'code': Form.STATUS_EMAIL_SENT, 'next': next }
def render_content(type): data, keys = None, None if with_data: data, keys = http_form_to_dict(with_data) return render_template('email/confirm.%s' % type, email=self.email, host=self.host, nonce_link=link, data=data, keys=keys)
def render_content(ext): data, keys = None, None if with_data: if type(with_data) in (ImmutableMultiDict, ImmutableOrderedMultiDict): data, keys = http_form_to_dict(with_data) else: data, keys = with_data, with_data.keys() return render_template('email/confirm.%s' % ext, email=self.email, host=self.host, nonce_link=link, data=data, keys=keys)
def render_content(ext): data, keys = None, None if store_data: if type(store_data) in (ImmutableMultiDict, ImmutableOrderedMultiDict): data, _ = http_form_to_dict(store_data) store_first_submission(nonce, data) else: store_first_submission(nonce, store_data) return render_template('email/confirm.%s' % ext, email=self.email, host=self.host, nonce_link=link, keys=keys)
def render_content(ext): data, keys = None, None if store_data: if type(store_data) in ( ImmutableMultiDict, ImmutableOrderedMultiDict): data, _ = http_form_to_dict(store_data) store_first_submission(nonce, data) else: store_first_submission(nonce, store_data) return render_template('email/confirm.%s' % ext, email=self.email, host=self.host, nonce_link=link, keys=keys)
def send(email_or_string): ''' Main endpoint, finds or creates the form row from the database, checks validity and state of the form and sends either form data or verification to email. ''' g.log = g.log.bind(target=email_or_string) if request.method == 'GET': if request_wants_json(): return jsonerror(405, {'error': "Please submit POST request."}) else: return render_template('info.html', title='Form should POST', text='Make sure your form has the <span ' 'class="code"><strong>method="POST"' '</strong></span> attribute'), 405 if request.form: received_data, sorted_keys = http_form_to_dict(request.form) else: received_data = request.get_json() or {} sorted_keys = received_data.keys() try: # Get stored hostname from redis (from captcha) host, referrer = get_temp_hostname(received_data['_host_nonce']) except KeyError: host, referrer = referrer_to_path(request.referrer), request.referrer if not host or host == 'www.google.com': if request_wants_json(): return jsonerror(400, {'error': "Invalid \"Referrer\" header"}) else: return render_template( 'error.html', title='Unable to submit form', text= '<p>Make sure you open this page through a web server, Formspree will not work in pages browsed as HTML files. Also make sure that you\'re posting to <b>{host}{path}</b>.</p><p>For geeks: could not find the "Referrer" header.</p>' .format(host=settings.SERVICE_URL, path=request.path)), 400 g.log = g.log.bind(host=host, wants='json' if request_wants_json() else 'html') g.log.info('Received submission.') if not IS_VALID_EMAIL(email_or_string): # in this case it can be a hashid identifying a # form generated from the dashboard hashid = email_or_string form = Form.get_with_hashid(hashid) if form: # Check if it has been assigned about using AJAX or not assign_ajax(form, request_wants_json()) if form.disabled: # owner has disabled the form, so it should not receive any submissions if request_wants_json(): return jsonerror(403, {'error': 'Form not active'}) else: return render_template( 'error.html', title='Form not active', text= 'The owner of this form has disabled this form and it is no longer accepting submissions. Your submissions was not accepted' ), 403 email = form.email if not form.host: # add the host to the form form.host = host DB.session.add(form) DB.session.commit() # it is an error when # form is sitewide, but submission came from a host rooted somewhere else, or # form is not sitewide, and submission came from a different host elif (not form.sitewide and form.host != host) or ( form.sitewide and ( not host.startswith(form.host) and \ not remove_www(host).startswith(form.host) ) ): g.log.info( 'Submission rejected. From a different host than confirmed.' ) if request_wants_json(): return jsonerror( 403, { 'error': "Submission from different host than confirmed", 'submitted': host, 'confirmed': form.host }) else: return render_template( 'error.html', title='Check form address', text='This submission came from "%s" but the form was\ confirmed for address "%s"' % (host, form.host)), 403 else: # no form row found. it is an error. g.log.info('Submission rejected. No form found for this target.') if request_wants_json(): return jsonerror(400, {'error': "Invalid email address"}) else: return render_template('error.html', title='Check email address', text='Email address %s is not formatted correctly' \ % str(email_or_string)), 400 else: # in this case, it is a normal email email = email_or_string.lower() # get the form for this request form = Form.query.filter_by(hash=HASH(email, host)).first() \ or Form(email, host) # or create it if it doesn't exists # Check if it has been assigned about using AJAX or not assign_ajax(form, request_wants_json()) if form.disabled: g.log.info('submission rejected. Form is disabled.') if request_wants_json(): return jsonerror(403, {'error': 'Form not active'}) else: return render_template( 'error.html', title='Form not active', text= 'The owner of this form has disabled this form and it is no longer accepting submissions. Your submissions was not accepted' ), 403 # If form exists and is confirmed, send email # otherwise send a confirmation email if form.confirmed: captcha_verified = verify_captcha(received_data, request) needs_captcha = not (request_wants_json() or captcha_verified or settings.TESTING) # if form is upgraded check if captcha is disabled if form.upgraded: needs_captcha = needs_captcha and not form.captcha_disabled if needs_captcha: data_copy = received_data.copy() # Temporarily store hostname in redis while doing captcha nonce = temp_store_hostname(form.host, request.referrer) data_copy['_host_nonce'] = nonce action = urljoin(settings.API_ROOT, email_or_string) return render_template('forms/captcha.html', data=data_copy, sorted_keys=sorted_keys, action=action) status = form.send(received_data, sorted_keys, referrer) else: status = form.send_confirmation() # Respond to the request accordingly to the status code if status['code'] == Form.STATUS_EMAIL_SENT: if request_wants_json(): return jsonify({'success': "email sent", 'next': status['next']}) else: return redirect(status['next'], code=302) elif status['code'] == Form.STATUS_EMAIL_EMPTY: if request_wants_json(): return jsonerror(400, {'error': "Can't send an empty form"}) else: return render_template( 'error.html', title='Can\'t send an empty form', text= u'<p>Make sure you have placed the <a href="http://www.w3schools.com/tags/att_input_name.asp" target="_blank"><code>"name"</code> attribute</a> in all your form elements. Also, to prevent empty form submissions, take a look at the <a href="http://www.w3schools.com/tags/att_input_required.asp" target="_blank"><code>"required"</code> property</a>.</p><p>This error also happens when you have an <code>"enctype"</code> attribute set in your <code><form></code>, so make sure you don\'t.</p><p><a href="{}">Return to form</a></p>' .format(referrer)), 400 elif status['code'] == Form.STATUS_CONFIRMATION_SENT or \ status['code'] == Form.STATUS_CONFIRMATION_DUPLICATED: if request_wants_json(): return jsonify({'success': "confirmation email sent"}) else: return render_template( 'forms/confirmation_sent.html', email=email, host=host, resend=status['code'] == Form.STATUS_CONFIRMATION_DUPLICATED) elif status['code'] == Form.STATUS_OVERLIMIT: if request_wants_json(): return jsonify({'error': "form over quota"}) else: return render_template( 'error.html', title='Form over quota', text= 'It looks like this form is getting a lot of submissions and ran out of its quota. Try contacting this website through other means or try submitting again later.' ), 402 elif status['code'] == Form.STATUS_REPLYTO_ERROR: if request_wants_json(): return jsonerror(500, { 'error': "_replyto or email field has not been sent correctly" }) else: return render_template( 'error.html', title='Invalid email address', text= u'You entered <span class="code">{address}</span>. That is an invalid email address. Please correct the form and try to submit again <a href="{back}">here</a>.<p style="font-size: small">This could also be a problem with the form. For example, there could be two fields with <span class="code">_replyto</span> or <span class="code">email</span> name attribute. If you suspect the form is broken, please contact the form owner and ask them to investigate</p>' ''.format(address=status['address'], back=status['referrer'])), 400 # error fallback -- shouldn't happen if request_wants_json(): return jsonerror(500, {'error': "Unable to send email"}) else: return render_template( 'error.html', title='Unable to send email', text= u'Unable to send email. If you can, please send the link to your form and the error information to <b>{email}</b>. And send them the following: <p><pre><code>{message}</code></pre></p>' .format(message=json.dumps(status), email=settings.CONTACT_EMAIL)), 500
def send(self, http_form, referrer): ''' Sends form to user's email. Assumes sender's email has been verified. ''' data, keys = http_form_to_dict(http_form) subject = data.get('_subject', 'New submission from %s' % referrer_to_path(referrer)) reply_to = data.get('_replyto', data.get('email', data.get('Email', None))) cc = data.get('_cc', None) next = next_url(referrer, data.get('_next')) spam = data.get('_gotcha', None) # prevent submitting empty form if not any(data.values()): return { 'code': Form.STATUS_EMAIL_EMPTY } # return a fake success for spam if spam: return { 'code': Form.STATUS_EMAIL_SENT, 'next': next } # increase the monthly counter request_date = datetime.datetime.now() self.increase_monthly_counter(basedate=request_date) # increment the forms counter self.counter = Form.counter + 1 DB.session.add(self) # archive the form contents sub = Submission(self.id) sub.data = data DB.session.add(sub) # commit changes DB.session.commit() # delete all archived submissions over the limit records_to_keep = settings.ARCHIVED_SUBMISSIONS_LIMIT newest = self.submissions.with_entities(Submission.id).limit(records_to_keep) DB.engine.execute( delete('submissions'). \ where(Submission.form_id == self.id). \ where(~Submission.id.in_(newest)) ) # check if the forms are over the counter and the user is not upgraded overlimit = False monthly_counter = self.get_monthly_counter() if monthly_counter > settings.MONTHLY_SUBMISSIONS_LIMIT: overlimit = True if self.controllers: for c in self.controllers: if c.upgraded: overlimit = False break now = datetime.datetime.utcnow().strftime('%I:%M %p UTC - %d %B %Y') if not overlimit: text = render_template('email/form.txt', data=data, host=self.host, keys=keys, now=now) html = render_template('email/form.html', data=data, host=self.host, keys=keys, now=now) else: if monthly_counter - settings.MONTHLY_SUBMISSIONS_LIMIT > 25: # only send this overlimit notification for the first 25 overlimit emails # after that, return an error so the user can know the website owner is not # going to read his message. return { 'code': Form.STATUS_EMAIL_FAILED } text = render_template('email/overlimit-notification.txt', host=self.host) html = render_template('email/overlimit-notification.html', host=self.host) result = send_email(to=self.email, subject=subject, text=text, html=html, sender=settings.DEFAULT_SENDER, reply_to=reply_to, cc=cc) if not result[0]: return{ 'code': Form.STATUS_EMAIL_FAILED } return { 'code': Form.STATUS_EMAIL_SENT, 'next': next }
def send(self, submitted_data, referrer): ''' Sends form to user's email. Assumes sender's email has been verified. ''' if type(submitted_data) in (ImmutableMultiDict, ImmutableOrderedMultiDict): data, keys = http_form_to_dict(submitted_data) else: data, keys = submitted_data, submitted_data.keys() subject = data.get('_subject', 'New submission from %s' % referrer_to_path(referrer)) reply_to = data.get('_replyto', data.get('email', data.get('Email', ''))).strip() cc = data.get('_cc', None) next = next_url(referrer, data.get('_next')) spam = data.get('_gotcha', None) format = data.get('_format', None) # turn cc emails into array if cc: cc = [email.strip() for email in cc.split(',')] # prevent submitting empty form if not any(data.values()): return { 'code': Form.STATUS_EMAIL_EMPTY } # return a fake success for spam if spam: g.log.info('Submission rejected.', gotcha=spam) return { 'code': Form.STATUS_EMAIL_SENT, 'next': next } # validate reply_to, if it is not a valid email address, reject if reply_to and not IS_VALID_EMAIL(reply_to): g.log.info('Submission rejected. Reply-To is invalid.', reply_to=reply_to) return { 'code': Form.STATUS_REPLYTO_ERROR, 'error-message': '"%s" is not a valid email address.' % reply_to } # increase the monthly counter request_date = datetime.datetime.now() self.increase_monthly_counter(basedate=request_date) # increment the forms counter self.counter = Form.counter + 1 DB.session.add(self) # archive the form contents sub = Submission(self.id) sub.data = data DB.session.add(sub) # commit changes DB.session.commit() # delete all archived submissions over the limit records_to_keep = settings.ARCHIVED_SUBMISSIONS_LIMIT newest = self.submissions.with_entities(Submission.id).limit(records_to_keep) DB.engine.execute( delete('submissions'). \ where(Submission.form_id == self.id). \ where(~Submission.id.in_(newest)) ) # check if the forms are over the counter and the user is not upgraded overlimit = False monthly_counter = self.get_monthly_counter() if monthly_counter > settings.MONTHLY_SUBMISSIONS_LIMIT: overlimit = True if self.controllers: for c in self.controllers: if c.upgraded: overlimit = False break now = datetime.datetime.utcnow().strftime('%I:%M %p UTC - %d %B %Y') if not overlimit: text = render_template('email/form.txt', data=data, host=self.host, keys=keys, now=now) # check if the user wants a new or old version of the email if format == 'plain': html = render_template('email/plain_form.html', data=data, host=self.host, keys=keys, now=now) else: html = render_template('email/form.html', data=data, host=self.host, keys=keys, now=now) else: if monthly_counter - settings.MONTHLY_SUBMISSIONS_LIMIT > 25: g.log.info('Submission rejected. Form over quota.', monthly_counter=monthly_counter) # only send this overlimit notification for the first 25 overlimit emails # after that, return an error so the user can know the website owner is not # going to read his message. return { 'code': Form.STATUS_OVERLIMIT } text = render_template('email/overlimit-notification.txt', host=self.host) html = render_template('email/overlimit-notification.html', host=self.host) result = send_email( to=self.email, subject=subject, text=text, html=html, sender=settings.DEFAULT_SENDER, reply_to=reply_to, cc=cc ) if not result[0]: g.log.warning('Failed to send email.', reason=result[1], code=result[2]) if result[1].startswith('Invalid replyto email address'): return { 'code': Form.STATUS_REPLYTO_ERROR} return{ 'code': Form.STATUS_EMAIL_FAILED, 'mailer-code': result[2], 'error-message': result[1] } return { 'code': Form.STATUS_EMAIL_SENT, 'next': next }
def send(self, submitted_data, referrer): ''' Sends form to user's email. Assumes sender's email has been verified. ''' if type(submitted_data) in (ImmutableMultiDict, ImmutableOrderedMultiDict): data, keys = http_form_to_dict(submitted_data) else: data, keys = submitted_data, submitted_data.keys() subject = data.get('_subject', 'New submission from %s' % referrer_to_path(referrer)) reply_to = data.get('_replyto', data.get('email', data.get('Email', ''))).strip() cc = data.get('_cc', None) next = next_url(referrer, data.get('_next')) spam = data.get('_gotcha', None) format = data.get('_format', None) # turn cc emails into array if cc: cc = [email.strip() for email in cc.split(',')] # prevent submitting empty form if not any(data.values()): return { 'code': Form.STATUS_EMAIL_EMPTY } # return a fake success for spam if spam: return { 'code': Form.STATUS_EMAIL_SENT, 'next': next } # validate reply_to, if it is not a valid email address, reject if reply_to and not IS_VALID_EMAIL(reply_to): return { 'code': Form.STATUS_REPLYTO_ERROR, 'error-message': '"%s" is not a valid email address.' % reply_to } # increase the monthly counter request_date = datetime.datetime.now() self.increase_monthly_counter(basedate=request_date) # increment the forms counter self.counter = Form.counter + 1 DB.session.add(self) # archive the form contents sub = Submission(self.id) sub.data = data DB.session.add(sub) # commit changes DB.session.commit() # delete all archived submissions over the limit records_to_keep = settings.ARCHIVED_SUBMISSIONS_LIMIT newest = self.submissions.with_entities(Submission.id).limit(records_to_keep) DB.engine.execute( delete('submissions'). \ where(Submission.form_id == self.id). \ where(~Submission.id.in_(newest)) ) # check if the forms are over the counter and the user is not upgraded overlimit = False monthly_counter = self.get_monthly_counter() if monthly_counter > settings.MONTHLY_SUBMISSIONS_LIMIT: overlimit = True if self.controllers: for c in self.controllers: if c.upgraded: overlimit = False break now = datetime.datetime.utcnow().strftime('%I:%M %p UTC - %d %B %Y') if not overlimit: text = render_template('email/form.txt', data=data, host=self.host, keys=keys, now=now) # check if the user wants a new or old version of the email if format == 'plain': html = render_template('email/plain_form.html', data=data, host=self.host, keys=keys, now=now) else: html = render_template('email/form.html', data=data, host=self.host, keys=keys, now=now) else: if monthly_counter - settings.MONTHLY_SUBMISSIONS_LIMIT > 25: # only send this overlimit notification for the first 25 overlimit emails # after that, return an error so the user can know the website owner is not # going to read his message. return { 'code': Form.STATUS_OVERLIMIT } text = render_template('email/overlimit-notification.txt', host=self.host) html = render_template('email/overlimit-notification.html', host=self.host) result = send_email(to=self.email, subject=subject, text=text, html=html, sender=settings.DEFAULT_SENDER, reply_to=reply_to, cc=cc) if not result[0]: if result[1].startswith('Invalid replyto email address'): return { 'code': Form.STATUS_REPLYTO_ERROR} return{ 'code': Form.STATUS_EMAIL_FAILED, 'mailer-code': result[2], 'error-message': result[1] } return { 'code': Form.STATUS_EMAIL_SENT, 'next': next }
def send(email_or_string): ''' Main endpoint, finds or creates the form row from the database, checks validity and state of the form and sends either form data or verification to email. ''' g.log = g.log.bind(target=email_or_string) if request.method == 'GET': if request_wants_json(): return jsonerror(405, {'error': "Please submit POST request."}) else: return render_template('info.html', title='Form should POST', text='Make sure your form has the <span ' 'class="code"><strong>method="POST"' '</strong></span> attribute'), 405 if request.form: received_data, sorted_keys = http_form_to_dict(request.form) else: received_data = request.get_json() or {} sorted_keys = received_data.keys() try: # Get stored hostname from redis (from captcha) host, referrer = get_temp_hostname(received_data['_host_nonce']) except KeyError: host, referrer = referrer_to_path(request.referrer), request.referrer except ValueError as err: g.log.error('Invalid hostname stored on Redis.', err=err) return render_template( 'error.html', title='Unable to submit form', text='<p>We had a problem identifying to whom we should have submitted this form. Please try submitting again. If it fails once more, please let us know at {email}</p>'.format( email=settings.CONTACT_EMAIL, ) ), 500 if not host or host == 'www.google.com': if request_wants_json(): return jsonerror(400, {'error': "Invalid \"Referrer\" header"}) else: return render_template( 'error.html', title='Unable to submit form', text='<p>Make sure you open this page through a web server, Formspree will not work in pages browsed as HTML files. Also make sure that you\'re posting to <b>{host}{path}</b>.</p><p>For geeks: could not find the "Referrer" header.</p>'.format( host=settings.SERVICE_URL, path=request.path ) ), 400 g.log = g.log.bind(host=host, wants='json' if request_wants_json() else 'html') g.log.info('Submitted.') if not IS_VALID_EMAIL(email_or_string): # in this case it can be a hashid identifying a # form generated from the dashboard hashid = email_or_string form = Form.get_with_hashid(hashid) if form: # Check if it has been assigned about using AJAX or not assign_ajax(form, request_wants_json()) if form.disabled: # owner has disabled the form, so it should not receive any submissions if request_wants_json(): return jsonerror(403, {'error': 'Form not active'}) else: return render_template('error.html', title='Form not active', text='The owner of this form has disabled this form and it is no longer accepting submissions. Your submissions was not accepted'), 403 email = form.email if not form.host: # add the host to the form form.host = host DB.session.add(form) DB.session.commit() # it is an error when # form is not sitewide, and submission came from a different host # form is sitewide, but submission came from a host rooted somewhere else, or elif (not form.sitewide and # ending slashes can be safely ignored here: form.host.rstrip('/') != host.rstrip('/')) or \ (form.sitewide and \ # removing www from both sides makes this a neutral operation: not remove_www(host).startswith(remove_www(form.host)) ): g.log.info('Submission rejected. From a different host than confirmed.') if request_wants_json(): return jsonerror(403, { 'error': "Submission from different host than confirmed", 'submitted': host, 'confirmed': form.host }) else: return render_template('error.html', title='Check form address', text='This submission came from "%s" but the form was\ confirmed for address "%s"' % (host, form.host)), 403 else: # no form row found. it is an error. g.log.info('Submission rejected. No form found for this target.') if request_wants_json(): return jsonerror(400, {'error': "Invalid email address"}) else: return render_template('error.html', title='Check email address', text='Email address %s is not formatted correctly' \ % str(email_or_string)), 400 else: # in this case, it is a normal email email = email_or_string.lower() # get the form for this request form = Form.query.filter_by(hash=HASH(email, host)).first() \ or Form(email, host) # or create it if it doesn't exists # Check if it has been assigned about using AJAX or not assign_ajax(form, request_wants_json()) if form.disabled: g.log.info('submission rejected. Form is disabled.') if request_wants_json(): return jsonerror(403, {'error': 'Form not active'}) else: return render_template('error.html', title='Form not active', text='The owner of this form has disabled this form and it is no longer accepting submissions. Your submissions was not accepted'), 403 # If form exists and is confirmed, send email # otherwise send a confirmation email if form.confirmed: captcha_verified = verify_captcha(received_data, request) needs_captcha = not (request_wants_json() or captcha_verified or settings.TESTING) # if form is upgraded check if captcha is disabled if form.upgraded: needs_captcha = needs_captcha and not form.captcha_disabled if needs_captcha: data_copy = received_data.copy() # Temporarily store hostname in redis while doing captcha nonce = temp_store_hostname(form.host, request.referrer) data_copy['_host_nonce'] = nonce action = urljoin(settings.API_ROOT, email_or_string) try: if '_language' in received_data: return render_template('forms/captcha_lang/{}.html'.format(received_data['_language']), data=data_copy, sorted_keys=sorted_keys, action=action, lang=received_data['_language']) except TemplateNotFound: g.log.error('Requested language not found for reCAPTCHA page, defaulting to English', referrer=request.referrer, lang=received_data['_language']) pass return render_template('forms/captcha.html', data=data_copy, sorted_keys=sorted_keys, action=action, lang=None) status = form.send(received_data, sorted_keys, referrer) else: status = form.send_confirmation(store_data=received_data) # Respond to the request accordingly to the status code if status['code'] == Form.STATUS_EMAIL_SENT: if request_wants_json(): return jsonify({'success': "email sent", 'next': status['next']}) else: return redirect(status['next'], code=302) elif status['code'] == Form.STATUS_EMAIL_EMPTY: if request_wants_json(): return jsonerror(400, {'error': "Can't send an empty form"}) else: return render_template( 'error.html', title='Can\'t send an empty form', text=u'<p>Make sure you have placed the <a href="http://www.w3schools.com/tags/att_input_name.asp" target="_blank"><code>"name"</code> attribute</a> in all your form elements. Also, to prevent empty form submissions, take a look at the <a href="http://www.w3schools.com/tags/att_input_required.asp" target="_blank"><code>"required"</code> property</a>.</p><p>This error also happens when you have an <code>"enctype"</code> attribute set in your <code><form></code>, so make sure you don\'t.</p><p><a href="{}">Return to form</a></p>'.format(referrer) ), 400 elif status['code'] == Form.STATUS_CONFIRMATION_SENT or \ status['code'] == Form.STATUS_CONFIRMATION_DUPLICATED: if request_wants_json(): return jsonify({'success': "confirmation email sent"}) else: return render_template('forms/confirmation_sent.html', email=email, host=host, resend=status['code'] == Form.STATUS_CONFIRMATION_DUPLICATED ) elif status['code'] == Form.STATUS_OVERLIMIT: if request_wants_json(): return jsonify({'error': "form over quota"}) else: return render_template('error.html', title='Form over quota', text='It looks like this form is getting a lot of submissions and ran out of its quota. Try contacting this website through other means or try submitting again later.'), 402 elif status['code'] == Form.STATUS_REPLYTO_ERROR: if request_wants_json(): return jsonerror(500, {'error': "_replyto or email field has not been sent correctly"}) else: return render_template( 'error.html', title='Invalid email address', text=u'You entered <span class="code">{address}</span>. That is an invalid email address. Please correct the form and try to submit again <a href="{back}">here</a>.<p style="font-size: small">This could also be a problem with the form. For example, there could be two fields with <span class="code">_replyto</span> or <span class="code">email</span> name attribute. If you suspect the form is broken, please contact the form owner and ask them to investigate</p>'''.format(address=status['address'], back=status['referrer']) ), 400 # error fallback -- shouldn't happen if request_wants_json(): return jsonerror(500, {'error': "Unable to send email"}) else: return render_template( 'error.html', title='Unable to send email', text=u'Unable to send email. If you can, please send the link to your form and the error information to <b>{email}</b>. And send them the following: <p><pre><code>{message}</code></pre></p>'.format(message=json.dumps(status), email=settings.CONTACT_EMAIL) ), 500
def send(self, http_form, referrer): ''' Sends form to user's email. Assumes sender's email has been verified. ''' data, keys = http_form_to_dict(http_form) subject = data.get( '_subject', 'New submission from %s' % referrer_to_path(referrer)) reply_to = data.get('_replyto', data.get('email', data.get('Email', None))) cc = data.get('_cc', None) next = next_url(referrer, data.get('_next')) spam = data.get('_gotcha', None) # prevent submitting empty form if not any(data.values()): return {'code': Form.STATUS_EMAIL_EMPTY} # return a fake success for spam if spam: return {'code': Form.STATUS_EMAIL_SENT, 'next': next} # increase the monthly counter request_date = datetime.datetime.now() self.increase_monthly_counter(basedate=request_date) # increment the forms counter self.counter = Form.counter + 1 DB.session.add(self) # archive the form contents sub = Submission(self.id) sub.data = data DB.session.add(sub) # commit changes DB.session.commit() # delete all archived submissions over the limit records_to_keep = settings.ARCHIVED_SUBMISSIONS_LIMIT newest = self.submissions.with_entities( Submission.id).limit(records_to_keep) DB.engine.execute( delete('submissions'). \ where(Submission.form_id == self.id). \ where(~Submission.id.in_(newest)) ) # check if the forms are over the counter and the user is not upgraded overlimit = False monthly_counter = self.get_monthly_counter() if monthly_counter > settings.MONTHLY_SUBMISSIONS_LIMIT: overlimit = True if self.controllers: for c in self.controllers: if c.upgraded: overlimit = False break now = datetime.datetime.utcnow().strftime('%I:%M %p UTC - %d %B %Y') if not overlimit: text = render_template('email/form.txt', data=data, host=self.host, keys=keys, now=now) html = render_template('email/form.html', data=data, host=self.host, keys=keys, now=now) else: if monthly_counter - settings.MONTHLY_SUBMISSIONS_LIMIT > 25: # only send this overlimit notification for the first 25 overlimit emails # after that, return an error so the user can know the website owner is not # going to read his message. return {'code': Form.STATUS_EMAIL_FAILED} text = render_template('email/overlimit-notification.txt', host=self.host) html = render_template('email/overlimit-notification.html', host=self.host) result = send_email(to=self.email, subject=subject, text=text, html=html, sender=settings.DEFAULT_SENDER, reply_to=reply_to, cc=cc) if not result[0]: return {'code': Form.STATUS_EMAIL_FAILED} return {'code': Form.STATUS_EMAIL_SENT, 'next': next}