Topic: Help with porting a php site

I'm doing a project where I have to port an existing php site that consists on an administrator backend kept neatly in a folder and then all the pages of the site are strewn about in the site root.

www/
    accessories.php
    admin/
        login.php
        users.php
        adduser.php
        deluser.php
        ...
    easter.php
    index.php
    meetings.php
    oct11.php
    ...

There are a lot more files then I showed there, I put the ... to save space.  I'm not really concerned about how to port the admin backend as it is a rather straightforward process, what I'm confused about is how to handle all the .php files in the site root.  See, the client has a few requirements about how all the pages are supposed to work:

1. All activity on the site has to be tracked e.g when a user hits the easter.php page, the username and the page they hit needs to be logged somewhere.
2. All the files have to be kept physically on the site (not have their content dynamically drawn in from a database) so that they can be edited by people using ftp & dreamweaver to make quick content changes on pages.
3. The client would prefer that they be kept in the site root or at least as subdirectory in the site root (in rails terms meaning that they should be put in app_name/public.
4. Some pages do have dynamic areas where the content changes based on who is currently logged in and what permissions that user has on the site.  So this requires the whole rails stack to be loaded when these pages are displayed so that there is access to the models in the database.

So with these requirements in mind, the first solution I came up with was to take all the .php files in the site root and move them to rails_app_name/public.  Rename them all to .rhtml files and put a bit of ruby code at the top of each file that logs the current user and the page name to the database.  Then make a "pages" controller that defines a method_missing function:

def method_missing(call, *args)
  # match the requested foo.rhtml and optionally any of the subdirectories
  # it is stored in from the request and put it in $1
  request.env['REQUEST_URI'] =~ /^\/pages(\/[\w\/]+)$/
  # render the matching .rhtml file from public/
  render :file => '/path/to/rails_app_name/public' + $1
end

To implement this solution I'm going to have to change every link in every file to add '/pages' to the front to trigger the pages controller. I'm not too concerned about the amount of effort this will take because it's just a regular expression applied to all the files.  But what I am concerned about is if the client goes into one of the files and adds a new link, they are going to have to remember to put '/pages' in front of the path because otherwise it will just trigger a 404.

Does anyone have any ideas about how to avoid adding '/pages' to all the current and future links on the site?  I have a feeling the solution lies in some fancy routing/url_rewrite rules, but I can't figure out how to do it.  Or do you have any ideas about an easier way to solve this problem?

thanks for reading!

"An egoless acceptance of stuckness is a key to an understanding of all Quality, in mechanical work as in other endeavors."
Zen and the Art of Motorcycle Maintenance

Re: Help with porting a php site

Use a before filter to track users. In app_name/app/controllers/application.rb, add:

  before_filter :track_users

  def track_users
    ... # code goes here
  end


All controllers inherits from ApplicationController so track_users() will run on every request. This way, you can avoid repeating the same code on multiple pages.



With static files, just leave them in app_name/public
For instance, your web server should serve app_name/public/images/rails.png when www.example.com/images/rails.png is requested.

With dynamic pages, let Rails handle the routing. Take a look at app_name/config/routes.rb
Here, you can specify where Rails should route requests. The first rule that matches win so you'll want to put your catch all rules at the bottom of the file.

map.connect '',              :controller => 'pages', :action => 'index'
map.connect 'admin/:action', :controller => 'admin'
map.connect ':action',       :controller => 'pages'

www.example.com/admin/deluser will match the second rule first so now Rails knows to process the request with the admin controller and deluser action

www.example.com/meetings will match only the last rule. The meetings action of the pages controller will run in this case.

Routes have lots of variations and I encourage you to look into it but this should get you started.

Vincent Woo Ruby on Rails Blog

Re: Help with porting a php site

Thanks for the suggestion of the before_filter, that makes a lot more sense.  But as I mentioned before all the pages need to be in the normal directory for static pages 'rails_app_name/public', but some of them are not actually static.  If I had the option to make those pages with some dynamic content part of a controller/view setup I would, but the client needs those pages left in the same location as all the rest of the static pages so that they can all be edited using ftp and dreamweaver.

And it's my understanding that the before_filter would never be run for static pages, so then a user going to a static page would not be logged, which goes against what I need to happen.

thanks for the suggestion.

"An egoless acceptance of stuckness is a key to an understanding of all Quality, in mechanical work as in other endeavors."
Zen and the Art of Motorcycle Maintenance

Re: Help with porting a php site

I would create the files in the public folder, but write them as if they were going mvc, then I would create symbolic links in the view and controller folders to the correct files in the public folder. then I would use the rails mapping/routing that vwoo was talking about. I am not sure if that would work, but it is definately worth a try.

"They say, Evil prevails when good men do nothing. What they should have said was: Evil Prevails."

http://www.vrazzle.com

Re: Help with porting a php site

hmm, another good solution, but it doesn't have the permanence I'm looking for.  If the client makes a new file they are going to have to create a symbolic link as well, and know the proper place to put it, let alone even what a symbolic link is.

thanks or the suggestion

"An egoless acceptance of stuckness is a key to an understanding of all Quality, in mechanical work as in other endeavors."
Zen and the Art of Motorcycle Maintenance

Re: Help with porting a php site

Well, I have improved on my original solution, mostly because my original solution had one huge security hole in it: if you drop the '/page' from the url, thus not triggering the controller, the actual file will be served up no matter if the user is logged in or not.  So I learned a bit about mod_rewrite and solved the problem with that. Let me preface this by saying that I think this solution only works if you are using apache, cause otherwise how would the rewrite rules be evaluated in public/.htaccess.  Anyways here's what I did

First I modified the .htaccess rewrite rules from the default:
[code=original .htaccess]
RewriteRule ^$ index.html [QSA]
RewriteRule ^([^.]+)$ $1.html [QSA]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ dispatch.fcgi [QSA,L]
[/code]
to
[code=new .htaccess]
RewriteRule ^$ index.html [QSA]
RewriteRule ^([^.]+)$ $1.html [QSA]
RewriteCond %{REQUEST_FILENAME} \.html$ [NC,OR]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ dispatch.fcgi [QSA,L]
[/code]

What the added line 3 does is checks the request filename to see if it ends with ".html" and if it does then it passes the request to rails. The [NC,OR] mean do case-insensitive matching on ".html", and if it doesn't match then it evaluates the next rule, thus OR.

So then I added this line to my routes:
[code=routes.rb]map.connect ':file', :controller => 'page', :action => 'show', :file => /[\w\d\.]+/[/code]
Beware that this route will match almost anything and pass it to the PageController::show, so make sure that it is at the very bottom of your routes.rb file, and that you have something in the show action that checks to see if the file actually exists and shows a 404 error if it doesn't.

Hope this helps anyone who needs to serve up a bunch of html file with mostly static content in them.  In additional improvements/criticisms are welcome.

"An egoless acceptance of stuckness is a key to an understanding of all Quality, in mechanical work as in other endeavors."
Zen and the Art of Motorcycle Maintenance