Rendering templates from plugin controller 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' =&gt; ['action' =&gt; '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:


The template path is set to the core template directory (which makes plugin templates inaccessible)
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()-&gt;path-&gt;getTemplatesPath();
    $newPath = craft()-&gt;path-&gt;getPluginsPath() . 'plugin/templates';

    craft()-&gt;path-&gt;setTemplatesPath($newPath);

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

    craft()-&gt;path-&gt;setTemplatesPath($oldPath);

    echo craft()-&gt;templates-&gt;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
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()-&gt;path-&gt;getTemplatesPath();
    $newPath = craft()-&gt;path-&gt;getPluginsPath() . 'plugin/templates';

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

    // Attempt to fetch the template content/extension.
    if(!$templatePath = craft()-&gt;templates-&gt;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()-&gt;path-&gt;setTemplatesPath($oldPath);

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

    if($output = craft()-&gt;templates-&gt;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' =&gt; '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()-&gt;templates-&gt;getHeadHtml();
        $footHtml = craft()-&gt;templates-&gt;getFootHtml();

        if($headHtml) {
          if(($endHeadPos = mb_stripos($output, '&lt;/head&gt;')) !== false) {
            $output = mb_substr($output, 0, $endHeadPos).$headHtml.mb_substr($output, $endHeadPos);
          } else {
            $output .= $headHtml;
          }
        }
        if($footHtml) {
          if(($endBodyPos = mb_stripos($output, '&lt;/body&gt;')) !== 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()-&gt;end();
    }
  }