Dynamic BlogCFC Instances

This may be better off at BlogCFC.com, but I've had a few requests for this lately so I thought I'd post it here.

One of the undocumented features of BlogCFC is the ability to pass in configuration information to the CFC. Normally you tell the CFC to load data from the INI file. However, you may want to create a completely dynamic setup. For example, maybe you want x.foo.com and y.foo.com to both run BlogCFC and both use one physical folder.

This is rather simple - but you have to pass a structure that contains all the keys that would normally be in the INI file. Here is an example I'm using on a real site now (unreleased to the public though). First - here is the code that sniffs the server name to grab the name of the blog, in this case it is based on the first part of the URL. If we were using x.foo.com and y.foo.com, the blog names would be X and Y.

<cfset blogname = listFirst(cgi.server_name,".")> <cfapplication name="_blog_#blogname#" sessionManagement="true" loginStorage="session">

Next I create a structure with all values that exist in a normal INI file:

<cfset instance = structNew()> <cfset instance.dsn = dsn> <cfset instance.owneremail="blog@blog.com"> <cfset instance.blogurl = "http://#cgi.server_name#/blog/index.cfm"> <cfset instance.blogtitle = "#blogname# Blog"> <cfset instance.blogdescription = "#blognme# Blog"> <cfset instance.blogDBType="MYSQL"> <cfset instance.locale="en_US"> <cfset instance.users = ""> <cfset instance.commentsFrom = ""> <cfset instance.mailServer = ""> <cfset instance.mailUsername = ""> <cfset instance.mailPassword = ""> <cfset instance.pingurls = ""> <cfset instance.offset = "0"> <cfset instance.allowtrackbacks = false> <cfset instance.trackbackspamlist="lots of bad words here"> <cfset instance.blogkeywords = ""> <cfset instance.ipblocklist = ""> <cfset instance.allowgravatars = true> <cfset instance.maxentries = "10"> <cfset instance.usecaptcha = false>

Once you have the structure populated, you then pass it to the CFC:

<cfset application.blog = createObject("component","org.camden.blog.blog").init(blogname,instance)>

That's it. Obviously you may need to tweak your instance settings. For example, you may have special logic for the users value. Anyway, let me know if this doesn't make sense.

Archived Comments

Comment 1 by Edd King posted on 11/27/2006 at 10:03 PM

How would you keep the blog name persistent if you don't have multiple sub domains?

Comment 2 by Raymond Camden posted on 11/27/2006 at 10:05 PM

The use of cgi.server_name was arbitrary. You can base it on anything really.

Comment 3 by Nick Tong posted on 11/27/2006 at 10:24 PM

How about adding a virtual directory if you have permissions?

Comment 4 by Raymond Camden posted on 11/28/2006 at 4:01 AM

Nick, it shouldn't matter. The idea is - one code base - and SOME kind of logic to determine current blog. You could even say, if it is morning, load the morning blog. It is up to you.

Comment 5 by Edd King posted on 11/28/2006 at 4:10 AM

But how do you keep the blogname constsiant?

Blog A has
http://blog.com
<cfset blogname="blogA">

blogb has
http://blog.com
<cfset blogname="blogB">

how do it know when you're in blog B as you can't use the application or session scope?

Am i missing the point?

Comment 6 by Raymond Camden posted on 11/28/2006 at 4:12 AM

I think you may be. :) THe point is that YOU determine the blog name and settings. Your code does it all and the passes it to the blog and also makes the cfapplication tag dynamic as well.

Comment 7 by Michael Marrotte posted on 3/9/2007 at 8:06 PM

Raymond,

It looks like the latest version (5.6.001) of BlogCFC includes the dynamic application and blog name logic, but not the instance vars. Would you mind commenting on this and how and if the latest version (5.6.001) of BlogCFC would change your approach, here?

Comment 8 by Raymond Camden posted on 3/9/2007 at 8:17 PM

I'm not quite sure what you mean. I don't use the feature itself in the code you unzip. It is supported - but not used. I certainly did not remove the feature though. It is still there.

Comment 9 by Michael Marrotte posted on 3/9/2007 at 9:20 PM

I was referring to the following in the Application.cfm:

<!---
The prefix is now dynamic in case 2 people want to run blog.cfc on the same machine. Normally they
would run both blogs with the same org, and use different names, but on an ISP that may not be possible.
So I base part of the application name on the file path.

Name can only be 64 max. So we will take right most part.
--->

But, now that I look at again it looks like it works with multiple installs of the application.

Where would you suggest injecting the code for the Dynamic Instances, say in the root Application.cfm?

Additionally, I was thinking of making an instance table keyed on some server value and running a query to grab all the instance vars after sniffing out the server value, and then creating a new instance structure and setting all the vars like you seggest, but from the query values.

Any comments are appreciated.

Comment 10 by Raymond Camden posted on 3/10/2007 at 1:14 AM

Yes, I do it in Application.cfm. Your idea seems ok. THis is why I built in the support - so you could make it based on anything.

Comment 11 by Michael Marrotte posted on 3/12/2007 at 12:10 AM

Raymond:

Does this "Dynamic Instances" approach break your administrator settings tool?

I'm thinking I'll need to re-write or override setProperty in Blog.cfc, unless I keep all the instance definitions in blog.ini.cfm.

I'm thinking I'll override setProperty to update my instance table in the DB instead of blog.ini.cfm.

How's you administrator settings tool working in your "real site"

Any comments are greatly appreciated.

Thanks,

--Mike M.

Comment 12 by Raymond Camden posted on 3/12/2007 at 8:09 AM

I recently added a setting that would hide the settings page from the admin. You would want to use this for multiserver blogs so that folks can't edit the settings.

Comment 13 by Matthew posted on 4/18/2007 at 1:22 PM

Hi Ray,

I have implemeted this functionality into my site and it seems to be now working to a point. I have downloaded and installed the latest blog version, added in the instance definitions to the Application.cfm file and some some tweaking on the settings to get it to load.
So i have one folder with the client dir in it, within this folder i have placed the org dir. I have one db schema (Oracle).

I tested this by firstly hardcoding the blogName var, this worked fine, an instance of the blog was created, i then logged into the admin this also worked fine and was able to add an entry and a category, all hunky dory.

The next step is to make the blog selection dynamic, so i thought the easy way is to create a blogselection.cfm page which passes a variable in blogName into the app via a url variable.

This seemed to work until i got to the admin, i passed the blogName var again to load the admin page, and tried to click onto add a new entry and it came back with blogName undefined.

OK i know exactly what it is doing here, its looking for the blogName variable again as the Application.cfm file has been hit again on trying to access the add entry page.

How can i get around this, everytime a page is called in the admin its looking for the blogName var, first thoughts were perhaps trying to store it in a session variable. Is this the best way to do this or can you suggestsomething else?

Any help appreciated.

Matt

Comment 14 by Matthew posted on 4/18/2007 at 2:17 PM

Hi Ray,

Its amazing what you can do with a little persistance.

I have managed to solve my above issue for my requirement.

I decided to use a session variable to hold the blogName and basically created a page with a link to blog1 and blog2.
Depending on what link was clicked, the session variable stores the blogname and so creates the instance for that blog.

OK it may not be the best solution but it seems to be working for what i need.

thankyou for the ideas and the application.

Matt

Comment 15 by Raymond Camden posted on 4/18/2007 at 2:22 PM

Glad you got it working. I was sleeping so thats my excuse for not helping earlier. :0

Comment 16 by James posted on 8/29/2007 at 12:45 AM

I'm trying to set up the dynamic instances in version 5.8.001. I created a new file called index_new.cfm:
(Please Note the DB information has been XXX'd out)
<!---Sets the Blog to the Server Name--->
<cfset blogname = "Development Blog 2">
<cfapplication name="_blog_#blogname#" sessionManagement="true" loginStorage="session">

<!---INI Structure---><cfset instance = structNew()>
<cfset instance.dsn = "xxxxxxxxx">
<cfset instance.username = "xxxxxxxx">
<cfset instance.password = "xxxxxxxx">
<cfset instance.owneremail="j.harvey@swidigital.com">
<cfset instance.blogurl = "http://localhost/blogcfc/index_new.cfm">
<cfset instance.blogtitle = "#blogname# Blog">
<cfset instance.blogdescription = "#blogname# Blog">
<cfset instance.blogDBType="MSSQL">
<cfset instance.locale="en_US">
<cfset instance.users = "">
<cfset instance.commentsFrom = "">
<cfset instance.mailServer = "xxxxxxxxxxx">
<cfset instance.mailUsername = "j.harvey">
<cfset instance.mailPassword = "jch1701">
<cfset instance.pingurls = "">
<cfset instance.offset = "0">
<cfset instance.allowtrackbacks = true> <!---True or False--->
<cfset instance.trackbackspamlist="lots of bad words here">
<cfset instance.blogkeywords = "">
<cfset instance.ipblocklist = "">
<cfset instance.allowgravatars = true><!---True or False--->
<cfset instance.maxentries = "10">
<cfset instance.usecaptcha = false><!---True or False--->

<!---Pass the Populated Structure to the CFC--->
<cfset application.blog = createObject("component","org.camden.blog.blog").init(blogname,instance)>

When I go to run the page, it comes up blank. Do I need to load these variables in the index.cfm?

Comment 17 by Raymond Camden posted on 8/29/2007 at 1:30 AM

Well, you should be using index.cfm, not index_new.cfm. Switch to that and let me know.

Comment 18 by James posted on 8/30/2007 at 10:56 PM

I tried doing it on the Index.cfm Page, and i just get the default blog, not the second one I'm trying to invoke.

Comment 19 by Raymond Camden posted on 8/30/2007 at 11:24 PM

Right - but the idea is to use ONE file base, not one file per blog. You need to keep things in index.cfm as it is an important file to the code base.

Comment 20 by James posted on 9/7/2007 at 1:19 AM

Ok, I've tried placing the Multiple Instances code in my application.cfm file and running the website. Now I get the following error:

Context validation error for tag cfif.
The start tag must have a matching end tag. An explicit end tag can be provided by adding </cfif>. If the body of the tag is empty you can use the shortcut <cfif .../>.

The error occurred in C:\Inetpub\wwwroot\blog_dev\blogcfc\Application.cfm: line 64

62 :
63 : <!--- By default we cache a lot of information. Allow reinit=1 in the URL to restart cache. --->
64 : <cfif not isDefined("application.init") or isDefined("url.reinit")>
65 :
66 : <!--- load and init blog --->

The code i did in my application.cfm looks like this:
<cfsetting enablecfoutputonly="true" showdebugoutput="false">
<!---
Name : Application.cfm
Author : Raymond Camden
Created : Some time ago
Last Updated : April 13, 2007
History : Reset history for version 5.7
: Added comments, and Scott P's pod manager cfc (rkc 4/13/07)
Purpose : Blog application page
--->

<cfset setEncoding("form","utf-8")>
<cfset setEncoding("url","utf-8")>

<!--- Edit this line if you are not using a default blog --->
<!---<cfset blogname = "Default">--->

<!---
The prefix is now dynamic in case 2 people want to run blog.cfc on the same machine. Normally they
would run both blogs with the same org, and use different names, but on an ISP that may not be possible.
So I base part of the application name on the file path.

Name can only be 64 max. So we will take right most part.
--->
<!---<cfset prefix = hash(getCurrentTemplatePath())>
<cfset prefix = reReplace(prefix, "[^a-zA-Z]","","all")>
<cfset prefix = right(prefix, 64 - len("_blog_#blogname#"))>
<cfapplication name="#prefix#_blog_#blogname#" sessionManagement="true" loginStorage="session">--->

<!---Multiple Instances Code Invokation--->
<cfset blogname = listFirst(cgi.server_name,".")>
<cfapplication name="_blog_#blogname#" sessionManagement="true" loginStorage="session">
<cfset instance = structNew()>
<cfset instance.dsn = BlogDB>
<cfset instance.owneremail="j.harvey@swidigital.com">
<cfset instance.blogurl = "http://#cgi.server_name#/blog_dev/blogcfc/index.cfm">
<cfset instance.blogtitle = "#blogname# Blog">
<cfset instance.blogdescription = "#blognme# Blog">
<cfset instance.blogDBType="MSSQL">
<cfset instance.locale="en_US">
<cfset instance.users = "admin">
<cfset instance.commentsFrom = "">
<cfset instance.mailServer = "xxxxxxxxxxxx">
<cfset instance.mailUsername = "xxxxxx">
<cfset instance.mailPassword = "xxxxxx">
<cfset instance.pingurls = "">
<cfset instance.offset = "0">
<cfset instance.allowtrackbacks = false>
<cfset instance.trackbackspamlist="lots of bad words here">
<cfset instance.blogkeywords = "">
<cfset instance.ipblocklist = "">
<cfset instance.allowgravatars = true>
<cfset instance.maxentries = "10">
<cfset instance.usecaptcha = false>
<cfset application.blog = createObject("component","org.camden.blog.blog").init(blogname,instance)>
<!---End of the Multiple Instance--->

<!--- Our exception template. --->
<cferror type="exception" template="error.cfm">

<cfinclude template="includes/udf.cfm">

<!--- By default we cache a lot of information. Allow reinit=1 in the URL to restart cache. --->
<cfif not isDefined("application.init") or isDefined("url.reinit")>

<!--- load and init blog --->
<cfset application.blog = createObject("component","org.camden.blog.blog").init(blogname)>

<!--- Root folder for uploaded images, used under images folder --->
<cfset application.imageroot = application.blog.getProperty("imageroot")>

<!--- locale related --->
<cfset application.resourceBundle = createObject("component","org.hastings.locale.resourcebundle")>

<!--- Path may be different if admin. --->
<cfif findNoCase("admin/", cgi.script_name) or findNoCase("xmlrpc/", cgi.script_name)>
<cfset theFile = expandPath("../includes/main")>
<cfset lylaFile = "../includes/captcha.xml">
<cfset slideshowdir = expandPath("../images/slideshows/" & application.imageroot)>
<cfelse>
<cfset theFile = expandPath("./includes/main")>
<cfset lylaFile = "./includes/captcha.xml">
<cfset slideshowdir = expandPath("./images/slideshows/" & application.imageroot)>
</cfif>

<cfset application.resourceBundle.loadResourceBundle(theFile, application.blog.getProperty("locale"))>
<cfset application.resourceBundleData = application.resourceBundle.getResourceBundleData()>
<cfset application.localeutils = createObject("component","org.hastings.locale.utils")>
<cfset application.localeutils.loadLocale(application.blog.getProperty("locale"))>

<!--- load slideshow --->
<cfset application.slideshow = createObject("component", "org.camden.blog.slideshow").init(slideshowdir)>

<!--- Use Captcha? --->
<cfset application.usecaptcha = application.blog.getProperty("usecaptcha")>

<cfif application.usecaptcha>
<cfset application.captcha = createObject("component","org.captcha.captchaService").init(configFile="#lylaFile#") />
<cfset application.captcha.setup() />
</cfif>

<!--- clear scopecache --->
<cfmodule template="tags/scopecache.cfm" scope="application" clearall="true">

<cfset majorVersion = listFirst(server.coldfusion.productversion)>
<cfset minorVersion = listGetAt(server.coldfusion.productversion,2)>
<cfset cfversion = majorVersion & "." & minorVersion>

<cfset application.isColdFusionMX7 = server.coldfusion.productname is "ColdFusion Server" and cfversion gte 7>

<!--- Used in various places --->
<cfset application.rootURL = application.blog.getProperty("blogURL")>
<!--- per documentation - rooturl should be http://www.foo.com/somethin... --->
<cfset application.rootURL = reReplace(application.rootURL, "(.*)/index.cfm", "\1")>

<!--- used for cache purposes is 60 minutes --->
<cfset application.timeout = 60*60>

<!--- how many entries? --->
<cfset application.maxEntries = application.blog.getProperty("maxentries")>

<!--- TBs allowed? --->
<cfset application.trackbacksAllowed = application.blog.getProperty("allowtrackbacks")>

<!--- Gravatars allowed? --->
<cfset application.gravatarsAllowed = application.blog.getProperty("allowgravatars")>

<!--- Load the Utils CFC --->
<cfset application.utils = createObject("component", "org.camden.blog.utils")>

<!--- Load the Page CFC --->
<cfset application.page = createObject("component", "org.camden.blog.page").init(dsn=application.blog.getProperty("dsn"), username=application.blog.getProperty("username"), password=application.blog.getProperty("password"),blog=blogname)>

<!--- Load the TB CFC --->
<cfset application.textblock = createObject("component", "org.camden.blog.textblock").init(dsn=application.blog.getProperty("dsn"), username=application.blog.getProperty("username"), password=application.blog.getProperty("password"),blog=blogname)>

<!--- Do we have comment moderation? --->
<cfset application.commentmoderation = application.blog.getProperty("moderate")>

<!--- Do we allow file browsing in the admin? --->
<cfset application.filebrowse = application.blog.getProperty("filebrowse")>

<!--- Do we allow settings in the admin? --->
<cfset application.settings = application.blog.getProperty("settings")>

<!--- load pod --->
<cfset application.pod = createObject("component", "org.camden.blog.pods")>

What am I doing wrong? I haven't been able to get the multiple instances to run for anyhting.

CF:7.02
Windows IIS5.4 (win XP Pro)

Comment 21 by Raymond Camden posted on 9/7/2007 at 1:21 AM

Well the error is pretty clear. You opened a CFIF someplace that you didn't close. Just start tracking through your code to see which one you forgot.

Comment 22 by James posted on 9/7/2007 at 4:48 PM

That's the thing ray, There's no <cfif> tag that I've modified, it only occurs when I add the (instance) codes above to the Application.cfm file...

If I remove them, It works ok.

Comment 23 by Raymond Camden posted on 9/7/2007 at 4:51 PM

If you pasted the entire code above -then that isn't the complete file. There is a problem with some blogcfc files and Dreamweaver. Are you using Dreamweaver to edit the files? If so - it is cropping the files because it doesn't like a character in the file. Redownload - and edit with Notepad.

Comment 24 by James posted on 9/7/2007 at 5:55 PM

I only pasted the code upto where I had added the multiple-instance code.

I have the complete Application.cfm, and each and everythime, without fail, when I add the instance code to the application.cfm (client/application.cfm) I get the CFif error, and to get the blog back ot funcitonal status I have to extract the original application.cfm and reupload it unmodified.

Comment 25 by Raymond Camden posted on 9/7/2007 at 6:00 PM

That is truly odd. So you scanned the file, and you see no missing </cfif>s? Maybe you can send me your file via email.

Comment 26 by James posted on 9/7/2007 at 6:21 PM

On On it's way

Comment 27 by Stephen posted on 4/15/2009 at 10:17 AM

Hey Ray, quick question.

I recently deployed BlogCFC using multiple instances with subdomains and it works great!

My question is, how can I go about assigning a custom enclosure directory for each instance?

I've got custom image root directories working, but it seems all the enclosures go to the same place. Thanks!

Comment 28 by Raymond Camden posted on 4/15/2009 at 12:55 PM

Given that your subdomains are your 'primary keys', I'd just set the enclosure dir to "enclosures/" + subdomain. Be sure to make the dir if it doesn't exist.

Comment 29 by Stephen posted on 4/15/2009 at 10:27 PM

Ray, where do I set the enclosures dir? I don't see a setting for it, did I miss something?

Comment 30 by Raymond Camden posted on 4/15/2009 at 10:28 PM

You don't set it - the code does. Look in Application.cfm. You would modify it there.

Comment 31 by Stephen posted on 4/15/2009 at 11:15 PM

Sorry if I'm dense, Ray, but I had looked through Application.cfm and did not see anything to customize the "enclosures" directory, would you give me a line # hint? :)

Comment 32 by Raymond Camden posted on 4/17/2009 at 5:44 AM

I was wrong. It is actually defined in

/admin/entry.cfm

when you try to save a file:

<cfif isDefined("form.enclosure") and len(trim(form.enclosure))>
<cfset destination = expandPath("../enclosures")>
<!--- first off, potentially make the folder --->
<cfif not directoryExists(destination)>
<cfdirectory action="create" directory="#destination#">
</cfif>

I should move that in v6. Anyway, you would need to fix that, and fix the links in index.cfm:

Line 145: <cfif len(enclosure)><cfoutput><img src="#application.rooturl#/images/disk.png" align="middle" title="#rb("download")#" height="16" width="16"> <a href="#application.rooturl#/enclosures/#urlEncodedFormat(getFileFromPath(enclosure))#">#rb("download")#</a> | </cfoutput></cfif>

And possibly in RSS as well. Yep, blog.cfc:

<enclosure url="#xmlFormat("#rootURL#/enclosures/#getFileFromPath(enclosure)#")#" length="#filesize#" type="#mimetype#"/>

Sorry!

Comment 33 by Stephen posted on 4/19/2009 at 3:50 AM

Ray, thank you very much for pointing those out, it was very helpful! Now I've got it pretty much working as desired, glad to hear you're going to change that for v6!

On another note, a question about the <more/> and <textblock> tags... When using a RTE, such as fckeditor, these tags stop working, the code is being rendered out literally as text, what's the best way to handle these in RTE? thx

Comment 34 by Raymond Camden posted on 4/19/2009 at 5:55 PM

Sorry, but this is exactly why I hate RTEs. ;) If you search the BlogCFC forums, you will find other people working with RTEs. I know some folks have got it working.

Comment 35 by Stephen posted on 4/20/2009 at 9:48 PM

I hear ya, Ray... I'm like you, back in my day we coded our own html in snowstorms uphill both ways...