1

Currently I am using "partials" concept in my FW/1 views: these chunks of layout that can be re-used by different views. They are prefixed with underscore for easier maintenance, but unlike the CFWheels these still can be used as implicit views which is not very good.

For example, there's a directory structure:

/views/member/_user.cfm
/views/member/profile.cfm
/views/member/register.cfm

This way actual user form is in the _user.cfm and can be included to the two others using #view('member/_user')#.

What I want is to prevent access to the pages like member._user on the website.

One solution is to create the _user method in member.cfc controller and redirect user somewhere. But creating such methods for each partial is kinda inefficient approach.

Alternative to this would be parsing the rc.action in before and checking if there's underscore in the prefix, but I'm not sure this is clean solution.

Is it possible to disable the action (throw 404) if there's no corresponding method in controller? Or maybe there are some framework events/flags which would allow me to handle "missing method" situation in before?

Thank you.

Sergey Galashyn
  • 6,946
  • 2
  • 19
  • 39

4 Answers4

2

You can create a method in a controller that checks rc.action to see if the item part starts with a _ and redirects elsewhere (or throws an error, or whatever you want to do). Then call this method using controller() function in your setupRequest() method in Application.cfc.

For example, I have a controllers/security.cfc controller with checkItem() method as follows:

function checkItem( rc ) {
    //check if restricted item hss been requested and redirect to main.default
    if ( left(variables.fw.getItem(), 1) eq "_" ) {
        variables.fw.redirect('main');
    }
}

And call it in setupRequest() in Application.cfc:

function setupRequest() {
    //controller( 'security.authorize' );
    controller( 'security.checkItem' );
}

This way it is automatically called on every request - no need to define a separate method for each _item in controllers.

azawaza
  • 3,065
  • 1
  • 17
  • 20
  • That's essentially the same as I've described in paragraph starting with `Alternative to this ...`, but thanks any way. – Sergey Galashyn May 31 '12 at 15:25
  • using before() method like you mentioned, while similar, will require you to repeat same code in every before() method in every controller that has restricted items; running it in setupRequest(), on the other hand, you only have ONE place the code is in (security.cfc in my example code). – azawaza May 31 '12 at 15:38
  • Well, actually you can have before/after methods in Application.cfc as well. – Sergey Galashyn May 31 '12 at 18:50
  • I think this approach is better than others because it is using built-in framework tools, easy to implement and does not require external setup. Let's consider this correct answer. – Sergey Galashyn Jun 01 '12 at 16:48
2

The easiest way to do this is to put them in a folder that is not in the web root. Therefore they are not web accessible. Then use a ColdFusion mapping to make them available to ColdFusion.

Make sense?

baynezy
  • 6,493
  • 10
  • 48
  • 73
  • Maybe it would in some cases. But it'd add one more layer of complexity with repository structure and workflow and IDE (second project, effectively) for me personally. – Sergey Galashyn Jun 01 '12 at 06:18
  • @Sergii this is the established way of achieving what you are asking for. You shouldn't have to create a new project. You can just have folder structure that supports it. i.e. /www/ (this is your webroot) /views/ (this is where your views go) then you promote it as a mapping. The mod_rewrite route will work but it is a bit of a sledgehammer approach to something that can be solved by CF itself – baynezy Jun 01 '12 at 10:15
  • I'm sorry, but it is not. We are talking here about `FW/1`, right?. Project structure is defined by framework -- it is convention-based. One can override it (of course), but it doesn't look as wise choice for long term maintenance. For example, if some other applications follow standard structure it'd be confusing. – Sergey Galashyn Jun 01 '12 at 12:05
  • I know that. It only cares where ColdFusion thinks it is though, so creating a mapping tells it the answer. It is obviously your choice, but I think you are just making it hard for yourself. – baynezy Jun 01 '12 at 15:12
  • You know, I think your answer was incorrect from very beginning. Let's say I've created the mapping for views which actually lay outside of the webroot. But problem is that application thinks that they are regular views and all the implicit actions going to work as usually, including partials. I `need` them to be accessible by application to include in the regular views. – Sergey Galashyn Jun 01 '12 at 16:44
  • But that is what a mapping does. Essentially ColdFusion will see them as if they are where you want then but your web server won't. – baynezy Jun 01 '12 at 20:37
  • Right, but problem not with direct access to templates (rewriting wont allow this any way), but with explicit actions possible with only views. – Sergey Galashyn Jun 02 '12 at 07:18
1

You could use mod_rewrite to detect URLs containing /_ and block/redirect as appropriate.

For example:

RewriteEngine On

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} ^/\w+/_
RewriteRule ^.* /error/404 [L]


The first RewriteCond makes sure that the file does not exist - so if you have a real file /css/_default.css any requests for that will fail this condition and not redirect.

The second RewriteCond accepts any alphanumeric for the first segment, then continues if there is a _ at the start of the second segment. (It's not necessary to match the entire URI, just the start is enough.)

Finally, the RewriteRule is applied only if both conditions were true, but matches all URLs, and performs a server-side redirect to /error/404 - you can update that part as appropriate. (The [L] flag tells mod_rewrite not to attempt any further rewrites.)

Peter Boughton
  • 110,170
  • 32
  • 120
  • 176
0

OK, since there's no answer which I've expected (which is fine), here's one more approach which I am using for email templates already.

In the /views/emails/ all the views are email templates which do not have actions, but invoked only like this:

local.body = variables.fw.view("emails/registration_confirmation", local.attrs);
local.template = variables.fw.view("emails/default", {body = local.body});

Where registration_confirmation.cfm is template for specific email body and default.cfm (could call it differently) is email template itself.

To prevent the problem I've described in question controller emails.cfc looks like this:

component extends="components.helpers.controller" {

    public void function before(required struct rc) {
        variables.fw.redirect("user.forbidden");
    }

}

I guess it would be possible to create one more such controller partials.cfc and keep all views in /views/partials/.

But this is my first real-life FW/1 project and I came with idea too late to refactor so many views (1), plus I think having partials in same directory is worse from maintenance point of view, than having them where it makes sense for them (2).

These are two reasons why I've asked this question.

Sergey Galashyn
  • 6,946
  • 2
  • 19
  • 39