mirror of
https://github.com/SuiteCRM/SuiteCRM-Core.git
synced 2025-08-29 17:46:02 +08:00
Linkup footer back to top with authentication
Signed-off-by: Dillon-Brown <dillon.brown@salesagility.com>
This commit is contained in:
parent
ca47dded0a
commit
9d484ed4a4
11 changed files with 105 additions and 41 deletions
|
@ -52,3 +52,4 @@ dunglas_angular_csrf:
|
|||
- { path: ^/logout$ }
|
||||
- { path: ^/(_(profiler|wdt))/ }
|
||||
- { path: ^/api }
|
||||
- { path: ^/session-status$ }
|
||||
|
|
|
@ -31,6 +31,8 @@ const routes: Routes = [
|
|||
{
|
||||
path: 'Home',
|
||||
loadChildren: () => import('../components/home/home.module').then(m => m.HomeUiModule),
|
||||
canActivate: [AuthGuard],
|
||||
runGuardsAndResolvers: 'always',
|
||||
resolve: {
|
||||
metadata: BaseMetadataResolver,
|
||||
}
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
<!-- Start of footer section -->
|
||||
|
||||
<div class="footer">
|
||||
<div class="copyright-links">
|
||||
<a (click)="openSuiteCopyright(suitecopyright)" class="footer-link" data-toggle="modal"
|
||||
data-target=".copyright-suitecrm">
|
||||
© Supercharged by SuiteCRM
|
||||
</a>
|
||||
<a (click)="openSugarCopyright(sugarcopyright)" class="footer-link" data-toggle="modal"
|
||||
data-target=".copyright-sugarcrm">
|
||||
© Powered By SugarCRM
|
||||
</a>
|
||||
</div>
|
||||
<div class="copyright-links">
|
||||
<a (click)="openSuiteCopyright(suitecopyright)" class="footer-link" data-toggle="modal"
|
||||
data-target=".copyright-suitecrm">
|
||||
© Supercharged by SuiteCRM
|
||||
</a>
|
||||
<a (click)="openSugarCopyright(sugarcopyright)" class="footer-link" data-toggle="modal"
|
||||
data-target=".copyright-sugarcrm">
|
||||
© Powered By SugarCRM
|
||||
</a>
|
||||
</div>
|
||||
<ng-container *ngIf="this.isUserLoggedIn">
|
||||
<div class="back-to-top">
|
||||
<a (click)="backToTop()" class="footer-link">
|
||||
<a (click)="backToTop()" class="footer-link">
|
||||
<span>
|
||||
Back To Top
|
||||
<scrm-image class="sicon back-top-icon" image="arrow_up_filled"></scrm-image>
|
||||
</span>
|
||||
</a>
|
||||
</a>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<!-- End of footer section -->
|
||||
|
||||
<!-- Start of copyright modal section -->
|
||||
|
@ -98,4 +98,4 @@
|
|||
<!-- End of SuiteCRM Copyright notice modal -->
|
||||
</div>
|
||||
|
||||
<!-- End of copyright modal section -->
|
||||
<!-- End of copyright modal section -->
|
||||
|
|
|
@ -5,6 +5,7 @@ import {RouterTestingModule} from '@angular/router/testing';
|
|||
import {HttpClientTestingModule} from '@angular/common/http/testing';
|
||||
|
||||
import {FooterUiComponent} from './footer.component';
|
||||
import {ApolloTestingModule} from "apollo-angular/testing";
|
||||
|
||||
describe('FooterUiComponent', () => {
|
||||
let component: FooterUiComponent;
|
||||
|
@ -13,7 +14,7 @@ describe('FooterUiComponent', () => {
|
|||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||
imports: [RouterTestingModule, HttpClientTestingModule, FormsModule],
|
||||
imports: [RouterTestingModule, HttpClientTestingModule, FormsModule, ApolloTestingModule],
|
||||
declarations: [FooterUiComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import {Component, OnInit} from '@angular/core';
|
||||
|
||||
import {ModalDismissReasons, NgbModal} from '@ng-bootstrap/ng-bootstrap';
|
||||
import {AuthService} from "@services/auth/auth.service";
|
||||
import {Subscription} from "rxjs";
|
||||
|
||||
@Component({
|
||||
selector: 'scrm-footer-ui',
|
||||
|
@ -9,9 +11,15 @@ import {ModalDismissReasons, NgbModal} from '@ng-bootstrap/ng-bootstrap';
|
|||
})
|
||||
export class FooterUiComponent implements OnInit {
|
||||
|
||||
closeResult: string;
|
||||
private authSub: Subscription;
|
||||
|
||||
constructor(private modalService: NgbModal) {
|
||||
closeResult: string;
|
||||
isUserLoggedIn: boolean;
|
||||
|
||||
constructor(
|
||||
private modalService: NgbModal,
|
||||
private authService: AuthService
|
||||
) {
|
||||
}
|
||||
|
||||
openSugarCopyright(sugarcopyright) {
|
||||
|
@ -54,6 +62,12 @@ export class FooterUiComponent implements OnInit {
|
|||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.authSub = this.authService.isUserLoggedIn.subscribe(value => {
|
||||
this.isUserLoggedIn = value;
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.authSub.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,8 +19,8 @@
|
|||
|
||||
<!-- Start of empty navbar with logo -->
|
||||
|
||||
<ng-template [ngIf]="loaded">
|
||||
<ng-template [ngIf]="!navbar.authenticated">
|
||||
<ng-container *ngIf="loaded">
|
||||
<ng-container *ngIf="!this.isUserLoggedIn">
|
||||
<nav class="navbar ml-0 pl-0">
|
||||
<div class="navbar-collapse">
|
||||
<ul class="navbar-nav">
|
||||
|
@ -30,13 +30,13 @@
|
|||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
<!-- End of empty navbar section with logo -->
|
||||
|
||||
<!-- Start of mobile navigation section -->
|
||||
|
||||
<ng-template [ngIf]="mobileNavbar">
|
||||
<ng-container *ngIf="this.isUserLoggedIn && mobileNavbar">
|
||||
<ul class="navbar mobile-nav-block mobilenavbar">
|
||||
<div class="position-static" ngbDropdown #myDrop="ngbDropdown" [autoClose]="false">
|
||||
<button aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"
|
||||
|
@ -110,13 +110,13 @@
|
|||
</ul>
|
||||
|
||||
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
<!-- End of mobile navigation section-->
|
||||
|
||||
<!-- Start of navbar section with data once authenticated -->
|
||||
|
||||
<ng-template [ngIf]="navbar.authenticated && !mobileNavbar">
|
||||
<ng-container *ngIf="this.isUserLoggedIn && !mobileNavbar">
|
||||
<nav class="navbar navbar-expand-md navbar-1">
|
||||
<div class="navbar-collapse collapse collapsenav" [ngbCollapse]="mainNavCollapse">
|
||||
<ul class="navbar-nav home-nav">
|
||||
|
@ -252,8 +252,8 @@
|
|||
<!-- End of navbar section with data once authenticated -->
|
||||
|
||||
<scrm-action-bar-ui></scrm-action-bar-ui>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<!-- End of main navbar section -->
|
||||
<!-- End of main navbar section -->
|
||||
|
|
|
@ -12,6 +12,7 @@ import {navigationMock} from '@base/facades/navigation/navigation.facade.spec.mo
|
|||
import {languageFacadeMock} from '@base/facades/language/language.facade.spec.mock';
|
||||
import {UserPreferenceFacade} from '@base/facades/user-preference/user-preference.facade';
|
||||
import {userPreferenceFacadeMock} from '@base/facades/user-preference/user-preference.facade.spec.mock';
|
||||
import {ApolloTestingModule} from "apollo-angular/testing";
|
||||
|
||||
describe('NavbarUiComponent', () => {
|
||||
|
||||
|
@ -26,7 +27,8 @@ describe('NavbarUiComponent', () => {
|
|||
imports: [
|
||||
RouterTestingModule,
|
||||
HttpClientTestingModule,
|
||||
NgbModule
|
||||
NgbModule,
|
||||
ApolloTestingModule
|
||||
],
|
||||
providers: [
|
||||
{provide: NavigationFacade, useValue: navigationMock},
|
||||
|
|
|
@ -10,6 +10,7 @@ import {LanguageFacade, LanguageListStringMap, LanguageStringMap} from '@base/fa
|
|||
import {UserPreferenceFacade, UserPreferenceMap} from '@base/facades/user-preference/user-preference.facade';
|
||||
|
||||
import {map} from 'rxjs/operators';
|
||||
import {AuthService} from "@services/auth/auth.service";
|
||||
|
||||
@Component({
|
||||
selector: 'scrm-navbar-ui',
|
||||
|
@ -21,6 +22,7 @@ export class NavbarUiComponent implements OnInit {
|
|||
protected static instances: NavbarUiComponent[] = [];
|
||||
|
||||
loaded = true;
|
||||
isUserLoggedIn: boolean;
|
||||
|
||||
mainNavCollapse = true;
|
||||
subItemCollapse = true;
|
||||
|
@ -75,7 +77,9 @@ export class NavbarUiComponent implements OnInit {
|
|||
constructor(protected navigationFacade: NavigationFacade,
|
||||
protected languageFacade: LanguageFacade,
|
||||
protected api: ApiService,
|
||||
protected userPreferenceFacade: UserPreferenceFacade) {
|
||||
protected userPreferenceFacade: UserPreferenceFacade,
|
||||
private authService: AuthService
|
||||
) {
|
||||
const navbar = new NavbarAbstract();
|
||||
this.setNavbar(navbar);
|
||||
|
||||
|
@ -115,6 +119,9 @@ export class NavbarUiComponent implements OnInit {
|
|||
ngOnInit(): void {
|
||||
const navbar = new NavbarAbstract();
|
||||
this.setNavbar(navbar);
|
||||
this.authService.isUserLoggedIn.subscribe(value => {
|
||||
this.isUserLoggedIn = value;
|
||||
});
|
||||
|
||||
window.dispatchEvent(new Event('resize'));
|
||||
}
|
||||
|
@ -127,4 +134,8 @@ export class NavbarUiComponent implements OnInit {
|
|||
protected isLoaded() {
|
||||
return this.loaded;
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.authService.isUserLoggedIn.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,27 +1,51 @@
|
|||
import {Injectable} from '@angular/core';
|
||||
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot} from '@angular/router';
|
||||
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree} from '@angular/router';
|
||||
import {MessageService} from '../message/message.service';
|
||||
import {AuthService} from '../auth/auth.service';
|
||||
import {Observable, of} from "rxjs";
|
||||
import {HttpClient, HttpHeaders} from "@angular/common/http";
|
||||
import {catchError, map, take, tap} from "rxjs/operators";
|
||||
import {StateManager} from "@base/facades/state-manager";
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class AuthGuard implements CanActivate {
|
||||
|
||||
constructor(
|
||||
protected message: MessageService,
|
||||
protected router: Router,
|
||||
private authService: AuthService
|
||||
protected http: HttpClient,
|
||||
private authService: AuthService,
|
||||
protected stateManager: StateManager,
|
||||
) {
|
||||
}
|
||||
|
||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||
const currentUser = this.authService.currentUserValue;
|
||||
|
||||
if (currentUser) {
|
||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
|
||||
if (this.authService.isUserLoggedIn.value) {
|
||||
return true;
|
||||
}
|
||||
const loginUrl = 'Login';
|
||||
const tree: UrlTree = this.router.parseUrl(loginUrl);
|
||||
|
||||
return true;
|
||||
const Url = 'session-status';
|
||||
const headers = new HttpHeaders().set('Content-Type', 'text/plain; charset=utf-8');
|
||||
|
||||
return this.http.get(Url, {headers})
|
||||
.pipe(
|
||||
take(1),
|
||||
map((user: any) => {
|
||||
if (user && user.active === true) {
|
||||
return true;
|
||||
}
|
||||
// Re-direct to login
|
||||
return tree;
|
||||
}),
|
||||
catchError(() => of(tree)),
|
||||
tap((result: boolean | UrlTree) => {
|
||||
if (result === true) {
|
||||
this.authService.isUserLoggedIn.next(true);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ export class AuthService {
|
|||
public currentUser$: Observable<User>;
|
||||
private currentUserSubject: BehaviorSubject<User>;
|
||||
defaultTimeout: string = '3600';
|
||||
public isUserLoggedIn: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
|
||||
constructor(
|
||||
private http: HttpClient,
|
||||
|
@ -32,10 +33,6 @@ export class AuthService {
|
|||
this.currentUser$ = this.currentUserSubject.asObservable();
|
||||
}
|
||||
|
||||
public get currentUserValue(): User {
|
||||
return this.currentUserSubject.value;
|
||||
}
|
||||
|
||||
doLogin(
|
||||
caller: LoginUiComponent,
|
||||
username: string,
|
||||
|
@ -58,6 +55,8 @@ export class AuthService {
|
|||
{headers}
|
||||
).subscribe((response: any) => {
|
||||
onSuccess(caller, response);
|
||||
this.isUserLoggedIn.next(true);
|
||||
|
||||
let duration = response.duration;
|
||||
|
||||
if (duration === 0 || duration === '0') {
|
||||
|
@ -107,6 +106,7 @@ export class AuthService {
|
|||
this.message.log('Logout success');
|
||||
const label = this.languageFacade.getAppString(messageKey);
|
||||
this.message.addSuccessMessage(label);
|
||||
this.isUserLoggedIn.next(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,4 +40,13 @@ class SecurityController extends AbstractController
|
|||
{
|
||||
throw new RuntimeException('This will be intercepted by the logout key');
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/session-status", name="app_session_status", methods={"GET"})
|
||||
* @throws Exception
|
||||
*/
|
||||
public function sessionStatus(): JsonResponse
|
||||
{
|
||||
return new JsonResponse(['active' => true], Response::HTTP_OK);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue