Регистрация  |  Вход

Email Service and PDF attachment

Всем привет!

Снова столкнулся с проблемой, и при этом там где ее не должно быть.

Работаю с Email Service. Кто-то отправляет письмо, обработчик этого сервиса в ответ на пришедшее письмо создает запись и отправляет в ответ др письмо с ПДФ файлом, в моем случае содержащий ID.

Все прекрасно работают, но не вместе.

Код отвечающий за создания ПДФ я нашел в двух руководствах (не workbookах): по апекс и VF.

Вот и он:

Messaging.reserveSingleEmailCapacity(5);

Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();

String[] toAddresses = new String[] {'my@mail.com'};

mail.setToAddresses(toAddresses);

mail.setReplyTo('my@mail.com');

mail.setSenderDisplayName('Support');

mail.setSubject('New Record Created : ' + newRecords[0].id);

mail.setBccSender(true);

mail.setUseSignature(false);

mail.setPlainTextBody('Your new record has been created.');

mail.setHtmlBody('Your new record has been created.');

PageReference pdf = Page.MyPDFpage;

pdf.getParameters().put('id', (String)newRecords[0].id);

pdf.getParameters().put('position', '1');

pdf.setRedirect(true);

// Take the PDF content

Blob b = pdf.getContent();

// Create the email attachment

Messaging.EmailFileAttachment efa = new Messaging.EmailFileAttachment();

efa.setFileName('PrintThisPDF.pdf');

efa.setBody(b);

mail.setFileAttachments(new Messaging.EmailFileAttachment[] {efa});



Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });

и он прекрасно работает, но только в анонимном окне консоли.

(в консоли я заменяю (String)newRecords[0].id на какой то реальный id).

я получаю письмо, а в нем прекрасный ПДФ.

но если этот код использовать в обработчике Email Service то вроде все ОК, возоращается письмо, а в нем ПДФ файл, но файл не открывается и весит вдвое меньше, чем работающий ПДФ пришедший при вызове кода из консоли.

У меня просто нет идей в чем может быть дело.

Ваши любые догадки - very welcome.

Всем привет!

Снова столкнулся с проблемой, и при этом там где ее не должно быть.

Работаю с Email Service. Кто-то отправляет письмо, обработчик этого сервиса в ответ на пришедшее письмо создает запись и отправляет в ответ др письмо с ПДФ файлом, в моем случае содержащий  ID.

Все прекрасно работают, но не вместе.

Код отвечающий за создания ПДФ я нашел в двух руководствах (не workbookах): по апекс и VF.

Вот и он:
[code]
Messaging.reserveSingleEmailCapacity(5);

Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();

String[] toAddresses = new String[] {'my@mail.com'}; 

mail.setToAddresses(toAddresses);

mail.setReplyTo('my@mail.com');

mail.setSenderDisplayName('Support');

mail.setSubject('New Record Created : ' + newRecords[0].id);

mail.setBccSender(true);

mail.setUseSignature(false);

mail.setPlainTextBody('Your new record has been created.');

mail.setHtmlBody('Your new record has been created.');


    PageReference pdf = Page.MyPDFpage;
    pdf.getParameters().put('id', (String)newRecords[0].id);
    pdf.getParameters().put('position', '1');
    pdf.setRedirect(true);

    // Take the PDF content
    Blob b = pdf.getContent();

    // Create the email attachment
    Messaging.EmailFileAttachment efa = new Messaging.EmailFileAttachment();
    efa.setFileName('PrintThisPDF.pdf');
    efa.setBody(b);                                                           

mail.setFileAttachments(new Messaging.EmailFileAttachment[] {efa});                                                          
                                                           
Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });     
[/code]

и он прекрасно работает, но только в анонимном окне консоли.
(в консоли я заменяю  (String)newRecords[0].id на какой то реальный id).

я получаю письмо, а в нем прекрасный ПДФ.

но если этот код использовать в обработчике Email Service то вроде все ОК, возоращается письмо, а в нем ПДФ файл, но файл[b] не открывается[/b] и весит вдвое меньше, чем работающий ПДФ пришедший при вызове кода из консоли.

У меня просто нет идей в чем может быть дело.

Ваши любые догадки - very welcome.

Привет Den Brown.

Хочется помочь, но тут надо смотреть, тестировать.

Пока что приходит на ум, это проблемы с правами под которыми выполняется код при вызове Email Service. Возможно страница рендерится с ошибкой и вместо содержиного в виде PDF ты вставляешь в атачмент какой-то текстовый вывод об ошибке.

два пути проверить это:

- поставить в код вывод в лог текущего пользователя под которыми выполняется email service. Узнаешь какие права.
- в аттачмент вставляй содержимое не в виде pdf, а в виде plain text. Возможно там будет html страница с ошибкой.

Привет Den Brown.

Хочется помочь, но тут надо смотреть, тестировать.

Пока что приходит на ум, это проблемы с правами под которыми выполняется код при вызове Email Service. Возможно страница рендерится с ошибкой и вместо содержиного в виде PDF ты вставляешь в атачмент какой-то текстовый вывод об ошибке.

два пути проверить это:

- поставить в код вывод  в лог текущего пользователя под которыми выполняется email service. Узнаешь  какие права.
- в аттачмент вставляй содержимое не в виде pdf, а в виде plain text. Возможно там будет html страница с ошибкой.

попробовал посмотреть в дебаге, что возвращает в блоб код если все работает в мейл сервисе. Так вот в блоб возвращается html документ, в котором скрипт, ведущий на данную страницу где в урл включены нужные параметры. я сохранил блоб как html, затем открыл полученный аттач в новом окне и увидел нужную страницу, отрендеренную как пдф - как она обычно и отображается, если на нее зайти (браузер залоген в тот орг).

но ведь этот скрип возвращает в консоли нормальный пдф документ (юзер везде один и тот же, проверил).

чудеса на виражах

попробовал посмотреть в дебаге, что возвращает в блоб код если все работает в мейл сервисе. Так вот в блоб возвращается html документ, в котором скрипт, ведущий на данную страницу где в урл включены нужные параметры. я сохранил блоб как html, затем открыл полученный аттач в новом окне и увидел нужную страницу, отрендеренную как пдф - как она обычно и отображается, если на нее зайти (браузер залоген в тот орг).

но ведь этот скрип возвращает в консоли нормальный пдф документ (юзер везде один и тот же, проверил).

чудеса на виражах

Задал вопрос на stackexchange.com о моем случае, и сразу получил нерадостный ответ:

You can't get the PDF content unfortunately.

While Email Services do run under a 'Context User', you will notice that UserInfo.getSessionId() returns null from any InboundEmailHandler implementation you may have.

The HTML with JavaScript is serving up a client-side redirect to a Salesforce login page. This is because pdf.getContent() runs in a different execution context that also lacks a Session ID at the time of making the request to render the page.

Salesforce have documented this:

This method can't be used in [...] Apex email services

Печалько...

Задал вопрос на stackexchange.com о моем случае, и сразу получил нерадостный ответ:

You can't get the PDF content unfortunately.

While Email Services do run under a 'Context User', you will notice that UserInfo.getSessionId() returns null from any InboundEmailHandler implementation you may have.

The HTML with JavaScript is serving up a client-side redirect to a Salesforce login page. This is because pdf.getContent() runs in a different execution context that also lacks a Session ID at the time of making the request to render the page.

Salesforce have documented this:

    This method can't be used in [...] Apex email services

Печалько...

В такой ситуации у меня осталось только две идеи:
- возможно ли програмно сохранить страницу (которая рендерится как ПДФ) в виде ПДФ файла в Доки или Аттачи нашего орга и оттуда немедленно прикрепить к письму. Еще большой вопрос сохранится ли она как ПДФ в без-сессионном Емэл-сервисном выполнении...
- ну и можно ли заставить код выполнится под какой-то сессией (фактически открыть програмно сессию, сделать свое дело, закрыть), как мы можем выполнять тесты под разными юзерами.

В такой ситуации у меня осталось только две идеи:
- возможно ли програмно сохранить страницу (которая рендерится как ПДФ) в виде ПДФ файла в Доки или Аттачи нашего орга и оттуда немедленно прикрепить к письму. Еще большой вопрос сохранится ли она как ПДФ  в без-сессионном Емэл-сервисном выполнении...
- ну и можно ли заставить код выполнится под какой-то сессией (фактически открыть програмно сессию, сделать свое дело, закрыть), как мы можем выполнять тесты под разными юзерами.

Den Brown, хорошую тему ты поднял.
Тут огромное поле для изучения.

возможно ли програмно сохранить страницу (которая рендерится как ПДФ) в виде ПДФ файла в Доки или Аттачи нашего орга и оттуда немедленно прикрепить к письму.

Не получится. При срабатывании Email Service нет никакой возможности сгенерировать PDF (как написано в английском ответе) без Session ID.

ну и можно ли заставить код выполнится под какой-то сессией (фактически открыть програмно сессию, сделать свое дело, закрыть), как мы можем выполнять тесты под разными юзерами

RunAs работает только в тест методах. А про другие способы работать под чужой сессией я не слышал.

Как вариант, если позволяет задача, можно генерировать PDF заранее (когда пользователь сохраняет какие-нибудь данные), а потом уже в Email Service вытягивать этот PDF из базы данных и приатачивать к письму.

Den Brown, хорошую тему ты поднял.
Тут огромное поле для изучения. 

[quote]возможно ли програмно сохранить страницу (которая рендерится как ПДФ) в виде ПДФ файла в Доки или Аттачи нашего орга и оттуда немедленно прикрепить к письму. [/quote]
Не получится. При срабатывании Email Service нет никакой возможности сгенерировать PDF (как написано в английском ответе) без Session ID.

[quote]ну и можно ли заставить код выполнится под какой-то сессией (фактически открыть програмно сессию, сделать свое дело, закрыть), как мы можем выполнять тесты под разными юзерами[/quote]
RunAs работает только в тест методах. А про другие способы работать под чужой сессией я не слышал.

Как вариант, если позволяет задача, можно генерировать PDF заранее (когда пользователь сохраняет какие-нибудь данные), а потом уже в Email Service вытягивать этот PDF из базы данных и приатачивать к письму.

В том то и дело, что ПДФ должен генится "на лету". Пришло письмо "поставьте на инвентальный учет 3 вещи" с фалом описания вещей, сервис парсит файл, создает записи в таблице учета, возвращает ПДФ с инвентарным номером новых учетных записей и QR кодом содержащий урлы этих записей (т.е. нужны живые ID).

Вообще то можно и заранее наклепать ПДФ доков с этими данными (если в них не требуется спцефичных деталей о самой вещи), а потом просто вносить в записи инфу и отправлять соответсвующий ПДФ. Но кол-во записей может быть разным в одном ответе.

Это достаточно типичная задача для будущих проектов, предпологающих взаимодействия др систем и людей с нашим Оргом.

Просто невероятно, что SFDC не сделал это возможным!

В том то и дело, что ПДФ должен генится "на лету". Пришло письмо "поставьте  на инвентальный учет 3 вещи" с фалом описания вещей, сервис парсит файл, создает записи в таблице учета, возвращает ПДФ с инвентарным номером новых учетных записей и QR кодом содержащий урлы этих записей (т.е. нужны живые ID). 

Вообще то можно и заранее наклепать ПДФ доков с этими данными (если в них не требуется спцефичных деталей о самой вещи), а потом просто вносить в записи инфу и отправлять соответсвующий ПДФ. Но кол-во записей может быть разным в одном ответе.

Это достаточно типичная задача для будущих проектов, предпологающих взаимодействия др систем и людей с нашим Оргом.

Просто невероятно, что SFDC  не сделал это возможным!

Ну вообще если порассуждать, то SF предоставляет возможность сгенерировать показать страницу в виде PDF, а не генерировать PDF файл на лету. Так что тут они конкретно не виноваты
А что если посмотреть в сторону специализированных сервисов с доступом по API?
Скажем приходит мыло, запускается Email Service, он достает необходимые данные и отсылает их на спец сервер, который возвращает сгенерированный PDF.
Возможно такой сценарий будет лучшим выходом в этом случае?

Ну вообще если порассуждать, то SF предоставляет возможность сгенерировать показать страницу в виде PDF, а не генерировать PDF файл на лету. Так что тут они конкретно не виноваты :)

А что если посмотреть в сторону специализированных сервисов с доступом по API? 
Скажем приходит мыло, запускается Email Service, он достает необходимые данные и отсылает их на спец сервер, который возвращает сгенерированный PDF.
Возможно такой сценарий будет лучшим выходом в этом случае?

Вот получил еще идею, но еще не разобрал ее, не знаю поможет ли она в моей ситуации, когда нужно создавать ПДФ на лету.

If you consider that the lowest common denominator in this scenario, is the creation of the new record - it might be worth considering a Workflow Rule to send out the email with the attachment using a Visualforce Email Template. That way, you don't have to write Apex code to send the email (thus preserving your daily limit).

Firstly, you'd need to create a Visualforce Email Template, for example:

<messaging:emailTemplate subject="New Record Created {!relatedTo.Id}" recipientType="User" relatedToType="Account">

<messaging:attachment renderAs="PDF" filename="{!relatedTo.Name}.pdf">
Place the Visualforce in MyPDFPage here
</messaging:attachment>

<messaging:plainTextEmailBody >
Your new record has been created.
</messaging:plainTextEmailBody>

</messaging:emailTemplate>

And then refer to that in a simple workflow rule that 'fires' everytime a new record is created (i.e. the thought being, your email service creates your new record but you delegate the sending of the email/attachment to workflow upon record creation).

There may be other reasons that exclude the use of workflow, but I thought it worth a mention as its often overlooked.

Вот получил еще идею, но еще не разобрал ее, не знаю поможет ли она в моей ситуации, когда нужно создавать ПДФ на лету.

If you consider that the lowest common denominator in this scenario, is the creation of the new record - it might be worth considering a Workflow Rule to send out the email with the attachment using a Visualforce Email Template. That way, you don't have to write Apex code to send the email (thus preserving your daily limit).

Firstly, you'd need to create a Visualforce Email Template, for example:

<messaging:emailTemplate subject="New Record Created {!relatedTo.Id}" recipientType="User" relatedToType="Account">

    <messaging:attachment renderAs="PDF" filename="{!relatedTo.Name}.pdf">
        Place the Visualforce in MyPDFPage here     
    </messaging:attachment>

    <messaging:plainTextEmailBody >
        Your new record has been created.   
    </messaging:plainTextEmailBody>

</messaging:emailTemplate>

And then refer to that in a simple workflow rule that 'fires' everytime a new record is created (i.e. the thought being, your email service creates your new record but you delegate the sending of the email/attachment to workflow upon record creation).

There may be other reasons that exclude the use of workflow, but I thought it worth a mention as its often overlooked.

Вот еще идея, которую вероятно можно\нужно скомбинировать с предыдущей. Здесь передаются переменные параметры в компонент, что возможно\вероятно\не-исключено приведет именно к тому результату, что мне и нужно - создавать налету ПДФ с разным содержимым.

**********************************************************************************************************************
<messaging:emailTemplate subject="hello" recipientType="Contact" relatedToType="sObjectType(your object type comes here)">
<messaging:htmlEmailBody >
*********your content of body
</messaging:htmlEmailBody>
<messaging:plainTextEmailBody >
---------- your content of body
</messaging:plainTextEmailBody>

<!--
<messaging:attachment filename="myContent.pdf" renderAs="pdf">
<c:myComponentToGeneratePDF position="1" id="{!relatedTo.Id}"/>
</messaging:attachment>
-->
</messaging:emailTemplate>
**********************************************************************************************************************

Вот еще идея, которую вероятно можно\нужно скомбинировать с предыдущей. Здесь передаются переменные параметры в компонент, что возможно\вероятно\не-исключено приведет именно к тому результату, что мне и нужно - создавать налету ПДФ с разным содержимым.

**********************************************************************************************************************
<messaging:emailTemplate subject="hello" recipientType="Contact" relatedToType="sObjectType(your object type comes here)">
<messaging:htmlEmailBody >
   *********your content of body
</messaging:htmlEmailBody>
<messaging:plainTextEmailBody >
    ---------- your content of body
</messaging:plainTextEmailBody>

<!--
<messaging:attachment filename="myContent.pdf" renderAs="pdf">
<c:myComponentToGeneratePDF position="1" id="{!relatedTo.Id}"/>
</messaging:attachment>
-->
</messaging:emailTemplate>
**********************************************************************************************************************