My solution
To keep the ‘index.html’ file of the magazines inside templates as per usual
Store the other assets inside a private directory outside of the public folder
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.