diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index b9f4083a3..2992f451b 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -265,10 +265,10 @@ jobs:
- name: backend login | Coverage check
uses: webcraftmedia/coverage-check-action@master
with:
- report_name: Coverage Backend
+ report_name: Coverage Backend Login
type: lcov
result_path: ./coverage/coverage.info
- min_coverage: 6
+ min_coverage: 13
token: ${{ github.token }}
##############################################################################
@@ -335,14 +335,14 @@ jobs:
#########################################################################
# COVERAGE CHECK BACKEND COMMUNITY-SERVER ####################################
##########################################################################
- #- name: backend community simplecov | Coverage check
- # uses: webcraftmedia/coverage-check-action@master
- # with:
- # report_name: Coverage Backend
- # type: simplecov
- # result_path: ./coverage/coverage.info
- # min_coverage: 8
- # token: ${{ github.token }}
+ - name: backend community | Coverage check
+ uses: einhornimmond/coverage-check-action@master
+ with:
+ report_name: Coverage Backend Community
+ type: phpunit
+ result_path: ./coverage/coverage.info
+ min_coverage: 10
+ token: ${{ github.token }}
#test:
# runs-on: ubuntu-latest
diff --git a/community_server/Dockerfile b/community_server/Dockerfile
index eabb37741..c553137c6 100644
--- a/community_server/Dockerfile
+++ b/community_server/Dockerfile
@@ -21,9 +21,10 @@ RUN apt-get update \
&& apt-get -y --no-install-recommends install php7.4-xdebug \
&& apt-get clean; rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/*
-WORKDIR /var/www/cakephp
+
ENV XDEBUG_MODE=coverage
+#RUN composer require --dev rregeer/phpunit-coverage-check
-CMD ./vendor/bin/phpunit --coverage-text=./webroot/coverage/coverage.info
-
+#CMD ./vendor/bin/phpunit --coverage-clover=./webroot/coverage/clover.xml
+CMD ./vendor/bin/phpunit --coverage-text=./webroot/coverage/coverage.info
diff --git a/community_server/src/Controller/AppRequestsController.php b/community_server/src/Controller/AppRequestsController.php
index 6b744ff69..ff3314e94 100644
--- a/community_server/src/Controller/AppRequestsController.php
+++ b/community_server/src/Controller/AppRequestsController.php
@@ -152,10 +152,7 @@ class AppRequestsController extends AppController
if($result !== true) {
return $this->returnJson($result);
}
- $required_fields = $this->checkAndCopyRequiredFields(['target_date'], $params, $data);
- if($required_fields !== true) {
- return $this->returnJson($required_fields);
- }
+
if(!isset($params['memo']) || strlen($params['memo']) < 5 || strlen($params['memo']) > 150) {
return $this->returnJson(['state' => 'error', 'msg' => 'memo is not set or not in expected range [5;150]']);
}
diff --git a/community_server/src/Model/Table/TransactionsTable.php b/community_server/src/Model/Table/TransactionsTable.php
index c4bc4b698..7e59c2d88 100644
--- a/community_server/src/Model/Table/TransactionsTable.php
+++ b/community_server/src/Model/Table/TransactionsTable.php
@@ -178,24 +178,20 @@ class TransactionsTable extends Table
}
if($prev && $decay == true)
{
-
if($prev->balance > 0) {
- // var_dump($stateUserTransactions);
$current = $su_transaction;
- //echo "decay between " . $prev->transaction_id . " and " . $current->transaction_id . "
";
+
$calculated_decay = $stateBalancesTable->calculateDecay($prev->balance, $prev->balance_date, $current->balance_date, true);
- $balance = floatval($prev->balance - $calculated_decay['balance']);
- // skip small decays (smaller than 0,00 GDD)
+ $balance = floatval($prev->balance - $calculated_decay['balance']);
- if(abs($balance) >= 100) {
- //echo $interval->format('%R%a days');
- //echo "prev balance: " . $prev->balance . ", diff_amount: $diff_amount, summe: " . (-intval($prev->balance - $diff_amount)) . "
";
- $final_transactions[] = [
- 'type' => 'decay',
- 'balance' => $balance,
- 'decay_duration' => $calculated_decay['interval']->format('%a days, %H hours, %I minutes, %S seconds'),
- 'memo' => ''
- ];
+ if($balance)
+ {
+ $final_transactions[] = [
+ 'type' => 'decay',
+ 'balance' => $balance,
+ 'decay_duration' => $calculated_decay['interval']->format('%a days, %H hours, %I minutes, %S seconds'),
+ 'memo' => ''
+ ];
}
}
}
@@ -207,9 +203,7 @@ class TransactionsTable extends Table
// date
// balance
$transaction = $transaction_indiced[$su_transaction->transaction_id];
- /*echo "transaction:
";
- var_dump($transaction);
- echo "
";*/
+
if($su_transaction->transaction_type_id == 1) { // creation
$creation = $transaction->transaction_creation;
$balance = $stateBalancesTable->calculateDecay($creation->amount, $creation->target_date, $transaction->received);
@@ -270,14 +264,16 @@ class TransactionsTable extends Table
$duration = $decay_start_date->timeAgoInWords();
}
$balance = floatval($su_transaction->balance - $calculated_decay['balance']);
- if($balance > 100) {
- $final_transactions[] = [
- 'type' => 'decay',
- 'balance' => $balance,
- 'decay_duration' => $duration,
- 'last_decay' => true,
- 'memo' => ''
- ];
+
+ if($balance)
+ {
+ $final_transactions[] = [
+ 'type' => 'decay',
+ 'balance' => $balance,
+ 'decay_duration' => $duration,
+ 'last_decay' => true,
+ 'memo' => ''
+ ];
}
}
}
diff --git a/frontend/package.json b/frontend/package.json
index 34ce37b76..910644e2c 100755
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -51,6 +51,7 @@
"nouislider": "^12.1.0",
"particles-bg-vue": "1.2.3",
"perfect-scrollbar": "^1.3.0",
+ "portal-vue": "^2.1.7",
"prettier": "^2.2.1",
"qrcode": "^1.4.4",
"quill": "^1.3.6",
@@ -58,6 +59,7 @@
"sweetalert2": "^9.5.4",
"vee-validate": "^3.4.5",
"vue": "^2.6.11",
+ "vue-bootstrap-toasts": "^1.0.7",
"vue-bootstrap-typeahead": "^0.2.6",
"vue-chartjs": "^3.5.0",
"vue-cli-plugin-i18n": "^1.0.1",
diff --git a/frontend/src/App.vue b/frontend/src/App.vue
index a6613bec1..500a7b2e1 100755
--- a/frontend/src/App.vue
+++ b/frontend/src/App.vue
@@ -3,6 +3,7 @@
diff --git a/frontend/src/apis/loginAPI.js b/frontend/src/apis/loginAPI.js
index 91e92a2cc..3453bd296 100644
--- a/frontend/src/apis/loginAPI.js
+++ b/frontend/src/apis/loginAPI.js
@@ -98,7 +98,6 @@ const loginAPI = {
}
return apiPost(CONFIG.LOGIN_API_URL + 'updateUserInfos', payload)
},
-
changePassword: async (sessionId, email, password) => {
const payload = {
session_id: sessionId,
@@ -120,12 +119,12 @@ const loginAPI = {
}
return apiPost(CONFIG.LOGIN_API_URL + 'updateUserInfos', payload)
},
- changeUsernameProfile: async (sessionId, email, usernameNew) => {
+ changeUsernameProfile: async (sessionId, email, username) => {
const payload = {
session_id: sessionId,
email,
update: {
- 'User.usernameNew': usernameNew,
+ 'User.username': username,
},
}
return apiPost(CONFIG.LOGIN_API_URL + 'updateUserInfos', payload)
diff --git a/frontend/src/assets/scss/argon.scss b/frontend/src/assets/scss/argon.scss
index 01a002691..1fa663a5f 100644
--- a/frontend/src/assets/scss/argon.scss
+++ b/frontend/src/assets/scss/argon.scss
@@ -23,38 +23,95 @@
// Bootstrap (4.1.3) components
-@import "~bootstrap/scss/root";
-@import "~bootstrap/scss/reboot";
-@import "~bootstrap/scss/type";
-@import "~bootstrap/scss/images";
-@import "~bootstrap/scss/code";
-@import "~bootstrap/scss/grid";
-@import "~bootstrap/scss/tables";
-@import "~bootstrap/scss/forms";
-@import "~bootstrap/scss/buttons";
-@import "~bootstrap/scss/transitions";
-@import "~bootstrap/scss/dropdown";
+@import "~bootstrap/scss/alert";
+@import "~bootstrap/scss/badge";
+@import "~bootstrap/scss/breadcrumb";
@import "~bootstrap/scss/button-group";
-@import "~bootstrap/scss/input-group";
+@import "~bootstrap/scss/buttons";
+@import "~bootstrap/scss/card";
+@import "~bootstrap/scss/carousel";
+@import "~bootstrap/scss/close";
+@import "~bootstrap/scss/code";
@import "~bootstrap/scss/custom-forms";
+@import "~bootstrap/scss/dropdown";
+@import "~bootstrap/scss/forms";
+@import "~bootstrap/scss/grid";
+@import "~bootstrap/scss/images";
+@import "~bootstrap/scss/input-group";
+@import "~bootstrap/scss/jumbotron";
+@import "~bootstrap/scss/list-group";
+@import "~bootstrap/scss/media";
+@import "~bootstrap/scss/modal";
@import "~bootstrap/scss/nav";
@import "~bootstrap/scss/navbar";
-@import "~bootstrap/scss/card";
-@import "~bootstrap/scss/breadcrumb";
@import "~bootstrap/scss/pagination";
-@import "~bootstrap/scss/badge";
-@import "~bootstrap/scss/jumbotron";
-@import "~bootstrap/scss/alert";
-@import "~bootstrap/scss/progress";
-@import "~bootstrap/scss/media";
-@import "~bootstrap/scss/list-group";
-@import "~bootstrap/scss/close";
-@import "~bootstrap/scss/modal";
-@import "~bootstrap/scss/tooltip";
@import "~bootstrap/scss/popover";
-@import "~bootstrap/scss/carousel";
-@import "~bootstrap/scss/utilities";
@import "~bootstrap/scss/print";
+@import "~bootstrap/scss/progress";
+@import "~bootstrap/scss/reboot";
+@import "~bootstrap/scss/root";
+@import "~bootstrap/scss/tables";
+@import "~bootstrap/scss/toasts";
+@import "~bootstrap/scss/tooltip";
+@import "~bootstrap/scss/transitions";
+@import "~bootstrap/scss/type";
+@import "~bootstrap/scss/utilities";
+@import "~bootstrap/scss/variables";
+
+// Utilities
+
+@import "~bootstrap/scss/utilities/align";
+@import "~bootstrap/scss/utilities/background";
+@import "~bootstrap/scss/utilities/borders";
+@import "~bootstrap/scss/utilities/clearfix";
+@import "~bootstrap/scss/utilities/display";
+@import "~bootstrap/scss/utilities/embed";
+@import "~bootstrap/scss/utilities/flex";
+@import "~bootstrap/scss/utilities/float";
+@import "~bootstrap/scss/utilities/overflow";
+@import "~bootstrap/scss/utilities/position";
+@import "~bootstrap/scss/utilities/screenreaders";
+@import "~bootstrap/scss/utilities/shadows";
+@import "~bootstrap/scss/utilities/sizing";
+@import "~bootstrap/scss/utilities/spacing";
+@import "~bootstrap/scss/utilities/stretched-link";
+@import "~bootstrap/scss/utilities/text";
+@import "~bootstrap/scss/utilities/visibility";
+
+
+// Mixins
+
+@import "~bootstrap/scss/mixins/alert";
+@import "~bootstrap/scss/mixins/badge";
+@import "~bootstrap/scss/mixins/border-radius";
+@import "~bootstrap/scss/mixins/box-shadow";
+@import "~bootstrap/scss/mixins/breakpoints";
+@import "~bootstrap/scss/mixins/buttons";
+@import "~bootstrap/scss/mixins/caret";
+@import "~bootstrap/scss/mixins/clearfix";
+@import "~bootstrap/scss/mixins/deprecate";
+@import "~bootstrap/scss/mixins/float";
+@import "~bootstrap/scss/mixins/forms";
+@import "~bootstrap/scss/mixins/gradients";
+@import "~bootstrap/scss/mixins/grid-framework";
+@import "~bootstrap/scss/mixins/grid";
+@import "~bootstrap/scss/mixins/hover";
+@import "~bootstrap/scss/mixins/image";
+@import "~bootstrap/scss/mixins/list-group";
+@import "~bootstrap/scss/mixins/lists";
+@import "~bootstrap/scss/mixins/nav-divider";
+@import "~bootstrap/scss/mixins/pagination";
+@import "~bootstrap/scss/mixins/reset-text";
+@import "~bootstrap/scss/mixins/resize";
+@import "~bootstrap/scss/mixins/screen-reader";
+@import "~bootstrap/scss/mixins/size";
+@import "~bootstrap/scss/mixins/table-row";
+@import "~bootstrap/scss/mixins/text-emphasis";
+@import "~bootstrap/scss/mixins/text-hide";
+@import "~bootstrap/scss/mixins/text-truncate";
+@import "~bootstrap/scss/mixins/transition";
+@import "~bootstrap/scss/mixins/visibility";
+
// Argon utilities and components
diff --git a/frontend/src/locales/de.json b/frontend/src/locales/de.json
index 0fcda6c30..6536a143b 100644
--- a/frontend/src/locales/de.json
+++ b/frontend/src/locales/de.json
@@ -108,7 +108,10 @@
"activity": {
"new":"Neue Gemeinschaftsstunden eintragen",
"list":"Meine Gemeinschaftsstunden Liste"
- }
+ },
+ "user-data": {
+ "change-success": "Deine Daten wurden gespeichert."
+ }
},
"navbar" : {
"my-profil":"Mein Profil",
diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json
index 1ea67c41d..f611e4b91 100644
--- a/frontend/src/locales/en.json
+++ b/frontend/src/locales/en.json
@@ -109,7 +109,10 @@
"activity": {
"new":"Register new community hours",
"list":"My Community Hours List"
- }
+ },
+ "user-data": {
+ "change-success": "Your data has been saved."
+ }
},
"navbar" : {
"my-profil":"My profile",
diff --git a/frontend/src/main.js b/frontend/src/main.js
index 20c8ffcb8..160ff73c7 100755
--- a/frontend/src/main.js
+++ b/frontend/src/main.js
@@ -54,10 +54,14 @@ extend('max', {
extend('gddSendAmount', {
validate(value, { min, max }) {
value = value.replace(',', '.')
- return value.match(/^[0-9]+(\.[0-9]{1,2})?$/) && Number(value) >= min && Number(value) <= max
+ return value.match(/^[0-9]+(\.[0-9]{0,2})?$/) && Number(value) >= min && Number(value) <= max
},
params: ['min', 'max'],
- message: (_, values) => i18n.t('form.validation.gddSendAmount', values),
+ message: (_, values) => {
+ values.min = i18n.n(values.min)
+ values.max = i18n.n(values.max)
+ return i18n.t('form.validation.gddSendAmount', values)
+ },
})
// eslint-disable-next-line camelcase
diff --git a/frontend/src/plugins/dashboard-plugin.js b/frontend/src/plugins/dashboard-plugin.js
index 3cb1cd862..2edac0995 100755
--- a/frontend/src/plugins/dashboard-plugin.js
+++ b/frontend/src/plugins/dashboard-plugin.js
@@ -4,6 +4,14 @@ import GlobalComponents from './globalComponents'
import GlobalDirectives from './globalDirectives'
import SideBar from '@/components/SidebarPlugin'
+import PortalVue from 'portal-vue'
+
+import VueBootstrapToasts from 'vue-bootstrap-toasts'
+
+// vue-bootstrap
+import { BootstrapVue, IconsPlugin } from 'bootstrap-vue'
+
+// asset imports
import '@/assets/scss/argon.scss'
import '@/assets/vendor/nucleo/css/nucleo.css'
import * as rules from 'vee-validate/dist/rules'
@@ -20,8 +28,6 @@ import VueMoment from 'vue-moment'
import Loading from 'vue-loading-overlay'
import 'vue-loading-overlay/dist/vue-loading.css'
-import { BootstrapVue, IconsPlugin } from 'bootstrap-vue'
-
Object.keys(rules).forEach((rule) => {
extend(rule, {
...rules[rule], // copies rule configuration
@@ -34,8 +40,10 @@ export default {
Vue.use(GlobalComponents)
Vue.use(GlobalDirectives)
Vue.use(SideBar)
+ Vue.use(PortalVue)
Vue.use(BootstrapVue)
Vue.use(IconsPlugin)
+ Vue.use(VueBootstrapToasts)
Vue.use(VueMoment)
Vue.use(VueQrcodeReader)
Vue.use(VueQrcode)
diff --git a/frontend/src/views/Layout/DashboardLayout_gdd.vue b/frontend/src/views/Layout/DashboardLayout_gdd.vue
index 68f3db88e..6adfe83b9 100755
--- a/frontend/src/views/Layout/DashboardLayout_gdd.vue
+++ b/frontend/src/views/Layout/DashboardLayout_gdd.vue
@@ -2,6 +2,7 @@
+
@@ -82,11 +82,6 @@ export default {
bookedBalance: 0,
transactionCount: 0,
pending: true,
- UserProfileTestData: {
- username: 'Mustermax',
- desc:
- 'Max Mustermann seine Beschreibung. Max Mustermann seine Beschreibung. Max Mustermann seine Beschreibung. Max Mustermann seine Beschreibung. ',
- },
}
},
methods: {
diff --git a/frontend/src/views/Pages/AccountOverview.vue b/frontend/src/views/Pages/AccountOverview.vue
index 561edbf9c..35490e093 100644
--- a/frontend/src/views/Pages/AccountOverview.vue
+++ b/frontend/src/views/Pages/AccountOverview.vue
@@ -17,7 +17,6 @@
:email="transactionData.email"
:amount="transactionData.amount"
:memo="transactionData.memo"
- :date="transactionData.target_date"
:loading="loading"
@send-transaction="sendTransaction"
@on-reset="onReset"
@@ -54,7 +53,6 @@ const EMPTY_TRANSACTION_DATA = {
email: '',
amount: 0,
memo: '',
- target_date: '',
}
export default {
@@ -96,7 +94,6 @@ export default {
},
methods: {
setTransaction(data) {
- data.target_date = new Date(Date.now()).toISOString()
this.transactionData = data
this.currentTransactionStep = 1
},
diff --git a/frontend/src/views/Pages/AccountOverview/GddSend/TransactionConfirmation.vue b/frontend/src/views/Pages/AccountOverview/GddSend/TransactionConfirmation.vue
index b4852df48..d81311b79 100644
--- a/frontend/src/views/Pages/AccountOverview/GddSend/TransactionConfirmation.vue
+++ b/frontend/src/views/Pages/AccountOverview/GddSend/TransactionConfirmation.vue
@@ -16,10 +16,6 @@
{{ memo ? memo : '-' }}
{{ $t('form.message') }}
-
- {{ $d($moment(date), 'long') }}
- {{ $t('form.date') }}
-
@@ -42,7 +38,6 @@ export default {
email: { type: String, default: '' },
amount: { type: Number, default: 0 },
memo: { type: String, default: '' },
- date: { type: String, default: '' },
loading: { type: Boolean, default: false },
},
}
diff --git a/frontend/src/views/Pages/ForgotPassword.vue b/frontend/src/views/Pages/ForgotPassword.vue
index 857278908..eebba0ced 100644
--- a/frontend/src/views/Pages/ForgotPassword.vue
+++ b/frontend/src/views/Pages/ForgotPassword.vue
@@ -70,17 +70,15 @@ export default {
},
}
},
+ created() {},
methods: {
getValidationState({ dirty, validated, valid = null }) {
return dirty || validated ? valid : null
},
async onSubmit() {
- const result = await loginAPI.sendEmail(this.form.email)
- if (result.success) {
- this.$router.push('/thx/password')
- } else {
- alert(result.result)
- }
+ await loginAPI.sendEmail(this.form.email)
+ // always give success to avoid email spying
+ this.$router.push('/thx/password')
},
},
}
diff --git a/frontend/src/views/Pages/ResetPassword.vue b/frontend/src/views/Pages/ResetPassword.vue
index 45f3e681d..1d62349b7 100644
--- a/frontend/src/views/Pages/ResetPassword.vue
+++ b/frontend/src/views/Pages/ResetPassword.vue
@@ -139,14 +139,14 @@ export default {
if (result.success) {
this.form.password = ''
/*
- this.$store.dispatch('login', {
- sessionId: result.result.data.session_id,
- email: result.result.data.user.email,
- })
- */
+ this.$store.dispatch('login', {
+ sessionId: result.result.data.session_id,
+ email: result.result.data.user.email,
+ })
+ */
this.$router.push('/thx/reset')
} else {
- alert(result.result.message)
+ this.$toast.error(result.result.message)
}
},
async authenticate() {
@@ -157,7 +157,7 @@ export default {
this.sessionId = result.result.data.session_id
this.email = result.result.data.user.email
} else {
- alert(result.result.message)
+ this.$toast.error(result.result.message)
}
},
},
diff --git a/frontend/src/views/Pages/UserProfile.vue b/frontend/src/views/Pages/UserProfile.vue
deleted file mode 100644
index 882b52e78..000000000
--- a/frontend/src/views/Pages/UserProfile.vue
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/frontend/src/views/Pages/UserProfile/UserCard_FormUserData.vue b/frontend/src/views/Pages/UserProfile/UserCard_FormUserData.vue
index a1f6a5919..1fa26b5e0 100644
--- a/frontend/src/views/Pages/UserProfile/UserCard_FormUserData.vue
+++ b/frontend/src/views/Pages/UserProfile/UserCard_FormUserData.vue
@@ -6,30 +6,35 @@
style="background-color: #ebebeba3 !important"
>
-
-
-
- {{ $t('form.edit') }}
-
-
+
+
+
+ {{ $t('form.change') }}
+
+
+
+
-
+
+
{{ $t('form.firstname') }}
-
+
{{ form.firstName }}
@@ -40,7 +45,7 @@
{{ $t('form.lastname') }}
-
+
{{ form.lastName }}
@@ -51,14 +56,30 @@
{{ $t('form.description') }}
-
+
{{ form.description }}
-
+
-
+
+
+
+
+
+ {{ $t('form.save') }}
+
+
+
+
+
@@ -68,22 +89,38 @@ import loginAPI from '../../../apis/loginAPI'
export default {
name: 'FormUserData',
- props: {
- UserProfileTestData: { type: Object },
- },
data() {
return {
- editUserdata: true,
+ showUserData: true,
sessionId: this.$store.state.sessionId,
form: {
firstName: this.$store.state.firstName,
lastName: this.$store.state.lastName,
description: this.$store.state.description,
},
+ loading: true,
}
},
methods: {
- async onSubmit() {
+ cancelEdit() {
+ this.form.firstName = this.$store.state.firstName
+ this.form.lastName = this.$store.state.lastName
+ this.form.description = this.$store.state.description
+ this.showUserData = true
+ },
+ loadSubmitButton() {
+ if (
+ this.form.firstName !== this.$store.state.firstName ||
+ this.form.lastName !== this.$store.state.lastName ||
+ this.form.description !== this.$store.state.description
+ ) {
+ this.loading = false
+ } else {
+ this.loading = true
+ }
+ },
+ async onSubmit(event) {
+ event.preventDefault()
const result = await loginAPI.updateUserInfos(
this.$store.state.sessionId,
this.$store.state.email,
@@ -97,9 +134,10 @@ export default {
this.$store.commit('firstName', this.form.firstName)
this.$store.commit('lastName', this.form.lastName)
this.$store.commit('description', this.form.description)
- this.editUserdata = true
+ this.showUserData = true
+ this.$toast.success(this.$t('site.profil.user-data.change-success'))
} else {
- alert(result.result.message)
+ this.$toast.error(result.result.message)
}
},
},
diff --git a/frontend/src/views/Pages/UserProfile/UserCard_FormUserPasswort.vue b/frontend/src/views/Pages/UserProfile/UserCard_FormUserPasswort.vue
index e1ade4ae0..6bc1d8942 100644
--- a/frontend/src/views/Pages/UserProfile/UserCard_FormUserPasswort.vue
+++ b/frontend/src/views/Pages/UserProfile/UserCard_FormUserPasswort.vue
@@ -1,62 +1,105 @@
-
-
-
- {{ $t('form.password') }} {{ $t('form.change') }}
-
-
-
-
-
-
-
- {{ $t('form.password_old') }}
-
-
-
+
-
-
- {{ $t('form.password_new') }}
-
-
-
-
-
-
-
- {{ $t('form.password_new_repeat') }}
-
-
-
-
-
-
+
+
+
+
+ {{ $t('form.password_old') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('form.password_new') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('form.password_new_repeat') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('form.save') }}
+
+
+
+
+
+
@@ -71,10 +114,30 @@ export default {
email: null,
password: '',
passwordNew: '',
- passwordNew2: '',
+ passwordNewRepeat: '',
+ passwordVisibleOldPwd: false,
+ passwordVisibleNewPwd: false,
+ passwordVisibleNewPwdRepeat: false,
+ loading: true,
}
},
methods: {
+ togglePasswordVisibilityNewPwd() {
+ this.passwordVisibleNewPwd = !this.passwordVisibleNewPwd
+ },
+ togglePasswordVisibilityNewPwdRepeat() {
+ this.passwordVisibleNewPwdRepeat = !this.passwordVisibleNewPwdRepeat
+ },
+ togglePasswordVisibilityOldPwd() {
+ this.passwordVisibleOldPwd = !this.passwordVisibleOldPwd
+ },
+ loadSubmitButton() {
+ if (this.passwordVisibleNewPwd === this.passwordVisibleNewPwdRepeat) {
+ this.loading = false
+ } else {
+ this.loading = true
+ }
+ },
async onSubmit() {
// console.log(this.data)
const result = await loginAPI.changePasswordProfile(
diff --git a/frontend/src/views/Pages/UserProfile/UserCard_FormUsername.vue b/frontend/src/views/Pages/UserProfile/UserCard_FormUsername.vue
index b195f6ef6..2b3b451fe 100644
--- a/frontend/src/views/Pages/UserProfile/UserCard_FormUsername.vue
+++ b/frontend/src/views/Pages/UserProfile/UserCard_FormUsername.vue
@@ -1,42 +1,61 @@
-
-
-
- {{ $t('form.username') }} {{ $t('form.change') }}
-
-
+
+
+
+ {{ $t('form.change') }}
+
+
+
-
+
+
{{ $t('form.username') }}
- @{{ $store.state.username }}
-
-
-
-
+ @{{ username }}
+
+
+
+
+
+ {{ $t('form.username') }}
+
+
+
+
+
{{ $t('form.change_username_info') }}
-
-
- {{ $t('form.save') }}
-
-
+
+
+
+
+
+ {{ $t('form.save') }}
+
+
+
+
@@ -47,16 +66,24 @@ export default {
name: 'FormUsername',
data() {
return {
- edit_username: true,
- username: '',
+ editUsername: true,
+ username: this.$store.state.username,
+ form: {
+ username: this.$store.state.username,
+ },
}
},
methods: {
async onSubmit() {
- // console.log(this.data)
- const result = await loginAPI.changeUsernameProfile(this.username)
+ const result = await loginAPI.changeUsernameProfile(
+ this.$store.state.sessionId,
+ this.$store.state.email,
+ this.form.username,
+ )
if (result.success) {
- alert('changeUsername success')
+ this.$store.commit('username', this.form.username)
+ this.editUserdata = this.editUsername = !this.editUsername
+ alert('Dein Username wurde geƤndert.')
} else {
alert(result.result.message)
}
diff --git a/frontend/src/views/Pages/UserProfileOverview.spec.js b/frontend/src/views/Pages/UserProfileOverview.spec.js
new file mode 100644
index 000000000..617e0ade0
--- /dev/null
+++ b/frontend/src/views/Pages/UserProfileOverview.spec.js
@@ -0,0 +1,38 @@
+import { shallowMount } from '@vue/test-utils'
+import UserProfileOverview from './UserProfileOverview'
+
+const localVue = global.localVue
+
+describe('UserProfileOverview', () => {
+ let wrapper
+
+ const mocks = {
+ $t: jest.fn((t) => t),
+ }
+
+ const Wrapper = () => {
+ return shallowMount(UserProfileOverview, { localVue, mocks })
+ }
+
+ describe('shallow Mount', () => {
+ beforeEach(() => {
+ wrapper = Wrapper()
+ })
+
+ it('has a user card', () => {
+ expect(wrapper.findComponent({ name: 'UserCard' }).exists()).toBeTruthy()
+ })
+
+ it('has a user data form', () => {
+ expect(wrapper.findComponent({ name: 'FormUserData' }).exists()).toBeTruthy()
+ })
+
+ it('has a user name form', () => {
+ expect(wrapper.findComponent({ name: 'FormUsername' }).exists()).toBeTruthy()
+ })
+
+ it('has a user password form', () => {
+ expect(wrapper.findComponent({ name: 'FormUserPasswort' }).exists()).toBeTruthy()
+ })
+ })
+})
diff --git a/frontend/src/views/Pages/UserProfileOverview.vue b/frontend/src/views/Pages/UserProfileOverview.vue
index def5289cb..0708a2329 100644
--- a/frontend/src/views/Pages/UserProfileOverview.vue
+++ b/frontend/src/views/Pages/UserProfileOverview.vue
@@ -1,7 +1,7 @@
-
+
@@ -22,7 +22,6 @@ export default {
props: {
balance: { type: Number, default: 0 },
transactionCount: { type: Number, default: 0 },
- UserProfileTestData: { type: Object },
},
}
diff --git a/frontend/yarn.lock b/frontend/yarn.lock
index a3a835813..65f3ff71d 100644
--- a/frontend/yarn.lock
+++ b/frontend/yarn.lock
@@ -13268,6 +13268,11 @@ vm-browserify@^1.0.1:
resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0"
integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==
+vue-bootstrap-toasts@^1.0.7:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/vue-bootstrap-toasts/-/vue-bootstrap-toasts-1.0.7.tgz#111c38855941e8eb0538e21f41c173e2af67dd53"
+ integrity sha512-JhurJOAwdNcINQ/QlT701sx0r447YTGpvtxtmZNC9pwDvEqp2I0Pyv15jS4neWwYHkA1gXB42nBsDRcWcj1hlg==
+
vue-bootstrap-typeahead@^0.2.6:
version "0.2.6"
resolved "https://registry.yarnpkg.com/vue-bootstrap-typeahead/-/vue-bootstrap-typeahead-0.2.6.tgz#8c1999a00bf4bf9fc906bae3a462482482cbc297"
diff --git a/login_server/src/cpp/JSONInterface/JsonCreateTransaction.cpp b/login_server/src/cpp/JSONInterface/JsonCreateTransaction.cpp
index 5478f78cf..96f5a13a6 100644
--- a/login_server/src/cpp/JSONInterface/JsonCreateTransaction.cpp
+++ b/login_server/src/cpp/JSONInterface/JsonCreateTransaction.cpp
@@ -140,6 +140,13 @@ Poco::JSON::Object* JsonCreateTransaction::transfer(Poco::Dynamic::Var params)
try {
auto transaction = model::gradido::Transaction::createTransfer(sender_user, target_pubkey, mTargetGroup, amount, mMemo, mBlockchainType);
+ if (mSession->lastTransactionTheSame(transaction)) {
+ return stateError("transaction are the same as the last (within 100 seconds)");
+ }
+ else {
+ mSession->setLastTransaction(transaction);
+ }
+
if (mAutoSign) {
Poco::JSON::Array errors;
transaction->sign(sender_user);
diff --git a/login_server/src/cpp/JSONInterface/JsonUpdateUserInfos.cpp b/login_server/src/cpp/JSONInterface/JsonUpdateUserInfos.cpp
index 7db31df33..804d06987 100644
--- a/login_server/src/cpp/JSONInterface/JsonUpdateUserInfos.cpp
+++ b/login_server/src/cpp/JSONInterface/JsonUpdateUserInfos.cpp
@@ -68,49 +68,49 @@ Poco::JSON::Object* JsonUpdateUserInfos::handle(Poco::Dynamic::Var params)
try {
- if ( "User.first_name" == name && value.size() > 0) {
- if (!value.isString()) {
- jsonErrorsArray.add("User.first_name isn't a string");
- }
- else {
- user_model->setFirstName(value.toString());
+ if ( "User.first_name" == name) {
+ std::string str_val = validateString(value, "User.first_name", jsonErrorsArray);
+
+ if (str_val.size() > 0) {
+ user_model->setFirstName(str_val);
extractet_values++;
}
}
- else if ("User.last_name" == name && value.size() > 0) {
- if (!value.isString()) {
- jsonErrorsArray.add("User.last_name isn't a string");
- }
- else {
- user_model->setLastName(value.toString());
+ else if ("User.last_name" == name ) {
+ std::string str_val = validateString(value, "User.last_name", jsonErrorsArray);
+
+ if (str_val.size() > 0) {
+ user_model->setLastName(str_val);
extractet_values++;
}
+
}
- else if ("User.username" == name && value.size() > 3) {
- if (!value.isString()) {
- jsonErrorsArray.add("User.username isn't a string");
- }
- else {
- auto new_username = value.toString();
- if (user_model->getUsername() != new_username) {
- if (user->isUsernameAlreadyUsed(new_username)) {
+ else if ("User.username" == name) {
+ std::string str_val = validateString(value, "User.username", jsonErrorsArray);
+
+ if (str_val.size() > 0) {
+ if (user_model->getUsername() != "") {
+ jsonErrorsArray.add("change username currently not supported!");
+ }
+ else if (user_model->getUsername() != str_val) {
+ if (user->isUsernameAlreadyUsed(str_val)) {
jsonErrorsArray.add("username already used");
}
else {
- user_model->setUsername(new_username);
+ user_model->setUsername(str_val);
extractet_values++;
}
}
}
}
- else if ("User.description" == name && value.size() > 3) {
- if (!value.isString()) {
- jsonErrorsArray.add("description isn't a string");
- }
- else {
- user_model->setDescription(value.toString());
+ else if ("User.description" == name) {
+ std::string str_val = validateString(value, "User.description", jsonErrorsArray);
+
+ if (str_val.size() > 0) {
+ user_model->setDescription(str_val);
extractet_values++;
}
+
}
else if ("User.disabled" == name) {
if (value.isBoolean()) {
@@ -130,11 +130,10 @@ Poco::JSON::Object* JsonUpdateUserInfos::handle(Poco::Dynamic::Var params)
}
}
else if ("User.language" == name && value.size() > 0) {
- if (!value.isString()) {
- jsonErrorsArray.add("User.language isn't a string");
- }
- else {
- auto lang = LanguageManager::languageFromString(value.toString());
+ std::string str_val = validateString(value, "User.language", jsonErrorsArray);
+
+ if (str_val.size() > 0) {
+ auto lang = LanguageManager::languageFromString(str_val);
if (LANG_NULL == lang) {
jsonErrorsArray.add("User.language isn't a valid language");
}
@@ -143,12 +142,13 @@ Poco::JSON::Object* JsonUpdateUserInfos::handle(Poco::Dynamic::Var params)
extractet_values++;
}
}
+
}
- else if ("User.password" == name && value.size() > 0 && (ServerConfig::g_AllowUnsecureFlags & ServerConfig::UNSECURE_PASSWORD_REQUESTS) == ServerConfig::UNSECURE_PASSWORD_REQUESTS) {
- if (!value.isString()) {
- jsonErrorsArray.add("User.password isn't string");
- }
- else {
+ else if ("User.password" == name && (ServerConfig::g_AllowUnsecureFlags & ServerConfig::UNSECURE_PASSWORD_REQUESTS) == ServerConfig::UNSECURE_PASSWORD_REQUESTS) {
+ std::string str_val = validateString(value, "User.password", jsonErrorsArray);
+
+ if (str_val.size() > 0) {
+
NotificationList errors;
if (!sm->checkPwdValidation(value.toString(), &errors, LanguageManager::getInstance()->getFreeCatalog(LANG_EN))) {
jsonErrorsArray.add("User.password isn't valid");
@@ -174,7 +174,9 @@ Poco::JSON::Object* JsonUpdateUserInfos::handle(Poco::Dynamic::Var params)
}
}
catch (Poco::Exception& ex) {
- jsonErrorsArray.add("update parameter invalid");
+ std::string error_message = "exception by parsing json: ";
+ error_message += ex.displayText();
+ jsonErrorsArray.add(error_message);
}
}
if (extractet_values > 0) {
@@ -189,4 +191,23 @@ Poco::JSON::Object* JsonUpdateUserInfos::handle(Poco::Dynamic::Var params)
result->set("state", "success");
return result;
+}
+
+std::string JsonUpdateUserInfos::validateString(Poco::Dynamic::Var value, const char* fieldName, Poco::JSON::Array& errorArray)
+{
+ std::string errorMessage = fieldName;
+
+ if (!value.isString()) {
+ errorMessage += " isn't a string";
+ errorArray.add(errorMessage);
+ return "";
+ }
+ std::string string_value = value.toString();
+
+ if (string_value.size() == 0) {
+ errorMessage += " is empty";
+ errorArray.add(errorArray);
+ return "";
+ }
+ return string_value;
}
\ No newline at end of file
diff --git a/login_server/src/cpp/JSONInterface/JsonUpdateUserInfos.h b/login_server/src/cpp/JSONInterface/JsonUpdateUserInfos.h
index 2c1ca94fc..f651fb345 100644
--- a/login_server/src/cpp/JSONInterface/JsonUpdateUserInfos.h
+++ b/login_server/src/cpp/JSONInterface/JsonUpdateUserInfos.h
@@ -18,6 +18,8 @@ public:
protected:
+ std::string validateString(Poco::Dynamic::Var value, const char* fieldName, Poco::JSON::Array& errorArray);
+
};
diff --git a/login_server/src/cpp/model/Session.cpp b/login_server/src/cpp/model/Session.cpp
index f50fa87df..767f1acb3 100644
--- a/login_server/src/cpp/model/Session.cpp
+++ b/login_server/src/cpp/model/Session.cpp
@@ -919,6 +919,17 @@ bool Session::useOrGeneratePassphrase(const std::string& passphase)
}
*/
+bool Session::lastTransactionTheSame(Poco::AutoPtr newTransaction)
+{
+ assert(!newTransaction.isNull());
+ lock();
+ if (mLastTransaction.isNull()) {
+ return false;
+ }
+ bool result = mLastTransaction->isTheSameTransaction(newTransaction);
+ unlock();
+ return result;
+}
bool Session::generateKeys(bool savePrivkey, bool savePassphrase)
{
diff --git a/login_server/src/cpp/model/Session.h b/login_server/src/cpp/model/Session.h
index 1607d23c9..26b1a1170 100644
--- a/login_server/src/cpp/model/Session.h
+++ b/login_server/src/cpp/model/Session.h
@@ -19,6 +19,8 @@
#include "../controller/EmailVerificationCode.h"
+#include "model/gradido/Transaction.h"
+
#include "Poco/Thread.h"
#include "Poco/Types.h"
#include "Poco/DateTime.h"
@@ -163,6 +165,8 @@ public:
// ------------------------ transactions functions ----------------------------
+ inline void setLastTransaction(Poco::AutoPtr lastTransaction) { lock(); mLastTransaction = lastTransaction; unlock(); }
+ bool lastTransactionTheSame(Poco::AutoPtr newTransaction);
inline LanguageCatalog* getLanguageCatalog() { return mLanguageCatalog.isNull() ? nullptr : mLanguageCatalog; }
void setLanguage(Languages lang);
@@ -188,6 +192,7 @@ protected:
private:
+
int mHandleId;
Poco::AutoPtr mNewUser;
std::string mPassphrase;
@@ -200,6 +205,7 @@ private:
Poco::AutoPtr mEmailVerificationCodeObject;
std::shared_mutex mSharedMutex;
+ Poco::AutoPtr mLastTransaction;
SessionStates mState;
diff --git a/login_server/src/cpp/model/gradido/Transaction.cpp b/login_server/src/cpp/model/gradido/Transaction.cpp
index 22ac8937f..404d4f747 100644
--- a/login_server/src/cpp/model/gradido/Transaction.cpp
+++ b/login_server/src/cpp/model/gradido/Transaction.cpp
@@ -630,6 +630,31 @@ namespace model {
}
+ bool Transaction::isTheSameTransaction(Poco::AutoPtr other)
+ {
+ bool result = false;
+
+ auto other_proto = other->getTransactionBody()->getBody();
+ auto other_created = other_proto->created();
+ auto own_body_bytes = getTransactionBody()->getBodyBytes();
+ auto own_body_updated = new proto::gradido::TransactionBody;
+ own_body_updated->ParseFromString(own_body_bytes);
+ auto own_created = own_body_updated->mutable_created();
+ Poco::Int64 timeDiff = other_created.seconds() - own_created->seconds();
+ *own_created = other_created;
+
+ result = own_body_updated->SerializeAsString() == other_proto->SerializeAsString();
+
+ delete own_body_updated;
+
+ // if they are more than 10 seconds between transaction they consider as not the same
+ if (abs(timeDiff) > 10) {
+ return false;
+ }
+
+ return result;
+ }
+
/// TASK ////////////////////////
SendTransactionTask::SendTransactionTask(Poco::AutoPtr transaction)
diff --git a/login_server/src/cpp/model/gradido/Transaction.h b/login_server/src/cpp/model/gradido/Transaction.h
index 80f5e7453..a35376040 100644
--- a/login_server/src/cpp/model/gradido/Transaction.h
+++ b/login_server/src/cpp/model/gradido/Transaction.h
@@ -86,6 +86,8 @@ namespace model {
std::string getTransactionAsJson(bool replaceBase64WithHex = false);
inline Poco::AutoPtr getPairedTransaction() { return mPairedTransaction; }
+ bool isTheSameTransaction(Poco::AutoPtr other);
+
protected: