diff --git a/.env b/.env index ccf4216f41a48afbe336fb0c7cdd11393819bcf1..4f355d47a8965728772de2e6e720b482a553e302 100644 --- a/.env +++ b/.env @@ -5,7 +5,7 @@ NODE_ENV="development" ZENKIT_API_KEY="kl71sotw-uSzlXbg6PaC6F41yTUeIPfOQhczzhZ9s" CLICKUP_PERSONAL_TOKEN="pk_48330716_DEXB42FRMG4924435NW8BQ6KB2O7OFXS" CLICKUP_PERSONAL_TOKEN_SPRINT_OVERVIEW="pk_10931364_AZ5ZPB7CKPG4L93OV0EQC5MYRDSFKYPK" -GITLAB_ACCESS_TOKEN="glpat-2n2UzKSvkN1pLQCUb89x" +GITLAB_ACCESS_TOKEN="glpat-bP-xdaaXsq4ot8uf_wuz" SMTP_FROM="Adornis Dev Mail Service <test@adornis.de>" SMTP_HOST="mail.adornis.de" SMTP_USER="test@adornis.de" diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 16abb2eb193ae4c49060fbad6d721ecc75f01e3c..38ebb83c46a2b09c721c326349896b7ddea99c1d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -8,8 +8,8 @@ variables: PRODUCTION_BRANCH: master # We dont use a test deployment (yet) but here are the envs to configure one. See https://code.adornis.de/docker/ci-templates/-/blob/master/git-flow-complete.yml?ref_type=heads for a description of all envs ENABLE_TEST_DEPLOYMENT: '' - TEST_TARGET: AdornisStaging - TEST_TARGET_HOST: ssh://adornis@staging.infra.adornis.de + TEST_TARGET: AdornisQA + TEST_TARGET_HOST: ssh://adornis@qa.infra.adornis.de TEST_INSTALL_CHROME_DEPS: '' TEST_EXTRA_APT_PACKAGES: '' TEST_COMPOSE_PROJECT: dashboard-testing @@ -17,13 +17,13 @@ variables: ENABLE_QA_DEPLOYMENT: 'false' # this is still required inspite of us disabling the qa deployment - QA_TARGET: AdornisStaging - QA_TARGET_HOST: 'ssh://adornis@staging.infra.adornis.de' + QA_TARGET: AdornisQA + QA_TARGET_HOST: 'ssh://adornis@qa.infra.adornis.de' QA_COMPOSE_PROJECT: dashboard-qa QA_COMPOSE_TEMPLATE: deploy/qa-compose.yml QA_ENVIRONMENT_URL: https://adornis-dashboard.qa.adornis.de - STAGING_TARGET: AdornisStaging - STAGING_TARGET_HOST: 'ssh://adornis@staging.infra.adornis.de' + STAGING_TARGET: AdornisStaging2 + STAGING_TARGET_HOST: 'ssh://adornis@staging2.infra.adornis.de' STAGING_COMPOSE_PROJECT: dashboard-staging STAGING_COMPOSE_TEMPLATE: deploy/staging-compose.yml STAGING_ENVIRONMENT_URL: https://adornis-dashboard.staging.adornis.de diff --git a/deploy/production-compose.yml b/deploy/production-compose.yml index 13c777938e0e8385454dff22daf2f45af70ff7d2..dacb0d7442f70b658e7eab29e89419de8e9ee574 100644 --- a/deploy/production-compose.yml +++ b/deploy/production-compose.yml @@ -17,7 +17,7 @@ services: - CLICKUP_PERSONAL_TOKEN=pk_48330716_DEXB42FRMG4924435NW8BQ6KB2O7OFXS - CLICKUP_PERSONAL_TOKEN_SPRINT_OVERVIEW=pk_10931364_AZ5ZPB7CKPG4L93OV0EQC5MYRDSFKYPK - PRIVATEKEY_FILE_PATH=/datadir/ - - GITLAB_ACCESS_TOKEN=CCACjsXDnFzbTkR-eghB + - GITLAB_ACCESS_TOKEN=glpat-bP-xdaaXsq4ot8uf_wuz - SMTP_FROM=Adornis Mailer - SMTP_HOST=mail.adornis.de - SMTP_USER=noreply@adornis.de diff --git a/deploy/qa-compose.yml b/deploy/qa-compose.yml index 8bed718dc09a039a0af8249c607cc1cf44ed859d..713d631aef33391c52e092a12f147dd42b3de553 100644 --- a/deploy/qa-compose.yml +++ b/deploy/qa-compose.yml @@ -16,7 +16,7 @@ services: - CLICKUP_PERSONAL_TOKEN=pk_48330716_DEXB42FRMG4924435NW8BQ6KB2O7OFXS - CLICKUP_PERSONAL_TOKEN_SPRINT_OVERVIEW=pk_10931364_AZ5ZPB7CKPG4L93OV0EQC5MYRDSFKYPK - PRIVATEKEY_FILE_PATH=/datadir/ - - GITLAB_ACCESS_TOKEN=CCACjsXDnFzbTkR-eghB + - GITLAB_ACCESS_TOKEN=glpat-bP-xdaaXsq4ot8uf_wuz - SMTP_FROM=Adornis Mailer - SMTP_HOST=mail.adornis.de - SMTP_USER=noreply@adornis.de diff --git a/deploy/staging-compose.yml b/deploy/staging-compose.yml index d6b14238dfef748a3f5f013eca48d75752d9da53..a057f0f6120bc3801085fd5877d0301c3d5f0541 100644 --- a/deploy/staging-compose.yml +++ b/deploy/staging-compose.yml @@ -16,7 +16,7 @@ services: - CLICKUP_PERSONAL_TOKEN=pk_48330716_DEXB42FRMG4924435NW8BQ6KB2O7OFXS - CLICKUP_PERSONAL_TOKEN_SPRINT_OVERVIEW=pk_10931364_AZ5ZPB7CKPG4L93OV0EQC5MYRDSFKYPK - PRIVATEKEY_FILE_PATH=/datadir/ - - GITLAB_ACCESS_TOKEN=CCACjsXDnFzbTkR-eghB + - GITLAB_ACCESS_TOKEN=glpat-bP-xdaaXsq4ot8uf_wuz - SMTP_FROM=Adornis Mailer - SMTP_HOST=mail.adornis.de - SMTP_USER=noreply@adornis.de @@ -43,7 +43,7 @@ services: - traefik.enable=true - traefik.http.routers.adornis-dashboard-staging.entrypoints=https - traefik.http.routers.adornis-dashboard-staging.rule=Host(`adornis-dashboard.staging.adornis.de`) - - traefik.http.routers.adornis-dashboard-staging.tls.certresolver=letsencrypt + - traefik.http.routers.adornis-dashboard-staging.tls.certresolver=ovh redis-adornisdashboard-staging: image: redis/redis-stack environment: diff --git a/src/client/profile/ad-profile.ts b/src/client/profile/ad-profile.ts index dbd7df379e1424b8aa8d291c1823011afc04de82..1e2384a8b9673b4524276de7ea649ce4e8e91a10 100644 --- a/src/client/profile/ad-profile.ts +++ b/src/client/profile/ad-profile.ts @@ -40,7 +40,7 @@ export class ADProfile extends ChemistryLitElement { <x-card> <x-flex space="md"> <x-text bold> Coding Time </x-text> - <x-input placeholder="Coder Name" ${this.fc.field('coderName')}></x-input> + <x-input placeholder="Coder Name (supports comma-separation)" ${this.fc.field('coderName')}></x-input> </x-flex> </x-card> <x-card> diff --git a/src/client/user/ad-user-activity.ts b/src/client/user/ad-user-activity.ts index c68cebb65a488ba82defef84d5d6173b38148342..9f7a23b9d2168f1a19692d654c46700ed442cb30 100644 --- a/src/client/user/ad-user-activity.ts +++ b/src/client/user/ad-user-activity.ts @@ -16,7 +16,7 @@ import { currentUser } from '@adornis/users/client/currentUser.js'; import { html, nothing } from 'lit'; import { customElement, state } from 'lit/decorators.js'; import { DateTime } from 'luxon'; -import { filter, map, of, switchMap } from 'rxjs'; +import { BehaviorSubject, combineLatest, filter, map, of, switchMap } from 'rxjs'; import { CodeTrackerData } from '../../db/code-tracker-data.js'; import { type DashboardUser } from '../../db/dashboard-user.js'; import { EmployeeRecord } from '../../db/employee-records/employee-record.js'; @@ -24,26 +24,35 @@ import { HolidayRecord } from '../../db/employee-records/holiday-record.js'; import { WorkingHoursRecord } from '../../db/employee-records/working-hours-record.js'; import { getApexOptions } from '../apex-chart-options.js'; -const daysBeforeToday = [...Array(28)].map((_, i) => DateTime.now().minus({ days: i })).reverse(); +const daysBeforeToday = (daysBack: number) => + [...Array(daysBack)].map((_, i) => DateTime.now().minus({ days: i })).reverse(); @customElement('ad-user-activity') export class ADUserActivity extends ChemistryLitElement { @state() private readonly user = new RXController(this, currentUser<DashboardUser>().whenReady); + @state() private readonly daysBack = new RXController(this, new BehaviorSubject(7)); - @state() private readonly hoursPerDay = new RXController(this, this.user.observable, user => - user.pipe( - filter(Boolean), - map(user => user.coderName ?? 'NO_CODER_SELECTED'), - switchMap(myPerspective => - myPerspective ? CodeTrackerData.subscribeHoursPerDay(28, myPerspective)(LineChartData.allFields) : of(null), + @state() private readonly hoursPerDay = new RXController( + this, + combineLatest([ + this.user.observable.pipe( + map(user => user?.coderName), + filter(Boolean), + ), + this.daysBack.observable, + ]), + obs => + obs.pipe( + switchMap(([coderName, limit]) => + coderName ? CodeTrackerData.subscribeHoursPerDay(limit, coderName)(LineChartData.allFields) : of(null), + ), ), - ), ); - @state() holidays = new RXController(this, this.user.observable, () => + @state() holidays = new RXController(this, combineLatest([this.user.observable, this.daysBack.observable]), () => HolidayRecord.subscribeEmployeeRecordsFor<HolidayRecord>(0, 100, new AdornisFilter({}))(HolidayRecord.allFields), ); - @state() workingDays = new RXController(this, this.user.observable, () => + @state() workingDays = new RXController(this, combineLatest([this.user.observable, this.daysBack.observable]), () => WorkingHoursRecord.subscribeEmployeeRecordsFor<WorkingHoursRecord>( 0, 100, @@ -51,77 +60,99 @@ export class ADUserActivity extends ChemistryLitElement { )(WorkingHoursRecord.allFields), ); - @state() private readonly commitsMA = new RXController(this, currentUser<DashboardUser>(), user => - user.pipe( - filter(Boolean), - switchMap(user => - user.gitlabUsername - ? GitLabEvent.getMovingAverageOfCommits(28, 7, user.gitlabUsername)() - : of(Array(28).fill(null)), + @state() private readonly commitsMA = new RXController( + this, + combineLatest([ + currentUser<DashboardUser>().pipe( + map(user => user?.gitlabUsername), + filter(Boolean), + ), + this.daysBack.observable, + ]), + obs => + obs.pipe( + switchMap(([gitlabUsername, limit]) => + gitlabUsername + ? GitLabEvent.getMovingAverageOfCommits(limit, 7, gitlabUsername)() + : of(Array(limit).fill(null)), + ), ), - ), ); l = new LoaderController(this); override render() { return this.l.guard(html` - <x-section space="lg" ${css({ marginTop: this.spacing.lg })}> - ${this.user.value?.coderName - ? nothing - : html` - <x-text tone="error"> - Stelle bitte sicher, dass du in deinen Einstellungen den korrekten Coder-Namen stehen hast um hier deine - Coding Time zu sehen - </x-text> - `} - <x-flex space="md"> - <x-h2> Aktivität </x-h2> - <x-text ${css({ maxWidth: this.sizes.maxReadableWidth })}> - Das sind die addierten Millisekunden, in denen du im Coder aktiv warst (Cursor bewegen reicht, 10s - cooldown). Normalerweise ein ganz guter Indikator für Arbeitszeit beim Coding. Erfahrungsgemäß sind Meetings - davon nicht gut abgedeckt, Recherche während dem Coding größtenteils schon. - </x-text> - <apex-chart - .options=${this.getApexOptions('codingtime', [ - { - name: 'Urlaubstage', - data: daysBeforeToday.map(day => { - const result = EmployeeRecord.getEntryForDay(day, this.holidays.value ?? []) ? 1 : null; - return result; - }), - type: 'bar', - color: this.colors.tone.warning.string, - }, - { - name: 'Aktiver Tag, Stundenzettel', - data: daysBeforeToday.map(day => { - const foundDay = this.workingDays.value?.find( - wd => wd.start.startOf('day') <= day.startOf('day') && wd.end.startOf('day') >= day.startOf('day'), - ); - const result = foundDay ? foundDay.end.diff(foundDay.start).as('hours') / 10 : null; - return result; - }), - type: 'bar', - color: lighten(this.colors.tone.success.string, 7), - }, - { - type: 'line', - name: 'commits: 4week MA', - data: this.commitsMA.value ?? [], - color: this.colors.tone.error.string, - }, - ...(this.hoursPerDay.value?.datasets.map(dataset => ({ - name: dataset.label, - data: dataset.data, - color: dataset.label.toLowerCase().includes('team') - ? this.colors.neutral.shade(100).string - : this.colors.accent.string, - })) ?? []), - ])} - ></apex-chart> - </x-flex> - </x-section> + <x-flex space="md"> + <x-section space="lg" ${css({ marginTop: this.spacing.lg })}> + ${this.user.value?.coderName + ? nothing + : html` + <x-text tone="error"> + Stelle bitte sicher, dass du in deinen Einstellungen den korrekten Coder-Namen stehen hast um hier + deine Coding Time zu sehen + </x-text> + `} + <x-flex space="md"> + <x-h2> Aktivität </x-h2> + <x-text ${css({ maxWidth: this.sizes.maxReadableWidth })}> + Das sind die addierten Millisekunden, in denen du im Coder aktiv warst (Cursor bewegen reicht, 10s + cooldown). Normalerweise ein ganz guter Indikator für Arbeitszeit beim Coding. Erfahrungsgemäß sind + Meetings davon nicht gut abgedeckt, Recherche während dem Coding größtenteils schon. + </x-text> + </x-flex> + </x-section> + <x-section horizontal crossaxis-center space="md"> + <x-button mode=${this.daysBack.value === 7 ? 'action' : 'outline'} @click=${() => this.daysBack.next(7)}> + Letzte Woche + </x-button> + <x-button mode=${this.daysBack.value === 28 ? 'action' : 'outline'} @click=${() => this.daysBack.next(28)}> + Letzter Monat + </x-button> + <x-button mode=${this.daysBack.value === 180 ? 'action' : 'outline'} @click=${() => this.daysBack.next(180)}> + Letzte 6 Monate + </x-button> + </x-section> + + <apex-chart + .options=${this.getApexOptions('codingtime', [ + { + name: 'Urlaubstage', + data: daysBeforeToday(this.daysBack.value).map(day => { + const result = EmployeeRecord.getEntryForDay(day, this.holidays.value ?? []) ? 1 : null; + return result; + }), + type: 'bar', + color: this.colors.tone.warning.string, + }, + { + name: 'Aktiver Tag, Stundenzettel', + data: daysBeforeToday(this.daysBack.value).map(day => { + const foundDay = this.workingDays.value?.find( + wd => wd.start.startOf('day') <= day.startOf('day') && wd.end.startOf('day') >= day.startOf('day'), + ); + const result = foundDay ? foundDay.end.diff(foundDay.start).as('hours') / 10 : null; + return result; + }), + type: 'bar', + color: lighten(this.colors.tone.success.string, 7), + }, + { + type: 'line', + name: 'commits: 4week MA', + data: this.commitsMA.value ?? [], + color: this.colors.tone.error.string, + }, + ...(this.hoursPerDay.value?.datasets.map(dataset => ({ + name: dataset.label, + data: dataset.data, + color: dataset.label.toLowerCase().includes('team') + ? this.colors.neutral.shade(100).string + : this.colors.accent.string, + })) ?? []), + ])} + ></apex-chart> + </x-flex> `); } @@ -130,7 +161,7 @@ export class ADUserActivity extends ChemistryLitElement { title: 'Daten', id, dataseries: data, - labels: daysBeforeToday.map(day => day.toLocaleString(DateTime.DATE_SHORT)), + labels: daysBeforeToday(this.daysBack.value).map(day => day.toLocaleString(DateTime.DATE_SHORT)), group: id, }); } diff --git a/src/db/code-tracker-data.ts b/src/db/code-tracker-data.ts index 050db3ea38212ef090f45c1fed161fcd936e676e..ed9aef00e265f71fa92eb9990be572b1ba3a9915 100644 --- a/src/db/code-tracker-data.ts +++ b/src/db/code-tracker-data.ts @@ -1,6 +1,6 @@ import { logger } from '@adornis/base/logging.js'; import { Float, Int } from '@adornis/baseql/baseqlTypes.js'; -import { Arg, Entity, Field, Mutation, Query, Subscription } from '@adornis/baseql/decorators.js'; +import { Arg, Entity, Field, Mutation, Subscription } from '@adornis/baseql/decorators.js'; import { MongoEntity } from '@adornis/baseql/entities/mongoEntity.js'; import { getCollectionHandle, getRawCollection } from '@adornis/baseql/server/collections.js'; import { type BaseQLSelectionSet } from '@adornis/baseql/utils/queryGeneration.js'; @@ -14,11 +14,10 @@ import { DashboardUser } from './dashboard-user.js'; export const AGGREGATION_TYPES = ['highest20', 'all', 'summary'] as const; export type AggregationType = (typeof AGGREGATION_TYPES)[number]; -// const FTE = 3.5; - async function assertHasPermission(myPerspective: string | null) { const coderName = ((await CurrentUserInfo.getMyself<DashboardUser>()({ coderName: 1 })) as DashboardUser).coderName; - if (coderName !== myPerspective) throw new Error(`User doesnt have permission to view ${myPerspective}`); + if (!myPerspective || !coderName?.includes(myPerspective)) + throw new Error(`User doesnt have permission to view ${myPerspective}`); return true; } @@ -100,95 +99,6 @@ export class CodeTrackerData extends MongoEntity { }; } - @Subscription(type => LineChartData) - static subscribeGraphData_ProjectsByUser( - @Arg('daysBack', type => Int) daysBack: number, - @Arg('onlyMine', type => String) myPerspective: string | null = null, - @Arg('aggregationType', type => String) aggregationType: AggregationType = 'highest20', - ) { - return (gqlFields: BaseQLSelectionSet<LineChartData>) => { - return from(assertHasPermission(myPerspective)).pipe( - switchMap(() => - watchAggregation<dataType>(getMongoQueryForDuration(Duration.fromObject({ day: daysBack })), [ - { - $group: { - _id: '$vcs_repo', - 'duration-all': { $sum: '$long' }, - 'duration-myPerspective': { - $sum: { $cond: { if: { $eq: ['$pcid', myPerspective] }, then: '$long', else: 0 } }, - }, - }, - }, - { $sort: myPerspective ? { 'duration-myPerspective': -1 } : { 'duration-all': -1 } }, - { $limit: aggregationType === 'highest20' ? 20 : Infinity }, - ]), - ), - map(data => aggregationDataToLineChartData(data, { chartType: 'line' })), - ); - }; - } - - @Subscription(type => LineChartData) - static subscribeGraphData_FileTypes( - @Arg('daysBack', type => Int) daysBack: number, - @Arg('aggregationType', type => String) aggregationType: AggregationType = 'highest20', - @Arg('onlyMine', type => String) myPerspective: string | null = null, - ) { - return (gqlFields: BaseQLSelectionSet<LineChartData>) => { - return from(assertHasPermission(myPerspective)).pipe( - switchMap(() => - watchAggregation<dataType>({ ...getMongoQueryForDuration(Duration.fromObject({ days: daysBack })) }, [ - ...(aggregationType === 'summary' - ? [ - { - $addFields: { - file: { - $cond: [ - { $regexFind: { input: '$file', regex: 'src/imports/client/(.*)' } }, - 'client', - { - $cond: [ - { $regexFind: { input: '$file', regex: 'src/(.*/)?db/(.*)' } }, - 'db', - { - $cond: [ - { $regexFind: { input: '$file', regex: 'src/server/(.*)' } }, - 'db', - { - $cond: [ - { $regexFind: { input: '$file', regex: 'modules/(.*)' } }, - 'modules', - 'other', - ], - }, - ], - }, - ], - }, - ], - }, - }, - }, - ] - : []), - { - $group: { - _id: '$file', - 'duration-all': { $sum: '$long' }, - 'duration-myPerspective': { - $sum: { $cond: { if: { $eq: ['$pcid', myPerspective] }, then: '$long', else: 0 } }, - }, - }, - }, - { $sort: myPerspective ? { 'duration-myPerspective': -1 } : { 'duration-all': -1 } }, - { $limit: aggregationType === 'highest20' ? 20 : Infinity }, - ]), - ), - map(data => aggregationDataToLineChartData(data)), - ); - }; - } - @Subscription(type => [Float]) static subscribeNumberHours( @Arg('daysBack', type => Float) daysBack: number, @@ -219,10 +129,12 @@ export class CodeTrackerData extends MongoEntity { @Subscription(type => LineChartData) static subscribeHoursPerDay( @Arg('daysBack', type => Float) daysBack: number, - @Arg('onlyMine', type => String) myPerspective: string | null = null, + @Arg('onlyMine', type => String) myPerspectives: string | null = null, ) { return (gqlFields: BaseQLSelectionSet<LineChartData>) => { - return from(assertHasPermission(myPerspective)).pipe( + return from( + Promise.all(myPerspectives?.split(',').map(myPerspective => assertHasPermission(myPerspective.trim())) ?? []), + ).pipe( switchMap(() => watchAggregation<dataType>({ ...getMongoQueryForDuration(Duration.fromObject({ days: daysBack })) }, [ { @@ -230,7 +142,7 @@ export class CodeTrackerData extends MongoEntity { _id: { $concat: [{ $toString: { $year: '$time' } }, '-', { $toString: { $dayOfYear: '$time' } }] }, 'duration-all': { $sum: '$long' }, 'duration-myPerspective': { - $sum: { $cond: { if: { $eq: ['$pcid', myPerspective] }, then: '$long', else: 0 } }, + $sum: { $cond: { if: { $in: ['$pcid', myPerspectives?.split(',')] }, then: '$long', else: 0 } }, }, }, }, @@ -246,16 +158,13 @@ export class CodeTrackerData extends MongoEntity { data.labels = [...Array(daysBack)].map((_, i) => DateTime.now() - .startOf('day') .minus({ days: daysBack - i }) .toFormat('dd.LL.yyyy'), ); data.datasets = data.datasets.map(dataset => { const data = dataset.data; dataset.data = [...Array(daysBack)].map((_, i) => { - const date = DateTime.now() - .startOf('day') - .minus({ days: daysBack - i }); + const date = DateTime.now().minus({ days: daysBack - (1 + i) }); const indexInDates = dates.indexOf(date.toFormat('dd.LL.yyyy')); if (indexInDates !== -1) return data[indexInDates]!; return 0; @@ -266,38 +175,4 @@ export class CodeTrackerData extends MongoEntity { ); }; } - - @Query(type => Int) - static getActiveDays( - @Arg('daysBack', type => Int) daysBack: number, - @Arg('myPerspective', type => String) myPerspective: string | null = null, - ) { - return async () => { - const projectBlacklist = []; - await assertHasPermission(myPerspective); - return (await getRawCollection<CodeTrackerData>(this._collectionName)) - .aggregate([ - { - $match: { - ...(myPerspective ? { pcid: { $regex: myPerspective, $options: 'i' } } : {}), - time: { - $gte: DateTime.now() - .startOf('day') - .minus({ days: daysBack || 7 }) - .toJSDate(), - }, - proj: { $nin: projectBlacklist }, - }, - }, - { - $group: { _id: { day: { $dayOfYear: '$time' }, year: { $year: '$time' } }, long: { $sum: '$long' } }, - }, - { - $match: { long: { $gt: 7000000 } }, - }, - ]) - .toArray() - .then(x => x.length); - }; - } } diff --git a/src/server/startup.ts b/src/server/startup.ts index b53500bdc88cad5c7db5ef027b230c06fb004f1a..250b69d87021b451d248a5c1cf46a70ae9283dcb 100644 --- a/src/server/startup.ts +++ b/src/server/startup.ts @@ -70,9 +70,8 @@ const contextManager = new ContextManager({ [EmployeeRecord._class]: user?.hasPermission('REVIEW_HOLIDAYS_AND_WORKING_HOURS') ? {} : { userID: user?._id ?? 'nothing' }, - // TODO implement correctly - [WikiEntry._class]: {}, - [WikiEntryDraft._class]: {}, + [WikiEntry._class]: !user ? { published: true } : {}, + [WikiEntryDraft._class]: !user ? { nothing: 'is here' } : {}, [FakturaDocument._class]: user?.hasPermission(permissions.USE_FAKTURA, GLOBAL_CONTEXT) ? {} : { tenantID: { $in: (user?.roles as AdornisRoleToContextsWrapper[])?.flatMap(r => r.contexts) ?? [] } }, @@ -107,8 +106,12 @@ const contextManager = new ContextManager({ [AdornisFile._class]: user ? { create: true, delete: true, modify: true } : {}, [CustomContent._class]: { create: true, delete: true, modify: true }, [Tenant._class]: { create: true, delete: true, modify: true }, - [WikiEntry._class]: { create: true, delete: true, modify: true }, - [WikiEntryDraft._class]: { create: true, delete: true, modify: true }, + [WikiEntry._class]: user + ? { create: true, delete: true, modify: true } + : { create: false, delete: false, modify: false }, + [WikiEntryDraft._class]: user + ? { create: true, delete: true, modify: true } + : { create: false, delete: false, modify: false }, [BuildifyGlobalSettings._class]: { create: true, delete: true, modify: true }, }; };