add Twig templates

This commit is contained in:
Daniel Seifert 2024-09-06 07:54:55 +02:00
parent 43e8291bf9
commit bedb60928d
25 changed files with 1284 additions and 20 deletions

View File

@ -15,6 +15,7 @@ declare(strict_types=1);
namespace D3\Totp\Application\Controller\Admin;
use D3\Totp\Application\Model\Constants;
use D3\Totp\Application\Model\d3backupcodelist;
use D3\Totp\Application\Model\d3totp;
use D3\Totp\Application\Model\d3totp_conf;
@ -32,7 +33,7 @@ use Psr\Log\LoggerInterface;
class d3totpadminlogin extends AdminController
{
protected $_sThisTemplate = 'd3totpadminlogin.tpl';
protected $_sThisTemplate = '@'.Constants::OXID_MODULE_ID.'/wave/d3totpadminlogin';
/**
* @return bool

View File

@ -15,6 +15,7 @@ declare(strict_types=1);
namespace D3\Totp\Application\Controller\Admin;
use D3\Totp\Application\Model\Constants;
use D3\Totp\Application\Model\d3totp;
use D3\Totp\Application\Model\d3backupcodelist;
use D3\Totp\Modules\Application\Model\d3_totp_user;
@ -30,7 +31,7 @@ class d3user_totp extends AdminDetailsController
{
protected $_sSaveError = null;
protected $_sThisTemplate = 'd3user_totp.tpl';
protected $_sThisTemplate = '@'.Constants::OXID_MODULE_ID.'/admin/d3user_totp';
public $aBackupCodes = [];

View File

@ -15,6 +15,7 @@ declare(strict_types=1);
namespace D3\Totp\Application\Controller;
use D3\Totp\Application\Model\Constants;
use D3\Totp\Application\Model\d3backupcodelist;
use D3\Totp\Application\Model\d3totp;
use D3\Totp\Modules\Application\Model\d3_totp_user;
@ -27,7 +28,7 @@ use OxidEsales\Eshop\Core\UtilsView;
class d3_account_totp extends AccountController
{
protected $_sThisTemplate = 'd3_account_totp.tpl';
protected $_sThisTemplate = '@'.Constants::OXID_MODULE_ID.'/wave/d3_account_totp';
public $aBackupCodes = [];

View File

@ -15,6 +15,7 @@ declare(strict_types=1);
namespace D3\Totp\Application\Controller;
use D3\Totp\Application\Model\Constants;
use D3\Totp\Application\Model\d3backupcodelist;
use D3\Totp\Application\Model\d3totp_conf;
use OxidEsales\Eshop\Application\Controller\FrontendController;
@ -24,7 +25,7 @@ use OxidEsales\Eshop\Core\Utils;
class d3totplogin extends FrontendController
{
protected $_sThisTemplate = 'd3totplogin.tpl';
protected $_sThisTemplate = '@'.Constants::OXID_MODULE_ID.'/admin/d3totplogin';
public function render()
{

View File

@ -0,0 +1,23 @@
<?php
/**
* Copyright (c) D3 Data Development (Inh. Thomas Dartsch)
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* https://www.d3data.de
*
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
* @author D3 Data Development - Daniel Seifert <info@shopmodule.com>
* @link https://www.oxidmodule.com
*/
declare(strict_types=1);
namespace D3\Totp\Application\Model;
class Constants
{
public const OXID_MODULE_ID = 'd3totp';
}

View File

@ -18,6 +18,7 @@ use D3\Totp\Application\Controller\Admin\d3user_totp;
use D3\Totp\Application\Controller\Admin\d3force_2fa;
use D3\Totp\Application\Controller\d3_account_totp;
use D3\Totp\Application\Controller\d3totplogin;
use D3\Totp\Application\Model\Constants;
use D3\Totp\Modules\Application\Component\d3_totp_UserComponent;
use D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController;
use D3\Totp\Modules\Application\Controller\d3_totp_OrderController;
@ -36,22 +37,13 @@ use OxidEsales\Eshop\Core\SystemEventHandler;
use OxidEsales\Eshop\Core\Utils;
use OxidEsales\Eshop\Application\Model as OxidModel;
/**
* Metadata version
*/
$sMetadataVersion = '2.1';
$sModuleId = 'd3totp';
$logo = '(D3)';
/**
* Module information
*/
$aModule = [
'id' => $sModuleId,
'id' => Constants::OXID_MODULE_ID,
'title' => [
'de' => $logo . ' zweiter Faktor - Einmalpasswort',
'en' => $logo . ' second factor - one-time password',
'de' => '(D3) zweiter Faktor - Einmalpasswort',
'en' => '(D3) second factor - one-time password',
],
'description' => [
'de' => 'Einmalpasswort (TOTP) als zweiter Faktor bei der Anmeldung im OXID eSales Shop',
@ -80,10 +72,14 @@ $aModule = [
'd3totpadminlogin' => d3totpadminlogin::class,
],
'templates' => [
'd3user_totp.tpl' => 'd3/totp/Application/views/admin/tpl/d3user_totp.tpl',
'd3totplogin.tpl' => 'd3/totp/Application/views/tpl/d3totplogin.tpl',
'd3_account_totp.tpl' => 'd3/totp/Application/views/tpl/d3_account_totp.tpl',
'd3totpadminlogin.tpl' => 'd3/totp/Application/views/admin/tpl/d3totplogin.tpl',
// 'd3user_totp.tpl' => 'd3/totp/Application/views/admin/tpl/d3user_totp.tpl',
'@'.Constants::OXID_MODULE_ID.'/admin/d3user_totp.tpl' => 'views/smarty/admin/d3user_totp.tpl',
'@'.Constants::OXID_MODULE_ID.'/admin/d3totplogin.tpl' => 'views/smarty/admin/d3totplogin.tpl',
// 'd3totplogin.tpl' => 'd3/totp/Application/views/tpl/d3totplogin.tpl',
// 'd3_account_totp.tpl' => 'd3/totp/Application/views/tpl/d3_account_totp.tpl',
'@'.Constants::OXID_MODULE_ID.'/wave/d3_account_totp.tpl' => 'views/smarty/wave/d3_account_totp.tpl',
'@'.Constants::OXID_MODULE_ID.'/wave/d3totpadminlogin.tpl' => 'views/smarty/wave/d3totpadminlogin.tpl',
// 'd3totpadminlogin.tpl' => 'd3/totp/Application/views/admin/tpl/d3totplogin.tpl',
],
'settings' => [
[

View File

@ -0,0 +1,127 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>{{ translate({ ident: "LOGIN_TITLE" }) }}</title>
<meta http-equiv="Content-Type" content="text/html; charset={{ charset }}">
<meta name="ROBOTS" content="NOINDEX, NOFOLLOW">
<link rel="shortcut icon" href="{{ oViewConf.getImageUrl() }}favicon.ico">
<link rel="stylesheet" href="{{ oViewConf.getResourceUrl() }}login.css">
<link rel="stylesheet" href="{{ oViewConf.getResourceUrl() }}colors_{{ oViewConf.getEdition()|lower }}.css">
</head>
<body>
<div class="admin-login-box">
<div id="shopLogo"><img src="{{ oViewConf.getImageUrl('logo_dark.svg') }}" alt="" /></div>
<form action="{{ oViewConf.getSelfLink() }}" method="post" id="login">
{% block admin_login_form %}
{{ oViewConf.getHiddenSid()|raw }}
<input type="hidden" name="fnc" value="checklogin">
<input type="hidden" name="cl" value="{{ oViewConf.getActiveClassName() }}">
<input type="hidden" name="profile" value="{{ selectedProfile }}">
<input type="hidden" name="chlanguage" value="{{ selectedChLanguage }}">
<h3>{{ translate({ ident: "TOTP_INPUT" }) }}</h3>
{% if not empty(Errors.default) %}
{% include "inc_error.html.twig" with {Errorlist: Errors.default} %}
{% endif %}
{{ oView.getBackupCodeCountMessage() }}
<div class="container">
<label for="1st">erste TOTP-Ziffer</label>
<input type="text" name="d3totp[]" class="digit" id='1st' inputmode="numeric" pattern="[0-9]*" maxlength="1" onkeyup="clickEvent(null, '2nd')" autofocus autocomplete="off">
<label for="2nd">zweite TOTP-Ziffer</label>
<input type="text" name="d3totp[]" class="digit" id="2nd" inputmode="numeric" pattern="[0-9]*" maxlength="1" onkeyup="clickEvent('1st', '3rd')" autocomplete="off">
<label for="3rd">dritte TOTP-Ziffer</label>
<input type="text" name="d3totp[]" class="digit" id="3rd" inputmode="numeric" pattern="[0-9]*" maxlength="1" onkeyup="clickEvent('2nd', '4th')" autocomplete="off">
<label for="4th">vierte TOTP-Ziffer</label>
<input type="text" name="d3totp[]" class="digit" id="4th" inputmode="numeric" pattern="[0-9]*" maxlength="1" onkeyup="clickEvent('3rd', '5th')" autocomplete="off">
<label for="5th">fünfte TOTP-Ziffer</label>
<input type="text" name="d3totp[]" class="digit" id="5th" inputmode="numeric" pattern="[0-9]*" maxlength="1" onkeyup="clickEvent('4th', '6th')" autocomplete="off">
<label for="6th">sechste TOTP-Ziffer</label>
<input type="text" name="d3totp[]" class="digit" id="6th" inputmode="numeric" pattern="[0-9]*" maxlength="1" onkeyup="clickEvent('5th', null)" autocomplete="off">
</div>
{% capture name = "d3js" %}
function clickEvent(previous, next){
const digitKeys = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
const deleteKeys = ['Backspace', 'Delete'];
if(next && digitKeys.includes(event.key)){
document.getElementById(next).focus();
} else if(previous && deleteKeys.includes(event.key)){
document.getElementById(previous).focus();
}
}
document.addEventListener("paste", function(e) {
if (e.target.type is same as("text") {
)
var data = e.clipboardData.getData('Text');
data = data.split('');
[].forEach.call(document.querySelectorAll("#login input[type=text]"), (node, index) => {
node.value = data[index];
});
}
});
{% endcapture %}
{{ script({ add: smarty.capture.d3js, dynamic: __oxid_include_dynamic }) }}
<div>{{ translate({ ident: "TOTP_INPUT_HELP" }) }}</div>
<input type="submit" value="{{ translate({ ident: "LOGIN_START" }) }}" class="btn"><br>
<input class="btn btn_cancel" value="{{ translate({ ident: "TOTP_CANCEL_LOGIN" }) }}" type="submit"
onclick="document.getElementById('login').fnc.value='d3CancelLogin'; document.getElementById('login').submit();"
>
{{ style({ include: oViewConf.getModuleUrl('d3totp',, out/admin/src/css/d3totplogin.css'): 'out/admin/src/css/d3totplogin.css') }) }}
{{ style() }}
{# *
{{ oViewConf.getHiddenSid()|raw }}
<input type="hidden" name="fnc" value="">
<input type="hidden" name="cl" value="login">
{% if not empty(Errors.default) %}
{% include "inc_error.html.twig" with {Errorlist: Errors.default} %}
{% endif %}
<div class="d3webauthn_icon">
<div class="svg-container">
{% include $oViewConf->getModulePath('d3webauthn', with {out/img/fingerprint.svg'): 'out/img/fingerprint.svg')} %}
</div>
<div class="message">{{ translate({ ident: "WEBAUTHN_INPUT_HELP" }) }}</div>
</div>
* #}
{# prevent cancel button (1st button) action when form is sent via Enter key #}
{# *
<input type="submit" style="display:none !important;">
<input class="btn btn_cancel" value="{{ translate({ ident: "WEBAUTHN_CANCEL_LOGIN" }) }}" type="submit"
onclick="document.getElementById('login').fnc.value='d3WebauthnCancelLogin'; document.getElementById('login').submit();"
>
{{ style({ include: oViewConf.getModuleUrl('d3webauthn',, out/admin/src/css/d3webauthnlogin.css'): 'out/admin/src/css/d3webauthnlogin.css') }) }}
{{ style() }}
* #}
{% endblock %}
</form>
</div>
{{ script({ dynamic: __oxid_include_dynamic }) }}
<script type="text/javascript">if (window !== window.top) top.location.href = document.location.href;</script>
</body>
</html>

View File

@ -0,0 +1,126 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>[{oxmultilang ident="LOGIN_TITLE"}]</title>
<meta http-equiv="Content-Type" content="text/html; charset=[{$charset}]">
<meta name="ROBOTS" content="NOINDEX, NOFOLLOW">
<link rel="shortcut icon" href="[{$oViewConf->getImageUrl()}]favicon.ico">
<link rel="stylesheet" href="[{$oViewConf->getResourceUrl()}]login.css">
<link rel="stylesheet" href="[{$oViewConf->getResourceUrl()}]colors_[{$oViewConf->getEdition()|lower}].css">
</head>
<body>
<div class="admin-login-box">
<div id="shopLogo"><img src="[{$oViewConf->getImageUrl('logo_dark.svg')}]" alt="" /></div>
<form action="[{$oViewConf->getSelfLink()}]" method="post" id="login">
[{block name="admin_login_form"}]
[{$oViewConf->getHiddenSid()}]
<input type="hidden" name="fnc" value="checklogin">
<input type="hidden" name="cl" value="[{$oViewConf->getActiveClassName()}]">
<input type="hidden" name="profile" value="[{$selectedProfile}]">
<input type="hidden" name="chlanguage" value="[{$selectedChLanguage}]">
<h3>[{oxmultilang ident="TOTP_INPUT"}]</h3>
[{if !empty($Errors.default)}]
[{include file="inc_error.tpl" Errorlist=$Errors.default}]
[{/if}]
[{$oView->getBackupCodeCountMessage()}]
<div class="container">
<label for="1st">erste TOTP-Ziffer</label>
<input type="text" name="d3totp[]" class="digit" id='1st' inputmode="numeric" pattern="[0-9]*" maxlength="1" onkeyup="clickEvent(null, '2nd')" autofocus autocomplete="off">
<label for="2nd">zweite TOTP-Ziffer</label>
<input type="text" name="d3totp[]" class="digit" id="2nd" inputmode="numeric" pattern="[0-9]*" maxlength="1" onkeyup="clickEvent('1st', '3rd')" autocomplete="off">
<label for="3rd">dritte TOTP-Ziffer</label>
<input type="text" name="d3totp[]" class="digit" id="3rd" inputmode="numeric" pattern="[0-9]*" maxlength="1" onkeyup="clickEvent('2nd', '4th')" autocomplete="off">
<label for="4th">vierte TOTP-Ziffer</label>
<input type="text" name="d3totp[]" class="digit" id="4th" inputmode="numeric" pattern="[0-9]*" maxlength="1" onkeyup="clickEvent('3rd', '5th')" autocomplete="off">
<label for="5th">fünfte TOTP-Ziffer</label>
<input type="text" name="d3totp[]" class="digit" id="5th" inputmode="numeric" pattern="[0-9]*" maxlength="1" onkeyup="clickEvent('4th', '6th')" autocomplete="off">
<label for="6th">sechste TOTP-Ziffer</label>
<input type="text" name="d3totp[]" class="digit" id="6th" inputmode="numeric" pattern="[0-9]*" maxlength="1" onkeyup="clickEvent('5th', null)" autocomplete="off">
</div>
[{capture name="d3js"}]
function clickEvent(previous, next){
const digitKeys = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
const deleteKeys = ['Backspace', 'Delete'];
if(next && digitKeys.includes(event.key)){
document.getElementById(next).focus();
} else if(previous && deleteKeys.includes(event.key)){
document.getElementById(previous).focus();
}
}
document.addEventListener("paste", function(e) {
if (e.target.type === "text") {
var data = e.clipboardData.getData('Text');
data = data.split('');
[].forEach.call(document.querySelectorAll("#login input[type=text]"), (node, index) => {
node.value = data[index];
});
}
});
[{/capture}]
[{oxscript add=$smarty.capture.d3js}]
<div>[{oxmultilang ident="TOTP_INPUT_HELP"}]</div>
<input type="submit" value="[{oxmultilang ident="LOGIN_START"}]" class="btn"><br>
<input class="btn btn_cancel" value="[{oxmultilang ident="TOTP_CANCEL_LOGIN"}]" type="submit"
onclick="document.getElementById('login').fnc.value='d3CancelLogin'; document.getElementById('login').submit();"
>
[{oxstyle include=$oViewConf->getModuleUrl('d3totp', 'out/admin/src/css/d3totplogin.css')}]
[{oxstyle}]
[{**
[{$oViewConf->getHiddenSid()}]
<input type="hidden" name="fnc" value="">
<input type="hidden" name="cl" value="login">
[{if !empty($Errors.default)}]
[{include file="inc_error.tpl" Errorlist=$Errors.default}]
[{/if}]
<div class="d3webauthn_icon">
<div class="svg-container">
[{include file=$oViewConf->getModulePath('d3webauthn', 'out/img/fingerprint.svg')}]
</div>
<div class="message">[{oxmultilang ident="WEBAUTHN_INPUT_HELP"}]</div>
</div>
**}]
[{* prevent cancel button (1st button) action when form is sent via Enter key *}]
[{**
<input type="submit" style="display:none !important;">
<input class="btn btn_cancel" value="[{oxmultilang ident="WEBAUTHN_CANCEL_LOGIN"}]" type="submit"
onclick="document.getElementById('login').fnc.value='d3WebauthnCancelLogin'; document.getElementById('login').submit();"
>
[{oxstyle include=$oViewConf->getModuleUrl('d3webauthn', 'out/admin/src/css/d3webauthnlogin.css')}]
[{oxstyle}]
**}]
[{/block}]
</form>
</div>
[{oxscript}]
<script type="text/javascript">if (window !== window.top) top.location.href = document.location.href;</script>
</body>
</html>

View File

@ -0,0 +1,194 @@
{% include "headitem.html.twig" with {title: "GENERAL_ADMIN_TITLE"|translate} %}
{{ style({ include: "https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" }) }}
{{ script({ include: "https://code.jquery.com/jquery-3.2.1.slim.min.js", dynamic: __oxid_include_dynamic }) }}
{{ script({ include: "https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js", dynamic: __oxid_include_dynamic }) }}
{{ script({ include: "https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js", dynamic: __oxid_include_dynamic }) }}
{{ style({ include: "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/solid.min.css" }) }}
{{ style() }}
{% set totp = edit.d3GetTotp() %}
{% set userid = edit.getId() %}
{{ totp.loadByUserId(userid) }}
{% if readonly %}
{% set readonly = "readonly disabled" %}
{% else %}
{% set readonly = "" %}
{% endif %}
<style>
td.edittext {
white-space: normal;
}
.hero {
display: inline-block;
}
.hero > h1 {
padding: 0.3em 0;
}
.hero > div {
text-align: right;
color: #6c7c98;
}
.container-fluid {
font-size: 13px;
}
</style>
{% if force2FA %}
<div class="hero">
<h1>{{ translate({ ident: "D3_TOTP_FORCE2FATITLE" }) }}</h1>
<div>{{ translate({ ident: "D3_TOTP_FORCE2FASUB" }) }}</div>
</div>
{% endif %}
<form name="transfer" id="transfer" action="{{ oViewConf.getSelfLink() }}" method="post">
{{ oViewConf.getHiddenSid()|raw }}
<input type="hidden" name="oxid" value="{{ oxid }}">
<input type="hidden" name="cl" value="{{ oViewConf.getActiveClassName() }}">
</form>
<form name="myedit" id="myedit" action="{{ oViewConf.getSelfLink() }}" method="post" style="padding: 0;margin: 0;height:0;">
{{ oViewConf.getHiddenSid()|raw }}
<input type="hidden" name="cl" value="{{ oViewConf.getActiveClassName() }}">
<input type="hidden" name="fnc" value="">
<input type="hidden" name="oxid" value="{{ oxid }}">
<input type="hidden" name="editval[d3totp__oxid]" value="{{ totp.getId() }}">
<input type="hidden" name="editval[d3totp__oxuserid]" value="{{ oxid }}">
{% if sSaveError %}
<table style="padding:0; border:0; width:98%;">
<tr>
<td></td>
<td class="errorbox">{{ translate({ ident: sSaveError }) }}</td>
</tr>
</table>
{% endif %}
{% if oxid and oxid != ' - 1' %}
<div class="container-fluid">
<div class="row">
<div class="col-4">
<div class="card">
{% block user_d3user_totp_form1 %}
{% if false == totp.getId() %}
<div class="card-header">
{{ translate({ ident: "D3_TOTP_REGISTERNEW" }) }}
</div>
<div class="card-body">
<div class="row">
<div class="col-4">
{{ translate({ ident: "D3_TOTP_QRCODE", suffix: "COLON" }) }}
</div>
<div class="col-8">
{{ totp.getQrCodeElement()|raw }}
{% include "inputhelp.html.twig" with {'sHelpId': help_id("D3_TOTP_QRCODE_HELP"), 'sHelpText': help_text("D3_TOTP_QRCODE_HELP")} %}
</div>
</div>
</div>
{% elseif force2FA %}
<div class="card-header">
{{ translate({ ident: "D3_TOTP_ADMINBACKEND" }) }}
</div>
<div class="card-body">
<input
type="submit" class="edittext" id="oLockButton" name="delete"
value="{{ translate({ ident: "D3_TOTP_ADMINCONTINUE" }) }}"
onClick="document.myedit.fnc.value='';document.myedit.cl.value='admin_start'"
>
</div>
{% else %}
<div class="card-header">
{{ translate({ ident: "D3_TOTP_REGISTEREXIST" }) }}
</div>
<div class="card-body">
<div class="row">
<div class="col-12">
{{ translate({ ident: "D3_TOTP_REGISTERDELETE_DESC" }) }}
<br>
<br>
<button type="submit" {{ readonly }} class="btn btn-primary btn-outline-danger btn-sm" onClick="document.myedit.fnc.value='delete'">
{{ translate({ ident: "D3_TOTP_REGISTERDELETE" }) }}
</button>
</div>
</div>
<br>
</div>
{% endif %}
{% endblock %}
</div>
</div>
<div class="col-8">
<div class="card">
{% block user_d3user_totp_form2 %}
{% if false == totp.getId() %}
<div class="card-header">
{{ translate({ ident: "D3_TOTP_CONFIRMATION" }) }}
</div>
<div class="card-body">
<div class="row">
<div class="col-4">
<label for="secret">{{ translate({ ident: "D3_TOTP_SECRET", suffix: "COLON" }) }}</label>
</div>
<div class="col-8">
<textarea rows="3" cols="50" id="secret" name="secret" class="editinput" readonly="readonly">{{ totp.getSecret() }}</textarea>
{% include "inputhelp.html.twig" with {'sHelpId': help_id("D3_TOTP_SECRET_HELP"), 'sHelpText': help_text("D3_TOTP_SECRET_HELP")} %}
</div>
</div>
<div class="row" style="margin-top: 20px;">
<div class="col-4">
<label for="otp">{{ translate({ ident: "D3_TOTP_CURROTP" }) }}</label>
</div>
<div class="col-8">
<input type="text" class="editinput" size="6" maxlength="6" id="otp" name="otp" value="" autofocus="autofocus" {{ readonly }}>
{% include "inputhelp.html.twig" with {'sHelpId': help_id("D3_TOTP_CURROTP_HELP"), 'sHelpText': help_text("D3_TOTP_CURROTP_HELP")} %}
</div>
</div>
<div class="row">
<div class="col-4"></div>
<div class="col-8">
<button type="submit" {{ readonly }} class="btn btn-primary btn-success btn-sm" onClick="document.myedit.fnc.value='save'">
{{ translate({ ident: "D3_TOTP_SAVE" }) }}
</button>
</div>
</div>
</div>
{% else %}
<div class="card-header">
{{ translate({ ident: "D3_TOTP_BACKUPCODES" }) }}
</div>
<div class="card-body">
{% if oView.getBackupCodes() %}
<div class="row">
<div class="col-6">
<label for="backupcodes">{{ translate({ ident: "D3_TOTP_BACKUPCODES_DESC" }) }}</label>
</div>
<div class="col-6">
<textarea id="backupcodes" rows="10" cols="20">{{ oView.getBackupCodes() }}</textarea>
</div>
</div>
{% else %}
<div class="row">
<div class="col-12">
{{ translate({ ident: "D3_TOTP_AVAILBACKUPCODECOUNT", args: oView.getAvailableBackupCodeCount() }) }}
<br>
<br>
{{ translate({ ident: "D3_TOTP_AVAILBACKUPCODECOUNT_DESC" }) }}
</div>
</div>
{% endif %}
</div>
{% endif %}
{% endblock %}
</div>
</div>
</div>
</div>
{% endif %}
</form>
{% if not force2FA %}
{% include "bottomnaviitem.html.twig" %}
{% include "bottomitem.html.twig" %}
{% endif %}

View File

@ -0,0 +1,194 @@
[{include file="headitem.tpl" title="GENERAL_ADMIN_TITLE"|oxmultilangassign}]
[{oxstyle include="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"}]
[{oxscript include="https://code.jquery.com/jquery-3.2.1.slim.min.js"}]
[{oxscript include="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"}]
[{oxscript include="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"}]
[{oxstyle include="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/solid.min.css"}]
[{oxstyle}]
[{assign var="totp" value=$edit->d3GetTotp()}]
[{assign var="userid" value=$edit->getId()}]
[{$totp->loadByUserId($userid)}]
[{if $readonly}]
[{assign var="readonly" value="readonly disabled"}]
[{else}]
[{assign var="readonly" value=""}]
[{/if}]
<style>
td.edittext {
white-space: normal;
}
.hero {
display: inline-block;
}
.hero > h1 {
padding: 0.3em 0;
}
.hero > div {
text-align: right;
color: #6c7c98;
}
.container-fluid {
font-size: 13px;
}
</style>
[{if $force2FA}]
<div class="hero">
<h1>[{oxmultilang ident="D3_TOTP_FORCE2FATITLE"}]</h1>
<div>[{oxmultilang ident="D3_TOTP_FORCE2FASUB"}]</div>
</div>
[{/if}]
<form name="transfer" id="transfer" action="[{$oViewConf->getSelfLink()}]" method="post">
[{$oViewConf->getHiddenSid()}]
<input type="hidden" name="oxid" value="[{$oxid}]">
<input type="hidden" name="cl" value="[{$oViewConf->getActiveClassName()}]">
</form>
<form name="myedit" id="myedit" action="[{$oViewConf->getSelfLink()}]" method="post" style="padding: 0;margin: 0;height:0;">
[{$oViewConf->getHiddenSid()}]
<input type="hidden" name="cl" value="[{$oViewConf->getActiveClassName()}]">
<input type="hidden" name="fnc" value="">
<input type="hidden" name="oxid" value="[{$oxid}]">
<input type="hidden" name="editval[d3totp__oxid]" value="[{$totp->getId()}]">
<input type="hidden" name="editval[d3totp__oxuserid]" value="[{$oxid}]">
[{if $sSaveError}]
<table style="padding:0; border:0; width:98%;">
<tr>
<td></td>
<td class="errorbox">[{oxmultilang ident=$sSaveError}]</td>
</tr>
</table>
[{/if}]
[{if $oxid && $oxid != '-1'}]
<div class="container-fluid">
<div class="row">
<div class="col-4">
<div class="card">
[{block name="user_d3user_totp_form1"}]
[{if false == $totp->getId()}]
<div class="card-header">
[{oxmultilang ident="D3_TOTP_REGISTERNEW"}]
</div>
<div class="card-body">
<div class="row">
<div class="col-4">
[{oxmultilang ident="D3_TOTP_QRCODE" suffix="COLON"}]
</div>
<div class="col-8">
[{$totp->getQrCodeElement()}]
[{oxinputhelp ident="D3_TOTP_QRCODE_HELP"}]
</div>
</div>
</div>
[{elseif $force2FA}]
<div class="card-header">
[{oxmultilang ident="D3_TOTP_ADMINBACKEND"}]
</div>
<div class="card-body">
<input
type="submit" class="edittext" id="oLockButton" name="delete"
value="[{oxmultilang ident="D3_TOTP_ADMINCONTINUE"}]"
onClick="document.myedit.fnc.value='';document.myedit.cl.value='admin_start'"
>
</div>
[{else}]
<div class="card-header">
[{oxmultilang ident="D3_TOTP_REGISTEREXIST"}]
</div>
<div class="card-body">
<div class="row">
<div class="col-12">
[{oxmultilang ident="D3_TOTP_REGISTERDELETE_DESC"}]
<br>
<br>
<button type="submit" [{$readonly}] class="btn btn-primary btn-outline-danger btn-sm" onClick="document.myedit.fnc.value='delete'">
[{oxmultilang ident="D3_TOTP_REGISTERDELETE"}]
</button>
</div>
</div>
<br>
</div>
[{/if}]
[{/block}]
</div>
</div>
<div class="col-8">
<div class="card">
[{block name="user_d3user_totp_form2"}]
[{if false == $totp->getId()}]
<div class="card-header">
[{oxmultilang ident="D3_TOTP_CONFIRMATION"}]
</div>
<div class="card-body">
<div class="row">
<div class="col-4">
<label for="secret">[{oxmultilang ident="D3_TOTP_SECRET" suffix="COLON"}]</label>
</div>
<div class="col-8">
<textarea rows="3" cols="50" id="secret" name="secret" class="editinput" readonly="readonly">[{$totp->getSecret()}]</textarea>
[{oxinputhelp ident="D3_TOTP_SECRET_HELP"}]
</div>
</div>
<div class="row" style="margin-top: 20px;">
<div class="col-4">
<label for="otp">[{oxmultilang ident="D3_TOTP_CURROTP"}]</label>
</div>
<div class="col-8">
<input type="text" class="editinput" size="6" maxlength="6" id="otp" name="otp" value="" autofocus="autofocus" [{$readonly}]>
[{oxinputhelp ident="D3_TOTP_CURROTP_HELP"}]
</div>
</div>
<div class="row">
<div class="col-4"></div>
<div class="col-8">
<button type="submit" [{$readonly}] class="btn btn-primary btn-success btn-sm" onClick="document.myedit.fnc.value='save'">
[{oxmultilang ident="D3_TOTP_SAVE"}]
</button>
</div>
</div>
</div>
[{else}]
<div class="card-header">
[{oxmultilang ident="D3_TOTP_BACKUPCODES"}]
</div>
<div class="card-body">
[{if $oView->getBackupCodes()}]
<div class="row">
<div class="col-6">
<label for="backupcodes">[{oxmultilang ident="D3_TOTP_BACKUPCODES_DESC"}]</label>
</div>
<div class="col-6">
<textarea id="backupcodes" rows="10" cols="20">[{$oView->getBackupCodes()}]</textarea>
</div>
</div>
[{else}]
<div class="row">
<div class="col-12">
[{oxmultilang ident="D3_TOTP_AVAILBACKUPCODECOUNT" args=$oView->getAvailableBackupCodeCount()}]
<br>
<br>
[{oxmultilang ident="D3_TOTP_AVAILBACKUPCODECOUNT_DESC"}]
</div>
</div>
[{/if}]
</div>
[{/if}]
[{/block}]
</div>
</div>
</div>
</div>
[{/if}]
</form>
[{if !$force2FA}]
[{include file="bottomnaviitem.tpl"}]
[{include file="bottomitem.tpl"}]
[{/if}]

View File

@ -0,0 +1,29 @@
{% if request_totp %}
{{ oViewConf.getHiddenSid()|raw }}
<input type="hidden" name="fnc" value="checklogin">
<input type="hidden" name="cl" value="login">
{% if not empty(Errors.default) %}
{% include "inc_error.html.twig" with {Errorlist: Errors.default} %}
{% endif %}
{{ oView.getBackupCodeCountMessage() }}
<label for="d3totp">{{ translate({ ident: "TOTP_INPUT" }) }}</label>
<input type="text" name="d3totp" id="d3totp" value="" size="49" autofocus autocomplete="off"><br>
{{ translate({ ident: "TOTP_INPUT_HELP" }) }}
{# prevent cancel button (1st button) action when form is sent via Enter key #}
<input type="submit" style="display:none !important;">
<input class="btn btn_cancel" value="{{ translate({ ident: "TOTP_CANCEL_LOGIN" }) }}" type="submit"
onclick="document.getElementById('login').fnc.value='d3CancelLogin'; document.getElementById('login').submit();"
>
{{ style({ include: oViewConf.getModuleUrl('d3totp',, out/admin/src/css/d3totplogin.css'): 'out/admin/src/css/d3totplogin.css') }) }}
{{ style() }}
{% else %}
{{ parent() }}
{% endif %}

View File

@ -0,0 +1,29 @@
[{if $request_totp}]
[{$oViewConf->getHiddenSid()}]
<input type="hidden" name="fnc" value="checklogin">
<input type="hidden" name="cl" value="login">
[{if !empty($Errors.default)}]
[{include file="inc_error.tpl" Errorlist=$Errors.default}]
[{/if}]
[{$oView->getBackupCodeCountMessage()}]
<label for="d3totp">[{oxmultilang ident="TOTP_INPUT"}]</label>
<input type="text" name="d3totp" id="d3totp" value="" size="49" autofocus autocomplete="off"><br>
[{oxmultilang ident="TOTP_INPUT_HELP"}]
[{* prevent cancel button (1st button) action when form is sent via Enter key *}]
<input type="submit" style="display:none !important;">
<input class="btn btn_cancel" value="[{oxmultilang ident="TOTP_CANCEL_LOGIN"}]" type="submit"
onclick="document.getElementById('login').fnc.value='d3CancelLogin'; document.getElementById('login').submit();"
>
[{oxstyle include=$oViewConf->getModuleUrl('d3totp', 'out/admin/src/css/d3totplogin.css')}]
[{oxstyle}]
[{else}]
[{$smarty.block.parent}]
[{/if}]

View File

@ -0,0 +1,11 @@
{{ parent() }}
<div class="panel panel-default">
<div class="panel-heading">
<a href="{{ seo_url({ ident: oViewConf.getSelfLink()|cat("cl=d3_account_totp") }) }}">{{ translate({ ident: "D3_TOTP_ACCOUNT" }) }}</a>
<a href="{{ seo_url({ ident: oViewConf.getSslSelfLink()|cat("cl=d3_account_totp") }) }}" class="btn btn-default btn-xs pull-right">
<i class="fa fa-arrow-right"></i>
</a>
</div>
<div class="panel-body">{{ translate({ ident: "D3_TOTP_ACCOUNT_DESC" }) }}</div>
</div>

View File

@ -0,0 +1,11 @@
[{$smarty.block.parent}]
<div class="panel panel-default">
<div class="panel-heading">
<a href="[{oxgetseourl ident=$oViewConf->getSelfLink()|cat:"cl=d3_account_totp"}]">[{oxmultilang ident="D3_TOTP_ACCOUNT"}]</a>
<a href="[{oxgetseourl ident=$oViewConf->getSslSelfLink()|cat:"cl=d3_account_totp"}]" class="btn btn-default btn-xs pull-right">
<i class="fa fa-arrow-right"></i>
</a>
</div>
<div class="panel-body">[{oxmultilang ident="D3_TOTP_ACCOUNT_DESC"}]</div>
</div>

View File

@ -0,0 +1,11 @@
{{ parent() }}
<div class="card">
<div class="card-header">
<a href="{{ seo_url({ ident: oViewConf.getSelfLink()|cat("cl=d3_account_totp") }) }}">{{ translate({ ident: "D3_TOTP_ACCOUNT" }) }}</a>
<a href="{{ seo_url({ ident: oViewConf.getSslSelfLink()|cat("cl=d3_account_totp") }) }}" class="btn btn-outline-dark btn-sm float-right edit-button">
<i class="fa fa-arrow-right"></i>
</a>
</div>
<div class="card-body">{{ translate({ ident: "D3_TOTP_ACCOUNT_DESC" }) }}</div>
</div>

View File

@ -0,0 +1,11 @@
[{$smarty.block.parent}]
<div class="card">
<div class="card-header">
<a href="[{oxgetseourl ident=$oViewConf->getSelfLink()|cat:"cl=d3_account_totp"}]">[{oxmultilang ident="D3_TOTP_ACCOUNT"}]</a>
<a href="[{oxgetseourl ident=$oViewConf->getSslSelfLink()|cat:"cl=d3_account_totp"}]" class="btn btn-outline-dark btn-sm float-right edit-button">
<i class="fa fa-arrow-right"></i>
</a>
</div>
<div class="card-body">[{oxmultilang ident="D3_TOTP_ACCOUNT_DESC"}]</div>
</div>

View File

@ -0,0 +1,4 @@
{{ parent() }}
<li class="list-group-item{% if active_link == "d3totp" %} active{% endif %}">
<a class="{# wave #} list-group-link" href="{{ seo_url({ ident: oViewConf.getSelfLink()|cat("cl=d3_account_totp") }) }}" title="{{ translate({ ident: "D3_TOTP_ACCOUNT" }) }}">{{ translate({ ident: "D3_TOTP_ACCOUNT" }) }}</a>
</li>

View File

@ -0,0 +1,4 @@
[{$smarty.block.parent}]
<li class="list-group-item[{if $active_link == "d3totp"}] active[{/if}]">
<a class="[{* wave *}] list-group-link" href="[{oxgetseourl ident=$oViewConf->getSelfLink()|cat:"cl=d3_account_totp"}]" title="[{oxmultilang ident="D3_TOTP_ACCOUNT"}]">[{oxmultilang ident="D3_TOTP_ACCOUNT"}]</a>
</li>

View File

@ -0,0 +1,4 @@
{{ parent() }}
<li>
<a href="{{ seo_url({ ident: oViewConf.getSslSelfLink()|cat("cl=d3_account_totp") }) }}">{{ translate({ ident: "D3_TOTP_ACCOUNT" }) }}</a>
</li>

View File

@ -0,0 +1,4 @@
[{$smarty.block.parent}]
<li>
<a href="[{oxgetseourl ident=$oViewConf->getSslSelfLink()|cat:"cl=d3_account_totp"}]">[{oxmultilang ident="D3_TOTP_ACCOUNT"}]</a>
</li>

View File

@ -0,0 +1,150 @@
{% capture append = "oxidBlock_content" %}
<h1 class="page-header">{{ translate({ ident: "D3_TOTP_ACCOUNT" }) }}</h1>
{% set totp = user.d3GetTotp() %}
{% set userid = user.getId() %}
{{ totp.loadByUserId(userid) }}
<style>
.registerNew {
display: none;
}
dt, dd {
width: 50%;
float: left;
margin-bottom: 10px;
}
dd textarea {
max-width: 98%;
}
dt label {
font-weight: bold;
}
{% if false == totp.getId() %}
.submitBtn {
display: none;
}
{% endif %}
</style>
{% block d3_account_totp %}
<form action="{{ oViewConf.getSelfActionLink() }}" name="d3totpform" class="form-horizontal" method="post">
<div class="hidden">
{{ oViewConf.getHiddenSid()|raw }}
{{ oViewConf.getNavFormParams() }}
<input type="hidden" id="fncname" name="fnc" value="">
<input type="hidden" name="cl" value="{{ oViewConf.getActiveClassName() }}">
</div>
<p>
<input id="totp_use" value="1" type="checkbox" name="totp_use" {% if totp.getId() %} checked{% endif %} {% if false == totp.getId() %}onclick="$('.registerNew').toggle(); $('.submitBtn').toggle();"{% endif %}>
<label for="totp_use">{{ translate({ ident: "D3_TOTP_ACCOUNT_USE" }) }}</label>
</p>
{% if false == totp.getId() %}
<div class="registerNew {# flow #} panel panel-default {# wave #} card">
<div class="{# flow #} panel-heading {# wave #} card-header">
{{ translate({ ident: "D3_TOTP_REGISTERNEW" }) }}
</div>
<div class="{# flow #} panel-body {# wave #} card-body">
<dl>
<dt>
{{ translate({ ident: "D3_TOTP_QRCODE" }) }}&nbsp;
</dt>
<dd>
{{ totp.getQrCodeElement() }}
</dd>
</dl>
<p>
{{ translate({ ident: "D3_TOTP_QRCODE_HELP" }) }}
</p>
<hr>
<dl>
<dt>
<label for="secret">{{ translate({ ident: "D3_TOTP_SECRET" }) }}</label>
</dt>
<dd>
<textarea rows="3" cols="50" id="secret" name="secret" class="editinput" readonly="readonly">{{ totp.getSecret() }}</textarea>
</dd>
</dl>
<p>
{{ translate({ ident: "D3_TOTP_SECRET_HELP" }) }}
</p>
<hr>
<dl>
<dt>
<label for="otp">{{ translate({ ident: "D3_TOTP_CURROTP" }) }}</label>
</dt>
<dd>
<input type="text" class="editinput" size="6" maxlength="6" id="otp" name="otp" value="" {{ readonly }}>
</dd>
</dl>
<p>
{{ translate({ ident: "D3_TOTP_CURROTP_HELP" }) }}
</p>
</div>
</div>
{% endif %}
{% if totp.getId() %}
{% block d3_account_totp_deletenotes %}
<div class="{# flow #} panel panel-default {# wave #} card">
<div class="{# flow #} panel-heading {# wave #} card-header">
{{ translate({ ident: "D3_TOTP_REGISTEREXIST" }) }}
</div>
<div class="{# flow #} panel-body {# wave #} card-body">
{{ translate({ ident: "D3_TOTP_REGISTERDELETE_DESC" }) }}
</div>
</div>
{% endblock %}
{% block d3_account_totp_backupcodes %}
<div class="{# flow #} panel panel-default {# wave #} card">
<div class="{# flow #} panel-heading {# wave #} card-header">
{{ translate({ ident: "D3_TOTP_BACKUPCODES" }) }}
</div>
<div class="{# flow #} panel-body {# wave #} card-body">
{% if oView.getBackupCodes() %}
{% block d3_account_totp_backupcodes_list %}
<label for="backupcodes">{{ translate({ ident: "D3_TOTP_BACKUPCODES_DESC" }) }}</label>
<textarea id="backupcodes" rows="10" cols="20">{{ oView.getBackupCodes() }}</textarea>
{% endblock %}
{% else %}
{% block d3_account_totp_backupcodes_info %}
{{ translate({ ident: "D3_TOTP_AVAILBACKUPCODECOUNT", args: oView.getAvailableBackupCodeCount() }) }}<br>
{{ translate({ ident: "D3_TOTP_AVAILBACKUPCODECOUNT_DESC" }) }}
{% endblock %}
{% endif %}
</div>
</div>
{% endblock %}
{% endif %}
<p class="submitBtn">
<button type="submit" class="btn btn-primary"
{% if totp.getId() %}
onclick="
if(false is same as(document.getElementById('totp_use').checked && false === confirm('{{ translate({ ident: "D3_TOTP_REGISTERDELETE_CONFIRM" }) }}')) {return false;}
)
document.getElementById('fncname').value = 'delete';
"
{% else %}
onclick="document.getElementById('fncname').value = 'create';"
{% endif %}
>
{{ translate({ ident: "D3_TOTP_ACCOUNT_SAVE" }) }}
</button>
</p>
</form>
{% endblock %}
{% endcapture %}
{% capture append = "oxidBlock_sidebar" %}
{% include "page/account/inc/account_menu.html.twig" with {active_link: "d3totp"} %}
{% endcapture %}
{% include "layout/page.html.twig" with {sidebar: "Left"} %}

View File

@ -0,0 +1,149 @@
[{capture append="oxidBlock_content"}]
<h1 class="page-header">[{oxmultilang ident="D3_TOTP_ACCOUNT"}]</h1>
[{assign var="totp" value=$user->d3GetTotp()}]
[{assign var="userid" value=$user->getId()}]
[{$totp->loadByUserId($userid)}]
<style>
.registerNew {
display: none;
}
dt, dd {
width: 50%;
float: left;
margin-bottom: 10px;
}
dd textarea {
max-width: 98%;
}
dt label {
font-weight: bold;
}
[{if false == $totp->getId()}]
.submitBtn {
display: none;
}
[{/if}]
</style>
[{block name="d3_account_totp"}]
<form action="[{$oViewConf->getSelfActionLink()}]" name="d3totpform" class="form-horizontal" method="post">
<div class="hidden">
[{$oViewConf->getHiddenSid()}]
[{$oViewConf->getNavFormParams()}]
<input type="hidden" id="fncname" name="fnc" value="">
<input type="hidden" name="cl" value="[{$oViewConf->getActiveClassName()}]">
</div>
<p>
<input id="totp_use" value="1" type="checkbox" name="totp_use" [{if $totp->getId()}] checked[{/if}] [{if false == $totp->getId()}]onclick="$('.registerNew').toggle(); $('.submitBtn').toggle();"[{/if}]>
<label for="totp_use">[{oxmultilang ident="D3_TOTP_ACCOUNT_USE"}]</label>
</p>
[{if false == $totp->getId()}]
<div class="registerNew [{* flow *}] panel panel-default [{* wave *}] card">
<div class="[{* flow *}] panel-heading [{* wave *}] card-header">
[{oxmultilang ident="D3_TOTP_REGISTERNEW"}]
</div>
<div class="[{* flow *}] panel-body [{* wave *}] card-body">
<dl>
<dt>
[{oxmultilang ident="D3_TOTP_QRCODE"}]&nbsp;
</dt>
<dd>
[{$totp->getQrCodeElement()}]
</dd>
</dl>
<p>
[{oxmultilang ident="D3_TOTP_QRCODE_HELP"}]
</p>
<hr>
<dl>
<dt>
<label for="secret">[{oxmultilang ident="D3_TOTP_SECRET"}]</label>
</dt>
<dd>
<textarea rows="3" cols="50" id="secret" name="secret" class="editinput" readonly="readonly">[{$totp->getSecret()}]</textarea>
</dd>
</dl>
<p>
[{oxmultilang ident="D3_TOTP_SECRET_HELP"}]
</p>
<hr>
<dl>
<dt>
<label for="otp">[{oxmultilang ident="D3_TOTP_CURROTP"}]</label>
</dt>
<dd>
<input type="text" class="editinput" size="6" maxlength="6" id="otp" name="otp" value="" [{$readonly}]>
</dd>
</dl>
<p>
[{oxmultilang ident="D3_TOTP_CURROTP_HELP"}]
</p>
</div>
</div>
[{/if}]
[{if $totp->getId()}]
[{block name="d3_account_totp_deletenotes"}]
<div class="[{* flow *}] panel panel-default [{* wave *}] card">
<div class="[{* flow *}] panel-heading [{* wave *}] card-header">
[{oxmultilang ident="D3_TOTP_REGISTEREXIST"}]
</div>
<div class="[{* flow *}] panel-body [{* wave *}] card-body">
[{oxmultilang ident="D3_TOTP_REGISTERDELETE_DESC"}]
</div>
</div>
[{/block}]
[{block name="d3_account_totp_backupcodes"}]
<div class="[{* flow *}] panel panel-default [{* wave *}] card">
<div class="[{* flow *}] panel-heading [{* wave *}] card-header">
[{oxmultilang ident="D3_TOTP_BACKUPCODES"}]
</div>
<div class="[{* flow *}] panel-body [{* wave *}] card-body">
[{if $oView->getBackupCodes()}]
[{block name="d3_account_totp_backupcodes_list"}]
<label for="backupcodes">[{oxmultilang ident="D3_TOTP_BACKUPCODES_DESC"}]</label>
<textarea id="backupcodes" rows="10" cols="20">[{$oView->getBackupCodes()}]</textarea>
[{/block}]
[{else}]
[{block name="d3_account_totp_backupcodes_info"}]
[{oxmultilang ident="D3_TOTP_AVAILBACKUPCODECOUNT" args=$oView->getAvailableBackupCodeCount()}]<br>
[{oxmultilang ident="D3_TOTP_AVAILBACKUPCODECOUNT_DESC"}]
[{/block}]
[{/if}]
</div>
</div>
[{/block}]
[{/if}]
<p class="submitBtn">
<button type="submit" class="btn btn-primary"
[{if $totp->getId()}]
onclick="
if(false === document.getElementById('totp_use').checked && false === confirm('[{oxmultilang ident="D3_TOTP_REGISTERDELETE_CONFIRM"}]')) {return false;}
document.getElementById('fncname').value = 'delete';
"
[{else}]
onclick="document.getElementById('fncname').value = 'create';"
[{/if}]
>
[{oxmultilang ident="D3_TOTP_ACCOUNT_SAVE"}]
</button>
</p>
</form>
[{/block}]
[{/capture}]
[{capture append="oxidBlock_sidebar"}]
[{include file="page/account/inc/account_menu.tpl" active_link="d3totp"}]
[{/capture}]
[{include file="layout/page.tpl" sidebar="Left"}]

View File

@ -0,0 +1,92 @@
{% capture append = "oxidBlock_content" %}
{% set template_title = "" %}
{% if oView.previousClassIsOrderStep() %}
{# ordering steps #}
{% include "page/checkout/inc/steps.html.twig" with {active: 2} %}
{% endif %}
<div class="row">
<div class="col-xs-12 col-sm-10 col-md-6 {# flow #} col-sm-offset-1 col-md-offset-3 {# wave #} offset-sm-1 offset-md-3 mainforms">
<form action="{{ oViewConf.getSelfActionLink() }}" method="post" name="login" id="login">
{{ oViewConf.getHiddenSid()|raw }}
<input type="hidden" name="fnc" value="d3TotpCheckTotpLogin">
<input type="hidden" name="cl" value="{{ oView.getPreviousClass() }}">
{{ navFormParams }}
<h3>{{ translate({ ident: "D3_TOTP_INPUT" }) }}</h3>
{% if not empty(Errors.default) %}
{% include "inc_error.html.twig" with {Errorlist: Errors.default} %}
{% endif %}
{{ oView.getBackupCodeCountMessage() }}
<div class="container">
<label for="1st">erste TOTP-Ziffer</label>
<input type="text" name="d3totp[]" class="digit" id='1st' inputmode="numeric" pattern="[0-9]*" maxlength="1" onkeyup="clickEvent(null, '2nd')" autofocus autocomplete="off">
<label for="2nd">zweite TOTP-Ziffer</label>
<input type="text" name="d3totp[]" class="digit" id="2nd" inputmode="numeric" pattern="[0-9]*" maxlength="1" onkeyup="clickEvent('1st', '3rd')" autocomplete="off">
<label for="3rd">dritte TOTP-Ziffer</label>
<input type="text" name="d3totp[]" class="digit" id="3rd" inputmode="numeric" pattern="[0-9]*" maxlength="1" onkeyup="clickEvent('2nd', '4th')" autocomplete="off">
<label for="4th">vierte TOTP-Ziffer</label>
<input type="text" name="d3totp[]" class="digit" id="4th" inputmode="numeric" pattern="[0-9]*" maxlength="1" onkeyup="clickEvent('3rd', '5th')" autocomplete="off">
<label for="5th">fünfte TOTP-Ziffer</label>
<input type="text" name="d3totp[]" class="digit" id="5th" inputmode="numeric" pattern="[0-9]*" maxlength="1" onkeyup="clickEvent('4th', '6th')" autocomplete="off">
<label for="6th">sechste TOTP-Ziffer</label>
<input type="text" name="d3totp[]" class="digit" id="6th" inputmode="numeric" pattern="[0-9]*" maxlength="1" onkeyup="clickEvent('5th', null)" autocomplete="off">
</div>
{% capture name = "d3js" %}
function clickEvent(previous, next){
const digitKeys = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
const deleteKeys = ['Backspace', 'Delete'];
if(next && digitKeys.includes(event.key)){
document.getElementById(next).focus();
} else if(previous && deleteKeys.includes(event.key)){
document.getElementById(previous).focus();
}
}
document.addEventListener("paste", function(e) {
if (e.target.type is same as("text") {
)
var data = e.clipboardData.getData('Text');
data = data.split('');
[].forEach.call(document.querySelectorAll("#login input[type=text]"), (node, index) => {
node.value = data[index];
});
}
});
{% endcapture %}
{{ script({ add: smarty.capture.d3js, dynamic: __oxid_include_dynamic }) }}
<div>{{ translate({ ident: "D3_TOTP_INPUT_HELP" }) }}</div>
<button type="submit" class="btn btn-primary">
{{ translate({ ident: "D3_TOTP_SUBMIT_LOGIN" }) }}
</button><br>
</form>
<form action="{{ oViewConf.getSelfActionLink() }}" method="post" name="login" id="login">
{{ oViewConf.getHiddenSid()|raw }}
<input type="hidden" name="fnc" value="d3TotpCancelTotpLogin">
<input type="hidden" name="cl" value="{{ oView.getPreviousClass() }}">
{{ navFormParams }}
<button class="btn btn_cancel" type="submit">
{{ translate({ ident: "D3_TOTP_CANCEL_LOGIN" }) }}
</button>
</form>
</div>
</div>
{{ style({ include: oViewConf.getModuleUrl('d3totp',, out/flow/src/css/d3totplogin.css'): 'out/flow/src/css/d3totplogin.css') }) }}
{{ style() }}
{{ insert_tracker({title: template_title}) }}
{% endcapture %}
{% include "layout/page.html.twig" %}

View File

@ -0,0 +1,91 @@
[{capture append="oxidBlock_content"}]
[{assign var="template_title" value=""}]
[{if $oView->previousClassIsOrderStep()}]
[{* ordering steps *}]
[{include file="page/checkout/inc/steps.tpl" active=2}]
[{/if}]
<div class="row">
<div class="col-xs-12 col-sm-10 col-md-6 [{* flow *}] col-sm-offset-1 col-md-offset-3 [{* wave *}] offset-sm-1 offset-md-3 mainforms">
<form action="[{$oViewConf->getSelfActionLink()}]" method="post" name="login" id="login">
[{$oViewConf->getHiddenSid()}]
<input type="hidden" name="fnc" value="d3TotpCheckTotpLogin">
<input type="hidden" name="cl" value="[{$oView->getPreviousClass()}]">
[{$navFormParams}]
<h3>[{oxmultilang ident="D3_TOTP_INPUT"}]</h3>
[{if !empty($Errors.default)}]
[{include file="inc_error.tpl" Errorlist=$Errors.default}]
[{/if}]
[{$oView->getBackupCodeCountMessage()}]
<div class="container">
<label for="1st">erste TOTP-Ziffer</label>
<input type="text" name="d3totp[]" class="digit" id='1st' inputmode="numeric" pattern="[0-9]*" maxlength="1" onkeyup="clickEvent(null, '2nd')" autofocus autocomplete="off">
<label for="2nd">zweite TOTP-Ziffer</label>
<input type="text" name="d3totp[]" class="digit" id="2nd" inputmode="numeric" pattern="[0-9]*" maxlength="1" onkeyup="clickEvent('1st', '3rd')" autocomplete="off">
<label for="3rd">dritte TOTP-Ziffer</label>
<input type="text" name="d3totp[]" class="digit" id="3rd" inputmode="numeric" pattern="[0-9]*" maxlength="1" onkeyup="clickEvent('2nd', '4th')" autocomplete="off">
<label for="4th">vierte TOTP-Ziffer</label>
<input type="text" name="d3totp[]" class="digit" id="4th" inputmode="numeric" pattern="[0-9]*" maxlength="1" onkeyup="clickEvent('3rd', '5th')" autocomplete="off">
<label for="5th">fünfte TOTP-Ziffer</label>
<input type="text" name="d3totp[]" class="digit" id="5th" inputmode="numeric" pattern="[0-9]*" maxlength="1" onkeyup="clickEvent('4th', '6th')" autocomplete="off">
<label for="6th">sechste TOTP-Ziffer</label>
<input type="text" name="d3totp[]" class="digit" id="6th" inputmode="numeric" pattern="[0-9]*" maxlength="1" onkeyup="clickEvent('5th', null)" autocomplete="off">
</div>
[{capture name="d3js"}]
function clickEvent(previous, next){
const digitKeys = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
const deleteKeys = ['Backspace', 'Delete'];
if(next && digitKeys.includes(event.key)){
document.getElementById(next).focus();
} else if(previous && deleteKeys.includes(event.key)){
document.getElementById(previous).focus();
}
}
document.addEventListener("paste", function(e) {
if (e.target.type === "text") {
var data = e.clipboardData.getData('Text');
data = data.split('');
[].forEach.call(document.querySelectorAll("#login input[type=text]"), (node, index) => {
node.value = data[index];
});
}
});
[{/capture}]
[{oxscript add=$smarty.capture.d3js}]
<div>[{oxmultilang ident="D3_TOTP_INPUT_HELP"}]</div>
<button type="submit" class="btn btn-primary">
[{oxmultilang ident="D3_TOTP_SUBMIT_LOGIN"}]
</button><br>
</form>
<form action="[{$oViewConf->getSelfActionLink()}]" method="post" name="login" id="login">
[{$oViewConf->getHiddenSid()}]
<input type="hidden" name="fnc" value="d3TotpCancelTotpLogin">
<input type="hidden" name="cl" value="[{$oView->getPreviousClass()}]">
[{$navFormParams}]
<button class="btn btn_cancel" type="submit">
[{oxmultilang ident="D3_TOTP_CANCEL_LOGIN"}]
</button>
</form>
</div>
</div>
[{oxstyle include=$oViewConf->getModuleUrl('d3totp', 'out/flow/src/css/d3totplogin.css')}]
[{oxstyle}]
[{insert name="oxid_tracker" title=$template_title}]
[{/capture}]
[{include file="layout/page.tpl"}]