mirror of
				https://github.com/simon987/task_tracker.git
				synced 2025-10-31 16:06:53 +00:00 
			
		
		
		
	More work on perms page
This commit is contained in:
		
							parent
							
								
									b936513eb9
								
							
						
					
					
						commit
						94c3ce3267
					
				
							
								
								
									
										26
									
								
								api/auth.go
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								api/auth.go
									
									
									
									
									
								
							| @ -155,6 +155,32 @@ func (api *WebAPI) GetManagerList(r *Request) { | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| func (api *WebAPI) GetManagerListWithRoleOn(r *Request) { | ||||
| 
 | ||||
| 	pid, err := strconv.ParseInt(r.Ctx.UserValue("id").(string), 10, 64) | ||||
| 	handleErr(err, r) //todo handle invalid id | ||||
| 
 | ||||
| 	sess := api.Session.StartFasthttp(r.Ctx) | ||||
| 	manager := sess.Get("manager") | ||||
| 
 | ||||
| 	if manager == nil { | ||||
| 		r.Json(JsonResponse{ | ||||
| 			Ok:      false, | ||||
| 			Message: "Unauthorized", | ||||
| 		}, 401) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	managers := api.Database.GetManagerListWithRoleOn(pid) | ||||
| 
 | ||||
| 	r.OkJson(JsonResponse{ | ||||
| 		Ok: true, | ||||
| 		Content: GetManagerListWithRoleOnResponse{ | ||||
| 			Managers: managers, | ||||
| 		}, | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| func (api *WebAPI) PromoteManager(r *Request) { | ||||
| 
 | ||||
| 	id, err := strconv.ParseInt(r.Ctx.UserValue("id").(string), 10, 64) | ||||
|  | ||||
| @ -106,8 +106,10 @@ func New() *WebAPI { | ||||
| 	api.router.GET("/logout", LogRequestMiddleware(api.Logout)) | ||||
| 	api.router.GET("/account", LogRequestMiddleware(api.GetAccountDetails)) | ||||
| 	api.router.GET("/manager/list", LogRequestMiddleware(api.GetManagerList)) | ||||
| 	api.router.GET("/manager/list_for_project/:id", LogRequestMiddleware(api.GetManagerListWithRoleOn)) | ||||
| 	api.router.GET("/manager/promote/:id", LogRequestMiddleware(api.PromoteManager)) | ||||
| 	api.router.GET("/manager/demote/:id", LogRequestMiddleware(api.DemoteManager)) | ||||
| 	api.router.POST("/manager/set_role_for_project/:id", LogRequestMiddleware(api.SetManagerRoleOnProject)) | ||||
| 
 | ||||
| 	api.router.NotFound = func(ctx *fasthttp.RequestCtx) { | ||||
| 
 | ||||
|  | ||||
| @ -81,6 +81,10 @@ type GetManagerListResponse struct { | ||||
| 	Managers *[]storage.Manager `json:"managers"` | ||||
| } | ||||
| 
 | ||||
| type GetManagerListWithRoleOnResponse struct { | ||||
| 	Managers *[]storage.ManagerRoleOn `json:"managers"` | ||||
| } | ||||
| 
 | ||||
| type GetLogRequest struct { | ||||
| 	Level storage.LogLevel `json:"level"` | ||||
| 	Since int64            `json:"since"` | ||||
| @ -263,6 +267,11 @@ func (w *CreateWorkerAccessRequest) isValid() bool { | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| type SetManagerRoleOnProjectRequest struct { | ||||
| 	Manager int64               `json:"manager"` | ||||
| 	Role    storage.ManagerRole `json:"role"` | ||||
| } | ||||
| 
 | ||||
| type Info struct { | ||||
| 	Name    string `json:"name"` | ||||
| 	Version string `json:"version"` | ||||
|  | ||||
| @ -100,7 +100,7 @@ func (api *WebAPI) CreateProject(r *Request) { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	api.Database.SetManagerRoleOn(manager.(*storage.Manager), id, | ||||
| 	api.Database.SetManagerRoleOn(manager.(*storage.Manager).Id, id, | ||||
| 		storage.ROLE_MANAGE_ACCESS|storage.ROLE_READ|storage.ROLE_EDIT) | ||||
| 	r.OkJson(JsonResponse{ | ||||
| 		Ok: true, | ||||
| @ -403,3 +403,35 @@ func (api *WebAPI) RejectAccessRequest(r *Request) { | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (api *WebAPI) SetManagerRoleOnProject(r *Request) { | ||||
| 
 | ||||
| 	pid, err := strconv.ParseInt(r.Ctx.UserValue("id").(string), 10, 64) | ||||
| 	handleErr(err, r) //todo handle invalid id | ||||
| 
 | ||||
| 	req := &SetManagerRoleOnProjectRequest{} | ||||
| 	err = json.Unmarshal(r.Ctx.Request.Body(), req) | ||||
| 	if err != nil { | ||||
| 		r.Json(JsonResponse{ | ||||
| 			Ok:      false, | ||||
| 			Message: "Could not parse request", | ||||
| 		}, 400) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	sess := api.Session.StartFasthttp(r.Ctx) | ||||
| 	manager := sess.Get("manager") | ||||
| 
 | ||||
| 	if !isActionOnProjectAuthorized(pid, manager, storage.ROLE_MANAGE_ACCESS, api.Database) { | ||||
| 		r.Json(JsonResponse{ | ||||
| 			Message: "Unauthorized", | ||||
| 			Ok:      false, | ||||
| 		}, 403) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	api.Database.SetManagerRoleOn(req.Manager, pid, req.Role) | ||||
| 	r.OkJson(JsonResponse{ | ||||
| 		Ok: true, | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| @ -3,6 +3,7 @@ package storage | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"crypto" | ||||
| 	"database/sql" | ||||
| 	"errors" | ||||
| 	"github.com/Sirupsen/logrus" | ||||
| ) | ||||
| @ -23,6 +24,11 @@ type Manager struct { | ||||
| 	RegisterTime int64  `json:"register_time"` | ||||
| } | ||||
| 
 | ||||
| type ManagerRoleOn struct { | ||||
| 	Manager Manager     `json:"manager"` | ||||
| 	Role    ManagerRole `json:"role"` | ||||
| } | ||||
| 
 | ||||
| func (database *Database) ValidateCredentials(username []byte, password []byte) (*Manager, error) { | ||||
| 
 | ||||
| 	db := database.getDB() | ||||
| @ -142,20 +148,28 @@ func (database *Database) GetManagerRoleOn(manager *Manager, projectId int64) Ma | ||||
| 	return role | ||||
| } | ||||
| 
 | ||||
| func (database *Database) SetManagerRoleOn(manager *Manager, projectId int64, role ManagerRole) { | ||||
| func (database *Database) SetManagerRoleOn(manager int64, projectId int64, role ManagerRole) { | ||||
| 
 | ||||
| 	db := database.getDB() | ||||
| 
 | ||||
| 	res, err := db.Exec(`INSERT INTO manager_has_role_on_project (manager, role, project)  | ||||
| 		VALUES ($1,$2,$3) ON CONFLICT (manager, project) DO UPDATE SET role=$2`, | ||||
| 		manager.Id, role, projectId) | ||||
| 	handleErr(err) | ||||
| 	var res sql.Result | ||||
| 	var err error | ||||
| 
 | ||||
| 	if role == 0 { | ||||
| 		res, err = db.Exec(`DELETE FROM manager_has_role_on_project WHERE manager=$1 AND project=$2`, | ||||
| 			manager, projectId) | ||||
| 	} else { | ||||
| 		res, err = db.Exec(`INSERT INTO manager_has_role_on_project (manager, role, project)  | ||||
| 		VALUES ($1,$2,$3) ON CONFLICT (manager, project) DO UPDATE SET role=$2`, | ||||
| 			manager, role, projectId) | ||||
| 	} | ||||
| 
 | ||||
| 	handleErr(err) | ||||
| 	rowsAffected, _ := res.RowsAffected() | ||||
| 
 | ||||
| 	logrus.WithFields(logrus.Fields{ | ||||
| 		"role":         role, | ||||
| 		"manager":      manager.Username, | ||||
| 		"manager":      manager, | ||||
| 		"rowsAffected": rowsAffected, | ||||
| 		"project":      projectId, | ||||
| 	}).Info("Set manager role on project") | ||||
| @ -177,3 +191,29 @@ func (database *Database) GetManagerList() *[]Manager { | ||||
| 
 | ||||
| 	return &managers | ||||
| } | ||||
| 
 | ||||
| func (database *Database) GetManagerListWithRoleOn(project int64) *[]ManagerRoleOn { | ||||
| 
 | ||||
| 	db := database.getDB() | ||||
| 
 | ||||
| 	rows, err := db.Query(`SELECT id, register_time, tracker_admin, username, role  | ||||
| 		FROM manager | ||||
| 		LEFT JOIN manager_has_role_on_project mhrop on | ||||
| 		  manager.id = mhrop.manager | ||||
| 		WHERE project=$1 ORDER BY id`, project) | ||||
| 	handleErr(err) | ||||
| 
 | ||||
| 	managers := make([]ManagerRoleOn, 0) | ||||
| 
 | ||||
| 	for rows.Next() { | ||||
| 		m := Manager{} | ||||
| 		var role ManagerRole | ||||
| 		_ = rows.Scan(&m.Id, &m.RegisterTime, &m.WebsiteAdmin, &m.Username, &role) | ||||
| 		managers = append(managers, ManagerRoleOn{ | ||||
| 			Manager: m, | ||||
| 			Role:    role, | ||||
| 		}) | ||||
| 	} | ||||
| 
 | ||||
| 	return &managers | ||||
| } | ||||
|  | ||||
| @ -69,10 +69,14 @@ export class ApiService { | ||||
|         return this.http.get(this.url + `/project/access_list/${project}`) | ||||
|     } | ||||
| 
 | ||||
|     getAllManagers() { | ||||
|     getManagerList() { | ||||
|         return this.http.get(this.url + "/manager/list") | ||||
|     } | ||||
| 
 | ||||
|     getManagerListWithRoleOn(project: number) { | ||||
|         return this.http.get(this.url + "/manager/list_for_project/" + project) | ||||
|     } | ||||
| 
 | ||||
|     promote(managerId: number) { | ||||
|         return this.http.get(this.url + `/manager/promote/${managerId}`) | ||||
|     } | ||||
| @ -89,4 +93,9 @@ export class ApiService { | ||||
|         return this.http.post(this.url + `/project/reject_request/${pid}/${wid}`, null) | ||||
|     } | ||||
| 
 | ||||
|     setManagerRoleOnProject(pid: number, role: number, manager: number) { | ||||
|         return this.http.post(this.url + `/manager/set_role_for_project/${pid}`, | ||||
|             {"role": role, "manager": manager}) | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -1,7 +1,3 @@ | ||||
| .nav-spacer { | ||||
|     flex: 1 1 auto; | ||||
| } | ||||
| 
 | ||||
| .icon { | ||||
|     padding: 0 14px; | ||||
| } | ||||
|  | ||||
| @ -38,7 +38,7 @@ | ||||
|             >{{"nav.manager_list" | translate}}</button> | ||||
|         </mat-menu> | ||||
|     </div> | ||||
|     <span class="nav-spacer"></span> | ||||
|     <span class="spacer"></span> | ||||
|     <button mat-button [class.mat-accent]="router.url == '/login'" | ||||
|             class="nav-link" *ngIf="!authService.account" | ||||
|             [routerLink]="'login'">{{"nav.login" | translate}}</button> | ||||
|  | ||||
| @ -50,6 +50,8 @@ import {WorkerDashboardComponent} from './worker-dashboard/worker-dashboard.comp | ||||
| import {ProjectPermsComponent} from './project-perms/project-perms.component'; | ||||
| import {ManagerListComponent} from './manager-list/manager-list.component'; | ||||
| import {ProjectSelectComponent} from './project-select/project-select.component'; | ||||
| import {ManagerSelectComponent} from './manager-select/manager-select.component'; | ||||
| import {ProjectIconComponent} from './project-icon/project-icon.component'; | ||||
| 
 | ||||
| 
 | ||||
| export function createTranslateLoader(http: HttpClient) { | ||||
| @ -72,6 +74,8 @@ export function createTranslateLoader(http: HttpClient) { | ||||
|         ProjectPermsComponent, | ||||
|         ManagerListComponent, | ||||
|         ProjectSelectComponent, | ||||
|         ManagerSelectComponent, | ||||
|         ProjectIconComponent, | ||||
|     ], | ||||
|     imports: [ | ||||
|         BrowserModule, | ||||
|  | ||||
| @ -3,6 +3,7 @@ import {ApiService} from "./api.service"; | ||||
| import {Credentials} from "./models/credentials"; | ||||
| import {MessengerService} from "./messenger.service"; | ||||
| import {Router} from "@angular/router"; | ||||
| import {Manager} from "./models/manager"; | ||||
| 
 | ||||
| @Injectable({ | ||||
|     providedIn: 'root' | ||||
|  | ||||
| @ -6,13 +6,20 @@ | ||||
|         <mat-card-content> | ||||
|             <mat-form-field appearance="outline"> | ||||
|                 <mat-label>{{"project.name" | translate}}</mat-label> | ||||
|                 <input type="text" matInput [(ngModel)]="project.name" [placeholder]="'project.name' | translate"> | ||||
|                 <input type="text" matInput [(ngModel)]="project.name" [placeholder]="'project.name' | translate" | ||||
|                        required> | ||||
|             </mat-form-field> | ||||
| 
 | ||||
|             <mat-form-field appearance="outline"> | ||||
|                 <mat-label>{{ "project.clone_url" | translate}}</mat-label> | ||||
|                 <input type="text" matInput [(ngModel)]="project.clone_url" (change)="cloneUrlChange()" | ||||
|                        [placeholder]="'project.clone_url_placeholder' | translate"> | ||||
|                        [placeholder]="'project.clone_url_placeholder' | translate" required> | ||||
|             </mat-form-field> | ||||
|             <mat-form-field appearance="outline"> | ||||
|                 <mat-label>{{"project.version" | translate}}</mat-label> | ||||
|                 <input type="text" matInput [(ngModel)]="project.version" name="version" | ||||
|                        [placeholder]="'project.version'|translate" | ||||
|                 > | ||||
|             </mat-form-field> | ||||
|             <mat-form-field appearance="outline"> | ||||
|                 <mat-label>{{ "project.git_repo" | translate }}</mat-label> | ||||
|  | ||||
| @ -55,7 +55,7 @@ export class ManagerListComponent implements OnInit { | ||||
|     } | ||||
| 
 | ||||
|     private getManagers() { | ||||
|         this.apiService.getAllManagers() | ||||
|         this.apiService.getManagerList() | ||||
|             .subscribe(data => { | ||||
|                     this.data.data = data["content"]["managers"] | ||||
|                 }, | ||||
|  | ||||
| @ -0,0 +1,3 @@ | ||||
| .mat-form-field { | ||||
|     width: 100%; | ||||
| } | ||||
| @ -0,0 +1,16 @@ | ||||
| <mat-form-field appearance="outline" style="margin-top: 1em"> | ||||
|     <mat-label>{{"project.manager_select" | translate}}</mat-label> | ||||
|     <mat-select [(ngModel)]="manager" (selectionChange)="managerChange.emit($event.value)" | ||||
|                 [placeholder]="'perms.manager_select' | translate" | ||||
|                 (opened)="loadManagerList()"> | ||||
|         <mat-select-trigger></mat-select-trigger> | ||||
|         <mat-option disabled *ngIf="managerList == undefined"> | ||||
|             {{"project_select.loading" | translate}} | ||||
|         </mat-option> | ||||
|         <mat-option *ngFor="let m of managerList" [value]="m"> | ||||
|             <mat-icon *ngIf="m.tracker_admin">supervisor_account</mat-icon> | ||||
|             <mat-icon *ngIf="!m.tracker_admin">person</mat-icon> | ||||
|             {{m.username}} | ||||
|         </mat-option> | ||||
|     </mat-select> | ||||
| </mat-form-field> | ||||
| @ -0,0 +1,30 @@ | ||||
| import {Component, EventEmitter, OnInit, Output} from '@angular/core'; | ||||
| import {ApiService} from "../api.service"; | ||||
| import {Manager} from "../models/manager"; | ||||
| 
 | ||||
| @Component({ | ||||
|     selector: 'manager-select', | ||||
|     templateUrl: './manager-select.component.html', | ||||
|     styleUrls: ['./manager-select.component.css'] | ||||
| }) | ||||
| export class ManagerSelectComponent implements OnInit { | ||||
| 
 | ||||
|     manager: Manager; | ||||
|     managerList: Manager[]; | ||||
| 
 | ||||
|     @Output() | ||||
|     managerChange = new EventEmitter<Manager>(); | ||||
| 
 | ||||
|     constructor(private apiService: ApiService) { | ||||
|     } | ||||
| 
 | ||||
|     ngOnInit() { | ||||
|     } | ||||
| 
 | ||||
|     loadManagerList() { | ||||
|         this.apiService.getManagerList() | ||||
|             .subscribe(data => this.managerList = data["content"]["managers"]) | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| @ -1,6 +1,54 @@ | ||||
| interface Manager { | ||||
| export interface Manager { | ||||
|     id: number; | ||||
|     username: string | ||||
|     tracker_admin: boolean | ||||
|     register_time: number | ||||
| } | ||||
| 
 | ||||
| export class ManagerRoleOnProject { | ||||
|     manager: Manager; | ||||
|     role: number; | ||||
| 
 | ||||
|     public static fromEntity(data: { role: number, manager: Manager }): ManagerRoleOnProject { | ||||
|         let m = new ManagerRoleOnProject(); | ||||
|         m.role = data.role; | ||||
|         m.manager = data.manager; | ||||
|         return m; | ||||
|     } | ||||
| 
 | ||||
|     get readRole(): boolean { | ||||
|         return (this.role & 1) != 0 | ||||
|     } | ||||
| 
 | ||||
|     set readRole(role: boolean) { | ||||
|         if (role) { | ||||
|             this.role |= 1 | ||||
|         } else { | ||||
|             this.role &= ~1 | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     get editRole(): boolean { | ||||
|         return (this.role & 2) != 0 | ||||
|     } | ||||
| 
 | ||||
|     set editRole(role: boolean) { | ||||
|         if (role) { | ||||
|             this.role |= 2 | ||||
|         } else { | ||||
|             this.role &= ~2 | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     get manageRole(): boolean { | ||||
|         return (this.role & 4) != 0 | ||||
|     } | ||||
| 
 | ||||
|     set manageRole(role: boolean) { | ||||
|         if (role) { | ||||
|             this.role |= 4 | ||||
|         } else { | ||||
|             this.role &= ~4 | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -5,6 +5,8 @@ import {ActivatedRoute} from "@angular/router"; | ||||
| 
 | ||||
| import {Chart} from "chart.js"; | ||||
| import {AssignedTasks, MonitoringSnapshot} from "../models/monitoring"; | ||||
| import {TranslateService} from "@ngx-translate/core"; | ||||
| import {MessengerService} from "../messenger.service"; | ||||
| 
 | ||||
| 
 | ||||
| @Component({ | ||||
| @ -41,7 +43,10 @@ export class ProjectDashboardComponent implements OnInit { | ||||
|     lastSnapshot: MonitoringSnapshot; | ||||
|     assignees: AssignedTasks[]; | ||||
| 
 | ||||
|     constructor(private apiService: ApiService, private route: ActivatedRoute) { | ||||
|     constructor(private apiService: ApiService, | ||||
|                 private route: ActivatedRoute, | ||||
|                 private translate: TranslateService, | ||||
|                 private messenger: MessengerService) { | ||||
|     } | ||||
| 
 | ||||
|     ngOnInit(): void { | ||||
| @ -305,28 +310,32 @@ export class ProjectDashboardComponent implements OnInit { | ||||
| 
 | ||||
|     private getProject() { | ||||
|         this.apiService.getProject(this.projectId).subscribe((data: any) => { | ||||
|             this.project = data.content.project; | ||||
|                 this.project = data.content.project; | ||||
| 
 | ||||
|             this.apiService.getMonitoringSnapshots(60, this.projectId) | ||||
|                 .subscribe((data: any) => { | ||||
|                     this.snapshots = data.content.snapshots; | ||||
|                     this.lastSnapshot = this.snapshots ? this.snapshots.sort((a, b) => { | ||||
|                         return b.time_stamp - a.time_stamp | ||||
|                     })[0] : null; | ||||
|                 this.apiService.getMonitoringSnapshots(60, this.projectId) | ||||
|                     .subscribe((data: any) => { | ||||
|                         this.snapshots = data.content.snapshots; | ||||
|                         this.lastSnapshot = this.snapshots ? this.snapshots.sort((a, b) => { | ||||
|                             return b.time_stamp - a.time_stamp | ||||
|                         })[0] : null; | ||||
| 
 | ||||
|                     this.setupTimeline(); | ||||
|                     this.setupStatusPie(); | ||||
|                         this.setupTimeline(); | ||||
|                         this.setupStatusPie(); | ||||
| 
 | ||||
|                     if (!this.snapshots) { | ||||
|                         return | ||||
|                     } | ||||
|                         if (!this.snapshots) { | ||||
|                             return | ||||
|                         } | ||||
| 
 | ||||
|                     this.apiService.getAssigneeStats(this.projectId) | ||||
|                         .subscribe((data: any) => { | ||||
|                             this.assignees = data.content.assignees; | ||||
|                             this.setupAssigneesPie(); | ||||
|                         }); | ||||
|                 }) | ||||
|         }) | ||||
|                         this.apiService.getAssigneeStats(this.projectId) | ||||
|                             .subscribe((data: any) => { | ||||
|                                 this.assignees = data.content.assignees; | ||||
|                                 this.setupAssigneesPie(); | ||||
|                             }); | ||||
|                     }) | ||||
|             }, | ||||
|             error => { | ||||
|                 this.translate.get("messenger.unauthorized").subscribe(t => | ||||
|                     this.messenger.show(t)) | ||||
|             }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -0,0 +1,3 @@ | ||||
| <mat-icon *ngIf="project.public" [title]="'project.public' | translate">public</mat-icon> | ||||
| <mat-icon *ngIf="!project.public && !project.hidden" [title]="'project.private'|translate">lock</mat-icon> | ||||
| <mat-icon *ngIf="project.hidden" [title]="'project.hidden'|translate">visibility_off</mat-icon> | ||||
							
								
								
									
										21
									
								
								web/angular/src/app/project-icon/project-icon.component.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								web/angular/src/app/project-icon/project-icon.component.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | ||||
| import {Component, Input, OnInit} from '@angular/core'; | ||||
| import {Project} from "../models/project"; | ||||
| 
 | ||||
| @Component({ | ||||
|     selector: 'project-icon', | ||||
|     templateUrl: './project-icon.component.html', | ||||
|     styleUrls: ['./project-icon.component.css'] | ||||
| }) | ||||
| export class ProjectIconComponent implements OnInit { | ||||
| 
 | ||||
| 
 | ||||
|     @Input() | ||||
|     project: Project; | ||||
| 
 | ||||
|     constructor() { | ||||
|     } | ||||
| 
 | ||||
|     ngOnInit() { | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -2,6 +2,6 @@ button { | ||||
|     margin-right: 15px; | ||||
| } | ||||
| 
 | ||||
| mat-panel-title > mat-icon { | ||||
| mat-panel-title > project-icon { | ||||
|     margin-right: 1em; | ||||
| } | ||||
|  | ||||
| @ -13,11 +13,7 @@ | ||||
|                 <mat-expansion-panel *ngFor="let project of projects" style="margin-top: 1em"> | ||||
|                     <mat-expansion-panel-header> | ||||
|                         <mat-panel-title> | ||||
|                             <mat-icon *ngIf="project.public" [title]="'project.public' | translate">public</mat-icon> | ||||
|                             <mat-icon *ngIf="!project.public && !project.hidden" [title]="'project.private'|translate"> | ||||
|                                 lock | ||||
|                             </mat-icon> | ||||
|                             <mat-icon *ngIf="project.hidden" [title]="'project.hidden'|translate">block</mat-icon> | ||||
|                             <project-icon [project]="project"></project-icon> | ||||
|                             <span style="width: 3em">{{project.id}}</span>{{project.name}} | ||||
|                         </mat-panel-title> | ||||
|                         <mat-panel-description>{{project.motd}}</mat-panel-description> | ||||
|  | ||||
| @ -6,3 +6,7 @@ button { | ||||
| .request { | ||||
|     color: #757575; | ||||
| } | ||||
| 
 | ||||
| mat-checkbox { | ||||
|     margin-right: 10px; | ||||
| } | ||||
|  | ||||
| @ -9,8 +9,9 @@ | ||||
|             <mat-card-subtitle>{{"perms.subtitle" | translate}}</mat-card-subtitle> | ||||
|         </mat-card-header> | ||||
| 
 | ||||
|         <mat-card-content> | ||||
|             <mat-list *ngIf="!unauthorized"> | ||||
|         <mat-card-content *ngIf="!unauthorized || !auth.account"> | ||||
|             <h3>{{"perms.workers" | translate}}</h3> | ||||
|             <mat-list *ngIf="accesses && accesses.length>0"> | ||||
|                 <mat-list-item *ngFor="let wa of accesses" [class.request]="wa.request"> | ||||
|                     <mat-icon mat-list-icon *ngIf="wa.submit" [title]="'perms.assign'|translate">library_add</mat-icon> | ||||
|                     <mat-icon mat-list-icon *ngIf="wa.assign" [title]="'perms.submit'|translate">get_app</mat-icon> | ||||
| @ -32,7 +33,33 @@ | ||||
|                     </button> | ||||
|                 </mat-list-item> | ||||
|             </mat-list> | ||||
|             <p *ngIf="unauthorized" class="unauthorized"> | ||||
|             <p *ngIf="!accesses || accesses.length == 0">{{"perms.no_workers"|translate}}</p> | ||||
| 
 | ||||
|             <h3>{{"perms.managers" | translate}}</h3> | ||||
|             <manager-select (managerChange)="onSelectManager($event)"></manager-select> | ||||
|             <mat-list> | ||||
|                 <mat-list-item *ngFor="let m of managerRoles"> | ||||
|                     <mat-icon *ngIf="m.manager.tracker_admin">supervisor_account</mat-icon> | ||||
|                     <mat-icon *ngIf="!m.manager.tracker_admin">person</mat-icon> | ||||
|                     {{m.manager.username}} | ||||
|                     <span class="spacer"></span> | ||||
|                     <mat-checkbox [(ngModel)]="m.readRole" | ||||
|                                   (change)="onRoleChange(m)" | ||||
|                                   [disabled]="m.manager.id==auth.account.id" | ||||
|                     >{{"perms.read"|translate}}</mat-checkbox> | ||||
|                     <mat-checkbox [(ngModel)]="m.editRole" | ||||
|                                   (change)="onRoleChange(m)" | ||||
|                                   [disabled]="m.manager.id==auth.account.id" | ||||
|                     >{{"perms.edit"|translate}}</mat-checkbox> | ||||
|                     <mat-checkbox [(ngModel)]="m.manageRole" | ||||
|                                   (change)="onRoleChange(m)" | ||||
|                                   [disabled]="m.manager.id==auth.account.id" | ||||
|                     >{{"perms.manage"|translate}}</mat-checkbox> | ||||
|                 </mat-list-item> | ||||
|             </mat-list> | ||||
|         </mat-card-content> | ||||
|         <mat-card-content *ngIf="unauthorized"> | ||||
|             <p class="unauthorized"> | ||||
|                 <mat-icon>block</mat-icon> | ||||
|                 {{"perms.unauthorized" | translate}} | ||||
|             </p> | ||||
|  | ||||
| @ -1,12 +1,14 @@ | ||||
| import {Component, OnInit} from '@angular/core'; | ||||
| import {ApiService} from "../api.service"; | ||||
| import {Project} from "../models/project"; | ||||
| import {ActivatedRoute, Router} from "@angular/router"; | ||||
| import {MessengerService} from "../messenger.service"; | ||||
| import {TranslateService} from "@ngx-translate/core"; | ||||
| import {ActivatedRoute} from "@angular/router"; | ||||
| 
 | ||||
| import * as moment from "moment" | ||||
| import {WorkerAccess} from "../models/worker-access"; | ||||
| import {AuthService} from "../auth.service"; | ||||
| import {Manager, ManagerRoleOnProject} from "../models/manager"; | ||||
| import {MessengerService} from "../messenger.service"; | ||||
| import {TranslateService} from "@ngx-translate/core"; | ||||
| 
 | ||||
| @Component({ | ||||
|     selector: 'app-project-perms', | ||||
| @ -17,14 +19,15 @@ export class ProjectPermsComponent implements OnInit { | ||||
| 
 | ||||
|     constructor(private apiService: ApiService, | ||||
|                 private route: ActivatedRoute, | ||||
|                 private messengerService: MessengerService, | ||||
|                 private translate: TranslateService, | ||||
|                 private router: Router) { | ||||
|                 private messenger: MessengerService, | ||||
|                 public auth: AuthService) { | ||||
|     } | ||||
| 
 | ||||
|     project: Project; | ||||
|     private projectId: number; | ||||
|     accesses: WorkerAccess[]; | ||||
|     managerRoles: ManagerRoleOnProject; | ||||
|     unauthorized: boolean = false; | ||||
|     moment = moment; | ||||
| 
 | ||||
| @ -33,17 +36,24 @@ export class ProjectPermsComponent implements OnInit { | ||||
|             this.projectId = params["id"]; | ||||
|             this.getProject(); | ||||
|             this.getProjectAccesses(); | ||||
|             this.getProjectManagers(); | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     public acceptRequest(wa: WorkerAccess) { | ||||
|         this.apiService.acceptWorkerAccessRequest(wa.worker.id, this.projectId) | ||||
|             .subscribe(() => this.getProjectAccesses()) | ||||
|             .subscribe(() => { | ||||
|                 this.getProjectAccesses(); | ||||
|                 this.translate.get("perms.set").subscribe(t => this.messenger.show(t)); | ||||
|             }) | ||||
|     } | ||||
| 
 | ||||
|     public rejectRequest(wa: WorkerAccess) { | ||||
|         this.apiService.rejectWorkerAccessRequest(wa.worker.id, this.projectId) | ||||
|             .subscribe(() => this.getProjectAccesses()) | ||||
|             .subscribe(() => { | ||||
|                 this.getProjectAccesses(); | ||||
|                 this.translate.get("perms.set").subscribe(t => this.messenger.show(t)); | ||||
|             }) | ||||
|     } | ||||
| 
 | ||||
|     private getProject() { | ||||
| @ -64,7 +74,31 @@ export class ProjectPermsComponent implements OnInit { | ||||
|             }) | ||||
|     } | ||||
| 
 | ||||
|     private getProjectManagers() { | ||||
|         this.apiService.getManagerListWithRoleOn(this.projectId) | ||||
|             .subscribe(data => { | ||||
|                 this.managerRoles = data["content"]["managers"].map(d => | ||||
|                     ManagerRoleOnProject.fromEntity(d)) | ||||
|             }) | ||||
|     } | ||||
| 
 | ||||
|     public refresh() { | ||||
|         this.getProjectAccesses() | ||||
|         this.getProjectAccesses(); | ||||
|         this.getProjectManagers(); | ||||
|     } | ||||
| 
 | ||||
|     public onSelectManager(manager: Manager) { | ||||
|         if (manager.id != this.auth.account.id) { | ||||
|             this.apiService.setManagerRoleOnProject(this.projectId, 1, manager.id) | ||||
|                 .subscribe(() => this.refresh()) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public onRoleChange(manager: ManagerRoleOnProject) { | ||||
|         this.apiService.setManagerRoleOnProject(this.projectId, manager.role, manager.manager.id) | ||||
|             .subscribe(() => { | ||||
|                 this.refresh(); | ||||
|                 this.translate.get("perms.set").subscribe(t => this.messenger.show(t)); | ||||
|             }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -11,8 +11,7 @@ | ||||
|             {{"project_select.none" | translate}} | ||||
|         </mat-option> | ||||
|         <mat-option *ngFor="let p of projectList" [value]="p"> | ||||
|             <mat-icon *ngIf="p.public">public</mat-icon> | ||||
|             <mat-icon *ngIf="!p.public">lock</mat-icon> | ||||
|             <project-icon [project]="p"></project-icon> | ||||
|             <span style="width: 3em; display: inline-block">{{p.id}}</span> | ||||
|             {{p.name}} | ||||
|         </mat-option> | ||||
|  | ||||
| @ -5,25 +5,37 @@ | ||||
|         <mat-card-content> | ||||
|             <form (ngSubmit)="onSubmit()" *ngIf="project != undefined" id="uf"> | ||||
|                 <mat-form-field appearance="outline"> | ||||
|                     <input type="text" matInput [(ngModel)]="project.name" name="name" placeholder="Name"> | ||||
|                     <mat-label>{{"project.name" | translate}}</mat-label> | ||||
|                     <input type="text" matInput [(ngModel)]="project.name" name="name" | ||||
|                            [placeholder]="'project.name'|translate" | ||||
|                     > | ||||
|                 </mat-form-field> | ||||
| 
 | ||||
|                 <mat-form-field appearance="outline"> | ||||
|                     <textarea matInput [(ngModel)]="project.motd" placeholder="Message of the day" | ||||
|                     <mat-label>{{"project.motd" | translate}}</mat-label> | ||||
|                     <textarea matInput [(ngModel)]="project.motd" | ||||
|                               [placeholder]="'project.motd'|translate" | ||||
|                               name="motd"></textarea> | ||||
|                 </mat-form-field> | ||||
| 
 | ||||
|                 <mat-form-field appearance="outline"> | ||||
|                     <mat-label>{{"project.clone_url" | translate}}</mat-label> | ||||
|                     <input type="text" matInput [(ngModel)]="project.clone_url" name="clone_url" | ||||
|                            placeholder="Git clone url"> | ||||
|                            [placeholder]="'project.clone_url'|translate" | ||||
|                     > | ||||
|                 </mat-form-field> | ||||
|                 <mat-form-field appearance="outline"> | ||||
|                     <mat-label>{{"project.version" | translate}}</mat-label> | ||||
|                     <input type="text" matInput [(ngModel)]="project.version" name="version" | ||||
|                            [placeholder]="'project.version'|translate" | ||||
|                     > | ||||
|                 </mat-form-field> | ||||
|                 <mat-form-field appearance="outline"> | ||||
|                     <mat-label>{{"project.git_repo" | translate}}</mat-label> | ||||
|                     <input type="text" matInput [(ngModel)]="project.git_repo" name="git_repo" | ||||
|                            placeholder='Full repository name (e.g. "simon987/task_tracker")'> | ||||
|                     <mat-hint align="start">Changes on the <strong>master</strong> branch will be tracked if webhooks | ||||
|                         are | ||||
|                         enabled | ||||
|                     </mat-hint> | ||||
|                            [placeholder]="'project.git_repo_placeholder'|translate" | ||||
|                     > | ||||
|                     <mat-hint align="start">{{'project.git_repo_hint'|translate}}</mat-hint> | ||||
|                 </mat-form-field> | ||||
|                 <project-select [(project)]="selectedProject"></project-select> | ||||
|             </form> | ||||
|  | ||||
| @ -51,7 +51,7 @@ | ||||
|     }, | ||||
|     "project": { | ||||
|         "name": "Project name", | ||||
|         "clone_url": "Clone url", | ||||
|         "clone_url": "Git clone url", | ||||
|         "clone_url_placeholder": "Example: \"https://github.com/simon987/task_tracker\"", | ||||
|         "git_repo_placeholder": "Example: \"simon987/task_tracker\"", | ||||
|         "git_repo_hint": "Changes in the master branch will be tracked if webhooks are enabled", | ||||
| @ -65,7 +65,9 @@ | ||||
|         "motd": "Message of the day", | ||||
|         "update": "Edit", | ||||
|         "perms": "Permissions", | ||||
|         "chain": "Chain tasks to" | ||||
|         "chain": "Chain tasks to", | ||||
|         "manager_select": "Give access to manager", | ||||
|         "version": "Git version (commit hash)" | ||||
|     }, | ||||
|     "dashboard": { | ||||
|         "title": "Dashboard for", | ||||
| @ -107,7 +109,14 @@ | ||||
|         "refresh": "Refresh", | ||||
|         "pending": "(Pending)", | ||||
|         "assign": "Assign", | ||||
|         "submit": "Submit" | ||||
|         "submit": "Submit", | ||||
|         "read": "Read", | ||||
|         "edit": "Edit", | ||||
|         "manage": "Manage permissions", | ||||
|         "set": "Changes saved", | ||||
|         "workers": "Workers", | ||||
|         "no_workers": "No workers have explicit access to this project", | ||||
|         "managers": "Managers" | ||||
|     }, | ||||
|     "messenger": { | ||||
|         "close": "Close", | ||||
|  | ||||
| @ -66,7 +66,9 @@ | ||||
|         "motd": "Message du jour", | ||||
|         "update": "Mettre à jour", | ||||
|         "perms": "Permissions", | ||||
|         "chain": "Enchainer les tâches vers" | ||||
|         "chain": "Enchainer les tâches vers", | ||||
|         "manager_select": "Donner accès à", | ||||
|         "version": "Version git (hash du commit)" | ||||
|     }, | ||||
|     "dashboard": { | ||||
|         "title": "Tableau de bord pour ", | ||||
| @ -100,7 +102,7 @@ | ||||
|     }, | ||||
|     "perms": { | ||||
|         "title": "Permissions du projet", | ||||
|         "subtitle": "Les Workers doivent faire un requête d'acces pour pouvoir travailler sur les projets privés", | ||||
|         "subtitle": "Les Workers doivent faire une requête d'acces pour pouvoir travailler sur les projets privés", | ||||
|         "unauthorized": "Vous devez avoir ROLE_GESTION_ACCES sur ce project pour accéder à cette page", | ||||
|         "created": "Créé le", | ||||
|         "grant": "Accepter la requête", | ||||
| @ -109,7 +111,14 @@ | ||||
|         "refresh": "Refraichir", | ||||
|         "pending": "(En attente)", | ||||
|         "assign": "Assigner", | ||||
|         "submit": "Soumettre" | ||||
|         "submit": "Soumettre", | ||||
|         "read": "Lecture", | ||||
|         "edit": "Modification", | ||||
|         "manage": "Gestion des permissions", | ||||
|         "set": "Changements enregistrés", | ||||
|         "workers": "Workers", | ||||
|         "no_workers": "Aucun Worker n'a explicitement accès à ce projet", | ||||
|         "managers": "Managers" | ||||
|     }, | ||||
|     "messenger": { | ||||
|         "close": "Fermer", | ||||
|  | ||||
| @ -99,3 +99,12 @@ pre { | ||||
|     color: #ff4081; | ||||
| } | ||||
| 
 | ||||
| .spacer { | ||||
|     flex: 1 1 auto; | ||||
| } | ||||
| 
 | ||||
| .mat-list-item-content > mat-icon { | ||||
|     margin-right: 15px; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user