Added reCaptcha to application form
This commit is contained in:
+306
@@ -0,0 +1,306 @@
|
||||
/**
|
||||
* angular-recaptcha build:2016-04-05
|
||||
* https://github.com/vividcortex/angular-recaptcha
|
||||
* Copyright (c) 2016 VividCortex
|
||||
**/
|
||||
|
||||
/*global angular, Recaptcha */
|
||||
(function (ng) {
|
||||
'use strict';
|
||||
|
||||
ng.module('vcRecaptcha', []);
|
||||
|
||||
}(angular));
|
||||
|
||||
/*global angular */
|
||||
(function (ng) {
|
||||
'use strict';
|
||||
|
||||
function throwNoKeyException() {
|
||||
throw new Error('You need to set the "key" attribute to your public reCaptcha key. If you don\'t have a key, please get one from https://www.google.com/recaptcha/admin/create');
|
||||
}
|
||||
|
||||
var app = ng.module('vcRecaptcha');
|
||||
|
||||
/**
|
||||
* An angular service to wrap the reCaptcha API
|
||||
*/
|
||||
app.provider('vcRecaptchaService', function(){
|
||||
var provider = this;
|
||||
var config = {};
|
||||
provider.onLoadFunctionName = 'vcRecaptchaApiLoaded';
|
||||
|
||||
/**
|
||||
* Sets the reCaptcha configuration values which will be used by default is not specified in a specific directive instance.
|
||||
*
|
||||
* @since 2.5.0
|
||||
* @param defaults object which overrides the current defaults object.
|
||||
*/
|
||||
provider.setDefaults = function(defaults){
|
||||
angular.copy(config, defaults);
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the reCaptcha key which will be used by default is not specified in a specific directive instance.
|
||||
*
|
||||
* @since 2.5.0
|
||||
* @param siteKey the reCaptcha public key (refer to the README file if you don't know what this is).
|
||||
*/
|
||||
provider.setSiteKey = function(siteKey){
|
||||
config.key = siteKey;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the reCaptcha theme which will be used by default is not specified in a specific directive instance.
|
||||
*
|
||||
* @since 2.5.0
|
||||
* @param theme The reCaptcha theme.
|
||||
*/
|
||||
provider.setTheme = function(theme){
|
||||
config.theme = theme;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the reCaptcha stoken which will be used by default is not specified in a specific directive instance.
|
||||
*
|
||||
* @since 2.5.0
|
||||
* @param stoken The reCaptcha stoken.
|
||||
*/
|
||||
provider.setStoken = function(stoken){
|
||||
config.stoken = stoken;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the reCaptcha size which will be used by default is not specified in a specific directive instance.
|
||||
*
|
||||
* @since 2.5.0
|
||||
* @param size The reCaptcha size.
|
||||
*/
|
||||
provider.setSize = function(size){
|
||||
config.size = size;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the reCaptcha type which will be used by default is not specified in a specific directive instance.
|
||||
*
|
||||
* @since 2.5.0
|
||||
* @param type The reCaptcha type.
|
||||
*/
|
||||
provider.setType = function(type){
|
||||
config.type = type;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the reCaptcha configuration values which will be used by default is not specified in a specific directive instance.
|
||||
*
|
||||
* @since 2.5.0
|
||||
* @param onLoadFunctionName string name which overrides the name of the onload function. Should match what is in the recaptcha script querystring onload value.
|
||||
*/
|
||||
provider.setOnLoadFunctionName = function(onLoadFunctionName){
|
||||
provider.onLoadFunctionName = onLoadFunctionName;
|
||||
};
|
||||
|
||||
provider.$get = ['$rootScope','$window', '$q', function ($rootScope, $window, $q) {
|
||||
var deferred = $q.defer(), promise = deferred.promise, recaptcha;
|
||||
|
||||
$window.vcRecaptchaApiLoadedCallback = $window.vcRecaptchaApiLoadedCallback || [];
|
||||
|
||||
var callback = function () {
|
||||
recaptcha = $window.grecaptcha;
|
||||
|
||||
deferred.resolve(recaptcha);
|
||||
};
|
||||
|
||||
$window.vcRecaptchaApiLoadedCallback.push(callback);
|
||||
|
||||
$window[provider.onLoadFunctionName] = function () {
|
||||
$window.vcRecaptchaApiLoadedCallback.forEach(function(callback) {
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
function getRecaptcha() {
|
||||
if (!!recaptcha) {
|
||||
return $q.when(recaptcha);
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
function validateRecaptchaInstance() {
|
||||
if (!recaptcha) {
|
||||
throw new Error('reCaptcha has not been loaded yet.');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Check if grecaptcha is not defined already.
|
||||
if (ng.isDefined($window.grecaptcha)) {
|
||||
callback();
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
/**
|
||||
* Creates a new reCaptcha object
|
||||
*
|
||||
* @param elm the DOM element where to put the captcha
|
||||
* @param conf the captcha object configuration
|
||||
* @throws NoKeyException if no key is provided in the provider config or the directive instance (via attribute)
|
||||
*/
|
||||
create: function (elm, conf) {
|
||||
|
||||
conf.sitekey = conf.key || config.key;
|
||||
conf.theme = conf.theme || config.theme;
|
||||
conf.stoken = conf.stoken || config.stoken;
|
||||
conf.size = conf.size || config.size;
|
||||
conf.type = conf.type || config.type;
|
||||
|
||||
if (!conf.sitekey || conf.sitekey.length !== 40) {
|
||||
throwNoKeyException();
|
||||
}
|
||||
return getRecaptcha().then(function (recaptcha) {
|
||||
return recaptcha.render(elm, conf);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Reloads the reCaptcha
|
||||
*/
|
||||
reload: function (widgetId) {
|
||||
validateRecaptchaInstance();
|
||||
|
||||
// $log.info('Reloading captcha');
|
||||
recaptcha.reset(widgetId);
|
||||
|
||||
// Let everyone know this widget has been reset.
|
||||
$rootScope.$broadcast('reCaptchaReset', widgetId);
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the response from the reCaptcha widget.
|
||||
*
|
||||
* @see https://developers.google.com/recaptcha/docs/display#js_api
|
||||
*
|
||||
* @returns {String}
|
||||
*/
|
||||
getResponse: function (widgetId) {
|
||||
validateRecaptchaInstance();
|
||||
|
||||
return recaptcha.getResponse(widgetId);
|
||||
}
|
||||
};
|
||||
|
||||
}];
|
||||
});
|
||||
|
||||
}(angular));
|
||||
|
||||
/*global angular, Recaptcha */
|
||||
(function (ng) {
|
||||
'use strict';
|
||||
|
||||
var app = ng.module('vcRecaptcha');
|
||||
|
||||
app.directive('vcRecaptcha', ['$document', '$timeout', 'vcRecaptchaService', function ($document, $timeout, vcRecaptcha) {
|
||||
|
||||
return {
|
||||
restrict: 'A',
|
||||
require: "?^^form",
|
||||
scope: {
|
||||
response: '=?ngModel',
|
||||
key: '=?',
|
||||
stoken: '=?',
|
||||
theme: '=?',
|
||||
size: '=?',
|
||||
type: '=?',
|
||||
tabindex: '=?',
|
||||
required: '=?',
|
||||
onCreate: '&',
|
||||
onSuccess: '&',
|
||||
onExpire: '&'
|
||||
},
|
||||
link: function (scope, elm, attrs, ctrl) {
|
||||
scope.widgetId = null;
|
||||
|
||||
if(ctrl && angular.isDefined(attrs.required)){
|
||||
scope.$watch('required', validate);
|
||||
}
|
||||
|
||||
var removeCreationListener = scope.$watch('key', function (key) {
|
||||
var callback = function (gRecaptchaResponse) {
|
||||
// Safe $apply
|
||||
$timeout(function () {
|
||||
scope.response = gRecaptchaResponse;
|
||||
validate();
|
||||
|
||||
// Notify about the response availability
|
||||
scope.onSuccess({response: gRecaptchaResponse, widgetId: scope.widgetId});
|
||||
});
|
||||
};
|
||||
|
||||
vcRecaptcha.create(elm[0], {
|
||||
|
||||
callback: callback,
|
||||
key: key,
|
||||
stoken: scope.stoken || attrs.stoken || null,
|
||||
theme: scope.theme || attrs.theme || null,
|
||||
type: scope.type || attrs.type || null,
|
||||
tabindex: scope.tabindex || attrs.tabindex || null,
|
||||
size: scope.size || attrs.size || null,
|
||||
'expired-callback': expired
|
||||
|
||||
}).then(function (widgetId) {
|
||||
// The widget has been created
|
||||
validate();
|
||||
scope.widgetId = widgetId;
|
||||
scope.onCreate({widgetId: widgetId});
|
||||
|
||||
scope.$on('$destroy', destroy);
|
||||
|
||||
scope.$on('reCaptchaReset', function(resetWidgetId){
|
||||
if(angular.isUndefined(resetWidgetId) || widgetId === resetWidgetId){
|
||||
scope.response = "";
|
||||
validate();
|
||||
}
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
// Remove this listener to avoid creating the widget more than once.
|
||||
removeCreationListener();
|
||||
});
|
||||
|
||||
function destroy() {
|
||||
if (ctrl) {
|
||||
// reset the validity of the form if we were removed
|
||||
ctrl.$setValidity('recaptcha', null);
|
||||
}
|
||||
|
||||
cleanup();
|
||||
}
|
||||
|
||||
function expired(){
|
||||
scope.response = "";
|
||||
validate();
|
||||
|
||||
// Notify about the response availability
|
||||
scope.onExpire({widgetId: scope.widgetId});
|
||||
}
|
||||
|
||||
function validate(){
|
||||
if(ctrl){
|
||||
ctrl.$setValidity('recaptcha', scope.required === false ? null : Boolean(scope.response));
|
||||
}
|
||||
}
|
||||
|
||||
function cleanup(){
|
||||
// removes elements reCaptcha added.
|
||||
angular.element($document[0].querySelectorAll('.pls-container')).parent().remove();
|
||||
}
|
||||
}
|
||||
};
|
||||
}]);
|
||||
|
||||
}(angular));
|
||||
@@ -1,6 +1,6 @@
|
||||
//app
|
||||
|
||||
app = angular.module('applicationApp', []);
|
||||
app = angular.module('applicationApp', ['vcRecaptcha']);
|
||||
|
||||
//tokens
|
||||
|
||||
@@ -26,12 +26,37 @@ var notySuccess = notyfication('success',2500);
|
||||
|
||||
//controllers
|
||||
|
||||
app.controller("applicationController", function($scope, $http, $location, $window) {
|
||||
app.controller("applicationController", function($scope, $http, $location, $window, vcRecaptchaService) {
|
||||
$scope.member = {};
|
||||
$scope.response = null;
|
||||
$scope.widgetId = null;
|
||||
$scope.model = {
|
||||
key: '6LevHAcUAAAAAA45B7c-7qja-2aSwHztr9xb4K2Z'
|
||||
};
|
||||
$scope.setResponse = function(response) {
|
||||
$scope.response = response;
|
||||
};
|
||||
$scope.setWidgetId = function(widgetId) {
|
||||
$scope.widgetId = widgetId;
|
||||
};
|
||||
$scope.cbExpiration = function() {
|
||||
vcRecaptchaService.reload($scope.widgetId);
|
||||
$scope.response = null;
|
||||
};
|
||||
$scope.send = function() {
|
||||
$http.post("/members/api/request", $scope.member).then(function(data){
|
||||
notySuccess("Hakemus lähetetty!");
|
||||
$window.location.href = "/application/";
|
||||
});
|
||||
var valid;
|
||||
//server side validation
|
||||
$scope.member.reCaptchaResponse = vcRecaptchaService.getResponse()
|
||||
if(resp === "") {
|
||||
alert("Ole hyvä ja täytä kuvavarmennus");
|
||||
} else {
|
||||
$http.post("/members/api/request", $scope.member).then(function(data){
|
||||
notySuccess("Hakemus lähetetty!");
|
||||
$window.location.href = "/application/";
|
||||
}, function(data){
|
||||
notyError("Jokin meni vikaan. Yritä uudelleen.");
|
||||
vcRecaptchaService.reload($scope.widgetId);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -18,7 +18,9 @@
|
||||
{%load staticfiles %}
|
||||
<script src="/static/js/jquery.noty.packaged.js"></script>
|
||||
<script src="/static/js/application_controllers.js"></script>
|
||||
|
||||
<!-- reCaptcha -->
|
||||
<script src="https://www.google.com/recaptcha/api.js?onload=vcRecaptchaApiLoaded&render=explicit" async defer></script>
|
||||
<script src="/static/js/angular-recaptcha.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Killan jäseneksi liittyminen on helppoa ja hauskaa. Täytä vain alla oleva lomake.</h2>
|
||||
@@ -59,6 +61,14 @@
|
||||
<label>Asuinkunta: </label>
|
||||
<input id="PORField" required type="text" placeholder="Otaniemi" class="form-control" ng-model="member.POR"></input>
|
||||
</div>
|
||||
<div
|
||||
vc-recaptcha
|
||||
theme="'light'"
|
||||
key="model.key"
|
||||
on-create="setWidgetId(widgetId)"
|
||||
on-success="setResponse(response)"
|
||||
on-expire="cbExpiration()"
|
||||
></div>
|
||||
<button ng-click="applicationForm.$valid && send()" type="submit" class="btn btn-success" id="sendmember">Tallenna</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -6,6 +6,32 @@ from django.http import HttpResponse, HttpResponseBadRequest
|
||||
from django.core.exceptions import ValidationError
|
||||
from members.models import Member, MemberRequest
|
||||
import json
|
||||
from django.core.mail import send_mail
|
||||
import requests
|
||||
from django.conf import settings
|
||||
|
||||
#function to validate reCaptcha
|
||||
def validateReCaptcha(response):
|
||||
values = {
|
||||
'secret': settings.GOOGLE_RECAPTCHA_SECRET_KEY,
|
||||
'response': response,
|
||||
}
|
||||
url = "https://www.google.com/recaptcha/api/siteverify"
|
||||
headers = {'Content-type': 'application/json'}
|
||||
resp = requests.post(url, data=json.dumps(values), headers=headers)
|
||||
result = json.loads(resp.read())
|
||||
if not result["success"]:
|
||||
return False
|
||||
return True
|
||||
|
||||
def sendmail(subject, message):
|
||||
send_mail(
|
||||
subject,
|
||||
message,
|
||||
'no-reply@sahkoinsinoorikilta.fi',
|
||||
['viestintamestari@sahkoinsinoorikilta.fi'],
|
||||
fail_silently=False
|
||||
)
|
||||
|
||||
@ensure_csrf_cookie
|
||||
@require_http_methods(["GET"])
|
||||
@@ -92,11 +118,33 @@ def member_requests(request, *args, **kwargs):
|
||||
def new_member_request(request, *args, **kwargs):
|
||||
try:
|
||||
data = json.loads(request.body.decode("utf-8"))
|
||||
#get captcha response from member
|
||||
captcha = data.pop("reCaptchaResponse", "")
|
||||
#send response to google and check it out
|
||||
captcha_ok = validateReCaptcha(captcha)
|
||||
#if not ok, inform user
|
||||
if not captcha_ok:
|
||||
return HttpResponseBadRequest('{"error" : "Captcha not ok. Please try again."}')
|
||||
#if ok continue
|
||||
mem = Member.create_from_dict(data)
|
||||
req = MemberRequest.objects.create(member=mem)
|
||||
subject = 'New application'
|
||||
message = 'You have new application\r\n'
|
||||
message += 'Member info:\r\n'
|
||||
message += 'First name: ' + mem.first_name + '\r\n'
|
||||
message += 'Last name: ' + mem.last_name + '\r\n'
|
||||
message += 'Email: ' + mem.email + '\r\n'
|
||||
message += 'Place of residence: ' + mem.POR + '\r\n'
|
||||
message += 'AYY-membership: ' + str(mem.AYY) + '\r\n'
|
||||
message += 'To mail list: ' + str(mem.jas) + '\r\n'
|
||||
message += 'Created: ' + mem.created.isoformat(' ') + '\r\n'
|
||||
message += 'Please go to the http://sika.sahkoinsinoorikilta.fi/members/ and do something about it!\r\n'
|
||||
sendmail(subject, message)
|
||||
return HttpResponse(json.dumps(mem.get_dict()))
|
||||
except ValueError:
|
||||
return HttpResponseBadRequest('{"error" : "Invalid parameters supplied"}')
|
||||
except TimeoutError:
|
||||
return HttpResponseBadRequest('{"error" : "Much error, no connection"}')
|
||||
|
||||
@ensure_csrf_cookie
|
||||
@require_http_methods(["GET", "POST", "DELETE"])
|
||||
|
||||
@@ -106,6 +106,20 @@ AUTH_PASSWORD_VALIDATORS = [
|
||||
},
|
||||
]
|
||||
|
||||
#Email
|
||||
# https://sendgrid.com/docs/Integrate/Frameworks/django.html
|
||||
|
||||
EMAIL_HOST = 'smtp.sendgrid.net'
|
||||
EMAIL_HOST_USER = 'sendgrid_username'
|
||||
EMAIL_HOST_PASSWORD = 'sendgrid_password'
|
||||
EMAIL_PORT = 587
|
||||
EMAIL_USE_TLS = True
|
||||
|
||||
#ReCaptcha
|
||||
# http://www.yaconiello.com/blog/integrating-google-recaptcha-to-django/
|
||||
|
||||
GOOGLE_RECAPTCHA_SITE_KEY = "YOUR-PUBLIC-KEY"
|
||||
GOOGLE_RECAPTCHA_SECRET_KEY = "YOUR-PRIVATE-KEY"
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/1.9/topics/i18n/
|
||||
|
||||
Reference in New Issue
Block a user