Activities of "Mehmet"

Hi,

We've created an internal issue about this. I'll inform you when it is done. Thanks!

Hi,

I've created an issue: https://github.com/abpframework/abp/issues/9343 Please follow that for the progress.

You can set the datatable messages in v4.4-preview. I am closing the question. If you encounter any problem, please let me know. Thanks!

Hi,

We've created an internal issue. Thanks for the reporting.

Hi,

We've added 2FA feature to Angular UI. It will be available in v4.4.

Thanks.

Hi,

Please follow the steps below to replace personal settings component:

  • Run the following command to generate account module:
yarn ng generate module account
  • Run command below to generate a component called CustomPersonalSettings:
yarn ng generate component account/custom-personal-settings --skip-tests --inline-style
  • Replace the generated src/account/account.module.ts content with following:
import { NgModule } from '@angular/core';
import { AccountPublicModule } from '@volo/abp.ng.account/public';
import { SharedModule } from '../shared/shared.module';
import { CustomPersonalSettingsComponent } from './custom-personal-settings/custom-personal-settings.component';

@NgModule({
  declarations: [CustomPersonalSettingsComponent],
  imports: [AccountPublicModule.forChild({}), SharedModule],
})
export class AccountModule {}
  • Replace the generated src/account/custom-personal-settings/custom-personal-settings.component.ts content with following:
import { ConfigStateService, ProfileService, SubscriptionService } from '@abp/ng.core';
import { Confirmation, ConfirmationService, ToasterService } from '@abp/ng.theme.shared';
import { Component, EmbeddedViewRef, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import {
  AccountService,
  ManageProfileStateService,
  ProfileResponse,
} from '@volo/abp.ng.account/public';
import { Observable } from 'rxjs';
import { filter, tap } from 'rxjs/operators';

@Component({
  selector: 'app-custom-personal-settings',
  templateUrl: './custom-personal-settings.component.html',
  styles: [],
})
export class CustomPersonalSettingsComponent implements OnInit {
  storedProfile: ProfileResponse;

  profile$: Observable<ProfileResponse> = this.manageProfileState.getProfile$();

  modalVisible = false;

  modalBusy: boolean = false;

  modalRef: EmbeddedViewRef<any>;

  form: FormGroup;

  token: string = '';

  isEmailUpdateEnabled: boolean = true;
  isUserNameUpdateEnabled: boolean = true;

  showEmailVerificationBtn$ = this.manageProfileState.createStateStream(
    state => !state.hideEmailVerificationBtn
  );

  get userId(): string {
    return this.configState.getDeep('currentUser.id');
  }

  askForImmediatePhoneNumberVerification = () => {
    this.confirmationService
      .info('AbpAccount::DoYouWantToVerifyPhoneNumberMessage', 'AbpAccount::ConfirmYourPhoneNumber')
      .pipe(filter(status => status === Confirmation.Status.confirm))
      .subscribe(this.initPhoneNumberConfirmation);
  };

  checkPhoneNumberChanges = ({ phoneNumber }: ProfileResponse) => {
    if (phoneNumber && phoneNumber !== this.storedProfile?.phoneNumber) {
      this.askForImmediatePhoneNumberVerification();
    }
  };

  buildForm = (profile: ProfileResponse) => {
    this.form = this.fb.group({
      userName: [profile.userName, [Validators.required, Validators.maxLength(256)]],
      email: [profile.email, [Validators.required, Validators.email, Validators.maxLength(256)]],
      name: [profile.name || '', [Validators.maxLength(64)]],
      surname: [profile.surname || '', [Validators.maxLength(64)]],
      phoneNumber: [profile.phoneNumber || '', [Validators.maxLength(16)]],
    });
  };

  initPhoneNumberConfirmation = () => {
    this.accountService
      .sendPhoneNumberConfirmationToken({
        phoneNumber: this.form.value.phoneNumber,
        userId: this.userId,
      })
      .pipe(tap(() => (this.token = '')))
      .subscribe(this.openModal);
  };

  openModal = () => {
    this.modalVisible = true;
  };

  removeModal = () => {
    this.modalVisible = false;
  };

  setPhoneNumberAsConfirmed = () => {
    this.storedProfile.phoneNumberConfirmed = true;
  };

  constructor(
    private fb: FormBuilder,
    private accountService: AccountService,
    private toasterService: ToasterService,
    private confirmationService: ConfirmationService,
    private configState: ConfigStateService,
    private subscription: SubscriptionService,
    private manageProfileState: ManageProfileStateService,
    private profileService: ProfileService
  ) {}

  confirmPhoneNumber() {
    this.accountService
      .confirmPhoneNumber({ token: this.token, userId: this.userId })
      .pipe(tap(this.setPhoneNumberAsConfirmed), tap(this.removeModal))
      .subscribe(() => {
        this.toasterService.success('AbpAccount::Verified', '', { life: 5000 });
      });
  }

  sendEmailVerificationToken() {
    this.accountService
      .sendEmailConfirmationToken({
        appName: 'Angular',
        email: this.form.value.email,
        userId: this.userId,
      })
      .subscribe(() => {
        this.toasterService.success('AbpAccount::EmailConfirmationSentMessage', '', {
          messageLocalizationParams: [this.form.value.email],
        });

        this.manageProfileState.setHideEmailVerificationBtn(true);
      });
  }

  ngOnDestroy() {
    this.removeModal();
  }

  ngOnInit() {
    this.subscription.addOne(
      this.profile$.pipe(
        filter<ProfileResponse>(Boolean),
        tap(profile => (this.storedProfile = profile)),
        tap(this.checkPhoneNumberChanges)
      ),
      this.buildForm
    );

    const settings = this.configState.getSettings();
    this.isEmailUpdateEnabled =
      (settings['Abp.Identity.User.IsEmailUpdateEnabled'] || '').toLowerCase() !== 'false';
    this.isUserNameUpdateEnabled =
      (settings['Abp.Identity.User.IsUserNameUpdateEnabled'] || '').toLowerCase() !== 'false';
  }

  submit() {
    if (this.form.invalid) return;

    const { phoneNumberConfirmed, ...profile } = this.form.value;

    this.profileService.update(profile).subscribe(res => {
      this.manageProfileState.setProfile(res);
      this.toasterService.success('AbpAccount::PersonalSettingsSaved', '', { life: 5000 });
    });
  }
}
  • Replace the generated src/account/custom-personal-settings/custom-personal-settings.component.html content with following:
<form validateOnSubmit *ngIf="form" [formGroup]="form" (ngSubmit)="submit()">
  <div class="form-group">
    <label for="username">{{ 'AbpIdentity::DisplayName:UserName' | abpLocalization }}</label
    ><span> * </span
    ><input
      type="text"
      id="username"
      class="form-control"
      formControlName="userName"
      autofocus
      (keydown.space)="$event.preventDefault()"
      [readonly]="!isUserNameUpdateEnabled"
    />
  </div>
  <div class="row">
    <div class="col col-md-6">
      <div class="form-group">
        <label for="name">{{ 'AbpIdentity::DisplayName:Name' | abpLocalization }}</label
        ><input type="text" id="name" class="form-control" formControlName="name" />
      </div>
    </div>
    <div class="col col-md-6">
      <div class="form-group">
        <label for="surname">{{ 'AbpIdentity::DisplayName:Surname' | abpLocalization }}</label
        ><input type="text" id="surname" class="form-control" formControlName="surname" />
      </div>
    </div>
  </div>
  <div class="form-group">
    <label for="email-address">{{ 'AbpIdentity::DisplayName:Email' | abpLocalization }}</label
    ><span> * </span>
    <div class="input-group" validationTarget validationStyle>
      <input
        type="text"
        id="email-address"
        class="form-control"
        formControlName="email"
        [readonly]="!isEmailUpdateEnabled"
      />
      <div
        *ngIf="{
          edited: form.value.email !== storedProfile?.email,
          confirmed: storedProfile?.emailConfirmed
        } as data"
        class="input-group-append"
      >
        <abp-personal-settings-verify-button
          *ngIf="(showEmailVerificationBtn$ | async) || data.edited || data.confirmed"
          [verified]="data.confirmed"
          [edited]="data.edited"
          (btnClick)="sendEmailVerificationToken()"
        ></abp-personal-settings-verify-button>
      </div>
    </div>
  </div>
  <div class="form-group">
    <label for="phone-number">{{ 'AbpIdentity::DisplayName:PhoneNumber' | abpLocalization }}</label>
    <div class="input-group mb-3">
      <input type="text" id="phone-number" class="form-control" formControlName="phoneNumber" />
      <div class="input-group-append" *ngIf="storedProfile?.phoneNumber">
        <abp-personal-settings-verify-button
          [verified]="storedProfile?.phoneNumberConfirmed"
          [edited]="form.value.phoneNumber !== storedProfile?.phoneNumber"
          (btnClick)="initPhoneNumberConfirmation()"
        ></abp-personal-settings-verify-button>
      </div>
    </div>
  </div>
  <abp-button
    iconClass="fa fa-check"
    buttonClass="btn btn-primary color-white"
    buttonType="submit"
    [disabled]="form?.invalid"
  >
    {{ 'AbpIdentity::Save' | abpLocalization }}</abp-button
  >
</form>

<abp-modal [(visible)]="modalVisible" [busy]="modalBusy" size="sm">
  <ng-template #abpHeader>
    <h5>{{ 'AbpAccount::Verify' | abpLocalization }}</h5>
  </ng-template>
  <ng-template #abpBody>
    <form (ngSubmit)="confirmPhoneNumber()">
      <div class="mt-2">
        <p>
          {{ 'AbpAccount::ConfirmationTokenSentMessage' | abpLocalization: form.value.phoneNumber }}
        </p>
        <div class="form-group">
          <label for="confirm-phone-number">{{
            'AbpAccount::PhoneConfirmationToken' | abpLocalization
          }}</label>
          <input
            [(ngModel)]="token"
            id="confirm-phone-number"
            name="confirm-phone-number"
            class="form-control"
            type="text"
            autofocus
          />
        </div>
      </div>
    </form>
  </ng-template>
  <ng-template #abpFooter>
    <button abpClose type="button" class="btn btn-secondary">
      {{ 'AbpAccount::Cancel' | abpLocalization }}
    </button>
    <abp-button type="abp-button" iconClass="fa fa-check" (click)="confirmPhoneNumber()">
      {{ 'AbpAccount::Save' | abpLocalization }}
    </abp-button>
  </ng-template>
</abp-modal>
  • Open src/app-routing.module.ts file modify account route like this:
  {
    path: 'account',
    loadChildren: () => import('./account/account.module').then(m => m.AccountModule),
  },
  • Open src/app.component.ts and replace the PersonalSettingsComponent as shown below:
import { Component, OnInit } from '@angular/core';
import {
  eAccountManageProfileTabNames,
  ManageProfileTabsService,
} from '@volo/abp.ng.account/public/config';
import { CustomPersonalSettingsComponent } from './account/custom-personal-settings/custom-personal-settings.component';

@Component({
  selector: 'app-root',
  template: `
    &lt;abp-loader-bar&gt;&lt;/abp-loader-bar&gt;
    &lt;abp-dynamic-layout&gt;&lt;/abp-dynamic-layout&gt;
  `,
})
export class AppComponent implements OnInit {
  constructor(
    private manageProfileTabs: ManageProfileTabsService
  ) {}

  ngOnInit() {
    this.manageProfileTabs.patch(eAccountManageProfileTabNames.PersonalInfo, {
      component: CustomPersonalSettingsComponent,
    });
  }
}

You can replace a tab in the my profile page by using the ManageProfileTabsService.

I hope this helps you.

We've fixed the problem. Please wait for v4.3.2. Thanks.

If I am right, it is related to this ticket

Yes, you can follow the other ticket for the progress. I am closing this one. If you encounter any problem, feel free to open the ticket.

Thanks.

Hi,

You can add an interceptor to get localizations in the language you specify. Please follow the steps below to achieve this:

  • Create a file called accept-language.interceptor.ts
  • Change its content with the following:
import { SessionStateService } from '@abp/ng.core';
import { HttpHandler, HttpHeaders, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class AcceptLanguageInterceptor implements HttpInterceptor {
  constructor(private sessionState: SessionStateService) {}

  intercept(request: HttpRequest<any>, next: HttpHandler) {
    return next.handle(
      request.clone({
        setHeaders: this.getAdditionalHeaders(request.headers),
      }),
    );
  }

  getAdditionalHeaders(existingHeaders?: HttpHeaders) {
    const headers = {} as any;

    // When sessionState.getLanguage() returns null at first initialization.
    // So you can set your default language as shown below:
    const lang = this.sessionState.getLanguage() || 'es' // put your default language here.
    if (!existingHeaders?.has('Accept-Language')) {
      headers['Accept-Language'] = lang;
    }

    return headers;
  }
}
  • Add the provider below to the AppModule:
providers: [
    //...
    {
      provide: HTTP_INTERCEPTORS,
      useExisting: AcceptLanguageInterceptor,
      multi: true,
    },
  ],

I hope this help you.

Hi,

I created an issue: https://github.com/abpframework/abp/issues/9160

Thanks for the reporting.

Hi,

We did not receive any email. Please email me (mehmet.erim@volosoft.com).

Showing 41 to 50 of 258 entries
Made with ❤️ on ABP v9.0.0-preview Updated on September 19, 2024, 10:13