From 19720ee6ca31a575d2b3bf46fb433e7fc3a1d73b Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Tue, 8 Apr 2025 18:33:16 +0200 Subject: [PATCH 01/57] adjust configs, add GMS_PLAYGROUND_ROUTE, move export script into apis folder like in humhub --- admin/vite.config.js | 4 ++ backend/package.json | 3 +- .../gmsUsers.ts => apis/gms/ExportUsers.ts} | 23 ++----- backend/src/config/index.ts | 1 + backend/src/config/schema.ts | 7 +++ .../util/authenticateGmsUserPlayground.ts | 2 +- .../graphql/resolver/util/sendUserToGms.ts | 8 ++- backend/src/seeds/gmsUserList.ts | 60 ------------------- frontend/vite.config.js | 4 ++ 9 files changed, 28 insertions(+), 84 deletions(-) rename backend/src/{seeds/gmsUsers.ts => apis/gms/ExportUsers.ts} (83%) delete mode 100644 backend/src/seeds/gmsUserList.ts diff --git a/admin/vite.config.js b/admin/vite.config.js index 29dd0a69a..72f7e8c47 100644 --- a/admin/vite.config.js +++ b/admin/vite.config.js @@ -40,6 +40,10 @@ export default defineConfig(async ({ command }) => { host: CONFIG.ADMIN_MODULE_HOST, // '0.0.0.0', port: CONFIG.ADMIN_MODULE_PORT, // 8080, }, + preview: { + host: CONFIG.ADMIN_MODULE_HOST, // '0.0.0.0', + port: CONFIG.ADMIN_MODULE_PORT, // 8080, + }, resolve: { alias: { '@': path.resolve(__dirname, './src'), diff --git a/backend/package.json b/backend/package.json index 2d4756afb..880d48a53 100644 --- a/backend/package.json +++ b/backend/package.json @@ -16,8 +16,7 @@ "test": "cross-env TZ=UTC NODE_ENV=development jest --runInBand --forceExit --detectOpenHandles", "seed": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/seeds/index.ts", "klicktipp": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/util/executeKlicktipp.ts", - "gmsusers": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/seeds/gmsUsers.ts", - "gmsuserList": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/seeds/gmsUserList.ts", + "gmsusers": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/apis/gms/ExportUsers.ts", "humhubUserExport": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/apis/humhub/ExportUsers.ts", "locales": "scripts/sort.sh" }, diff --git a/backend/src/seeds/gmsUsers.ts b/backend/src/apis/gms/ExportUsers.ts similarity index 83% rename from backend/src/seeds/gmsUsers.ts rename to backend/src/apis/gms/ExportUsers.ts index 6f059697d..4b8a303f6 100644 --- a/backend/src/seeds/gmsUsers.ts +++ b/backend/src/apis/gms/ExportUsers.ts @@ -4,7 +4,6 @@ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-return */ -import { entities } from '@entity/index' import { User as DbUser } from '@entity/User' // import { createTestClient } from 'apollo-server-testing' @@ -18,6 +17,8 @@ import { LogError } from '@/server/LogError' import { backendLogger as logger } from '@/server/logger' CONFIG.EMAIL = false +// use force to copy over all user even if gmsRegistered is set to true +const forceMode = process.argv.includes('--force') const context = { token: '', @@ -31,21 +32,6 @@ const context = { clientTimezoneOffset: 0, } -export const cleanDB = async () => { - // this only works as long we do not have foreign key constraints - for (const entity of entities) { - await resetEntity(entity) - } -} - -const resetEntity = async (entity: any) => { - const items = await entity.find({ withDeleted: true }) - if (items.length > 0) { - const ids = items.map((e: any) => e.id) - await entity.delete(ids) - } -} - const run = async () => { const server = await createServer(context) // const seedClient = createTestClient(server.apollo) @@ -59,8 +45,7 @@ const run = async () => { const userIds = await DbUser.createQueryBuilder() .select('id') .where({ foreign: false }) - // .andWhere('deleted_at is null') - // .andWhere({ gmsRegistered: false }) + .andWhere('deleted_at is null') .getRawMany() logger.debug('userIds:', userIds) @@ -73,7 +58,7 @@ const run = async () => { if (user) { logger.debug('found local User:', user) if (user.gmsAllowed) { - await sendUserToGms(user, homeCom) + await sendUserToGms(user, homeCom, forceMode) /* const gmsUser = new GmsUser(user) try { diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 32118e248..af62fc76b 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -138,6 +138,7 @@ const gms = { // koordinates of Illuminz-instance of GMS GMS_API_URL: process.env.GMS_API_URL ?? 'http://localhost:4044/', GMS_DASHBOARD_URL: process.env.GMS_DASHBOARD_URL ?? 'http://localhost:8080/', + GMS_PLAYGROUND_ROUTE: process.env.GMS_PLAYGROUND_ROUTE ?? 'usersearch-playground', // used as secret postfix attached at the gms community-auth-url endpoint ('/hook/gms/' + 'secret') GMS_WEBHOOK_SECRET: process.env.GMS_WEBHOOK_SECRET ?? 'secret', } diff --git a/backend/src/config/schema.ts b/backend/src/config/schema.ts index 19eed15ed..0158dfdaa 100644 --- a/backend/src/config/schema.ts +++ b/backend/src/config/schema.ts @@ -279,6 +279,13 @@ export const schema = Joi.object({ .default('http://localhost:8080/') .description('The URL for the GMS dashboard'), + GMS_PLAYGROUND_ROUTE: Joi.string() + .pattern(/^[\w_-]*$/) + .default('usersearch-playground') + .description( + 'gms frontend playground route, playground for standalone playground, usersearch-playground for old', + ), + GMS_WEBHOOK_SECRET: Joi.string() .min(1) .default('secret') diff --git a/backend/src/graphql/resolver/util/authenticateGmsUserPlayground.ts b/backend/src/graphql/resolver/util/authenticateGmsUserPlayground.ts index 11e92ce53..41307eb55 100644 --- a/backend/src/graphql/resolver/util/authenticateGmsUserPlayground.ts +++ b/backend/src/graphql/resolver/util/authenticateGmsUserPlayground.ts @@ -14,7 +14,7 @@ export async function authenticateGmsUserPlayground( const result = new GmsUserAuthenticationResult() const dashboardUrl = ensureUrlEndsWithSlash(CONFIG.GMS_DASHBOARD_URL) - result.url = dashboardUrl.concat('usersearch-playground') + result.url = dashboardUrl.concat(CONFIG.GMS_PLAYGROUND_ROUTE) result.token = await verifyAuthToken(dbUser.communityUuid, token) logger.info('GmsUserAuthenticationResult:', result) return result diff --git a/backend/src/graphql/resolver/util/sendUserToGms.ts b/backend/src/graphql/resolver/util/sendUserToGms.ts index 22af795d4..90351125f 100644 --- a/backend/src/graphql/resolver/util/sendUserToGms.ts +++ b/backend/src/graphql/resolver/util/sendUserToGms.ts @@ -7,14 +7,18 @@ import { CONFIG } from '@/config' import { LogError } from '@/server/LogError' import { backendLogger as logger } from '@/server/logger' -export async function sendUserToGms(user: DbUser, homeCom: DbCommunity): Promise { +export async function sendUserToGms( + user: DbUser, + homeCom: DbCommunity, + alwaysCreateUser?: boolean, +): Promise { if (homeCom.gmsApiKey === null) { throw new LogError('HomeCommunity needs GMS-ApiKey to publish user data to GMS.') } logger.debug('User send to GMS:', user) const gmsUser = new GmsUser(user) try { - if (!user.gmsRegistered && user.gmsRegisteredAt === null) { + if (alwaysCreateUser === true || (!user.gmsRegistered && user.gmsRegisteredAt === null)) { logger.debug('create user in gms:', gmsUser) // eslint-disable-next-line @typescript-eslint/no-unsafe-argument if (await createGmsUser(homeCom.gmsApiKey, gmsUser)) { diff --git a/backend/src/seeds/gmsUserList.ts b/backend/src/seeds/gmsUserList.ts deleted file mode 100644 index 7603ca116..000000000 --- a/backend/src/seeds/gmsUserList.ts +++ /dev/null @@ -1,60 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-unsafe-return */ - -import { entities } from '@entity/index' -// import { createTestClient } from 'apollo-server-testing' - -import { CONFIG } from '@/config' -import { createServer } from '@/server/createServer' -import { backendLogger as logger } from '@/server/logger' - -CONFIG.EMAIL = false - -const context = { - token: '', - setHeaders: { - push: (value: { key: string; value: string }): void => { - context.token = value.value - }, - // eslint-disable-next-line @typescript-eslint/no-empty-function - forEach: (): void => {}, - }, - clientTimezoneOffset: 0, -} - -export const cleanDB = async () => { - // this only works as long we do not have foreign key constraints - for (const entity of entities) { - await resetEntity(entity) - } -} - -const resetEntity = async (entity: any) => { - const items = await entity.find({ withDeleted: true }) - if (items.length > 0) { - const ids = items.map((e: any) => e.id) - await entity.delete(ids) - } -} - -const run = async () => { - const server = await createServer(context) - // const seedClient = createTestClient(server.apollo) - const { con } = server - - // test GMS-Api Client - try { - // const gmsComArray = await communityList() - // logger.debug('GMS-Community-List:', gmsComArray) - // const gmsUserArray = await userList() - // logger.debug('GMS-Community-User-List:', gmsUserArray) - } catch (err) { - logger.error('Error in GMS-API:', err) - } - await con.close() -} - -void run() diff --git a/frontend/vite.config.js b/frontend/vite.config.js index 42e025e4b..28a222388 100644 --- a/frontend/vite.config.js +++ b/frontend/vite.config.js @@ -48,6 +48,10 @@ export default defineConfig(async ({ command }) => { minify: CONFIG.PRODUCTION === true, }, }, + preview: { + host: CONFIG.FRONTEND_MODULE_HOST, // '0.0.0.0', + port: CONFIG.FRONTEND_MODULE_PORT, // 3000, + }, resolve: { alias: { '@': path.resolve(__dirname, './src'), From 9f181a20791630638bf8a1518667e5a51e2f6107 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Tue, 8 Apr 2025 18:57:54 +0200 Subject: [PATCH 02/57] change name and default value --- backend/src/config/index.ts | 2 +- backend/src/config/schema.ts | 6 +++--- .../graphql/resolver/util/authenticateGmsUserPlayground.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index af62fc76b..4ef64f3ea 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -138,7 +138,7 @@ const gms = { // koordinates of Illuminz-instance of GMS GMS_API_URL: process.env.GMS_API_URL ?? 'http://localhost:4044/', GMS_DASHBOARD_URL: process.env.GMS_DASHBOARD_URL ?? 'http://localhost:8080/', - GMS_PLAYGROUND_ROUTE: process.env.GMS_PLAYGROUND_ROUTE ?? 'usersearch-playground', + GMS_USER_SEARCH_FRONTEND_ROUTE: process.env.GMS_USER_SEARCH_FRONTEND_ROUTE ?? 'user-search', // used as secret postfix attached at the gms community-auth-url endpoint ('/hook/gms/' + 'secret') GMS_WEBHOOK_SECRET: process.env.GMS_WEBHOOK_SECRET ?? 'secret', } diff --git a/backend/src/config/schema.ts b/backend/src/config/schema.ts index 0158dfdaa..bf7c6c810 100644 --- a/backend/src/config/schema.ts +++ b/backend/src/config/schema.ts @@ -279,11 +279,11 @@ export const schema = Joi.object({ .default('http://localhost:8080/') .description('The URL for the GMS dashboard'), - GMS_PLAYGROUND_ROUTE: Joi.string() + GMS_USER_SEARCH_FRONTEND_ROUTE: Joi.string() .pattern(/^[\w_-]*$/) - .default('usersearch-playground') + .default('user-search') .description( - 'gms frontend playground route, playground for standalone playground, usersearch-playground for old', + 'gms frontend playground route, user-search for standalone playground, usersearch-playground for old', ), GMS_WEBHOOK_SECRET: Joi.string() diff --git a/backend/src/graphql/resolver/util/authenticateGmsUserPlayground.ts b/backend/src/graphql/resolver/util/authenticateGmsUserPlayground.ts index 41307eb55..b3acc8ae0 100644 --- a/backend/src/graphql/resolver/util/authenticateGmsUserPlayground.ts +++ b/backend/src/graphql/resolver/util/authenticateGmsUserPlayground.ts @@ -14,7 +14,7 @@ export async function authenticateGmsUserPlayground( const result = new GmsUserAuthenticationResult() const dashboardUrl = ensureUrlEndsWithSlash(CONFIG.GMS_DASHBOARD_URL) - result.url = dashboardUrl.concat(CONFIG.GMS_PLAYGROUND_ROUTE) + result.url = dashboardUrl.concat(CONFIG.GMS_USER_SEARCH_FRONTEND_ROUTE) result.token = await verifyAuthToken(dbUser.communityUuid, token) logger.info('GmsUserAuthenticationResult:', result) return result From b4d65827d52d54893a05a77249539e800d12f4c6 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Sun, 13 Apr 2025 13:57:40 +0200 Subject: [PATCH 03/57] more infos, show minimal progress indicator for export gms user --- backend/src/apis/gms/ExportUsers.ts | 3 +++ backend/src/config/schema.ts | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/backend/src/apis/gms/ExportUsers.ts b/backend/src/apis/gms/ExportUsers.ts index 4b8a303f6..43d8200c6 100644 --- a/backend/src/apis/gms/ExportUsers.ts +++ b/backend/src/apis/gms/ExportUsers.ts @@ -49,6 +49,7 @@ const run = async () => { .getRawMany() logger.debug('userIds:', userIds) + let alreadyUpdatedUserCount = 0 for (const idStr of userIds) { logger.debug('Id:', idStr.id) const user = await DbUser.findOne({ @@ -78,6 +79,8 @@ const run = async () => { logger.debug('GMS-Publishing not allowed by user settings:', user) } } + alreadyUpdatedUserCount++ + process.stdout.write(`updated user: ${alreadyUpdatedUserCount}/${userIds.length}\r`) } logger.info('##gms## publishing all local users successful...') diff --git a/backend/src/config/schema.ts b/backend/src/config/schema.ts index bf7c6c810..e5a2f786e 100644 --- a/backend/src/config/schema.ts +++ b/backend/src/config/schema.ts @@ -281,9 +281,10 @@ export const schema = Joi.object({ GMS_USER_SEARCH_FRONTEND_ROUTE: Joi.string() .pattern(/^[\w_-]*$/) + .allow('') .default('user-search') .description( - 'gms frontend playground route, user-search for standalone playground, usersearch-playground for old', + 'gms frontend playground route, user-search for standalone playground, usersearch-playground for old, empty for testing local', ), GMS_WEBHOOK_SECRET: Joi.string() From a3f984b61345778207c1bf8565702adea62b5819 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Mon, 14 Apr 2025 17:31:13 +0200 Subject: [PATCH 04/57] reduce coverage --- backend/jest.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/jest.config.js b/backend/jest.config.js index d4dc80ff1..1de74762b 100644 --- a/backend/jest.config.js +++ b/backend/jest.config.js @@ -7,7 +7,7 @@ module.exports = { collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**', '!src/seeds/**', '!build/**'], coverageThreshold: { global: { - lines: 78, + lines: 77, }, }, setupFiles: ['/test/testSetup.ts'], From aa9e8468d554378f42c82e6128bc230a28907aa7 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Mon, 14 Apr 2025 17:54:27 +0200 Subject: [PATCH 05/57] update user location in local store on save to update button on overview --- .../UserSettings/UserGmsLocationCapturing.vue | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/frontend/src/components/UserSettings/UserGmsLocationCapturing.vue b/frontend/src/components/UserSettings/UserGmsLocationCapturing.vue index 8620ad318..2934afedb 100644 --- a/frontend/src/components/UserSettings/UserGmsLocationCapturing.vue +++ b/frontend/src/components/UserSettings/UserGmsLocationCapturing.vue @@ -33,8 +33,10 @@ import UserLocationMap from '@/components/UserSettings/UserLocationMap' import { BButton, BModal } from 'bootstrap-vue-next' import { userLocationQuery } from '@/graphql/queries' import CONFIG from '@/config' +import { useStore } from 'vuex' const { t } = useI18n() +const store = useStore() const { mutate: updateUserInfo } = useMutation(updateUserInfos) const { onResult, onError } = useQuery(userLocationQuery, {}, { fetchPolicy: 'network-only' }) const { toastSuccess, toastError } = useAppToast() @@ -73,14 +75,11 @@ const saveUserLocation = async () => { try { const loc = { longitude: capturedLocation.value.lng, latitude: capturedLocation.value.lat } - await updateUserInfo({ - gmsLocation: { - longitude: capturedLocation.value.lng, - latitude: capturedLocation.value.lat, - }, - }) + await updateUserInfo({ gmsLocation: loc }) toastSuccess(t('settings.GMS.location.updateSuccess')) userLocation.value = capturedLocation.value + // update in local storage to update button on overview + store.commit('userLocation', loc) isModalOpen.value = false } catch (error) { toastError(error.message) From 35f28dc0609e790c55e996da0df637b69184b83e Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Wed, 16 Apr 2025 10:06:06 +0200 Subject: [PATCH 06/57] update public name logic, test and usage, distinguish between unique username and publicName --- .../ContributionMessagesList.vue | 4 +- .../graphql/adminListContributions.graphql | 3 +- backend/src/apis/gms/model/GmsUser.ts | 6 +- backend/src/apis/humhub/model/AbstractUser.ts | 9 -- backend/src/apis/humhub/model/Account.ts | 2 +- backend/src/data/PublishName.logic.test.ts | 88 ++++++++++++++++--- backend/src/data/PublishName.logic.ts | 41 +++++---- backend/src/graphql/model/User.ts | 9 +- backend/src/graphql/resolver/UserResolver.ts | 5 +- .../util/compareGmsRelevantUserSettings.ts | 3 +- database/logging/UserLogging.view.ts | 2 +- 11 files changed, 123 insertions(+), 49 deletions(-) diff --git a/admin/src/components/ContributionMessages/ContributionMessagesList.vue b/admin/src/components/ContributionMessages/ContributionMessagesList.vue index 8f06d841e..837f2ade1 100644 --- a/admin/src/components/ContributionMessages/ContributionMessagesList.vue +++ b/admin/src/components/ContributionMessages/ContributionMessagesList.vue @@ -12,7 +12,7 @@ {{ $t('filter.byEmail') }}   - {{ contribution.user.humhubUsername }} + {{ contribution.user.publicName }}   { if (url.endsWith('/')) { url = url.slice(0, -1) } - return `${url}/u/${props.contribution.humhubUsername}` + return `${url}/u/${props.contribution.user.uniqueUsername}` }) const messages = ref([]) diff --git a/admin/src/graphql/adminListContributions.graphql b/admin/src/graphql/adminListContributions.graphql index cb696a51b..f98c9083f 100644 --- a/admin/src/graphql/adminListContributions.graphql +++ b/admin/src/graphql/adminListContributions.graphql @@ -13,7 +13,8 @@ query adminListContributions( email } ...UserCommonFields - humhubUsername + publicName + uniqueUsername createdAt } amount diff --git a/backend/src/apis/gms/model/GmsUser.ts b/backend/src/apis/gms/model/GmsUser.ts index 7dc81432f..92c2b67a6 100644 --- a/backend/src/apis/gms/model/GmsUser.ts +++ b/backend/src/apis/gms/model/GmsUser.ts @@ -15,11 +15,11 @@ export class GmsUser { this.email = this.getGmsEmail(user) this.countryCode = this.getGmsCountryCode(user) this.mobile = this.getGmsPhone(user) - const fn = pnLogic.getFirstName(user.gmsPublishName) + const fn = pnLogic.getFirstName(user.gmsPublishName as PublishNameType) this.firstName = fn !== '' ? fn : null // getGmsFirstName(user) - const ln = pnLogic.getLastName(user.gmsPublishName) + const ln = pnLogic.getLastName(user.gmsPublishName as PublishNameType) this.lastName = ln !== '' ? ln : null // getGmsLastName(user) - this.alias = this.getGmsAlias(user) + this.alias = pnLogic.getPublicName(user.gmsPublishName as PublishNameType) this.type = user.gmsPublishLocation // GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM this.location = user.location } diff --git a/backend/src/apis/humhub/model/AbstractUser.ts b/backend/src/apis/humhub/model/AbstractUser.ts index 8f876f053..abcf9dcda 100644 --- a/backend/src/apis/humhub/model/AbstractUser.ts +++ b/backend/src/apis/humhub/model/AbstractUser.ts @@ -1,8 +1,5 @@ import { User } from '@entity/User' -import { PublishNameLogic } from '@/data/PublishName.logic' -import { PublishNameType } from '@/graphql/enum/PublishNameType' - import { Account } from './Account' import { Profile } from './Profile' @@ -10,12 +7,6 @@ export abstract class AbstractUser { public constructor(user: User) { this.account = new Account(user) this.profile = new Profile(user) - // temp fix for prevent double usernames in humhub, if the username ist created from initials - const publishNameLogic = new PublishNameLogic(user) - if (publishNameLogic.isUsernameFromInitials(user.humhubPublishName as PublishNameType)) { - this.profile.firstname = this.account.username - this.account.username = user.gradidoID - } } account: Account diff --git a/backend/src/apis/humhub/model/Account.ts b/backend/src/apis/humhub/model/Account.ts index 1eb3e37be..1ccc911ad 100644 --- a/backend/src/apis/humhub/model/Account.ts +++ b/backend/src/apis/humhub/model/Account.ts @@ -8,7 +8,7 @@ import { PublishNameType } from '@/graphql/enum/PublishNameType' export class Account { public constructor(user: User) { const publishNameLogic = new PublishNameLogic(user) - this.username = publishNameLogic.getUsername(user.humhubPublishName as PublishNameType) + this.username = publishNameLogic.getUniqueUsername(user.humhubPublishName as PublishNameType) this.email = user.emailContact.email this.language = convertGradidoLanguageToHumhub(user.language) this.status = 1 diff --git a/backend/src/data/PublishName.logic.test.ts b/backend/src/data/PublishName.logic.test.ts index 280836c5e..6356444f0 100644 --- a/backend/src/data/PublishName.logic.test.ts +++ b/backend/src/data/PublishName.logic.test.ts @@ -1,23 +1,87 @@ import { User } from '@entity/User' +import { v4 as uuidv4 } from 'uuid' import { PublishNameType } from '@/graphql/enum/PublishNameType' import { PublishNameLogic } from './PublishName.logic' +const gradidoUuid = uuidv4() +let user: User +let logic: PublishNameLogic + +/* +export enum PublishNameType { + PUBLISH_NAME_ALIAS_OR_INITALS = 0, + PUBLISH_NAME_INITIALS = 1, + PUBLISH_NAME_FIRST = 2, + PUBLISH_NAME_FIRST_INITIAL = 3, + PUBLISH_NAME_FULL = 4, +} +*/ + describe('test publish name logic', () => { - describe('test username', () => { - it('alias or initials with alias set', () => { - const user = new User() - user.alias = 'alias' - const logic = new PublishNameLogic(user) - expect(logic.getUsername(PublishNameType.PUBLISH_NAME_ALIAS_OR_INITALS)).toBe(user.alias) + beforeEach(() => { + user = new User() + user.alias = 'alias' + user.firstName = 'John' + user.lastName = 'Smith' + user.gradidoID = gradidoUuid + logic = new PublishNameLogic(user) + }) + describe('test unique username', () => { + it('for alias or initials with alias set', () => { + expect(logic.getUniqueUsername(PublishNameType.PUBLISH_NAME_ALIAS_OR_INITALS)).toBe('alias') }) - it('alias or initials with empty alias', () => { - const user = new User() - user.firstName = 'John' - user.lastName = 'Smith' - const logic = new PublishNameLogic(user) - expect(logic.getUsername(PublishNameType.PUBLISH_NAME_ALIAS_OR_INITALS)).toBe('JoSm') + it('for alias or initials with empty alias', () => { + user.alias = '' + expect(logic.getUniqueUsername(PublishNameType.PUBLISH_NAME_ALIAS_OR_INITALS)).toBe( + gradidoUuid, + ) + }) + it('for publish name initials', () => { + expect(logic.getUniqueUsername(PublishNameType.PUBLISH_NAME_INITIALS)).toBe(gradidoUuid) + }) + it('for publish name first', () => { + expect(logic.getUniqueUsername(PublishNameType.PUBLISH_NAME_FIRST)).toBe(gradidoUuid) + }) + it('for publish name first initial', () => { + expect(logic.getUniqueUsername(PublishNameType.PUBLISH_NAME_FIRST_INITIAL)).toBe(gradidoUuid) + }) + it('for publish name full', () => { + expect(logic.getUniqueUsername(PublishNameType.PUBLISH_NAME_FULL)).toBe(gradidoUuid) + }) + }) + + describe('test public name', () => { + it('for alias or initials with alias set', () => { + expect(logic.getPublicName(PublishNameType.PUBLISH_NAME_ALIAS_OR_INITALS)).toBe('alias') + }) + it('for alias or initials with empty alias', () => { + user.alias = '' + expect(logic.getPublicName(PublishNameType.PUBLISH_NAME_ALIAS_OR_INITALS)).toBe('JoSm') + }) + it('for alias or initials with empty alias and lower case written names', () => { + user.alias = '' + user.firstName = 'john' + user.lastName = 'smith' + expect(logic.getPublicName(PublishNameType.PUBLISH_NAME_ALIAS_OR_INITALS)).toBe('JoSm') + }) + it('for publish name initials', () => { + expect(logic.getPublicName(PublishNameType.PUBLISH_NAME_INITIALS)).toBe('JoSm') + }) + it('for publish name initials with lower case written names', () => { + user.firstName = 'john' + user.lastName = 'smith' + expect(logic.getPublicName(PublishNameType.PUBLISH_NAME_INITIALS)).toBe('JoSm') + }) + it('for publish name first', () => { + expect(logic.getPublicName(PublishNameType.PUBLISH_NAME_FIRST)).toBe('John') + }) + it('for publish name first initial', () => { + expect(logic.getPublicName(PublishNameType.PUBLISH_NAME_FIRST_INITIAL)).toBe('John S') + }) + it('for publish name full', () => { + expect(logic.getPublicName(PublishNameType.PUBLISH_NAME_FULL)).toBe('John Smith') }) }) }) diff --git a/backend/src/data/PublishName.logic.ts b/backend/src/data/PublishName.logic.ts index 4e092cfe4..70fab21cd 100644 --- a/backend/src/data/PublishName.logic.ts +++ b/backend/src/data/PublishName.logic.ts @@ -59,23 +59,34 @@ export class PublishNameLogic { } /** - * get username from user.alias for PUBLISH_NAME_ALIAS_OR_INITALS and if user has alias - * get first name first two characters and last name first two characters for PUBLISH_NAME_ALIAS_OR_INITALS - * if no alias or PUBLISH_NAME_INITIALS + * get unique username * @param publishNameType - * @returns user.alias for publishNameType = PUBLISH_NAME_ALIAS_OR_INITALS and user has alias - * else return user.firstName[0,2] + user.lastName[0,2] for publishNameType = [PUBLISH_NAME_ALIAS_OR_INITALS, PUBLISH_NAME_INITIALS] + * @return when alias if exist and publishNameType = [PUBLISH_NAME_ALIAS_OR_INITALS, PUBLISH_NAME_INITIALS] + * return alias + * else return gradido id */ - public getUsername(publishNameType: PublishNameType): string { - if (this.isUsernameFromInitials(publishNameType)) { - return ( - this.firstUpperCaseSecondLowerCase(this.filterOutInvalidChar(this.user.firstName)) + - this.firstUpperCaseSecondLowerCase(this.filterOutInvalidChar(this.user.lastName)) - ) - } else if (this.isUsernameFromAlias(publishNameType)) { - return this.filterOutInvalidChar(this.user.alias) - } - return this.user.gradidoID + public getUniqueUsername(publishNameType: PublishNameType): string { + return this.isUsernameFromAlias(publishNameType) + ? this.filterOutInvalidChar(this.user.alias) + : this.user.gradidoID + } + + /** + * get public name based on publishNameType: PublishNameType value + * @param publishNameType: PublishNameType + * @return alias if exist and type = PUBLISH_NAME_ALIAS_OR_INITALS + * initials if type = PUBLISH_NAME_INITIALS + * full first name if type = PUBLISH_NAME_FIRST + * full first name and last name initial if type = PUBLISH_NAME_FIRST_INITIAL + * full first name and full last name if type = PUBLISH_NAME_FULL + */ + public getPublicName(publishNameType: PublishNameType): string { + return this.isUsernameFromAlias(publishNameType) + ? this.filterOutInvalidChar(this.user.alias) + : this.isUsernameFromInitials(publishNameType) + ? this.firstUpperCaseSecondLowerCase(this.user.firstName) + + this.firstUpperCaseSecondLowerCase(this.user.lastName) + : (this.getFirstName(publishNameType) + ' ' + this.getLastName(publishNameType)).trim() } public isUsernameFromInitials(publishNameType: PublishNameType): boolean { diff --git a/backend/src/graphql/model/User.ts b/backend/src/graphql/model/User.ts index b665bde98..970ff28b3 100644 --- a/backend/src/graphql/model/User.ts +++ b/backend/src/graphql/model/User.ts @@ -26,7 +26,9 @@ export class User { this.alias = user.alias const publishNameLogic = new PublishNameLogic(user) - this.humhubUsername = publishNameLogic.getUsername(user.humhubPublishName as PublishNameType) + const publishNameType = user.humhubPublishName as PublishNameType + this.publicName = publishNameLogic.getPublicName(publishNameType) + this.uniqueUsername = publishNameLogic.getUniqueUsername(publishNameType) if (user.emailContact) { this.emailChecked = user.emailContact.emailChecked @@ -71,7 +73,10 @@ export class User { alias: string | null @Field(() => String, { nullable: true }) - humhubUsername: string | null + publicName: string | null + + @Field(() => String, { nullable: true }) + uniqueUsername: string | null @Field(() => String, { nullable: true }) firstName: string | null diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 5e8843650..bbac91597 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -8,6 +8,7 @@ import { ProjectBranding } from '@entity/ProjectBranding' import { TransactionLink as DbTransactionLink } from '@entity/TransactionLink' import { User as DbUser } from '@entity/User' import { UserContact as DbUserContact } from '@entity/UserContact' +import { UserLoggingView } from '@logging/UserLogging.view' import { GraphQLResolveInfo } from 'graphql' import i18n from 'i18n' import { @@ -738,7 +739,7 @@ export class UserResolver { }) await queryRunner.commitTransaction() - logger.debug('writing User data successful...', user) + logger.debug('writing User data successful...', new UserLoggingView(user)) } catch (e) { await queryRunner.rollbackTransaction() throw new LogError('Error on writing updated user data', e) @@ -832,7 +833,7 @@ export class UserResolver { throw new LogError('cannot create humhub client') } const userNameLogic = new PublishNameLogic(dbUser) - const username = userNameLogic.getUsername(dbUser.humhubPublishName as PublishNameType) + const username = userNameLogic.getUniqueUsername(dbUser.humhubPublishName as PublishNameType) let humhubUser = await humhubClient.userByUsername(username) if (!humhubUser) { humhubUser = await humhubClient.userByEmail(dbUser.emailContact.email) diff --git a/backend/src/graphql/resolver/util/compareGmsRelevantUserSettings.ts b/backend/src/graphql/resolver/util/compareGmsRelevantUserSettings.ts index 2144c9833..edfba7457 100644 --- a/backend/src/graphql/resolver/util/compareGmsRelevantUserSettings.ts +++ b/backend/src/graphql/resolver/util/compareGmsRelevantUserSettings.ts @@ -1,5 +1,6 @@ import { Point } from '@dbTools/typeorm' import { User as DbUser } from '@entity/User' +import { UserLoggingView } from '@logging/UserLogging.view' import { UpdateUserInfosArgs } from '@/graphql/arg/UpdateUserInfosArgs' import { GmsPublishLocationType } from '@/graphql/enum/GmsPublishLocationType' @@ -16,7 +17,7 @@ export function compareGmsRelevantUserSettings( if (!orgUser) { throw new LogError('comparison without any user is impossible') } - logger.debug('compareGmsRelevantUserSettings:', orgUser, updateUserInfosArgs) + logger.debug('compareGmsRelevantUserSettings:', new UserLoggingView(orgUser), updateUserInfosArgs) // nach GMS updaten, wenn alias gesetzt wird oder ist und PublishLevel die alias-Übermittlung erlaubt if ( updateUserInfosArgs.alias && diff --git a/database/logging/UserLogging.view.ts b/database/logging/UserLogging.view.ts index 19b3ca911..839b0ad8b 100644 --- a/database/logging/UserLogging.view.ts +++ b/database/logging/UserLogging.view.ts @@ -32,7 +32,7 @@ export class UserLoggingView extends AbstractLoggingView { lastName: this.self.lastName?.substring(0, 3) + '...', createdAt: this.dateToString(this.self.createdAt), deletedAt: this.dateToString(this.self.deletedAt), - passwordEncryptionType: this.self.passwordEncryptionType as PasswordEncryptionType, + passwordEncryptionType: PasswordEncryptionType[this.self.passwordEncryptionType], language: this.self.language, hideAmountGDD: this.self.hideAmountGDD, hideAmountGDT: this.self.hideAmountGDT, From b894544a6b725577486f3d8782cea3f470fed781 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Wed, 16 Apr 2025 10:13:48 +0200 Subject: [PATCH 07/57] change function and variable name --- .../ContributionMessagesList.spec.js | 3 ++- .../ContributionMessagesList.vue | 2 +- admin/src/graphql/adminListContributions.graphql | 2 +- backend/src/apis/humhub/model/Account.ts | 2 +- backend/src/data/PublishName.logic.test.ts | 14 +++++++------- backend/src/data/PublishName.logic.ts | 2 +- backend/src/graphql/model/User.ts | 4 ++-- backend/src/graphql/resolver/UserResolver.ts | 2 +- 8 files changed, 16 insertions(+), 15 deletions(-) diff --git a/admin/src/components/ContributionMessages/ContributionMessagesList.spec.js b/admin/src/components/ContributionMessages/ContributionMessagesList.spec.js index 46773c7d8..a47c78601 100644 --- a/admin/src/components/ContributionMessages/ContributionMessagesList.spec.js +++ b/admin/src/components/ContributionMessages/ContributionMessagesList.spec.js @@ -71,7 +71,8 @@ const defaultData = { const defaultUser = { firstName: 'Peter', lastName: 'Lustig', - humhubUsername: 'peter.lustig', + uniqueUsername: 'peter.lustig', + publicName: 'PeLu', createdAt: new Date().toString(), emailContact: { email: 'peter.lustig@example.com', diff --git a/admin/src/components/ContributionMessages/ContributionMessagesList.vue b/admin/src/components/ContributionMessages/ContributionMessagesList.vue index 837f2ade1..09b5a69ac 100644 --- a/admin/src/components/ContributionMessages/ContributionMessagesList.vue +++ b/admin/src/components/ContributionMessages/ContributionMessagesList.vue @@ -103,7 +103,7 @@ const humhubProfileLink = computed(() => { if (url.endsWith('/')) { url = url.slice(0, -1) } - return `${url}/u/${props.contribution.user.uniqueUsername}` + return `${url}/u/${props.contribution.user.userIdentifier}` }) const messages = ref([]) diff --git a/admin/src/graphql/adminListContributions.graphql b/admin/src/graphql/adminListContributions.graphql index f98c9083f..dde9750af 100644 --- a/admin/src/graphql/adminListContributions.graphql +++ b/admin/src/graphql/adminListContributions.graphql @@ -14,7 +14,7 @@ query adminListContributions( } ...UserCommonFields publicName - uniqueUsername + userIdentifier createdAt } amount diff --git a/backend/src/apis/humhub/model/Account.ts b/backend/src/apis/humhub/model/Account.ts index 1ccc911ad..934b8931e 100644 --- a/backend/src/apis/humhub/model/Account.ts +++ b/backend/src/apis/humhub/model/Account.ts @@ -8,7 +8,7 @@ import { PublishNameType } from '@/graphql/enum/PublishNameType' export class Account { public constructor(user: User) { const publishNameLogic = new PublishNameLogic(user) - this.username = publishNameLogic.getUniqueUsername(user.humhubPublishName as PublishNameType) + this.username = publishNameLogic.getUserIdentifier(user.humhubPublishName as PublishNameType) this.email = user.emailContact.email this.language = convertGradidoLanguageToHumhub(user.language) this.status = 1 diff --git a/backend/src/data/PublishName.logic.test.ts b/backend/src/data/PublishName.logic.test.ts index 6356444f0..6db3a996c 100644 --- a/backend/src/data/PublishName.logic.test.ts +++ b/backend/src/data/PublishName.logic.test.ts @@ -28,27 +28,27 @@ describe('test publish name logic', () => { user.gradidoID = gradidoUuid logic = new PublishNameLogic(user) }) - describe('test unique username', () => { + describe('test user identifier ', () => { it('for alias or initials with alias set', () => { - expect(logic.getUniqueUsername(PublishNameType.PUBLISH_NAME_ALIAS_OR_INITALS)).toBe('alias') + expect(logic.getUserIdentifier(PublishNameType.PUBLISH_NAME_ALIAS_OR_INITALS)).toBe('alias') }) it('for alias or initials with empty alias', () => { user.alias = '' - expect(logic.getUniqueUsername(PublishNameType.PUBLISH_NAME_ALIAS_OR_INITALS)).toBe( + expect(logic.getUserIdentifier(PublishNameType.PUBLISH_NAME_ALIAS_OR_INITALS)).toBe( gradidoUuid, ) }) it('for publish name initials', () => { - expect(logic.getUniqueUsername(PublishNameType.PUBLISH_NAME_INITIALS)).toBe(gradidoUuid) + expect(logic.getUserIdentifier(PublishNameType.PUBLISH_NAME_INITIALS)).toBe(gradidoUuid) }) it('for publish name first', () => { - expect(logic.getUniqueUsername(PublishNameType.PUBLISH_NAME_FIRST)).toBe(gradidoUuid) + expect(logic.getUserIdentifier(PublishNameType.PUBLISH_NAME_FIRST)).toBe(gradidoUuid) }) it('for publish name first initial', () => { - expect(logic.getUniqueUsername(PublishNameType.PUBLISH_NAME_FIRST_INITIAL)).toBe(gradidoUuid) + expect(logic.getUserIdentifier(PublishNameType.PUBLISH_NAME_FIRST_INITIAL)).toBe(gradidoUuid) }) it('for publish name full', () => { - expect(logic.getUniqueUsername(PublishNameType.PUBLISH_NAME_FULL)).toBe(gradidoUuid) + expect(logic.getUserIdentifier(PublishNameType.PUBLISH_NAME_FULL)).toBe(gradidoUuid) }) }) diff --git a/backend/src/data/PublishName.logic.ts b/backend/src/data/PublishName.logic.ts index 70fab21cd..c7935b289 100644 --- a/backend/src/data/PublishName.logic.ts +++ b/backend/src/data/PublishName.logic.ts @@ -65,7 +65,7 @@ export class PublishNameLogic { * return alias * else return gradido id */ - public getUniqueUsername(publishNameType: PublishNameType): string { + public getUserIdentifier(publishNameType: PublishNameType): string { return this.isUsernameFromAlias(publishNameType) ? this.filterOutInvalidChar(this.user.alias) : this.user.gradidoID diff --git a/backend/src/graphql/model/User.ts b/backend/src/graphql/model/User.ts index 970ff28b3..e7a8288f0 100644 --- a/backend/src/graphql/model/User.ts +++ b/backend/src/graphql/model/User.ts @@ -28,7 +28,7 @@ export class User { const publishNameLogic = new PublishNameLogic(user) const publishNameType = user.humhubPublishName as PublishNameType this.publicName = publishNameLogic.getPublicName(publishNameType) - this.uniqueUsername = publishNameLogic.getUniqueUsername(publishNameType) + this.userIdentifier = publishNameLogic.getUserIdentifier(publishNameType) if (user.emailContact) { this.emailChecked = user.emailContact.emailChecked @@ -76,7 +76,7 @@ export class User { publicName: string | null @Field(() => String, { nullable: true }) - uniqueUsername: string | null + userIdentifier: string | null @Field(() => String, { nullable: true }) firstName: string | null diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index bbac91597..518ef9f46 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -833,7 +833,7 @@ export class UserResolver { throw new LogError('cannot create humhub client') } const userNameLogic = new PublishNameLogic(dbUser) - const username = userNameLogic.getUniqueUsername(dbUser.humhubPublishName as PublishNameType) + const username = userNameLogic.getUserIdentifier(dbUser.humhubPublishName as PublishNameType) let humhubUser = await humhubClient.userByUsername(username) if (!humhubUser) { humhubUser = await humhubClient.userByEmail(dbUser.emailContact.email) From 3690e627366baa6b342528e01988ec5561747d2b Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Wed, 16 Apr 2025 12:21:51 +0200 Subject: [PATCH 08/57] fix Mitgliedssuche instead of Mitgliedsuche --- frontend/src/locales/de.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/locales/de.json b/frontend/src/locales/de.json index 7659b21eb..3736f35ce 100644 --- a/frontend/src/locales/de.json +++ b/frontend/src/locales/de.json @@ -43,7 +43,7 @@ "card-user-search": { "headline": "Geografische Mitgliedssuche (experimentell)", "allowed": { - "button": "Öffne Mitgliedsuche...", + "button": "Öffne Mitgliedssuche...", "disabled-button": "GMS offline...", "text": "Finde Mitglieder aller Communities auf einer Landkarte." }, @@ -334,7 +334,7 @@ "warningText": "Bist du noch da?" }, "settings": { - "community": "Kreise & Mitgliedsuche", + "community": "Kreise & Mitgliedssuche", "emailInfo": "Kann aktuell noch nicht geändert werden.", "GMS": { "disabled": "Daten werden nicht nach GMS exportiert", From 58b006193f346dfc571ac4d81b31c9cefce3fdc2 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Wed, 16 Apr 2025 13:10:33 +0200 Subject: [PATCH 09/57] fix update to exact gms location type --- .../src/graphql/resolver/util/compareGmsRelevantUserSettings.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/util/compareGmsRelevantUserSettings.ts b/backend/src/graphql/resolver/util/compareGmsRelevantUserSettings.ts index edfba7457..609a8d487 100644 --- a/backend/src/graphql/resolver/util/compareGmsRelevantUserSettings.ts +++ b/backend/src/graphql/resolver/util/compareGmsRelevantUserSettings.ts @@ -45,7 +45,7 @@ export function compareGmsRelevantUserSettings( return true } if ( - updateUserInfosArgs.gmsPublishLocation && + updateUserInfosArgs.gmsPublishLocation !== undefined && (orgUser.gmsPublishLocation as GmsPublishLocationType) !== updateUserInfosArgs.gmsPublishLocation ) { From c0ec697f647442c5dcdd462b574cb49178c6eb3c Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Thu, 17 Apr 2025 08:46:11 +0200 Subject: [PATCH 10/57] remove build-css from serve, add info link for gms --- frontend/package.json | 1 - frontend/src/components/Overview/CardUserSearch.vue | 3 +++ frontend/src/locales/de.json | 1 + frontend/src/locales/en.json | 1 + 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/frontend/package.json b/frontend/package.json index 24922b0f4..20bfb8bdd 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -7,7 +7,6 @@ "dev": "concurrently \"yarn watch-scss\" \"vite\"", "prebuild": "yarn compile-scss", "build": "vite build", - "preserve": "yarn compile-scss", "serve": "vite preview", "postbuild": "find build -type f -regex '.*\\.\\(html\\|js\\|css\\|svg\\|json\\)' -exec gzip -9 -k {} +", "analyse-bundle": "yarn build && webpack-bundle-analyzer build/webpack.stats.json", diff --git a/frontend/src/components/Overview/CardUserSearch.vue b/frontend/src/components/Overview/CardUserSearch.vue index 2632c123d..c7d92ba55 100644 --- a/frontend/src/components/Overview/CardUserSearch.vue +++ b/frontend/src/components/Overview/CardUserSearch.vue @@ -20,6 +20,9 @@
+ + {{ $t('info') }} +
diff --git a/frontend/src/locales/de.json b/frontend/src/locales/de.json index 3736f35ce..2407653a7 100644 --- a/frontend/src/locales/de.json +++ b/frontend/src/locales/de.json @@ -276,6 +276,7 @@ "recruited-member": "Eingeladenes Mitglied" }, "h": "h", + "info": "Info", "language": "Sprache", "link-load": "den letzten Link nachladen | die letzten {n} Links nachladen", "link-load-more": "weitere {n} Links nachladen", diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index a8b47b05c..77c18856d 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -276,6 +276,7 @@ "recruited-member": "Invited member" }, "h": "h", + "info": "Info", "language": "Language", "link-load": "Load the last link | Load the last {n} links", "link-load-more": "Load more {n} links", From 26ed7e5a196bbd5f1fb7a823d1a4438f942fb0f4 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Thu, 17 Apr 2025 13:52:27 +0200 Subject: [PATCH 11/57] add time for confirmedAt --- admin/src/pages/CreationConfirm.vue | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/admin/src/pages/CreationConfirm.vue b/admin/src/pages/CreationConfirm.vue index 006c463fb..59fc2e490 100644 --- a/admin/src/pages/CreationConfirm.vue +++ b/admin/src/pages/CreationConfirm.vue @@ -135,6 +135,8 @@ const noHashtag = ref(null) const hideResubmissionModel = ref(true) const formatDateOrDash = (value) => (value ? new Date(value).toLocaleDateString() : '—') +const formatDateTimeOrDash = (value) => (value ? new Date(value).toLocaleString() : '—') + const baseFields = { firstName: { key: 'user.firstName', label: t('firstname'), class: 'no-select' }, lastName: { key: 'user.lastName', label: t('lastname'), class: 'no-select' }, @@ -156,7 +158,7 @@ const baseFields = { key: 'confirmedAt', label: t('contributions.confirms'), class: 'no-select', - formatter: formatDateOrDash, + formatter: formatDateTimeOrDash, }, confirmedBy: { key: 'confirmedBy', label: t('moderator.moderator'), class: 'no-select' }, } From a317ade9fb2b2638a80356636cb0d91f7980472f Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Thu, 17 Apr 2025 16:55:58 +0200 Subject: [PATCH 12/57] show amount also on confirmed page --- admin/src/pages/CreationConfirm.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/admin/src/pages/CreationConfirm.vue b/admin/src/pages/CreationConfirm.vue index 59fc2e490..9a70c188d 100644 --- a/admin/src/pages/CreationConfirm.vue +++ b/admin/src/pages/CreationConfirm.vue @@ -135,7 +135,6 @@ const noHashtag = ref(null) const hideResubmissionModel = ref(true) const formatDateOrDash = (value) => (value ? new Date(value).toLocaleDateString() : '—') -const formatDateTimeOrDash = (value) => (value ? new Date(value).toLocaleString() : '—') const baseFields = { firstName: { key: 'user.firstName', label: t('firstname'), class: 'no-select' }, @@ -158,7 +157,7 @@ const baseFields = { key: 'confirmedAt', label: t('contributions.confirms'), class: 'no-select', - formatter: formatDateTimeOrDash, + formatter: formatDateOrDash, }, confirmedBy: { key: 'confirmedBy', label: t('moderator.moderator'), class: 'no-select' }, } @@ -183,6 +182,7 @@ const fields = computed( [ baseFields.firstName, baseFields.lastName, + baseFields.amount, baseFields.memo, baseFields.contributionDate, baseFields.createdAt, From b0444420a9686cb5046fecf0e695ab9c86409385 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Thu, 17 Apr 2025 17:52:59 +0200 Subject: [PATCH 13/57] make sure that changing data for gms automatic disable random location type --- backend/src/apis/gms/model/GmsUser.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/backend/src/apis/gms/model/GmsUser.ts b/backend/src/apis/gms/model/GmsUser.ts index 92c2b67a6..6fbe13cad 100644 --- a/backend/src/apis/gms/model/GmsUser.ts +++ b/backend/src/apis/gms/model/GmsUser.ts @@ -2,6 +2,7 @@ import { User as dbUser } from '@entity/User' import { PublishNameLogic } from '@/data/PublishName.logic' // import { GmsPublishLocationType } from '@/graphql/enum/GmsPublishLocationType' +import { GmsPublishLocationType } from '@/graphql/enum/GmsPublishLocationType' import { GmsPublishPhoneType } from '@/graphql/enum/GmsPublishPhoneType' import { PublishNameType } from '@/graphql/enum/PublishNameType' @@ -21,6 +22,9 @@ export class GmsUser { this.lastName = ln !== '' ? ln : null // getGmsLastName(user) this.alias = pnLogic.getPublicName(user.gmsPublishName as PublishNameType) this.type = user.gmsPublishLocation // GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM + if ((this.type as GmsPublishLocationType) === GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM) { + this.type = GmsPublishLocationType.GMS_LOCATION_TYPE_APPROXIMATE + } this.location = user.location } From 6fdcb0a86d256c732af91975b3ae31d98158f7a9 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Thu, 17 Apr 2025 18:04:48 +0200 Subject: [PATCH 14/57] fix the fix --- backend/src/apis/gms/model/GmsUser.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/backend/src/apis/gms/model/GmsUser.ts b/backend/src/apis/gms/model/GmsUser.ts index 6fbe13cad..f97726772 100644 --- a/backend/src/apis/gms/model/GmsUser.ts +++ b/backend/src/apis/gms/model/GmsUser.ts @@ -22,10 +22,13 @@ export class GmsUser { this.lastName = ln !== '' ? ln : null // getGmsLastName(user) this.alias = pnLogic.getPublicName(user.gmsPublishName as PublishNameType) this.type = user.gmsPublishLocation // GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM + this.location = user.location if ((this.type as GmsPublishLocationType) === GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM) { this.type = GmsPublishLocationType.GMS_LOCATION_TYPE_APPROXIMATE } - this.location = user.location + if (!this.location) { + this.type = GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM + } } id: number From 9ff6470b47646f523004a832053c1530e76944d3 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Tue, 22 Apr 2025 10:10:16 +0200 Subject: [PATCH 15/57] make authenticateHumhubAutoLogin a mutation, auto register user on humhub if project was set and user don't exist on humhub --- backend/src/graphql/resolver/UserResolver.ts | 23 ++-- .../src/graphql/resolver/util/syncHumhub.ts | 13 ++- .../src/components/Overview/CardCircles.vue | 31 ++++-- frontend/src/graphql/mutations.js | 11 ++ frontend/src/graphql/queries.js | 11 -- frontend/src/pages/Circles.spec.js | 103 ------------------ frontend/src/pages/Circles.vue | 79 -------------- frontend/src/pages/Login.vue | 18 +-- 8 files changed, 67 insertions(+), 222 deletions(-) delete mode 100644 frontend/src/pages/Circles.spec.js delete mode 100644 frontend/src/pages/Circles.vue diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 518ef9f46..fea150338 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -47,13 +47,13 @@ import { UserContact } from '@model/UserContact' import { UserLocationResult } from '@model/UserLocationResult' import { HumHubClient } from '@/apis/humhub/HumHubClient' +import { Account as HumhubAccount } from '@/apis/humhub/model/Account' import { GetUser } from '@/apis/humhub/model/GetUser' import { PostUser } from '@/apis/humhub/model/PostUser' import { subscribe } from '@/apis/KlicktippController' import { encode } from '@/auth/JWT' import { RIGHTS } from '@/auth/RIGHTS' import { CONFIG } from '@/config' -import { PublishNameLogic } from '@/data/PublishName.logic' import { sendAccountActivationEmail, sendAccountMultiRegistrationEmail, @@ -75,7 +75,6 @@ import { EVENT_ADMIN_USER_DELETE, EVENT_ADMIN_USER_UNDELETE, } from '@/event/Events' -import { PublishNameType } from '@/graphql/enum/PublishNameType' import { isValidPassword } from '@/password/EncryptorUtils' import { encryptPassword, verifyPassword } from '@/password/PasswordEncryptor' import { Context, getUser, getClientTimezoneOffset } from '@/server/context' @@ -821,7 +820,7 @@ export class UserResolver { } @Authorized([RIGHTS.HUMHUB_AUTO_LOGIN]) - @Query(() => String) + @Mutation(() => String) async authenticateHumhubAutoLogin( @Ctx() context: Context, @Arg('project', () => String, { nullable: true }) project?: string | null, @@ -832,19 +831,23 @@ export class UserResolver { if (!humhubClient) { throw new LogError('cannot create humhub client') } - const userNameLogic = new PublishNameLogic(dbUser) - const username = userNameLogic.getUserIdentifier(dbUser.humhubPublishName as PublishNameType) - let humhubUser = await humhubClient.userByUsername(username) - if (!humhubUser) { - humhubUser = await humhubClient.userByEmail(dbUser.emailContact.email) + // should rarely happen, so we don't optimize for parallel processing + if (!dbUser.humhubAllowed && project) { + await ProjectBranding.findOneOrFail({ where: { alias: project } }) + dbUser.humhubAllowed = true + await dbUser.save() } + const humhubUserAccount = new HumhubAccount(dbUser) + const autoLoginUrlPromise = humhubClient.createAutoLoginUrl(humhubUserAccount.username, project) + const humhubUser = await syncHumhub(null, dbUser) if (!humhubUser) { - throw new LogError("user don't exist (any longer) on humhub") + throw new LogError("user don't exist (any longer) on humhub and couldn't be created") } if (humhubUser.account.status !== 1) { throw new LogError('user status is not 1', humhubUser.account.status) } - return await humhubClient.createAutoLoginUrl(humhubUser.account.username, project) + const autoLoginUrl = await autoLoginUrlPromise + return autoLoginUrl } @Authorized([RIGHTS.SEARCH_ADMIN_USERS]) diff --git a/backend/src/graphql/resolver/util/syncHumhub.ts b/backend/src/graphql/resolver/util/syncHumhub.ts index b8becd8a2..90500bbc5 100644 --- a/backend/src/graphql/resolver/util/syncHumhub.ts +++ b/backend/src/graphql/resolver/util/syncHumhub.ts @@ -3,7 +3,9 @@ import { User } from '@entity/User' import { HumHubClient } from '@/apis/humhub/HumHubClient' import { GetUser } from '@/apis/humhub/model/GetUser' import { ExecutedHumhubAction, syncUser } from '@/apis/humhub/syncUser' +import { PublishNameLogic } from '@/data/PublishName.logic' import { UpdateUserInfosArgs } from '@/graphql/arg/UpdateUserInfosArgs' +import { PublishNameType } from '@/graphql/enum/PublishNameType' import { backendLogger as logger } from '@/server/logger' /** @@ -16,7 +18,7 @@ export async function syncHumhub( updateUserInfosArg: UpdateUserInfosArgs | null, user: User, spaceId?: number | null, -): Promise { +): Promise { // check for humhub relevant changes if ( updateUserInfosArg && @@ -36,7 +38,9 @@ export async function syncHumhub( return } logger.debug('retrieve user from humhub') - let humhubUser = await humhubClient.userByUsername(user.alias ?? user.gradidoID) + const userNameLogic = new PublishNameLogic(user) + const username = userNameLogic.getUserIdentifier(user.humhubPublishName as PublishNameType) + let humhubUser = await humhubClient.userByUsername(username) if (!humhubUser) { humhubUser = await humhubClient.userByEmail(user.emailContact.email) } @@ -58,5 +62,8 @@ export async function syncHumhub( await humhubClient.addUserToSpace(humhubUser.id, spaceId) logger.debug(`user added to space ${spaceId}`) } - return user.id + if (result !== ExecutedHumhubAction.SKIP) { + return await humhubClient.userByUsername(username) + } + return humhubUser } diff --git a/frontend/src/components/Overview/CardCircles.vue b/frontend/src/components/Overview/CardCircles.vue index 7452042b5..dc5522448 100644 --- a/frontend/src/components/Overview/CardCircles.vue +++ b/frontend/src/components/Overview/CardCircles.vue @@ -32,10 +32,10 @@