4

I have a project that I am writing a component, module, and editor plugin for.

I have managed to piece together a rudimentary plugin that works with TinyMCE (at least) which:

  1. Displays a custom button
  2. Triggers a popup modal window containing a dynamic table of buttons using db data
  3. Inserts a custom, curly-braced placeholder into an article.

The part that I am concerned with is making my solution "upgrade proof".

I followed https://mrgott.com/13-joomla/17-how-to-create-joomla-editors-xtd-plugin-button-and-open-modal-popup as much as I could, until I needed to break away and build beyond what is mentioned. That resource warns:

NOTE: Yes, the first question that arises is: What if after each update your file will be removed as it’s not a part of Joomla’s com_content component. Yeah, that’s right, but actually I’m fine by keeping this file, and uploading it after each system update, rather then doing plugin development in the wrong and dirty way.

If you are building this plugin for your client, you should better build a component, or maybe you already built some component and just add a new view file there for your editors-xtd plugin.

I feel like my component will need more than just a new file in views. I would rather learn "the right way". I don't want to "give up" like Philippe. What files do I need to create (copy into my component and modify) to make my plugin long-lasting? I could also use a tip on how to add a little icon to my custom button in the editor; right now I only have text with an empty space on its left side.

The onDisplay() function in /plugins/editors-xtd/picturelinkcode/picturelinkcode.php:

public function onDisplay($name)
{       
    $query_string = [
        "option" => "com_content",      // I want this to be "com_picture",
        "view"   => "article",
        "layout" => "picturelinkcode",  // some examples use "modal" here
        "tmpl"   => "component",
        "editor" => $name
    ];

    $link = 'index.php?' . http_build_query($query_string);

    $button = new JObject;
    $button->modal = true;
    $button->class = 'btn';
    $button->link  = $link;
    $button->text  = JText::_('Linkcode');
    $button->name  = 'picturelinkcode';
    $button->options = "{handler: 'iframe', size: {x: 600, y: 400}}";

    return $button;
}

The /administrator/components/com_content/views/article/tmpl/picturelinkcode.php file:

<?php
defined('_JEXEC') or die;

$document     = JFactory::getDocument();
$this->editor = JFactory::getApplication()->input->getCmd('editor', '');
$this->editor = preg_replace('~[^\w[\]-]+~', '', $this->editor);  // sanitize

/**
 * ...better coding practice would probably be
 * to store the js and the data processing
 * outside of this tmpl file
 *   --apologies while sprinting
 */
$script  = 'function insertPictureLinkcode(linkcode) {' . "\n";
$script .= '  var tag = "{loadpicture " + linkcode + "}";' . "\n";
$script .= '  window.parent.jInsertEditorText(tag, ' . json_encode($this->editor) . ');' . "\n"; // "jform_articletext"
$script .= '  window.parent.jModalClose();' . "\n";
$script .= '  return false;' . "\n";
$script .= '}' . "\n";

JFactory::getDocument()->addScriptDeclaration($script);

?><h3>Picture Linkcodes</h3><?php

// Retrieve all linkcodes and their associated counts
$db = JFactory::getDBO();
$subquery = $db->getQuery(true)
               ->select("id")
               ->from("#__fields")
               ->where([
                   "context = 'com_picture.picture'",
                   "name = 'linkcode'"
                 ]);
$query = $db->getQuery(true)
            ->select("value AS linkcode, COUNT(*) AS picturecount")
            ->from("#__fields_values")
            ->where("field_id = ($subquery)")
            ->group("value");
$db->setQuery($query);

try
{
    $resultset = $db->loadObjectList();
    if (!$resultset)
    {
        echo "No Linkcodes Available Yet<br>";
    }
    else
    {
        ?>
        <form class="form">
            <table>
                <?php
                foreach ($resultset as $row)
                {
                    ?>
                    <tr>
                        <td>
                            <button onclick="insertPictureLinkcode('<?php echo $row->linkcode; ?>');" class="btn btn-primary">
                                <?php echo $row->linkcode; ?>
                            </button>
                        </td>
                        <td>
                            (<?php echo $row->picturecount; ?>)
                        </td>
                    </tr>
                    <?php
                }
                ?>
            </table>
        </form>
        <?php
    }
} catch (Exception $e) {
    echo "<div class='bg-danger'>Picture Linkcode Query Error - Contact Developer</div>";
    //echo "<div class='bg-danger'>" , $query->dump() , "<br>" , $e->getMessage() , "</div>";
}

Screenshots of my currently functional coding attempt: https://imgur.com/a/kP2WGKo (I should ask @Eoin what tech he uses to make those nifty animated gifs)

In case it matters, my component is nearly identical to the core's adminstistrator/components/com_contact and my module will only have one job -- to replace placeholders in articles with dynamic content. My plugin will eventually have greater functionality than I am showing in this question.

  • The button should link to the item list view of your component. In com_contact this would be the contacts view. Does your component have a model-view where items are retrieved using the database queries shown in your question? – Sharky Dec 10 '18 at 7:23
  • My com_picture doesn't yet have any altered files. All of the folders and files are just like com_contact with the exception that I have altered filenames, variable names, class names, and maybe a function name or two to align with picture instead of contact. If I can get away with not writing a mod_picture module to render my {placeholder} that would be best. Have I answered your question properly? @Sharky – mickmackusa Dec 10 '18 at 7:45
  • I blindly attempted to copy my working picturelinkcode.php file from /administrator/components/com_content/view/article/tmpl to /administrator/components/com_picture/view/article/tmpl but that resulted in View not found [name, type, prefix]: article, html, pictureView I only understand about 45% of what I am doing, the rest is "Joomla magic" to me. I don't fully understand the data that I am using in $query_string. ...Ah, I have not yet created an index.html and view.html.php files in com_picture/views/article/tmpl – mickmackusa Dec 10 '18 at 7:55
  • Should I be using a modal.php file in my article/tmpl folder instead of calling the file picturelinkcode.php? More intuitive I think. – mickmackusa Dec 10 '18 at 7:59
  • I'm still unsure of how and where you're planning to store your items. If you renamed everything from contact to picture, you probably have #__picture_details table in your database. But you're querying #__fields_values table instead? So, are your items just custom field values or do you intend to manage them like in other components (e.g. com_contact) and store them in their own table? – Sharky Dec 10 '18 at 8:06
2

Normally, the plugin would open a list of items. In this case, the component doesn't have the model/view for the items. So you have to create them.

As a bare minimum, the model must contain getListQuery() method which returns the query used to retrieve the items.

administrator/components/com_picture/models/items.php:

defined('_JEXEC') or die;

class PictureModelItems extends JModelList
{
    protected function getListQuery()
    {
        // Retrieve all linkcodes and their associated counts
        $db = $this->_db;
        $subQuery = $db->getQuery(true)
            ->select($db->quoteName('id'))
            ->from($db->quoteName('#__fields'))
            ->where(
                [
                    $db->quoteName('context') . ' = ' . $db->quote('com_picture.picture'),
                    $db->quoteName('name') . ' = ' . $db->quote('linkcode'),
                ]
            );

        $query = $db->getQuery(true)
            ->select([$db->quoteName('value', 'linkcode'), 'COUNT(*) AS picturecount'])
            ->from($db->quoteName('#__fields_values'))
            ->where($db->quoteName('field_id') . ' = (' . $subQuery . ')')
            ->group($db->quoteName('value'));

        return $query;
    }
}

The view, as a bare minimum, must retrieve the items.

administrator/components/com_picture/views/items/view.html.php:

defined('_JEXEC') or die;

class PictureViewItems extends JViewLegacy
{
    protected $items;

    public function display($tpl = null)
    {
        $this->items = $this->get('Items');

        return parent::display($tpl);
    }
}

Then, in the view layout output the list of items in a table or whatever other format you prefer.

In the button plugin, change link property to point to your new view:

$button->link = 'index.php?option=com_picture&amp;view=items&amp;layout=modal&amp;tmpl=component&amp;editor=' . $name . '&amp;' . JSession::getFormToken() . '=1';

I could also use a tip on how to add a little icon to my custom button in the editor

The icon is coming from button's name property. It gets icon- added in front to form a IcoMoon icon class. E.g. if you set it to joomla the resulting class will be icon-joomla. See all icons here.

  • I've had to continue sprinting with this project, but I do intend to circle back and try your advice. Could you please specify where these new files need to be located? And can you explain why your solution (and several others that I have seen) are using &amp; in $button->link while my original script works fine with just &? – mickmackusa Dec 12 '18 at 1:53
  • 1
    Updated the answer to indicate file locations. – Sharky Dec 12 '18 at 6:48
  • 1
    My guess is ampersand is escaped for compatibility with older browsers/templates and HTML validity. – Sharky Dec 12 '18 at 7:15
  • Okay @Sharky I have com_picture/models/items.php returning the successful query. The var_dump($this->items) is delivering the expected result set. I am calling $this->items from inside of com_picture/views/items/tmpl/modal.php and writing all my dom there. This is what your are advising me to do, right? – mickmackusa Feb 4 at 2:28
  • 1
    That snippet is related to this code in modal.php layout: JFactory::getDocument()->addScriptOptions('xtd-contacts', array('editor' => $editor));. Change xtd-contacts in both places to something else, e.g. xtd-pictures. Regarding Bootstrap, it is still unclear whether your template includes BS styling globally (in index.php file) or not. If it does, it should also do so in component.php file. – Sharky Feb 4 at 9:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.