Lesson 13
Admin Pages
Goals
In this lesson, we implement the actual administrative dashboard using improvements we make to the mini-CMS we are building. We finish up with an improvement to our navigation code so we can build the tabbed interface more dynamically.
Setup
We begin with the code with which we ended Lesson 12. These zip files contain the beginning and ending states of the code:
- Learning Rails example app code as of the end of Lesson 12
- Learning Rails example app code as of the end of Lesson 13
Admin Pages
Of course, it is tiresome to keep typing the URLs manually to get to our administrative pages. Let’s create an admin dashboard page that connects us to these sub-pages. Let’s also make the admin page easy to reach when we are appropriately logged in.
Make the Admin page attribute
If we are going to use our baby-CMS to implement this page, what is missing? We need to know when a page is an admin type page so we can protect it properly.
Make a migration to add an admin attribute to pages, and then set some default values in the migration to keep things tidy.
class AddAdminPageAttribute < ActiveRecord::Migration
def self.up
add_column :pages, :admin, :boolean
@pages = Page.find(:all)
@pages.each do |page|
page.update_attribute(:admin, false)
end
end
def self.down
remove_column :pages, :admin
end
end
Now, we need to update the Page Admin html so we can see/edit the new admin attribute:
First, index.html.erb gets a couple of new table entries:
<h1>Listing pages</h1>
<table>
<tr>
<th>Name</th>
<th>Title</th>
<th>Body</th>
<th>Admin?</th>
</tr>
<% for page in @pages %>
<tr>
<td><%=h page.name %></td>
<td><%=h page.title %></td>
<td><%=h page.body %></td>
<td><%= page.admin? ? "TRUE" : "FALSE" %></td>
<td><%= link_to 'Show', page %></td>
<td><%= link_to 'Edit', edit_page_path(page) %></td>
<td><%= link_to 'Destroy', page, :confirm => 'Are you sure?', :method => :delete %></td>
</tr>
<% end %>
</table>
<br />
<%= link_to 'New page', new_page_path %>
Then add a snippet to show.html.erb to see the Admin value:
<p> <b>Admin?</b><br /> <%= @page.admin? ? "TRUE" : "FALSE" %> </p>
Update both edit.html.erb and new.html.erb with a snippet to use a check box for the state of the admin attribute (put this in before the submit):
<p> <b>Admin?</b><br /> <%= f.check_box :admin %> </p>
We should be all set. Check it out and create a page with the admin bit set. Can you see it when you are logged in? What about when you aren’t? Oops!
Update the viewer controller to handle special pages
Of course, we don’t filter in anyway for admin pages, so anyone can see a page marked with the Admin bit. Let’s fix that by protecting admin viewable pages by updating the viewer controller:
def show @page = Page.find_by_name(params[:name]) login_required if @page.admin? end
We are leveraging the login_required function provided to us by the restful_authentication plugin. If the requested page has the admin value set to true, we’ll check to see if the user is logged in with login_required.
Add an admin dashboard page in the DB
Finally, we can create a page using the mini-CMS’ Page Admin. Make one now and we’ll call it “admin”. Make the page contents as follows:
<a href="/pages">"Page Admin"</a> <br> <a href="/users">"User Admin"</a>
Test it. Check it logged in and logged out. It works!
Make layout dynamic
It is getting really tiresome to keep updating our simple navigator, since we need to access the admin pages regularly, let’s take this opportunity to make a new tab appear for each page automatically.
Update the application layout to support rendering tabs dynamically. Here is the new navbar div:
<div id='navbar'> <ul> <% @tabs.each do |page| -%> <li><%= link_to page.title, view_page_path(page.name) %></li> <% end -%> <li><% if logged_in? %> <%= link_to "Log Out", logout_path %> <% else %> <%= link_to "Log In", login_path %> <% end %> </li> </ul> <div>
We need to add a before_filter to application.rb to load up pages into that @tabs variable:
before_filter :get_pages_for_tabs
def get_pages_for_tabs
if logged_in?
@tabs = Page.find(:all)
else
@tabs = Page.find(:all, :conditions => ["admin != ?", true])
end
end
This won’t deal well with lots of pages, so in the future we’ll improve this solution with the notion of page groups, sub-navigation, and other tricks.
Wrapping up
Try out the new navigation at localhost:3000. You may want to tweak the titles of the pages to make the tabs look better. We are reusing the same data as is used for the page titles in the title bar of the browser. A better solution over time may be to add a separate attribute so we can keep SEO friendly titles.
We finally have support for “administrative” pages in our simple CMS, so we can create the dashboard using our own technology!
Coming up
Next time, we’ll add the ability to use Textile markup in our CMS pages (via a plugin) and start playing with AJAX to make the UI a little easier to use.

Reader Comments
32 comments
undefined method admin
From: Lyyza, 10/21/10 12:07 AM
i do know why..I alredy follow the steps but when i want to login as a admin...the message display..login incorrect...so i add admin as a new user...then when 1 want to run it....the errormsg display as shown below: NoMethodError in Pages#index Showing app/views/pages/index.html.erb where line #17 raised: undefined method `admin?' for #
Extracted source (around line #17):
14: <%=h page.name %>
15: <%=h page.title %>
16: <%=h page.body %>
17: <%= page.admin? ? "TRUE" : "FALSE" %>
18: <%= link_to 'Show', page %>
19: <%= link_to 'Edit', edit_page_path(page) %>
20: <%= link_to 'Destroy', page, :confirm => 'Are you sure?', :method => :delete %>
admin user
From: angelo, 06/19/10 01:32 PM
whats the point of having a admin user if every user can log in as admin? well anyway great screencasts! thanks.
where to put in link_to page.title
From: Liam, 11/12/09 02:18 PM
I have some css that requires a tag in the navbar "tabs" - could you please explain where to put this in your code that dynamically generates the tabs (needless to say had it working when doing the manual hard-coded way!)... <% @tabs.each do |page| -%>
Slightly Disappointed
From: SwizzleCode, 10/06/09 12:18 AM
As a newbie to Ruby on Rails, I've enjoyed and learned quite a bit from your tutorials, but I must say that I'm slightly disappointed that the shownotes and the downloadable code haven't been corrected to coincide with the screencast. I, like many copy your code from the shownotes or download and paste it when you have long blocks of new code that you yourself do the same ("so you don't have to wait for me to type this, I'll get from the clipboard"). Is it really that difficult to correct the name of the migration so newbs like me don't have to take time to see where we screwed up? Luckily, I caught the issue relatively quickly, but why should we have to debug these types of problems? Do I now have to make it a practice to scroll to the bottom of the shownotes first and see what errors others have encountered before I even start a lesson? Thanks for listening.
Need to close the Navbar div
From: Wayne Simacek, 09/17/09 03:33 AM
Small thing....but you need to close the Navbar div. Thanks again for the lesson. Wayne
rake db:migrate errors
From: Charlie Magee, 04/08/09 04:20 PM
I've been getting the same rake error as "fil" at the bottom of this comments page: Error is: rake aborted! uninitialized constant AddAdminPageAttributeToPages I've had intermittent problems on several other of the Lessons as well and I think I tracked down what I'm doing wrong. My problem is that I have been downloading the app code supplied on the lesson pages and using those to copy and paste into my code files because there's no way I want to type all that when I can just listen to Michael and Christopher explain. However, the app files don't always match what they're telling us to do onscreen! At least not in this lesson. In this lesson we're told to make a migration called AddAdminPageAttributeToPages. But the migration in the downloaded lesson is called AddAdminPageAttribute. So I wasn't paying enough attention, so after pasting everything in the .rb file then my rake didn't match the code anymore. I hope this helps. Charlie Magee
rake db:migrate errors
From: Charles Ford, 01/04/09 11:40 AM
Micheal and Christopher, You guys have produced an excellent and very professional set of tutorials. 'Learning Rails' is a sophisticated demonstration. Thank you for all the work you've put into the product. I'm new to Ruby on Rails and am enjoying 'Learning Rails' very much. I'm running Ruby 1.8.7p72 and Rails 2.2.2 on a Mac and have encountered only one problem that took any real effort to solve. I thought I'd pass along my work-around in case it could help someone else who runs into the same frustration. Of course, there's probably a simple explanation and fix as to the error I got, but, being new to the whole Rails concept and community, I have no idea what it is. (I tried posting this solution earlier, before I realized the message would be going through TextTile, so the earlier message looked pretty garbled. Hence, the double posting.) Again, thanks for the large contribution you've put forth. Charles h3. add_column migration error Tip: export sqlite database, convert and import into mysql A) Export sqlite database: cd app_root sqlite3 db/development.sqlite3 .dump .quit >> export.sqlite.sql B) This dump file should be modified before the mysql import: -Replace ” (double-quotes) with ` (grave accent) -Remove “BEGIN TRANSACTION;”, “COMMIT;”, and all lines related to “sqlite_sequence” -Replace “autoincrement” with “auto_increment” C) Save file as import.mysql.sql D) Run the import into mysql : (assumes database has been created) mysql -uroot -p myapp_development < import.mysql.sql E) Edit the database.yml file : adapter: mysql username: root password: =your mysql password= host: 127.0.0.1
rake db:migrate errors
From: Charles Ford, 01/02/09 09:38 AM
Lesson 13 Step: AddAdminPageAttributeToPages Problem: rake db:migrate errors Solution: After trying several things and searching for a solution, I decided to switch the database from SQLite3 to MySQL. Platform: Mac OS X 4.11 Ruby 1.8.7p27 Rails 2.2.2 Export sqlite database, convert and import into mysql 1) Export sqlite database: # cd
# sqlite3 db/development.sqlite3 .dump .quit >> export.sqlite.sql
2) This dump file should be modified before the mysql import:
- Replace " (double-quotes) with ` (grave accent)
- Remove "BEGIN TRANSACTION;" "COMMIT;", and lines related to "sqlite_sequence"
- Replace "autoincrement" with "auto_increment"
3) Save file as import.mysql.sql
4) Run the import into mysql :
mysql -uroot -p myapp_development < import.mysql.sql
I got errors when importing into MySQL. Since the database is so small, I used the CREATE and CREATE lines as an SQL Query. That worked. Then, since the database is so small, I just inserted the data into the tables manually.
The fourth migration file ( in my case: app>db>migrate>20090102062925_add_admin_page_attribute_to_pages.rb ) was still intact from following the tutorial. -- the prefix '20090102062925_' shows I'm running a Mac as it's a timestamp, not a sequentially ordered number --
Next, I edited the config>database.yml file, replacing everything having to do with SQLite3 with these lines:
development:
adapter: mysql
database: myapp_development
username: root
password:
host: localhost
After exporting the SQLite database, importing the MySQL database, verifying the new data, verifying the migration files, and editing the database.yml file, I went back to the terminal command line and entered the migration command:
rake db:migration
It worked like a charm and I'm back in business.
Hope this helps...
. I commented this line and added a to the line before
Layout is different than shown in screencast
From: Dale Bremer, 12/07/08 01:36 PM
I believe I found the layout problem: In layout/application.html.erb you have a line 26 which seems to be an unmatched