mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
merge master
This commit is contained in:
commit
15bb6f4448
61
.github/workflows/test.yml
vendored
61
.github/workflows/test.yml
vendored
@ -214,41 +214,34 @@ jobs:
|
||||
# run: docker-compose exec -T backend yarn test
|
||||
|
||||
##############################################################################
|
||||
# JOB: UNIT TEST WEBAPP ######################################################
|
||||
# JOB: UNIT TEST FRONTEND ###################################################
|
||||
##############################################################################
|
||||
#unit_test_webapp:
|
||||
# name: Unit tests - webapp
|
||||
# runs-on: ubuntu-latest
|
||||
# needs: [build_test_webapp]
|
||||
# steps:
|
||||
# ##########################################################################
|
||||
# # CHECKOUT CODE ##########################################################
|
||||
# ##########################################################################
|
||||
# - name: Checkout code
|
||||
# uses: actions/checkout@v2
|
||||
# ##########################################################################
|
||||
# # DOWNLOAD DOCKER IMAGES #################################################
|
||||
# ##########################################################################
|
||||
# - name: Download Docker Image (Webapp)
|
||||
# uses: actions/download-artifact@v2
|
||||
# with:
|
||||
# name: docker-webapp-test
|
||||
# path: /tmp
|
||||
# - name: Load Docker Image
|
||||
# run: docker load < /tmp/webapp.tar
|
||||
# ##########################################################################
|
||||
# # UNIT TESTS WEBAPP #####################################################
|
||||
# ##########################################################################
|
||||
# # TODO: Why do we need those .envs?
|
||||
# - name: backend | copy env files webapp
|
||||
# run: cp webapp/.env.template webapp/.env
|
||||
# - name: backend | copy env files backend
|
||||
# run: cp backend/.env.template backend/.env
|
||||
# - name: backend | docker-compose
|
||||
# run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps webapp
|
||||
# - name: webapp | Unit tests
|
||||
# #run: docker run --rm ocelotsocialnetwork/webapp:build yarn run test
|
||||
# run: docker-compose exec -T webapp yarn test
|
||||
unit_test_frontend:
|
||||
name: Unit tests - Frontend
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build_test_frontend]
|
||||
steps:
|
||||
##########################################################################
|
||||
# CHECKOUT CODE ##########################################################
|
||||
##########################################################################
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
##########################################################################
|
||||
# DOWNLOAD DOCKER IMAGES #################################################
|
||||
##########################################################################
|
||||
- name: Download Docker Image (Webapp)
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: docker-frontend-test
|
||||
path: /tmp
|
||||
- name: Load Docker Image
|
||||
run: docker load < /tmp/frontend.tar
|
||||
##########################################################################
|
||||
# UNIT TESTS FRONTEND ####################################################
|
||||
##########################################################################
|
||||
- name: frontend | Unit tests
|
||||
run: docker run --rm gradido/frontend:test yarn run test
|
||||
|
||||
|
||||
#test:
|
||||
# runs-on: ubuntu-latest
|
||||
|
||||
@ -55,7 +55,7 @@ class JsonRequestClientComponent extends Component
|
||||
]), '/getRunningUserTasks');
|
||||
}
|
||||
|
||||
public function getUsers($session_id, $searchString)
|
||||
public function getUsers($session_id, $searchString, $accountState)
|
||||
{
|
||||
if($searchString == "") {
|
||||
return ['state' => 'error', 'type' => 'parameter error', 'msg' => 'search string is empty'];
|
||||
@ -66,7 +66,8 @@ class JsonRequestClientComponent extends Component
|
||||
|
||||
return $this->sendRequest(json_encode([
|
||||
'session_id' => $session_id,
|
||||
'search' => $searchString
|
||||
'search' => $searchString,
|
||||
'account_state' => $accountState,
|
||||
]), '/getUsers');
|
||||
}
|
||||
|
||||
|
||||
@ -197,7 +197,7 @@ class StateBalancesController extends AppController
|
||||
}
|
||||
uasort($transactions, array($this, 'sortTransactions'));
|
||||
$this->set('transactions', $transactions);
|
||||
$this->set('transactionExecutingCount', $session->read('Transaction.executing'));
|
||||
$this->set('transactionExecutingCount', $session->read('Transactions.executing'));
|
||||
$this->set('balance', $session->read('StateUser.balance'));
|
||||
$this->set('timeUsed', microtime(true) - $startTime);
|
||||
$this->set('gdtSum', $gdtSum);
|
||||
@ -370,7 +370,7 @@ class StateBalancesController extends AppController
|
||||
return $this->returnJson([
|
||||
'state' => 'success',
|
||||
'transactions' => $transactions,
|
||||
'transactionExecutingCount' => $session->read('Transaction.executing'),
|
||||
'transactionExecutingCount' => $session->read('Transactions.executing'),
|
||||
'count' => count($transactions),
|
||||
'gdtSum' => $gdtSum,
|
||||
'timeUsed' => microtime(true) - $startTime
|
||||
|
||||
@ -196,7 +196,7 @@ class StateUserTransactionsController extends AppController
|
||||
return $this->returnJson([
|
||||
'state' => 'success',
|
||||
'transactions' => $transactions,
|
||||
'transactionExecutingCount' => $session->read('Transaction.executing'),
|
||||
'transactionExecutingCount' => $session->read('Transactions.executing'),
|
||||
'count' => $all_user_transactions_count,
|
||||
'timeUsed' => microtime(true) - $startTime
|
||||
]);
|
||||
|
||||
@ -99,9 +99,14 @@ class StateUsersController extends AppController
|
||||
//$this->set('timeUsed', $timeUsed);
|
||||
$csfr_token = $this->request->getParam('_csrfToken');
|
||||
$this->set(compact('timeUsed', 'searchForm', 'csfr_token'));
|
||||
|
||||
$empty_string = '... empty ...';
|
||||
if ($this->request->is('post')) {
|
||||
$finalUserEntrys = [];
|
||||
$requestData = $this->request->getData();
|
||||
$account_state = $requestData['account_state'];
|
||||
if($requestData['search'] == '' && $account_state != 'all') {
|
||||
$requestData['search'] = $empty_string;
|
||||
}
|
||||
|
||||
if ($searchForm->validate($requestData)) {
|
||||
//var_dump($requestData);
|
||||
@ -111,7 +116,7 @@ class StateUsersController extends AppController
|
||||
$searchType = 'email';
|
||||
}
|
||||
// find users on login server
|
||||
$resultJson = $this->JsonRequestClient->getUsers($session->read('session_id'), $searchString);
|
||||
$resultJson = $this->JsonRequestClient->getUsers($session->read('session_id'), $searchString, $account_state);
|
||||
$loginServerUser = [];
|
||||
if ($resultJson['state'] == 'success') {
|
||||
$dataJson = $resultJson['data'];
|
||||
@ -139,28 +144,42 @@ class StateUsersController extends AppController
|
||||
}
|
||||
}
|
||||
// find user on community server db
|
||||
$globalSearch = '%' . $searchString . '%';
|
||||
|
||||
|
||||
|
||||
$communityUsers = $this->StateUsers
|
||||
->find('all')
|
||||
->contain(['StateBalances' => ['fields' => ['amount', 'state_user_id']]]);
|
||||
|
||||
$communityUsers->where(['OR' => [
|
||||
'first_name LIKE' => $globalSearch,
|
||||
'last_name LIKE' => $globalSearch,
|
||||
//'username LIKE' => $globalSearch,
|
||||
'email LIKE' => $globalSearch
|
||||
]]);
|
||||
|
||||
//var_dump($communityUsers->toArray());
|
||||
foreach ($communityUsers as $u) {
|
||||
$pubkey_hex = bin2hex(stream_get_contents($u->public_key));
|
||||
$u->public_hex = $pubkey_hex;
|
||||
if (!isset($pubkeySorted[$pubkey_hex])) {
|
||||
$pubkeySorted[$pubkey_hex] = ['login' => [], 'community' => []];
|
||||
|
||||
if($account_state == 'email not activated') {
|
||||
if(count($pubkeySorted) > 0) {
|
||||
$communityUsers->where(['hex(public_key) IN' => array_keys($pubkeySorted)]);
|
||||
} else {
|
||||
$communityUsers = null;
|
||||
}
|
||||
array_push($pubkeySorted[$pubkey_hex]['community'], $u);
|
||||
} else {
|
||||
$globalSearch = '%' . $searchString . '%';
|
||||
$communityUsers->where(['OR' => [
|
||||
'first_name LIKE' => $globalSearch,
|
||||
'last_name LIKE' => $globalSearch,
|
||||
//'username LIKE' => $globalSearch,
|
||||
'email LIKE' => $globalSearch
|
||||
]]);
|
||||
}
|
||||
$finalUserEntrys = [];
|
||||
|
||||
|
||||
//var_dump($communityUsers->toArray());
|
||||
if($communityUsers) {
|
||||
foreach ($communityUsers as $u) {
|
||||
$pubkey_hex = bin2hex(stream_get_contents($u->public_key));
|
||||
$u->public_hex = $pubkey_hex;
|
||||
if (!isset($pubkeySorted[$pubkey_hex])) {
|
||||
$pubkeySorted[$pubkey_hex] = ['login' => [], 'community' => []];
|
||||
}
|
||||
array_push($pubkeySorted[$pubkey_hex]['community'], $u);
|
||||
}
|
||||
}
|
||||
|
||||
// detect states
|
||||
foreach ($pubkeySorted as $pubhex => $user) {
|
||||
$finalUser = [];
|
||||
|
||||
@ -323,7 +323,7 @@ class TransactionCreationsController extends AppController
|
||||
$this->set('firstDayLastMonth', $firstDayLastMonth);
|
||||
$this->set('activeUser', $user);
|
||||
$this->set('creationForm', $creationForm);
|
||||
$this->set('transactionExecutingCount', $session->read('Transaction.executing'));
|
||||
$this->set('transactionExecutingCount', $session->read('Transactions.executing'));
|
||||
$this->set('timeUsed', microtime(true) - $startTime);
|
||||
$this->set('countUsers', $countUsers);
|
||||
$this->set('limit', $limit);
|
||||
|
||||
@ -11,7 +11,9 @@ class UserSearchForm extends Form
|
||||
|
||||
protected function _buildSchema(Schema $schema)
|
||||
{
|
||||
return $schema->addField('search', ['type' => 'string']);
|
||||
return $schema
|
||||
->addField('search', ['type' => 'string'])
|
||||
->addField('account_state', ['type' => 'select']);
|
||||
}
|
||||
|
||||
function validationDefault(Validator $validator)
|
||||
|
||||
@ -11,6 +11,19 @@ $this->assign('title', __('Benutzer suchen'));
|
||||
$this->loadHelper('Form', [
|
||||
'templates' => 'horizontal_form',
|
||||
]);
|
||||
|
||||
$stateOptions = [
|
||||
'all' => __('Alle'),
|
||||
//'account created'=>__('Konto angelegt'),
|
||||
//'account not on login-server' => __('Konto nicht auf Login-Server'),
|
||||
//'email activated' => __('Konto aktiviert'),
|
||||
//'account copied to community' => __('Konto auf Gemeinschafts-Server'),
|
||||
'email not activated' => __('Konto nicht aktiviert'),
|
||||
//'account multiple times on login-server' => __('Konto mehrfach vorhanden'),
|
||||
//'account not on community server' => __('Konto nicht auf Gemeinschafts-Server'),
|
||||
//'no keys' => __('Keine Schlüssel generiert')
|
||||
];
|
||||
|
||||
?>
|
||||
<?= $this->Html->css([
|
||||
'loginServer/style.css',
|
||||
@ -41,7 +54,8 @@ $this->loadHelper('Form', [
|
||||
<p class="form-header">Benutzer suchen</p>
|
||||
<div class="form-body">
|
||||
<?= $this->Form->create($searchForm, []) ?>
|
||||
<?= $this->Form->control('search', ['label' => __('Suchbegriff'), 'class' => 'form-control', 'id' => 'inlineFormInputGroup', 'placeholder' => __('Vorname/Nachname/E-Mail')]) ?>
|
||||
<?= $this->Form->control('search', ['label' => __('Suchbegriff'), 'class' => 'form-control', 'id' => 'inlineFormInputGroup', 'placeholder' => __('Vorname/Nachname/E-Mail'), 'required' => false]) ?>
|
||||
<?= $this->Form->control('account_state', ['label' => __('Konto Status'), 'class' => 'form-control', 'type' => 'select', 'options' => $stateOptions]) ?>
|
||||
<?= $this->Form->button('<i class="material-icons-outlined">search</i> ' . __('Suchen'), ['class' => 'form-button']) ?>
|
||||
<?= $this->Form->hidden('order_row', ['id' => 'input-order-row']) ?>
|
||||
</div>
|
||||
@ -59,7 +73,7 @@ $this->loadHelper('Form', [
|
||||
csfr_token = '<?= $csfr_token ?>';
|
||||
|
||||
</script>
|
||||
<?= $this->Html->script('userSearch') ?>
|
||||
<?= $this->Html->script('userSearch.min') ?>
|
||||
<!-- npm run build im mithril client! -->
|
||||
<!-- keybase://team/gradido/gradido_mithril_user_search -->
|
||||
|
||||
|
||||
@ -185,7 +185,7 @@ process.chdir = function (dir) {
|
||||
process.umask = function() { return 0; };
|
||||
|
||||
},{}],2:[function(require,module,exports){
|
||||
(function (setImmediate,clearImmediate){
|
||||
(function (setImmediate,clearImmediate){(function (){
|
||||
var nextTick = require('process/browser.js').nextTick;
|
||||
var apply = Function.prototype.apply;
|
||||
var slice = Array.prototype.slice;
|
||||
@ -262,9 +262,9 @@ exports.setImmediate = typeof setImmediate === "function" ? setImmediate : funct
|
||||
exports.clearImmediate = typeof clearImmediate === "function" ? clearImmediate : function(id) {
|
||||
delete immediateIds[id];
|
||||
};
|
||||
}).call(this,require("timers").setImmediate,require("timers").clearImmediate)
|
||||
}).call(this)}).call(this,require("timers").setImmediate,require("timers").clearImmediate)
|
||||
},{"process/browser.js":1,"timers":2}],3:[function(require,module,exports){
|
||||
(function (global,setImmediate){
|
||||
(function (global,setImmediate){(function (){
|
||||
new function() {
|
||||
|
||||
function Vnode(tag, key, attrs0, children, text, dom) {
|
||||
@ -1425,7 +1425,7 @@ m.vnode = Vnode
|
||||
if (typeof module !== "undefined") module["exports"] = m
|
||||
else window.m = m
|
||||
}
|
||||
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("timers").setImmediate)
|
||||
}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("timers").setImmediate)
|
||||
},{"timers":2}],4:[function(require,module,exports){
|
||||
/*! @preserve
|
||||
* numeral.js
|
||||
@ -2442,10 +2442,10 @@ return numeral;
|
||||
}));
|
||||
|
||||
},{}],5:[function(require,module,exports){
|
||||
(function (global){
|
||||
(function (global){(function (){
|
||||
/**!
|
||||
* @fileOverview Kickass library to create and place poppers near their reference elements.
|
||||
* @version 1.16.0
|
||||
* @version 1.16.1
|
||||
* @license
|
||||
* Copyright (c) 2016 Federico Zivolo and contributors
|
||||
*
|
||||
@ -2797,7 +2797,7 @@ function getBordersSize(styles, axis) {
|
||||
var sideA = axis === 'x' ? 'Left' : 'Top';
|
||||
var sideB = sideA === 'Left' ? 'Right' : 'Bottom';
|
||||
|
||||
return parseFloat(styles['border' + sideA + 'Width'], 10) + parseFloat(styles['border' + sideB + 'Width'], 10);
|
||||
return parseFloat(styles['border' + sideA + 'Width']) + parseFloat(styles['border' + sideB + 'Width']);
|
||||
}
|
||||
|
||||
function getSize(axis, body, html, computedStyle) {
|
||||
@ -2952,8 +2952,8 @@ function getOffsetRectRelativeToArbitraryNode(children, parent) {
|
||||
var scrollParent = getScrollParent(children);
|
||||
|
||||
var styles = getStyleComputedProperty(parent);
|
||||
var borderTopWidth = parseFloat(styles.borderTopWidth, 10);
|
||||
var borderLeftWidth = parseFloat(styles.borderLeftWidth, 10);
|
||||
var borderTopWidth = parseFloat(styles.borderTopWidth);
|
||||
var borderLeftWidth = parseFloat(styles.borderLeftWidth);
|
||||
|
||||
// In cases where the parent is fixed, we must ignore negative scroll in offset calc
|
||||
if (fixedPosition && isHTML) {
|
||||
@ -2974,8 +2974,8 @@ function getOffsetRectRelativeToArbitraryNode(children, parent) {
|
||||
// differently when margins are applied to it. The margins are included in
|
||||
// the box of the documentElement, in the other cases not.
|
||||
if (!isIE10 && isHTML) {
|
||||
var marginTop = parseFloat(styles.marginTop, 10);
|
||||
var marginLeft = parseFloat(styles.marginLeft, 10);
|
||||
var marginTop = parseFloat(styles.marginTop);
|
||||
var marginLeft = parseFloat(styles.marginLeft);
|
||||
|
||||
offsets.top -= borderTopWidth - marginTop;
|
||||
offsets.bottom -= borderTopWidth - marginTop;
|
||||
@ -3914,8 +3914,8 @@ function arrow(data, options) {
|
||||
// Compute the sideValue using the updated popper offsets
|
||||
// take popper margin in account because we don't have this info available
|
||||
var css = getStyleComputedProperty(data.instance.popper);
|
||||
var popperMarginSide = parseFloat(css['margin' + sideCapitalized], 10);
|
||||
var popperBorderSide = parseFloat(css['border' + sideCapitalized + 'Width'], 10);
|
||||
var popperMarginSide = parseFloat(css['margin' + sideCapitalized]);
|
||||
var popperBorderSide = parseFloat(css['border' + sideCapitalized + 'Width']);
|
||||
var sideValue = center - data.offsets.popper[side] - popperMarginSide - popperBorderSide;
|
||||
|
||||
// prevent arrowElement from being placed not contiguously to its popper
|
||||
@ -5068,11 +5068,11 @@ return Popper;
|
||||
})));
|
||||
|
||||
|
||||
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
|
||||
}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
|
||||
},{}],6:[function(require,module,exports){
|
||||
(function (process){
|
||||
(function (process){(function (){
|
||||
/**!
|
||||
* tippy.js v5.1.4
|
||||
* tippy.js v5.2.1
|
||||
* (c) 2017-2020 atomiks
|
||||
* MIT License
|
||||
*/
|
||||
@ -5100,7 +5100,7 @@ function _extends() {
|
||||
return _extends.apply(this, arguments);
|
||||
}
|
||||
|
||||
var version = "5.1.4";
|
||||
var version = "5.2.1";
|
||||
|
||||
/**
|
||||
* Triggers reflow
|
||||
@ -5749,7 +5749,6 @@ function bindGlobalEventListeners() {
|
||||
var isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined';
|
||||
var ua = isBrowser ? navigator.userAgent : '';
|
||||
var isIE = /MSIE |Trident\//.test(ua);
|
||||
var isUCBrowser = /UCBrowser\//.test(ua);
|
||||
var isIOS = isBrowser && /iPhone|iPad|iPod/.test(navigator.platform);
|
||||
function updateIOSClass(isAdd) {
|
||||
var shouldAdd = isAdd && isIOS && currentInput.isTouch;
|
||||
@ -5939,8 +5938,9 @@ function updatePopperElement(popper, prevProps, nextProps) {
|
||||
*/
|
||||
|
||||
function updateTransitionEndListener(tooltip, action, listener) {
|
||||
var eventName = isUCBrowser && document.body.style.webkitTransition !== undefined ? 'webkitTransitionEnd' : 'transitionend';
|
||||
tooltip[action + 'EventListener'](eventName, listener);
|
||||
['transitionend', 'webkitTransitionEnd'].forEach(function (event) {
|
||||
tooltip[action + 'EventListener'](event, listener);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Adds/removes theme from tooltip's classList
|
||||
@ -6070,6 +6070,7 @@ function createTippy(reference, passedProps) {
|
||||
var pluginsHooks = plugins.map(function (plugin) {
|
||||
return plugin.fn(instance);
|
||||
});
|
||||
var hadAriaExpandedAttributeOnCreate = reference.hasAttribute('aria-expanded');
|
||||
addListenersToTriggerTarget();
|
||||
handleAriaExpandedAttribute();
|
||||
|
||||
@ -6090,8 +6091,9 @@ function createTippy(reference, passedProps) {
|
||||
instance.clearDelayTimeouts();
|
||||
}
|
||||
});
|
||||
popper.addEventListener('mouseleave', function () {
|
||||
popper.addEventListener('mouseleave', function (event) {
|
||||
if (instance.props.interactive && includes(instance.props.trigger, 'mouseenter')) {
|
||||
debouncedOnMouseMove(event);
|
||||
doc.addEventListener('mousemove', debouncedOnMouseMove);
|
||||
}
|
||||
});
|
||||
@ -6170,6 +6172,13 @@ function createTippy(reference, passedProps) {
|
||||
}
|
||||
|
||||
function handleAriaExpandedAttribute() {
|
||||
// If the user has specified `aria-expanded` on their reference when the
|
||||
// instance was created, we have to assume they're controlling it externally
|
||||
// themselves
|
||||
if (hadAriaExpandedAttributeOnCreate) {
|
||||
return;
|
||||
}
|
||||
|
||||
var nodes = normalizeToArray(instance.props.triggerTarget || reference);
|
||||
nodes.forEach(function (node) {
|
||||
if (instance.props.interactive) {
|
||||
@ -6300,7 +6309,11 @@ function createTippy(reference, passedProps) {
|
||||
break;
|
||||
|
||||
case 'focus':
|
||||
on(isIE ? 'focusout' : 'blur', onBlur);
|
||||
on(isIE ? 'focusout' : 'blur', onBlurOrFocusOut);
|
||||
break;
|
||||
|
||||
case 'focusin':
|
||||
on('focusout', onBlurOrFocusOut);
|
||||
break;
|
||||
}
|
||||
});
|
||||
@ -6371,7 +6384,7 @@ function createTippy(reference, passedProps) {
|
||||
return el === reference || el === popper;
|
||||
});
|
||||
|
||||
if (isCursorOverReferenceOrPopper) {
|
||||
if (event.type === 'mousemove' && isCursorOverReferenceOrPopper) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -6405,14 +6418,15 @@ function createTippy(reference, passedProps) {
|
||||
doc.body.addEventListener('mouseleave', scheduleHide);
|
||||
doc.addEventListener('mousemove', debouncedOnMouseMove);
|
||||
pushIfUnique(mouseMoveListeners, debouncedOnMouseMove);
|
||||
debouncedOnMouseMove(event);
|
||||
return;
|
||||
}
|
||||
|
||||
scheduleHide(event);
|
||||
}
|
||||
|
||||
function onBlur(event) {
|
||||
if (event.target !== getCurrentTarget()) {
|
||||
function onBlurOrFocusOut(event) {
|
||||
if (!includes(instance.props.trigger, 'focusin') && event.target !== getCurrentTarget()) {
|
||||
return;
|
||||
} // If focus was moved to within the popper
|
||||
|
||||
@ -6993,7 +7007,6 @@ exports.hideAll = hideAll;
|
||||
exports.includes = includes;
|
||||
exports.isBrowser = isBrowser;
|
||||
exports.isMouseEvent = isMouseEvent;
|
||||
exports.isUCBrowser = isUCBrowser;
|
||||
exports.normalizeToArray = normalizeToArray;
|
||||
exports.removeProperties = removeProperties;
|
||||
exports.setVisibilityState = setVisibilityState;
|
||||
@ -7002,11 +7015,11 @@ exports.useIfDefined = useIfDefined;
|
||||
exports.warnWhen = warnWhen;
|
||||
|
||||
|
||||
}).call(this,require('_process'))
|
||||
}).call(this)}).call(this,require('_process'))
|
||||
},{"_process":1,"popper.js":5}],7:[function(require,module,exports){
|
||||
(function (process){
|
||||
(function (process){(function (){
|
||||
/**!
|
||||
* tippy.js v5.1.4
|
||||
* tippy.js v5.2.1
|
||||
* (c) 2017-2020 atomiks
|
||||
* MIT License
|
||||
*/
|
||||
@ -7256,7 +7269,7 @@ var animateFill = {
|
||||
var _instance$popperChild = instance.popperChildren,
|
||||
tooltip = _instance$popperChild.tooltip,
|
||||
content = _instance$popperChild.content;
|
||||
var backdrop = instance.props.animateFill && !index.isUCBrowser ? createBackdropElement() : null;
|
||||
var backdrop = instance.props.animateFill ? createBackdropElement() : null;
|
||||
|
||||
function addBackdropToPopperChildren() {
|
||||
instance.popperChildren.backdrop = backdrop;
|
||||
@ -7391,7 +7404,7 @@ var followCursor = {
|
||||
// scroll for "vertical"
|
||||
|
||||
|
||||
if (getIsEnabled() && (getIsInitialBehavior() || instance.props.followCursor !== true)) {
|
||||
if (getIsEnabled() && getIsInitialBehavior()) {
|
||||
instance.popperInstance.disableEventListeners();
|
||||
}
|
||||
}
|
||||
@ -7432,7 +7445,6 @@ var followCursor = {
|
||||
var isCursorOverReference = index.closestCallback(event.target, function (el) {
|
||||
return el === reference;
|
||||
});
|
||||
var rect = reference.getBoundingClientRect();
|
||||
var followCursor = instance.props.followCursor;
|
||||
var isHorizontal = followCursor === 'horizontal';
|
||||
var isVertical = followCursor === 'vertical';
|
||||
@ -7456,6 +7468,7 @@ var followCursor = {
|
||||
clientWidth: 0,
|
||||
clientHeight: 0,
|
||||
getBoundingClientRect: function getBoundingClientRect() {
|
||||
var rect = reference.getBoundingClientRect();
|
||||
return {
|
||||
width: isVerticalPlacement ? size : 0,
|
||||
height: isVerticalPlacement ? 0 : size,
|
||||
@ -7721,7 +7734,7 @@ exports.inlinePositioning = inlinePositioning;
|
||||
exports.sticky = sticky;
|
||||
|
||||
|
||||
}).call(this,require('_process'))
|
||||
}).call(this)}).call(this,require('_process'))
|
||||
},{"./tippy.chunk.cjs.js":6,"_process":1,"popper.js":5}],8:[function(require,module,exports){
|
||||
'use strict';
|
||||
|
||||
@ -7749,7 +7762,7 @@ function launch() {
|
||||
});
|
||||
})(document, window, domIsReady);
|
||||
|
||||
},{"./texte/de":14,"./view":15,"mithril":3}],9:[function(require,module,exports){
|
||||
},{"./texte/de":13,"./view":14,"mithril":3}],9:[function(require,module,exports){
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
@ -7906,24 +7919,6 @@ exports["default"] = _default;
|
||||
},{"mithril":3}],12:[function(require,module,exports){
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports["default"] = encode;
|
||||
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
function encode(receiver, subject, body) {
|
||||
//return encodeURIComponent(receiver + '?subject=' + subject + '&body=' + body)
|
||||
return encodeURIComponent(receiver) + '?subject=' + encodeURIComponent(subject) + '&body=' + encodeURIComponent(body);
|
||||
}
|
||||
|
||||
},{}],13:[function(require,module,exports){
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
@ -7949,9 +7944,7 @@ __('account multiple times on login-server');
|
||||
__('account not on community server');
|
||||
__('no keys');
|
||||
*/
|
||||
var AccountState =
|
||||
/*#__PURE__*/
|
||||
function () {
|
||||
var AccountState = /*#__PURE__*/function () {
|
||||
function AccountState(stateName) {
|
||||
_classCallCheck(this, AccountState);
|
||||
|
||||
@ -8050,7 +8043,7 @@ function () {
|
||||
|
||||
exports["default"] = AccountState;
|
||||
|
||||
},{"mithril":3}],14:[function(require,module,exports){
|
||||
},{"mithril":3}],13:[function(require,module,exports){
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
@ -8226,7 +8219,6 @@ var _default = {
|
||||
DELETE_FROM_COMMUNITY_SUCCESS: 'Benutzer Konto vom Gemeinschafts-Server erfolgreich gelöscht',
|
||||
VERIFICATION_EMAIL_RESEND: 'Verification Email erneut zusenden',
|
||||
VERIFICATION_EMAIL_RESEND_SUCCESS: 'Verification Email wird erneut zugestellt',
|
||||
MAILTO_VERIFICATION_EMAIL: 'Verification Email selbst verschicken',
|
||||
COPY_FAILED: 'Fehler beim Kopieren',
|
||||
DELETE_FAILED: 'Fehler beim löschen',
|
||||
RESEND_FAILED: 'Senden fehlgeschlagen',
|
||||
@ -8240,7 +8232,7 @@ var _default = {
|
||||
};
|
||||
exports["default"] = _default;
|
||||
|
||||
},{}],15:[function(require,module,exports){
|
||||
},{}],14:[function(require,module,exports){
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
@ -8305,7 +8297,7 @@ var _default = {
|
||||
};
|
||||
exports["default"] = _default;
|
||||
|
||||
},{"../model/AccountState":13,"./userTable":23,"mithril":3}],16:[function(require,module,exports){
|
||||
},{"../model/AccountState":12,"./userTable":21,"mithril":3}],15:[function(require,module,exports){
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
@ -8405,7 +8397,7 @@ var _default = {
|
||||
};
|
||||
exports["default"] = _default;
|
||||
|
||||
},{"../../../lib/dialog":11,"mithril":3}],17:[function(require,module,exports){
|
||||
},{"../../../lib/dialog":11,"mithril":3}],16:[function(require,module,exports){
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
@ -8450,7 +8442,7 @@ var _default = {
|
||||
};
|
||||
exports["default"] = _default;
|
||||
|
||||
},{"./actionBase":16,"mithril":3}],18:[function(require,module,exports){
|
||||
},{"./actionBase":15,"mithril":3}],17:[function(require,module,exports){
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
@ -8575,7 +8567,7 @@ var _default = {
|
||||
};
|
||||
exports["default"] = _default;
|
||||
|
||||
},{"../../../lib/dialog":11,"./actionBase":16,"mithril":3}],19:[function(require,module,exports){
|
||||
},{"../../../lib/dialog":11,"./actionBase":15,"mithril":3}],18:[function(require,module,exports){
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
@ -8671,127 +8663,7 @@ var _default = {
|
||||
};
|
||||
exports["default"] = _default;
|
||||
|
||||
},{"../../../lib/dialog":11,"./actionBase":16,"mithril":3}],20:[function(require,module,exports){
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports["default"] = void 0;
|
||||
|
||||
var _mithril = _interopRequireDefault(require("mithril"));
|
||||
|
||||
var _actionBase = _interopRequireDefault(require("./actionBase"));
|
||||
|
||||
var _dialog = _interopRequireDefault(require("../../../lib/dialog"));
|
||||
|
||||
var _emailToLink = _interopRequireDefault(require("../../../lib/emailToLink"));
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
|
||||
|
||||
/*
|
||||
* @author: Dario Rekowski
|
||||
*
|
||||
* @date: 20.03.20
|
||||
*
|
||||
* @brief: getting email verification code in silence
|
||||
*/
|
||||
function oninit(vnode) {
|
||||
vnode.state.loading = true;
|
||||
vnode.state.results = null;
|
||||
vnode.state.additionalUserData = [];
|
||||
vnode.state.serverData = [];
|
||||
|
||||
_mithril["default"].request({
|
||||
method: 'POST',
|
||||
url: window.location.protocol + '//' + document.domain + '/state-users/ajaxGetUserEmailVerificationCode',
|
||||
data: vnode.attrs.user,
|
||||
headers: {
|
||||
'X-CSRF-Token': csfr_token
|
||||
}
|
||||
}).then(function (result) {
|
||||
vnode.state.loading = false;
|
||||
|
||||
if (result.state === 'success') {
|
||||
vnode.state.copyResult = 'success';
|
||||
vnode.state.additionalUserData = result.data.userData;
|
||||
vnode.state.serverData = result.data.server; //console.log("ajax result: %o", result)
|
||||
} else {//console.log("result error")
|
||||
}
|
||||
})["catch"](function (e) {
|
||||
vnode.state.loading = false;
|
||||
console.error("ajax error: %s in file: %s in line: %d", e.message, e.fileName, e.lineNumber);
|
||||
});
|
||||
}
|
||||
|
||||
function getField(vnode, index) {
|
||||
if (null === vnode.state.results) {
|
||||
return (0, _mithril["default"])('i.spinner-border.spinner-border-sm');
|
||||
} else if (index in vnode.state.results) {
|
||||
return vnode.state.results[index];
|
||||
} else {
|
||||
return '0';
|
||||
}
|
||||
}
|
||||
|
||||
function view(vnode) {
|
||||
var email = vnode.attrs.user.email;
|
||||
var first_name = vnode.attrs.user.first_name;
|
||||
var last_name = vnode.attrs.user.last_name;
|
||||
var recevier = first_name + ' ' + last_name + ' <' + email + '>';
|
||||
var userData = vnode.state.additionalUserData;
|
||||
var serverData = vnode.state.serverData; //console.log('Server data: %o', serverData)
|
||||
//vnode.state.additionalUserData.verificationCode
|
||||
|
||||
var link = serverData['loginServer.path'] + '/checkEmail/' + userData['EmailVerificationCode.Register'];
|
||||
var body = 'Liebe(r) ' + first_name + ' ' + last_name + ',\n\
|
||||
\n\
|
||||
Der Admin hat ein erneutes zusenden deiner Bestätigungsemail angefordert. \n\
|
||||
Du hast vor einer Weile ein Gradido-Konto mit dieser E-Mail angelegt, aber es noch nicht bestätigt. \n\
|
||||
\n\
|
||||
Bitte klicke zur Bestätigung auf den Link: ' + link + '\n\
|
||||
oder kopiere den obigen Link in Dein Browserfenster.\n\
|
||||
\n\
|
||||
Mit freundlichen Grüßen\n\
|
||||
Dario, Gradido Server Admin\n\
|
||||
';
|
||||
|
||||
if (true === vnode.state.loading) {
|
||||
return (0, _mithril["default"])('span', [(0, _mithril["default"])('span', [(0, _mithril["default"])('button.btn.btn-secondary.btn-xs', {
|
||||
title: window.texte.MAILTO_VERIFICATION_EMAIL,
|
||||
disabled: true
|
||||
}, (0, _mithril["default"])('i.spinner-border.spinner-border-sm')), window.texte.MAILTO_VERIFICATION_EMAIL])]);
|
||||
} else {
|
||||
return (0, _mithril["default"])('span', [(0, _mithril["default"])('span', [(0, _mithril["default"])('a.btn.btn-primary.btn-xs', {
|
||||
title: window.texte.MAILTO_VERIFICATION_EMAIL,
|
||||
href: 'mailto:' + (0, _emailToLink["default"])(recevier, 'Gradido: E-Mail Verification', body)
|
||||
}, (0, _mithril["default"])('i.mdi.mdi-email-outline')), window.texte.MAILTO_VERIFICATION_EMAIL])]);
|
||||
}
|
||||
|
||||
return (0, _mithril["default"])('span', [(0, _mithril["default"])('span', [(0, _mithril["default"])('a.btn.btn-secondary.btn-xs', {
|
||||
title: window.texte.MAILTO_VERIFICATION_EMAIL,
|
||||
href: 'mailto:' + (0, _emailToLink["default"])(recevier, 'Gradido: E-Mail Verification', body),
|
||||
disabled: vnode.state.loading === true
|
||||
}, vnode.state.loading === true ? (0, _mithril["default"])('i.spinner-border.spinner-border-sm') : (0, _mithril["default"])('i.mdi.mdi-email-outline')), window.texte.MAILTO_VERIFICATION_EMAIL])]);
|
||||
/*return m('span', [
|
||||
window.texte.RECEIVE_TRANSACTIONS_COUNT,
|
||||
getField(vnode, 'receive'),
|
||||
', ',
|
||||
window.texte.SENDED_TRANSACTIONS_COUNT,
|
||||
getField(vnode, 'sended'),
|
||||
', ',
|
||||
window.texte.CREATION_TRANSACTIONS_COUNT,
|
||||
getField(vnode, 'creation')
|
||||
])*/
|
||||
}
|
||||
|
||||
var _default = {
|
||||
view: view,
|
||||
oninit: oninit
|
||||
};
|
||||
exports["default"] = _default;
|
||||
|
||||
},{"../../../lib/dialog":11,"../../../lib/emailToLink":12,"./actionBase":16,"mithril":3}],21:[function(require,module,exports){
|
||||
},{"../../../lib/dialog":11,"./actionBase":15,"mithril":3}],19:[function(require,module,exports){
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
@ -8830,12 +8702,12 @@ function oninit(vnode) {
|
||||
vnode.state.loading = false;
|
||||
|
||||
if (result.state === 'success') {
|
||||
//vnode.state.message = m('div.alert.alert-success', window.texte.DELETE_FROM_COMMUNITY_SUCCESS)
|
||||
vnode.state.message = (0, _mithril["default"])('div.alert.alert-success', window.texte.DELETE_FROM_COMMUNITY_SUCCESS);
|
||||
vnode.state.copyResult = 'success';
|
||||
vnode.state.results = result.counts;
|
||||
} else {
|
||||
//console.log("result error")
|
||||
//vnode.state.message = m('div.alert.alert-danger', window.texte.DELETE_FAILED)
|
||||
vnode.state.message = (0, _mithril["default"])('div.alert.alert-danger', window.texte.DELETE_FAILED);
|
||||
vnode.state.copyResult = 'error';
|
||||
}
|
||||
})["catch"](function (e) {
|
||||
@ -8866,7 +8738,7 @@ var _default = {
|
||||
};
|
||||
exports["default"] = _default;
|
||||
|
||||
},{"../../../lib/dialog":11,"./actionBase":16,"mithril":3}],22:[function(require,module,exports){
|
||||
},{"../../../lib/dialog":11,"./actionBase":15,"mithril":3}],20:[function(require,module,exports){
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
@ -8896,7 +8768,7 @@ function view(vnode) {
|
||||
ajaxData: vnode.attrs.user,
|
||||
alertSuccess: window.texte.VERIFICATION_EMAIL_RESEND_SUCCESS,
|
||||
alertFailed: window.texte.RESEND_FAILED,
|
||||
btnColor: 'btn-gradido-orange',
|
||||
btnColor: 'btn-primary',
|
||||
btnSymbol: 'mdi-email',
|
||||
btnTitle: window.texte.VERIFICATION_EMAIL_RESEND,
|
||||
progessText: window.texte.RESEND_IN_PROGRESS
|
||||
@ -8908,7 +8780,7 @@ var _default = {
|
||||
};
|
||||
exports["default"] = _default;
|
||||
|
||||
},{"./actionBase":16,"mithril":3}],23:[function(require,module,exports){
|
||||
},{"./actionBase":15,"mithril":3}],21:[function(require,module,exports){
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
@ -8932,6 +8804,10 @@ function oninit(vnode) {
|
||||
}
|
||||
|
||||
vnode.state.openedUser = -1;
|
||||
vnode.state.order = {
|
||||
field: 'default',
|
||||
dir: 'DESC'
|
||||
};
|
||||
}
|
||||
|
||||
function openButtonClick(vnode, index) {
|
||||
@ -8942,6 +8818,27 @@ function openButtonClick(vnode, index) {
|
||||
}
|
||||
}
|
||||
|
||||
function changeOrder(vnode, fieldName) {
|
||||
var field = vnode.state.order.field;
|
||||
var dir = vnode.state.order.dir;
|
||||
|
||||
if (field != fieldName) {
|
||||
vnode.state.order.field = fieldName;
|
||||
vnode.state.order.dir = 'DESC';
|
||||
} else if (field == fieldName) {
|
||||
var new_dir;
|
||||
|
||||
if (dir == 'DESC') {
|
||||
new_dir = 'ASC';
|
||||
} else {
|
||||
new_dir = 'DESC';
|
||||
}
|
||||
|
||||
vnode.state.order.dir = new_dir;
|
||||
} //console.log("change to %s %s", vnode.state.order.field, vnode.state.order.dir)
|
||||
|
||||
}
|
||||
|
||||
function updateStateForActiveUser(newState, vnode) {
|
||||
//console.log('updateStateForActiveUser')
|
||||
if (-1 !== vnode.state.openedUser) {
|
||||
@ -8957,12 +8854,154 @@ function deleteActiveUser(vnode) {
|
||||
}
|
||||
}
|
||||
|
||||
function sortByCreated(var1, var2, dir) {
|
||||
if (var1.created == var2.created) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var var1_date = new Date(var1.created);
|
||||
var var2_date = new Date(var2.created); // kleiner als null => niedriger index, a kommt zuerst, b kommt als nächstes
|
||||
// größer als null => höherer index, b kommt zuerst
|
||||
|
||||
if (dir == 'DESC') {
|
||||
// descending
|
||||
if (var1_date < var2_date) {
|
||||
return 1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
// ascending
|
||||
if (var1_date < var2_date) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function sortyByString(var1, var2, dir, field) {
|
||||
if (var1[field] == var2[field]) {
|
||||
return 0;
|
||||
} // kleiner als null => niedriger index, a kommt zuerst, b kommt als nächstes
|
||||
// größer als null => höherer index, b kommt zuerst
|
||||
|
||||
|
||||
if (dir == 'DESC') {
|
||||
// descending
|
||||
if (var1[field] < var2[field]) {
|
||||
return 1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
// ascending
|
||||
if (var1[field] < var2[field]) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function sortByBalance(var1, var2, dir) {
|
||||
if (var1.balance == var2.balance) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var var1_balance = parseFloat(var1.balance);
|
||||
var var2_balance = parseFloat(var2.balance); // kleiner als null => niedriger index, a kommt zuerst, b kommt als nächstes
|
||||
// größer als null => höherer index, b kommt zuerst
|
||||
|
||||
if (dir == 'DESC') {
|
||||
// descending
|
||||
if (var1_balance < var2_balance) {
|
||||
return 1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
// ascending
|
||||
if (var1_balance < var2_balance) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
} // js sort work in place, so no copy is created
|
||||
|
||||
|
||||
function sort(vnode, user) {
|
||||
var field = vnode.state.order.field;
|
||||
var dir = vnode.state.order.dir;
|
||||
|
||||
if (field == 'created') {
|
||||
user.sort(function (var1, var2) {
|
||||
return sortByCreated(var1, var2, dir);
|
||||
});
|
||||
} else if (field == 'name' || field == 'email' || field == 'pubkeyhex') {
|
||||
user.sort(function (var1, var2) {
|
||||
return sortyByString(var1, var2, dir, field);
|
||||
});
|
||||
} else if (field == 'balance') {
|
||||
user.sort(function (var1, var2) {
|
||||
return sortByBalance(var1, var2, dir);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getArrow(vnode, fieldName) {
|
||||
// Arrow-up: ↑
|
||||
// Arrow-down: ↓
|
||||
if (vnode.state.order.field == fieldName) {
|
||||
if (vnode.state.order.dir == 'DESC') {
|
||||
return _mithril["default"].trust('↓ ');
|
||||
} else {
|
||||
return _mithril["default"].trust('↑ ');
|
||||
}
|
||||
}
|
||||
|
||||
return _mithril["default"].trust(' ');
|
||||
}
|
||||
|
||||
function view(vnode) {
|
||||
// js sort work in place, so me made a deep copy first
|
||||
var user_sorted = JSON.parse(JSON.stringify(vnode.state.orderedUsers));
|
||||
|
||||
if (vnode.state.order.field != 'default') {
|
||||
sort(vnode, user_sorted);
|
||||
}
|
||||
|
||||
return (0, _mithril["default"])('table.table.table-hover.table-sm', [(0, _mithril["default"])('thead', (0, _mithril["default"])('tr.solid-header', [(0, _mithril["default"])('th', {
|
||||
style: {
|
||||
'padding-left': '1.5rem'
|
||||
}
|
||||
}), (0, _mithril["default"])('th', window.texte.NAME), (0, _mithril["default"])('th', window.texte.EMAIL), (0, _mithril["default"])('th', window.texte.BALANCE), (0, _mithril["default"])('th', _mithril["default"].trust(window.texte.PUBLIC_KEY)), (0, _mithril["default"])('th', window.texte.CREATED)])), (0, _mithril["default"])('tbody', vnode.state.orderedUsers.map(function (value, index) {
|
||||
}), (0, _mithril["default"])('th', (0, _mithril["default"])('a', {
|
||||
onclick: function onclick() {
|
||||
changeOrder(vnode, 'name');
|
||||
},
|
||||
className: 'grd_clickable'
|
||||
}, [getArrow(vnode, 'name'), window.texte.NAME])), (0, _mithril["default"])('th', (0, _mithril["default"])('a', {
|
||||
onclick: function onclick() {
|
||||
changeOrder(vnode, 'email');
|
||||
},
|
||||
className: 'grd_clickable'
|
||||
}, [getArrow(vnode, 'email'), window.texte.EMAIL])), (0, _mithril["default"])('th', (0, _mithril["default"])('a', {
|
||||
onclick: function onclick() {
|
||||
changeOrder(vnode, 'balance');
|
||||
},
|
||||
className: 'grd_clickable'
|
||||
}, [getArrow(vnode, 'balance'), window.texte.BALANCE])), (0, _mithril["default"])('th', (0, _mithril["default"])('a', {
|
||||
onclick: function onclick() {
|
||||
changeOrder(vnode, 'pubkeyhex');
|
||||
},
|
||||
className: 'grd_clickable'
|
||||
}, [getArrow(vnode, 'pubkeyhex'), _mithril["default"].trust(window.texte.PUBLIC_KEY)])), (0, _mithril["default"])('th', (0, _mithril["default"])('a', {
|
||||
onclick: function onclick() {
|
||||
changeOrder(vnode, 'created');
|
||||
},
|
||||
className: 'grd_clickable'
|
||||
}, [getArrow(vnode, 'created'), window.texte.CREATED]))])), (0, _mithril["default"])('tbody', user_sorted.map(function (value, index) {
|
||||
var open = vnode.state.openedUser === index;
|
||||
return [(0, _mithril["default"])(_rowView["default"], {
|
||||
user: value,
|
||||
@ -8989,7 +9028,7 @@ var _default = {
|
||||
};
|
||||
exports["default"] = _default;
|
||||
|
||||
},{"./rowAction":24,"./rowView":25,"mithril":3}],24:[function(require,module,exports){
|
||||
},{"./rowAction":22,"./rowView":23,"mithril":3}],22:[function(require,module,exports){
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
@ -9009,8 +9048,6 @@ var _copyCommunityLogin = _interopRequireDefault(require("./actions/copyCommunit
|
||||
|
||||
var _verificationResend = _interopRequireDefault(require("./actions/verificationResend"));
|
||||
|
||||
var _mailtoVerificationResend = _interopRequireDefault(require("./actions/mailtoVerificationResend"));
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
|
||||
|
||||
var checkTodoAction = new RegExp(/{{([a-z-]*)}}/);
|
||||
@ -9034,9 +9071,6 @@ function getAction(name) {
|
||||
|
||||
case 'verification-resend':
|
||||
return _verificationResend["default"];
|
||||
|
||||
case 'mailto-verification-resend':
|
||||
return _mailtoVerificationResend["default"];
|
||||
}
|
||||
|
||||
return null;
|
||||
@ -9077,7 +9111,7 @@ var _default = {
|
||||
};
|
||||
exports["default"] = _default;
|
||||
|
||||
},{"./actions/copyCommunityLogin":17,"./actions/copyLoginCommunity":18,"./actions/deleteCommunityServer":19,"./actions/mailtoVerificationResend":20,"./actions/userTransactionsOverview":21,"./actions/verificationResend":22,"mithril":3}],25:[function(require,module,exports){
|
||||
},{"./actions/copyCommunityLogin":16,"./actions/copyLoginCommunity":17,"./actions/deleteCommunityServer":18,"./actions/userTransactionsOverview":19,"./actions/verificationResend":20,"mithril":3}],23:[function(require,module,exports){
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
@ -9148,4 +9182,4 @@ var _default = {
|
||||
};
|
||||
exports["default"] = _default;
|
||||
|
||||
},{"../../lib/Gradido":9,"../../lib/Tooltip":10,"../../model/AccountState":13,"mithril":3}]},{},[8]);
|
||||
},{"../../lib/Gradido":9,"../../lib/Tooltip":10,"../../model/AccountState":12,"mithril":3}]},{},[8]);
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -15,8 +15,10 @@ mit:
|
||||
```ini
|
||||
unsercure.allow_cors_all = 1
|
||||
```
|
||||
Wird bei allen JSON-Requests zum Header: Access-Control-Allow-Origin:*
|
||||
hinzugefügt.
|
||||
Wird bei allen JSON-Requests zum Header hinzugefügt:
|
||||
- Access-Control-Allow-Origin:*
|
||||
- Access-Control-Allow-Headers: "Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers"
|
||||
|
||||
|
||||
In diesen Beispielen gehe ich jetzt davon aus, das du das gesamte Gradido Projekt mit Docker gebaut hast und auf dem lokalen Rechner laufen lässt.
|
||||
|
||||
@ -81,4 +83,130 @@ data: {"session_id": -127182}
|
||||
Wenn alles okay ist erhältst du:
|
||||
```json
|
||||
{"state":"success"}
|
||||
```
|
||||
```
|
||||
|
||||
## Update User Data
|
||||
Update first name, last name, user language and enable/disable user
|
||||
Language currently supported de and en
|
||||
User will be disabled if he wants a account delete but has transactions.
|
||||
Until transactions are saved in real blockchain, we need this data because the public key
|
||||
is in db only saved in state_users so we wenn delete this entry, validating all transactions not longer possible.
|
||||
Disabled User cannot login and cannot receive transactions.
|
||||
In update Object only one of the sets needs to be there.
|
||||
|
||||
Update password can only be used if in Login-Server config:
|
||||
```ini
|
||||
unsecure.allow_passwort_via_json_request = 1
|
||||
```
|
||||
|
||||
POST http://localhost/login_api/updateUserInfos
|
||||
```json
|
||||
{"session_id": -127182, "email": "max.musterman@gmail.de", "update": {
|
||||
"User.first_name": "Max",
|
||||
"User.last_name" : "Musterman",
|
||||
"User.disabled": 0,
|
||||
"User.language": "de"
|
||||
"User.password": "1234"
|
||||
}
|
||||
}
|
||||
```
|
||||
also valid
|
||||
```json
|
||||
{"session_id": -127182, "email": "max.musterman@gmail.de", "update": {
|
||||
"User.last_name" : "Musterman"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
It returns if everything is okay
|
||||
```json
|
||||
{"state":"success", "valid_values": 4, "errors":[]}
|
||||
```
|
||||
- valid_values: should contain count of entrys in update if no error occured (User.password will not be counted)
|
||||
- errors: contain on error string for every entry in update, which type isn't like expected
|
||||
- password:
|
||||
- "new password is the same as old password": no change taking place
|
||||
- "password changed, coludn"t load private key for re-encryption": password was successfully changed, is at the moment only a warning as long as user_backups are unencrypted, safe to ignore
|
||||
- "stored pubkey and private key didn't match": error by re-encryption keys, no changes saved
|
||||
- "User.password isn't valid": if password validation failed, followed by reasons why (additional array in array)
|
||||
example:
|
||||
```json
|
||||
{"errors":[
|
||||
"User.password isn't valid",[
|
||||
"Passwort: Dein Passwort ist zu kurz!\n",
|
||||
"Passwort: Bitte gebe ein gültiges Password ein mit mindestens 8 Zeichen, Groß- und Kleinbuchstaben, mindestens einer Zahl und einem Sonderzeichen (@$!%*?&+-_) ein!\n"
|
||||
]
|
||||
],
|
||||
"state":"success",
|
||||
"valid_values":0
|
||||
}
|
||||
```
|
||||
## Retrieve User Data
|
||||
Retrieve different user data, in ask only one field is needed, or every possible combination
|
||||
from the available fields
|
||||
|
||||
Normal User can only retrieve data for himself, admins (login-server admin) can retrieve data from every user
|
||||
Email is also the email address of user from which data are asked
|
||||
|
||||
POST http://localhost/login_api/getUserInfos
|
||||
```json
|
||||
{"session_id": -127182, "email": "max.musterman@gmail.de", "ask": [
|
||||
"EmailVerificationCode.Register",
|
||||
"loginServer.path",
|
||||
"user.pubkeyhex",
|
||||
"user.first_name",
|
||||
"user.last_name",
|
||||
"user.disabled",
|
||||
"user.email_checked",
|
||||
]
|
||||
}
|
||||
```
|
||||
returns if no error occured:
|
||||
```json
|
||||
{"state": "success", "userData": {
|
||||
"EmailVerificationCode.Register": "2718271129122",
|
||||
"pubkeyhex": "131c7f68dd94b2be4c913400ff7ff4cdc03ac2bda99c2d29edcacb3b065c67e6",
|
||||
"first_name": "Max",
|
||||
"last_name": "Musterman",
|
||||
"disabled": 0,
|
||||
"email_checked": 1
|
||||
}, "server": {
|
||||
"loginServer.path": "http://localhost/account"
|
||||
},
|
||||
"errors": []
|
||||
}
|
||||
```
|
||||
|
||||
Return only the fields which are defined in ask
|
||||
- EmailVerificationCode.Register: return the email verification code for check email (create one if none exist), work only if logged in user is admin and the email isn't from him
|
||||
- loginServer.path: the redirect path to login-server, for example for login with login-server html frontend
|
||||
- user.pubkeyhex: public key of user in hex-format
|
||||
- user.first_name: first name of user
|
||||
- user.last_name: last name of user
|
||||
- user.disabled: User will be disabled if he wants a account delete but has transactions.
|
||||
Until transactions are saved in real blockchain, we need this data because the public key
|
||||
is in db only saved in state_users so we wenn delete this entry, validating all transactions not longer possible.
|
||||
Disabled User cannot login and cannot receive transactions.
|
||||
- email_checked: If user has clicked on link in verification email (register), can only transfer gradidos if email_checked is 1
|
||||
|
||||
- errors: array of strings if error occure
|
||||
|
||||
## Login by Email Verification Code
|
||||
Used for replace http://localhost/account/checkEmail
|
||||
Can be used to set check_email to 1 (will be done automaticly if called with valid email verification code of type register or registerDirect)
|
||||
Can be used for password reset (additional step required: call update user info with new password)
|
||||
|
||||
GET http://localhost/login_api/loginViaEmailVerificationCode?emailVerificationCode=382738273892983
|
||||
|
||||
return
|
||||
```json
|
||||
{"state":"success", "email_verification_code_type":"resetPassword","info":[],"session_id":1853761475}
|
||||
```
|
||||
- email_verification_code_type
|
||||
- resetPassword: for password resets, will be deleted immediately, is a only one use code
|
||||
- registerDirect: code generated by register for check email
|
||||
- register: code generated by auto-register via elopage for check email
|
||||
- info can contain additional info strings
|
||||
- user hasn't password: if user hasn't set a password yet (for example if he was registered via elopage)
|
||||
- email already activated: if email was already checked
|
||||
- session_id: session_id for new session
|
||||
22
frontend/.babelrc
Normal file
22
frontend/.babelrc
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env"
|
||||
]
|
||||
],
|
||||
"env": {
|
||||
"test": {
|
||||
"plugins": ["require-context-hook"],
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"targets": {
|
||||
"node": "10"
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
2
frontend/.gitignore
vendored
2
frontend/.gitignore
vendored
@ -18,3 +18,5 @@ package-lock.json
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
|
||||
*~
|
||||
34
frontend/jest.config.js
Normal file
34
frontend/jest.config.js
Normal file
@ -0,0 +1,34 @@
|
||||
module.exports = {
|
||||
verbose: true,
|
||||
collectCoverageFrom: [
|
||||
"**/*.{js,vue}",
|
||||
"!**/node_modules/**",
|
||||
"!**/?(*.)+(spec|test).js?(x)"
|
||||
],
|
||||
moduleFileExtensions: [
|
||||
'js',
|
||||
//'jsx',
|
||||
'json',
|
||||
'vue',
|
||||
],
|
||||
coverageReporters: [
|
||||
"lcov"
|
||||
],
|
||||
moduleNameMapper: {
|
||||
'^@/(.*)$': '<rootDir>/src/$1'
|
||||
},
|
||||
transform: {
|
||||
'^.+\\.vue$': 'vue-jest',
|
||||
// '.+\\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub',
|
||||
"^.+\\.(js|jsx)?$": "babel-jest"
|
||||
},
|
||||
//setupFiles: [
|
||||
// "<rootDir>/test/registerContext.js"
|
||||
//],
|
||||
testMatch: [
|
||||
"**/?(*.)+(spec|test).js?(x)"
|
||||
],
|
||||
// snapshotSerializers: ['jest-serializer-vue'],
|
||||
transformIgnorePatterns: ['<rootDir>/node_modules/']
|
||||
};
|
||||
|
||||
@ -8,10 +8,15 @@
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "vue-cli-service lint",
|
||||
"dev": "yarn run serve",
|
||||
"i18n:report": "vue-cli-service i18n:report --src './src/**/*.?(js|vue)' --locales './src/locales/**/*.json'"
|
||||
"i18n:report": "vue-cli-service i18n:report --src './src/**/*.?(js|vue)' --locales './src/locales/**/*.json'",
|
||||
"test": "jest"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vue/test-utils": "^1.1.3",
|
||||
"axios": "^0.21.1",
|
||||
"babel-core": "^7.0.0-bridge.0",
|
||||
"babel-jest": "^26.6.3",
|
||||
"babel-plugin-require-context-hook": "^1.0.0",
|
||||
"bootstrap": "4.3.1",
|
||||
"bootstrap-vue": "^2.5.0",
|
||||
"chart.js": "^2.9.3",
|
||||
@ -27,6 +32,7 @@
|
||||
"flatpickr": "^4.5.7",
|
||||
"fuse.js": "^3.2.0",
|
||||
"google-maps": "^3.2.1",
|
||||
"jest": "^26.6.3",
|
||||
"nouislider": "^12.1.0",
|
||||
"particles-bg-vue": "1.2.3",
|
||||
"perfect-scrollbar": "^1.3.0",
|
||||
@ -45,6 +51,7 @@
|
||||
"vue-flatpickr-component": "^8.1.2",
|
||||
"vue-good-table": "^2.21.3",
|
||||
"vue-i18n": "^8.22.4",
|
||||
"vue-jest": "^3.0.7",
|
||||
"vue-moment": "^4.1.0",
|
||||
"vue-qrcode": "^0.3.5",
|
||||
"vue-qrcode-reader": "^2.3.16",
|
||||
|
||||
32
frontend/src/components/CloseButton.spec.js
Normal file
32
frontend/src/components/CloseButton.spec.js
Normal file
@ -0,0 +1,32 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
|
||||
import CloseButton from './CloseButton'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
describe('CloseButton', () => {
|
||||
|
||||
let wrapper
|
||||
let propsData = {
|
||||
target: "Target",
|
||||
expanded: false,
|
||||
}
|
||||
|
||||
const Wrapper = () => {
|
||||
return mount(CloseButton, { localVue, propsData })
|
||||
}
|
||||
|
||||
describe('mount', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
it('emmits click event', () => {
|
||||
wrapper.find('.navbar-toggler').trigger('click')
|
||||
expect(wrapper.emitted('click')).toBeTruthy()
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
@ -66,7 +66,7 @@ export const store = new Vuex.Store({
|
||||
state.session_id = session_id
|
||||
},
|
||||
user_balance: (state,balance) => {
|
||||
//console.log('mutation: user_balance')
|
||||
console.log('mutation: user_balance')
|
||||
state.user.balance = (balance/10000)
|
||||
},
|
||||
user_balance_gdt: (state,balance) => {
|
||||
|
||||
1897
frontend/yarn.lock
1897
frontend/yarn.lock
File diff suppressed because it is too large
Load Diff
@ -71,11 +71,13 @@ Poco::JSON::Object* JsonGetLogin::handle(Poco::Dynamic::Var params)
|
||||
em->addError(new Error("JsonGetLogin::handle", "generic exception calling userModel->getJson: "));
|
||||
em->sendErrorsAsEmail();
|
||||
}
|
||||
|
||||
result->set("Transactions.pending", session->getProcessingTransactionCount());
|
||||
auto executing = observer->getTaskCount(userModel->getEmail(), TASK_OBSERVER_SIGN_TRANSACTION);
|
||||
if (executing < 0) {
|
||||
executing = 0;
|
||||
}
|
||||
|
||||
result->set("Transactions.executing", executing);
|
||||
//printf("pending: %d\n", session->getProcessingTransactionCount());
|
||||
//std::string user_string = userModel->toString();
|
||||
|
||||
@ -7,6 +7,26 @@
|
||||
|
||||
#include "../ServerConfig.h"
|
||||
|
||||
Poco::UInt64 JsonGetUserInfos::readOrCreateEmailVerificationCode(int user_id, model::table::EmailOptInType type)
|
||||
{
|
||||
try {
|
||||
auto emailVerificationCode = controller::EmailVerificationCode::load(user_id, type);
|
||||
if (!emailVerificationCode) {
|
||||
emailVerificationCode = controller::EmailVerificationCode::create(user_id, type);
|
||||
UniLib::controller::TaskPtr insert = new model::table::ModelInsertTask(emailVerificationCode->getModel(), false);
|
||||
insert->scheduleTask(insert);
|
||||
}
|
||||
return emailVerificationCode->getModel()->getCode();
|
||||
}
|
||||
catch (Poco::Exception& ex) {
|
||||
ErrorList errors;
|
||||
//printf("exception: %s\n", ex.displayText().data());
|
||||
errors.addError(new ParamError("JsonGetUserInfos::readOrCreateEmailVerificationCode", "exception: ", ex.displayText()));
|
||||
errors.sendErrorsAsEmail();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Poco::JSON::Object* JsonGetUserInfos::handle(Poco::Dynamic::Var params)
|
||||
{
|
||||
/*
|
||||
@ -54,11 +74,21 @@ Poco::JSON::Object* JsonGetUserInfos::handle(Poco::Dynamic::Var params)
|
||||
return customStateError("not found", "session not found");
|
||||
}
|
||||
|
||||
auto session_user = session->getNewUser();
|
||||
auto session_user_model = session_user->getModel();
|
||||
bool isAdmin = false;
|
||||
if (model::table::ROLE_ADMIN == session_user_model->getRole()) {
|
||||
isAdmin = true;
|
||||
}
|
||||
if (session_user_model->getEmail() != email && !isAdmin) {
|
||||
return customStateError("not same", "email don't belong to logged in user");
|
||||
}
|
||||
|
||||
auto user = controller::User::create();
|
||||
if (1 != user->load(email)) {
|
||||
return customStateError("not found", "user not found");
|
||||
}
|
||||
auto userModel = user->getModel();
|
||||
auto user_model = user->getModel();
|
||||
|
||||
|
||||
Poco::JSON::Object* result = new Poco::JSON::Object;
|
||||
@ -72,42 +102,32 @@ Poco::JSON::Object* JsonGetUserInfos::handle(Poco::Dynamic::Var params)
|
||||
std::string parameterString;
|
||||
try {
|
||||
parameter.convert(parameterString);
|
||||
if (parameterString == "EmailVerificationCode.Register") {
|
||||
try {
|
||||
auto emailVerificationCode = controller::EmailVerificationCode::load(
|
||||
userModel->getID(), model::table::EMAIL_OPT_IN_REGISTER
|
||||
);
|
||||
if (!emailVerificationCode) {
|
||||
emailVerificationCode = controller::EmailVerificationCode::create(userModel->getID(), model::table::EMAIL_OPT_IN_REGISTER);
|
||||
UniLib::controller::TaskPtr insert = new model::table::ModelInsertTask(emailVerificationCode->getModel(), false);
|
||||
insert->scheduleTask(insert);
|
||||
}
|
||||
jsonUser.set("EmailVerificationCode.Register", std::to_string(emailVerificationCode->getModel()->getCode()));
|
||||
}
|
||||
catch (Poco::Exception& ex) {
|
||||
printf("exception: %s\n", ex.displayText().data());
|
||||
if (parameterString == "EmailVerificationCode.Register" && isAdmin && session_user_model->getEmail() != user_model->getEmail()) {
|
||||
auto code = readOrCreateEmailVerificationCode(user_model->getID(), model::table::EMAIL_OPT_IN_REGISTER_DIRECT);
|
||||
if (code) {
|
||||
jsonUser.set("EmailVerificationCode.Register", std::to_string(code));
|
||||
}
|
||||
}
|
||||
else if (parameterString == "loginServer.path") {
|
||||
jsonServer.set("loginServer.path", ServerConfig::g_serverPath);
|
||||
}
|
||||
else if (parameterString == "user.pubkeyhex") {
|
||||
jsonUser.set("pubkeyhex", userModel->getPublicKeyHex());
|
||||
jsonUser.set("pubkeyhex", user_model->getPublicKeyHex());
|
||||
}
|
||||
else if (parameterString == "user.first_name") {
|
||||
jsonUser.set("first_name", userModel->getFirstName());
|
||||
jsonUser.set("first_name", user_model->getFirstName());
|
||||
}
|
||||
else if (parameterString == "user.last_name") {
|
||||
jsonUser.set("last_name", userModel->getLastName());
|
||||
jsonUser.set("last_name", user_model->getLastName());
|
||||
}
|
||||
else if (parameterString == "user.disabled") {
|
||||
jsonUser.set("disabled", userModel->isDisabled());
|
||||
jsonUser.set("disabled", user_model->isDisabled());
|
||||
}
|
||||
else if (parameterString == "user.email_checked") {
|
||||
jsonUser.set("email_checked", userModel->isEmailChecked());
|
||||
jsonUser.set("email_checked", user_model->isEmailChecked());
|
||||
}
|
||||
else if (parameterString == "user.identHash") {
|
||||
auto email = userModel->getEmail();
|
||||
auto email = user_model->getEmail();
|
||||
jsonUser.set("identHash", DRMakeStringHash(email.data(), email.size()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
#define __JSON_INTERFACE_JSON_GET_USER_INFOS_
|
||||
|
||||
#include "JsonRequestHandler.h"
|
||||
|
||||
#include "../model/table/EmailOptIn.h"
|
||||
/*!
|
||||
* @author Dario Rekowski
|
||||
* @date 2020-03-21
|
||||
@ -17,7 +17,7 @@ public:
|
||||
Poco::JSON::Object* handle(Poco::Dynamic::Var params);
|
||||
|
||||
protected:
|
||||
|
||||
Poco::UInt64 readOrCreateEmailVerificationCode(int user_id, model::table::EmailOptInType type);
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -12,7 +12,8 @@ Poco::JSON::Object* JsonGetUsers::handle(Poco::Dynamic::Var params)
|
||||
|
||||
int session_id = 0;
|
||||
std::string searchString;
|
||||
|
||||
std::string accountState = "";
|
||||
static std::string emptySearchString = "... empty ...";
|
||||
// if is json object
|
||||
if (params.type() == typeid(Poco::JSON::Object::Ptr)) {
|
||||
Poco::JSON::Object::Ptr paramJsonObject = params.extract<Poco::JSON::Object::Ptr>();
|
||||
@ -23,6 +24,9 @@ Poco::JSON::Object* JsonGetUsers::handle(Poco::Dynamic::Var params)
|
||||
/// Throws InvalidAccessException if Var is empty.
|
||||
try {
|
||||
paramJsonObject->get("search").convert(searchString);
|
||||
if (paramJsonObject->has("account_state")) {
|
||||
paramJsonObject->get("account_state").convert(accountState);
|
||||
}
|
||||
paramJsonObject->get("session_id").convert(session_id);
|
||||
}
|
||||
catch (Poco::Exception& ex) {
|
||||
@ -67,17 +71,20 @@ Poco::JSON::Object* JsonGetUsers::handle(Poco::Dynamic::Var params)
|
||||
}
|
||||
|
||||
auto user = session->getNewUser();
|
||||
if (searchString == emptySearchString) {
|
||||
searchString = "";
|
||||
}
|
||||
if (user.isNull()) {
|
||||
return customStateError("not found", "Session didn't contain user");
|
||||
}
|
||||
else if (searchString == "") {
|
||||
return customStateError("not found", "Search string is empty");
|
||||
else if (searchString == "" && (accountState == "" || accountState == "all")) {
|
||||
return customStateError("not found", "Search string is empty and account_state is all or empty");
|
||||
}
|
||||
else if (user->getModel()->getRole() != model::table::ROLE_ADMIN) {
|
||||
return customStateError("wrong role", "User hasn't correct role");
|
||||
}
|
||||
|
||||
auto results = controller::User::search(searchString);
|
||||
auto results = controller::User::search(searchString, accountState);
|
||||
if (!results.size()) {
|
||||
return stateSuccess();
|
||||
}
|
||||
|
||||
@ -0,0 +1,70 @@
|
||||
#include "JsonLoginViaEmailVerificationCode.h"
|
||||
|
||||
#include "JsonUnsecureLogin.h"
|
||||
|
||||
#include "../SingletonManager/SessionManager.h"
|
||||
#include "../SingletonManager/SingletonTaskObserver.h"
|
||||
#include "../SingletonManager/ErrorManager.h"
|
||||
|
||||
#include "../controller/User.h"
|
||||
|
||||
#include "../lib/DataTypeConverter.h"
|
||||
|
||||
#include "Poco/URI.h"
|
||||
#include "Poco/JSON/Array.h"
|
||||
|
||||
Poco::JSON::Object* JsonLoginViaEmailVerificationCode::handle(Poco::Dynamic::Var params)
|
||||
{
|
||||
|
||||
auto sm = SessionManager::getInstance();
|
||||
|
||||
/*
|
||||
email verification code
|
||||
*/
|
||||
// incoming
|
||||
unsigned long long code = 0;
|
||||
if (params.isVector()) {
|
||||
const Poco::URI::QueryParameters queryParams = params.extract<Poco::URI::QueryParameters>();
|
||||
std::string codeString;
|
||||
for (auto it = queryParams.begin(); it != queryParams.end(); it++) {
|
||||
if (it->first == "emailVerificationCode") {
|
||||
codeString = it->second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (codeString == "") {
|
||||
return stateError("emailVerificationCode not found");
|
||||
}
|
||||
if (DataTypeConverter::NUMBER_PARSE_OKAY != DataTypeConverter::strToInt(codeString, code)) {
|
||||
return stateError("couldn't parse emailVerificationCode");
|
||||
}
|
||||
|
||||
}
|
||||
auto session = sm->findByEmailVerificationCode(code);
|
||||
if (!session) {
|
||||
session = sm->getNewSession();
|
||||
if (!session->loadFromEmailVerificationCode(code)) {
|
||||
return stateError("couldn't login with emailVerificationCode");
|
||||
}
|
||||
}
|
||||
session->setClientIp(mClientIP);
|
||||
auto result = new Poco::JSON::Object;
|
||||
result->set("state", "success");
|
||||
result->set("session_id", session->getHandle());
|
||||
result->set("email_verification_code_type", model::table::EmailOptIn::typeToString(session->getEmailVerificationType()));
|
||||
Poco::JSON::Array info;
|
||||
|
||||
if (!session->getNewUser()->getModel()->getPasswordHashed()) {
|
||||
info.add("user hasn't password");
|
||||
}
|
||||
auto update_email_verification_result = session->updateEmailVerification(code);
|
||||
if (1 == update_email_verification_result) {
|
||||
info.add("email already activated");
|
||||
}
|
||||
|
||||
result->set("info", info);
|
||||
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
#ifndef __JSON_INTERFACE_JSON_LOGIN_VIA_EMAIL_VERIFICATION_CODE_
|
||||
#define __JSON_INTERFACE_JSON_LOGIN_VIA_EMAIL_VERIFICATION_CODE_
|
||||
|
||||
#include "JsonRequestHandler.h"
|
||||
|
||||
class JsonLoginViaEmailVerificationCode : public JsonRequestHandler
|
||||
{
|
||||
public:
|
||||
JsonLoginViaEmailVerificationCode(Poco::Net::IPAddress ip) : mClientIP(ip) {}
|
||||
Poco::JSON::Object* handle(Poco::Dynamic::Var params);
|
||||
|
||||
protected:
|
||||
Poco::Net::IPAddress mClientIP;
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // __JSON_INTERFACE_JSON_LOGIN_VIA_EMAIL_VERIFICATION_CODE_
|
||||
@ -10,6 +10,7 @@
|
||||
#include "JsonTransaction.h"
|
||||
#include "JsonGetRunningUserTasks.h"
|
||||
#include "JsonGetUsers.h"
|
||||
#include "JsonLoginViaEmailVerificationCode.h"
|
||||
#include "JsonAdminEmailVerificationResend.h"
|
||||
#include "JsonGetUserInfos.h"
|
||||
#include "JsonUpdateUserInfos.h"
|
||||
@ -67,6 +68,9 @@ Poco::Net::HTTPRequestHandler* JsonRequestHandlerFactory::createRequestHandler(c
|
||||
else if (url_first_part == "/unsecureLogin" && (ServerConfig::g_AllowUnsecureFlags & ServerConfig::UNSECURE_PASSWORD_REQUESTS)) {
|
||||
return new JsonUnsecureLogin(client_host);
|
||||
}
|
||||
else if (url_first_part == "/loginViaEmailVerificationCode") {
|
||||
return new JsonLoginViaEmailVerificationCode(client_host);
|
||||
}
|
||||
else if (url_first_part == "/logout") {
|
||||
return new JsonLogout(client_host);
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
#include "../SingletonManager/SessionManager.h"
|
||||
#include "../SingletonManager/LanguageManager.h"
|
||||
#include "../tasks/AuthenticatedEncryptionCreateKeyTask.h"
|
||||
|
||||
Poco::JSON::Object* JsonUpdateUserInfos::handle(Poco::Dynamic::Var params)
|
||||
{
|
||||
@ -117,6 +118,34 @@ Poco::JSON::Object* JsonUpdateUserInfos::handle(Poco::Dynamic::Var params)
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ("User.password" == name && (ServerConfig::g_AllowUnsecureFlags & ServerConfig::UNSECURE_PASSWORD_REQUESTS) == ServerConfig::UNSECURE_PASSWORD_REQUESTS) {
|
||||
if (!value.isString()) {
|
||||
jsonErrorsArray.add("User.password isn't string");
|
||||
}
|
||||
else {
|
||||
ErrorList errors;
|
||||
if (!sm->checkPwdValidation(value.toString(), &errors)) {
|
||||
jsonErrorsArray.add("User.password isn't valid");
|
||||
jsonErrorsArray.add(errors.getErrorsArray());
|
||||
}
|
||||
else {
|
||||
auto result_new_password = user->setNewPassword(value.toString());
|
||||
|
||||
switch (result_new_password) {
|
||||
// 0 = new and current passwords are the same
|
||||
case 0: jsonErrorsArray.add("new password is the same as old password"); break;
|
||||
// 1 = password changed, private key re-encrypted and saved into db
|
||||
//case 1: extractet_values++; break;
|
||||
// 2 = password changed, only hash stored in db, couldn't load private key for re-encryption
|
||||
case 2: jsonErrorsArray.add("password changed, couldn't load private key for re-encryption"); break;
|
||||
// -1 = stored pubkey and private key didn't match
|
||||
case -1: jsonErrorsArray.add("stored pubkey and private key didn't match"); break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Poco::Exception& ex) {
|
||||
jsonErrorsArray.add("update parameter invalid");
|
||||
|
||||
@ -92,6 +92,8 @@ int EmailManager::ThreadFunction()
|
||||
if (mPendingEmails.empty()) return 0;
|
||||
|
||||
auto lm = LanguageManager::getInstance();
|
||||
ErrorList errors;
|
||||
static const char* function_name = "PrepareEmailTask";
|
||||
|
||||
Poco::Net::SecureSMTPClientSession mailClientSession(mEmailAccount.url, mEmailAccount.port);
|
||||
mailClientSession.login();
|
||||
@ -100,6 +102,7 @@ int EmailManager::ThreadFunction()
|
||||
mailClientSession.login(Poco::Net::SMTPClientSession::AUTH_LOGIN, mEmailAccount.username, mEmailAccount.password);
|
||||
}
|
||||
catch (Poco::Net::SSLException& ex) {
|
||||
errors.addError(new ParamError(function_name, "ssl certificate error", ex.displayText()));
|
||||
printf("[PrepareEmailTask] ssl certificate error: %s\nPlease make sure you have cacert.pem (CA/root certificates) next to binary from https://curl.haxx.se/docs/caextract.html\n", ex.displayText().data());
|
||||
return -1;
|
||||
}
|
||||
@ -151,6 +154,7 @@ int EmailManager::ThreadFunction()
|
||||
else {
|
||||
// error drafting email, shouldn't happend
|
||||
printf("[EmailManager::ThreadFunction] Error drafting email\n");
|
||||
errors.addError(new Error(function_name, "Error drafting email"));
|
||||
}
|
||||
delete email;
|
||||
email = nullptr;
|
||||
|
||||
@ -54,9 +54,15 @@ namespace controller {
|
||||
Poco::AutoPtr<EmailVerificationCode> EmailVerificationCode::load(int user_id, model::table::EmailOptInType type) {
|
||||
auto db = new model::table::EmailOptIn();
|
||||
std::vector<std::string> fields = { "user_id", "email_opt_in_type_id" };
|
||||
if (db->loadFromDB(fields, user_id, (int)type) == 1) {
|
||||
return Poco::AutoPtr<EmailVerificationCode>(new EmailVerificationCode(db));
|
||||
std::vector<int> field_values = { user_id, (int)type };
|
||||
auto results = db->loadFromDB<int, model::table::EmailOptInTuple>(fields, field_values);
|
||||
if (results.size() > 0) {
|
||||
db->release();
|
||||
return Poco::AutoPtr<EmailVerificationCode>(new EmailVerificationCode(new model::table::EmailOptIn(results[0])));
|
||||
}
|
||||
/*if (db->loadFromDB(fields, user_id, (int)type) == 1) {
|
||||
return Poco::AutoPtr<EmailVerificationCode>(new EmailVerificationCode(db));
|
||||
}*/
|
||||
db->release();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -46,24 +46,49 @@ namespace controller {
|
||||
return Poco::AutoPtr<User>(user);
|
||||
}
|
||||
|
||||
std::vector<User*> User::search(const std::string& searchString)
|
||||
std::vector<User*> User::search(const std::string& searchString, const std::string& accountState /* = "all" */)
|
||||
{
|
||||
|
||||
auto sm = SessionManager::getInstance();
|
||||
auto cm = ConnectionManager::getInstance();
|
||||
auto db = new model::table::User();
|
||||
static const char* functionName = "User::search";
|
||||
|
||||
std::string globalSearch = "%" + searchString + "%";
|
||||
|
||||
std::vector<model::table::UserTuple> resultFromDB;
|
||||
// check if search string is email
|
||||
/*if (sm->isValid(searchString, VALIDATE_EMAIL)) {
|
||||
resultFromDB = db->loadFromDB <std::string, model::table::UserTuple>("email", globalSearch);
|
||||
if (accountState == "email not activated") {
|
||||
|
||||
std::vector<std::string> fieldNames = { "first_name", "last_name", "email", "email_checked" };
|
||||
auto session = cm->getConnection(CONNECTION_MYSQL_LOGIN_SERVER);
|
||||
std::vector<model::table::UserTuple> results;
|
||||
|
||||
using namespace Poco::Data::Keywords;
|
||||
Poco::Data::Statement select(session);
|
||||
// typedef Poco::Tuple<std::string, std::string, std::string, Poco::Nullable<Poco::Data::BLOB>, int> UserTuple;
|
||||
select << "SELECT id, first_name, last_name, email, pubkey, created, email_checked, disabled FROM " << db->getTableName();
|
||||
select << " where email_checked = 0 ";
|
||||
select, into(resultFromDB);
|
||||
if (searchString != "") {
|
||||
select << "AND (first_name LIKE ? OR last_name LIKE ? OR email LIKE ?)";
|
||||
select, useRef(globalSearch), useRef(globalSearch), useRef(globalSearch);
|
||||
}
|
||||
try {
|
||||
select.execute();
|
||||
}
|
||||
catch (Poco::Exception& ex) {
|
||||
ErrorList errors;
|
||||
errors.addError(new ParamError(functionName, "mysql error ", ex.displayText()));
|
||||
errors.addError(new ParamError(functionName, "search string", searchString));
|
||||
errors.addError(new ParamError(functionName, "account state", accountState));
|
||||
errors.sendErrorsAsEmail();
|
||||
}
|
||||
}
|
||||
else {*/
|
||||
else {
|
||||
std::vector<std::string> fieldNames = { "first_name", "last_name", "email" };
|
||||
std::vector<std::string> fieldValues = { globalSearch, globalSearch, globalSearch };
|
||||
resultFromDB = db->loadFromDB<std::string, model::table::UserTuple>(fieldNames, fieldValues, model::table::MYSQL_CONDITION_OR);
|
||||
//}
|
||||
}
|
||||
|
||||
db->release();
|
||||
db = nullptr;
|
||||
|
||||
@ -28,7 +28,7 @@ namespace controller {
|
||||
static Poco::AutoPtr<User> create();
|
||||
static Poco::AutoPtr<User> create(const std::string& email, const std::string& first_name, const std::string& last_name, Poco::UInt64 passwordHashed = 0, std::string languageKey = "de");
|
||||
|
||||
static std::vector<User*> search(const std::string& searchString);
|
||||
static std::vector<User*> search(const std::string& searchString, const std::string& accountState = "all");
|
||||
|
||||
//! \brief go through whole db and search users with email_checked = false and schedule resend 7 days after email_opt_in created date
|
||||
//!
|
||||
|
||||
@ -90,6 +90,33 @@ namespace model {
|
||||
return select;
|
||||
}
|
||||
|
||||
Poco::Data::Statement EmailOptIn::_loadMultipleFromDB(Poco::Data::Session session, const std::vector<std::string> fieldNames, MysqlConditionType conditionType/* = MYSQL_CONDITION_AND*/)
|
||||
{
|
||||
Poco::Data::Statement select(session);
|
||||
if (fieldNames.size() <= 1) {
|
||||
throw Poco::NullValueException("EmailOptIn::_loadFromDB fieldNames empty or contain only one field");
|
||||
}
|
||||
|
||||
select << "SELECT id, user_id, verification_code, email_opt_in_type_id, created, resend_count, updated FROM " << getTableName()
|
||||
<< " where " << fieldNames[0] << " = ? ";
|
||||
if (conditionType == MYSQL_CONDITION_AND) {
|
||||
for (int i = 1; i < fieldNames.size(); i++) {
|
||||
select << " AND " << fieldNames[i] << " = ? ";
|
||||
}
|
||||
}
|
||||
else if (conditionType == MYSQL_CONDITION_OR) {
|
||||
for (int i = 1; i < fieldNames.size(); i++) {
|
||||
select << " OR " << fieldNames[i] << " = ? ";
|
||||
}
|
||||
}
|
||||
else {
|
||||
addError(new ParamError("EmailOptIn::_loadFromDB", "condition type not implemented", conditionType));
|
||||
}
|
||||
|
||||
return select;
|
||||
}
|
||||
|
||||
|
||||
Poco::Data::Statement EmailOptIn::_loadFromDB(Poco::Data::Session session, const std::vector<std::string>& fieldNames, MysqlConditionType conditionType/* = MYSQL_CONDITION_AND*/)
|
||||
{
|
||||
Poco::Data::Statement select(session);
|
||||
|
||||
@ -48,6 +48,7 @@ namespace model {
|
||||
Poco::Data::Statement _loadFromDB(Poco::Data::Session session, const std::string& fieldName);
|
||||
Poco::Data::Statement _loadIdFromDB(Poco::Data::Session session);
|
||||
Poco::Data::Statement _loadMultipleFromDB(Poco::Data::Session session, const std::string& fieldName);
|
||||
Poco::Data::Statement _loadMultipleFromDB(Poco::Data::Session session, const std::vector<std::string> fieldNames, MysqlConditionType conditionType = MYSQL_CONDITION_AND);
|
||||
Poco::Data::Statement _loadFromDB(Poco::Data::Session session, const std::vector<std::string>& fieldNames, MysqlConditionType conditionType = MYSQL_CONDITION_AND);
|
||||
Poco::Data::Statement _insertIntoDB(Poco::Data::Session session);
|
||||
|
||||
|
||||
@ -40,10 +40,19 @@ namespace model {
|
||||
size_t loadFromDB(const std::string& fieldName, const T& fieldValue);
|
||||
template<class T>
|
||||
bool isExistInDB(const std::string& fieldName, const T& fieldValue);
|
||||
|
||||
template<class WhereFieldType, class Tuple>
|
||||
std::vector<Tuple> loadFromDB(const std::string& fieldName, const WhereFieldType& fieldValue, int expectedResults = 0);
|
||||
|
||||
template<class T1, class T2>
|
||||
size_t loadFromDB(const std::vector<std::string>& fieldNames, const T1& field1Value, const T2& field2Value, MysqlConditionType conditionType = MYSQL_CONDITION_AND);
|
||||
|
||||
template<class Tuple, class T1, class T2, class T3, class T4>
|
||||
std::vector<Tuple> loadMultipleFromDB(
|
||||
const std::vector<std::string>& fieldNames,
|
||||
const T1& field1Value, const T2& field2Value, const T3& field3Value, const T4& field4Value,
|
||||
MysqlConditionType conditionType = MYSQL_CONDITION_AND);
|
||||
|
||||
template<class WhereFieldType, class Tuple>
|
||||
std::vector<Tuple> loadFromDB(const std::vector<std::string>& fieldNames, const std::vector<WhereFieldType>& fieldValues, MysqlConditionType conditionType = MYSQL_CONDITION_AND, int expectedResults = 0);
|
||||
bool insertIntoDB(bool loadId);
|
||||
@ -211,6 +220,43 @@ namespace model {
|
||||
return resultCount;
|
||||
}
|
||||
|
||||
template<class Tuple, class T1, class T2, class T3, class T4>
|
||||
std::vector<Tuple> ModelBase::loadMultipleFromDB(
|
||||
const std::vector<std::string>& fieldNames,
|
||||
const T1& field1Value, const T2& field2Value, const T3& field3Value, const T4& field4Value,
|
||||
MysqlConditionType conditionType/* = MYSQL_CONDITION_AND*/)
|
||||
{
|
||||
auto cm = ConnectionManager::getInstance();
|
||||
std::vector<Tuple> results;
|
||||
if (fieldNames.size() != 4) {
|
||||
addError(new Error(getTableName(), "error in loadFromDB with 4 different field values, fieldNames count isn't 4"));
|
||||
return results;
|
||||
}
|
||||
Poco::ScopedLock<Poco::Mutex> _lock(mWorkMutex);
|
||||
|
||||
auto session = cm->getConnection(CONNECTION_MYSQL_LOGIN_SERVER);
|
||||
Poco::Data::Statement select = _loadMultipleFromDB(session, fieldNames, conditionType);
|
||||
select, Poco::Data::Keywords::into(results),
|
||||
Poco::Data::Keywords::useRef(field1Value), Poco::Data::Keywords::useRef(field2Value),
|
||||
Poco::Data::Keywords::useRef(field3Value), Poco::Data::Keywords::useRef(field4Value);
|
||||
|
||||
size_t resultCount = 0;
|
||||
try {
|
||||
resultCount = select.execute();
|
||||
}
|
||||
catch (Poco::Exception& ex) {
|
||||
lock();
|
||||
addError(new ParamError(getTableName(), "mysql error by selecting with 4 different field types", ex.displayText()));
|
||||
int count = 0;
|
||||
for (auto it = fieldNames.begin(); it != fieldNames.end(); it++) {
|
||||
addError(new ParamError(getTableName(), "field name for select: ", *it));
|
||||
}
|
||||
|
||||
//addError(new ParamError(getTableName(), "field name for select: ", fieldName.data()));
|
||||
unlock();
|
||||
}
|
||||
return resultCount;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
size_t ModelBase::updateIntoDB(const std::string& fieldName, const T& fieldValue)
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
//#include "lib/Thread.h"
|
||||
//#include "UniversumLib.h"
|
||||
#include "Thread.h"
|
||||
#include "../lib/ErrorList.h"
|
||||
|
||||
namespace UniLib {
|
||||
namespace lib {
|
||||
@ -55,6 +56,8 @@ namespace UniLib {
|
||||
|
||||
void Thread::run()
|
||||
{
|
||||
static const char* function_name = "Thread::run";
|
||||
ErrorList errors;
|
||||
//Thread* t = this;
|
||||
while (true) {
|
||||
try {
|
||||
@ -77,6 +80,7 @@ namespace UniLib {
|
||||
{
|
||||
//EngineLog.writeToLog("error-code: %d", ret);
|
||||
printf("[Thread::%s] error running thread functon: %d, exit thread\n", __FUNCTION__, ret);
|
||||
errors.addError(new ParamError(function_name, "error running thread function, exit thread", mPocoThread->getName()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -85,13 +89,19 @@ namespace UniLib {
|
||||
threadUnlock();
|
||||
//LOG_ERROR("Fehler in Thread, exit", -1);
|
||||
printf("[Thread::%s] exception: %s\n", __FUNCTION__, e.message().data());
|
||||
errors.addError(new ParamError(function_name, "poco exception", e.message()));
|
||||
errors.addError(new ParamError(function_name, "thread name", mPocoThread->getName()));
|
||||
return;
|
||||
}
|
||||
|
||||
} catch (Poco::TimeoutException& e) {
|
||||
printf("[Thread::%s] timeout exception\n", __FUNCTION__);
|
||||
errors.addError(new ParamError(function_name, "poco timeout exception", e.message()));
|
||||
errors.addError(new ParamError(function_name, "thread name", mPocoThread->getName()));
|
||||
} catch (Poco::Exception& e) {
|
||||
printf("[Thread::%s] exception: %s\n", __FUNCTION__, e.message().data());
|
||||
errors.addError(new ParamError(function_name, "poco exception 2", e.message()));
|
||||
errors.addError(new ParamError(function_name, "thread name", mPocoThread->getName()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user