Browse Source
Task/guard system tags in admin control panel (#7532)
* Guard system tags against deletion and renaming
* Update changelog
pull/7543/head
Thomas Kaul
3 days ago
committed by
GitHub
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with
39 additions and
8 deletions
-
CHANGELOG.md
-
apps/api/src/app/endpoints/tags/tags.controller.ts
-
apps/client/src/app/components/admin-tag/admin-tag.component.html
-
apps/client/src/app/components/admin-tag/admin-tag.component.ts
-
libs/common/src/lib/config.ts
-
libs/common/src/lib/helper.ts
|
|
|
@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 |
|
|
|
|
|
|
|
- Added the platform logo to the account selector in the create or update activity dialog |
|
|
|
|
|
|
|
### Changed |
|
|
|
|
|
|
|
- Guarded the system tags against deletion and renaming in the tag management of the admin control panel |
|
|
|
|
|
|
|
## 3.42.0 - 2026-08-04 |
|
|
|
|
|
|
|
### Changed |
|
|
|
|
|
|
|
@ -2,6 +2,7 @@ import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorat |
|
|
|
import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard'; |
|
|
|
import { TagService } from '@ghostfolio/api/services/tag/tag.service'; |
|
|
|
import { CreateTagDto, UpdateTagDto } from '@ghostfolio/common/dtos'; |
|
|
|
import { isSystemTag } from '@ghostfolio/common/helper'; |
|
|
|
import { hasPermission, permissions } from '@ghostfolio/common/permissions'; |
|
|
|
import { RequestWithUser } from '@ghostfolio/common/types'; |
|
|
|
|
|
|
|
@ -69,7 +70,7 @@ export class TagsController { |
|
|
|
id |
|
|
|
}); |
|
|
|
|
|
|
|
if (!originalTag) { |
|
|
|
if (!originalTag || isSystemTag(originalTag)) { |
|
|
|
throw new HttpException( |
|
|
|
getReasonPhrase(StatusCodes.FORBIDDEN), |
|
|
|
StatusCodes.FORBIDDEN |
|
|
|
@ -94,7 +95,7 @@ export class TagsController { |
|
|
|
id |
|
|
|
}); |
|
|
|
|
|
|
|
if (!originalTag) { |
|
|
|
if (!originalTag || isSystemTag(originalTag)) { |
|
|
|
throw new HttpException( |
|
|
|
getReasonPhrase(StatusCodes.FORBIDDEN), |
|
|
|
StatusCodes.FORBIDDEN |
|
|
|
|
|
|
|
@ -83,7 +83,11 @@ |
|
|
|
<ion-icon name="ellipsis-horizontal" /> |
|
|
|
</button> |
|
|
|
<mat-menu #tagMenu="matMenu" xPosition="before"> |
|
|
|
<button mat-menu-item (click)="onUpdateTag(element)"> |
|
|
|
<button |
|
|
|
mat-menu-item |
|
|
|
[disabled]="isSystemTag(element)" |
|
|
|
(click)="onUpdateTag(element)" |
|
|
|
> |
|
|
|
<span class="align-items-center d-flex"> |
|
|
|
<ion-icon class="mr-2" name="create-outline" /> |
|
|
|
<span><ng-container i18n>Edit</ng-container>...</span> |
|
|
|
@ -92,7 +96,11 @@ |
|
|
|
<hr class="m-0" /> |
|
|
|
<button |
|
|
|
mat-menu-item |
|
|
|
[disabled]="element.accountCount > 0 || element.activityCount > 0" |
|
|
|
[disabled]=" |
|
|
|
element.accountCount > 0 || |
|
|
|
element.activityCount > 0 || |
|
|
|
isSystemTag(element) |
|
|
|
" |
|
|
|
(click)="onDeleteTag(element.id)" |
|
|
|
> |
|
|
|
<span class="align-items-center d-flex"> |
|
|
|
|
|
|
|
@ -2,7 +2,11 @@ import { UserService } from '@ghostfolio/client/services/user/user.service'; |
|
|
|
import { DEFAULT_PAGE_SIZE } from '@ghostfolio/common/config'; |
|
|
|
import { CreateTagDto, UpdateTagDto } from '@ghostfolio/common/dtos'; |
|
|
|
import { ConfirmationDialogType } from '@ghostfolio/common/enums'; |
|
|
|
import { getLocale, getLowercase } from '@ghostfolio/common/helper'; |
|
|
|
import { |
|
|
|
getLocale, |
|
|
|
getLowercase, |
|
|
|
isSystemTag |
|
|
|
} from '@ghostfolio/common/helper'; |
|
|
|
import { translate } from '@ghostfolio/ui/i18n'; |
|
|
|
import { NotificationService } from '@ghostfolio/ui/notifications'; |
|
|
|
import { DataService } from '@ghostfolio/ui/services'; |
|
|
|
@ -67,6 +71,7 @@ export class GfAdminTagComponent implements OnInit { |
|
|
|
'activities', |
|
|
|
'actions' |
|
|
|
]; |
|
|
|
protected readonly isSystemTag = isSystemTag; |
|
|
|
protected readonly pageSize = DEFAULT_PAGE_SIZE; |
|
|
|
protected tags: Tag[]; |
|
|
|
protected readonly translate = translate; |
|
|
|
@ -99,7 +104,7 @@ export class GfAdminTagComponent implements OnInit { |
|
|
|
return id === params['tagId']; |
|
|
|
}); |
|
|
|
|
|
|
|
if (tag) { |
|
|
|
if (tag && !isSystemTag(tag)) { |
|
|
|
this.openUpdateTagDialog(tag); |
|
|
|
} |
|
|
|
} else { |
|
|
|
|
|
|
|
@ -338,10 +338,16 @@ export const SUPPORTED_LANGUAGE_CODES = [ |
|
|
|
'zh' |
|
|
|
] as const; |
|
|
|
|
|
|
|
export const TAG_ID_DEMO = 'efa08cb3-9b9d-4974-ac68-db13a19c4874'; |
|
|
|
export const TAG_ID_EMERGENCY_FUND = '4452656d-9fa4-4bd0-ba38-70492e31d180'; |
|
|
|
export const TAG_ID_EXCLUDE_FROM_ANALYSIS = |
|
|
|
'f2e868af-8333-459f-b161-cbc6544c24bd'; |
|
|
|
export const TAG_ID_DEMO = 'efa08cb3-9b9d-4974-ac68-db13a19c4874'; |
|
|
|
|
|
|
|
export const TAG_IDS_SYSTEM = [ |
|
|
|
TAG_ID_DEMO, |
|
|
|
TAG_ID_EMERGENCY_FUND, |
|
|
|
TAG_ID_EXCLUDE_FROM_ANALYSIS |
|
|
|
]; |
|
|
|
|
|
|
|
export const THROTTLE_DAILY_KEY = 'daily'; |
|
|
|
export const THROTTLE_DAILY_TTL = ms('1 day'); |
|
|
|
|
|
|
|
@ -43,7 +43,8 @@ import { |
|
|
|
ghostfolioFearAndGreedIndexSymbolStocks, |
|
|
|
ghostfolioPrefix, |
|
|
|
SEARCH_QUERY_MINIMUM_LENGTH, |
|
|
|
TAG_ID_EXCLUDE_FROM_ANALYSIS |
|
|
|
TAG_ID_EXCLUDE_FROM_ANALYSIS, |
|
|
|
TAG_IDS_SYSTEM |
|
|
|
} from './config'; |
|
|
|
import { |
|
|
|
AssetProfileIdentifier, |
|
|
|
@ -557,6 +558,12 @@ export function isSplitRatio({ |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
export function isSystemTag(tag?: { id: string }) { |
|
|
|
return TAG_IDS_SYSTEM.some((id) => { |
|
|
|
return id === tag?.id; |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
export function isValidCustomAssetProfileSymbol(aSymbol: string) { |
|
|
|
return hasGhostfolioPrefix(aSymbol) || isUUID(aSymbol); |
|
|
|
} |
|
|
|
|