Can I get some direction on acheiving the following two issues related to the logon screen
- Remove teh tenant selection from the logon screen and provide it only to admin users after logon. This could be on another page prior to showing the app or from a button in the app, maybe on the top toolbar.
- Modify the layout of the logon page to display items other than the logon card, for say marketing information.
Thank you.
16 Answer(s)
- 
    0Are you using razor page or angular UI? 
- 
    0Angular 
- 
    0See https://docs.abp.io/en/abp/latest/UI/Angular/Component-Replacement#how-to-replace-a-layout. You should replace AccountLayoutComponent.
- 
    0Yes I have seen that. Two issues - That solution requires us to recreate what is already there which we do not know. (i.e. what apis are called to accomplish login and language selection etc?). I suppose we can hide what we need using css but this is not desirable.
- How do we provide tenant login without the logon page?
 FYI. Just a thought. I still do not understand why these layouts are not simple provided so we can just modify them as needed. Is it intended to push sales of higher licenses? 
- 
    0Hi, I think you can achieve what you want by replacing AccountComponent.Run the following command to create a component: yarn ng generate component account --entryComponentThen open account.component.tsinapp/accountfolder and replace the content with below:import { ApplicationConfiguration, Config, ConfigState, getAbpRoutes, SessionState, SetLanguage, } from '@abp/ng.core'; import { Component } from '@angular/core'; import { Router } from '@angular/router'; import { Select, Store } from '@ngxs/store'; import { eAccountComponents } from '@volo/abp.ng.account'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import snq from 'snq'; @Component({ selector: 'abp-account', templateUrl: './account.component.html', }) export class AccountComponent { @Select(ConfigState.getDeep('multiTenancy.isEnabled')) isMultiTenancyEnabled$: Observable<boolean>; @Select(ConfigState.getDeep('localization.languages')) languages$: Observable<ApplicationConfiguration.Language[]>; tenantBoxKey = eAccountComponents.TenantBox; get defaultLanguage$(): Observable<{ displayName: string; flagIcon: string }> { return this.languages$.pipe( map(languages => { const lang: Partial<ApplicationConfiguration.Language> = snq( () => languages.find(l => l.cultureName === this.selectedLangCulture), {} as any, ); return { displayName: lang.displayName || '', flagIcon: lang.flagIcon, }; }), ); } get dropdownLanguages$(): Observable<ApplicationConfiguration.Language[]> { return this.languages$.pipe( map( languages => snq(() => languages.filter(lang => lang.cultureName !== this.selectedLangCulture)), [], ), ); } get selectedLangCulture(): string { return this.store.selectSnapshot(SessionState.getLanguage); } get appInfo(): Config.Application { return this.store.selectSnapshot(ConfigState.getApplicationInfo); } get pageLabel(): string { return getAbpRoutes() .find(route => route.path === 'account') .children.reduce((acc, val) => { if (this.router.url.includes(val.path)) { return val.name; } return acc; }, ''); } constructor(private store: Store, private router: Router) {} onChangeLang(cultureName: string) { this.store.dispatch(new SetLanguage(cultureName)); } }Open account.component.htmland replace content with the following:<div class="row"> <div class="col col-md-6 col-lg-4 offset-md-3 offset-lg-4"> <div class="account-brand p-4 text-center mb-1"> <a #navbarBrand class="navbar-brand" routerLink="/" alt="Logo"></a> </div> <div class="card"> <div class="card-header"> <h2 class="card-title d-inline-block">{{ pageLabel | abpLocalization }}</h2> <nav class="navbar navbar-expand p-0 pt-1 float-right"> <ul class="navbar-nav ml-auto toolbar-nav"> <li class="nav-item"> <div class="dropdown" ngbDropdown> <a *ngIf="defaultLanguage$ | async as defaultLang" class="pointer" role="button" id="dropdownMenuLink" ngbDropdownToggle [class.dropdown-toggle]="false" > <span class="flag-icon flag-icon-squared flag-icon-{{ defaultLang.flagIcon }}" ></span> </a> <div ngbDropdownMenu class="dropdown-menu dropdown-menu-right" *ngIf="(dropdownLanguages$ | async).length > 0" > <a *ngFor="let lang of dropdownLanguages$ | async" class="dropdown-item pointer" (click)="onChangeLang(lang.cultureName)" > <span class="flag-icon flag-icon-{{ lang.flagIcon }} flag-icon-squared mr-2" ></span> {{ lang?.displayName }}</a > </div> </div> </li> </ul> </nav> </div> <div class="card-body"> <router-outlet></router-outlet> </div> </div> <div class="p-3 text-center text-muted"> <div class="copyright"> <span>© {{ appInfo.name }}</span ><br /> </div> </div> </div> </div>Now, you should replace the AccountComponent with your own AccountComponent. Open app.component.ts;- Inject the Store
- Dispatch the AddReplaceableComponentaction.
 import { ..., AddReplaceableComponent } from '@abp/ng.core'; // import AddReplaceableComponent import { Store } from '@ngxs/store'; // import Store import { AccountComponent } from './account/account.component'; // import your AccountComponent import { eAccountComponents } from '@volo/abp.ng.account'; // import eAccountComponents // ... export class AppComponent implements OnInit { constructor(..., private store: Store) {} // injected store ngOnInit() { //... this.store.dispatch( new AddReplaceableComponent({ component: AccountComponent, key: eAccountComponents.Account }), ); } }
- Inject the 
- 
    0Almost there. So finally how do I make the starter templete open up directly with the login screen rather that the public page with a login button? I can find the path in app-routing.module.ts? 
- 
    0@ian@order2invoice.com showing login page everytime you open the website is weird. Do you want to show login page even the user is authenticated? 
- 
    0Hi, You can protect your home page with the AuthGuardthat redirects the users to the login page when the user is not authenticated. Replace the home page route inapp-routing.module.tsas shown below:// AuthGuard can be imported from @abp/ng.core { path: '', loadChildren: () => import('./home/home.module').then(m => m.HomeModule), canActivate: [AuthGuard], // added this line data: { routes: { name: '::Menu:Home', iconClass: 'fa fa-home', } as ABP.Route, }, }
- 
    0Hi Mehmet, The above works thanks. Thanks. However we need to provide our own Forget Password Page (we need to user Mailgun for emails which we can't find a way of doing within the existing framework), and Registration Page (we register a company, not a user). The links to both of these are hidden in the card-body as follows <div class="card-body"> <router-outlet></router-outlet> </div> Is there a way of modifying these to links to take user to our own pages? 
- 
    0Hi Ian, I created two GitHub gists to explain how to replace the RegisterComponent and the ForgotPasswordComponent. 
- 
    0Thanks Mehmet, Almost there. - Registration: If I want to process further information, like tenant name to create a new tenant for the user do I need to do another call to the server in the finalise clause? Or should I create a new endpoint to emulate the register call. If the latter, What is happening in this call. Is it simply creating a user, or is there more I need to do, or can to create an account service like in the easyCrm example and call registration that way.
 this.accountService.register(newUser)- Forgotten User: The logic and emails for Forgotten User is together in this.accountService.sendPasswordResetCode. If I am using my own emailer I need to replace this with my own endpoint. What do I need to send. In .Net Core Identity I would do something like this but I don't know what to do is this case because I can't see the code.
 ... var user = await _userManager.FindByEmailAsync(Input.Email); var code = await _userManager.GeneratePasswordResetTokenAsync(user); var callbackUrl = Url.ResetPasswordCallbackLink(user.Id, code, Request.Scheme); ...
- 
    0- You can pass another fields to registermethod like below:
 this.accountService.register({...newUser, foo: 1, bar: 2} as any);If you create a new endpoint in backend, you should create your own service. 
- You can pass another fields to 
- 
    0
- 
    0ut where is the register function? 
- 
    0Forgotten User: The logic and emails for Forgotten User is together in this.accountService.sendPasswordResetCode. If I am using my own emailer I need to replace this with my own endpoint. What do I need to send. In .Net Core Identity I would do something like this but I don't know what to do is this case because I can't see the code. to replace the existing email sender service; https://support.abp.io/QA/Questions/260/How-to-replace-the-existing-email-sender-with-my-own#answer-cc999500-981c-25df-e932-39f5f3e7f71b 
- 
    0Hi @arifharsono Can you create a new ticket and describe it in detail? 

 
                                