#127_config_in_wi #327
| @@ -174,7 +174,7 @@ class DataIntegrityService: | ||||
|  | ||||
|                     self._logger.warn(__name__, f"User not found in database: {u.id}") | ||||
|                     self._logger.debug(__name__, f"Add user: {u.id}") | ||||
|                     self._users.add_user(User(u.id, 0, server)) | ||||
|                     self._users.add_user(User(u.id, 0, 0, 0, server)) | ||||
|                     self._db_context.save_changes() | ||||
|  | ||||
|                     self._logger.debug(__name__, f"Added User: {u.id}") | ||||
|   | ||||
| @@ -74,6 +74,6 @@ class TechnicianPingUrlConfig(TableABC): | ||||
|         return str( | ||||
|             f""" | ||||
|                 DELETE FROM `CFG_TechnicianPingUrls` | ||||
|                 WHERE `URL` = {self._ping_url}; | ||||
|                 WHERE `URL` = '{self._ping_url}'; | ||||
|             """ | ||||
|         ) | ||||
|   | ||||
| @@ -1,6 +1,8 @@ | ||||
| from cpl_core.database.context import DatabaseContextABC | ||||
| from cpl_discord.service import DiscordBotServiceABC | ||||
| from cpl_query.extension import List | ||||
|  | ||||
| from bot_api.logging.api_logger import ApiLogger | ||||
| from bot_data.abc.server_repository_abc import ServerRepositoryABC | ||||
| from bot_data.abc.technician_config_repository_abc import TechnicianConfigRepositoryABC | ||||
| from bot_data.model.technician_config import TechnicianConfig | ||||
| @@ -13,6 +15,7 @@ from bot_graphql.abc.query_abc import QueryABC | ||||
| class TechnicianConfigMutation(QueryABC): | ||||
|     def __init__( | ||||
|         self, | ||||
|         logger: ApiLogger, | ||||
|         bot: DiscordBotServiceABC, | ||||
|         servers: ServerRepositoryABC, | ||||
|         technician_configs: TechnicianConfigRepositoryABC, | ||||
| @@ -20,6 +23,7 @@ class TechnicianConfigMutation(QueryABC): | ||||
|     ): | ||||
|         QueryABC.__init__(self, "TechnicianConfigMutation") | ||||
|  | ||||
|         self._logger = logger | ||||
|         self._bot = bot | ||||
|         self._servers = servers | ||||
|         self._technician_configs = technician_configs | ||||
| @@ -45,9 +49,13 @@ class TechnicianConfigMutation(QueryABC): | ||||
|         technician_config.cache_max_messages = ( | ||||
|             input["cacheMaxMessages"] if "cacheMaxMessages" in input else technician_config.cache_max_messages | ||||
|         ) | ||||
|         technician_config.ping_urls = input["pingURLs"] if "pingURLs" in input else technician_config.ping_urls | ||||
|         technician_config.ping_urls = ( | ||||
|             List(str, input["pingURLs"]) if "pingURLs" in input else technician_config.ping_urls | ||||
|         ) | ||||
|         technician_config.technician_ids = ( | ||||
|             input["technicianIds"] if "technicianIds" in input else technician_config.technician_ids | ||||
|             List(int).extend([int(x) for x in input["technicianIds"]]) | ||||
|             if "technicianIds" in input | ||||
|             else technician_config.technician_ids | ||||
|         ) | ||||
|  | ||||
|         self._technician_configs.update_technician_config(technician_config) | ||||
| @@ -76,14 +84,19 @@ class TechnicianConfigMutation(QueryABC): | ||||
|  | ||||
|     def _update_technician_ids(self, new_config: TechnicianConfig): | ||||
|         old_config = self._technician_configs.get_technician_config() | ||||
|         for url in old_config.technician_ids: | ||||
|             if url in new_config.technician_ids: | ||||
|         for technician_id in old_config.technician_ids: | ||||
|             if technician_id in new_config.technician_ids: | ||||
|                 continue | ||||
|  | ||||
|             self._technician_configs.delete_technician_id_config(TechnicianIdConfig(url)) | ||||
|             self._technician_configs.delete_technician_id_config(TechnicianIdConfig(technician_id)) | ||||
|  | ||||
|         for url in new_config.technician_ids: | ||||
|             if url in old_config.technician_ids: | ||||
|         for technician_id in new_config.technician_ids: | ||||
|             user = self._bot.get_user(technician_id) | ||||
|             if user is None: | ||||
|                 raise ValueError(f"Invalid technicianId") | ||||
|  | ||||
|         for technician_id in new_config.technician_ids: | ||||
|             if technician_id in old_config.technician_ids: | ||||
|                 continue | ||||
|  | ||||
|             self._technician_configs.add_technician_id_config(TechnicianIdConfig(url)) | ||||
|             self._technician_configs.add_technician_id_config(TechnicianIdConfig(technician_id)) | ||||
|   | ||||
| @@ -51,4 +51,4 @@ | ||||
|         "tslib": "^2.4.1", | ||||
|         "typescript": "~4.9.5" | ||||
|     } | ||||
| } | ||||
| } | ||||
| @@ -160,6 +160,100 @@ | ||||
|       </div> | ||||
|     </div> | ||||
|  | ||||
|  | ||||
|     <div class="content-divider"></div> | ||||
|     <div class="content-row"> | ||||
|       <div class="content-column"> | ||||
|         <div class="content-data-name"> | ||||
|           {{'admin.settings.bot.ping_urls' | translate}}: | ||||
|         </div> | ||||
|         <div class="content-data-value-row"> | ||||
|           <p-table #dt [value]="pingUrls" dataKey="id" editMode="row"> | ||||
|             <ng-template pTemplate="caption"> | ||||
|               <div class="table-caption"> | ||||
|                 <div class="table-caption-btn-wrapper btn-wrapper"> | ||||
|                   <button pButton class="icon-btn btn" | ||||
|                           icon="pi pi-plus" (click)="pingUrlsAddNew(dt)"> | ||||
|                   </button> | ||||
|                 </div> | ||||
|               </div> | ||||
|             </ng-template> | ||||
|             <ng-template pTemplate="body" let-pingUrl let-editing="editing" let-ri="rowIndex"> | ||||
|               <tr [pEditableRow]="pingUrl"> | ||||
|                 <td> | ||||
|                   <p-cellEditor> | ||||
|                     <ng-template pTemplate="input"> | ||||
|                       <input class="table-edit-input" pInputText type="text" [(ngModel)]="pingUrl.value"> | ||||
|                     </ng-template> | ||||
|                     <ng-template pTemplate="output"> | ||||
|                       {{pingUrl.value}} | ||||
|                     </ng-template> | ||||
|                   </p-cellEditor> | ||||
|                 </td> | ||||
|                 <td> | ||||
|                   <div class="btn-wrapper"> | ||||
|                     <button *ngIf="!editing" pButton type="button" pInitEditableRow class="btn icon-btn" icon="pi pi-pencil" (click)="pingUrlsEditInit(pingUrl, ri)"></button> | ||||
|                     <button *ngIf="!editing" pButton type="button" class="btn danger-icon-btn" icon="pi pi-trash" (click)="pingUrlsDelete(ri)"></button> | ||||
|  | ||||
|                     <button *ngIf="editing" pButton type="button" pSaveEditableRow class="btn icon-btn" icon="pi pi-check" (click)="pingUrlsEditSave(pingUrl, ri)"></button> | ||||
|                     <button *ngIf="editing" pButton type="button" pCancelEditableRow class="btn danger-icon-btn" icon="pi pi-times" | ||||
|                             (click)="pingUrlsEditCancel(pingUrl, ri)"></button> | ||||
|                   </div> | ||||
|                 </td> | ||||
|               </tr> | ||||
|             </ng-template> | ||||
|           </p-table> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="content-divider"></div> | ||||
|  | ||||
|     <div class="content-row"> | ||||
|       <div class="content-column"> | ||||
|         <div class="content-data-name"> | ||||
|           {{'admin.settings.bot.technicianIds' | translate}}: | ||||
|         </div> | ||||
|         <div class="content-data-value-row"> | ||||
|           <p-table #dt [value]="technicianIds" dataKey="id" editMode="row"> | ||||
|             <ng-template pTemplate="caption"> | ||||
|               <div class="table-caption"> | ||||
|                 <div class="table-caption-btn-wrapper btn-wrapper"> | ||||
|                   <button pButton class="icon-btn btn" | ||||
|                           icon="pi pi-plus" (click)="technicianIdsAddNew(dt)"> | ||||
|                   </button> | ||||
|                 </div> | ||||
|               </div> | ||||
|             </ng-template> | ||||
|             <ng-template pTemplate="body" let-technicianId let-editing="editing" let-ri="rowIndex"> | ||||
|               <tr [pEditableRow]="technicianId"> | ||||
|                 <td> | ||||
|                   <p-cellEditor> | ||||
|                     <ng-template pTemplate="input"> | ||||
|                       <input class="table-edit-input" pInputText type="text" [(ngModel)]="technicianId.value"> | ||||
|                     </ng-template> | ||||
|                     <ng-template pTemplate="output"> | ||||
|                       {{technicianId.value}} | ||||
|                     </ng-template> | ||||
|                   </p-cellEditor> | ||||
|                 </td> | ||||
|                 <td> | ||||
|                   <div class="btn-wrapper"> | ||||
|                     <button *ngIf="!editing" pButton type="button" pInitEditableRow class="btn icon-btn" icon="pi pi-pencil" (click)="technicianIdsEditInit(technicianId, ri)"></button> | ||||
|                     <button *ngIf="!editing" pButton type="button" class="btn danger-icon-btn" icon="pi pi-trash" (click)="technicianIdsDelete(ri)"></button> | ||||
|  | ||||
|                     <button *ngIf="editing" pButton type="button" pSaveEditableRow class="btn icon-btn" icon="pi pi-check" (click)="technicianIdsEditSave(technicianId, ri)"></button> | ||||
|                     <button *ngIf="editing" pButton type="button" pCancelEditableRow class="btn danger-icon-btn" icon="pi pi-times" | ||||
|                             (click)="technicianIdsEditCancel(technicianId, ri)"></button> | ||||
|                   </div> | ||||
|                 </td> | ||||
|               </tr> | ||||
|             </ng-template> | ||||
|           </p-table> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="content-divider"></div> | ||||
|  | ||||
|     <div class="content-row"> | ||||
|       <button pButton icon="pi pi-save" label="{{'common.save' | translate}}" class="btn login-form-submit-btn" | ||||
|               (click)="saveTechnicianConfig()" [disabled]="technicianConfigForm.invalid"></button> | ||||
|   | ||||
| @@ -14,12 +14,20 @@ import { TechnicianConfig } from "../../../../../models/config/technician-config | ||||
| import { TechnicianConfigQuery } from "../../../../../models/graphql/query.model"; | ||||
| import { Queries } from "../../../../../models/graphql/queries.model"; | ||||
| import { DataService } from "../../../../../services/data/data.service"; | ||||
| import { LevelMutationResult, TechnicianConfigMutationResult } from "../../../../../models/graphql/result.model"; | ||||
| import { TechnicianConfigMutationResult } from "../../../../../models/graphql/result.model"; | ||||
| import { Mutations } from "../../../../../models/graphql/mutations.model"; | ||||
| import { AuthService } from "../../../../../services/auth/auth.service"; | ||||
| import { ConfirmationDialogService } from "../../../../../services/confirmation-dialog/confirmation-dialog.service"; | ||||
| import { SidebarService } from "../../../../../services/sidebar/sidebar.service"; | ||||
| import { ActivatedRoute } from "@angular/router"; | ||||
| import { Table } from "primeng/table"; | ||||
|  | ||||
| type PingUrl = { | ||||
|   id: number; | ||||
|   value: string; | ||||
| }; | ||||
|  | ||||
| type TechnicianId = { | ||||
|   id: number; | ||||
|   value: string; | ||||
| }; | ||||
|  | ||||
| @Component({ | ||||
|   selector: "app-settings", | ||||
| @@ -33,9 +41,12 @@ export class SettingsComponent implements OnInit { | ||||
|     helpCommandReferenceUrl: [null, [Validators.required]], | ||||
|     waitForRestart: [null, [Validators.required]], | ||||
|     waitForShutdown: [null, [Validators.required]], | ||||
|     cacheMaxMessages: [null, [Validators.required]], | ||||
|     pingUrls: this.formBuilder.array([]) | ||||
|     cacheMaxMessages: [null, [Validators.required]] | ||||
|   }); | ||||
|   pingUrls: PingUrl[] = []; | ||||
|   clonedPingUrls: { [s: number]: PingUrl } = {}; | ||||
|   technicianIds: TechnicianId[] = []; | ||||
|   clonedTechnicianIds: { [s: number]: TechnicianId } = {}; | ||||
|   data: SettingsDTO = { | ||||
|     webVersion: "", | ||||
|     apiVersion: "", | ||||
| @@ -71,7 +82,7 @@ export class SettingsComponent implements OnInit { | ||||
|     private toastService: ToastService, | ||||
|     private translate: TranslateService, | ||||
|     private authService: AuthService, | ||||
|     private spinner: SpinnerService, | ||||
|     private spinner: SpinnerService | ||||
|   ) { | ||||
|   } | ||||
|  | ||||
| @@ -107,12 +118,18 @@ export class SettingsComponent implements OnInit { | ||||
|       helpCommandReferenceUrl: [this.config.helpCommandReferenceUrl, [Validators.required]], | ||||
|       waitForRestart: [this.config.waitForRestart, [Validators.required]], | ||||
|       waitForShutdown: [this.config.waitForShutdown, [Validators.required]], | ||||
|       cacheMaxMessages: [this.config.cacheMaxMessages, [Validators.required]], | ||||
|       pingUrls: this.formBuilder.array([]) | ||||
|       cacheMaxMessages: [this.config.cacheMaxMessages, [Validators.required]] | ||||
|     }); | ||||
|     const pingUrls = <FormArray>this.technicianConfigForm.controls["pingUrls"]; | ||||
|     let id = 0; | ||||
|     for (const url of this.config.pingURLs ?? []) { | ||||
|       pingUrls.push(new FormControl(url, [Validators.required])); | ||||
|       this.pingUrls.push({ id: id, value: url }); | ||||
|       id++; | ||||
|     } | ||||
|  | ||||
|     id = 0; | ||||
|     for (const technicianId of this.config.technicianIds ?? []) { | ||||
|       this.technicianIds.push({ id: id, value: technicianId }); | ||||
|       id++; | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -165,22 +182,93 @@ export class SettingsComponent implements OnInit { | ||||
|  | ||||
|   saveTechnicianConfig() { | ||||
|     this.spinner.showSpinner(); | ||||
|       this.dataService.mutation<TechnicianConfigMutationResult>(Mutations.updateTechnicianConfig, { | ||||
|           helpCommandReferenceUrl: this.config.helpCommandReferenceUrl, | ||||
|           waitForRestart: this.config.waitForRestart, | ||||
|           waitForShutdown: this.config.waitForShutdown, | ||||
|           cacheMaxMessages: this.config.cacheMaxMessages, | ||||
|           pingURLs: this.config.pingURLs, | ||||
|           technicianIds: this.config.technicianIds, | ||||
|         } | ||||
|       ).pipe(catchError(err => { | ||||
|         this.spinner.hideSpinner(); | ||||
|         this.toastService.error(this.translate.instant("admin.settings.message.technician_config_create_failed"), this.translate.instant("admin.settings.message.technician_config_create_failed_d")); | ||||
|         return throwError(err); | ||||
|       })).subscribe(result => { | ||||
|         this.spinner.hideSpinner(); | ||||
|         this.toastService.success(this.translate.instant("admin.settings.message.technician_config_create"), this.translate.instant("admin.settings.message.technician_config_create_d")); | ||||
|       }); | ||||
|     this.dataService.mutation<TechnicianConfigMutationResult>(Mutations.updateTechnicianConfig, { | ||||
|         helpCommandReferenceUrl: this.config.helpCommandReferenceUrl, | ||||
|         waitForRestart: this.config.waitForRestart, | ||||
|         waitForShutdown: this.config.waitForShutdown, | ||||
|         cacheMaxMessages: this.config.cacheMaxMessages, | ||||
|         pingURLs: this.pingUrls.filter(value => value.value).map(value => { | ||||
|           return value.value; | ||||
|         }), | ||||
|         technicianIds: this.technicianIds.filter(value => value.value).map(value => { | ||||
|           return value.value; | ||||
|         }) | ||||
|       } | ||||
|     ).pipe(catchError(err => { | ||||
|       this.spinner.hideSpinner(); | ||||
|       this.toastService.error(this.translate.instant("admin.settings.message.technician_config_create_failed"), this.translate.instant("admin.settings.message.technician_config_create_failed_d")); | ||||
|       return throwError(err); | ||||
|     })).subscribe(result => { | ||||
|       this.spinner.hideSpinner(); | ||||
|       this.toastService.success(this.translate.instant("admin.settings.message.technician_config_create"), this.translate.instant("admin.settings.message.technician_config_create_d")); | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   pingUrlsAddNew(table: Table) { | ||||
|     const id = Math.max.apply(Math, this.pingUrls.map(url => { | ||||
|       return url.id ?? 0; | ||||
|     })) + 1; | ||||
|     const newItem = { id: id, value: "" }; | ||||
|     this.pingUrls.push(newItem); | ||||
|  | ||||
|     table.initRowEdit(newItem); | ||||
|     const index = this.pingUrls.findIndex(l => l.id == newItem.id); | ||||
|     this.pingUrlsEditInit(newItem, index); | ||||
|   } | ||||
|  | ||||
|   pingUrlsEditInit(url: PingUrl, index: number) { | ||||
|     this.clonedPingUrls[index] = { ...url }; | ||||
|   } | ||||
|  | ||||
|   pingUrlsDelete(index: number) { | ||||
|     this.pingUrls.splice(index, 1); | ||||
|   } | ||||
|  | ||||
|   pingUrlsEditSave(url: PingUrl, index: number) { | ||||
|     if (!url.value || this.pingUrls[index] == this.clonedPingUrls[index]) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     delete this.clonedPingUrls[index]; | ||||
|   } | ||||
|  | ||||
|   pingUrlsEditCancel(url: PingUrl, index: number) { | ||||
|     this.pingUrls[index] = this.clonedPingUrls[index]; | ||||
|     delete this.clonedPingUrls[index]; | ||||
|   } | ||||
|  | ||||
|  | ||||
|   technicianIdsAddNew(table: Table) { | ||||
|     const id = Math.max.apply(Math, this.technicianIds.map(url => { | ||||
|       return url.id ?? 0; | ||||
|     })) + 1; | ||||
|     const newItem = { id: id, value: "" }; | ||||
|     this.technicianIds.push(newItem); | ||||
|  | ||||
|     table.initRowEdit(newItem); | ||||
|     const index = this.technicianIds.findIndex(l => l.id == newItem.id); | ||||
|     this.technicianIdsEditInit(newItem, index); | ||||
|   } | ||||
|  | ||||
|   technicianIdsEditInit(url: PingUrl, index: number) { | ||||
|     this.clonedTechnicianIds[index] = { ...url }; | ||||
|   } | ||||
|  | ||||
|   technicianIdsDelete(index: number) { | ||||
|     this.technicianIds.splice(index, 1); | ||||
|   } | ||||
|  | ||||
|   technicianIdsEditSave(url: PingUrl, index: number) { | ||||
|     if (!url.value || this.technicianIds[index] == this.clonedTechnicianIds[index]) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     delete this.clonedTechnicianIds[index]; | ||||
|   } | ||||
|  | ||||
|   technicianIdsEditCancel(url: PingUrl, index: number) { | ||||
|     this.technicianIds[index] = this.clonedTechnicianIds[index]; | ||||
|     delete this.clonedTechnicianIds[index]; | ||||
|   } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -25,4 +25,4 @@ | ||||
|             "Name": "sh-edraft-dark-theme" | ||||
|         } | ||||
|     ] | ||||
| } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user