5

Right now I'm trying to accomplish routing users through a controller action before rendering the plugin's CP template...

[Plugin]Plugin.php

// ...
public function registerCpRoutes()
  {
    return [
      'plugin/controller' => ['action' => 'plugin/controller/action']
    ];
  }
// ...

The idea here is that I want to perform validation checks in the controller before I serve up the view.

I'm running into two issues when it comes to rendering the template from the controller:

  1. The template path is set to the core template directory (which makes plugin templates inaccessible)
  2. If I reset the template path to, say, the plugin's directory then I can no-longer extend core layouts (like _layouts/cp) within my template.

My solution was to then just fetch the plugin's template as a string and then render it using the renderString method so that way I can use the template without modifying any path variables...

[Plugin]Controller.php

// ...
protected function renderPluginTemplate($template, $variables = array())
  {
    $oldPath = craft()->path->getTemplatesPath();
    $newPath = craft()->path->getPluginsPath() . 'plugin/templates';

    craft()->path->setTemplatesPath($newPath);

    if(!$templatePath = craft()->templates->findTemplate($template)) {
      throw new Exception(Craft::t('Could not find template with path: ' . $template));
    } else {
      $templateContent = file_get_contents($templatePath);
    }

    craft()->path->setTemplatesPath($oldPath);

    echo craft()->templates->renderString($templateContent, $variables);
  }
// ...

This works for the most part. Now the layout isn't pulling in the right Javascript dependencies – no JS gets fired – so the panel content is hidden and the dropdown menus don't work.

There must be a better way to do this. I'd be super grateful if anyone could provide any insight.

Jon

1 Answer 1

7

Figured it out!

I went ahead and took at look at how Craft's renderTemplate method works which cleared it up for me. A few things...

  • Craft caches CSS/JS resources inside of getHeadHtml() and getFootHtml() which are automatically injected into every template rendered through renderTemplate. Injecting these into my template fixed the JS problem.
  • I forgot to set both the Content-Type and charset headers. Not necessary in my case but it's something that's good to add anyway.
  • To keep things consistent with the way Craft does things I outputted everything into a buffer and terminated the request.

Here's the working concept of my renderPluginTemplate method; I borrowed lots from Craft's renderTemplate function within the BaseController:

protected function renderPluginTemplate($template, $variables = array())
  {

    // Grab the new template's content
    // ---------------------------------------------------------------------

    $oldPath = craft()->path->getTemplatesPath();
    $newPath = craft()->path->getPluginsPath() . 'plugin/templates';

    // Set template path to the plugin directory (temporarily)
    craft()->path->setTemplatesPath($newPath);

    // Attempt to fetch the template content/extension.
    if(!$templatePath = craft()->templates->findTemplate($template)) {
      throw new Exception(Craft::t('Could not find template with path: ' . $template));
    } else {
      $templateContent   = file_get_contents($templatePath);
      $templateExtension = IOHelper::getExtension($templatePath, 'html');
    }

    // Reset the template path
    craft()->path->setTemplatesPath($oldPath);

    // Set up the headers and render the template
    // ---------------------------------------------------------------------

    if($output = craft()->templates->renderString($templateContent, $variables)) {
      // Set the MIME type for the request based on the matched template's file extension (unless the
      // Content-Type header was already set, perhaps by the template via the {% header %} tag)
      if (!HeaderHelper::isHeaderSet('Content-Type')) {
        HeaderHelper::setContentTypeByExtension($templateExtension);
      }

      // Set the charset header
      HeaderHelper::setHeader(array('charset' => 'utf-8'));

      // Are we serving HTML or XHTML?
      if(in_array(HeaderHelper::getMimeType(), array('text/html', 'application/xhtml+xml'))) {
        // Are there any header/footer nodes in the queue?
        $headHtml = craft()->templates->getHeadHtml();
        $footHtml = craft()->templates->getFootHtml();

        if($headHtml) {
          if(($endHeadPos = mb_stripos($output, '</head>')) !== false) {
            $output = mb_substr($output, 0, $endHeadPos).$headHtml.mb_substr($output, $endHeadPos);
          } else {
            $output .= $headHtml;
          }
        }
        if($footHtml) {
          if(($endBodyPos = mb_stripos($output, '</body>')) !== false) {
            $output = mb_substr($output, 0, $endBodyPos).$footHtml.mb_substr($output, $endBodyPos);
          } else {
            $output .= $footHtml;
          }
        }
      }

      // Output it into a buffer, in case TasksService wants to close the connection prematurely
      ob_start();
      echo $output;

      // End the request
      craft()->end();
    }
  }
1
  • Thanks for that, Jon. For anyone interested in a Craft 3 Solution: Set Headers: Craft::$app->response->headers->set('charset', 'utf-8'); To render: $markup = Craft::$app->view->renderString($template, $variables); Head HTML: $headHtml = Craft::$app->view->getHeadHtml(); Body HTML: $bodyHtml = Craft::$app->view->getBodyHtml(); Jun 9, 2021 at 20:47

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.