XF 2.1 Programmatically creating editor button without creating a custom BB Code for it?

Jake B.

Well-known member
For our Giphy add-on we're manually inserting the editor button without adding a custom BBCode (since ultimately it resolves to the built in media site for Giphy, but we add an editor button to allow you to search for the image you want to use without leaving the editor). This has broken in 2.1 with the editor manager, is there a recommended way to add in a button that can be managed using the editor manager without creating a new BB Code for it?
 
Sure, there's a new event which fires in XF\Data\Editor::getButtonData() which is named editor_button_data.

Just add a definition for your button in there:
PHP:
$buttons['thGiphy'] = [
    'fa' => 'fa-some-icon',
    'title' => \XF::phrase('some_phrase')
];
 
I may be missing something obvious again, but we store the icon you want to use (either fontawesome or an image, with a default value of image) in style properties. I'm loading this value with:

$templater->fnProperty($templater, $escape, 'th_giphyIconValue_giphy')

However, for obvious reasons this doesn't show up in the manager interface since this style property doesn't exist in the admin style. For now I'm setting a fallback if this value doesn't exist, but just wondering if there would be a cleaner way to handle this
 
I have tried that and I can see the button in the button manager but not in the editor itself. Em I missing something?

Did you drag it into the editor for your viewport width? After doing that it showed up for me
 
Have you defined the command for it in the javascript classes? The editor_button_data event doesn't actually tell the editor about the styling of the buttons.

For my post macros add-on, I have this in a listener:

PHP:
public static function editorButtonData(array &$buttons, \XF\Data\Editor $editorData)
{
   $buttons['lwPostMacrosInsert'] = [
      'fa' => 'fa-bolt',
      'title' => \XF::phrase('liamw_postmacros_insert_macro'),
      'type' => 'dropdown'
   ];
}

And then this is called during the editor:start Javascript event:

JavaScript:
registerButtonCommands: function () {
   $.FE.DefineIcon('lwPostMacrosInsert', {NAME: "bolt"});
   $.FE.RegisterCommand('lwPostMacrosInsert', {
      type: 'dropdown',
      title: 'Insert Macro',
      undo: true,
      focus: true,
      html: function () {
         var o = '<ul class="fr-dropdown-list">';
         LiamW.PostMacros.macroInserter.macros.forEach(function (macro, k) {
            o += '<li><a class="fr-command" data-cmd="lwPostMacrosInsert" data-param1="' + k + '">' + macro['title'] + '</a></li>';
         });
         o += '</ul>';

         return o;
      },
      callback: function (cmd, val) {
         var ed = this;

         if (LiamW.PostMacros.macroInserter.macros[val]['hasTokens'])
         {
            XF.ajax("get", XF.canonicalizeUrl("index.php?account/macros/tokens"), {
               "macro_id": LiamW.PostMacros.macroInserter.macros[val]['macro_id'],
               "action_url": ed.$oel.closest('form').attr("action"),
               "editor_id": ed.$oel.attr('name')
            }, function (data, status, xhr) {
               XF.setupHtmlInsert(data.html, function ($html, container, onComplete) {
                  var overlay = new XF.Overlay(XF.getOverlayHtml({
                     html: $html,
                     title: container.title || container.h1
                  }));

                  overlay.on('overlay:hidden', function () {
                     overlay.destroy();
                  });

                  overlay.show();
               });
            });
         } else
         {
            XF.ajax("get", XF.canonicalizeUrl('index.php?account/macros/get'), {
               "macro_id": LiamW.PostMacros.macroInserter.macros[val]['macro_id'],
               "action_url": ed.$oel.closest('form').attr("action")
            }, function (data, status, xhr) {
               LiamW.PostMacros.macroInserter.processMacroResponse(data.macro, ed);
            });
         }
      }
   });
},

If I don't call that, then the button doesn't appear.

Liam
 
I have managed to get it to work by using $buttons['xfCustom_chat'] instead of $buttons['chat'] in the event listener.
@Chris D, any thoughts on this, please?

Ah, It looks like editor.js defines the froala command and icons for the custom bb code entries (and bbcode with an xfCustom_ prefix). (See line 2516 onwards).
 
Last edited:
Just want to clarify for everyone reading this topic.

This:
Sure, there's a new event which fires in XF\Data\Editor::getButtonData() which is named editor_button_data.

Just add a definition for your button in there:
PHP:
$buttons['thGiphy'] = [
    'fa' => 'fa-some-icon',
    'title' => \XF::phrase('some_phrase')
];
will only add your button to "BB code button manager" page in ACP. The button will not appear in Froala editor on frontend!

In order to add it to Froala, you are to write a JS script and load it at <!--[XF:include_js]--> marker in editor template:

JavaScript:
(function ($, window, document)
{
    "use strict";

    $(document).on('editor:first-start', function ()
    {
        $.FE.DefineIcon('MyButton', { NAME: ' FONT AWSOME ICON CODE WITHOUT fa- ' });
        $.FE.RegisterCommand('MyButton', {
            title: 'My Button',
            icon: 'MyButton',
            undo: true,
            focus: true,
            callback: function ()
            {
                // Do cool stuff here!
            }
        });

        XF.editorStart.custom.push('MyButton');
    });

})
(jQuery, window, document);
MyButton must be the ID of the button you specify when adding a button at editor_button_data event. So it is going to be thGiphy for the quote above.

Don't forget to include translated button title phrase. This can be done by through editor template modification. Search "__lang end__": "" Replace:
Code:
"My Button": "{{ phrase('my_button')|escape('json') }}",

$0

Note how "My Button" equals title: 'My Button' you specified in JS script!
 
Last edited:
Top Bottom