Properly Accessing Resource Files in a Mango Plugin
December 18 2008 by
Adam
As your Mango Plugins get more complex, you will find yourself needing to include a JavaScript file on pages, or access images, css, or other resources. With some work, you can programatically find the path to your plugin's folder inside the components/plugins/… folder, but that's a bad idea.
Well, it breaks convention… so it's sort-of bad.
One of the folders in the root of every Mango install is the assets folder. Inside are two more folders: plugins and content. The content folder is the default place that files are stored when you use the File Explorer in your Mango admin to upload files. The plugins folder is intended to house files that a reader will need remote access to.
Convention dictates that when your plugin is activated, it should place any files that will be accessed remotely (ajax, css, images, js, etc) into [mango]/assets/plugins/[plugin name]/. For instance, my Lightbox2 plugin copies a dozen or so css, js, and image files to its asset folder; and then the plugin itself injects HTML onto pages that access the files in this location.
Convention also dictates that when the files are no longer needed — when your plugin is de-activated — you should remove them. Clutter is bad, mmkay?
I wouldn't say the code to accomplish this is necessarily overly complex, but it's certainly going to be frequently repeated, which means it's a good case for some encapsulation.
Here are two methods I've written that will copy the contents of [mango]/components/plugins/user/[plugin name]/assets/ to [mango]/assets/plugins/[plugin name]/, and delete them.
<cffunction name="copyAssets" access="private" output="false" returntype="void"
hint="I'm used during plugin activation to copy files to a public location">
<!--- copy assets to correct public folder --->
<cfset var local = structNew()/>
<cfset local.src = getCurrentTemplatePath() />
<cfset local.src = listAppend(listDeleteAt(local.src, listLen(local.src, "\/"), "\/"), "assets", "/")/>
<cfset local.dest = expandPath('#variables.blogManager.getBlog().getBasePath()#/assets/plugins/#variables.name#')/>
<!--- create the destination folder if it doesn't exist --->
<cfif not directoryExists(local.dest)>
<cfdirectory action="create" directory="#local.dest#"/>
</cfif>
<!--- copy our assets to the root/assets/plugins/RelatedEntries folder so that they are web-accessible --->
<cfdirectory action="list" directory="#local.src#" name="local.assets"/>
<cfloop query="local.assets">
<cffile action="copy" source="#local.assets.directory#/#local.assets.name#"
destination="#local.dest#/#local.assets.name#"/>
</cfloop>
</cffunction>
<cffunction name="clearAssets" access="private" output="false" returntype="void"
hint="I'm used during plugin de-activation to remove public files">
<cfset var local = StructNew()/>
<cfset local.dir = expandPath('#variables.blogManager.getBlog().getBasePath()#/assets/plugins/#variables.name#')/>
<!--- delete assets --->
<cfdirectory action="delete" directory="#local.dir#" recurse="yes"/>
</cffunction>
To use them, simply add <cfset copyAssets() /> to your setup function, and <cfset clearAssets() /> to your
unsetup function. The copyAssets funtion is not recursive, which means you can't use sub-folders. It would be simple enough to
convert to recursive, so I won't include that here.
This methodology also facilitates really keeping your plugin architecture simple. Again, using my Lightbox2 plugin as an example, here are the contents of the plugin folder:

And the contents of the assets folder:

Pretty straight-forward, right?
So now that you're copying your resource files to the appropriate folder for remote access, how do you get that URL?
I'm glad you asked…
Similar to finding out what DBMS the current Mango install is using, you can use Mango's API to find its base path and then build on it. Remember: not all users will install Mango to the root of their site. It could be in /blog, /some/other/folder, or anything else. This practice will make sure your plugin works no matter where Mango is installed.
The method that will return the blog base path is getBasePath() and it belongs to the blog object, so you get its value like so:
variables.blogManager.getBlog().getBasePath()
Remember that variables.blogManager is a variable passed into and saved from the plugin's init function.
If Mango is installed at the root of the site, this method will return "/". If installed at /blog, it will return "/blog/" — it always has the trailing slash.
From there, add "assets/plugins/[plugin name]/" and your files are ready and waiting.
What, you want an example of that too? Ok, here's some code from my Lightbox2 plugin that adds the css and JavaScript files to a page:
<cfset data = arguments.event.outputData />
<cfset data = data & '#chr(13)##chr(10)#<link rel="stylesheet" href="#variables.blogManager.getBlog().getBasePath()#assets/plugins/#variables.name#/lightbox.css" type="text/css" media="screen" />'/>
<cfset data = data & '#chr(13)##chr(10)#<xscript src="#variables.blogManager.getBlog().getBasePath()#assets/plugins/#variables.name#/prototype.js" type="text/javascript"></xscript>'/>
...
<cfset arguments.event.outputData = data />
Note that I had to replace "script" tags with "xscript" tags for them to be displayed here, just drop the X.
Posted in Mango |
4 comments



Dude you rock Adam! This script will make it totally easy to add assets to the correct folder. I think having recursion would be better, but I am sure it's more work to figure out and test. Just having this script will vastly improve the plugins being created. Great work!
Good work Adam - as ever...
Just been integrating into my current plugin project. One point: although you're using the API to determine the installation directory, you're not using that when you uninstall the files...
Thanks for pointing that out, Seb. I've corrected it in the post.
Awesome post, some really good stuff!!