Add set present toggle in user menu
adds a "is present" toggle to the user menu Refactor user menu into own component Add a config variable to determine if the user is allowed to set themselve as present
This commit is contained in:
parent
91be76a263
commit
39ccfe3147
@ -416,6 +416,13 @@ export class OperatorService implements OnAfterAppsLoaded {
|
||||
this.operatorSubject.next(this.user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the operators presence to isPresent
|
||||
*/
|
||||
public async setPresence(isPresent: boolean): Promise<void> {
|
||||
await this.http.post(environment.urlPrefix + '/users/setpresence/', isPresent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a default WhoAmI response
|
||||
*/
|
||||
|
@ -0,0 +1,74 @@
|
||||
<mat-expansion-panel class="user-menu mat-elevation-z0">
|
||||
<mat-expansion-panel-header class="username">
|
||||
<!-- Get the username from operator -->
|
||||
{{ username }}
|
||||
</mat-expansion-panel-header>
|
||||
<mat-nav-list>
|
||||
<!-- select languate -->
|
||||
<a mat-list-item [matMenuTriggerFor]="languageMenu">
|
||||
<mat-icon class="menu-icon">language</mat-icon>
|
||||
<span class="menu-text">{{ getLangName() }}</span>
|
||||
</a>
|
||||
<div *ngIf="user && isLoggedIn">
|
||||
<!-- present toggle -->
|
||||
<button
|
||||
[ngClass]="{ active: user.is_present }"
|
||||
mat-menu-item
|
||||
(click)="toggleUserIsPresent()"
|
||||
*ngIf="allowSelfSetPresent"
|
||||
>
|
||||
<mat-icon [color]="user.is_present ? 'primary' : ''" class="menu-icon">
|
||||
{{ user.is_present ? 'check_box' : 'check_box_outline_blank' }}
|
||||
</mat-icon>
|
||||
<span class="menu-text" translate>Present</span>
|
||||
</button>
|
||||
<!-- Show profile -->
|
||||
<a
|
||||
[ngClass]="{ active: isOnProfilePage() }"
|
||||
[routerLink]="user ? ['/users/', user.id] : []"
|
||||
(click)="onClickNavEntry()"
|
||||
mat-list-item
|
||||
>
|
||||
<mat-icon class="menu-icon">person</mat-icon>
|
||||
<span class="menu-text" translate>Show profile</span>
|
||||
</a>
|
||||
<!-- Change password -->
|
||||
<ng-container *ngIf="authType === 'default'">
|
||||
<a
|
||||
[ngClass]="{ active: isOnChangePasswordPage() }"
|
||||
*osPerms="'users.can_change_password'"
|
||||
routerLink="/users/password"
|
||||
(click)="onClickNavEntry()"
|
||||
mat-list-item
|
||||
>
|
||||
<mat-icon class="menu-icon">vpn_key</mat-icon>
|
||||
<span class="menu-text" translate>Change password</span>
|
||||
</a>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="authType === 'saml'">
|
||||
<a *osPerms="'users.can_change_password'" [href]="samlChangePasswordUrl" mat-list-item>
|
||||
<mat-icon class="menu-icon">vpn_key</mat-icon>
|
||||
<span class="menu-text" translate>Change password</span>
|
||||
</a>
|
||||
</ng-container>
|
||||
<!-- logout -->
|
||||
<a (click)="logout()" mat-list-item>
|
||||
<mat-icon class="menu-icon">exit_to_app</mat-icon>
|
||||
<span class="menu-text" translate>Logout</span>
|
||||
</a>
|
||||
</div>
|
||||
</mat-nav-list>
|
||||
</mat-expansion-panel>
|
||||
<mat-nav-list *ngIf="!isLoggedIn">
|
||||
<a routerLink="/login" mat-list-item>
|
||||
<mat-icon class="menu-icon">exit_to_app</mat-icon>
|
||||
<span class="menu-text" translate>Login</span>
|
||||
</a>
|
||||
</mat-nav-list>
|
||||
|
||||
<mat-menu #languageMenu="matMenu">
|
||||
<button mat-menu-item (click)="selectLang('en')">{{ getLangName('en') }}</button>
|
||||
<button mat-menu-item (click)="selectLang('de')">{{ getLangName('de') }}</button>
|
||||
<button mat-menu-item (click)="selectLang('ru')">{{ getLangName('ru') }}</button>
|
||||
<button mat-menu-item (click)="selectLang('cs')">{{ getLangName('cs') }}</button>
|
||||
</mat-menu>
|
@ -0,0 +1,16 @@
|
||||
.username {
|
||||
span {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-icon {
|
||||
min-width: 20px !important; //puts the text to the right on the same level
|
||||
margin-right: 25px !important;
|
||||
}
|
||||
|
||||
.menu-text {
|
||||
font-size: 16px;
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { E2EImportsModule } from 'e2e-imports.module';
|
||||
|
||||
import { UserMenuComponent } from './user-menu.component';
|
||||
|
||||
describe('UserMenuComponent', () => {
|
||||
let component: UserMenuComponent;
|
||||
let fixture: ComponentFixture<UserMenuComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [E2EImportsModule]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(UserMenuComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,137 @@
|
||||
import { Component, EventEmitter, OnInit, Output } from '@angular/core';
|
||||
import { MatSnackBar } from '@angular/material';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { AuthService } from 'app/core/core-services/auth.service';
|
||||
import { OperatorService } from 'app/core/core-services/operator.service';
|
||||
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||
import { LoginDataService } from 'app/core/ui-services/login-data.service';
|
||||
import { OverlayService } from 'app/core/ui-services/overlay.service';
|
||||
import { DEFAULT_AUTH_TYPE } from 'app/shared/models/users/user';
|
||||
import { BaseViewComponent } from 'app/site/base/base-view';
|
||||
import { ViewUser } from 'app/site/users/models/view-user';
|
||||
|
||||
@Component({
|
||||
selector: 'os-user-menu',
|
||||
templateUrl: './user-menu.component.html',
|
||||
styleUrls: ['./user-menu.component.scss']
|
||||
})
|
||||
export class UserMenuComponent extends BaseViewComponent implements OnInit {
|
||||
public isLoggedIn: boolean;
|
||||
|
||||
public user: ViewUser;
|
||||
|
||||
public username = '';
|
||||
|
||||
public authType = DEFAULT_AUTH_TYPE;
|
||||
|
||||
public samlChangePasswordUrl: string | null = null;
|
||||
|
||||
public allowSelfSetPresent: boolean;
|
||||
|
||||
private selfPresentConfStr = 'users_allow_self_set_present';
|
||||
|
||||
@Output()
|
||||
private navEvent: EventEmitter<void> = new EventEmitter();
|
||||
|
||||
public constructor(
|
||||
titleService: Title,
|
||||
protected translate: TranslateService,
|
||||
protected matSnackBar: MatSnackBar,
|
||||
private operator: OperatorService,
|
||||
private authService: AuthService,
|
||||
private overlayService: OverlayService, // private vp: ViewportService,
|
||||
private loginDataService: LoginDataService,
|
||||
private configService: ConfigService,
|
||||
private router: Router
|
||||
) {
|
||||
super(titleService, translate, matSnackBar);
|
||||
}
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.operator.getViewUserObservable().subscribe(user => {
|
||||
if (user) {
|
||||
this.user = user;
|
||||
}
|
||||
if (!this.operator.isAnonymous) {
|
||||
this.username = user ? user.short_name : '';
|
||||
this.isLoggedIn = true;
|
||||
} else {
|
||||
this.username = this.translate.instant('Guest');
|
||||
this.isLoggedIn = false;
|
||||
}
|
||||
});
|
||||
|
||||
this.operator.authType.subscribe(authType => (this.authType = authType));
|
||||
|
||||
this.loginDataService.samlSettings.subscribe(
|
||||
samlSettings => (this.samlChangePasswordUrl = samlSettings ? samlSettings.changePasswordUrl : null)
|
||||
);
|
||||
|
||||
this.configService
|
||||
.get<boolean>(this.selfPresentConfStr)
|
||||
.subscribe(allowed => (this.allowSelfSetPresent = allowed));
|
||||
}
|
||||
|
||||
public isOnProfilePage(): boolean {
|
||||
const ownProfilePageUrl = `/users/${this.user.id}`;
|
||||
return ownProfilePageUrl === this.router.url;
|
||||
}
|
||||
|
||||
public isOnChangePasswordPage(): boolean {
|
||||
const changePasswordPageUrl = '/users/password';
|
||||
return changePasswordPageUrl === this.router.url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Let the user change the language
|
||||
* @param lang the desired language (en, de, cs, ...)
|
||||
*/
|
||||
public selectLang(selection: string): void {
|
||||
this.translate.use(selection).subscribe();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of a Language by abbreviation.
|
||||
*
|
||||
* @param abbreviation The abbreviation of the languate or null, if the current
|
||||
* language should be used.
|
||||
*/
|
||||
public getLangName(abbreviation?: string): string {
|
||||
if (!abbreviation) {
|
||||
abbreviation = this.translate.currentLang;
|
||||
}
|
||||
|
||||
if (abbreviation === 'en') {
|
||||
return 'English';
|
||||
} else if (abbreviation === 'de') {
|
||||
return 'Deutsch';
|
||||
} else if (abbreviation === 'cs') {
|
||||
return 'Čeština';
|
||||
} else if (abbreviation === 'ru') {
|
||||
return 'русский';
|
||||
}
|
||||
}
|
||||
|
||||
public toggleUserIsPresent(): void {
|
||||
this.operator.setPresence(!this.user.is_present).catch(this.raiseError);
|
||||
}
|
||||
|
||||
public onClickNavEntry(): void {
|
||||
this.navEvent.next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to log out the current user
|
||||
*/
|
||||
public logout(): void {
|
||||
if (this.operator.guestsEnabled) {
|
||||
this.overlayService.showSpinner(null, true);
|
||||
}
|
||||
this.authService.logout();
|
||||
this.overlayService.logout();
|
||||
}
|
||||
}
|
@ -107,7 +107,7 @@ import { SuperSearchComponent } from 'app/site/common/components/super-search/su
|
||||
import { OverlayComponent } from 'app/site/common/components/overlay/overlay.component';
|
||||
import { PreviewComponent } from './components/preview/preview.component';
|
||||
import { PdfViewerModule } from 'ng2-pdf-viewer';
|
||||
import { GlobalSpinnerComponent } from 'app/site/common/components/global-spinner/global-spinner.component';
|
||||
|
||||
import { HeightResizingDirective } from './directives/height-resizing.directive';
|
||||
import { TrustPipe } from './pipes/trust.pipe';
|
||||
import { LocalizedDatePipe } from './pipes/localized-date.pipe';
|
||||
@ -125,6 +125,9 @@ import { VotingPrivacyWarningComponent } from './components/voting-privacy-warni
|
||||
import { MotionPollDetailContentComponent } from './components/motion-poll-detail-content/motion-poll-detail-content.component';
|
||||
import { AssignmentPollDetailContentComponent } from './components/assignment-poll-detail-content/assignment-poll-detail-content.component';
|
||||
|
||||
import { GlobalSpinnerComponent } from './components/global-spinner/global-spinner.component';
|
||||
import { UserMenuComponent } from './components/user-menu/user-menu.component';
|
||||
|
||||
/**
|
||||
* Share Module for all "dumb" components and pipes.
|
||||
*
|
||||
@ -272,6 +275,7 @@ import { AssignmentPollDetailContentComponent } from './components/assignment-po
|
||||
ExtensionFieldComponent,
|
||||
RoundedInputComponent,
|
||||
GlobalSpinnerComponent,
|
||||
UserMenuComponent,
|
||||
OverlayComponent,
|
||||
PreviewComponent,
|
||||
NgxMaterialTimepickerModule,
|
||||
@ -332,6 +336,7 @@ import { AssignmentPollDetailContentComponent } from './components/assignment-po
|
||||
RoundedInputComponent,
|
||||
ProgressSnackBarComponent,
|
||||
GlobalSpinnerComponent,
|
||||
UserMenuComponent,
|
||||
SuperSearchComponent,
|
||||
OverlayComponent,
|
||||
PreviewComponent,
|
||||
|
@ -15,83 +15,16 @@
|
||||
>
|
||||
<div class="nav-toolbar">
|
||||
<!-- logo -->
|
||||
<a routerLink="/" (click)="toggleSideNav()">
|
||||
<a routerLink="/" (click)="mobileAutoCloseNav()">
|
||||
<os-logo class="os-logo-container" [footer]="false"></os-logo>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- User Menu -->
|
||||
<mat-expansion-panel class="user-menu mat-elevation-z0">
|
||||
<mat-expansion-panel-header class="username">
|
||||
<!-- Get the username from operator -->
|
||||
{{ username }}
|
||||
</mat-expansion-panel-header>
|
||||
<mat-nav-list>
|
||||
<a mat-list-item [matMenuTriggerFor]="languageMenu">
|
||||
<mat-icon>language</mat-icon>
|
||||
<span>{{ getLangName() }}</span>
|
||||
</a>
|
||||
<div *ngIf="isLoggedIn">
|
||||
<a
|
||||
[routerLink]="operator.user ? ['/users/', operator.user.id] : []"
|
||||
(click)="mobileAutoCloseNav()"
|
||||
mat-list-item
|
||||
>
|
||||
<mat-icon>person</mat-icon>
|
||||
<span translate>Show profile</span>
|
||||
</a>
|
||||
<ng-container *ngIf="authType === 'default'">
|
||||
<a
|
||||
*osPerms="'users.can_change_password'"
|
||||
routerLink="/users/password"
|
||||
(click)="mobileAutoCloseNav()"
|
||||
mat-list-item
|
||||
>
|
||||
<mat-icon>vpn_key</mat-icon>
|
||||
<span translate>Change password</span>
|
||||
</a>
|
||||
|
||||
</ng-container>
|
||||
<ng-container *ngIf="authType === 'saml'">
|
||||
<a
|
||||
*osPerms="'users.can_change_password'"
|
||||
[href]="samlChangePasswordUrl"
|
||||
mat-list-item
|
||||
>
|
||||
<mat-icon>vpn_key</mat-icon>
|
||||
<span translate>Change password</span>
|
||||
</a>
|
||||
</ng-container>
|
||||
<a (click)="logout()" mat-list-item>
|
||||
<mat-icon>exit_to_app</mat-icon>
|
||||
<span translate>Logout</span>
|
||||
</a>
|
||||
</div>
|
||||
<div *ngIf="!isLoggedIn">
|
||||
<a routerLink="/login" mat-list-item>
|
||||
<mat-icon>exit_to_app</mat-icon>
|
||||
<span translate>Login</span>
|
||||
</a>
|
||||
</div>
|
||||
</mat-nav-list>
|
||||
</mat-expansion-panel>
|
||||
<mat-menu #languageMenu="matMenu">
|
||||
<button mat-menu-item (click)="selectLang('en')">{{ getLangName('en') }}</button>
|
||||
<button mat-menu-item (click)="selectLang('de')">{{ getLangName('de') }}</button>
|
||||
<button mat-menu-item (click)="selectLang('ru')">{{ getLangName('ru') }}</button>
|
||||
<button mat-menu-item (click)="selectLang('cs')">{{ getLangName('cs') }}</button>
|
||||
</mat-menu>
|
||||
<os-user-menu (navEvent)="mobileAutoCloseNav()"></os-user-menu>
|
||||
|
||||
<!-- navigation -->
|
||||
<mat-nav-list class="main-nav">
|
||||
<ng-container *ngIf="!isLoggedIn">
|
||||
<a routerLink="/login" mat-list-item>
|
||||
<mat-icon>exit_to_app</mat-icon>
|
||||
<span translate>Login</span>
|
||||
</a>
|
||||
<mat-divider></mat-divider>
|
||||
</ng-container>
|
||||
|
||||
<span *ngFor="let entry of mainMenuService.entries">
|
||||
<a
|
||||
[@navItemAnim]
|
||||
|
@ -39,14 +39,6 @@ mat-sidenav-container {
|
||||
}
|
||||
}
|
||||
|
||||
.username {
|
||||
span {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-toolbar {
|
||||
display: flex;
|
||||
margin: auto;
|
||||
|
@ -29,8 +29,7 @@
|
||||
}
|
||||
|
||||
/** style and align the nav icons the icons*/
|
||||
.main-nav,
|
||||
.user-menu {
|
||||
.main-nav {
|
||||
mat-icon {
|
||||
color: mat-color($foreground, icon);
|
||||
}
|
||||
|
@ -11,11 +11,8 @@ import { filter } from 'rxjs/operators';
|
||||
|
||||
import { navItemAnim } from '../shared/animations';
|
||||
import { OfflineService } from 'app/core/core-services/offline.service';
|
||||
import { LoginDataService } from 'app/core/ui-services/login-data.service';
|
||||
import { OverlayService } from 'app/core/ui-services/overlay.service';
|
||||
import { UpdateService } from 'app/core/ui-services/update.service';
|
||||
import { DEFAULT_AUTH_TYPE } from 'app/shared/models/users/user';
|
||||
import { AuthService } from '../core/core-services/auth.service';
|
||||
import { BaseComponent } from '../base.component';
|
||||
import { MainMenuService } from '../core/core-services/main-menu.service';
|
||||
import { OpenSlidesStatusService } from '../core/core-services/openslides-status.service';
|
||||
@ -44,13 +41,6 @@ export class SiteComponent extends BaseComponent implements OnInit {
|
||||
@ViewChild('sideNav', { static: true })
|
||||
public sideNav: MatSidenav;
|
||||
|
||||
/**
|
||||
* Get the username from the operator (should be known already)
|
||||
*/
|
||||
public username = '';
|
||||
|
||||
public authType = DEFAULT_AUTH_TYPE;
|
||||
|
||||
/**
|
||||
* is the user logged in, or the anonymous is active.
|
||||
*/
|
||||
@ -76,12 +66,8 @@ export class SiteComponent extends BaseComponent implements OnInit {
|
||||
*/
|
||||
private delayedUpdateAvailable = false;
|
||||
|
||||
public samlChangePasswordUrl: string | null = null;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param authService
|
||||
* @param route
|
||||
* @param operator
|
||||
* @param vp
|
||||
@ -96,7 +82,6 @@ export class SiteComponent extends BaseComponent implements OnInit {
|
||||
protected translate: TranslateService,
|
||||
offlineService: OfflineService,
|
||||
private updateService: UpdateService,
|
||||
private authService: AuthService,
|
||||
private router: Router,
|
||||
public operator: OperatorService,
|
||||
public vp: ViewportService,
|
||||
@ -105,31 +90,15 @@ export class SiteComponent extends BaseComponent implements OnInit {
|
||||
public OSStatus: OpenSlidesStatusService,
|
||||
public timeTravel: TimeTravelService,
|
||||
private matSnackBar: MatSnackBar,
|
||||
private overlayService: OverlayService,
|
||||
private loginDataService: LoginDataService
|
||||
private overlayService: OverlayService
|
||||
) {
|
||||
super(title, translate);
|
||||
overlayService.showSpinner(translate.instant('Loading data. Please wait...'));
|
||||
|
||||
this.operator.getViewUserObservable().subscribe(user => {
|
||||
if (!operator.isAnonymous) {
|
||||
this.username = user ? user.short_name : '';
|
||||
this.isLoggedIn = true;
|
||||
} else {
|
||||
this.username = translate.instant('Guest');
|
||||
this.isLoggedIn = false;
|
||||
}
|
||||
});
|
||||
this.operator.authType.subscribe(authType => (this.authType = authType));
|
||||
|
||||
offlineService.isOffline().subscribe(offline => {
|
||||
this.isOffline = offline;
|
||||
});
|
||||
|
||||
this.loginDataService.samlSettings.subscribe(
|
||||
samlSettings => (this.samlChangePasswordUrl = samlSettings ? samlSettings.changePasswordUrl : null)
|
||||
);
|
||||
|
||||
this.searchform = new FormGroup({ query: new FormControl([]) });
|
||||
|
||||
// detect routing data such as base perm and noInterruption
|
||||
@ -230,47 +199,6 @@ export class SiteComponent extends BaseComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Let the user change the language
|
||||
* @param lang the desired language (en, de, cs, ...)
|
||||
*/
|
||||
public selectLang(selection: string): void {
|
||||
this.translate.use(selection).subscribe();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of a Language by abbreviation.
|
||||
*
|
||||
* @param abbreviation The abbreviation of the languate or null, if the current
|
||||
* language should be used.
|
||||
*/
|
||||
public getLangName(abbreviation?: string): string {
|
||||
if (!abbreviation) {
|
||||
abbreviation = this.translate.currentLang;
|
||||
}
|
||||
|
||||
if (abbreviation === 'en') {
|
||||
return 'English';
|
||||
} else if (abbreviation === 'de') {
|
||||
return 'Deutsch';
|
||||
} else if (abbreviation === 'cs') {
|
||||
return 'Čeština';
|
||||
} else if (abbreviation === 'ru') {
|
||||
return 'русский';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to log out the current user
|
||||
*/
|
||||
public logout(): void {
|
||||
if (this.operator.guestsEnabled) {
|
||||
this.overlayService.showSpinner(null, true);
|
||||
}
|
||||
this.authService.logout();
|
||||
this.overlayService.logout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle swipes and gestures
|
||||
*/
|
||||
|
@ -15,7 +15,7 @@
|
||||
@import './app/shared/components/projector-button/projector-button.component.scss';
|
||||
@import './app/site/agenda/components/list-of-speakers/list-of-speakers.component.scss-theme.scss';
|
||||
@import './app/shared/components/sorting-tree/sorting-tree.component.scss';
|
||||
@import './app/site/common/components/global-spinner/global-spinner.component.scss';
|
||||
@import './app/shared/components/global-spinner/global-spinner.component.scss';
|
||||
@import './app/shared/components/tile/tile.component.scss';
|
||||
@import './app/shared/components/block-tile/block-tile.component.scss';
|
||||
@import './app/shared/components/icon-container/icon-container.component.scss';
|
||||
|
@ -34,6 +34,15 @@ def get_config_variables():
|
||||
group="Participants",
|
||||
)
|
||||
|
||||
yield ConfigVariable(
|
||||
name="users_allow_self_set_present",
|
||||
default_value=False,
|
||||
input_type="boolean",
|
||||
label="Allow users to set themselves as present",
|
||||
weight=512,
|
||||
group="Participants",
|
||||
)
|
||||
|
||||
# PDF
|
||||
|
||||
yield ConfigVariable(
|
||||
|
@ -9,6 +9,7 @@ urlpatterns = [
|
||||
url(r"^logout/$", views.UserLogoutView.as_view(), name="user_logout"),
|
||||
url(r"^whoami/$", views.WhoAmIView.as_view(), name="user_whoami"),
|
||||
url(r"^setpassword/$", views.SetPasswordView.as_view(), name="user_setpassword"),
|
||||
url(r"^setpresence/$", views.SetPresenceView.as_view(), name="user_setpresence"),
|
||||
url(
|
||||
r"^reset-password/$",
|
||||
views.PasswordResetView.as_view(),
|
||||
|
@ -719,6 +719,23 @@ class PersonalNoteViewSet(ModelViewSet):
|
||||
# Special API views
|
||||
|
||||
|
||||
class SetPresenceView(APIView):
|
||||
http_method_names = ["post"]
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
user = request.user
|
||||
if not config["users_allow_self_set_present"] or not user.is_authenticated:
|
||||
raise ValidationError({"detail": "You cannot set your own presence"})
|
||||
|
||||
present = request.data
|
||||
if present not in (True, False):
|
||||
raise ValidationError({"detail": "Data must be a boolean"})
|
||||
|
||||
user.is_present = present
|
||||
user.save()
|
||||
return Response()
|
||||
|
||||
|
||||
class WhoAmIDataView(APIView):
|
||||
def get_whoami_data(self):
|
||||
"""
|
||||
|
Loading…
Reference in New Issue
Block a user