-
-
Save stracker-phil/59da31e6143bbdd133a3de3d4bc88263 to your computer and use it in GitHub Desktop.
| <?php | |
| /** | |
| * Custom Block to output Code. | |
| */ | |
| namespace MailPoet\Newsletter\Renderer\Blocks; | |
| // Exit if accessed directly. | |
| defined( 'ABSPATH' ) || exit; | |
| /** | |
| * Registers a custom MailPoet Block: Code. | |
| * | |
| * Note that the capitalization is important here. | |
| */ | |
| class Mycode { | |
| /** | |
| * The Module-Slug of this content block. | |
| * | |
| * @var string | |
| */ | |
| private static $module_key = 'mycode'; // MUST match the classname, but should be all lowercase!! | |
| /** | |
| * Initializes the custom content block. | |
| */ | |
| public static function init() { | |
| add_action( | |
| 'admin_footer-admin_page_mailpoet-newsletter-editor', | |
| [ __CLASS__, 'js_templates' ] | |
| ); | |
| } | |
| /** | |
| * Outputs JS templates for the MailPoet editor to enable the custom | |
| * content block. | |
| * | |
| * @return void | |
| */ | |
| public static function js_templates() { | |
| ?> | |
| <script type="text/x-handlebars-template" id="tmpl_widget_<?php echo esc_attr( self::$module_key ); ?>"> | |
| <div class="mailpoet_widget_icon"> | |
| <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"> | |
| <path fill="currentColor" d="M278.9 511.5l-61-17.7c-6.4-1.8-10-8.5-8.2-14.9L346.2 8.7c1.8-6.4 8.5-10 14.9-8.2l61 17.7c6.4 1.8 10 8.5 8.2 14.9L293.8 503.3c-1.9 6.4-8.5 10.1-14.9 8.2zm-114-112.2l43.5-46.4c4.6-4.9 4.3-12.7-.8-17.2L117 256l90.6-79.7c5.1-4.5 5.5-12.3.8-17.2l-43.5-46.4c-4.5-4.8-12.1-5.1-17-.5L3.8 247.2c-5.1 4.7-5.1 12.8 0 17.5l144.1 135.1c4.9 4.6 12.5 4.4 17-.5zm327.2.6l144.1-135.1c5.1-4.7 5.1-12.8 0-17.5L492.1 112.1c-4.8-4.5-12.4-4.3-17 .5L431.6 159c-4.6 4.9-4.3 12.7.8 17.2L523 256l-90.6 79.7c-5.1 4.5-5.5 12.3-.8 17.2l43.5 46.4c4.5 4.9 12.1 5.1 17 .6z"></path> | |
| </svg> | |
| </div> | |
| <div class="mailpoet_widget_title">Code</div> | |
| </script> | |
| <script type="text/x-handlebars-template" id="tmpl_block_<?php echo esc_attr( self::$module_key ); ?>"> | |
| <div class="mailpoet_tools"></div> | |
| <div class="custom_block_code_wrap"> | |
| <pre style="margin:0;padding:10px 12px;border-radius:5px;background:#3c4858;color:#eee;font-size:15px;font-family:monospace" class="mailpoet_content custom_block_code_content" data-automation-id="text_block_in_editor">{{{ model.text }}}</pre> | |
| </div> | |
| <div class="mailpoet_block_highlight"></div> | |
| </script> | |
| <script type="text/x-handlebars-template" id="tmpl_settings_<?php echo esc_attr( self::$module_key ); ?>"> | |
| <h3>Text</h3> | |
| Text: <textarea name="text" class="text" rows="5" cols="40">{{ model.text }}</textarea> | |
| </script> | |
| <script> | |
| window.templates['<?php echo esc_attr( self::$module_key ); ?>Insertion'] = Handlebars.compile( | |
| jQuery('#tmpl_widget_<?php echo esc_attr( self::$module_key ); ?>').html() | |
| ); | |
| window.templates['<?php echo esc_attr( self::$module_key ); ?>Block'] = Handlebars.compile( | |
| jQuery('#tmpl_block_<?php echo esc_attr( self::$module_key ); ?>').html() | |
| ); | |
| window.templates['<?php echo esc_attr( self::$module_key ); ?>Settings'] = Handlebars.compile( | |
| jQuery('#tmpl_settings_<?php echo esc_attr( self::$module_key ); ?>').html() | |
| ); | |
| window.config.blockDefaults['<?php echo esc_attr( self::$module_key ); ?>'] = { | |
| text: 'function test() { ... }' | |
| }; | |
| window.EditorApplication.on("before:start", function(App) { | |
| registerCustomBlock(App, '<?php echo esc_attr( self::$module_key ); ?>' ); | |
| }); | |
| </script> | |
| <?php | |
| } | |
| /** | |
| * Generates the HTML code for a custom content block. | |
| * | |
| * @param array $block Block settings. | |
| * @return string The rendered content. | |
| */ | |
| public static function render( $block ) { | |
| $html = $block['text']; | |
| $html = str_replace( [ "\r\n", "\r" ], "\n", $html ); | |
| $html = preg_replace( '/<br.*?>/', "\n", $html ); | |
| $html = preg_replace( '/<p>|<p .*?>/', "\n\n", $html ); | |
| $html = str_replace( '/<p>/', '', $html ); | |
| $html = str_replace( ' ', ' ', $html ); | |
| $html = str_replace( '\xc2\xa0', ' ', $html ); | |
| $lines = explode( "\n", $html ); | |
| $rows = []; | |
| $style = 'padding:3px 12px;font-family:monospace;font-size:15px;color:#f0f0f0'; | |
| $last_ind = count( $lines ) - 1; | |
| foreach ( $lines as $ind => $line ) { | |
| $line = rtrim( $line ); | |
| $line = preg_replace( '/^\s/', ' ', $line ); | |
| $line = preg_replace( '/>\s/', '> ', $line ); | |
| $line = str_replace( ' ', ' ', $line ); | |
| if ( ! $line ) { | |
| $line = ' '; | |
| } | |
| $row_style = ''; | |
| if ( ! $ind ) { | |
| $row_style = ';padding-top:10px'; | |
| } elseif ( $last_ind === $ind ) { | |
| $row_style = ';padding-bottom:10px'; | |
| } | |
| $rows[] = sprintf( | |
| '<tr><td style="%s">%s</td></tr>', | |
| $style . $row_style, | |
| $line | |
| ); | |
| } | |
| $template = ' | |
| <tr> | |
| <td class="mailpoet_text mailpoet_padded_vertical mailpoet_padded_side" valign="top" style="word-break:break-word;word-wrap:break-word;"> | |
| <table style="border-radius: 5px;background: #3c4858" width="100%"> | |
| ' . implode( "\n", $rows ) . ' | |
| </table> | |
| </td> | |
| </tr>'; | |
| return $template; | |
| } | |
| } | |
| Mycode::init(); |
| <?php | |
| /** | |
| * Plugin Name: Sample MailPoet blocks | |
| * Description: Sample Code-Block for MailPoet | |
| * Author: Philipp Stracker | |
| * | |
| * ----- | |
| * | |
| * Proof of concept for https://feedback.mailpoet.com/feature-requests/p/api-to-create-custom-content-blocks | |
| * | |
| * To make this demo work, a single line of JS needs to be added here | |
| * https://github.com/mailpoet/mailpoet/blob/master/assets/js/src/newsletter_editor/blocks/base.js#L340 | |
| * | |
| * Add this line: | |
| * window.MailPoetBaseModule = Module; | |
| */ | |
| namespace MyPlugin\MailPoetBlocks; | |
| // Exit if accessed directly. | |
| defined( 'ABSPATH' ) || exit; | |
| /** | |
| * Enqueues our custom assets for the MailPoet Email editor. | |
| * Those assets provide additional content blocks to use in emails. | |
| * | |
| * @return void | |
| */ | |
| function enqueue_block_scripts() { | |
| wp_enqueue_script( | |
| 'demo-block', | |
| plugins_url( '/register-mailpoet-block.js', __FILE__ ), | |
| [ 'jquery' ], | |
| false, | |
| true | |
| ); | |
| } | |
| add_action( 'load-admin_page_mailpoet-newsletter-editor', __NAMESPACE__ . '\enqueue_block_scripts' ); | |
| // Include our custom module: Code Block. | |
| require_once _path( 'class-mycode.php' ); |
| function registerCustomBlock(App, moduleKey) { | |
| var Module = {}; | |
| if (!window.MailPoetBaseModule) { | |
| /** | |
| * Instructions to apply the MailPoet Patch: | |
| * | |
| * CMD + P: plugins/mailpoet/assets/dist/js/newsletter_editor.*.chunk.js | |
| * Search for: ,(\w+)\.(WidgetView=[\w\.]+\(\{.*?\}\))(,window\.MailPoetBaseModule=Object.assign\(.*?\))?([,;]) | |
| * Options: Match Case + Regular Expression | |
| * Replace with: ,$1.$2,window.MailPoetBaseModule=Object.assign({},$1)$4 | |
| */ | |
| console.error('Instructions to apply the MailPoet Patch:'); | |
| console.log(' 1. Edit file:', 'plugins/mailpoet/assets/dist/js/newsletter_editor.*.chunk.js'); | |
| console.log(' 2. Regex search:', ',(\\w+)\\.(WidgetView=[\\w\\.]+\\(\\{.*?\\}\\))(,window\\.MailPoetBaseModule=Object.assign\\(.*?\\))?([,;])'); | |
| console.log(' 3. Replace with:', ',$1.$2,window.MailPoetBaseModule=Object.assign({},$1)$4'); | |
| alert('MailPoetBaseModule is not available. Please apply the patch manually (details in JS console)'); | |
| return; | |
| } | |
| // Define the Block Model. | |
| Module.BlockModel = MailPoetBaseModule.BlockModel.extend({ | |
| defaults: function defaults() { | |
| return this._getDefaults( | |
| { | |
| type: moduleKey, | |
| text: '', | |
| }, | |
| App.getConfig().get('blockDefaults.' + moduleKey) | |
| ); | |
| }, | |
| }); | |
| // The tools-view mainly creates the settings-icon in the editor area. | |
| Module.BlockToolsView = MailPoetBaseModule.BlockToolsView.extend({ | |
| getSettingsView: function () { return Module.BlockSettingsView; }, | |
| }); | |
| // The settings view defines block settings that are displayed in the settings modal on the right side. | |
| Module.BlockSettingsView = MailPoetBaseModule.BlockSettingsView.extend({ | |
| getTemplate: function getTemplate() { return window.templates.textBlockSettings; }, | |
| }); | |
| // Define the Block View. | |
| Module.BlockView = MailPoetBaseModule.BlockView.extend({ | |
| className: 'mailpoet_block mailpoet_' + moduleKey + '_block mailpoet_droppable_block', | |
| getTemplate: function getTemplate() { | |
| return window.templates[moduleKey + 'Block']; | |
| }, | |
| modelEvents: _.omit(MailPoetBaseModule.BlockView.prototype.modelEvents, 'change'), // Prevent rerendering on model change due to text editor redrawing | |
| behaviors: _.extend({}, MailPoetBaseModule.BlockView.prototype.behaviors, { | |
| TextEditorBehavior: { | |
| toolbar1: 'bold italic underline forecolor | link unlink | code mailpoet_shortcodes', | |
| validElements: 'p[],span[class|style],a[href|class|title|target|style],h1[class|style],h2[class|style],h3[class|style],strong[class|style],em[class|style],strike,br,ins,del', | |
| invalidElements: 'script,style,ul,ol,pre', | |
| plugins: 'link code mailpoet_shortcodes paste', | |
| configurationFilter: function configurationFilter(originalSettings) { | |
| return _.extend({}, originalSettings, { | |
| mailpoet_shortcodes: App.getConfig().get('shortcodes').toJSON(), | |
| mailpoet_shortcodes_window_title: MailPoet.I18n.t('shortcodesWindowTitle'), | |
| }); | |
| }, | |
| }, | |
| }), | |
| onDragSubstituteBy: function onDragSubstituteBy() { | |
| return Module.WidgetView; | |
| }, | |
| initialize: function initialize(options) { | |
| MailPoetBaseModule.BlockView.prototype.initialize.apply(this, arguments); | |
| this.renderOptions = _.defaults(options.renderOptions || {}, { | |
| disableTextEditor: false, | |
| }); | |
| this.disableTextEditor = this.renderOptions.disableTextEditor; | |
| }, | |
| onRender: function () { | |
| this.toolsView = new Module.BlockToolsView({ | |
| model: this.model, | |
| tools: { | |
| settings: false, | |
| } | |
| }); | |
| this.showChildView('toolsRegion', this.toolsView); | |
| }, | |
| onTextEditorChange: function onTextEditorChange(newContent) { | |
| this.model.set('text', newContent); | |
| }, | |
| onTextEditorFocus: function onTextEditorFocus() { | |
| this.disableDragging(); | |
| this.disableShowingTools(); | |
| }, | |
| onTextEditorBlur: function onTextEditorBlur() { | |
| this.enableDragging(); | |
| this.enableShowingTools(); | |
| }, | |
| onBeforeDestroy: function onBeforeDestroy() { | |
| this.stopListening(this.model); | |
| }, | |
| }); | |
| // Define the Block Widget. | |
| Module.WidgetView = MailPoetBaseModule.WidgetView.extend({ | |
| id: 'automation_editor_block_' + moduleKey, | |
| getTemplate: function () { | |
| return window.templates[moduleKey + 'Insertion']; | |
| }, | |
| behaviors: { | |
| DraggableBehavior: { | |
| cloneOriginal: true, | |
| drop: function () { | |
| return new Module.BlockModel(); | |
| }, | |
| }, | |
| }, | |
| }); | |
| // Register the block when the editor loads. | |
| App.registerBlockType(moduleKey, { | |
| blockModel: Module.BlockModel, | |
| blockView: Module.BlockView, | |
| }); | |
| App.registerWidget({ | |
| name: moduleKey, | |
| widgetView: Module.WidgetView, | |
| priority: 95, | |
| }); | |
| } |
How it works:
- register-mailpoet-block.js ..renders the block inside the newsletter editor
- class-mycode.php .. configures the JS block and generates the HTML for the actual email
- my-mailpoet-blocks.php .. this is the plugin file that enqueued the JS and hooks up the class
I just tried this and it's throwing an error when I try to activate the plugin.
Fatal error: Uncaught Error: Call to undefined function MyPlugin\MailPoetBlocks_path() in \wp-content\plugins\my-mailpoet-blocks\my-mailpoet-blocks.php on line 42
Hey @BenComicGraphics - thanks for the heads-up. Apparently, MailPoet introduced some changes to their plugin that do not work with the above instructions...
Unfortunately, we're not using MailPoet anymore, so I cannot update this gist for a working solution.
I think your best bet it to upvote the official feature request here: https://mailpoet.canny.io/feature-requests/p/api-to-create-custom-content-blocks
@stracker-phil if I may ask, how much would you charge to build a custom poet block with some specific requriements? Just one block.
@BenComicGraphics, unfortunately I'm fully booked for the next 3 months and won't be able to take on any new projects.
It's worth noting that custom blocks for MailPoet will be removed and potentially broken with each MailPoet update. Every update could require additional development to adjust the custom block to the MailPoet plugin.
URL to the feature request: https://feedback.mailpoet.com/feature-requests/p/api-to-create-custom-content-blocks