Answers.Body: <p>My solution</p>
  
  <ol>
  <li><p>To keep the ‘index.html’ file of the magazines inside templates as per usual</p></li>
  <li><p>Store the other assets inside a private directory outside of the public folder</p></li>
  <li><p>Write a plugin that would look for requests to these files and serve them from the private place instead.</p></li>
  </ol>
  
  <p>I began by wireframing the plugin; I created a new folder inside craft/plugins named ‘magazineassets’. Inside this folder I added another folder named ‘controllers’ and a file named ‘MagazineAssetsPlugin.php’.</p>
  
  <pre><code>cd plugins
  mkdir magazineassets magazineassets/controllers magazineassets/MagazineAssetsPlugin.php
  </code></pre>
  
  <p>MagazineAssets.php</p>
  
  <pre><code>&lt;?php
  namespace Craft;
  
  class MagazineAssetsPlugin extends BasePlugin
  {
      public function getName()
      {
              return 'Magazine Assets';
      }
  
      public function getVersion()
      {
          return '1.0';
      }
  
      public function registerSiteRoutes()
      {
          return [
              'members/magazine/(?P&lt;issue&gt;.*)/(?P&lt;folder&gt;.*)/(?P&lt;file&gt;.*)' =&gt; ['action' =&gt; 'magazineAssets/getMagAsset'],
          ];
      }
  }
  </code></pre>
  
  <p>Once this was written, I went into the website’s admin panel and enabled the plugin.</p>
  
  <p>The method of interest here is the ‘registerSiteRoutes’ one. What this does is looks for any requests to members/magazine/[issue]/[folder]/[file] and ‘sends’ them to a controller that we haven’t written yet…
  MagazineAssetsController.php</p>
  
  <p>(stored inside the, earlier created, ‘controllers’ folder)</p>
  
  <pre><code>&lt;?php
  namespace Craft;
  
  class MagazineAssetsController extends BaseController
  {
      public function init()
      {
          if (!craft()-&gt;plugins-&gt;getPlugin('MagazineAssets'))
              throw new Exception('Couldn\'t find the Magazine Assets Plugin!');
      }
  
      public function actionGetMagAsset(array $variables = [])
      {
          $path = craft()-&gt;config-&gt;get('environmentVariables')['internalAssetsPath'].'magazines'.DIRECTORY_SEPARATOR.$variables['issue'].DIRECTORY_SEPARATOR.$variables['folder'].DIRECTORY_SEPARATOR.$variables['file'];
  
          if (IOHelper::fileExists($path)) {
                  $content = IOHelper::getFileContents($path);
                  craft()-&gt;request-&gt;sendFile($path, $content, ['forceDownload' =&gt; false]);
          } else {
              die('file not found at: '.$path);
          }
      }
  }
  </code></pre>
  
  <p>The method of interest here is the ‘actionGetMagAsset’ function which matches the ‘action’ that we are now routing to via the ‘registerSiteRoutes()’ plugin method.</p>
  
  <p>There’s no doubt in my mind that there’s probably some fancy way of writing that path using some baked in CraftCMS methods but I a) wasn’t certain what they are and b) wanted to be very rigid and certain that I definitely had the right path.</p>
  
  <p>The path begins using a config setting (at config/general.php) that I already had thanks to another plugin that I was already using followed by ‘/magazines/[issue]/[folder]/[file]’ (sound familiar?). The config is below but I guess I could have just as easily written it as if I didn’t have the config or didn’t want it…</p>
  
  <pre><code>&lt;?php
      $path = CRAFT_BASE_PATH."../files/".'magazines'.DIRECTORY_SEPARATOR.$variables['issue'].DIRECTORY_SEPARATOR.$variables['folder'].DIRECTORY_SEPARATOR.$variables['file'];
  ?&gt;
  </code></pre>
  
  <p>My config/general.php file:</p>
  
  <pre><code>&lt;?php
      return [
          //etc.
          'environmentVariables' =&gt; [
              'internalAssetsPath' =&gt; CRAFT_BASE_PATH . "../files/",
          ]
      ];
  ?&gt;
  </code></pre>
  
  <p>I then used Craft’s built in InputOutput helper class to display the file if it exists and tested via the front-end.</p>
  
  <p>Boom! All working now. Assets are loaded from the files folder, my file structure is like this (simplified obviously):</p>
  
  <pre><code>craft
      plugins
          magazineassets
              controllers
                  MagazineAssetsController.php
              MagazineAssetsPlugin.php
      templates
              members
                  magazine
                      july-2018
                          mag
                              index.html &lt;--- the magazine that needs the assets
  </code></pre>
  
  <p>Yeah, okay, but the magazine is still public!</p>
  
  <p>Right you are, it took two seconds to fix that. Into craft/templates/members/magazine/july-2018/mag/index.html I went and simply added…</p>
  
  <pre><code>{% requireLogin %}
  </code></pre>
  
  <p>…to the top of the file.</p>
  
  <p>I hope this post helps anyone who faces a similar conundrum.</p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-07-02T08:01:24.247Z
Answers.LastActivityDate: 2018-07-02T16:11:59.797Z
Answers.LastEditDate: 2018-07-02T16:11:59.797Z
Answers.UserDisplayName: H2ONOCK
Answers.Body: <p>Please note that the way you have this structured, all of your static resources such as images, JavaScripts, CSS, etc. are going to be run through PHP and Craft before being served up.</p>
  
  <p>This is going to be quite inefficient; static resources such as images, JavaScript, CSS, etc. (anything that <em>doesn't</em> have Twig code inside of it) shouldn't be served up from the <code>templates/</code> directory.</p>
  
  <p>Rather, they should be in your web root. I would reconsider the way you have your static resources located in <code>templates/</code></p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-06-29T10:49:25.230Z
Answers.Text: Thanks, I'm aware of that but for this use-case I'm not really too concerned.
Answers.UserDisplayName: H2ONOCK
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-06-28T04:08:48.410Z
Answers.LastActivityDate: 2018-06-28T04:08:48.410Z
Answers.UserDisplayName: andrew.welch
Answers.Body: <p>You'll need to create custom route to access those files. This is just an example of a custom route in my current module, so you need to find a pattern that matches the one you receive from the tool</p>
  
  <pre><code>$event-&gt;rules[] = [
      'pattern' =&gt; '/userfiles/&lt;fileType:\w+&gt;/&lt;fileName:\S+&gt;',
      'route'   =&gt; 'thaff/profile/get-file'
  ];
  </code></pre>
  
  <p>And your Controller action could be something like this</p>
  
  <pre><code>public function actionGetFile($view, $fileName)
  {
      // create an alias to your folderpath $view should be thumb/mobile-ext or mobile depending on the route
      $localPath = Craft::getAlias('@yourFolderAlias') . DIRECTORY_SEPARATOR . $view;
  
      // you can include several checks if the image should be visible for the current user or not...
  
      $resp = Craft::$app-&gt;getResponse();
      $resp-&gt;headers-&gt;set('Content-Type' /*, insert your mime-type here */);
      $resp-&gt;format = Response::FORMAT_RAW;
      // check if the file exists
      if ($localPath &amp;&amp; file_exists($localPath . $fileName)) {
          // open the file
          $handle = fopen($localPath . $fileName, 'rb');
          // send the result to your response object
          $resp-&gt;stream = $handle;
          // send the response and close the file
          $resp-&gt;send();
          @fclose($handle);
      }
  }
  </code></pre>
  
  <p>Note: this is just a little code snipped of my current project and I removed many conditional checks so it's easier to read. </p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-06-27T19:29:38.160Z
Answers.Text: Thanks for your help, so I need to write a new plugin that contains code based on the above do I? Or can this be placed somewhere else? I can add the custom route via admin can't I? Where would I put the controller?
Answers.UserDisplayName: H2ONOCK
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-06-27T21:00:31.960Z
Answers.Text: Those questions don't belong here - please read the documentation. Such things are asked daily and can't be answered in a comment. You'll need a custom plugin or module for it
Answers.UserDisplayName: Robin Schambach
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-06-30T13:47:36.300Z
Answers.Text: Thanks for your help I've sorted it out now thanks to the inspiration you gave me. I've written a [blog post](https://www.james-nock.co.uk/blog/tutorials/2018/06/30/craftcms-flipbuilder-plugin/) about the solution to help anyone with stumbles on a similar problem in the future.
Answers.UserDisplayName: H2ONOCK
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-07-02T03:10:33.603Z
Answers.Text: Awesome that you wrote a blog post covering your solution!
Answers.UserDisplayName: andrew.welch
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-07-02T08:01:55.700Z
Answers.Text: Cheers, I've just copied and pasted it as the answer too.
Answers.UserDisplayName: H2ONOCK
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-06-27T09:33:08.500Z
Answers.LastActivityDate: 2018-06-27T09:33:08.500Z
Answers.UserDisplayName: Robin Schambach
Body: <p>I have some 'Flip PDF' generated folders that I need only logged in users to be able to access. If I put them in templates I get the problem that the .jpg images <code>500</code>. Is there a way of allowing .jpg images to be served from within the templates folder? This isn't as simple as using an 'internal assets' plugin and adding them manually as all of the flip pdf JS is created automatically and I don't want to have to edit it every time a new one needs to be uploaded.</p>
  
  <p>I've searched high and low for the answer to this over the last few weeks as well as asking on slack but I've had no luck.</p>
  
  <p>Edit: maybe there's a .htaccess file I can add in the template sub directory that will prevent images being loaded in the normal 'craft' way?</p>
  
  <p>The folder structure looks like this:</p>
  
  <pre><code>craft
  templates
      emag
          july-2018
              files
                  mobile
                      *.jpg
                  mobile-ext
                      *.jpg
                  thumb
                      *.jpg
                      etc.
              mag
                  javascript
                      *.js (loads fine)
                  index.html &lt;--- the 'magazine' that needs the jpg's
  </code></pre>
  
  <p>The contents inside 'july-2018' is what the software gives me. All works fine, except for the .jpg images.</p>
  
Tags: <templating><image>
Title: Flip PDF embed inside templates
ThreadText: # Flip PDF embed inside templates
  
  (Asked by: username_0 on 2018-06-24)
  
  I have some 'Flip PDF' generated folders that I need only logged in users to be able to access. If I put them in templates I get the problem that the .jpg images `500`. Is there a way of allowing .jpg images to be served from within the templates folder? This isn't as simple as using an 'internal assets' plugin and adding them manually as all of the flip pdf JS is created automatically and I don't want to have to edit it every time a new one needs to be uploaded.
  
  I've searched high and low for the answer to this over the last few weeks as well as asking on slack but I've had no luck.
  
  Edit: maybe there's a .htaccess file I can add in the template sub directory that will prevent images being loaded in the normal 'craft' way?
  
  The folder structure looks like this:
  
  ```
  craft
  templates
      emag
          july-2018
              files
                  mobile
                      *.jpg
                  mobile-ext
                      *.jpg
                  thumb
                      *.jpg
                      etc.
              mag
                  javascript
                      *.js (loads fine)
                  index.html <--- the 'magazine' that needs the jpg's
  
  ```
  
  The contents inside 'july-2018' is what the software gives me. All works fine, except for the .jpg images.
  
  **Tags:** templating, image
  
  
  ---
  
  ## 3 answers
  
  ### Answer by username_0 on 2018-07-02. Score: 1 | Accepted answer
  
  My solution
  
  1. To keep the ‘index.html’ file of the magazines inside templates as per usual
  2. Store the other assets inside a private directory outside of the public folder
  3. Write a plugin that would look for requests to these files and serve them from the private place instead.
  
  I began by wireframing the plugin; I created a new folder inside craft/plugins named ‘magazineassets’. Inside this folder I added another folder named ‘controllers’ and a file named ‘MagazineAssetsPlugin.php’.
  
  ```
  cd plugins
  mkdir magazineassets magazineassets/controllers magazineassets/MagazineAssetsPlugin.php
  
  ```
  
  MagazineAssets.php
  
  ```
  <?php
  namespace Craft;
  
  class MagazineAssetsPlugin extends BasePlugin
  {
      public function getName()
      {
              return 'Magazine Assets';
      }
  
      public function getVersion()
      {
          return '1.0';
      }
  
      public function registerSiteRoutes()
      {
          return [
              'members/magazine/(?P<issue>.*)/(?P<folder>.*)/(?P<file>.*)' => ['action' => 'magazineAssets/getMagAsset'],
          ];
      }
  }
  
  ```
  
  Once this was written, I went into the website’s admin panel and enabled the plugin.
  
  The method of interest here is the ‘registerSiteRoutes’ one. What this does is looks for any requests to members/magazine/[issue]/[folder]/[file] and ‘sends’ them to a controller that we haven’t written yet…
  MagazineAssetsController.php
  
  (stored inside the, earlier created, ‘controllers’ folder)
  
  ```
  <?php
  namespace Craft;
  
  class MagazineAssetsController extends BaseController
  {
      public function init()
      {
          if (!craft()->plugins->getPlugin('MagazineAssets'))
              throw new Exception('Couldn\'t find the Magazine Assets Plugin!');
      }
  
      public function actionGetMagAsset(array $variables = [])
      {
          $path = craft()->config->get('environmentVariables')['internalAssetsPath'].'magazines'.DIRECTORY_SEPARATOR.$variables['issue'].DIRECTORY_SEPARATOR.$variables['folder'].DIRECTORY_SEPARATOR.$variables['file'];
  
          if (IOHelper::fileExists($path)) {
                  $content = IOHelper::getFileContents($path);
                  craft()->request->sendFile($path, $content, ['forceDownload' => false]);
          } else {
              die('file not found at: '.$path);
          }
      }
  }
  
  ```
  
  The method of interest here is the ‘actionGetMagAsset’ function which matches the ‘action’ that we are now routing to via the ‘registerSiteRoutes()’ plugin method.
  
  There’s no doubt in my mind that there’s probably some fancy way of writing that path using some baked in CraftCMS methods but I a) wasn’t certain what they are and b) wanted to be very rigid and certain that I definitely had the right path.
  
  The path begins using a config setting (at config/general.php) that I already had thanks to another plugin that I was already using followed by ‘/magazines/[issue]/[folder]/[file]’ (sound familiar?). The config is below but I guess I could have just as easily written it as if I didn’t have the config or didn’t want it…
  
  ```
  <?php
      $path = CRAFT_BASE_PATH."../files/".'magazines'.DIRECTORY_SEPARATOR.$variables['issue'].DIRECTORY_SEPARATOR.$variables['folder'].DIRECTORY_SEPARATOR.$variables['file'];
  ?>
  
  ```
  
  My config/general.php file:
  
  ```
  <?php
      return [
          //etc.
          'environmentVariables' => [
              'internalAssetsPath' => CRAFT_BASE_PATH . "../files/",
          ]
      ];
  ?>
  
  ```
  
  I then used Craft’s built in InputOutput helper class to display the file if it exists and tested via the front-end.
  
  Boom! All working now. Assets are loaded from the files folder, my file structure is like this (simplified obviously):
  
  ```
  craft
      plugins
          magazineassets
              controllers
                  MagazineAssetsController.php
              MagazineAssetsPlugin.php
      templates
              members
                  magazine
                      july-2018
                          mag
                              index.html <--- the magazine that needs the assets
  
  ```
  
  Yeah, okay, but the magazine is still public!
  
  Right you are, it took two seconds to fix that. Into craft/templates/members/magazine/july-2018/mag/index.html I went and simply added…
  
  ```
  {% requireLogin %}
  
  ```
  
  …to the top of the file.
  
  I hope this post helps anyone who faces a similar conundrum.
  
  
  ---
  
  ### Answer by username_1 on 2018-06-28. Score: 2
  
  Please note that the way you have this structured, all of your static resources such as images, JavaScripts, CSS, etc. are going to be run through PHP and Craft before being served up.
  
  This is going to be quite inefficient; static resources such as images, JavaScript, CSS, etc. (anything that *doesn't* have Twig code inside of it) shouldn't be served up from the `templates/` directory.
  
  Rather, they should be in your web root. I would reconsider the way you have your static resources located in `templates/`
  
  > **Comments:**
  > **username_0**: Thanks, I'm aware of that but for this use-case I'm not really too concerned.  
  
  ---
  
  ### Answer by username_2 on 2018-06-27. Score: 2
  
  You'll need to create custom route to access those files. This is just an example of a custom route in my current module, so you need to find a pattern that matches the one you receive from the tool
  
  ```
  $event->rules[] = [
      'pattern' => '/userfiles/<fileType:\w+>/<fileName:\S+>',
      'route'   => 'thaff/profile/get-file'
  ];
  
  ```
  
  And your Controller action could be something like this
  
  ```
  public function actionGetFile($view, $fileName)
  {
      // create an alias to your folderpath $view should be thumb/mobile-ext or mobile depending on the route
      $localPath = Craft::getAlias('@yourFolderAlias') . DIRECTORY_SEPARATOR . $view;
  
      // you can include several checks if the image should be visible for the current user or not...
  
      $resp = Craft::$app->getResponse();
      $resp->headers->set('Content-Type' /*, insert your mime-type here */);
      $resp->format = Response::FORMAT_RAW;
      // check if the file exists
      if ($localPath && file_exists($localPath . $fileName)) {
          // open the file
          $handle = fopen($localPath . $fileName, 'rb');
          // send the result to your response object
          $resp->stream = $handle;
          // send the response and close the file
          $resp->send();
          @fclose($handle);
      }
  }
  
  ```
  
  Note: this is just a little code snipped of my current project and I removed many conditional checks so it's easier to read.
  
  > **Comments:**
  > **username_0**: Thanks for your help, so I need to write a new plugin that contains code based on the above do I? Or can this be placed somewhere else? I can add the custom route via admin can't I? Where would I put the controller?  
  > **username_2**: Those questions don't belong here - please read the documentation. Such things are asked daily and can't be answered in a comment. You'll need a custom plugin or module for it  
  > **username_0**: Thanks for your help I've sorted it out now thanks to the inspiration you gave me. I've written a [blog post](https://www.james-nock.co.uk/blog/tutorials/2018/06/30/craftcms-flipbuilder-plugin/) about the solution to help anyone with stumbles on a similar problem in the future.  
  > **username_1**: Awesome that you wrote a blog post covering your solution!  
  > **username_0**: Cheers, I've just copied and pasted it as the answer too.  
  
  ---
  
  