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?
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) -
0
Angular
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) -
0
See https://docs.abp.io/en/abp/latest/UI/Angular/Component-Replacement#how-to-replace-a-layout. You should replace
AccountLayoutComponent.Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) -
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?
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) -
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 --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 }), ); } }Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) - 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?
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) -
0
Hi,
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, }, }Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) -
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?
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) -
0
Hi Ian,
I created two GitHub gists to explain how to replace the RegisterComponent and the ForgotPasswordComponent.
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) -
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); ...Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) -
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.
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) - You can pass another fields to
-
0Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
-
0
ut where is the register function?
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) -
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
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
