I'm trying to create a piece of code that would retrieve the whole content of an INBOX, using Spring Integration Mail.
By default, an ImapIdleChannelAdapter will only fetch recent, unseen, unanswered etc... emails.
So when I created my adapter, I tried to use a special strategy that would retrieve all emails without a given unique user flag (so basically, all the mails I haven't retrieve yet).
Here is my adapter:
Mail.imapIdleAdapter(imapUrl(user, pw, provider))
.javaMailProperties { p: PropertiesBuilder -> p.put("mail.debug", "false") }
.userFlag(uniqueFlag)
.shouldMarkMessagesAsRead(false)
.searchTermStrategy(GetAllMailsStrategy(uniqueFlag))
.shouldReconnectAutomatically(true)
.autoCloseFolder(false)
val integrationFlowBuilder = IntegrationFlows.from(imapIdleAdapter)
.handle { message -> onNewEmail(user, uniqueFlag, message as org.springframework.messaging.Message<Message>) }
val flow: IntegrationFlow = integrationFlowBuilder.get()
flowContext.registration(flow).register()
And here is my strategy:
inner class GetAllMailsStrategy(private val uniqueFlag: String) : SearchTermStrategy {
override fun generateSearchTerm(supportedFlags: Flags?, folder: Folder?): SearchTerm {
val userFlag = Flags()
userFlag.add(uniqueFlag)
return NotTerm(FlagTerm(userFlag, true))
}
}
This piece of code does work, on small inboxes. As soon as I try to retrieve mails from an inbox with thousands of mails, at some point it just stops (sometime with FolderClosedException, even though I set autoCloseFolder to false, and sometime without any exception or log...), and then won't retrieve missing emails even if I start it all over with the same unique user flag. As if all the mails where flagged even though I never retrieved them. It does work on all new incoming emails though...
Any idea on what strategy I should use? Is there a way to get all the mails once, without flagging them?
Should I use Spring mail only to get incoming new mail but something else for the task of retrieving all the mails once?
Thanks
I never did it, but I think you are right: you should use an ImapIdleChannelAdapter for new (in IMAP terms RECENT) message, and some other ImapMailReceiver instance for initial call with your custom SearchTermStrategy.
The folder might be closed for some other reason, e.g. too many messages too long processed.
Related
Because of an error which is now fixed, our customers did not received order confirmation emails for a while.
Is there a way to resent the order emails for some orders once?
We know about the plugin, but as this is supposed be needed only once, it's kind of overkill to install, test and deploy a plugin.
Are there some code snippets or a bin/console command to accomplish this task?
Or is it even possible via the admin panel?
It is possible to define a flowbuilder flow to send order confirmation mails, but you'd to define an appropriate trigger action of course, that you can trigger yourself for the affected orders. After the oneshot command you could disable/delete the flow.
Not sure if this fits your needs...
You could write your own CLI command to re-fire the event when an order is placed.
// $this->orderRepostitory - DI service `order.repository`
// $this->orderConverter - DI service `Shopware\Core\Checkout\Cart\Order\OrderConverter`
// $this->eventDispatcher - DI service `event_dispatcher`
$criteria = new Criteria();
$criteria
->addAssociation('orderCustomer.customer')
->addAssociation('orderCustomer.salutation')
->addAssociation('deliveries.shippingMethod')
->addAssociation('deliveries.shippingOrderAddress.country')
->addAssociation('deliveries.shippingOrderAddress.countryState')
->addAssociation('transactions.paymentMethod')
->addAssociation('lineItems.cover')
->addAssociation('currency')
->addAssociation('addresses.country')
->addAssociation('addresses.countryState')
->getAssociation('transactions')->addSorting(new FieldSorting('createdAt'));
// add a filter for the orders for which no mail has been sent before
$criteria->addFilter(new RangeFilter('createdAt', [RangeFilter::LTE => (new \DateTime('2020-01-01'))->format(\DATE_ATOM)]));
$orderEntities = $this->orderRepository->search($criteria, $context->getContext())->getEntities();
foreach ($orderEntities as $orderEntity) {
$salesChannelContext = $this->orderConverter->assembleSalesChannelContext($orderEntity, Context::createDefaultContext());
$event = new CheckoutOrderPlacedEvent(
$salesChannelContext->getContext(),
$orderEntity,
$salesChannelContext->getSalesChannel()->getId()
);
$this->eventDispatcher->dispatch($event);
}
This should in theory also trigger the mails to be sent once more. Obviously try this with a test order before mass dispatching events like this. Also you should probably do this in smaller batches to avoid memory issues.
I'm making by a requirement a code able to send an E-mail to an specific list of E-mails, due the fact that I must to include the attachments of the record I decided to use an apex class instead an e-mail alert. This object (A custom object ) must populate some fields in an email template with some of the record´s fields. I implemented the following code
Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
mail.setToAddresses(lista);
mail.setTemplateId('00X21000000QR22');
//mail.setWhatId(idMinuta);
mail.setTargetObjectId('005d0000005NMIx');
mail.setSaveAsActivity(false);
List<Messaging.Emailfileattachment> fileAttachments = new List<Messaging.Emailfileattachment>();
for (ContentVersion document: documents)
{
Messaging.Emailfileattachment efa = new Messaging.Emailfileattachment();
efa.setFileName(document.Title);
efa.setBody(document.VersionData);
fileAttachments.add(efa);
}
mail.setFileAttachments(fileAttachments);
Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
I understood that to make the fields merge it´s necesary to use the WhatId method. In the related code, I have commented it because It generates an error (INVALID_ID_FIELD, WhatId is not available for sending emails to UserIds.)
My question is, if is it possible to do this with a custom object. I´m a little confuse with salesforce documentation beacuse it looks like the method supports a custom object, or maybe If I am forggeting something to include in the code.
If i keep the WhatID line commented, effectively the email is sent with the attachments and the Template but it is not populated.
I really need this kind of solution because the org have in this object at least 20 email templates, for me will be easier just to pass the Id of the template instead of makig a code with 20 different html codes for each situation
Thanks a lot
Please publish this question at Salesforce StackExcahnge https://salesforce.stackexchange.com/
I am trying to send emails using my MVC5 application. To do this, I have installed Mailkit v 1.22.0 through NuGet package manager. And this is how my code looks like:
var FromAddress = "no-reply#email.com";
var FromAddressTitle = "My Org";
var connection = ConfigurationManager.ConnectionStrings["SmtpServer"].ConnectionString;
var Email = new MimeMessage();
Email.From.Add(new MailboxAddress(FromAddressTitle, FromAddress));
var AddressArray = value.SentTo.Split(';');
foreach (var item in AddressArray)
{
Email.To.Add(new MailboxAddress(item));
}
Email.Subject = value.Subject;
Email.Body = new TextPart("html")
{
Text = value.Content
};
using (var client = new SmtpClient())
{
client.Connect(connection);
client.Send(Email);
}
return "Email Successfully Sent";
which works fine except if a wrong recipient Email address has been entered, the application does not detect if the Email was actually sent or not (client.Send(Email) returns void). Is there a way to know if it really ended up getting sent to the recipient or not? If it is not possible with Mailkit, is there any other NuGet package that can do this?
The reason that SmtpClient.Send() returns void is that the SMTP protocol does not specify whether the message gets delivered successfully. All it can do us tell the client that the messages was accepted by the server or not (in which case MailKit will throw an exception).
If you need to know whether the message was successfully delivered, you will need to check for bounce messages sent to you which could take minutes or even hours.
The first thing you'll have to do, however, is subclass SmtpClient and override the GetEnvelopeId and GetDeliveryStatusNotifications methods.
Then, when you receive a bounce message, the top-level MIME part will typically be a multipart/report (represented by a MultipartReport object when using MimeKit). This multipart/report will then contain a message/delivery-status MIME part (and possibly others), which will have a list of header-like fields that specify the details about the delivery status for 1 or more recipients.
MimeKit will parse a lot of this for you (e.g. it has a MessageDeliveryStatus class which contains a StatusGroups property that you will want to use. However, what MimeKit does not do is parse the individual field values (but they shouldn't be that difficult for you to do, typically a few Split(';')'s should be enough iirc for some quick & dirty parsing).
You will want to read the spec for this at https://www.rfc-editor.org/rfc/rfc3464
The MimeKit docs linked above specify which sections to look closely at (I think 2.2 and 2.3).
I would recommend looking specifically at the Original-Recipient and Action fields.
original-recipient-field =
"Original-Recipient" ":" address-type ";" generic-address
generic-address = *text
action-field = "Action" ":" action-value
action-value =
"failed" / "delayed" / "delivered" / "relayed" / "expanded"
You will also need the Original-Envelope-Id field to figure out which message is being reported on:
original-envelope-id-field =
"Original-Envelope-Id" ":" envelope-id
envelope-id = *text
The envelope-id text will be the same string returned by your GetEnvelopeId implementation in the SmtpClient class.
I am able to fetch email from my email account using POP3 via "Mail Reader Sampler" listener. But its not retrieving latest email.
Is it possible to extract the latest email using Beanshell Sampler. If yes, can you please share the code if this is achievable.
As per below discussion - looks like it is not doable. But, wanted to check if this is achievable using any means?
Stackoverflow Discussion on how to fetch required email
You can do this programmatically, check out the following methods:
Folder.getMessageCount() - Get total number of messages in this Folder
Folder.getMessage(int msgnum) - Get the Message object corresponding to the given message number
According to the JavaDoc
Messages are numbered starting at 1 through the total number of message in the folder.
So the number of the last message will always be the same as the total number of messages in the given folder.
Example code which reads last email using POP3 protocol
import javax.mail.Folder
import javax.mail.Message
import javax.mail.Session
import javax.mail.Store
String host = "host"
String user = "username"
String password = "password"
Properties properties = System.getProperties();
Session session = Session.getDefaultInstance(properties)
Store store = session.getStore("pop3")
store.connect(host, user, password)
Folder inbox = store.getFolder("Inbox")
inbox.open(Folder.READ_ONLY)
int msgCount = inbox.getMessageCount()
Message last = inbox.getMessage(msgCount)
//do what you need with the "last" message
inbox.close(true)
store.close()
I would also recommend forgetting about Beanshell, whenever you need to perform scripting - use JSR223 Elements and Groovy language as Groovy has much better performance, it is more Java-compliant and it has some nice language features. See Apache Groovy - Why and How You Should Use It guide for more details.
I have a form sent by email that travels through different persons like this.
Person A --> Person B --> Person C
I want the person A to be informed when the form is treated by person C. So Person A needs to be in copy of the email sent by person B.
Because person A isn't always the same one, I think the best way to put him/her in copy is to use the "from" field of the email received by person B and to put it in copy.
But how can I find this address with infopath and how can I place it into my email data connection ?
I had this same question today myself and could not find much in the way of answers.
So... I did some work myself and came up with a few solutions.
First I don't believe there is any way to get/set the "From" address using the InfoPath OM. This means you will have to use one of the following options:
No Code:
You will be limited to providing a field on the form where "Person A" can put their email address and use this in the CC. for subsequent stages. That's kind of the only way and while it an extra burden to the user it does have the benefit of providing flexibility.
Code:
Write your own code to send the mail using Outlook Interop or System.Net.Mail and then you will be setting all of the addresses manually anyway.
If you are using AD or something else then you could always get the email address of the current use using System.DirectoryServices.AccountManagement.
Based on an assumption which I cannot find any documentation to back up. That InfoPath uses the account associated with the default store to send email using EmailSubmitConnection. You should be able to use Outlook Interop to find the address that InfoPath will use.
Here is a code sample:
using Outlook = Microsoft.Office.Interop.Outlook;
public string GetDefaultSenderAddress()
{
// This actually opens outlook in the same way as InfoPath does to send the message.
// which can be slow.
string DefaultAddress = string.Empty;
Outlook.Application OutlookApplication = new Outlook.Application();
string DefaultStoreId = OutlookApplication.Session.DefaultStore.StoreID;
foreach (Outlook.Account Account in OutlookApplication.Session.Accounts)
{
if (Account.DeliveryStore.StoreID == DefaultStoreId)
{
DefaultAddress = Account.SmtpAddress;
}
}
// Note you probably won't want to quit if you are about to send the email.
// However I have noticed that this doesn't seem to close Outlook anyway.
OutlookApplication.Quit();
return DefaultAddress;
}
You may have to provide a few more checks in case of different account types etc. But I believe it will work. (I tested it for my scenario and it does).
Note: Of course this opens an outlook instance which you will have to close as well. And it can be slow. Unless outlook is already open in which case it will be very quick. Anyhow when sending from InfoPath Outlook will have to be opened so if you do this just before sending then there should be no noticeable difference.
I would advise using a combination of the no code/with code options so provide a return address which is automatically complete to save the user time. But can be corrected if the user wishes to have the email returned to a different address of if there is a mistake.
Hope that you find that useful.