several fixes and improvements

- Add projector logo/headerimage
- Fixed opening multiselect action menu for users.
- Used defined toolbar color for multiselect mode.
- (server) Added check for 'request.data._mutable = True' to edit user
  profile as normal user.
This commit is contained in:
Emanuel Schütze 2019-02-18 11:26:47 +01:00
parent cc45525678
commit c9c4566c08
7 changed files with 76 additions and 61 deletions

View File

@ -35,5 +35,5 @@
} }
mat-toolbar.multi-select { mat-toolbar.multi-select {
background-color: #757575; background-color: #757575 !important;
} }

View File

@ -1,7 +1,9 @@
<div id="container" [osResized]="resizeSubject" [ngStyle]="containerStyle" #container> <div id="container" [osResized]="resizeSubject" [ngStyle]="containerStyle" #container>
<div id="projector" [ngStyle]="projectorStyle"> <div id="projector" [ngStyle]="projectorStyle">
<div id="header" [ngStyle]="headerFooterStyle" *ngIf="enableHeaderAndFooter"> <div id="header" [ngStyle]="headerFooterStyle" *ngIf="enableHeaderAndFooter">
<!-- TODO: Logo <img *ngIf="enableLogo" id="logo"> --> <!-- projector logo -->
<os-logo *ngIf="enableLogo" inputAction="logo_projector_main" class="projector-logo-main"> </os-logo>
<div *ngIf="enableTitle" id="eventdata"> <div *ngIf="enableTitle" id="eventdata">
<div <div
*ngIf="eventName" *ngIf="eventName"

View File

@ -21,12 +21,12 @@
margin-bottom: 20px; margin-bottom: 20px;
z-index: 1; z-index: 1;
#logo { .projector-logo-main {
height: 50px;
padding-left: 50px; padding-left: 50px;
padding-top: 10px; padding-top: 10px;
height: 50px;
margin-right: 25px;
float: left; float: left;
display: flex;
} }
#eventdata { #eventdata {

View File

@ -157,20 +157,33 @@ export class ProjectorComponent extends BaseComponent implements OnDestroy {
super(titleService, translate); super(titleService, translate);
// Get all important config variables. // Get all important config variables.
// enable header/footer
this.configService this.configService
.get<boolean>('projector_enable_header_footer') .get<boolean>('projector_enable_header_footer')
.subscribe(val => (this.enableHeaderAndFooter = val)); .subscribe(val => (this.enableHeaderAndFooter = val));
this.configService.get<boolean>('projector_enable_title').subscribe(val => (this.enableTitle = val)); this.configService.get<boolean>('projector_enable_title').subscribe(val => (this.enableTitle = val));
// projector colors
this.configService this.configService
.get<string>('projector_header_fontcolor') .get<string>('projector_header_fontcolor')
.subscribe(val => (this.headerFooterStyle.color = val)); .subscribe(val => (this.headerFooterStyle.color = val));
this.configService this.configService
.get<string>('projector_header_backgroundcolor') .get<string>('projector_header_backgroundcolor')
.subscribe(val => (this.headerFooterStyle['background-color'] = val)); .subscribe(val => (this.headerFooterStyle['background-color'] = val));
this.configService.get<boolean>('projector_enable_logo').subscribe(val => (this.enableLogo = val));
this.configService this.configService
.get<string>('projector_background_color') .get<string>('projector_background_color')
.subscribe(val => (this.projectorStyle['background-color'] = val)); .subscribe(val => (this.projectorStyle['background-color'] = val));
// projector logo / background-image
this.configService.get<boolean>('projector_enable_logo').subscribe(val => (this.enableLogo = val));
this.configService.get<{ path?: string }>('logo_projector_header').subscribe(val => {
if (val && val.path) {
this.headerFooterStyle['background-image'] = "url('" + val.path + "')";
}
});
// event data
this.configService.get<string>('general_event_name').subscribe(val => (this.eventName = val)); this.configService.get<string>('general_event_name').subscribe(val => (this.eventName = val));
this.configService.get<string>('general_event_description').subscribe(val => (this.eventDescription = val)); this.configService.get<string>('general_event_description').subscribe(val => (this.eventDescription = val));
this.configService.get<string>('general_event_date').subscribe(val => (this.eventDate = val)); this.configService.get<string>('general_event_date').subscribe(val => (this.eventDate = val));

View File

@ -47,7 +47,6 @@
*ngIf="user" *ngIf="user"
(keydown)="onKeyDown($event)" (keydown)="onKeyDown($event)"
> >
<!-- <h3 translate>Personal Data</h3> -->
<div *ngIf="isAllowed('seeName')"> <div *ngIf="isAllowed('seeName')">
<!-- Title --> <!-- Title -->
<mat-form-field <mat-form-field
@ -106,7 +105,7 @@
</mat-form-field> </mat-form-field>
<!-- Gender --> <!-- Gender -->
<mat-form-field class="form25 force-min-with" *ngIf="user.gender || editUser"> <mat-form-field class="form25 force-min-with" *ngIf="user.gender || (editUser && isAllowed('manage'))">
<mat-select placeholder="{{ 'Gender' | translate }}" formControlName="gender"> <mat-select placeholder="{{ 'Gender' | translate }}" formControlName="gender">
<mat-option>-</mat-option> <mat-option>-</mat-option>
<mat-option *ngFor="let gender of genderList" [value]="gender">{{ gender | translate }}</mat-option> <mat-option *ngFor="let gender of genderList" [value]="gender">{{ gender | translate }}</mat-option>

View File

@ -124,72 +124,71 @@
<mat-icon>cloud_upload</mat-icon> <mat-icon>cloud_upload</mat-icon>
<span translate>Import</span><span>&nbsp;...</span> <span translate>Import</span><span>&nbsp;...</span>
</button> </button>
</div>
<div *ngIf="isMultiSelect">
<button mat-menu-item (click)="selectAll()">
<mat-icon>done_all</mat-icon>
<span translate>Select all</span>
</button>
<div *ngIf="isMultiSelect"> <button mat-menu-item (click)="deselectAll()">
<button mat-menu-item (click)="selectAll()"> <mat-icon>clear</mat-icon>
<mat-icon>done_all</mat-icon> <span translate>Deselect all</span>
<span translate>Select all</span> </button>
<div *osPerms="'users.can_manage'">
<mat-divider></mat-divider>
<button mat-menu-item (click)="setGroupSelected()">
<mat-icon>people</mat-icon>
<span translate>Add/remove groups ...</span>
</button> </button>
<button mat-menu-item (click)="deselectAll()"> <div *ngIf="presenceViewConfigured">
<mat-icon>clear</mat-icon> <button mat-menu-item *osPerms="'users.can_manage'" routerLink="presence">
<span translate>Deselect all</span> <mat-icon>transfer_within_a_station</mat-icon>
<span translate>Presence</span>
</button>
</div>
<button mat-menu-item *osPerms="'users.can_manage'" routerLink="import">
<mat-icon>save_alt</mat-icon>
<span translate>Import</span><span>&nbsp;...</span>
</button> </button>
<div *osPerms="'users.can_manage'"> <div *osPerms="'users.can_manage'">
<mat-divider></mat-divider> <mat-divider></mat-divider>
<button mat-menu-item (click)="setGroupSelected()">
<mat-icon>people</mat-icon> <button mat-menu-item (click)="setActiveSelected()">
<span translate>Add/remove groups ...</span> <mat-icon>block</mat-icon>
<span translate>Enable/disable account ...</span>
</button> </button>
<div *ngIf="presenceViewConfigured"> <button mat-menu-item (click)="setPresentSelected()">
<button mat-menu-item *osPerms="'users.can_manage'" routerLink="presence"> <mat-icon>check_box</mat-icon>
<mat-icon>transfer_within_a_station</mat-icon> <span translate>Set presence ...</span>
<span translate>Presence</span>
</button>
</div>
<button mat-menu-item *osPerms="'users.can_manage'" routerLink="import">
<mat-icon>save_alt</mat-icon>
<span translate>Import</span><span>&nbsp;...</span>
</button> </button>
<div *osPerms="'users.can_manage'"> <button mat-menu-item (click)="setCommitteeSelected()">
<mat-divider></mat-divider> <mat-icon>account_balance</mat-icon>
<span translate>Set committee ...</span>
</button>
<button mat-menu-item (click)="setActiveSelected()"> <mat-divider></mat-divider>
<mat-icon>block</mat-icon>
<span translate>Enable/disable account ...</span>
</button>
<button mat-menu-item (click)="setPresentSelected()"> <button mat-menu-item (click)="sendInvitationEmailSelected()">
<mat-icon>check_box</mat-icon> <mat-icon>mail</mat-icon>
<span translate>Set presence ...</span> <span translate>Send invitation email</span>
</button> </button>
<button mat-menu-item (click)="setCommitteeSelected()"> <button mat-menu-item (click)="resetPasswordsSelected()">
<mat-icon>account_balance</mat-icon> <mat-icon>vpn_key</mat-icon>
<span translate>Set committee ...</span> <span translate>Generate new passwords</span>
</button> </button>
<mat-divider></mat-divider>
<mat-divider></mat-divider> <button mat-menu-item class="red-warning-text" (click)="deleteSelected()">
<mat-icon>delete</mat-icon>
<button mat-menu-item (click)="sendInvitationEmailSelected()"> <span translate>Delete</span>
<mat-icon>mail</mat-icon> </button>
<span translate>Send invitation email</span>
</button>
<button mat-menu-item (click)="resetPasswordsSelected()">
<mat-icon>vpn_key</mat-icon>
<span translate>Generate new passwords</span>
</button>
<mat-divider></mat-divider>
<button mat-menu-item class="red-warning-text" (click)="deleteSelected()">
<mat-icon>delete</mat-icon>
<span translate>Delete</span>
</button>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -16,6 +16,7 @@ from django.contrib.sites.shortcuts import get_current_site
from django.core import mail from django.core import mail
from django.core.exceptions import ValidationError as DjangoValidationError from django.core.exceptions import ValidationError as DjangoValidationError
from django.db import transaction from django.db import transaction
from django.http.request import QueryDict
from django.utils.encoding import force_bytes, force_text from django.utils.encoding import force_bytes, force_text
from django.utils.http import urlsafe_base64_decode, urlsafe_base64_encode from django.utils.http import urlsafe_base64_decode, urlsafe_base64_encode
@ -114,7 +115,8 @@ class UserViewSet(ModelViewSet):
self.permission_denied(request) self.permission_denied(request)
# This is a hack to make request.data mutable. Otherwise fields can not be deleted. # This is a hack to make request.data mutable. Otherwise fields can not be deleted.
request.data._mutable = True if isinstance(request.data, QueryDict):
request.data._mutable = True
# Remove fields that the user is not allowed to change. # Remove fields that the user is not allowed to change.
# The list() is required because we want to use del inside the loop. # The list() is required because we want to use del inside the loop.
for key in list(request.data.keys()): for key in list(request.data.keys()):