Base solution for your next web application

Activities of "schlarmanp"

I've generated a test lookup entity "TestLookup" with just one property "LookupDescription" (string).

I then generated a test entity "TestEntity" with one property "Description" and one navigation property "TestLookupId" which references "TestLookup". "LookupDescription" is the display property. I've tried generating with setting the "nullable" checkbox check and also unchecked.

Regardless, when I generate "TestEntity" and test the UI, the edit modal always displays the lookup control with the "Pick" search button (magnifying glass) icon. I don't want this. I simply want a dropdown list containing the lookup values.

How do I accomplish this via PowerTools?

Cheers

Is there any write-up or guidance how to modify the generated code to accomplish this?

New File Upload ASP.NET Core API method (clone of user upload) not working - 401 unauthorized error

This is v6.9 angular/asp.net core template.

  1. I have a requirement to upload/import a spreadsheet and load the data into the database.
  2. The pattern follows nicely the same pattern implemented in your framework to upload users.
  3. I cloned the modules related to user upload from Excel and transformed into my custom modules:

**asp.net core side solution:

.Application project:

  1. Cloned and modified all related class files under /Authorization/Users/Importing to /CashSheets/Importing a. Dto/ImportUserDto.cs to Dto/ImportCashSheetTransactionDto.cs b. /IInvalidUserExporter.cs to /IInvalidCashSheetTransactionExporter.cs c. /ImportUsersToExcelJob.cs to /ImportCashSheetTransactionsFromExcelJob.cs d. /InvalidUserExporter.cs to /InvalidCashSheetTransactionExporter.cs e. /UserListExcelDataReader.cs to /CashSheetTransactionListExcelDataReader.cs f. /IUserListExcelDataReader.cs to /ICashSheetTransactionListExcelDataReader.cs

.Shared project:

  1. Cloned and modified /Authorization/Users/Dto/ImportUsersFromExcelJobArgs.cs to /CashSheets/Dto/ImportCashSheetTransactionsFromExcelJobArgs.cs

.Web.Core project:

  1. Cloned and modified /Controllers/UsersControllerBase.cs to /Controllers/CashSheetsControllerBase.cs

.Web.Host project:

  1. Cloned and modified /Controllers/UsersController.cs to /Controllers/CashSheetsController.cs

**CashSheetsControllerBase.cs:

using System; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Abp.IO.Extensions; using Abp.UI; using Abp.Web.Models; using SchlarmanConsulting.OneTAFUnify.Authorization.Users.Dto; using SchlarmanConsulting.OneTAFUnify.Storage; using Abp.BackgroundJobs; using SchlarmanConsulting.OneTAFUnify.Authorization; using Abp.AspNetCore.Mvc.Authorization; using Abp.Authorization; using Abp.Runtime.Session; using GraphQL; using Microsoft.AspNetCore.Authorization; using SchlarmanConsulting.OneTAFUnify.CashSheets; using SchlarmanConsulting.OneTAFUnify.CashSheets.Dto; using SchlarmanConsulting.OneTAFUnify.CashSheets.Importing;

namespace SchlarmanConsulting.OneTAFUnify.Web.Controllers { public abstract class CashSheetsControllerBase : OneTAFUnifyControllerBase { protected readonly IBinaryObjectManager BinaryObjectManager; protected readonly IBackgroundJobManager BackgroundJobManager;

    protected CashSheetsControllerBase(
        IBinaryObjectManager binaryObjectManager,
        IBackgroundJobManager backgroundJobManager)
    {
        BinaryObjectManager = binaryObjectManager;
        BackgroundJobManager = backgroundJobManager;
    }

    //[AllowAnonymous] //currently only way this works, but then can't get abp user object needed for transactions :\
    //[AbpAllowAnonymous] //this doesn't work either
    [HttpPost]
    [AbpAuthorize,
     AbpMvcAuthorize,
     AbpMvcAuthorize(AppPermissions.Pages_ProcessCashSheets), 
     AbpMvcAuthorize(AppPermissions.Pages_ProcessCashSheets_CashSheetBatches_Create), 
     AbpMvcAuthorize(AppPermissions.Pages_ProcessCashSheets_CashSheetBatches_Edit)]
    public async Task<JsonResult> ImportFromExcel()
    {
        try
        {
            var file = Request.Form.Files.First();
            //TODO: parse file name for batch number and org, etc.

            if (file == null)
            {
                throw new UserFriendlyException(L("File_Empty_Error"));
            }

            if (file.Length > 1048576 * 100) //100 MB
            {
                throw new UserFriendlyException(L("File_SizeLimit_Error"));
            }

            byte[] fileBytes;
            using (var stream = file.OpenReadStream())
            {
                fileBytes = stream.GetAllBytes();
            }

            var tenantId = AbpSession.TenantId;
            var fileObject = new BinaryObject(tenantId, fileBytes);
            //set CashSheetBatchID also to assign to args
            var dict = Request.Form.ToDictionary(x => x.Key, x => x.Value.ToString());
            var cashSheetBatchID = Int32.Parse(dict["CashSheetBatchID"]);

            await BinaryObjectManager.SaveAsync(fileObject);

            await BackgroundJobManager.EnqueueAsync<ImportCashSheetTransactionsFromExcelJob, ImportCashSheetTransactionsFromExcelJobArgs>(new ImportCashSheetTransactionsFromExcelJobArgs
            {
                //These fields are required for core operation
                //This is where arg values are set
                TenantId = tenantId,
                BinaryObjectId = fileObject.Id,
                User = AbpSession.ToUserIdentifier(), //think this isn't working because entering anonymously
                //User = objUser,
                //need to also set passed in CashSheetBatchID
                CashSheetBatchID = cashSheetBatchID

            });

            return Json(new AjaxResponse(new { }));
        }
        catch (UserFriendlyException ex)
        {
            return Json(new AjaxResponse(new ErrorInfo(ex.Message)));
        }
    }
}

}

**CashSheetsController.cs:

using Abp.AspNetCore.Mvc.Authorization; using SchlarmanConsulting.OneTAFUnify.Authorization; using SchlarmanConsulting.OneTAFUnify.Storage; using Abp.BackgroundJobs;

namespace SchlarmanConsulting.OneTAFUnify.Web.Controllers { //auth seems to be working fine here [AbpMvcAuthorize(AppPermissions.Pages_ProcessCashSheets_CashSheetBatches_Create), AbpMvcAuthorize(AppPermissions.Pages_ProcessCashSheets_CashSheetBatches_Edit)] public class CashSheetsController : CashSheetsControllerBase { public CashSheetsController(IBinaryObjectManager binaryObjectManager, IBackgroundJobManager backgroundJobManager) : base(binaryObjectManager, backgroundJobManager) { } } }

Angular solution:

Create-or-edit-cashSheetBatch-modal.component.html:

                    <a href="javascript:;" *ngIf="isGranted('Pages.ProcessCashSheets')" class="no-padding">
                        <span>                          
                            <p-fileUpload customUpload="true"
                                            name="ExcelFileUpload"
                                            #ExcelFileUpload
                                            maxFileSize="10000000"
                                            auto="auto"
                                            accept=".csv,.xls,.xlsx"
                                            (uploadHandler)="uploadExcel($event)"
                                            (onError)="onUploadExcelError()"
                                            chooseLabel="{{'ImportFromExcel' | localize}}">
                            </p-fileUpload>
                        </span>
                    </a>

Create-or-edit-cashSheetBatch-modal.component.ts:

//upload import { FileUpload } from 'primeng/fileupload'; import { HttpClient } from '@angular/common/http';

export class CreateOrEditCashSheetBatchModalComponent extends AppComponentBase implements OnInit{

//CONSTRUCTOR
constructor(
    injector: Injector,
    private _cashSheetTransactionsServiceProxy: CashSheetTransactionsServiceProxy, //added for local use to load bank filter dropdown
    private _cashSheetBatchesServiceProxy: CashSheetBatchesServiceProxy,
    public formBuilderStepper:FormBuilder, //stepper visual
    private _httpClient: HttpClient, //for file upload
    private formBuilderCashSheetAccountSummaries: FormBuilder, //for accountsummaries 
    public cashSheetAccountSummariesEditService: CashSheetAccountSummariesEditService //for account summaries
) {
    super(injector);
    this.uploadUrl = AppConsts.remoteServiceBaseUrl + '/CashSheets/ImportFromExcel'; //for file upload
}

//IMPORT TAB DECLARATIONS
//file upload
@ViewChild('ExcelFileUpload') excelFileUpload: FileUpload;
//file upload
uploadUrl: string;

//IMPORT TAB EVENT HANDLERS

uploadExcel(data: { files: File }): void {
    
    const formData: FormData = new FormData();
    const file = data.files[0];
    var cashSheetBatchID: string = "";

    formData.append('file', file, file.name);

    //get cash sheet batch id - this should be created in show
    if (this.cashSheetBatch.id == null) {
        //do nothing, accept initialized value
    } else {
        //cast to string
        cashSheetBatchID=this.cashSheetBatch.id.toString();
    }

    //Append Cash Sheet Batch ID to form object
    formData.append("CashSheetBatchID", cashSheetBatchID);
    //post the request - transfer the Excel file to the API for import/processing
    //TODO: get list of errors back from API - this will populate the new "problems" table to replace
    //the field in the equivalent LP table
    //TODO: Also need to pass in QBBatchSummary start and end date values - remote function will populate
    //based on detail data
    this._httpClient
        .post<any>(this.uploadUrl, formData)
        .pipe(finalize(() => this.excelFileUpload.clear()))
        .subscribe(response => {
            if (response.success) {
                this.notify.success(this.l('ImportCashSheetProcessStart'));
            } else if (response.error != null) {
                this.notify.error(this.l('ImportCashSheetUploadFailed'));
            }
        });
}

onUploadExcelError(): void {
    this.notify.error(this.l('ImportCashSheetUploadFailed'));
}

**So, I have a catch 22:

If I leave the [AllowAnonymous] decorator off CashSheetsControllerBase.ImportFromExcel, then I get a permission error:

Failed to load resource: the server responded with a status of 401 (Unauthorized) [http://localhost:22742/CashSheets/ImportFromExcel] ERROR core.js:15724 HttpErrorResponse {headers: HttpHeaders, status: 401, statusText: "Unauthorized", url: "http://localhost:22742/CashSheets/ImportFromExcel", ok: false, …} core.js:15724

I've tried multiple combinations of [AbpMvcAuthorize] decorator values, and nothing seems to work.

If I set the decorator on CashSheetsControllerBase.ImportFromExcel to [AllowAnonymous], I can use the method without the above permissions error from angular, but the User object is not populated, and I can’t use it further on in processing as I need.

I’ve compared all the modules in the UserUpload process to my cloned modules, and it seems like I’m doing everything the same. I must be missing something. Any thoughts?

Hi,

Yes, I can import users (if the input file contains certain data in the exact right format) and also the httppost method/file upload works without permission error. I think there are some other bugs in the user import (it doesn’t return erroneous records correctly, etc.), but the main thing is I need to get past this permission error. Other thoughts on what to try?

Lastly (but secondary importance), is there a fix for known users import bugs that I can implement without upgrading the template?

Hi again - could you tell me how the abp auth works User method "ImportFromExcel" (of UsersControllerBase.cs) and not my clone of that class/method (CashSheetsControllerBase.cs)? Seems I'm missing some config or decorator somewhere, but I've tried everything I can think of. As you can see, I've also tried a straight [AbpMvcAuthorize] (which just requires a user logon, correct?) which doesn't work either.

Any help is much appreciated.

Cheers

Hi @ismcagdas - no, still have not solved this.

Hi,

The addition of AbpHttpInterceptor references as suggested worked for passing authentication to the upload httppost method. The method can be adorned with [AbpAuthorize] successfullly and once inside the method, I can see the user object is populated. Thank you.

    [HttpPost]
    [AbpMvcAuthorize]
    //[AbpAuthorize,
    // AbpMvcAuthorize,
    // AbpMvcAuthorize(AppPermissions.Pages_ProcessCashSheets), 
    // AbpMvcAuthorize(AppPermissions.Pages_ProcessCashSheets_CashSheetBatches_Create), 
    // AbpMvcAuthorize(AppPermissions.Pages_ProcessCashSheets_CashSheetBatches_Edit)]
    public async Task<JsonResult> ImportFromExcel()
    {
        try
        {
            var file = Request.Form.Files.First();
            //TODO: parse file name for batch number and org, etc.

            if (file == null)
            {
            ...
    

User obj is populated:

            ?AbpSession.ToUserIdentifier()

{3@1} TenantId: 1 UserId: 3

I just have one follow-up question. Once further along inside the method, when I make a call to an app object to interact with (PowerTools generated) entity objects, if the method is adorned with any [AbpAuthorize] tag, an error "Current user did not login to the application!" is thrown.

CashSheetTransactionAppService.cs:

namespace SchlarmanConsulting.OneTAFUnify.CashSheets { [AbpAuthorize] //if I remove this everything works public class CashSheetTransactionsAppService : OneTAFUnifyAppServiceBase, ICashSheetTransactionsAppService { private readonly IRepository<CashSheetTransaction> _cashSheetTransactionRepository; private readonly ICashSheetTransactionsExcelExporter _cashSheetTransactionsExcelExporter;

	  public CashSheetTransactionsAppService(IRepository&lt;CashSheetTransaction&gt; cashSheetTransactionRepository, ICashSheetTransactionsExcelExporter cashSheetTransactionsExcelExporter ) 
	  {
		_cashSheetTransactionRepository = cashSheetTransactionRepository;
		_cashSheetTransactionsExcelExporter = cashSheetTransactionsExcelExporter;
		
	  }

...

[AbpAuthorize] private async Task Create(CreateOrEditCashSheetTransactionDto input) { var cashSheetTransaction = ObjectMapper.Map<CashSheetTransaction>(input);

        await _cashSheetTransactionRepository.InsertAsync(cashSheetTransaction);
     }
     ...
     

When I attempt to call this further along after the httppost method entry,

      //Create insertable object
        var cashSheetTransaction = new CreateOrEditCashSheetTransactionDto
        {
            CashSheetBatchID = input.CashSheetBatchID,
            CashSheetBatchAccountSummaryID = input.CashSheetBatchAccountSummaryID,
            CostpointProjectCode = input.CostpointProjectCode,
            CostpointAccountNumber = input.CostpointAccountNumber,
            AccountAbbreviation = input.AccountAbbreviation,
            CostpointOrganizationCode = input.CostpointOrganizationCode,
            TransactionDescription = input.TransactionDescription,
            TransactionAmountUSD = input.TransactionAmountUSD,
            QBAccountNumber = input.QBAccountNumber,
            QBAmount = input.QBAmount,
            QBDescription = input.QBDescription,
            QBPayee = input.QBPayee,
            QBTransactionDate = input.QBTransactionDate,
            QBTransactionNumber = input.QBTransactionNumber,
            TransactionState = input.TransactionState,
            ProblemCodes = input.ProblemCodes,
            BankAccountID = input.BankAccountID,
            CashSheetBankAccountDescription = input.CashSheetBankAccountDescription
            //Id = 0
        };

        //Create new transaction
        //await _cashSheetTransactionRepository.InsertAsync(cashSheetTransaction);
        await _cashSheetTransactionsAppService.CreateOrEdit(cashSheetTransaction);

I receive the error:

?exception {"Current user did not login to the application!"} Data: {System.Collections.ListDictionaryInternal} HResult: -2146233088 HelpLink: null InnerException: null Message: "Current user did not login to the application!"

I don't understand how the credentials could be lost along the way once inside the API method. I also started from scratch and made sure I logged on fresh to eliminate that possibility.

Thoughts?

For v6.9.0.0: The build date or release date that displays in the site footer - internal var releaseDate does not consistentlly update and is rarely correct. It seems to just randomly update. Most of the time I can go multiple days with multiple builds (dev AND --prod) and the releaseDate does not update. How can I get this to update consistently and correctly?

Cheers

That seemed to do the trick - thanks!

Prerequisites

Please answer the following questions before submitting an issue. YOU MAY DELETE THE PREREQUISITES SECTION.

  • What is your product version? 6.9.0
  • What is your product type (Angular or MVC)? Angular
  • What is product framework type (.net framework or .net core)? Core

If issue related with ABP Framework

  • What is ABP Framework version?

If issue is about UI

  • Which theme are you using?
  • What are the theme settings?

Using the standard generated methods for exporting a grid to Excel, my users are frequently finding when they open the Excel file, it only contains the heading. What might be the cause here and how can I make the full file download consistently?

Showing 1 to 10 of 17 entries