forked from EVOgeek/Vmeda.Online
		
	MOBILE-3742 core: Support redirect to another site
This commit is contained in:
		
							parent
							
								
									21ccad82f0
								
							
						
					
					
						commit
						ef2f68a5fd
					
				| @ -26,7 +26,6 @@ import { | ||||
| } from '@angular/router'; | ||||
| 
 | ||||
| import { CoreArray } from '@singletons/array'; | ||||
| import { CoreRedirectGuard } from '@guards/redirect'; | ||||
| 
 | ||||
| /** | ||||
|  * Build app routes. | ||||
| @ -35,16 +34,7 @@ import { CoreRedirectGuard } from '@guards/redirect'; | ||||
|  * @return App routes. | ||||
|  */ | ||||
| function buildAppRoutes(injector: Injector): Routes { | ||||
|     const appRoutes = CoreArray.flatten(injector.get<Routes[]>(APP_ROUTES, [])); | ||||
| 
 | ||||
|     return appRoutes.map(route => { | ||||
|         route.canLoad = route.canLoad ?? []; | ||||
|         route.canActivate = route.canActivate ?? []; | ||||
|         route.canLoad.push(CoreRedirectGuard); | ||||
|         route.canActivate.push(CoreRedirectGuard); | ||||
| 
 | ||||
|         return route; | ||||
|     }); | ||||
|     return CoreArray.flatten(injector.get<Routes[]>(APP_ROUTES, [])); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  | ||||
| @ -56,7 +56,7 @@ export class CoreContentLinksHelperProvider { | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the first valid action in the list of possible actions to do for a URL. | ||||
|      * Get the first valid action for a URL. | ||||
|      * | ||||
|      * @param url URL to handle. | ||||
|      * @param courseId Course ID related to the URL. Optional but recommended. | ||||
| @ -75,6 +75,16 @@ export class CoreContentLinksHelperProvider { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         return this.getFirstValidAction(actions); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the first valid action in a list of possible actions. | ||||
|      * | ||||
|      * @param actions Actions. | ||||
|      * @return First valid action if any. | ||||
|      */ | ||||
|     getFirstValidAction(actions: CoreContentLinksAction[]): CoreContentLinksAction | undefined { | ||||
|         return actions.find((action) => action && action.sites && action.sites.length); | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -311,11 +311,8 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { | ||||
|             this.sectionChanged(sections[0]); | ||||
|         } else if (this.initialSectionId || this.initialSectionNumber) { | ||||
|             // We have an input indicating the section ID to load. Search the section.
 | ||||
|             const section = sections.find((section) => { | ||||
|                 if (section.id != this.initialSectionId && (!section.section || section.section != this.initialSectionNumber)) { | ||||
|                     return false; | ||||
|                 } | ||||
|             }); | ||||
|             const section = sections.find((section) => | ||||
|                 section.id == this.initialSectionId || (section.section && section.section == this.initialSectionNumber)); | ||||
| 
 | ||||
|             // Don't load the section if it cannot be viewed by the user.
 | ||||
|             if (section && this.canViewSection(section)) { | ||||
|  | ||||
| @ -11,5 +11,5 @@ | ||||
|     </ion-toolbar> | ||||
| </ion-header> | ||||
| <ion-content> | ||||
|     <core-tabs-outlet [tabs]="tabs" [hideUntil]="loaded"></core-tabs-outlet> | ||||
|     <core-tabs-outlet [tabs]="tabs" [hideUntil]="loaded" (ionChange)="tabSelected()"></core-tabs-outlet> | ||||
| </ion-content> | ||||
|  | ||||
| @ -46,6 +46,8 @@ export class CoreCourseIndexPage implements OnInit, OnDestroy { | ||||
|     protected currentPagePath = ''; | ||||
|     protected selectTabObserver: CoreEventObserver; | ||||
|     protected firstTabName?: string; | ||||
|     protected module?: CoreCourseWSModule; | ||||
|     protected modParams?: Params; | ||||
|     protected contentsTab: CoreTabsOutletTab = { | ||||
|         page: CONTENTS_PAGE_NAME, | ||||
|         title: 'core.course.contents', | ||||
| @ -82,8 +84,8 @@ export class CoreCourseIndexPage implements OnInit, OnDestroy { | ||||
|         // Get params.
 | ||||
|         this.course = CoreNavigator.getRouteParam('course'); | ||||
|         this.firstTabName = CoreNavigator.getRouteParam('selectedTab'); | ||||
|         const module = CoreNavigator.getRouteParam<CoreCourseWSModule>('module'); | ||||
|         const modParams = CoreNavigator.getRouteParam<Params>('modParams'); | ||||
|         this.module = CoreNavigator.getRouteParam<CoreCourseWSModule>('module'); | ||||
|         this.modParams = CoreNavigator.getRouteParam<Params>('modParams'); | ||||
| 
 | ||||
|         this.currentPagePath = CoreNavigator.getCurrentPath(); | ||||
|         this.contentsTab.page = CoreTextUtils.concatenatePaths(this.currentPagePath, this.contentsTab.page); | ||||
| @ -93,9 +95,8 @@ export class CoreCourseIndexPage implements OnInit, OnDestroy { | ||||
|             sectionNumber: CoreNavigator.getRouteNumberParam('sectionNumber'), | ||||
|         }; | ||||
| 
 | ||||
|         if (module) { | ||||
|             this.contentsTab.pageParams!.moduleId = module.id; | ||||
|             CoreCourseHelper.openModule(module, this.course!.id, this.contentsTab.pageParams!.sectionId, modParams); | ||||
|         if (this.module) { | ||||
|             this.contentsTab.pageParams!.moduleId = this.module.id; | ||||
|         } | ||||
| 
 | ||||
|         this.tabs.push(this.contentsTab); | ||||
| @ -107,6 +108,18 @@ export class CoreCourseIndexPage implements OnInit, OnDestroy { | ||||
|         ]); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * A tab was selected. | ||||
|      */ | ||||
|     tabSelected(): void { | ||||
|         if (this.module) { | ||||
|             // Now that the first tab has been selected we can load the module.
 | ||||
|             CoreCourseHelper.openModule(this.module, this.course!.id, this.contentsTab.pageParams!.sectionId, this.modParams); | ||||
| 
 | ||||
|             delete this.module; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Load course option handlers. | ||||
|      * | ||||
|  | ||||
| @ -17,6 +17,7 @@ import { Routes } from '@angular/router'; | ||||
| 
 | ||||
| import { AppRoutingModule } from '@/app/app-routing.module'; | ||||
| import { CoreLoginHelperProvider } from './services/login-helper'; | ||||
| import { CoreRedirectGuard } from '@guards/redirect'; | ||||
| 
 | ||||
| export const CORE_LOGIN_SERVICES = [ | ||||
|     CoreLoginHelperProvider, | ||||
| @ -26,6 +27,8 @@ const appRoutes: Routes = [ | ||||
|     { | ||||
|         path: 'login', | ||||
|         loadChildren: () => import('./login-lazy.module').then(m => m.CoreLoginLazyModule), | ||||
|         canActivate: [CoreRedirectGuard], | ||||
|         canLoad: [CoreRedirectGuard], | ||||
|     }, | ||||
| ]; | ||||
| 
 | ||||
|  | ||||
| @ -14,7 +14,8 @@ | ||||
| </ion-header> | ||||
| <ion-content> | ||||
|     <core-loading [hideUntil]="loaded"> | ||||
|         <core-tabs-outlet *ngIf="tabs.length > 0" [selectedIndex]="selectedTab" [hideUntil]="loaded" [tabs]="tabs"> | ||||
|         <core-tabs-outlet *ngIf="tabs.length > 0" [selectedIndex]="selectedTab" [hideUntil]="loaded" [tabs]="tabs" | ||||
|             (ionChange)="tabSelected()"> | ||||
|         </core-tabs-outlet> | ||||
|         <ng-container *ngIf="tabs.length == 0"> | ||||
|             <core-empty-box icon="fas-home" [message]="'core.courses.nocourses' | translate"></core-empty-box> | ||||
|  | ||||
| @ -20,6 +20,12 @@ import { CoreEventObserver, CoreEvents } from '@singletons/events'; | ||||
| import { CoreTabsOutletComponent, CoreTabsOutletTab } from '@components/tabs-outlet/tabs-outlet'; | ||||
| import { CoreMainMenuHomeDelegate, CoreMainMenuHomeHandlerToDisplay } from '../../services/home-delegate'; | ||||
| import { CoreUtils } from '@services/utils/utils'; | ||||
| import { ActivatedRoute } from '@angular/router'; | ||||
| import { CoreNavigator, CoreRedirectPayload } from '@services/navigator'; | ||||
| import { CoreCourseHelper } from '@features/course/services/course-helper'; | ||||
| import { CoreCourse } from '@features/course/services/course'; | ||||
| import { CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate'; | ||||
| import { CoreContentLinksHelper } from '@features/contentlinks/services/contentlinks-helper'; | ||||
| 
 | ||||
| /** | ||||
|  * Page that displays the Home. | ||||
| @ -40,11 +46,29 @@ export class CoreMainMenuHomePage implements OnInit { | ||||
| 
 | ||||
|     protected subscription?: Subscription; | ||||
|     protected updateSiteObserver?: CoreEventObserver; | ||||
|     protected pendingRedirect?: CoreRedirectPayload; | ||||
|     protected urlToOpen?: string; | ||||
| 
 | ||||
|     constructor( | ||||
|         protected route: ActivatedRoute, | ||||
|     ) { | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Initialize the component. | ||||
|      */ | ||||
|     ngOnInit(): void { | ||||
|         this.route.queryParams.subscribe((params: Partial<CoreRedirectPayload> & { urlToOpen?: string }) => { | ||||
|             this.urlToOpen = params.urlToOpen; | ||||
| 
 | ||||
|             if (params.redirectPath) { | ||||
|                 this.pendingRedirect = { | ||||
|                     redirectPath: params.redirectPath, | ||||
|                     redirectParams: params.redirectParams, | ||||
|                 }; | ||||
|             } | ||||
|         }); | ||||
| 
 | ||||
|         this.loadSiteName(); | ||||
| 
 | ||||
|         this.subscription = CoreMainMenuHomeDelegate.getHandlersObservable().subscribe((handlers) => { | ||||
| @ -111,6 +135,57 @@ export class CoreMainMenuHomePage implements OnInit { | ||||
|         this.siteName = CoreSites.getCurrentSite()!.getSiteName(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Handle a redirect. | ||||
|      * | ||||
|      * @param data Data received. | ||||
|      */ | ||||
|     protected handleRedirect(data: CoreRedirectPayload): void { | ||||
|         const params = data.redirectParams; | ||||
|         const coursePathMatches = data.redirectPath.match(/^course\/(\d+)\/?$/); | ||||
| 
 | ||||
|         if (coursePathMatches) { | ||||
|             if (!params?.course) { | ||||
|                 CoreCourseHelper.getAndOpenCourse(Number(coursePathMatches[1]), params); | ||||
|             } else { | ||||
|                 CoreCourse.openCourse(params.course, params); | ||||
|             } | ||||
|         } else { | ||||
|             CoreNavigator.navigateToSitePath(data.redirectPath, { | ||||
|                 params: data.redirectParams, | ||||
|                 preferCurrentTab: false, | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Handle a URL to open. | ||||
|      * | ||||
|      * @param url URL to open. | ||||
|      */ | ||||
|     protected async handleUrlToOpen(url: string): Promise<void> { | ||||
|         const actions = await CoreContentLinksDelegate.getActionsFor(url, undefined); | ||||
| 
 | ||||
|         const action = CoreContentLinksHelper.getFirstValidAction(actions); | ||||
|         if (action) { | ||||
|             action.action(action.sites![0]); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Tab was selected. | ||||
|      */ | ||||
|     tabSelected(): void { | ||||
|         if (this.pendingRedirect) { | ||||
|             this.handleRedirect(this.pendingRedirect); | ||||
|         } else if (this.urlToOpen) { | ||||
|             this.handleUrlToOpen(this.urlToOpen); | ||||
|         } | ||||
| 
 | ||||
|         delete this.pendingRedirect; | ||||
|         delete this.urlToOpen; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * User entered the page. | ||||
|      */ | ||||
|  | ||||
| @ -19,14 +19,12 @@ import { BackButtonEvent } from '@ionic/core'; | ||||
| import { Subscription } from 'rxjs'; | ||||
| 
 | ||||
| import { CoreApp } from '@services/app'; | ||||
| import { CoreSites } from '@services/sites'; | ||||
| import { CoreTextUtils } from '@services/utils/text'; | ||||
| import { CoreEvents, CoreEventObserver } from '@singletons/events'; | ||||
| import { CoreMainMenu, CoreMainMenuProvider } from '../../services/mainmenu'; | ||||
| import { CoreMainMenuDelegate, CoreMainMenuHandlerToDisplay } from '../../services/mainmenu-delegate'; | ||||
| import { CoreDomUtils } from '@services/utils/dom'; | ||||
| import { Translate } from '@singletons'; | ||||
| import { CoreNavigator, CoreRedirectPayload } from '@services/navigator'; | ||||
| 
 | ||||
| /** | ||||
|  * Page that displays the main menu of the app. | ||||
| @ -47,9 +45,6 @@ export class CoreMainMenuPage implements OnInit, OnDestroy { | ||||
|     morePageName = CoreMainMenuProvider.MORE_PAGE_NAME; | ||||
| 
 | ||||
|     protected subscription?: Subscription; | ||||
|     protected redirectObs?: CoreEventObserver; | ||||
|     protected pendingRedirect?: CoreRedirectPayload; | ||||
|     protected urlToOpen?: string; | ||||
|     protected keyboardObserver?: CoreEventObserver; | ||||
|     protected resizeFunction: () => void; | ||||
|     protected backButtonFunction: (event: BackButtonEvent) => void; | ||||
| @ -72,24 +67,6 @@ export class CoreMainMenuPage implements OnInit, OnDestroy { | ||||
|      * Initialize the component. | ||||
|      */ | ||||
|     ngOnInit(): void { | ||||
|         // @TODO this should be handled by route guards and can be removed
 | ||||
|         if (!CoreSites.isLoggedIn()) { | ||||
|             CoreNavigator.navigate('/login/init', { reset: true }); | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         this.route.queryParams.subscribe((params: Partial<CoreRedirectPayload> & { urlToOpen?: string }) => { | ||||
|             if (params.redirectPath) { | ||||
|                 this.pendingRedirect = { | ||||
|                     redirectPath: params.redirectPath, | ||||
|                     redirectParams: params.redirectParams, | ||||
|                 }; | ||||
|             } | ||||
| 
 | ||||
|             this.urlToOpen = params.urlToOpen; | ||||
|         }); | ||||
| 
 | ||||
|         this.showTabs = true; | ||||
| 
 | ||||
|         this.subscription = CoreMainMenuDelegate.getHandlersObservable().subscribe((handlers) => { | ||||
| @ -97,16 +74,6 @@ export class CoreMainMenuPage implements OnInit, OnDestroy { | ||||
|             this.allHandlers = handlers.filter((handler) => !handler.onlyInMore); | ||||
| 
 | ||||
|             this.initHandlers(); | ||||
| 
 | ||||
|             if (this.loaded && this.pendingRedirect) { | ||||
|                 // Wait for tabs to be initialized and then handle the redirect.
 | ||||
|                 setTimeout(() => { | ||||
|                     if (this.pendingRedirect) { | ||||
|                         this.handleRedirect(this.pendingRedirect); | ||||
|                         delete this.pendingRedirect; | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|         }); | ||||
| 
 | ||||
|         window.addEventListener('resize', this.resizeFunction); | ||||
| @ -158,18 +125,6 @@ export class CoreMainMenuPage implements OnInit, OnDestroy { | ||||
|             this.tabs.sort((a, b) => (b.priority || 0) - (a.priority || 0)); | ||||
| 
 | ||||
|             this.loaded = CoreMainMenuDelegate.areHandlersLoaded(); | ||||
| 
 | ||||
|             if (this.loaded && this.mainTabs && !this.mainTabs.getSelected()) { | ||||
|                 // Select the first tab.
 | ||||
|                 setTimeout(() => { | ||||
|                     this.mainTabs!.select(this.tabs[0]?.page || this.morePageName); | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (this.urlToOpen) { | ||||
|             // There's a content link to open.
 | ||||
|             // @todo: Treat URL.
 | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @ -189,36 +144,11 @@ export class CoreMainMenuPage implements OnInit, OnDestroy { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Handle a redirect. | ||||
|      * | ||||
|      * @param data Data received. | ||||
|      */ | ||||
|     protected handleRedirect(data: CoreRedirectPayload): void { | ||||
|         // Check if the redirect page is the root page of any of the tabs.
 | ||||
|         const i = this.tabs.findIndex((tab) => tab.page == data.redirectPath); | ||||
| 
 | ||||
|         if (i >= 0) { | ||||
|             // Tab found. Open it with the params.
 | ||||
|             CoreNavigator.navigate(data.redirectPath, { | ||||
|                 params: data.redirectParams, | ||||
|                 animated: false, | ||||
|             }); | ||||
|         } else { | ||||
|             // Tab not found, use a phantom tab.
 | ||||
|             // @todo
 | ||||
|         } | ||||
| 
 | ||||
|         // Force change detection, otherwise sometimes the tab was selected before the params were applied.
 | ||||
|         this.changeDetector.detectChanges(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Page destroyed. | ||||
|      */ | ||||
|     ngOnDestroy(): void { | ||||
|         this.subscription?.unsubscribe(); | ||||
|         this.redirectObs?.off(); | ||||
|         window.removeEventListener('resize', this.resizeFunction); | ||||
|         document.removeEventListener('ionBackButton', this.backButtonFunction); | ||||
|         this.keyboardObserver?.off(); | ||||
|  | ||||
| @ -40,52 +40,48 @@ export class CoreRedirectGuard implements CanLoad, CanActivate { | ||||
|      * Check if there is a pending redirect and trigger it. | ||||
|      */ | ||||
|     private async guard(): Promise<true | UrlTree> { | ||||
|         const redirect = CoreApp.getRedirect(); | ||||
|         const redirect = CoreApp.consumeRedirect(); | ||||
| 
 | ||||
|         if (!redirect) { | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|         try { | ||||
|             // Only accept the redirect if it was stored less than 20 seconds ago.
 | ||||
|             if (!redirect.timemodified || Date.now() - redirect.timemodified < 20000) { | ||||
|                 return true; | ||||
|             } | ||||
|         // Only accept the redirect if it was stored less than 20 seconds ago.
 | ||||
|         if (!redirect.timemodified || Date.now() - redirect.timemodified > 20000) { | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|             // Redirect to site path.
 | ||||
|             if (redirect.siteId && redirect.siteId !== CoreConstants.NO_SITE_ID) { | ||||
|                 const loggedIn = await CoreSites.loadSite( | ||||
|                     redirect.siteId, | ||||
|                     redirect.page, | ||||
|                     redirect.params, | ||||
|                 ); | ||||
|                 const route = Router.parseUrl('/main'); | ||||
| 
 | ||||
|                 route.queryParams = { | ||||
|                     redirectPath: redirect.page, | ||||
|                     redirectParams: redirect.params, | ||||
|                 }; | ||||
| 
 | ||||
|                 return loggedIn ? route : true; | ||||
|             } | ||||
| 
 | ||||
|             // Abort redirect.
 | ||||
|             if (!redirect.page) { | ||||
|                 return true; | ||||
|             } | ||||
| 
 | ||||
|             // Redirect to non-site path.
 | ||||
|             const route = Router.parseUrl(redirect.page); | ||||
|         // Redirect to site path.
 | ||||
|         if (redirect.siteId && redirect.siteId !== CoreConstants.NO_SITE_ID) { | ||||
|             const loggedIn = await CoreSites.loadSite( | ||||
|                 redirect.siteId, | ||||
|                 redirect.page, | ||||
|                 redirect.params, | ||||
|             ); | ||||
|             const route = Router.parseUrl('/main/home'); | ||||
| 
 | ||||
|             route.queryParams = { | ||||
|                 redirectPath: redirect.page, | ||||
|                 redirectParams: redirect.params, | ||||
|             }; | ||||
| 
 | ||||
|             return route; | ||||
|         } finally { | ||||
|             CoreApp.forgetRedirect(); | ||||
|             return loggedIn ? route : true; | ||||
|         } | ||||
| 
 | ||||
|         // Abort redirect.
 | ||||
|         if (!redirect.page) { | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|         // Redirect to non-site path.
 | ||||
|         const route = Router.parseUrl(redirect.page); | ||||
| 
 | ||||
|         route.queryParams = { | ||||
|             redirectPath: redirect.page, | ||||
|             redirectParams: redirect.params, | ||||
|         }; | ||||
| 
 | ||||
|         return route; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -549,6 +549,19 @@ export class CoreAppProvider { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Retrieve and forget redirect data. | ||||
|      * | ||||
|      * @return Redirect data if any. | ||||
|      */ | ||||
|     consumeRedirect(): CoreRedirectData | null { | ||||
|         const redirect = this.getRedirect(); | ||||
| 
 | ||||
|         this.forgetRedirect(); | ||||
| 
 | ||||
|         return redirect; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Forget redirect data. | ||||
|      */ | ||||
| @ -559,7 +572,7 @@ export class CoreAppProvider { | ||||
|     /** | ||||
|      * Retrieve redirect data. | ||||
|      * | ||||
|      * @return Object with siteid, state, params and timemodified. | ||||
|      * @return Redirect data if any. | ||||
|      */ | ||||
|     getRedirect(): CoreRedirectData | null { | ||||
|         return this.redirect || null; | ||||
|  | ||||
| @ -30,6 +30,7 @@ import { makeSingleton, NavController, Router } from '@singletons'; | ||||
| import { CoreScreen } from './screen'; | ||||
| import { filter } from 'rxjs/operators'; | ||||
| import { CoreApp } from './app'; | ||||
| import { CoreSitePlugins } from '@features/siteplugins/services/siteplugins'; | ||||
| 
 | ||||
| const DEFAULT_MAIN_MENU_TAB = CoreMainMenuHomeHandlerService.PAGE_NAME; | ||||
| 
 | ||||
| @ -208,7 +209,14 @@ export class CoreNavigatorService { | ||||
| 
 | ||||
|         // If we are logged into a different site, log out first.
 | ||||
|         if (CoreSites.isLoggedIn() && CoreSites.getCurrentSiteId() !== siteId) { | ||||
|             // @todo: Check site plugins and store redirect.
 | ||||
|             if (CoreSitePlugins.hasSitePluginsLoaded) { | ||||
|                 // The site has site plugins so the app will be restarted. Store the data and logout.
 | ||||
|                 CoreApp.instance.storeRedirect(siteId, path, options.params || {}); | ||||
| 
 | ||||
|                 await CoreSites.logout(); | ||||
| 
 | ||||
|                 return true; | ||||
|             } | ||||
| 
 | ||||
|             await CoreSites.logout(); | ||||
|         } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user