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)
-
0
Are you using razor page or angular UI?
-
0
Angular
-
0
See https://docs.abp.io/en/abp/latest/UI/Angular/Component-Replacement#how-to-replace-a-layout. You should replace
AccountLayoutComponent
. -
0
Yes 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?
-
0
Hi,
I think you can achieve what you want by replacing
AccountComponent
.Run the following command to create a component:
yarn ng generate component account --entryComponent
Then open
account.component.ts
inapp/account
folder 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.html
and 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
AddReplaceableComponent
action.
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
-
0
Almost 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?
-
0
Hi,
You can protect your home page with the
AuthGuard
that redirects the users to the login page when the user is not authenticated. Replace the home page route inapp-routing.module.ts
as 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, }, }
-
0
Hi 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?
-
0
Hi Ian,
I created two GitHub gists to explain how to replace the RegisterComponent and the ForgotPasswordComponent.
-
0
Thanks 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
register
method 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
-
0
ut where is the register function?
-
0
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.
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
-
0
Hi @arifharsono
Can you create a new ticket and describe it in detail?