13: Admin Pages




Learning Rails show

Summary: <h2>Goals</h2> <p>In this lesson, we implement the actual administrative dashboard using improvements we make to the mini-<span class="caps">CMS</span> we are building. We finish up with an improvement to our navigation code so we can build the tabbed interface more dynamically.</p> <h2>Setup</h2> <p>We begin with the code with which we ended Lesson 12. These zip files contain the beginning and ending states of the code:</p> <ul> <li><a href="/learningrails_12.zip">Learning Rails example app code as of the end of Lesson 12</a></li> <li><a href="/learningrails_13.zip">Learning Rails example app code as of the end of Lesson 13</a></li> </ul> <h2>Admin Pages</h2> <p>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.</p> <h2>Make the Admin page attribute</h2> <p>If we are going to use our baby-<span class="caps">CMS</span> 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.</p> <p>Make a migration to add an admin attribute to pages, and then set some default values in the migration to keep things tidy.</p> <pre> class AddAdminPageAttribute &lt; 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 </pre> <p>Now, we need to update the Page Admin html so we can see/edit the new admin attribute:</p> <p>First, index.html.erb gets a couple of new table entries:</p> <pre> &lt;h1&gt;Listing pages&lt;/h1&gt; &lt;table&gt; &lt;tr&gt; &lt;th&gt;Name&lt;/th&gt; &lt;th&gt;Title&lt;/th&gt; &lt;th&gt;Body&lt;/th&gt; &lt;th&gt;Admin?&lt;/th&gt; &lt;/tr&gt; &lt;% for page in @pages %&gt; &lt;tr&gt; &lt;td&gt;&lt;%=h page.name %&gt;&lt;/td&gt; &lt;td&gt;&lt;%=h page.title %&gt;&lt;/td&gt; &lt;td&gt;&lt;%=h page.body %&gt;&lt;/td&gt; &lt;td&gt;&lt;%= page.admin? ? "TRUE" : "FALSE" %&gt;&lt;/td&gt; &lt;td&gt;&lt;%= link_to 'Show', page %&gt;&lt;/td&gt; &lt;td&gt;&lt;%= link_to 'Edit', edit_page_path(page) %&gt;&lt;/td&gt; &lt;td&gt;&lt;%= link_to 'Destroy', page, :confirm =&gt; 'Are you sure?', :method =&gt; :delete %&gt;&lt;/td&gt; &lt;/tr&gt; &lt;% end %&gt; &lt;/table&gt; &lt;br /&gt; &lt;%= link_to 'New page', new_page_path %&gt; </pre> <p>Then add a snippet to show.html.erb to see the Admin value:</p> <pre> &lt;p&gt; &lt;b&gt;Admin?&lt;/b&gt;&lt;br /&gt; &lt;%= @page.admin? ? "TRUE" : "FALSE" %&gt; &lt;/p&gt; </pre> <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> <pre> &lt;p&gt; &lt;b&gt;Admin?&lt;/b&gt;&lt;br /&gt; &lt;%= f.check_box :admin %&gt; &lt;/p&gt; </pre> <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!</p> <h2>Update the viewer controller to handle special pages</h2> <p>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:</p> <pre> def show @page = Page.find_by_name(params[:name]) login_required if @page.admin? end </pre> <p>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.</p> <h2>Add an admin dashboard page in the DB</h2> <p>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:</p> <pre> &lt;a href="/pages"&gt;"Page Admin"&lt;/a&gt; &lt;br&gt; &lt;a href="/users"&gt;"User Admin"&lt;/a&gt; </pre> <p>Test it. Check it logged in and logged out. It works!</p> <h2>Make layout dynamic</h2> <p>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.</p> <p>Update the application layout to support rendering tabs dynamically. Here is the new navbar div:</p> <pre> &lt;div id='navbar'&gt; &lt;ul&gt; &lt;% @tabs.each do |page| -%&gt; &lt;li&gt;&lt;%= link_to page.title, view_page_path(page.name) %&gt;&lt;/li&gt; &lt;% end -%&gt; &lt;li&gt;&lt;% if logged_in? %&gt; &lt;%= link_to "Log Out", logout_path %&gt; &lt;% else %&gt; &lt;%= link_to "Log In", login_path %&gt; &lt;% end %&gt; &lt;/li&gt; &lt;/ul&gt; &lt;div&gt; </pre> <p>We need to add a before_filter to application.rb to load up pages into that @tabs variable:</p> <pre> before_filter :get_pages_for_tabs def get_pages_for_tabs if logged_in? @tabs = Page.find(:all) else @tabs = Page.find(:all, :conditions =&gt; ["admin != ?", true]) end end </pre> <p>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.</p> <h2>Wrapping up</h2> <p>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 <span class="caps">SEO</span> friendly titles.</p> <p>We finally have support for “administrative” pages in our simple <span class="caps">CMS</span>, so we can create the dashboard using our own technology!</p> <h2>Coming up</h2> <p>Next time, we’ll add the ability to use Textile markup in our <span class="caps">CMS</span> pages (via a plugin) and start playing with <span class="caps">AJAX</span> to make the UI a little easier to use.</p>