James Nock

Web Development & Management

Craft CMS Flipbuilder Plugin

Recently, I ran into a problem whereby I needed to incorporate a magazine into a CraftCMS website that had been generated using a neat bit of software by the name of ‘FlipBuilder’. This would be a regular thing, so I was hoping I’d simply be able to drag and drop the outputted files into the templates folder and that it would magically work.

It didn’t.

The problem

By default, anything in templates gets routed through CraftCMS’s templating engine which looks for images in the public folder. This was a problem for me as these magazines would be for logged in users only. If I put them in the public folder then any ‘Tom, Dick or Harry’ would be able to view them.

Hmmmm. I consulted the official CraftCMS slack channel and eventually the CraftCMS StackExchange forum with my problem.

Thankfully, someone popped up with an idea that inspired me into a relatively simple solution, albeit by writing a custom plugin.

The 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.

Work began…

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 getDeveloper()
	{
		return 'James Nock Web Development & Management';
	}

	public function getDeveloperUrl()
	{
		return 'https://www.james-nock.co.uk';
	}

	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 (without the spaces between % and the curly braces)

I hope this post helps anyone who faces a similar conundrum.