Hello When i try to send email whit method SendCourseEnrollementEmail the emailSender use host configuration and not tenant configuration, if i use the test email from tenant ui Smtp Settings it work fine.
Best Regards
se answer the following questions before submitting an issue. YOU MAY DELETE THE PREREQUISITES SECTION.
AllowTenantsToChangeEmailSettings = true* ` public class EcoSectConsts { public const string LocalizationSourceName = "EcoSect";
public const string ConnectionStringName = "Default";
public const bool MultiTenancyEnabled = true;
public const bool AllowTenantsToChangeEmailSettings = true;
public const string Currency = "EUR";
public const string CurrencySign = "€";
public const string AbpApiClientUserAgent = "AbpApiClient";
// Note:
// Minimum accepted payment amount. If a payment amount is less then that minimum value payment progress will continue without charging payment
// Even though we can use multiple payment methods, users always can go and use the highest accepted payment amount.
//For example, you use Stripe and PayPal. Let say that stripe accepts min 5$ and PayPal accepts min 3$. If your payment amount is 4$.
// User will prefer to use a payment method with the highest accept value which is a Stripe in this case.
public const decimal MinimumUpgradePaymentAmount = 1M;
}`
Application Service Method call Job
` [AbpAuthorize(AppPermissions.Pages_CourseLeads_SendEmail)]
public async Task SendCourseEnrollementEmail(SendCourseEnrollementEmailInput input)
{
using(CurrentUnitOfWork.SetTenantId(AbpSession.TenantId))
{
await _backgroundJobManager.EnqueueAsync<SendCourseEnrollementEmailJob, SendCourseEnrollementEmailJobArgs>(
new SendCourseEnrollementEmailJobArgs
{
CourseId = input.CourseId,
CustomersIds = input.CustomersIds,
User = AbpSession.ToUserIdentifier(),
TenantId = AbpSession.TenantId
});
}
}`
Job Worker Code
` public override void Execute(SendCourseEnrollementEmailJobArgs args)
{
var courseLeads = _courseLeadRepository.GetAll()
.Include(e => e.Course)
.ThenInclude(e => e.CourseCategory)
.Include(e => e.Course)
.ThenInclude(e => e.CourseLocation)
.Include(e => e.Course)
.ThenInclude(e => e.CourseLessons)
.ThenInclude(e => e.Teacher)
.Include(e => e.Customer)
.Include(e => e.CourseLeadEmployees)
.ThenInclude(e => e.Employee)
.Where(e => e.CourseId == args.CourseId)
.WhereIf(args.CustomersIds.Count > 0, e => args.CustomersIds.Any(id => id == e.CustomerId))
.ToList();
string courseName = courseLeads.FirstOrDefault()?.Course.Name;
string invalidSendMessage = "Non è stato possibile inviare il messaggio al seguente destinatario {0} per il seguente motivo: {1}";
foreach (var courseLead in courseLeads)
{
try
{
if (string.IsNullOrEmpty(courseLead.Customer?.MarketingEmail))
{
AsyncHelper.RunSync(() => SendInvalidEmailNotification(args, string.Format(invalidSendMessage, courseLead.Customer.Name, "Al cliente non è stato impostato l'indirizzo Email Marketing.")));
}
else
{
TryToSendCourseEnrollementEmailInput tryToSendCourseEnrollementEmailInput = new TryToSendCourseEnrollementEmailInput();
tryToSendCourseEnrollementEmailInput.TenantId = args.TenantId;
tryToSendCourseEnrollementEmailInput.CourseLead = courseLead;
AsyncHelper.RunSync(() => _courseEmailer.TryToSendCourseEnrollementEmail(tryToSendCourseEnrollementEmailInput));
courseLead.Status = CourseLeadStatus.SendedComunication;
}
}
catch (UserFriendlyException exception)
{
AsyncHelper.RunSync(() => SendInvalidEmailNotification(args, string.Format(invalidSendMessage, courseLead.Customer.Name, exception.Message)));
}
catch (Exception exception)
{
AsyncHelper.RunSync(() => SendInvalidEmailNotification(args, string.Format(invalidSendMessage, courseLead.Customer.Name, exception.ToString())));
}
}
AsyncHelper.RunSync(() => SendSurveyCompletedNotification(args, string.Format("L'invio delle email per il corso {0} è stato avviato.", courseName)));
}`
Course Emailer code TryToSendCourseEnrollementEmail
`public async Task TryToSendCourseEnrollementEmail(TryToSendCourseEnrollementEmailInput input)
{
var emailTemplate = new StringBuilder(_emailTemplateProvider.GetCourseEnrollementTemplate(input.TenantId));
var emailTransformed = TransformEmail(emailTemplate, input.CourseLead);
await _emailSender.SendAsync(new MailMessage
{
To = { input.CourseLead.Customer.MarketingEmail },
Subject = "Certificazioni in Scadenza",
Body = emailTransformed,
IsBodyHtml = true,
CC = { _ecoSectSmtpEmailSenderConfiguration.DefaultFromAddress },
});
}`
Please answer the following questions before submitting an issue. YOU MAY DELETE THE PREREQUISITES SECTION.
Hello I have a specific user that is created for each tenant with the same username and password who must be able to manage tasks that i asigned from different tenants. Each time the user login to a tenant is shown a table with the tasks that the various tenants have assigned to him, I get this data using a query filtered data by username and disabling the filter for tenant using (CurrentUnitOfWork.DisableFilter (AbpDataFilters.MustHaveTenant) ) However, whenever the user has need to update the task data, he must log out and log in again on the specific tenant, it is possible to create a script that allows the user to connect to the specified tenant by clicking to a link or button ?. I have this information in task data TenantId, TenancyName, UserId, Username.
Tankyou
Hello I need to add a custom field CustomerId to Abp Session , what is the best way to extend AbpSession? Then I need to use this field to filter the data. this is a correct solution https://docs.microsoft.com/en-us/ef/core/querying/filters ?.
I have AspNet Zero 8.1, Angular, .Net Core.
Best Regards Dennis Bellancini
hi, I would need to redirect a user on a specific route based on the user's role, becouse i have utilized the roles system that customize the navigation menu base on role. when user login the user is redirect to nofications pages, it possible tu redirect user to specific route base on it roles ?
Best Regards
I need to create a drop-down menu with a form inside, I used the code from the following link https://keenthemes.com/metronic/preview/demo2/features/bootstrap/dropdown.html
but it doesn't work. What is the correct way to make it happen whit your framework?
Myprodut is: ASP.NET Zero 8.1 for Angular and .Net core 3.1
Best Regards Dennis Bellancini
Hello, How can I upgrade my module zero version 8.0-angular-core 3.0 to version 8.1? is there a documentation or tutorial ? Do you just need to update packages via NuGet Package Manager and Installa .net core 3.1 ? How can update de angular application to 8.1 ?
Thank you.
Hello, i have write a BackgroundJob for send email to some user, but after the job is completed with out exception it remain in queue in DB table AbpBackgroundJobs, so the emails are sent continuously until I delete manually the record from the database. My Version: ABP.Zero 8.0, Angular, AspNet Core 3.0
[AbpAuthorize(AppPermissions.Pages_Surveies_SendRequest)]
public async Task SendSurvey(SendSurveyInput input)
{
string jobId = await _backgroundJobManager.EnqueueAsync<SendSurveyJob, SendSurveyJobArgs>(
new SendSurveyJobArgs
{
Contacts = input.Contacts,
SurveyId = input.SurveyId,
TenantId = AbpSession.TenantId.Value,
User = AbpSession.ToUserIdentifier(),
});
}
public class SendSurveyJob : BackgroundJob<SendSurveyJobArgs>, ITransientDependency
{
private readonly IAppNotifier _appNotifier;
private readonly ILocalizationSource _localizationSource;
private readonly IObjectMapper _objectMapper;
private readonly IRepository<Survey> _surveyRepository;
private readonly IRepository<Contact> _contactRepository;
private readonly EMailTemplateManager _eMailTemplateManager;
private readonly TenantManager _tenantManager;
private readonly SurveyManager _surveyManager;
private readonly IRepository<SurveyRequest> _surveyRequestRepository;
private readonly IUserEmailer _userEmailer;
public SendSurveyJob(
IRepository<Survey> surveyRepository,
IRepository<Contact> contactRepository,
EMailTemplateManager eMailTemplateManager,
IAppNotifier appNotifier,
ILocalizationManager localizationManager,
IObjectMapper objectMapper,
TenantManager tenantManager,
SurveyManager surveyManager,
IRepository<SurveyRequest> surveyRequestRepository,
IUserEmailer userEmailer)
{
_contactRepository = contactRepository;
_appNotifier = appNotifier;
_objectMapper = objectMapper;
_localizationSource = localizationManager.GetSource(GerecoConsts.LocalizationSourceName);
_tenantManager = tenantManager;
_surveyRepository = surveyRepository;
_eMailTemplateManager = eMailTemplateManager;
_surveyManager = surveyManager;
_surveyRequestRepository = surveyRequestRepository;
_userEmailer = userEmailer;
}
[UnitOfWork]
public override void Execute(SendSurveyJobArgs args)
{
using (CurrentUnitOfWork.SetTenantId(args.TenantId))
{
Survey survey = _surveyRepository.Get(args.SurveyId);
SendSurveyRequest(args, survey);
}
}
private void SendSurveyRequest(SendSurveyJobArgs args, Survey survey)
{
string invalidSendMessage = "Non è stato possibile inviare il messaggio al seguente destinatario {0} per il seguente motivo {1}";
List<Contact> recipients = _contactRepository
.GetAll()
.Include(c => c.Location)
.Where(c => args.Contacts.Select(cs => cs.Id).Contains(c.Id))
.ToList();
EmailTemplate emailTemplate = _eMailTemplateManager.GetDefaultEMailTemplate();
Tenant tenant = _tenantManager.GetById(args.TenantId);
foreach (var recipient in recipients)
{
try
{
var PendingOrVisitedRequest = _surveyRequestRepository
.GetAll()
.Where(r => r.ContactId == recipient.Id & r.SurveyId == survey.Id & (r.Status == SurveyRequestStatus.Pending | r.Status == SurveyRequestStatus.Visited));
//Imposto lo status su cancelled se il contatto ha già aperte delle richieste per il questionario che si sta inviano nello stato di Pending o Visited
foreach (var request in PendingOrVisitedRequest)
{
request.Status = SurveyRequestStatus.Cancelled;
}
//Inserisco la survey request in pending
SurveyRequest surveyRequest = new SurveyRequest()
{
SurveyLinkId = Guid.NewGuid(),
SurveyId = survey.Id,
ContactId = recipient.Id,
EmailTo = recipient.EMail,
Status = SurveyRequestStatus.Pending
};
_surveyRequestRepository.Insert(surveyRequest);
_userEmailer.TryToSendEmailSurveyRequest(survey, recipient, tenant, surveyRequest, emailTemplate);
CurrentUnitOfWork.SaveChanges();
}
catch (UserFriendlyException exception)
{
SendInvalidEmailNotification(args, string.Format(invalidSendMessage, recipient.FullCompanyName, exception.Message));
}
catch (Exception exception)
{
SendInvalidEmailNotification(args, string.Format(invalidSendMessage, recipient.FullCompanyName, exception.ToString()));
}
}
SendSurveyCompletedNotification(args, string.Format("L'invio del questionario {0} è stato completato.", survey.Name));
}
private void SendInvalidSurveyNotification(SendSurveyJobArgs args)
{
_appNotifier.SendMessageAsync(
args.User,
_localizationSource.GetString("SendInvalidSurveyNotification"),
Abp.Notifications.NotificationSeverity.Warn);
}
private void SendInvalidEmailNotification(SendSurveyJobArgs args, string message)
{
_appNotifier.SendMessageAsync(
args.User,
message,
Abp.Notifications.NotificationSeverity.Warn);
}
private void SendSurveyCompletedNotification(SendSurveyJobArgs args, string message)
{
_appNotifier.SendMessageAsync(
args.User,
message,
Abp.Notifications.NotificationSeverity.Success);
}
}
public async Task TryToSendEmailSurveyRequest(Survey survey, Contact recipient, Tenant tenant, SurveyRequest surveyRequest, EmailTemplate emailBodyTemplate = null)
{
try
{
var emailTemplate = new StringBuilder(_emailTemplateProvider.GetSurveyRequestTemplate(tenant.Id));
emailTemplate.Replace("{EMAIL_TITLE}", L("SendSurveyRequestMessageEmail_Title"));
emailTemplate.Replace("{EMAIL_SUB_TITLE}", L("SendSurveyRequestMessageEmail_SubTitle"));
var surveyRequestMessage = GetSurveyRequestMessage(survey, recipient, emailBodyTemplate, tenant, surveyRequest);
var surveyRequestSubject = GetSurveyRequestSubject(survey, recipient, emailBodyTemplate, tenant);
await ReplaceBodyAndSend(recipient.EMail, surveyRequestSubject.ToString(), emailTemplate, surveyRequestMessage);
}
catch (Exception exception)
{
Logger.Error(exception.Message, exception);
}
}
Hello, i have override the methods ValidateTenancyName and ValidateTenancyNameAsync in TenancyManager becouse my customer has need that tenant name can be only number. These methods check the regular expression TenancyNameRegex "^[a-zA-Z][a-zA-Z0-9_-]{1,}$". This costumization can cause any problems in the future?
Thanks. Best Regards