@ -0,0 +1,41 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Anjhana A K (odoo@cybrosys.com) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################### |
||||
|
from odoo import http |
||||
|
from odoo.http import Controller, request |
||||
|
|
||||
|
|
||||
|
class LoginController(Controller): |
||||
|
"""Controller that works when Login With QR clicked""" |
||||
|
|
||||
|
@http.route(['/web/redirect'], type='json', auth='none', website=True, |
||||
|
csrf=False, csrf_token=None) |
||||
|
def scanner(self, scanned_qr): |
||||
|
"""This code scans the QR provided and Login to the corresponding user |
||||
|
note: Only Internal User can log in through it""" |
||||
|
users = request.env['res.users'].sudo().search([('share', '=', False)]) |
||||
|
login = users.mapped('login') |
||||
|
if scanned_qr in login: |
||||
|
request.session.authenticate_without_passwd( |
||||
|
request.session.db, scanned_qr) |
||||
|
return request.redirect('/') |
||||
|
else: |
||||
|
return False |
@ -1,68 +0,0 @@ |
|||||
import cv2 |
|
||||
from pyzbar.pyzbar import decode |
|
||||
from odoo.http import Controller, request |
|
||||
from odoo import http |
|
||||
|
|
||||
SIGN_UP_REQUEST_PARAMS = {'db', 'login', 'debug', 'token', 'message', 'error', |
|
||||
'scope', 'mode', |
|
||||
'redirect', 'redirect_hostname', 'email', 'name', |
|
||||
'partner_id', |
|
||||
'password', 'confirm_password', 'city', 'country_id', |
|
||||
'lang', 'signup_email'} |
|
||||
|
|
||||
|
|
||||
class LoginController(Controller): |
|
||||
"""controller that works when Login With QR clicked""" |
|
||||
|
|
||||
@http.route(['/web/redirect'], type='http', auth='none', website=True, |
|
||||
csrf=False, csrf_token=None) |
|
||||
def open_scanner(self, *args, **kw): |
|
||||
"""This code scan the QR provided and Login to the corresponding user |
|
||||
note: Only Internal User can login through it""" |
|
||||
try: |
|
||||
cap = cv2.VideoCapture(0) |
|
||||
cap.set(3, 640) |
|
||||
cap.set(4, 480) |
|
||||
|
|
||||
while True: |
|
||||
ret, frame = cap.read() |
|
||||
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) |
|
||||
for qr_code in decode(gray): |
|
||||
(x, y, w, h) = qr_code.rect |
|
||||
cv2.rectangle(frame, (x, y), (x + w, y + h), |
|
||||
(0, 255, 0), 2) |
|
||||
decoded_text = qr_code.data.decode("utf-8") |
|
||||
users = request.env['res.users'].search( |
|
||||
[('share', '=', False)]) |
|
||||
login = users.mapped('login') |
|
||||
if decoded_text in login: |
|
||||
request.session.authenticate_without_passwd( |
|
||||
request.session.db, decoded_text) |
|
||||
cap.release() |
|
||||
cv2.destroyAllWindows() |
|
||||
return request.redirect('/') |
|
||||
else: |
|
||||
cap.release() |
|
||||
cv2.destroyAllWindows() |
|
||||
# Use the overridden web_login method to show error message |
|
||||
values = {k: v for k, v in request.params.items() if |
|
||||
k in SIGN_UP_REQUEST_PARAMS} |
|
||||
|
|
||||
values['error'] = ("Wrong QR Code") |
|
||||
request.update_env(user=request.session.uid) |
|
||||
request.env["ir.http"]._auth_method_public() |
|
||||
response = request.render('web.login', values) |
|
||||
|
|
||||
return response |
|
||||
|
|
||||
# Display the resulting frame |
|
||||
cv2.imshow('scanner- to exit press "q"', frame) |
|
||||
code = cv2.waitKey(1) |
|
||||
|
|
||||
if code == ord('q'): |
|
||||
cap.release() |
|
||||
cv2.destroyAllWindows() |
|
||||
return request.redirect('/web/login') |
|
||||
except Exception: |
|
||||
return request.render("login_using_qr.be_patient") |
|
||||
|
|
@ -1,7 +1,6 @@ |
|||||
## Module <login_using_qr> |
## Module <login_using_qr> |
||||
|
|
||||
#### 29.07.2023 |
#### 09.02.2024 |
||||
#### Version 16.0.1.0.0 |
#### Version 17.0.1.0.0 |
||||
#### ADD |
#### ADD |
||||
|
- Initial commit for Login using Qr code |
||||
- Initial commit for Login using QR Code |
|
||||
|
After Width: | Height: | Size: 80 KiB |
After Width: | Height: | Size: 85 KiB |
Before Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 76 KiB |
Before Width: | Height: | Size: 92 KiB |
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 76 KiB |
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 284 KiB |
After Width: | Height: | Size: 79 KiB |
After Width: | Height: | Size: 89 KiB |
Before Width: | Height: | Size: 81 KiB |
Before Width: | Height: | Size: 112 KiB |
Before Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 357 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 199 KiB |
After Width: | Height: | Size: 54 KiB |
After Width: | Height: | Size: 343 KiB |
After Width: | Height: | Size: 536 KiB |
After Width: | Height: | Size: 374 KiB |
After Width: | Height: | Size: 339 KiB |
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 73 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 6.8 KiB |
@ -0,0 +1,34 @@ |
|||||
|
.clearfix.oe_login_buttons.text-center.mb-1.pt-3{ |
||||
|
position:relative; |
||||
|
} |
||||
|
|
||||
|
.qr_video { |
||||
|
position: absolute; |
||||
|
top: -353px; |
||||
|
left: -269px; |
||||
|
} |
||||
|
|
||||
|
.video-container{ |
||||
|
width:717px; |
||||
|
} |
||||
|
|
||||
|
.video-container video{ |
||||
|
width:100% !important; |
||||
|
height:100% !important; |
||||
|
} |
||||
|
|
||||
|
.o_database_list .input-group-prepend .btn, .input-group-append a{ |
||||
|
z-index:0 !important; |
||||
|
} |
||||
|
|
||||
|
@media (max-width: 767.98px) { |
||||
|
.qr_video{ |
||||
|
position: absolute; |
||||
|
top: -281px; |
||||
|
min-width: 200px; |
||||
|
left: -63px; |
||||
|
} |
||||
|
.video-container { |
||||
|
width:360px; |
||||
|
|
||||
|
}} |
@ -0,0 +1,54 @@ |
|||||
|
/** @odoo-module **/ |
||||
|
import rpc from 'web.rpc'; |
||||
|
var publicWidget = require('web.public.widget'); |
||||
|
var ajax = require('web.ajax'); |
||||
|
publicWidget.registry.QrLogin = publicWidget.Widget.extend({ |
||||
|
selector: '.oe_qr_login', |
||||
|
events: { |
||||
|
'click #login_click': '_onLoginClick', |
||||
|
'click #close_qr_scanner':'_onClickClose' |
||||
|
}, |
||||
|
|
||||
|
_onClickClose: function(ev) { |
||||
|
window.location.reload(); |
||||
|
}, |
||||
|
|
||||
|
async _onLoginClick(ev) { |
||||
|
ev.target.offsetParent.querySelector('.close_button').classList.remove('d-none'); |
||||
|
const video = ev.target.offsetParent.querySelector('#video'); |
||||
|
var cam_stream = await navigator.mediaDevices.getUserMedia({ video: true}); |
||||
|
video.srcObject = cam_stream; |
||||
|
video.addEventListener('loadedmetadata', (event) => { |
||||
|
// Adjust video size once metadata is loaded
|
||||
|
video.width = video.videoWidth; |
||||
|
video.height = video.videoHeight; |
||||
|
}); |
||||
|
video.addEventListener('canplay', () => { |
||||
|
const canvas = document.createElement('canvas'); |
||||
|
const context = canvas.getContext('2d'); |
||||
|
canvas.width = video.width; |
||||
|
canvas.height = video.height; |
||||
|
setInterval(() => { |
||||
|
context.drawImage(video, 0, 0, canvas.width, canvas.height); |
||||
|
const imageData = context.getImageData(0, 0, canvas.width, canvas.height); |
||||
|
const code = jsQR(imageData.data, imageData.width, imageData.height); |
||||
|
if (code) { |
||||
|
ajax.jsonRpc('/web/redirect', 'call', {'scanned_qr': code.data} |
||||
|
).then((token) => { |
||||
|
if(token){ |
||||
|
cam_stream.getTracks().forEach(function(track) { |
||||
|
track.stop(); |
||||
|
window.location.href = '/'; |
||||
|
}); |
||||
|
} |
||||
|
else{ |
||||
|
alert('Scanned QR does not exist. Please try again.'); |
||||
|
window.location.reload(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}, 1000); // Adjust the interval as needed
|
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
}) |
@ -1,11 +1,24 @@ |
|||||
<?xml version="1.0" encoding="UTF-8" ?> |
<?xml version="1.0" encoding="UTF-8" ?> |
||||
<odoo> |
<odoo> |
||||
<!--calling the controller of scanner from login page--> |
<!--Calling the controller of scanner from login page--> |
||||
<template id="qr_login" inherit_id="web.login" name="QR scanner"> |
<template id="qr_login" inherit_id="web.login" name="QR scanner"> |
||||
<xpath expr="//button[hasclass('btn-primary')]" position="after"> |
<xpath expr="//div[hasclass('o_login_auth')]" position="before"> |
||||
<div class="justify-content-between mt-2 d-flex small"> |
<div class="justify-content-between mt-2 d-flex small oe_qr_login"> |
||||
<a href="/web/redirect">Login With QR</a> |
<a href="#" id="login_click" t-on-click='_onLoginClick'>Login With QR</a> |
||||
|
<div class="qr_video"> |
||||
|
<div class="close_button d-none position-absolute" t-ref="close_button"> |
||||
|
<button id="close_qr_scanner" style="position: absolute; right: 0px; z-index: 111"> |
||||
|
X |
||||
|
</button> |
||||
|
<div class="video-container"> |
||||
|
|
||||
|
<video id="video" width="" height="" autoplay="true"/> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
</div> |
</div> |
||||
</xpath> |
</xpath> |
||||
</template> |
</template> |
||||
</odoo> |
</odoo> |
||||
|
|
||||
|
|
||||
|
@ -0,0 +1,33 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8" ?> |
||||
|
<odoo> |
||||
|
<!-- Template to show if the scanned QR Code is not accepted --> |
||||
|
<template id="redirect_to"> |
||||
|
<div> |
||||
|
<center> |
||||
|
<h5> |
||||
|
This feature is currently not available for you |
||||
|
</h5> |
||||
|
<a href="/web/login"> |
||||
|
<h5> |
||||
|
Redirect to Login Page |
||||
|
</h5> |
||||
|
</a> |
||||
|
</center> |
||||
|
</div> |
||||
|
</template> |
||||
|
<!-- Template to show if any error occurs in opening of scanner --> |
||||
|
<template id="be_patient"> |
||||
|
<div> |
||||
|
<center> |
||||
|
<h5> |
||||
|
Scan the QR, or else, |
||||
|
</h5> |
||||
|
<a href="/web/login"> |
||||
|
<h5> |
||||
|
Redirect to Login Page |
||||
|
</h5> |
||||
|
</a> |
||||
|
</center> |
||||
|
</div> |
||||
|
</template> |
||||
|
</odoo> |
@ -1,18 +0,0 @@ |
|||||
<?xml version="1.0" encoding="UTF-8" ?> |
|
||||
<odoo> |
|
||||
<!-- template to show if any error occur in opening of scanner --> |
|
||||
<template id="be_patient"> |
|
||||
<div> |
|
||||
<center> |
|
||||
<h5> |
|
||||
Scan the QR, or else, |
|
||||
</h5> |
|
||||
<a href="/web/login"> |
|
||||
<h5> |
|
||||
Redirect to login page |
|
||||
</h5> |
|
||||
</a> |
|
||||
</center> |
|
||||
</div> |
|
||||
</template> |
|
||||
</odoo> |
|
@ -1,20 +1,17 @@ |
|||||
<?xml version="1.0" encoding="UTF-8" ?> |
<?xml version="1.0" encoding="UTF-8" ?> |
||||
<odoo> |
<odoo> |
||||
<!--adding field in res.users--> |
<!--Adding field in res.users--> |
||||
<record id="view_users_form" model="ir.ui.view"> |
<record id="view_users_form" model="ir.ui.view"> |
||||
<field name="name">res.users.view.form.inherit.login.using.qr</field> |
<field name="name">res.users.view.form.inherit.login.using.qr</field> |
||||
<field name="model">res.users</field> |
<field name="model">res.users</field> |
||||
<field name="inherit_id" ref="base.view_users_form"/> |
<field name="inherit_id" ref="base.view_users_form"/> |
||||
<field name="arch" type="xml"> |
<field name="arch" type="xml"> |
||||
<data> |
|
||||
<xpath expr="//page[@name='access_rights']" position="after"> |
<xpath expr="//page[@name='access_rights']" position="after"> |
||||
<page string="Login QR Code" name="qr_page"> |
<page string="Login QR Code" name="qr_page"> |
||||
<span>LOGIN QR</span><br/> |
<span>LOGIN QR</span><br/> |
||||
<field name="qr_code" class="oe_form_binary_file" widget="image"/> |
<field name="qr_code" class="oe_form_binary_file" widget="image"/> |
||||
</page> |
</page> |
||||
</xpath> |
</xpath> |
||||
</data> |
|
||||
</field> |
</field> |
||||
</record> |
</record> |
||||
|
|
||||
</odoo> |
</odoo> |
||||
|