Merge pull request #49 from expsa/mig_sttl_otp_login

mig sttl_otp_login by yahya
This commit is contained in:
yahyaDevelopOdoo 2025-10-12 09:55:53 +03:00 committed by GitHub
commit 3d9967d1d1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
43 changed files with 1435 additions and 0 deletions

View File

@ -0,0 +1,2 @@
from . import controller
from . import models

View File

@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
{
"name": "Email OTP Authentication",
"version": "18.0.1.0.0",
"author": "Silver Touch Technologies Limited",
'category': 'Tools',
"website": "https://www.silvertouch.com/",
"description": """
""",
"summary": """
This module allows the user authentication of the database via OTP.
""",
'depends': ['base', 'mail', 'web', 'website', 'auth_signup'],
'data': [
"security/ir.model.access.csv",
"security/security_group.xml",
"views/otp_verification.xml",
"views/login_view.xml",
"views/otp_signup.xml",
"data/cron.xml"
],
"price": 0,
"currency": "USD",
"license": "LGPL-3",
'installable': True,
'application': False,
'images': ['static/description/banner.png']
}

View File

@ -0,0 +1,2 @@
from . import otp_login
from . import otp_signup

View File

@ -0,0 +1,152 @@
from odoo import http, _
from odoo.http import request
from odoo.addons.web.controllers.home import Home
from odoo.exceptions import AccessDenied
from random import choice
import string
import logging
from odoo.addons.web.controllers.utils import ensure_db
_logger = logging.getLogger(__name__)
class OtpLoginHome(Home):
@http.route(website=True, auth='public')
def web_login(self, redirect=None, **kw):
"""Customized web_login for OTP flow (Odoo 18 compatible)"""
ensure_db()
qcontext = request.params.copy()
if request.httprequest.method == 'GET':
otp_login = kw.get('otp_login')
otp = kw.get('otp')
if otp_login and otp:
_logger.info("Rendering OTP verification page.")
return request.render("sttl_otp_login.custom_login_template", {
'otp': True,
'otp_login': True,
})
if otp_login:
_logger.info("Rendering OTP login page.")
return request.render("sttl_otp_login.custom_login_template", {
'otp_login': True,
})
return super(OtpLoginHome, self).web_login(redirect, **kw)
else:
login = kw.get('login')
password = kw.get('password')
if login:
request.params['login'] = login.strip()
if password:
request.params['password'] = password.strip()
_logger.info("Processing standard login for %s", login)
return super(OtpLoginHome, self).web_login(redirect, **kw)
def generate_otp(self, digits=4):
otp = ''.join(choice(string.digits) for _ in range(digits))
_logger.info("Generated OTP: %s", otp)
return otp
@http.route('/otp/login', type='http', auth='public', website=True, csrf=False)
def web_otp_login(self, **kw):
email = (kw.get('login') or '').strip()
if not email:
return request.render("sttl_otp_login.custom_login_template", {
'error': _('Please enter your email or login first.'),
'otp_login': True,
})
user = request.env['res.users'].sudo().search([('login', '=', email)], limit=1)
if not user:
_logger.warning(" No user found for OTP login: %s", email)
return request.render("sttl_otp_login.custom_login_template", {
'error': _('No user found with this email.'),
'otp_login': True,
})
OTP = self.generate_otp(4)
request.env['otp.verification'].sudo().create({
'email': email,
'otp': OTP,
'state': 'unverified',
})
_logger.info(" OTP for %s is %s", email, OTP)
# عرض صفحة إدخال OTP
return request.render("sttl_otp_login.custom_login_template", {
'otp_login': True,
'otp': True,
'login': email,
'otp_no': OTP,
# 'info_message': _("A verification code was sent to your email.")
})
@http.route('/otp/verify', type='http', auth='public', website=True, csrf=False)
def web_otp_verify(self, **kw):
email = kw.get('login')
otp_input = kw.get('otp')
otp_rec = request.env['otp.verification'].sudo().search(
[('email', '=', email)], order='create_date desc', limit=1
)
if not otp_rec or otp_rec.otp != otp_input:
return request.render('sttl_otp_login.custom_login_template', {
'error': _("Invalid OTP. Please try again."),
'otp_login': True,
'login': email,
})
otp_rec.state = 'verified'
user = request.env['res.users'].sudo().search([('login', '=', email)], limit=1)
if not user:
return request.render('sttl_otp_login.custom_login_template', {
'error': _("No account found for this email."),
'otp_login': True,
'login': email,
})
try:
credential = {'login': email, 'password': user.password or ''}
auth_info = request.session.authenticate(request.db, credential)
if auth_info and auth_info.get('uid'):
with request.env.registry.cursor() as cr:
env = request.env(user=auth_info['uid'])
request.session.finalize(env)
_logger.info(" User %s logged in successfully via OTP", email)
return request.redirect('/web')
except Exception as e:
_logger.exception(" Failed OTP login for %s: %s", email, e)
return request.render('sttl_otp_login.custom_login_template', {
'error': _("An unexpected error occurred. Please try again."),
'otp_login': True,
'login': email,
})

View File

@ -0,0 +1,148 @@
from odoo import http, _
from odoo.http import request
from odoo.addons.web.controllers.home import Home
from odoo.exceptions import UserError
import logging
import string
from random import choice
_logger = logging.getLogger(__name__)
class OtpSignup(Home):
def generate_otp(self, digits=4):
otp = ''.join(choice(string.digits) for _ in range(digits))
_logger.info(" Generated OTP: %s", otp)
return otp
@http.route('/web/signup/otp', type='http', auth='public', website=True, sitemap=False)
def web_signup_otp(self, **kw):
qcontext = request.params.copy()
if request.httprequest.method == 'GET':
return request.render('sttl_otp_login.custom_otp_signup', qcontext)
OTP = self.generate_otp(4)
email = qcontext.get("login")
password = qcontext.get("password")
confirm_password = qcontext.get("confirm_password")
if not email or not password or not confirm_password:
qcontext["error"] = _("All fields are required.")
return request.render('sttl_otp_login.custom_otp_signup', qcontext)
if password != confirm_password:
qcontext["error"] = _("Passwords do not match, please retype them.")
return request.render('sttl_otp_login.custom_otp_signup', qcontext)
user_exist = request.env["res.users"].sudo().search([("login", "=", email)], limit=1)
if user_exist:
qcontext["error"] = _("Another user is already registered using this email address.")
return request.render('sttl_otp_login.custom_otp_signup', qcontext)
else:
email = str(qcontext.get('login'))
# name = str(qcontext.get('name'))
# vals = {
# 'otp': OTP,
# 'email': email
# }
#send to email
# email_from = request.env.company.email
# mail_body = """\
# <html>
# <body>
# <p>
# Dear <b>%s</b>,
# <br>
# <p>
# To complete the verification process for your Odoo account,
# <br>Please use the following One-Time Password (OTP): <b>%s</b>
# </p>
# Thanks & Regards.
# </p>
# </body>
# </html>
# """ % (name, OTP)
# mail = request.env['mail.mail'].sudo().create({
# 'subject': _('Verify Your Odoo Account - OTP Required'),
# 'email_from': email_from,
# 'email_to': email,
# 'body_html': mail_body,
# })
# mail.send()
# response = request.render('sttl_otp_login.custom_otp_signup', {'otp': True, 'otp_login': True,
# 'login': qcontext["login"],
# 'otp_no': OTP,
# 'name': qcontext["name"],
# 'password': qcontext["password"],
# 'confirm_password': qcontext[
# "confirm_password"]})
# res = request.env['otp.verification'].sudo().create(vals)
# return response
#send in local
request.env['otp.verification'].sudo().create({
'otp': OTP,
'email': email,
'state': 'unverified',
})
qcontext.update({
'otp': True,
'otp_login': True,
'login': email,
'otp_no': OTP,
'password': password,
'confirm_password': confirm_password,
'info_message': f"Your OTP is: {OTP}",
})
return request.render('sttl_otp_login.custom_otp_signup', qcontext)
@http.route('/web/signup/otp/verify', type='http', auth='public', website=True, sitemap=False)
def web_otp_signup_verify(self, **kw):
email = kw.get('login')
otp_input = kw.get('otp')
name = kw.get('name')
password = kw.get('password')
confirm_password = kw.get('confirm_password')
otp_rec = request.env['otp.verification'].sudo().search(
[('email', '=', email)], order="create_date desc", limit=1
)
if not otp_rec or otp_rec.otp != otp_input:
return request.render('sttl_otp_login.custom_otp_signup', {
'otp': True, 'otp_login': True,
'login': email, 'name': name,
'password': password, 'confirm_password': confirm_password,
'error': _("Invalid OTP. Please try again."),
})
otp_rec.state = 'verified'
user = request.env['res.users'].sudo().create({
'name': name,
'login': email,
'password': password,
})
request.env.cr.commit()
_logger.info(" User %s created successfully.", email)
credential = {'login': email, 'password': password}
request.session.authenticate(request.db, credential)
_logger.info(" User %s logged in successfully.", email)
return request.redirect('/web')

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<data noupdate="0">
<record id="ir_cron_otp_verify" model="ir.cron">
<field name="name">OTP : Delete the OTPs generated </field>
<field name="model_id" ref="model_otp_verification"/>
<field name="state">code</field>
<field name="code">model._cron_delete_verified_otp()</field>
<field name="interval_number">1</field>
<field name="interval_type">weeks</field>
<field name="active">True</field>
</record>
</data>
</odoo>

View File

@ -0,0 +1,2 @@
from . import otp_verification
from . import res_users

View File

@ -0,0 +1,18 @@
from odoo import fields, models, api
class OtpVerification(models.Model):
_name = "otp.verification"
_description = 'Otp Verification'
otp = fields.Text(string="OTP")
state = fields.Selection([
('verified', 'Verified'),
('unverified', 'Unverified'),
('rejected', 'Rejected')], string="State", default="unverified")
email = fields.Char(string="email")
@api.model
def _cron_delete_verified_otp(self):
otp = self.search([])
otp.unlink()

View File

@ -0,0 +1,58 @@
import logging
from odoo import api, models, SUPERUSER_ID, _
from odoo.exceptions import AccessDenied
from odoo.http import request
from odoo.addons.mail.models.fetchmail import name
_logger = logging.getLogger(name)
class ResUsers(models.Model):
_inherit = "res.users"
@classmethod
def _login(cls, db, credential, user_agent_env):
login = credential.get('login')
password = credential.get('password')
login_type = credential.get('type', 'password')
otp = credential.get('otp')
ip = request.httprequest.environ.get('REMOTE_ADDR') if request else 'n/a'
_logger.info(" Login attempt: %s (type=%s)", login, login_type)
try:
with cls.pool.cursor() as cr:
env = api.Environment(cr, SUPERUSER_ID, {})
user = env[cls._name].search(env[cls._name]._get_login_domain(login),
order=env[cls._name]._get_login_order(),
limit=1)
if not user:
raise AccessDenied(_("Invalid login"))
user = user.with_user(user)
if login_type == 'otp':
otp_record = env['otp.verification'].sudo().search([
('email', '=', user.login),
('otp', '=', otp),
('state', '=', 'unverified')
], order='create_date desc', limit=1)
if not otp_record:
raise AccessDenied(_("Invalid OTP"))
otp_record.state = 'verified'
auth_info = {'uid': user.id, 'auth_method': 'otp', 'mfa': 'skip'}
else:
user._check_credentials({'type': 'password', 'password': password}, user_agent_env)
auth_info = {'uid': user.id, 'auth_method': 'password'}
user._update_last_login()
except AccessDenied:
_logger.warning(" Login failed for %s from %s", login, ip)
raise
_logger.info(" Login success for %s from %s", login, ip)
return auth_info

View File

@ -0,0 +1,2 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_otp_verification,sttl_custom_reports_so.access_otp_verification,model_otp_verification,,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_otp_verification sttl_custom_reports_so.access_otp_verification model_otp_verification 1 1 1 1

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record model="ir.module.category" id="otp_verification">
<field name="name">OTP Table</field>
<field name="description">User access levels for OTP module</field>
<field name="sequence">10</field>
</record>
<record id="otp_verification_access" model="res.groups">
<field name="name">Admin</field>
<field name="category_id" ref="otp_verification"/>
</record>
</odoo>

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,28 @@
<svg xmlns="http://www.w3.org/2000/svg" width="47" height="47" viewBox="0 0 47 47">
<g id="support" transform="translate(4)">
<circle id="Ellipse_978" data-name="Ellipse 978" cx="23.5" cy="23.5" r="23.5" transform="translate(-4)" fill="#f5f9ff"/>
<g id="handshake" transform="translate(2.001 -44.999)">
<path id="Path_78185" data-name="Path 78185" d="M77.23,127.821l-6.257,1.829-1.4,5.538c.242,2.034.623,3.938,1.88,5l10.378,8.435,1.256.966c1.89,1.454,3.681-.578,2.244-2.08-.02-.022-.042-.044-.064-.066a1.855,1.855,0,0,0,1.507.446,1.327,1.327,0,0,0,.8-2.32A1.505,1.505,0,1,0,89.6,143.36a1.973,1.973,0,0,0,1.733-3.434c-.012-.014-.026-.03-.04-.044L87,133.489l-5.759-3.69Z" transform="translate(-64.821 -66.913)" fill="#ffbc85" fill-rule="evenodd"/>
<path id="Path_78186" data-name="Path 78186" d="M195.141,395.929a1.612,1.612,0,0,0-2.02-2.506l-1.379,1.312a.434.434,0,0,1-.046.042c-1.158,1.164.946,3.816,2.687,1.96Z" transform="translate(-178.283 -314.035)" fill="#fed1a3" fill-rule="evenodd"/>
<path id="Path_78187" data-name="Path 78187" d="M157.165,353.556l-1.457,1.528-.006.008a1.6,1.6,0,0,0,.641,2.43,1.506,1.506,0,0,0,1.8-.238l1.561-.024-.136-1.33.2-.188a1.7,1.7,0,1,0-2.451-2.348Z" transform="translate(-144.732 -276.542)" fill="#fed1a3" fill-rule="evenodd"/>
<path id="Path_78188" data-name="Path 78188" d="M117.194,313.633l-1.365,1.45-.192.206c-1.347,1.43,1.066,3.682,2.457,2.222l.24-.248c.02-.022,1.483-.174,1.643-.192l-.026-1.506.467-.49a1.668,1.668,0,1,0-2.433-2.28Z" transform="translate(-107.364 -238.714)" fill="#fed1a3" fill-rule="evenodd"/>
<path id="Path_78189" data-name="Path 78189" d="M84.37,292.665l-.463.5c-1.425,1.548.7,4.016,2.292,2.588l1.308-.2.058-1.248a1.736,1.736,0,0,0-2.733-2.14Z" transform="translate(-77.733 -219.386)" fill="#fed1a3" fill-rule="evenodd"/>
<path id="Path_78190" data-name="Path 78190" d="M8.593,58.664l-6.338,6.1L.452,66.5A1.487,1.487,0,0,0,.41,68.595L3.1,71.4a.413.413,0,0,0,.581.012l1.076-1.036,7.653-7.366.95-.916a.412.412,0,0,0,.012-.582l-2.689-2.8a1.48,1.48,0,0,0-2.09-.042Z" transform="translate(0 -2.1)" fill="#62a1e2" fill-rule="evenodd"/>
<path id="Path_78191" data-name="Path 78191" d="M275.8,219.46l-1.924.44.755.766,5.829,5.8.04.044a2.113,2.113,0,0,1,.591,2.188,2.005,2.005,0,0,0,.579-3.394l-.04-.044-1.749-1.738Z" transform="translate(-255.151 -152.287)" fill="#f5a96c" fill-rule="evenodd"/>
<path id="Path_78192" data-name="Path 78192" d="M164.832,127.821,163.606,129l3.831,1.89,1.407-1.09Z" transform="translate(-152.422 -66.913)" fill="#f5a96c" fill-rule="evenodd"/>
<path id="Path_78193" data-name="Path 78193" d="M181.692,123.83l-1.407,1.09-1.325,1.024c-3.348,2.592-.571,5.152,1.415,4.274l2.234-1.346,1.383-.316,1.924-.44,4.08,4.058,1.749,1.738c1.1-.412,2.655-1.6,4.334-4.3l-2.552-5.339-5.96-2.855-1.529,1.222c-.252.2-.176.162-.493.152a5.758,5.758,0,0,0-3.463.74Z" transform="translate(-165.271 -60.943)" fill="#fed1a3" fill-rule="evenodd"/>
<path id="Path_78194" data-name="Path 78194" d="M184.454,210.013l-2.234,1.346a2.18,2.18,0,0,1-1.852-.064,2.2,2.2,0,0,0,2.775.954l2.234-1.346,1.383-.316,1.9-.434c-.88-.754-1.038-.864-2.825-.454Z" transform="translate(-168.039 -142.975)" fill="#fed1a3" fill-rule="evenodd"/>
<path id="Path_78195" data-name="Path 78195" d="M371.736,221.713a10.769,10.769,0,0,1-3.954,4.14,3.616,3.616,0,0,1-.347.152l1.357,1.35c1.1-.412,2.655-1.6,4.334-4.3Z" transform="translate(-342.317 -154.386)" fill="#fec58c" fill-rule="evenodd"/>
<path id="Path_78196" data-name="Path 78196" d="M93.99,60.751a1.48,1.48,0,0,1,2.082.048l2.689,2.8a.413.413,0,0,1-.012.582l1.226-1.178.95-.916a.412.412,0,0,0,.012-.582l-2.689-2.8a1.48,1.48,0,0,0-2.09-.042Z" transform="translate(-87.565 -2.099)" fill="#3987d0" fill-rule="evenodd"/>
<path id="Path_78197" data-name="Path 78197" d="M83.448,305.65a1.694,1.694,0,0,0,2.761,1.4l.856-.908.509-.542a1.613,1.613,0,0,0,.072-1.994,1.5,1.5,0,0,1-.383,1l-.906.962-.457.488A1.572,1.572,0,0,1,83.448,305.65Z" transform="translate(-77.744 -230.685)" fill="#fec58c" fill-rule="evenodd"/>
<path id="Path_78198" data-name="Path 78198" d="M116.154,321.3a1.7,1.7,0,0,0,2.791.86l.24-.248a.026.026,0,0,1,.006-.008l1.457-1.528c.052-.058.1-.112.154-.162l.467-.49a1.6,1.6,0,0,0-.192-2.506,1.614,1.614,0,0,1-.465,1.57l-.467.49c-.052.05-.1.1-.154.16v0l-1.457,1.528a.027.027,0,0,1-.008.006l-.238.25a1.527,1.527,0,0,1-2.134.076Z" transform="translate(-108.214 -243.36)" fill="#fec58c" fill-rule="evenodd"/>
<path id="Path_78199" data-name="Path 78199" d="M158.971,363.067l1.064-1.01.315-.3.2-.188a1.637,1.637,0,0,0-.1-2.482,1.555,1.555,0,0,1-.465,1.59l-.2.188-.563.534-.818.78-.046.04a1.509,1.509,0,0,1-1.8.24,1.887,1.887,0,0,1-.367-.262,1.935,1.935,0,0,0,.934,1.152,1.506,1.506,0,0,0,1.8-.238Z" transform="translate(-145.512 -282.368)" fill="#fec58c" fill-rule="evenodd"/>
<path id="Path_78200" data-name="Path 78200" d="M195.153,403.132a1.592,1.592,0,0,0,.072-2.324,1.8,1.8,0,0,1-.539,1.174l-.759.81a1.61,1.61,0,0,1-2.551.024,1.711,1.711,0,0,0,3.018,1.124Z" transform="translate(-178.294 -321.238)" fill="#fec58c" fill-rule="evenodd"/>
<path id="Path_78201" data-name="Path 78201" d="M326.327,321.446a.526.526,0,0,1-.36-.142l-2.128-1.982a.528.528,0,0,1,.72-.774l2.128,1.982a.529.529,0,0,1-.36.915Z" transform="translate(-301.546 -244.471)" fill="#f5a96c"/>
<path id="Path_78202" data-name="Path 78202" d="M296.719,353.78a.526.526,0,0,1-.36-.142l-2.13-1.984a.528.528,0,0,1,.72-.774l2.13,1.984a.529.529,0,0,1-.36.915Z" transform="translate(-273.96 -274.592)" fill="#f5a96c"/>
<path id="Path_78203" data-name="Path 78203" d="M278.211,395.523a.527.527,0,0,1-.359-.141l-1.084-1.008a.528.528,0,0,1,.719-.774l1.084,1.008a.529.529,0,0,1-.36.916Z" transform="translate(-257.691 -314.39)" fill="#f5a96c"/>
<path id="Path_78204" data-name="Path 78204" d="M315.5,60.471l7.122,6.856,1.389,1.338.511.492a.413.413,0,0,0,.581-.012l1.4-1.464,1.286-1.34a1.487,1.487,0,0,0-.042-2.094l-1.244-1.2-6.9-6.64a1.48,1.48,0,0,0-2.09.042l-2.689,2.8a.412.412,0,0,0,.012.582Z" transform="translate(-293.199)" fill="#fc4350" fill-rule="evenodd"/>
<path id="Path_78205" data-name="Path 78205" d="M435.436,151.725v0Zm-1.25,3.428-1.4,1.466a.411.411,0,0,1-.581.01l1.264,1.218.511.492a.413.413,0,0,0,.581-.012l1.4-1.464,1.286-1.34a1.487,1.487,0,0,0-.042-2.094l-1.573-1.514.134.13-.329-.318a1.487,1.487,0,0,1,.036,2.086Z" transform="translate(-402.658 -89.183)" fill="#fb2a39" fill-rule="evenodd"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,733 @@
<!DOCTYPE html>
<html style="box-sizing: border-box;--bs-breakpoint-xs: 0;--bs-breakpoint-sm: 576px;--bs-breakpoint-md: 768px;--bs-breakpoint-lg: 992px;--bs-breakpoint-xl: 1200px;--bs-breakpoint-xxl: 1400px;">
<head style="box-sizing: border-box;">
<meta charset="utf-8" style="box-sizing: border-box;">
<meta content="IE=edge" http-equiv="X-UA-Compatible" style="box-sizing: border-box;">
<title style="box-sizing: border-box;">odoo</title>
<meta content="width=device-width, initial-scale=1" name="viewport" style="box-sizing: border-box;">
</head>
<body style="box-sizing: border-box;margin: 0;font-family: 'Poppins', sans-serif;font-size: var(--bs-body-font-size);font-weight: var(--bs-body-font-weight);line-height: var(--bs-body-line-height);color: var(--bs-body-color);text-align: var(--bs-body-text-align);background-color: var(--bs-body-bg);-webkit-text-size-adjust: 100%;-webkit-tap-highlight-color: transparent;padding: 0;">
<!-- oddo bussiness start -->
<section class="bussiness space"
style="box-sizing: border-box;padding: 70px 0; position: relative; padding:5px;">
<div class="container"
style="box-sizing: border-box;--bs-gutter-x: 1.5rem;--bs-gutter-y: 0;width: 100%;padding-right: calc(var(--bs-gutter-x) * 0.5);padding-left: calc(var(--bs-gutter-x) * 0.5);margin-right: auto;margin-left: auto;max-width: 1440px;margin: 0 auto;">
<div class="row"
style="box-sizing: border-box;--bs-gutter-x: 1.5rem;--bs-gutter-y: 0;display: flex;flex-wrap: wrap;margin-top: calc(-1 * var(--bs-gutter-y));margin-right: calc(-0.5 * var(--bs-gutter-x));margin-left: calc(-0.5 * var(--bs-gutter-x));align-items: center;">
<div class="col-md-7"
style="box-sizing: border-box;flex-shrink: 0;width: 50%; padding-right: calc(var(--bs-gutter-x) * 0.5);padding-left: calc(var(--bs-gutter-x) * 0.5);margin-top: var(--bs-gutter-y);">
<div class="bussiness_content" style="box-sizing: border-box;">
<h1 style="box-sizing: border-box;margin-top: 0;margin-bottom: 10px;font-weight: bold;line-height: 64px;color: #1465b4;font-size: 54px;">
Email OTP Authentication</h1>
<div class="bussiness_text" style="box-sizing: border-box;max-width: 462px;width: 100%;">
<span class="s6"
style="box-sizing: border-box;font-size: 24px;line-height: 35px;font-weight: 600;margin-bottom: 15px;color: #323232;display: block;">Simplifying Authentication with OTP via Email - Experience Seamless Login and Signup</span>
</div>
<div class="bussiness_award" style="box-sizing: border-box;">
<ul style="box-sizing: border-box;padding-left: 2rem;margin-top: 0;margin-bottom: 1rem;list-style-type: none;margin: 0;padding: 0;padding-bottom: 10px;">
<li style="box-sizing: border-box;display: inline-block;padding-right: 15px;"><img
src="image/15.png"
style="box-sizing: border-box;vertical-align: middle;max-width: 100%;height: auto;">
</li>
<li style="box-sizing: border-box;display: inline-block;padding-right: 15px;"><img
src="image/16A.png"
style="box-sizing: border-box;vertical-align: middle;max-width: 100%;height: auto;">
</li>
</ul>
</div>
<div class="bussiness_btn" style="box-sizing: border-box;margin: 15px 0;">
<a class="orange_btn" href="#"
style="box-sizing: border-box;text-decoration: none;color: #ffff;transition: 0.5s all ease-out;-webkit-transition: 0.5s all ease-out;-moz-transition: 0.5s all ease-out;display: inline-block;padding: 13px 17px;font-size: 22px;line-height: 27px;font-weight: 600;background-color: #ff6420;border-radius: 8px;border: 1px solid transparent;position: relative;margin-right: 10px;">Demo
Available</a>
<a class="trans_btn" href="#"
style="box-sizing: border-box;text-decoration: none;color: #ff6420;transition: 0.5s all ease-out;-webkit-transition: 0.5s all ease-out;-moz-transition: 0.5s all ease-out;display: inline-block;padding: 13px 17px;font-size: 22px;line-height: 27px;font-weight: 600;background: transparent;border: 1px solid #ff6420;border-radius: 8px;">30
Days Support</a>
</div>
<div class="bussiness_suggest"
style="box-sizing: border-box;display: flex;align-items: center;justify-content: left;flex-wrap: wrap;">
<div class="suggestion"
style="box-sizing: border-box;display: flex;align-items: center;justify-content: center;padding: 9px 18px;border-radius: 25px;background-color: #1465b4;color: #fff;margin-right: 10px;margin-top: 25px;font-size: 16px;line-height: 22px;">
<div class="sug_icon" style="box-sizing: border-box;padding-right: 6px;">
<i class="fa fa-check-circle" style="box-sizing: border-box;"></i>
</div>
<span style="box-sizing: border-box;font-size: 16px;line-height: 25px;font-weight: 400;">Odoo Community</span>
</div>
<div class="suggestion"
style="box-sizing: border-box;display: flex;align-items: center;justify-content: center;padding: 9px 18px;border-radius: 25px;background-color: #1465b4;color: #fff;margin-right: 10px;margin-top: 25px;font-size: 16px;line-height: 22px;">
<div class="sug_icon" style="box-sizing: border-box;padding-right: 6px;">
<i class="fa fa-check-circle" style="box-sizing: border-box;"></i>
</div>
<span style="box-sizing: border-box;font-size: 16px;line-height: 25px;font-weight: 400;">Odoo Enterprise</span>
</div>
<div class="suggestion"
style="box-sizing: border-box;display: flex;align-items: center;justify-content: center;padding: 9px 18px;border-radius: 25px;background-color: #1465b4;color: #fff;margin-right: 10px;margin-top: 25px;font-size: 16px;line-height: 22px;">
<div class="sug_icon" style="box-sizing: border-box;padding-right: 6px;">
<i class="fa fa-check-circle" style="box-sizing: border-box;"></i>
</div>
<span style="box-sizing: border-box;font-size: 16px;line-height: 25px;font-weight: 400;">Odoo.SH</span>
</div>
</div>
</div>
</div>
<div class="col-md-5"
style="box-sizing: border-box;flex-shrink: 0;width: 50%;padding-right: calc(var(--bs-gutter-x) * 0.5);padding-left: calc(var(--bs-gutter-x) * 0.5);margin-top: var(--bs-gutter-y);">
<div class="bussiness_right" style="box-sizing: border-box;">
<img src="otp_login_laptop.png"
style="border-radius:20px; box-sizing: border-box;vertical-align: middle;max-width: 100%;height: auto;">
</div>
</div>
</div>
</div>
</section>
<!-- oddo bussiness end -->
<!-- consultion start -->
<section class="consultion"
style="box-sizing: border-box; position: relative; margin-top: 100px; background-color: #1465b4; padding:20px;
border-radius: 15px;">
<div style="display:flex;">
<div class="cons_text" style="box-sizing: border-box;max-width: 750px;width: 100%;">
<h2 style="box-sizing: border-box;margin-top: 0;margin-bottom: 2px;font-weight: 400;line-height: 55px;color: var(--bs-heading-color);font-size: 46px;">
<strong style="box-sizing: border-box;font-weight: bolder;color: #ff6420;">CONSULT OUR EXPERTS</strong>
</h2>
<p style="color: white; box-sizing: border-box;margin-top: 0;margin-bottom: 1rem;font-size: 14px;line-height: 18px;font-weight: 600;max-width: 682px;width: 100%;">
Unlock the potential of your business with Silver Touch! Avail a complimentary 1-hour Odoo
consulting session and discover tailored solutions for your unique needs.</p>
<div class="cons_info"
style="box-sizing: border-box;display: flex;align-items: center;justify-content: start;">
<div class="cons_btn" style="box-sizing: border-box;margin-right: 30px;">
<a class="orange_btn" href="mailto:info@silvertouch.com"
style="box-sizing: border-box;text-decoration: none;color: #ffff;transition: 0.5s all ease-out;-webkit-transition: 0.5s all ease-out;-moz-transition: 0.5s all ease-out;display: inline-block;background-color: #ff6420;border-radius: 8px;border: 1px solid transparent;position: relative;padding: 12px 30px;font-size: 16px;line-height: 22px;font-weight: bold;">Let's
Connect</a>
</div>
<div class="cons_con_details"
style="box-sizing: border-box;display: flex;align-items: center;justify-content: center;">
<div style="position: relative; z-index: 2;">
<img src="image/support.svg" style="max-width: 100%; height: auto;">
</div>
<div style="padding: 8px 12px 8px 35px; background: #276CD6; border-radius: 0 19px 19px 0; margin-left: -25px;">
<span style="font-size: 14px; line-height: 18px; display: inline-block; margin-right: 10px;">
<i class="fa fa-envelope" style="color:white"></i>
<a href="mailto:info@silvertouch.com" style="color: #FFF; font-size: 14px; line-height: 18px;
transition: 0.5s all ease-out;">info@silvertouch.com</a>
</span>
<span style="margin-right: 0; font-size: 14px; line-height: 18px; display: inline-block;">
<i class="fa fa-phone" style="color:white"></i>
<a href="skype:+91 79-4002-2770/1/2/3/4?chat" style="color: #FFF;
font-size: 14px;
line-height: 18px; transition: 0.5s all ease-out;">+91 79-4002-2770/1/2/3/4</a>
</span>
</div>
</div>
</div>
</div>
<!-- <div style="margin-top:px; width:108px; height:108px; padding:20px; border-radius:100px; left:45%; ">
<img src="assets/icons/1Hour.png"/>
</div>
</div> -->
<div class="one_hours"
style="margin: 7px 0;background-color: #ffff;width: 130px;height: 130px;border-radius: 100px;padding: 25px 0px 0px 10px;">
<div class="one_hrs_circle"
style="display: flex;text-align: center;">
<span style="color: #ff6420;font-size: 18px;line-height: 22px;font-weight: bold;">
01
<br>
HOUR
<br>
DISCUSSION
</span>
</div>
</div>
</div>
</section>
<!-- consultion end -->
<section class="oe_container" style="margin-top: 2rem !important;">
<div class="container"
style="box-sizing: border-box;--bs-gutter-x: 1.5rem;--bs-gutter-y: 0;width: 100%;padding-right: calc(var(--bs-gutter-x) 0.5);padding-left: calc(var(--bs-gutter-x) 0.5);margin-right: auto;margin-left: auto;max-width: 1440px;margin: 0 auto;">
<div style="width: 100%;">
<img src="assets/icons/footer.png" alt="Silver Touch Footer" width="100%"/>
</div>
</div>
</section>
<!-- features start -->
<section class="features" style="box-sizing: border-box; padding: 70px 0;">
<div class="container"
style="box-sizing: border-box;--bs-gutter-x: 1.5rem;--bs-gutter-y: 0;width: 100%;padding-right: calc(var(--bs-gutter-x) * 0.5);padding-left: calc(var(--bs-gutter-x) * 0.5);margin-right: auto;margin-left: auto;max-width: 1440px;margin: 0 auto;">
<div class="row"
style="box-sizing: border-box;--bs-gutter-x: 1.5rem;--bs-gutter-y: 0;display: flex;flex-wrap: wrap;margin-top: calc(-1 * var(--bs-gutter-y));margin-right: calc(-0.5 * var(--bs-gutter-x));margin-left: calc(-0.5 * var(--bs-gutter-x));">
<div class="col-md-12"
style="box-sizing: border-box;flex-shrink: 0;width: 100%;max-width: 100%;padding-right: calc(var(--bs-gutter-x) * 0.5);padding-left: calc(var(--bs-gutter-x) * 0.5);margin-top: var(--bs-gutter-y);">
<div class="title_text " style="box-sizing: border-box;">
<h3 class="text-center"
style="box-sizing: border-box;margin-top: 0;margin-bottom: 0.5rem;font-weight: 700;line-height: 60px;color: #1465b4;font-size: 40px;text-align: center;">
Features</h3>
<center>
<hr
style="border: 3px solid #000133 !important; background-color: #000133 !important; width: 80px !important; margin-bottom: 2rem !important;"/>
</center>
</div>
<div class="row">
<div class="col-sm-6 mt-3" style="padding:0px; border:1px solid #7e827f; border-radius: 15px;">
<div class="card-deck" style="height:100%">
<div class="card-body" style="height:100%">
<h5 class="card-title" style="color: #1465b4;"><i class="fa fa-arrow-right"
style="margin-right: 10px;"/>Seamless Authentication Experience</h5>
<p class="card-text">Our Odoo module revolutionizes authentication by offering OTP login via email. Users can opt for password or OTP login at the login page, enhancing security and user convenience effortlessly.
</p>
</div>
</div>
</div>
<div class="col-sm-6 mt-3" style="padding:0px; border:1px solid #7e827f; border-radius: 15px;">
<div class="card-deck" style="height:100%">
<div class="card-body" style="height:100%">
<h5 class="card-title" style="color: #1465b4;"><i class="fa fa-arrow-right"
style="margin-right: 10px;"/>Seamless Integration</h5>
<p class="card-text">Our module seamlessly integrates with Odoo's existing authentication system, ensuring compatibility and ease of implementation for users and administrators alike.</p>
</div>
</div>
</div>
<div class="col-sm-6 mt-3" style="padding:0px; border:1px solid #7e827f; border-radius: 15px;">
<div class="card-deck" style="height:100%">
<div class="card-body" style="height:100%">
<h5 class="card-title" style="color: #1465b4;"><i class="fa fa-arrow-right"
style="margin-right: 10px;"/>Time-saving Authentication
</h5>
<p class="card-text">With OTP login, users can swiftly access their accounts without the hassle of remembering complex passwords, saving time and improving user satisfaction.
</p>
</div>
</div>
</div>
<div class="col-sm-6 mt-3" style="padding:0px; border:1px solid #7e827f; border-radius: 15px;">
<div class="card-deck" style="height:100%">
<div class="card-body" style="height:100%">
<h5 class="card-title" style="color: #1465b4;"><i class="fa fa-arrow-right"
style="margin-right: 10px;"/>Email verification</h5>
<p class="card-text">Our module also verifies email address at the time of sign up by sending otp on the given email address, which ensures only users with valid email address gets registered in database</p>
</div>
</div>
</div>
<div class="col-sm-6 mt-3" style="padding:0px; border:1px solid #7e827f; border-radius: 15px;">
<div class="card-deck" style="height:100%">
<div class="card-body" style="height:100%">
<h5 class="card-title" style="color: #1465b4;"><i class="fa fa-arrow-right"
style="margin-right: 10px;"/>Enhanced User Privacy
</h5>
<p class="card-text">With OTP-based authentication, our module prioritizes user privacy by reducing the reliance on passwords. OTPs sent to their email addresses ensure secure access to accounts, minimizing the risk of unauthorized access or data breaches.
</p>
</div>
</div>
</div>
<div class="col-sm-6 mt-3" style="padding:0px; border:1px solid #7e827f; border-radius: 15px;">
<div class="card-deck" style="height:100%">
<div class="card-body" style="height:100%">
<h5 class="card-title" style="color: #1465b4;"><i class="fa fa-arrow-right"
style="margin-right: 10px;"/>Flexible Login Options</h5>
<p class="card-text">Our module offers flexibility in login methods, allowing users to choose between password-based or OTP-based login. This adaptability caters to diverse user preferences and enhances accessibility while maintaining robust security protocols.</p>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row" style="margin-top:25px">
<div class="col-5"
style="padding-left: 16%; padding-top:13px; padding-bottom:13px; color: #ff6420; display: inline-block; border-radius: 8px;font-size: 20px;line-height: 18px;font-weight:bold;">
Website : <span
style="font-size:14px; padding:8px; border-radius:15px; color:white; background-color:#1465b4">www.silvertouch.com</span>
</div>
<div class="col-1"></div>
<div class="col-5" style="padding:0px 0px">
<div style="width:100%; padding:13px 0px; color: #ff6420; border-radius: 8px;font-size: 20px;line-height: 18px;font-weight:bold;">
For any queries : <span
style="font-size:14px; padding:8px; border-radius:15px; color:white; background-color:#1465b4">www.silvertouch.com/contact</span>
</div>
</div>
</div>
</div>
</section>
<!-- features end -->
<!-- screenshots start -->
<section class="screenshots"
style="box-sizing: border-box;background-color: #e1f0ed; padding: 70px 0; border-radius:15px;">
<div class="container"
style="box-sizing: border-box;--bs-gutter-x: 1.5rem;--bs-gutter-y: 0;width: 100%;padding-right: calc(var(--bs-gutter-x) * 0.5);padding-left: calc(var(--bs-gutter-x) * 0.5);margin-right: auto;margin-left: auto;max-width: 1440px;margin: 0 auto;">
<div class="row"
style="box-sizing: border-box;--bs-gutter-x: 1.5rem;--bs-gutter-y: 0;display: flex;flex-wrap: wrap;margin-top: calc(-1 * var(--bs-gutter-y));margin-right: calc(-0.5 * var(--bs-gutter-x));margin-left: calc(-0.5 * var(--bs-gutter-x));">
<div class="col-md-12"
style="box-sizing: border-box;flex-shrink: 0;width: 100%;max-width: 100%;padding-right: calc(var(--bs-gutter-x) * 0.5);padding-left: calc(var(--bs-gutter-x) * 0.5);margin-top: var(--bs-gutter-y);">
<div class="title_text" style="box-sizing: border-box;">
<h3 class="text-center"
style="box-sizing: border-box;margin-top: 0;margin-bottom: 0.5rem;font-weight: 700;line-height: 60px;color: #1465b4;font-size: 40px;text-align: center;">
App Preview</h3>
<center>
<hr
style="border: 3px solid #000133 !important; background-color: #000133 !important; width: 80px !important; margin-bottom: 2rem !important;"/>
</center>
<div class="mb-4 position-relative" style="border-radius:10px">
<div class="p-md-5 p-3 position-relative">
<div class="row ">
<div class="col-md-4 d-flex align-items-center">
<div class="pl-md-2 pl-0">
<h3 class="mb-3"
style="font-family:'Inter', sans-serif; font-style:normal; font-weight:bold; font-size:calc(1.1rem + 1vw); line-height:103%; text-transform:capitalize; color:#2B5FB2">
YouTube Preview<br/>
<span style="color:#333333; font-size:calc(1.1rem + 1vw)"></span>
</h3>
<p style="font-family:Inter; font-style:normal; font-weight:500; font-size:16px; line-height:150%; letter-spacing:0.02em; color:#535456">
Witness the power of our module through the comprehensive
demonstration in the linked YouTube video.
</p>
</div>
</div>
<div class="col-md-8">
<div class="d-inline-block text-center p-3 shadow-sm"
style="background-color:#fff; border-radius:10px">
<!-- <video class="img-fluid"-->
<!-- src="email_otp_authentication.mkv" controls width="725px"/>-->
<a href="https://www.youtube.com/watch?v=hFF35idmPQY" target="new">
<img width="625px" height="350px" src="email_otp.png"/>
</a>
</div>
</div>
</div>
</div>
</div>
<div class="mb-4 position-relative" style="border-radius:10px">
<div class="p-md-5 p-3 position-relative">
<div class="row ">
<div class="col-md-8">
<div class="d-inline-block text-center p-3 shadow-sm"
style="background-color:#fff; border-radius:10px">
<img class="img-fluid"
src="signup.png" width="725px"/>
</div>
</div>
<div class="col-md-4 d-flex align-items-center">
<div class="pl-md-2 pl-0">
<h3 class="mb-3"
style="font-family:'Inter', sans-serif; font-style:normal; font-weight:bold; font-size:calc(1.1rem + 1vw); line-height:103%; text-transform:capitalize; color:#2B5FB2">
Sign Up<br/>
<span style="color:#333333; font-size:calc(1.1rem + 1vw)"></span>
</h3>
<p style="font-family:Inter; font-style:normal; font-weight:500; font-size:16px; line-height:150%; letter-spacing:0.02em; color:#535456">
Enter user's credentials and click on send otp.
</p>
</div>
</div>
</div>
</div>
</div>
<div class="mb-4 position-relative" style="border-radius:10px">
<div class="p-md-5 p-3 position-relative">
<div class="row ">
<div class="col-md-4 d-flex align-items-center">
<div class="pl-md-2 pl-0">
<h2 class="mb-3"
style="font-family:'Inter', sans-serif; font-style:normal; font-weight:bold; font-size:calc(1.1rem + 1vw); line-height:103%; text-transform:capitalize; color:#2B5FB2">
OTP
<span style="color:#333333; font-size:calc(1.1rem + 1vw)"> </span>
</h2>
<p style="font-family:Inter; font-style:normal; font-weight:500; font-size:16px; line-height:150%; letter-spacing:0.02em; color:#535456">
Enter the otp received on user's entered email address.
</p>
</div>
</div>
<div class="col-md-8">
<div class="d-inline-block text-center p-3 shadow-sm"
style="background-color:#fff; border-radius:10px">
<img class="img-fluid"
src="signup_otp.png" width="725px"/>
</div>
</div>
</div>
</div>
</div>
<div class="mb-4 position-relative" style="border-radius:10px">
<div class="p-md-5 p-3 position-relative">
<div class="row ">
<div class="col-md-8">
<div class="d-inline-block text-center p-3 shadow-sm"
style="background-color:#fff; border-radius:10px">
<img class="img-fluid"
src="otp_login_link.png" width="725px"/>
</div>
</div>
<div class="col-md-4 d-flex align-items-center">
<div class="pl-md-2 pl-0">
<h3 class="mb-3"
style="font-family:'Inter', sans-serif; font-style:normal; font-weight:bold; font-size:calc(1.1rem + 1vw); line-height:103%; text-transform:capitalize; color:#2B5FB2">
OTP Login link<br/>
<span style="color:#333333; font-size:calc(1.1rem + 1vw)"></span>
</h3>
<p style="font-family:Inter; font-style:normal; font-weight:500; font-size:16px; line-height:150%; letter-spacing:0.02em; color:#535456">
<b>Login with OTP</b> link will redirect to use otp login functionality(User can also use default odoo login functionality).
</p>
</div>
</div>
</div>
</div>
</div>
<div class="mb-4 position-relative" style="border-radius:10px">
<div class="p-md-5 p-3 position-relative">
<div class="row ">
<div class="col-md-4 d-flex align-items-center">
<div class="pl-md-2 pl-0">
<h2 class="mb-3"
style="font-family:'Inter', sans-serif; font-style:normal; font-weight:bold; font-size:calc(1.1rem + 1vw); line-height:103%; text-transform:capitalize; color:#2B5FB2">
OTP Login page
<span style="color:#333333; font-size:calc(1.1rem + 1vw)"> </span>
</h2>
<p style="font-family:Inter; font-style:normal; font-weight:500; font-size:16px; line-height:150%; letter-spacing:0.02em; color:#535456">
Enter user's registered email address here.
</p>
</div>
</div>
<div class="col-md-8">
<div class="d-inline-block text-center p-3 shadow-sm"
style="background-color:#fff; border-radius:10px">
<img class="img-fluid"
src="login.png" width="725px"/>
</div>
</div>
</div>
</div>
</div>
<div class="mb-4 position-relative" style="border-radius:10px">
<div class="p-md-5 p-3 position-relative">
<div class="row ">
<div class="col-md-8">
<div class="d-inline-block text-center p-3 shadow-sm"
style="background-color:#fff; border-radius:10px">
<img class="img-fluid"
src="login_otp.png" width="725px"/>
</div>
</div>
<div class="col-md-4 d-flex align-items-center">
<div class="pl-md-2 pl-0">
<h3 class="mb-3"
style="font-family:'Inter', sans-serif; font-style:normal; font-weight:bold; font-size:calc(1.1rem + 1vw); line-height:103%; text-transform:capitalize; color:#2B5FB2">
OTP Login<br/>
<span style="color:#333333; font-size:calc(1.1rem + 1vw)"></span>
</h3>
<p style="font-family:Inter; font-style:normal; font-weight:500; font-size:16px; line-height:150%; letter-spacing:0.02em; color:#535456">
Enter the otp received on user's entered email address.
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- screenshots end -->
<!-- other apps start -->
<section class="other_app space" style="box-sizing: border-box;padding: 35px 0;">
<div class="container"
style="box-sizing: border-box;--bs-gutter-x: 1.5rem;--bs-gutter-y: 0;width: 100%;padding-right: calc(var(--bs-gutter-x) 0.5);padding-left: calc(var(--bs-gutter-x) 0.5);margin-right: auto;margin-left: auto;max-width: 1440px;margin: 0 auto;">
<div class="row"
style="box-sizing: border-box;--bs-gutter-x: 1.5rem;--bs-gutter-y: 0;display: flex;flex-wrap: wrap;margin-top: calc(-1 var(--bs-gutter-y));margin-right: calc(-0.5 var(--bs-gutter-x));margin-left: calc(-0.5 * var(--bs-gutter-x));">
<div class="col-md-12"
style="box-sizing: border-box;flex-shrink: 0;width: 100%;max-width: 100%;padding-right: calc(var(--bs-gutter-x) 0.5);padding-left: calc(var(--bs-gutter-x) 0.5);margin-top: var(--bs-gutter-y);">
<div class="title_text" style="box-sizing: border-box;margin-bottom: 15px;">
<h3 class="text-center"
style="box-sizing: border-box;margin-top: 0;margin-bottom: 0.5rem;font-weight: 700;line-height: 60px;color: #1465b4;font-size: 40px;text-align: center;">
Other Apps By Silver Touch</h3>
<center>
<hr
style="border: 3px solid #000133 !important; background-color: #000133 !important; width: 80px !important; margin-bottom: 2rem !important;"/>
</center>
</div>
</div>
<div class="col-md-3"
style="width:25%; padding-right:calc(var(--bs-gutter-x) 0.5); padding-left:calc(var(--bs-gutter-x) 0.5); margin-top:var(--bs-gutter-y)">
<div class="app_sec" style="margin:16px 0">
<div class="app_img" style="border-radius:12px; min-height:170px; margin:0 10px">
<a href="https://apps.odoo.com/apps/modules/16.0/sttl_email_validation">
<img src="app_images/email_validation.png"
style="vertical-align:middle; height:auto; max-width:100%; width:100%"></a>
</div>
<div class="app_text">
<span class="text"
style="text-align:center; font-size:20px; line-height:30px; font-weight:600; display:block; color:black">Email Validation</span>
</div>
</div>
</div>
<div class="col-md-3"
style="width:25%; padding-right:calc(var(--bs-gutter-x) 0.5); padding-left:calc(var(--bs-gutter-x) 0.5); margin-top:var(--bs-gutter-y)">
<div class="app_sec" style="margin:16px 0">
<div class="app_img" style="border-radius:12px; min-height:170px; margin:0 10px">
<a href="https://apps.odoo.com/apps/modules/16.0/sttl_timesheet_calendar">
<img src="app_images/timesheet_calendar.png"
style="vertical-align:middle; height:auto; max-width:100%; width:100%">
</a>
</div>
<div class="app_text">
<span class="text"
style="text-align:center; font-size:20px; line-height:30px; font-weight:600; display:block; color:black">Timesheet Calender</span>
</div>
</div>
</div>
<div class="col-md-3"
style="width:25%; padding-right:calc(var(--bs-gutter-x) 0.5); padding-left:calc(var(--bs-gutter-x) 0.5); margin-top:var(--bs-gutter-y)">
<div class="app_sec" style="margin:16px 0">
<div class="app_img" style="border-radius:12px; min-height:170px; margin:0 10px">
<a href="https://apps.odoo.com/apps/modules/16.0/sttl_timesheet_forecasting">
<img src="app_images/timesheet_forecasting.png"
style="vertical-align:middle; height:auto; max-width:100%; width:100%">
</a>
</div>
<div class="app_text">
<span class="text"
style="text-align:center; font-size:20px; line-height:30px; font-weight:600; display:block; color:black">Timesheet Forecasting </span>
</div>
</div>
</div>
<div class="col-md-3"
style="width:25%; padding-right:calc(var(--bs-gutter-x) 0.5); padding-left:calc(var(--bs-gutter-x) 0.5); margin-top:var(--bs-gutter-y)">
<div class="app_sec" style="margin:16px 0">
<div class="app_img" style="border-radius:12px; min-height:170px; margin:0 10px">
<a href="https://apps.odoo.com/apps/modules/16.0/sttl_product_description/">
<img src="app_images/product_description.png"
style="vertical-align:middle; height:auto; max-width:100%; width:100%">
</a>
</div>
<div class="app_text">
<span class="text"
style="text-align:center; font-size:20px; line-height:30px; font-weight:600; display:block; color:black">AI Product Description</span>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-4"/>
<a href="https://apps.odoo.com/apps/modules/browse?search=silver+touch" target="_blank"
class="col-md-3 btn btn-block mb-2 deep_hover"
style="color: #ffff; margin-left:28px; transition: 0.5s all ease-out;-webkit-transition: 0.5s all ease-out;-moz-transition: 0.5s all ease-out;display: inline-block;background-color: #ff6420;border-radius: 8px;border: 1px solid transparent;position: relative;padding: 12px 20px;font-size: 19px;line-height: 18px;font-weight:bold;">All
Silver Touch Apps</a>
<div class="col-md-5"/>
</div>
</div>
</section>
<!-- other apps end -->
<!-- service start -->
<section class="service" style="box-sizing: border-box;background-color: #e1f0ed;padding: 70px 0; border-radius:15px;">
<div class="container"
style="box-sizing: border-box;--bs-gutter-x: 1.5rem;--bs-gutter-y: 0;width: 100%;padding-right: calc(var(--bs-gutter-x) * 0.5);padding-left: calc(var(--bs-gutter-x) * 0.5);margin-right: auto;margin-left: auto;max-width: 1440px;margin: 0 auto;">
<div class="row"
style="box-sizing: border-box;--bs-gutter-x: 1.5rem;--bs-gutter-y: 0;display: flex;flex-wrap: wrap;margin-top: calc(-1 * var(--bs-gutter-y));margin-right: calc(-0.5 * var(--bs-gutter-x));margin-left: calc(-0.5 * var(--bs-gutter-x));">
<div class="col-md-12"
style="box-sizing: border-box;flex-shrink: 0;width: 100%;max-width: 100%;padding-right: calc(var(--bs-gutter-x) * 0.5);padding-left: calc(var(--bs-gutter-x) * 0.5);margin-top: var(--bs-gutter-y);">
<div class="title_text" style="box-sizing: border-box;">
<h3 class="text-center"
style="box-sizing: border-box;margin-top: 0;margin-bottom: 0.5rem;font-weight: 700;line-height: 60px;color: #1465b4;font-size: 40px;text-align: center;">
Our Expertise</h3>
<center>
<hr
style="border: 3px solid #000133 !important; background-color: #000133 !important; width: 80px !important; margin-bottom: 2rem !important;"/>
</center>
</div>
<img src="assets/icons/services1.png" style="width: 99.8%;"/>
</div>
</div>
<div class="row">
<div class="col-5"/>
<a href="mailto:info@silvertouch.com"
class="col-2 btn btn-block mb-2 deep_hover"
style="color: #ffff; transition: 0.5s all ease-out;-webkit-transition: 0.5s all ease-out;-moz-transition: 0.5s all ease-out;display: inline-block;background-color: #ff6420;border-radius: 8px;border: 1px solid transparent;position: relative;padding: 12px 20px;font-size: 19px;line-height: 18px;font-weight:bold;">
Get in Touch
</a>
<div class="col-5"/>
</div>
</div>
</section>
<!-- service end -->
<!-- industries start -->
<!--industries 2.0 start-->
<section class="oe_container" style="margin-top: 6rem !important; margin-right:10px; margin-left:10px">
<div class="container"
style="--bs-gutter-x: 1.5rem;--bs-gutter-y: 0;width: 100%;padding-right: calc(var(--bs-gutter-x) 0.5);padding-left: calc(var(--bs-gutter-x) 0.5);margin-right: auto;margin-left: auto;max-width: 1440px;margin: 0 auto;">
<div class="row"
style="box-sizing: border-box;--bs-gutter-x: 1.5rem;--bs-gutter-y: 0;display: flex;flex-wrap: wrap;margin-top: calc(-1 var(--bs-gutter-y));margin-right: calc(-0.5 var(--bs-gutter-x));margin-left: calc(-0.5 * var(--bs-gutter-x));">
<div class="col-md-12"
style="box-sizing: border-box;flex-shrink: 0;width: 100%;max-width: 100%;padding-right: calc(var(--bs-gutter-x) 0.5);padding-left: calc(var(--bs-gutter-x) 0.5);margin-top: var(--bs-gutter-y);">
<div class="title_text" style="box-sizing: border-box;">
<h3 class="text-center"
style="box-sizing: border-box;margin-top: 0;margin-bottom: 0.5rem;font-weight: 700;line-height: 60px;color: #1465b4;font-size: 40px;text-align: center;">
Industries</h3>
<center>
<hr
style="border: 3px solid #000133 !important; background-color: #000133 !important; width: 80px !important; margin-bottom: 2rem !important;"/>
</center>
</div>
</div>
</div>
<div class="row" style="background-color: #f4f4f4">
<div class="col-5" style="background-color: #f4f4f4">
<div>
<p style="color:rgb(30,30,30); margin-top:30px; font-size:25px; font-weight:bold; margin-left:30px">
Industries we grow</p>
</div>
<div>
<p style="width:75%; margin-top:10px; font-size:17px; color:#347cc9; margin-left:30px">Tech
Solutions for Industries to improve the way they operate.</p>
</div>
<div>
<p style="width:95%; margin-top:10px; font-size:15px; margin-left:30px">Every industry has special
needs and all those special needs ask for a special solution.
At Silvertouch we offer solutions that are especially built to aid special characteristics
of that specific industry.</p>
</div>
</div>
<div class="col-7" style="background-color: #f4f4f4;">
<img src="assets/icons/industry.png" alt="Industries Icons" width="100%">
</div>
</div>
</div>
</section>
<!--industries 2.0 end-->
<!-- industries end -->
<!-- contact details start -->
<section class="oe_container" id="presense" style="margin-top: 6rem !important;">
<div class="container"
style="box-sizing: border-box;--bs-gutter-x: 1.5rem;--bs-gutter-y: 0;width: 100%;padding-right: calc(var(--bs-gutter-x) 0.5);margin-right: auto;max-width: 1440px;">
<div class="row"
style="box-sizing: border-box;--bs-gutter-x: 1.5rem;--bs-gutter-y: 0;display: flex;flex-wrap: wrap;margin-top: calc(-1 var(--bs-gutter-y));margin-right: calc(-0.5 var(--bs-gutter-x));margin-left: calc(-0.5 * var(--bs-gutter-x));">
<div class="col-md-12"
style="box-sizing: border-box;flex-shrink: 0;width: 100%;max-width: 100%;padding-right: calc(var(--bs-gutter-x) 0.5);padding-left: calc(var(--bs-gutter-x) 0.5);margin-top: var(--bs-gutter-y);">
<div class="title_text" style="box-sizing: border-box;">
<h3 class="text-center"
style="box-sizing: border-box;margin-top: 0;margin-bottom: 0.5rem;font-weight: 700;line-height: 60px;color: #1465b4;font-size: 40px;text-align: center;">
Our Presence</h3>
<center>
<hr
style="border: 3px solid #000133 !important; background-color: #000133 !important; width: 80px !important; margin-bottom: 2rem !important;"/>
</center>
</div>
</div>
</div>
<div class="row">
<div class="col-md-3"
style="padding:10px; font-size: 14px; border: 1px solid #cacccc; border-width: 0 1px 0 0">
<div style="padding:10px;">
<div style="display:flex;">
<img src="image/india-flag.jpg" style="border-radius: 15px; width:32px; height:34px;"/>
<p class="mx-2">
<p style="font-size: 24px; font-weight:bold;">India</p></p>
</div>
<p style="font-weight: bold;">Silver Touch Technologies Limited</p>
<p style="padding-top:10px">2nd Floor, Saffron Tower, Opp. Central Mall, Panchvati Cross Road,
Ahmedabad - 380006 Gujarat,
India</p>
<div style="padding-top:5px; "><i class="fa fa-phone" style="margin-right:10px;"></i><a
href="tel:+91 79-4002-2770/1/2/3/4"
style="box-sizing: border-box;text-decoration: none;-webkit-transition: 0.5s all ease;-moz-transition: 0.5s all ease;transition: 0.5s all ease-out;font-size: 14px;line-height: 18px;">
+91 79-4002-2770/1/2/3/4</a></div>
<div><i class="fa fa-envelope" style="margin-right:10px;"></i><a href="mailto:info@silvertouch.com">info@silvertouch.com</a>
</div>
<div><i class="fa fa-globe" style="margin-right:10px;"></i><a href="https://www.silvertouch.com/">https://www.silvertouch.com</a>
</div>
</div>
</div>
<div class="col-md-3"
style="padding:10px; font-size: 14px; border: 1px solid #cacccc; border-width: 0 1px 0 0">
<div style="padding:10px;">
<div style="display:flex;">
<img src="image/united-state-flag.jpg" style="border-radius: 15px; width:32px; height:34px;"/>
<p class="mx-2">
<p style="font-size: 24px; font-weight:bold;">USA</p></p>
</div>
<p style="font-weight: bold;">Silver Touch Technologies INC</p>
<p style="padding-top:16px; ">1149 Green Street, Iselin, NJ 08830, United States of America</p>
<div style="padding-top:20px; "><i class="fa fa-phone" style="margin-right:10px;"></i><a
href="tel:+1 (201) 331-9818"
style="box-sizing: border-box;text-decoration: none;-webkit-transition: 0.5s all ease;-moz-transition: 0.5s all ease;transition: 0.5s all ease-out;font-size: 14px;line-height: 18px;">
+1 (201) 331-9818
</a></div>
<div><i class="fa fa-envelope" style="margin-right:10px;"></i><a href="mailto:info@silvertouch.com">info@silvertouch.com</a>
</div>
<div><i class="fa fa-globe" style="margin-right:10px;"></i><a
href="https://www.silvertouchinc.com/">https://www.silvertouchinc.com</a></div>
</div>
</div>
<div class="col-md-3"
style="padding:10px; font-size: 14px; border: 1px solid #cacccc; border-width: 0 1px 0 0">
<div style="padding:10px;">
<div style="display:flex;">
<img src="image/uk-flag-img.jpg" style="border-radius: 15px; width:32px; height:34px;"/>
<p class="mx-2">
<p style="font-size: 24px; font-weight:bold;">UK</p></p>
</div>
<p style="font-weight: bold;">Silver Touch Technologies UK Limited</p>
<p style="padding-top:16px; ">4th Floor, Victoria House, Victoria Road, Chelmsford, Essex,
United Kingdom - CM1 1JR</p>
<div><i class="fa fa-phone" style="margin-right:10px;"></i><a
href="tel:+44 203-872-5140"
style="box-sizing: border-box;text-decoration: none;-webkit-transition: 0.5s all ease;-moz-transition: 0.5s all ease;transition: 0.5s all ease-out;font-size: 14px;line-height: 18px;">
+44 203-872-5140
</a></div>
<div><i class="fa fa-envelope" style="margin-right:10px;"></i><a
href="mailto:info@silvertouchtech.co.uk">info@silvertouchtech.co.uk
</a>
</div>
<div><i class="fa fa-globe" style="margin-right:10px;"></i><a
href="https://www.silvertouchtech.co.uk/">https://www.silvertouchtech.co.uk</a></div>
</div>
</div>
<div class="col-md-3" style="padding:10px; font-size: 14px;">
<div style="padding:10px;">
<div style="display:flex;">
<img src="image/canada-flag.png" style="border-radius: 15px; width:32px; height:34px;"/>
<p class="mx-2">
<p style="font-size: 24px; font-weight:bold;">Canada</p></p>
</div>
<p style="font-weight: bold;">Silver Touch Technologies CA</p>
<p style="padding-top:16px; ">55 Albert Street, Suite 100,
Markham, ON, L3P 2T4,
Canada</p>
<div style="padding-top:20px; "><i class="fa fa-phone" style="margin-right:10px;"></i><a
href="tel:+1 (647) 829-2482"
style="box-sizing: border-box;text-decoration: none;-webkit-transition: 0.5s all ease;-moz-transition: 0.5s all ease;transition: 0.5s all ease-out;font-size: 14px;line-height: 18px;">
+1 (647) 829-2482</a></div>
<div><i class="fa fa-envelope" style="margin-right:10px;"></i><a href="mailto:info@silvertouch.ca">info@silvertouch.ca
</a>
</div>
<div><i class="fa fa-globe" style="margin-right:10px;"></i><a
href="https://www.silvertouch.ca/">https://www.silvertouch.ca</a></div>
</div>
</div>
</div>
</div>
</section>
<!-- contact details end -->
<!-- Footer Logos Start -->
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

View File

@ -0,0 +1,109 @@
<odoo>
<template id="custom_login_otp_checkbox_template" inherit_id="web.login">
<xpath expr="//input[@name='password']" position="after">
<div class="o_login_otp_container">
<div style="margin-top:10px; display:flex;">
<a t-attf-href="/otp/login" class="btn btn-link" role="button">
Login with OTP
</a>
</div>
</div>
</xpath>
<xpath expr="//form[1]" position="attributes">
<attribute name="t-if">not otp_login</attribute>
</xpath>
</template>
<template id="custom_login_template" inherit_id="web.login">
<xpath expr="//form[1]" position="after">
<form t-if="otp_login and not otp" id="form_otp_login" class="oe_login_form" role="form"
t-attf-action="/otp/login" method="post">
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
<div class="form-group field-login">
<label for="login">Email</label>
<input type="text" placeholder="Email" name="login" t-att-value="login" id="login"
required="required"
t-attf-class="form-control #{'form-control-sm' if form_small else ''}"
autofocus="autofocus" autocapitalize="off"/>
</div>
<p t-if="login_error" class="alert alert-danger" role="alert">
The Email entered is incorrect.
</p>
<div t-attf-class="clearfix oe_login_buttons text-center mb-1 {{'pt-2' if form_small else 'pt-3'}}">
<button type="submit" class="btn btn-primary btn-block">Send OTP</button>
</div>
<div style="margin-top:10px; display:flex;">
<a t-attf-href="/web/login" class="btn btn-link" role="button">
Login with Password
</a>
</div>
</form>
<form t-if="otp and otp_no" id="form_otp" class="oe_login_form" role="form"
t-attf-action="/otp/verify" method="post">
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
<div class="form-group">
<label for="otp">OTP</label>
<input type="text" placeholder="OTP" name="otp" id="otp"
t-attf-class="form-control #{'form-control-sm' if form_small else ''}" required="required"/>
<input t-att-value="login" id="login" type="hidden" name="login"/>
</div>
<div t-attf-class="clearfix oe_login_buttons text-center mb-1 {{'pt-2' if form_small else 'pt-3'}}">
<button type="submit" class="btn btn-primary btn-block">Verify OTP</button>
</div>
<div class="text-center mt-2">
<a href="/web/login" class="btn btn-link btn-sm">Back to Login</a>
</div>
<div t-if="otp_no" class="alert alert-info mt-3 text-center">
<b>Development OTP:</b> <t t-esc="otp_no"/>
</div>
</form>
<form t-if="otp and not otp_no" id="form_resend_otp" class="oe_login_form" role="form"
t-attf-action="/otp/login" method="post">
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
<label for="otp">OTP</label>
<input type="text" placeholder="OTP" name="otp" id="otp"
t-attf-class="form-control #{'form-control-sm' if form_small else ''}"/>
<input t-att-value="login" id="login" type="hidden" name="login"/>
<p class="alert alert-danger" role="alert">
The OTP entered is incorrect. Please use the below button to resend the OTP.
</p>
<div t-attf-class="clearfix oe_login_buttons text-center mb-1 {{'pt-2' if form_small else 'pt-3'}}">
<button type="submit" class="btn btn-primary btn-block">Resend OTP</button>
</div>
</form>
</xpath>
</template>
<template id="custom_login_footer_links" inherit_id="web.login" name="Custom Login with Signup Link">
<xpath expr="//form" position="after">
<div t-if="not otp_login and not otp" class="text-center mt-3">
<a href="/web/signup/otp" class="btn btn-link">Don't have an account?</a>
<a t-if="reset_password_enabled"
t-attf-href="/web/reset_password?{{ keep_query() }}" class="btn btn-link">
Reset Password
</a>
</div>
</xpath>
</template>
</odoo>

View File

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="custom_otp_signup" inherit_id="auth_signup.signup">
<xpath expr="//form" position="replace">
<form t-if="not otp" t-attf-action="/web/signup/otp" method="post" class="oe_signup_form">
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
<div class="mb-3">
<label for="name" class="col-form-label">Full Name</label>
<input type="text" name="name" id="name" class="form-control" required="required"
placeholder="Enter your full name" t-att-value="name"/>
</div>
<div class="mb-3">
<label for="login" class="col-form-label">Email</label>
<input type="email" name="login" id="login" class="form-control" required="required"
placeholder="Enter your email" t-att-value="login"/>
</div>
<div class="mb-3">
<label for="password" class="col-form-label">Password</label>
<input type="password" name="password" id="password" class="form-control" required="required"
placeholder="Enter password" t-att-value="password"/>
</div>
<div class="mb-3">
<label for="confirm_password" class="col-form-label">Confirm Password</label>
<input type="password" name="confirm_password" id="confirm_password" class="form-control"
required="required" placeholder="Confirm password"
t-att-value="confirm_password"/>
</div>
<p t-if="error" class="alert alert-danger" role="alert">
<t t-esc="error"/>
</p>
<div class="d-grid text-center mt-3">
<button type="submit" class="btn btn-primary w-100">Sign up with OTP</button>
</div>
<div class="text-center mt-2">
<a href="/web/login" class="btn btn-link btn-sm">Back to Login</a>
</div>
</form>
<form t-if="otp" t-attf-action="/web/signup/otp/verify" method="post" class="oe_signup_form">
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
<input type="hidden" name="login" t-att-value="login"/>
<input type="hidden" name="name" t-att-value="name"/>
<input type="hidden" name="password" t-att-value="password"/>
<input type="hidden" name="confirm_password" t-att-value="confirm_password"/>
<div class="mb-3 text-center">
<label for="otp" class="col-form-label">Enter OTP</label>
<input type="text" name="otp" id="otp" class="form-control text-center"
placeholder="Enter the OTP code" required="required"/>
</div>
<p t-if="error" class="alert alert-danger" role="alert">
<t t-esc="error"/>
</p>
<div t-if="otp_no" class="alert alert-info text-center">
🔐 Your OTP code: <b><t t-esc="otp_no"/></b>
</div>
<div class="d-grid text-center mt-3">
<button type="submit" class="btn btn-success w-100">Verify Create Account</button>
</div>
<div class="text-center mt-2">
<a href="/web/signup/otp" class="btn btn-link btn-sm">Back to Sign up</a>
</div>
</form>
</xpath>
</template>
</odoo>

View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="otp_verification_view_form" model="ir.ui.view">
<field name="name">otp_verification_view_form</field>
<field name="model">otp.verification</field>
<field name="arch" type="xml">
<form string="otp_verification_form">
<sheet>
<group>
<field name="otp"/>
<field name="state"/>
<field name="email"/>
</group>
</sheet>
</form>
</field>
</record>
<record id="otp_verification_view_tree" model="ir.ui.view">
<field name="name">otp_verification_view_tree</field>
<field name="model">otp.verification</field>
<field name="arch" type="xml">
<list string="_tree">
<field name="otp"/>
<field name="state"/>
<field name="email"/>
</list>
</field>
</record>
<record id="otp_verification_action" model="ir.actions.act_window">
<field name="name">OTP Verification</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">otp.verification</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem
id="otp_verify_menu"
name="OTP VERIFICATION"
parent="base.menu_users"
action="otp_verification_action"
groups="otp_verification_access"
sequence="0"/>
</data>
</odoo>