I've been using SendGrid dynamic templates for some time now. I want to start using custom images for the emails sent, however with no success.
I attach the base64 images as .png attachments, and in the template editor, use an HTML to render the image, using it src as cid . The image is sent as an attachment, but is not rendered on the dedicates placeholder.
That's the attachment section:
const email = {
from: ``, // sender address
to: '',
unsubscribeLink: `${this.baseUrl}${emailAlertParams.unsubscribeLink}`,
personalizations:[
{
'dynamic_template_data': {
...emailPlanParams,
...emailAlertParams
}
}
],
attachments: [
{
cid: 'planmap',
content_id: 'planmap',
filename: 'plan_map.png',
content: emailPlanParams.firstPlan.map,
encoding: 'base64'
}
],
And that's the HTML section code from the dynamic template editor:
<img src="cid::planmap" alt="Plan Map" border="0" align="center" style="width:100%; max-width: 600px; margin-bottom: 20px; height: auto; background: #dddddd;">
That's a screenshot of the image received:
Thank you guys!
Answer is:
use this syntacx for the image
<img src="cid:planmap"...
Add disposition:inline to the attachment:
content: emailPlanParams.firstPlan.map,
encoding: 'base64',
disposition: 'inline',
Related
I want the users of my app to be able to select text (words, but also whole sentences) in a WebViewWidget, and have an audio of that text played to them (via tts). I only want this to work with u̲n̲d̲e̲r̲l̲i̲n̲e̲d̲ text (or text visually highlighted and set apart from the rest in any other way).
This is part of my Widget tree, where I have tested a few gestureRecognizers (please read the comments):
child: GestureDetector(
//gestureRecognizers of WebViewWidget don't do anything without this GestureDetector,
//see: https://stackoverflow.com/questions/58811375/tapgesturerecognizer-not-working-in-flutter-webview#answer-59298134
onTap: () => print('this line doesnt get printed'),
child: WebViewWidget(
controller: _controller,
gestureRecognizers: {
Factory<LongPressGestureRecognizer>(() => LongPressGestureRecognizer()
..onLongPress = () {
print('onLongPress');
}
..onLongPressStart = (LongPressStartDetails details) {
print('onLongPressStart, ${details.globalPosition}, ${details.localPosition}');
}),
Factory<TapGestureRecognizer>(() => TapGestureRecognizer()
..onTap = () {
print('onTap'); //this doesn't get printed at all
}
..onTapDown = (TapDownDetails details) {
//comment: this doesn't get printed unless I tap for a little long time (not as long as longPress)
print('onTapDown, details: $details');
/*TODO: I want to get a text selection and play audio (with tts)
String selectedText = ...?
TTSHelper.speak(text: selectedText);
*/
}),
},
),
),
Another approach to a possible solution for what I want
I have an initialization function, where I take html (without the html, body & head tags...) from firestore and wrap it in the html, body & head tags along with some custom CSS.
If there is no way to achieve my goal with Dart code, maybe there is a way for me to add JavaScript to the html, so that underlined words/sentences can be tapped to play audio via tts, or at least send that text over to my Dart code somehow?
String _prepareHTML(String content) {
return """<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body {background-color: ${getCSSColor(context, CustomColors.mainAppBG)};}
h1, h2, h3, h4, h5, h6 {color: ${getCSSColor(context, CustomColors.blueWhiteText)};}
p {color: ${getCSSColor(context, CustomColors.text)};}
table {
width: 100%;
table-layout: fixed;
word-wrap: break-word;
}
table, tr, th, td {
color: ${getCSSColor(context, CustomColors.text)};
border: 1px solid ${getCSSColor(context, CustomColors.text)};
border-collapse: collapse;
}
th, td {
padding: 6px 12px;
font-weight: bold;
font-size: 1.25em;
}
</style>
</head>
<body>
$content
</body>
</html>""";
}
I think you might be able to achieve what you want by using some javascript and a javascriptChannel on the WebViewWidget. General idea below:
var controller = WebViewController();
controller.addJavascriptChannel(
JavascriptChannel(
name: 'underlinedTextSelectChannel',
onMessageReceived: (message) {
var data = json.decode(message.message);
var text = data.text as String;
TTSHelper.speak(text: selectedText);
}
)
)
...
var html = """
<html>
...
<body>
$content
# Within content:
This is some text <p class="underlined-text">Say this!</p> Don't say this.
<script>
var underlinedTexts = document.querySelectorAll('.underlined-text');
for(var t of underlinedTexts) {
t.addEventListener('onClick', (e) => {
underlinedTextSelectChannel.postMessage(JSON.stringify({
text: e.target.innerText
}))
})
}
</script>
</body>
</html>
"""
TYPO3 11.5.4
I want to add a Plain HTML content element to a page, e.g.:
<style type="text/css">
h1#testheader { font-weight: bold; }
</style>
<h1 id="testheader">
Header
</h1>
(just as a simplified example)
But rendered output is:
<style type="text/css"> h1#testheader { font-weight: bold; } </style>
Header
I thought that style is allowed because I set:
lib.parseFunc.allowTags = ...,style,...
lib.parseFunc_RTE.allowTags = ...,style,...
but it doesn't work.
Setting
lib.parseFunc.htmlSanitize = 0
helps but is there something better to allow <style> in Plain HTML content elements?
EDIT:
Looking into the DB entry the html tags are not encoded, so no "<" or ">" instead of "<" and ">", so it's not an issue of the RTE.
After some more research (hours of hours again invested) I come to the conclusion that the only way is to extend the HTMLSanitizer.
The following sources have been used for this solution:
https://punkt.de/de/blog/2021/htmlsanitizer-in-typo3.html
https://docs.typo3.org/c/typo3/cms-core/main/en-us/Changelog/9.5.x/Important-94484-IntroduceHTMLSanitizer.html
https://gist.github.com/ohader/2239dab247e18d23e677fd1b816f4fd5
The complete solution to allow <style>,<script> and <iframe> is shown below.
Still I am not able to allow <font> with this approach!
It's implemented in a local site package (gpcf_theme) and composer is used.
Add <style>,<script> and <iframe> to
lib.parseFunc.allowTags = ... style, ..., iframe, script, ...
in the TypoScript part of the site package.
style seems to be standard and is already part of this list.
The path to the local site package is:
local_packages/gpcf_theme
with a symbolic link to it from:
public/typo3conf/ext/gpcf_theme -> ../../../local_packages/gpcf_theme
These are the important file changes:
local_packages/gpcf_theme/composer.json:
{
"name": "oheil/gpcf_theme",
...
"autoload": {
"psr-4": {
"Oheil\\GpcfTheme\\": "Classes/"
}
},
...
}
local_packages/gpcf_theme/ext_localconf.php:
<?php
defined('TYPO3_MODE') || die();
...
$GLOBALS['TYPO3_CONF_VARS']['SYS']['htmlSanitizer']['default'] = \Oheil\GpcfTheme\MyDefaultBuilder::class;
...
local_packages/gpcf_theme/Classes/MyDefaultBuilder.php
<?php
namespace Oheil\GpcfTheme;
use TYPO3\CMS\Core\Html\DefaultSanitizerBuilder;
use TYPO3\HtmlSanitizer\Behavior;
use TYPO3\HtmlSanitizer\Behavior\Attr;
use TYPO3\HtmlSanitizer\Behavior\Tag;
class MyDefaultBuilder extends \TYPO3\CMS\Core\Html\DefaultSanitizerBuilder
{
protected function createBehavior(): \TYPO3\HtmlSanitizer\Behavior
{
// https://docs.typo3.org/c/typo3/cms-core/main/en-us/Changelog/9.5.x/Important-94484-IntroduceHTMLSanitizer.html
return parent::createBehavior()
->withName('common')
->withTags(
(new Tag(
'style',
Tag::ALLOW_CHILDREN + Behavior::ENCODE_INVALID_TAG
))->addAttrs(
(new Attr('type')),
...$this->globalAttrs
),
(new Tag(
'iframe',
Tag::ALLOW_CHILDREN
))->addAttrs(
...array_merge(
$this->globalAttrs,
[$this->srcAttr],
$this->createAttrs('scrolling', 'marginwidth', 'marginheight', 'frameborder', 'vspace', 'hspace', 'height', 'width')
)
),
(new Tag(
'script',
Tag::ALLOW_CHILDREN
))->addAttrs(
...array_merge(
$this->globalAttrs,
[$this->srcAttr]
)
),
// more tags...
);
}
}
After these changes of course you need to clear the TYPO3 cache and (not sure here) you need to do:
composer remove "oheil/gpcf_theme"
composer require "oheil/gpcf_theme"
The above is working now, but I am still happy for any expert to give some more insights in what is wrong or can be done better, perhaps easier?
And why does this not work for <font> ?
Example:
<font class="font713099">Some Text</font>
I implemented the Klaviyo Popup in the way that is explained at https://help.klaviyo.com/hc/en-us/articles/115005249548-Add-and-Customize-a-Legacy-Popup. How can I configure the error messages when the form does not validate? For example in the documentation I found this:
<script type="text/javascript" src="//www.klaviyo.com/media/js/public/klaviyo_subscribe.js"></script>
<script type="text/javascript">
KlaviyoSubscribe.attachModalSignUp({
list: 'LIST_ID',
delay_seconds: 10.5,
content: {
clazz: ' klaviyo_modal_LIST_ID',
header: 'Join our Newsletter!',
subheader: 'Get the latest and greatest news from us.',
button: 'Subscribe Me!',
success: 'Thanks! Check your email for a confirmation.',
extra_fields: ["$first_name", "$last_name"],
styles: '.klaviyo_modal.klaviyo_modal_LIST_ID { font-family: "Helvetica Neue", Arial}.klaviyo_modal.klaviyo_modal_LIST_ID .klaviyo_header { color:#222;}.klaviyo_modal.klaviyo_modal_LIST_ID .klaviyo_subheader { color:#222;}.klaviyo_modal.klaviyo_modal_LIST_ID .klaviyo_submit_button,.klaviyo_modal.klaviyo_modal_LIST_ID .klaviyo_submit_button span { background-color:#0064cd; background-image: none; border-radius: 2px;}.klaviyo_modal.klaviyo_modal_LIST_ID .klaviyo_submit_button:hover,.klaviyo_modal.klaviyo_modal_LIST_ID .klaviyo_submit_button span:hover { background-color:#0064cd; background-image: none; }.klaviyo_modal.klaviyo_modal_LIST_ID .klaviyo_inner,.klaviyo_modal.klaviyo_modal_LIST_ID .klaviyo_fieldset .klaviyo_field_group input[type=text],.klaviyo_modal.klaviyo_modal_LIST_ID .klaviyo_fieldset .klaviyo_field_group input[type=email] { border-radius: 2px;}'
}
});
</script>
Notice how in the documentation they include this part:
success: 'Thanks! Check your email for a confirmation.',
I was expecting to see something like this for error messages:
error_message: 'My custom error message.',
Is there a way to configure custom error messages? Thank you.
When reading this Klaviyo's blog entry at https://www.klaviyo.com/blog/new-klaviyo-popup-builder, it is my impression that they want to focus on this New Klaviyo Form Builder, which is great for people who need something easier than writing HTML/CSS. It would not surprise me if Klaviyo does not have a way to customize error messages using the "Legacy Popup", as they call it at https://help.klaviyo.com/hc/en-us/articles/115005249548-Add-and-Customize-a-Legacy-Popup, even though to me it should not be a "legacy" way to build pop-ups in Klaviyo.
I'm using tinymce a trying to extend a plugin to show a dialog with specific layout:
editor.windowManager.open({
title: 'Title of my dialog',
body: [
{type: 'label', text: 'my label'},
{ name:'my_input', type: 'textbox'},
// { type: 'text', html:'some content with <b>bold</b> if posilbe!'},
// { type: 'html', value:'<div>with custom formating</div>'}
]
}
I checked the the documentation for tinymce.ui several times but can find a way to add html or text component in the constructor of the dialog (like the comment rows in the example).
I know there is a option using a ready html template for the dialog.. but there are also a lot of events and triggers so using the constructor and .ui components is more suitable for my case.
I used to use JQuery UI dialog for this but ran into some issues after TinyMCE 4.0.
I have a TinyMCE plugin that lets people fetch the plain text version of their post in the WordPress editor. Then I show them that text using this:
var plain_block = {
type: 'container',
html: '<textarea style="margin: 10px; width: 550px !important; height: 450px !important; background-color: #eee;" readonly="readonly">Whatever plain text I need to show goes here</textarea>'
};
ed.windowManager.open({
title: "Plain Text of This Post",
spacing: 10,
padding: 10,
items: [
plain_block
],
buttons: [
{
text: "Close",
onclick: function() { ed.windowManager.close();}
}
]
});
End result is a pretty plain-jane dialog box with some HTML and a Close button
I am using Tiny 4.3.3 for MODx
I need to add a
<p class="classname">
<em class="openImg"></em>
Some randome Input text by the user
<em class="closeImg"></em>
</p>
I don't mind if is an extra menu Item or is in the Paragraph dropdown menu. I just want the less time consuming work around possible.
I have tried this http://alexzag.blogspot.co.uk/2009/12/custom-tags-in-tinymce.html but somehow this doesn't work.
Could anyone point me to a good tutorial or tell me how could i add a icon or name to the drop down menu that creates the p and em tags with the right classes automatically please?
Thanks
It has been a while since the question was asked, but as i am currently making exactly the same, i thought i share my discoveries and solutions regarding this matter. :)
I am extending TinyMCE for a test-project at work and our solution needs custom tags - in some of them the user should be able to enter only one line, in others (as your em) a lot of text.
Steps to be done, in order to achieve the desired solution:
tell the TinyMCE editor, that your elements are good using the two configuration keywords extended_valid_elements and custom_elements:
tinymce.init({
selector: "textarea#editor",
// ...
extended_valid_elements : "emstart,emend",
custom_elements: "emstart,emend",
content_css: "editor.css"
});
create the two images for the opening and the closing tag. I named mine for the example emstart.png and emend.png.
create a custom CSS style for your custom elements and put them in the custom CSS file (the one that is specified in the TinyMCE configuration, in my case editor.css):
emstart {
background: url(emstart.png) no-repeat;
background-position: left -3px top -3px;
padding: 10px 10px 5px 10px;
background-color:#aabbcc;
border:1px dotted #CCCCCC;
height:50px;
width:100px;
}
emend {
background: url(emend.png) no-repeat;
background-position: left -3px bottom -3px;
padding: 5px 10px 10px 10px;
background-color:#aabbcc;
border:1px dotted #CCCCCC;
height:50px;
width:100px;
}
write a custom plugin that inputs the new tags and put it in the plugins directory. I called mine customem:
plugin code:
tinymce.PluginManager.add('customem', function(editor, url) {
// Add a button that opens a window
editor.addButton('customEmElementButton', {
text: 'Custom EM',
icon: false,
onclick: function() {
// Open window
editor.windowManager.open({
title: 'Please input text',
body: [
{type: 'textbox', name: 'description', label: 'Text'}
],
onsubmit: function(e) {
// Insert content when the window form is submitted
editor.insertContent('<emstart>EM Start</emstart><p>' + e.data.description + '</p><emend>EM End</emend>');
}
});
}
});
// Adds a menu item to the tools menu
editor.addMenuItem('customEmElementMenuItem', {
text: 'Custom EM Element',
context: 'tools',
onclick: function() {
editor.insertContent('<emstart>EM Start</emstart><p>Example text!</p><emend>EM End</emend>');
}
});
});
The last step is to load your custom plugin to the editor (using the plugin and toolbar configuration option) and enjoy the result:
tinymce.init({
selector: "textarea#editor",
height: "500px",
plugins: [
"code, preview, contextmenu, image, link, searchreplace, customem"
],
toolbar: "bold italic | example | code | preview | link | searchreplace | customEmElementButton",
contextmenu: "bold italic",
extended_valid_elements : "emstart,emend",
custom_elements: "emstart,emend",
content_css: "editor.css",
});
The editor now looks like this:
and the source like in your example:
First of all you will need to modify the tinymce setting valid_elements and valid_children to your needs (add em to the valid_elements and em as child to the tags desired (probably p) to valid_children).
Second you will need an own plugin with an own drop down or button to insert this code.
You can add one or more tag structures simply using the template plugin.
See documentation
https://www.tiny.cloud/docs/plugins/opensource/template/
See interactive example:
https://codepen.io/gpsblues/pen/WNdLgvb
tinymce.init({
selector: 'textarea#template',
height: 300,
plugins: 'template code',
menubar: 'insert',
toolbar: 'template code',
extended_valid_elements: "emstart[*],emend[*]",
templates : [
{
title: 'emstart/emend',
description: 'Add a personal tag structure with personal tags <emstart></emstart> <emend></emend>.',
content: '<p class="classname"><emstart class="openImg"></emstart>Input text<emend class="closeImg"></emend></p>'
}
],
content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:14px}'
});