<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>app/views/cms/menus/_menu.html.erb</filename>
    </added>
    <added>
      <filename>app/views/cms/menus/_menu_item.html.erb</filename>
    </added>
    <added>
      <filename>app/views/cms/shared/access_denied.html.erb</filename>
    </added>
    <added>
      <filename>app/views/cms/users/show.html.erb</filename>
    </added>
    <added>
      <filename>test/functional/cms/content_block_controller_test.rb</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -33,6 +33,7 @@ class Cms::ContentBlockController &lt; Cms::BaseController
       after_create_on_failure
     end
   rescue Exception =&gt; @exception
+    raise @exception if @exception.is_a?(Cms::Errors::AccessDenied)
     after_create_on_error
   end
   
@@ -50,6 +51,7 @@ class Cms::ContentBlockController &lt; Cms::BaseController
   rescue ActiveRecord::StaleObjectError =&gt; @exception
     after_update_on_edit_conflict
   rescue Exception =&gt; @exception
+    raise @exception if @exception.is_a?(Cms::Errors::AccessDenied)
     after_update_on_error
   end
   
@@ -127,15 +129,18 @@ class Cms::ContentBlockController &lt; Cms::BaseController
       options[:order] = params[:order] unless params[:order].blank?
       scope = model_class.respond_to?(:list) ? model_class.list : model_class
       @blocks = scope.searchable? ? scope.search(params[:search]).paginate(options) : scope.paginate(options)
+      check_permissions
     end
   
     def load_block
       @block = model_class.find(params[:id])
+      check_permissions
     end
   
     def load_block_draft
-      load_block
+      @block = model_class.find(params[:id])
       @block = @block.as_of_draft_version if model_class.versioned?
+      check_permissions
     end
   
     # path related methods - available in the view as helpers
@@ -162,6 +167,7 @@ class Cms::ContentBlockController &lt; Cms::BaseController
   
     def build_block
       @block = model_class.new(params[model_name])
+      check_permissions
     end
 
     def set_default_category
@@ -244,6 +250,23 @@ class Cms::ContentBlockController &lt; Cms::BaseController
         false
       end
     end
+    
+    # Use a &quot;whitelist&quot; approach to access to avoid mistakes
+    # By default everyone can create new block and view them and their properties,
+    # but blocks can only be modified based on the permissions of the pages they
+    # are connected to.
+    def check_permissions
+      case action_name
+        when &quot;index&quot;, &quot;show&quot;, &quot;new&quot;, &quot;create&quot;, &quot;version&quot;, &quot;versions&quot;, &quot;usages&quot;
+          # Allow
+        when &quot;edit&quot;, &quot;update&quot;
+          raise Cms::Errors::AccessDenied unless current_user.able_to_edit?(@block)
+        when &quot;destroy&quot;, &quot;publish&quot;, &quot;revert_to&quot;
+          raise Cms::Errors::AccessDenied unless current_user.able_to_publish?(@block)
+        else
+          raise Cms::Errors::AccessDenied
+      end
+    end
 
     # methods to setup the view
 
@@ -259,4 +282,4 @@ class Cms::ContentBlockController &lt; Cms::BaseController
       &quot;cms/blocks&quot;
     end
 
-end
\ No newline at end of file
+end</diff>
      <filename>app/controllers/cms/content_block_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -58,6 +58,7 @@ class Cms::ContentController &lt; Cms::ApplicationController
   # if caching is not enabled
   def render_page
     @_page_route.execute(self) if @_page_route
+    prepare_connectables_for_render unless (logged_in? &amp;&amp; current_user.able_to?(:administrate, :edit_content, :publish_content))
     render :layout =&gt; @page.layout, :action =&gt; 'show'
   end
   
@@ -94,11 +95,34 @@ class Cms::ContentController &lt; Cms::ApplicationController
         @template.instance_variable_set(&quot;#{v}&quot;, nil)
       end
       
+      prepare_connectables_for_render unless (logged_in? &amp;&amp; current_user.able_to?(:administrate, :edit_content, :publish_content))
       render :layout =&gt; @page.layout, :template =&gt; 'cms/content/show', :status =&gt; status
     else
       handle_server_error(exception)
     end      
-  end    
+  end   
+  
+  # If any of the page's connectables (portlets, etc) are renderable, they may have a render method
+  # which does &quot;controller&quot; stuff, so we need to get that run before rendering the page.
+  def prepare_connectables_for_render
+    worst_exception = nil
+    @page.connectables_by_connector.values.each do |c| 
+      begin
+        c.prepare_to_render(self) 
+      rescue
+        logger.debug &quot;THROWN EXCEPTION by connectable #{c}: #{$!}&quot;
+        case $!
+        when ActiveRecord::RecordNotFound
+          raise
+        when Cms::Errors::AccessDenied
+          worst_exception = $!
+         else
+          c.render_exception = $!
+        end
+      end
+    end
+    raise worst_exception if worst_exception
+  end 
 
   # ----- Before Filters -------------------------------------------------------
   def construct_path
@@ -224,4 +248,4 @@ class Cms::ContentController &lt; Cms::ApplicationController
   
   
   
-end
\ No newline at end of file
+end</diff>
      <filename>app/controllers/cms/content_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,8 +2,9 @@ class Cms::DashboardController &lt; Cms::BaseController
       
   def index
     @unpublished_pages = Page.unpublished.all(:order =&gt; &quot;updated_at desc&quot;)
+    @unpublished_pages = @unpublished_pages.select { |page| current_user.able_to_publish?(page) }
     @incomplete_tasks = current_user.tasks.incomplete.all(
       :include =&gt; :page, 
       :order =&gt; &quot;tasks.due_date desc, pages.name&quot;)
   end
-end
\ No newline at end of file
+end</diff>
      <filename>app/controllers/cms/dashboard_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,7 +2,8 @@ module Cms
   module ErrorHandling
     def self.included(controller)
       controller.class_eval do
-          rescue_from Exception, :with =&gt; :handle_server_error
+        rescue_from Exception, :with =&gt; :handle_server_error unless RAILS_ENV == &quot;test&quot;
+        rescue_from Cms::Errors::AccessDenied, :with =&gt; :handle_access_denied
       end
     end
     
@@ -13,6 +14,12 @@ module Cms
         :status =&gt; :internal_server_error,
         :locals =&gt; {:exception =&gt; exception}
     end
+    
+    def handle_access_denied(exception)
+      render :layout   =&gt; 'cms/application', 
+             :template =&gt; 'cms/shared/access_denied',
+             :status =&gt; 403
+    end
 
   end
-end
\ No newline at end of file
+end</diff>
      <filename>app/controllers/cms/error_handling.rb</filename>
    </modified>
    <modified>
      <diff>@@ -47,10 +47,12 @@ class Cms::LinksController &lt; Cms::BaseController
 
     def load_section
       @section = Section.find(params[:section_id])
+      raise Cms::Errors::AccessDenied unless current_user.able_to_edit?(@section)
     end
 
     def load_link
       @link = Link.find(params[:id])
+      raise Cms::Errors::AccessDenied unless current_user.able_to_edit?(@link)
     end
     
     def load_draft_link</diff>
      <filename>app/controllers/cms/links_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -60,9 +60,9 @@ class Cms::PagesController &lt; Cms::BaseController
   {:publish =&gt; &quot;published&quot;, :hide =&gt; &quot;hidden&quot;, :archive =&gt; &quot;archived&quot;}.each do |status, verb|
     define_method status do
       if params[:page_ids]
-        params[:page_ids].each do |id|
-          Page.find(id).send(status)
-        end
+        @pages = params[:page_ids].map { |id| Page.find(id) }
+        raise Cms::Errors::AccessDenied unless @pages.all? { |page| current_user.able_to_edit?(page) }
+        @pages.each { |page| page.send(status) }
         flash[:notice] = &quot;#{params[:page_ids].size} pages #{verb}&quot;
         redirect_to cms_dashboard_url
       else
@@ -103,6 +103,7 @@ class Cms::PagesController &lt; Cms::BaseController
 
     def load_page
       @page = Page.find(params[:id])
+      raise Cms::Errors::AccessDenied unless current_user.able_to_edit?(@page)
     end
     
     def load_draft_page
@@ -112,6 +113,7 @@ class Cms::PagesController &lt; Cms::BaseController
   
     def load_section
       @section = Section.find(params[:section_id])
+      raise Cms::Errors::AccessDenied unless current_user.able_to_edit?(@section)
     end
     
     def hide_toolbar</diff>
      <filename>app/controllers/cms/pages_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,5 +1,5 @@
 class Cms::SectionNodesController &lt; Cms::BaseController
-  check_permissions :publish_content, :only =&gt; [:move_before, :move_after]
+  check_permissions :publish_content, :except =&gt; [:index]
   
   def index
     @toolbar_tab = :sitemap</diff>
      <filename>app/controllers/cms/section_nodes_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,6 +1,7 @@
 class Cms::SectionsController &lt; Cms::BaseController
 
   before_filter :load_parent, :only =&gt; [:new, :create]
+  before_filter :load_section, :only =&gt; [:edit, :update, :destroy, :move]
   before_filter :set_toolbar_tab
   
   helper_method :public_groups
@@ -16,12 +17,13 @@ class Cms::SectionsController &lt; Cms::BaseController
   
   def new
     @section = @parent.sections.build
-    @section.groups = public_groups + cms_groups
+    @section.groups = @parent.groups
   end
   
   def create
     @section = Section.new(params[:section])
     @section.parent = @parent
+    @section.groups = @section.parent.groups unless current_user.able_to?(:administrate)
     if @section.save
       flash[:notice] = &quot;Section '#{@section.name}' was created&quot;
       redirect_to [:cms, @section]
@@ -31,13 +33,12 @@ class Cms::SectionsController &lt; Cms::BaseController
   end
 
   def edit
-    @section = Section.find(params[:id])
-    raise Cms::Errors::AccessDenied unless current_user.able_to_edit?(@section)
   end
   
   def update
-    @section = Section.find(params[:id])
-    if @section.update_attributes(params[:section])
+    params[:section].delete('group_ids') if params[:section] &amp;&amp;  !current_user.able_to?(:administrate)
+    @section.attributes = params[:section]
+    if @section.save
       flash[:notice] = &quot;Section '#{@section.name}' was updated&quot;
       redirect_to [:cms, @section]
     else
@@ -46,7 +47,6 @@ class Cms::SectionsController &lt; Cms::BaseController
   end
   
   def destroy
-    @section = Section.find(params[:id])  
     respond_to do |format|
       if @section.deletable? &amp;&amp; @section.destroy
         message = &quot;Section '#{@section.name}' was deleted.&quot;
@@ -61,7 +61,6 @@ class Cms::SectionsController &lt; Cms::BaseController
   end  
   
   def move
-    @section = Section.find(params[:id])
     if params[:section_id]
       @move_to = Section.find(params[:section_id])
     else
@@ -81,6 +80,12 @@ class Cms::SectionsController &lt; Cms::BaseController
   protected
     def load_parent
       @parent = Section.find(params[:section_id])
+      raise Cms::Errors::AccessDenied unless current_user.able_to_edit?(@parent)
+    end
+
+    def load_section
+      @section = Section.find(params[:id])
+      raise Cms::Errors::AccessDenied unless current_user.able_to_edit?(@section)
     end
 
     def handle_file_browser_upload</diff>
      <filename>app/controllers/cms/sections_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -21,7 +21,7 @@ class Cms::SessionsController &lt; Cms::ApplicationController
       handle_remember_cookie! new_cookie_flag
       flash[:notice] = &quot;Logged in successfully&quot;
       if params[:success_url] # Coming from login portlet
-        redirect_to(session[:return_to] || params[:success_url] || &quot;/&quot;)          
+        redirect_to((!params[:success_url].blank? &amp;&amp; params[:success_url]) || session[:return_to] || &quot;/&quot;)          
         session[:return_to] = nil
       else
         redirect_back_or_default(cms_home_url)</diff>
      <filename>app/controllers/cms/sessions_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,7 +1,9 @@
 class Cms::UsersController &lt; Cms::ResourceController
   layout 'cms/administration'
 
-  check_permissions :administrate
+  check_permissions :administrate, :except =&gt; [:show, :change_password, :update_password]
+  before_filter :only_self_or_administrator, :only =&gt; [:show, :change_password, :update_password]
+  
   before_filter :set_menu_section
   after_filter :update_group_membership, :only =&gt; [:update, :create]
   after_filter :update_flash, :only =&gt; [ :update, :create ]
@@ -33,10 +35,6 @@ class Cms::UsersController &lt; Cms::ResourceController
     @users = User.paginate(:page =&gt; params[:page], :per_page =&gt; per_page, :include =&gt; :user_group_memberships, :conditions =&gt; conditions, :order =&gt; &quot;first_name, last_name, email&quot;)
   end
 
-  def show
-    redirect_to [:edit, :cms, user]
-  end
-
   def change_password
     user
   end
@@ -44,7 +42,7 @@ class Cms::UsersController &lt; Cms::ResourceController
   def update_password
     if user.update_attributes(params[:user])
       flash[:notice] = &quot;Password for '#{user.login}' was changed&quot;
-      redirect_to cms_users_path
+      redirect_to(current_user.able_to?(:administrate) ? cms_users_path : cms_user_path(user))
     else
       render :action =&gt; 'change_password'
     end
@@ -95,4 +93,8 @@ class Cms::UsersController &lt; Cms::ResourceController
     def set_menu_section
       @menu_section = 'users'
     end
+    
+    def only_self_or_administrator
+      raise Cms::Errors::AccessDenied if !current_user.able_to?(:administrate) &amp;&amp; params[:id].to_i != current_user.id
+    end
 end</diff>
      <filename>app/controllers/cms/users_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -24,12 +24,8 @@ module Cms
       text
     end
 
-    def render_connector(connector)
-      connectable = connector.connectable_with_deleted
-      if logged_in? &amp;&amp; @mode == &quot;edit&quot;
-        if connectable.class.versioned?
-          connectable = connectable.as_of_version(connector.connectable_version)
-        end
+    def render_connector_and_connectable(connector, connectable)
+      if logged_in? &amp;&amp; @mode == &quot;edit&quot; &amp;&amp; current_user.able_to_edit?(connector.page)
         render :partial =&gt; 'cms/pages/edit_connector', 
           :locals =&gt; { :connector =&gt; connector, :connectable =&gt; connectable}
       else</diff>
      <filename>app/helpers/cms/application_helper.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,30 +1,63 @@
 module Cms
   module MenuHelper
-    # This will render a menu based on the page
-    # With no options passed, it will render a menu that shows all the child sections of the root
-    # and then it will show the path of decendent sections all the way to the current page.
-    # The resulting HTML is a DIV with a UL in it.  Each LI will have an A in it.  If the item is a Section,
-    # the HREF of the A will be the URL of the first non-archived page that is a direct child of that Section.
-    # Hidden pages will not show up, but if the first page in a Section is hidden, it will be used as the URL
-    # for that Section.  This is commonly done to have a page for a Section and avoid having duplicates in the 
-    # navigation.
+    # Renders a menu. There are two options, neither are required:
+    # 
+    # ==== Options
+    # * &lt;tt&gt;:items&lt;/tt&gt; - The items which should appear in the menu. This defaults to calling 
+    #   menu_items which generates items automatically based on the current page. But you can use 
+    #   this option to pass in a custom menu structure.
+    # * &lt;tt&gt;:partial&lt;/tt&gt; - The partial used to render the menu. By default this is &quot;partials/menu&quot;,
+    #   which can be customised through the CMS. The partial gets a local variable &lt;tt&gt;items&lt;/tt&gt;.
+    #
+    # ==== Structure of items
     #
+    # The items should be an array of hashes, in a tree. Each hash can have the following keys (name
+    # and url are required, others are optional):
+    # 
+    # * &lt;tt&gt;:name&lt;/tt&gt; - The name which appears in the menu
+    # * &lt;tt&gt;:url&lt;/tt&gt; - The URL to link to
+    # * &lt;tt&gt;:id&lt;/tt&gt; - The id for the menu item
+    # * &lt;tt&gt;:selected&lt;/tt&gt; - Boolean value to indicate whether the menu item is the current page
+    # * &lt;tt&gt;:target&lt;/tt&gt; - The target attribute for the link
+    # * &lt;tt&gt;:children&lt;/tt&gt; - An array of hashes containing the child menu items. This is where the
+    #   tree structure comes in.
+    def render_menu(options = {})
+      options[:items] ||= menu_items
+      options[:partial] ||= &quot;cms/menus/menu&quot;
+      options[:id] ||= &quot;menu&quot;
+      options[:class] ||= &quot;menu&quot;
+      render :partial =&gt; options[:partial], :locals =&gt; { :items =&gt; options[:items], :css_id =&gt; options[:id], :css_class =&gt; options[:class] }
+    end
+    
+    # This will render generate an array-of-hashes tree structure based on the page, which can be
+    # passed to render_menu in order to generate a menu.
+    # 
+    # With no options passed, it will generate a structure that includes all the child sections of 
+    # the root and then it will include the path of decendent sections all the way to the current
+    # page.
+    # 
+    # Hidden pages will not be included, but if the first page in a Section is hidden, it will be
+    # used as the URL for that Section. This is commonly done to have a page for a Section and avoid
+    # having duplicates in the navigation.
+    # 
     # You can change the behavior with the following options, all of these are optional:
     #
     # ==== Options
-    # * &lt;tt&gt;:page&lt;/tt&gt; - What page should be used as the current page.  If this value is omitted, the value in @page will be used.
-    # * &lt;tt&gt;:path&lt;/tt&gt; - This will be used to look up a section and that section will used to generate the menu.  The current page will
-    #   still be the value of the page option or @page.  Note that this is the path to a section, not a path to a page.
+    # * &lt;tt&gt;:page&lt;/tt&gt; - What page should be used as the current page.  If this value is omitted, 
+    #   the value in @page will be used.
+    # * &lt;tt&gt;:path&lt;/tt&gt; - This will be used to look up a section and that section will used to 
+    #   generate the menu structure. The current page will still be the value of the page option or
+    #   @page. Note that this is the path to a section, not a path to a page.
     # * &lt;tt&gt;:from_top&lt;/tt&gt; - How many below levels from the root the tree should start at.  
     #   All sections at this level will be shown.  The default is 0, which means show all
-    #   section that are direct children of the root
+    #   nodes that are direct children of the root
     # * &lt;tt&gt;:depth&lt;/tt&gt; - How many levels deep should the tree go, relative to from_top.  
     #   If no value is supplied, the tree will go all the way down to the current page.
     #   If a value is supplied, the tree will be that many levels underneath from_top deep.
     # * &lt;tt&gt;:limit&lt;/tt&gt; - Limits the number of top-level elements that will be included in the list
-    # * &lt;tt&gt;:class&lt;/tt&gt; - The CSS Class that will be applied to the div.  The default value is &quot;menu&quot;.
-    # * &lt;tt&gt;:show_all_siblings&lt;/tt&gt; - Passing true for this option will make all sibilings appear in the tree.
-    #   the default is false, in which case only the siblings of nodes within the open path will appear.
+    # * &lt;tt&gt;:show_all_siblings&lt;/tt&gt; - Passing true for this option will make all sibilings appear in
+    #   the tree. The default is false, in which case only the siblings of nodes within the open
+    #   path will appear.
     # 
     # ==== Examples
     #
@@ -32,89 +65,41 @@ module Cms
     # with teams being a Page, everything else a Section.  Also, assume we are on the
     # Baltimore Ravens page. If you're not a footbal fan, see http://sports.yahoo.com/nfl/teams
     #
-    #   render_menu
-    #   # =&gt; &lt;div class=&quot;menu&quot;&gt;
-    #     &lt;ul&gt;
-    #       &lt;li id=&quot;section_2&quot; class=&quot;first open&quot;&gt;
-    #         &lt;a href=&quot;/buf&quot;&gt;AFC&lt;/a&gt;
-    #         &lt;ul&gt;
-    #           &lt;li id=&quot;section_3&quot; class=&quot;first&quot;&gt;
-    #             &lt;a href=&quot;/buf&quot;&gt;East&lt;/a&gt;
-    #           &lt;/li&gt;
-    #           &lt;li id=&quot;section_4&quot; class=&quot;open&quot;&gt;
-    #             &lt;a href=&quot;/bal&quot;&gt;North&lt;/a&gt;
-    #             &lt;ul&gt;
-    #               &lt;li id=&quot;page_5&quot; class=&quot;first on&quot;&gt;
-    #                 &lt;a href=&quot;/bal&quot;&gt;Baltimore Ravens&lt;/a&gt;
-    #               &lt;/li&gt;
-    #               &lt;li id=&quot;page_6&quot;&gt;
-    #                 &lt;a href=&quot;/cin&quot;&gt;Cincinnati Bengals&lt;/a&gt;
-    #               &lt;/li&gt;
-    #               &lt;li id=&quot;page_7&quot;&gt;
-    #                 &lt;a href=&quot;/cle&quot;&gt;Cleveland Browns&lt;/a&gt;
-    #               &lt;/li&gt;
-    #               &lt;li id=&quot;page_8&quot; class=&quot;last&quot;&gt;
-    #                 &lt;a href=&quot;/pit&quot;&gt;Pittsburgh Steelers&lt;/a&gt;
-    #               &lt;/li&gt;
-    #             &lt;/ul&gt;
-    #           &lt;/li&gt;
-    #           &lt;li id=&quot;section_5&quot;&gt;
-    #             &lt;a href=&quot;/hou&quot;&gt;South&lt;/a&gt;
-    #           &lt;/li&gt;
-    #           &lt;li id=&quot;section_6&quot; class=&quot;last&quot;&gt;
-    #             &lt;a href=&quot;/den&quot;&gt;West&lt;/a&gt;
-    #           &lt;/li&gt;
-    #         &lt;/ul&gt;
-    #       &lt;/li&gt;
-    #       &lt;li id=&quot;section_7&quot; class=&quot;last&quot;&gt;
-    #         &lt;a href=&quot;/dal&quot;&gt;NFC&lt;/a&gt;
-    #       &lt;/li&gt;
-    #     &lt;/ul&gt;
-    #   &lt;/div&gt;
+    #   menu_items
+    #   # =&gt; [
+    #     { :id =&gt; &quot;section_2&quot;, :url =&gt; &quot;/buf&quot;, :name =&gt; &quot;AFC&quot;, :children =&gt; [
+    #       { :id =&gt; &quot;section_3&quot;, :url =&gt; &quot;/buf&quot;, :name =&gt; &quot;East&quot; },
+    #       { :id =&gt; &quot;section_4&quot;, :url =&gt; &quot;/bal&quot;, :name =&gt; &quot;North&quot;, :children =&gt; [
+    #         { :id =&gt; &quot;page_5&quot;, :selected =&gt; true, :url =&gt; &quot;/bal&quot;, :name =&gt; &quot;Baltimore Ravens&quot; },
+    #         { :id =&gt; &quot;page_6&quot;, :url =&gt; &quot;/cin&quot;, :name =&gt; &quot;Cincinnati Bengals&quot; },
+    #         { :id =&gt; &quot;page_7&quot;, :url =&gt; &quot;/cle&quot;, :name =&gt; &quot;Cleveland Browns&quot; },
+    #         { :id =&gt; &quot;page_8&quot;, :url =&gt; &quot;/pit&quot;, :name =&gt; &quot;Pittsburgh Steelers&quot; }
+    #       ] },
+    #       { :id =&gt; &quot;section_9&quot;, :url =&gt; &quot;/hou&quot;, :name =&gt; &quot;South&quot; },
+    #       { :id =&gt; &quot;section_10}&quot;, :url =&gt; &quot;/den&quot;, :name =&gt; &quot;West&quot; }
+    #       ] },
+    #     { :id =&gt; &quot;section_11&quot;, :url =&gt; &quot;/dal&quot;, :name =&gt; &quot;NFC&quot; }
+    #   ]
     #
-    #   render_menu(:depth =&gt; 2, :show_all_siblings =&gt; true)
-    #   # =&gt; &lt;div class=&quot;menu&quot;&gt;
-    #     &lt;ul&gt;
-    #       &lt;li id=&quot;section_2&quot; class=&quot;first open&quot;&gt;
-    #         &lt;a href=&quot;/buf&quot;&gt;AFC&lt;/a&gt;
-    #         &lt;ul&gt;
-    #           &lt;li id=&quot;section_3&quot; class=&quot;first&quot;&gt;
-    #             &lt;a href=&quot;/buf&quot;&gt;East&lt;/a&gt;
-    #           &lt;/li&gt;
-    #           &lt;li id=&quot;section_4&quot; class=&quot;open&quot;&gt;
-    #             &lt;a href=&quot;/bal&quot;&gt;North&lt;/a&gt;
-    #           &lt;/li&gt;
-    #           &lt;li id=&quot;section_5&quot;&gt;
-    #             &lt;a href=&quot;/hou&quot;&gt;South&lt;/a&gt;
-    #           &lt;/li&gt;
-    #           &lt;li id=&quot;section_6&quot; class=&quot;last&quot;&gt;
-    #             &lt;a href=&quot;/den&quot;&gt;West&lt;/a&gt;
-    #           &lt;/li&gt;
-    #         &lt;/ul&gt;
-    #       &lt;/li&gt;
-    #       &lt;li id=&quot;section_7&quot; class=&quot;last&quot;&gt;
-    #         &lt;a href=&quot;/dal&quot;&gt;NFC&lt;/a&gt;
-    #         &lt;ul&gt;
-    #           &lt;li id=&quot;section_8&quot; class=&quot;first&quot;&gt;
-    #             &lt;a href=&quot;/dal&quot;&gt;East&lt;/a&gt;
-    #           &lt;/li&gt;
-    #           &lt;li id=&quot;section_9&quot;&gt;
-    #             &lt;a href=&quot;/chi&quot;&gt;North&lt;/a&gt;
-    #           &lt;/li&gt;
-    #           &lt;li id=&quot;section_10&quot;&gt;
-    #             &lt;a href=&quot;/atl&quot;&gt;South&lt;/a&gt;
-    #           &lt;/li&gt;
-    #           &lt;li id=&quot;section_11&quot; class=&quot;last&quot;&gt;
-    #             &lt;a href=&quot;/ari&quot;&gt;West&lt;/a&gt;
-    #           &lt;/li&gt;
-    #         &lt;/ul&gt;
-    #       &lt;/li&gt;
-    #     &lt;/ul&gt;
-    #   &lt;/div&gt;    
-    def render_menu(options={})
-      #Intialize parameters
-      page = options[:page] || @page
-      return nil unless page
+    #   menu_items(:depth =&gt; 2, :show_all_siblings =&gt; true)
+    #   # =&gt; [
+    #     { :id =&gt; &quot;section_2&quot;, :url =&gt; &quot;/buf&quot;, :name =&gt; &quot;AFC&quot;, :children =&gt; [
+    #         { :id =&gt; &quot;section_3&quot;, :url =&gt; &quot;/buf&quot;, :name =&gt; &quot;East&quot; },
+    #         { :id =&gt; &quot;section_4&quot;, :url =&gt; &quot;/bal&quot;, :name =&gt; &quot;North&quot; },
+    #         { :id =&gt; &quot;section_5&quot;, :url =&gt; &quot;/hou&quot;, :name =&gt; &quot;South&quot; },
+    #         { :id =&gt; &quot;section_6&quot;, :url =&gt; &quot;/den&quot;, :name =&gt; &quot;West&quot; }
+    #       ] },
+    #     { :id =&gt; &quot;section_7&quot;, :url =&gt; &quot;/dal&quot;, :name =&gt; &quot;NFC&quot;, :children =&gt; [
+    #         { :id =&gt; &quot;section_8&quot;, :url =&gt; &quot;/dal&quot;, :name =&gt; &quot;East&quot; },
+    #         { :id =&gt; &quot;section_9&quot;, :url =&gt; &quot;/chi&quot;, :name =&gt; &quot;North&quot; },
+    #         { :id =&gt; &quot;section_10&quot;, :url =&gt; &quot;/atl&quot;, :name =&gt; &quot;South&quot; },
+    #         { :id =&gt; &quot;section_11&quot;, :url =&gt; &quot;/ari&quot;, :name =&gt; &quot;West&quot; }
+    #       ] }
+    #   ]
+    def menu_items(options = {})
+      # Intialize parameters
+      selected_page = options[:page] || @page
+      return nil unless selected_page
       
       # Path to the section
       if options.has_key?(:path)
@@ -122,65 +107,52 @@ module Cms
         raise &quot;Could not find section for path '#{options[:path]}'&quot; unless section_for_path
         ancestors = section_for_path.ancestors(:include_self =&gt; true)
       else
-        ancestors = page.ancestors
+        ancestors = selected_page.ancestors
       end
       
-      from_top = options.has_key?(:from_top) ? options[:from_top].to_i : 0
-      depth = options.has_key?(:depth) ? options[:depth].to_i : 1.0/0
-      id = options[:id] || &quot;menu&quot;
-      css_class = options[:class] || &quot;menu&quot;
-      show_all_siblings = !!(options[:show_all_siblings])
-      limit = options[:limit]
-
-      html = &quot;&lt;div id=\&quot;#{id}\&quot; class=\&quot;#{css_class}\&quot;&gt;\n&quot;
-      
-      if from_top &gt; ancestors.size
-        return html &lt;&lt; &quot;&lt;/div&gt;\n&quot;
-      else
-        ancestors = ancestors[from_top..-1]
+      if options.has_key?(:from_top)
+        ancestors = ancestors[options[:from_top].to_i..-1] || []
       end
       
-      #We are defining a recursive lambda that takes the top-level sections
-      #d is the current depth
-      fn = lambda do |nodes, d|
-        indent = (d-1)*4
-        html &lt;&lt; &quot;&lt;ul&gt;\n&quot;.indent(indent+2)
-        nodes.each_with_index do |sn, i|
-
-          #Construct the CSS classes that the LI should have
-          classes = [&quot;depth-#{d}&quot;]
-          if i == 0
-            classes &lt;&lt; &quot;first&quot;
-          elsif i == nodes.size-1
-            classes &lt;&lt; &quot;last&quot;
-          end
-          classes &lt;&lt; &quot;open&quot; if ancestors.include?(sn.node)
-          classes &lt;&lt; &quot;on&quot; if page == sn.node
-          cls = classes.empty? ? nil : classes.join(&quot; &quot;)
+      depth = options.has_key?(:depth) ? options[:depth].to_i : 1.0/0
+      show_all_siblings = options[:show_all_siblings] || false
+      
+      # We are defining a recursive lambda that takes the top-level sections
+      fn = lambda do |section_nodes, current_depth|
+        section_nodes.map do |section_node|
+          node = section_node.node
           
-          html &lt;&lt; %Q{&lt;li id=&quot;#{sn.node_type.underscore}_#{sn.node.id}&quot;#{cls ? &quot; class=\&quot;#{cls}\&quot;&quot; : ''}&gt;\n}.indent(indent+4)
+          item = {}
+          item[:selected] = true if selected_page == node
+          item[:id] = &quot;#{section_node.node_type.underscore}_#{section_node.node_id}&quot;
           
-          #Figure out what this link for this node should be
-          #If it is a page, then the page will simply be used
-          #But if is a page, we call the first_page_or_link method
-          p = sn.node_type == &quot;Section&quot; ? sn.node.first_page_or_link : sn.node
-          html &lt;&lt; %Q{&lt;a href=&quot;#{p ? p.path : '#'}&quot;#{(p.respond_to?(:new_window) &amp;&amp; p.new_window?) ? ' target=&quot;_blank&quot;' : ''}&gt;#{sn.node.name}&lt;/a&gt;\n}.indent(indent+6)
+          # If we are showing a section item, we want to use the path for the first page
+          page = section_node.section? ? node.first_page_or_link : node
           
-          #Now if this is a section, we do the child nodes, 
-          #but only if the show_all_siblings parameter is true, 
-          #or if this section is one of the current page's ancestors
-          #and also if the current depth is less than the target depth
-          if sn.node_type == &quot;Section&quot; &amp;&amp; (show_all_siblings || ancestors.include?(sn.node)) &amp;&amp; d &lt; depth
-            fn.call(sn.node.visible_child_nodes, d+1)
-          end
+          item[:url] = page &amp;&amp; page.path || '#'
+          item[:name] = node.name
+          item[:target] = &quot;_blank&quot; if page.respond_to?(:new_window?) &amp;&amp; page.new_window?
           
-          html &lt;&lt; %Q{&lt;/li&gt;\n}.indent(indent+4)
+          # Now if this is a section, we do the child nodes, 
+          # but only if the show_all_siblings parameter is true, 
+          # or if this section is one of the current page's ancestors
+          # and also if the current depth is less than the target depth
+          if section_node.section? &amp;&amp;
+             current_depth &lt; depth &amp;&amp;
+             (show_all_siblings || ancestors.include?(node)) &amp;&amp;
+             !node.visible_child_nodes.empty?
+            item[:children] = fn.call(node.visible_child_nodes, current_depth + 1)
+          end
           
+          item
         end
-        html &lt;&lt; &quot;&lt;/ul&gt;\n&quot;.indent(indent+2)
       end
-      fn.call(ancestors.first.visible_child_nodes(:limit =&gt; limit), 1) unless ancestors.first.blank?
-      html &lt;&lt; &quot;&lt;/div&gt;\n&quot;
+      
+      if ancestors.empty?
+        []
+      else
+        fn.call(ancestors.first.visible_child_nodes(:limit =&gt; options[:limit]), 1)
+      end
     end
   end
-end
\ No newline at end of file
+end</diff>
      <filename>app/helpers/cms/menu_helper.rb</filename>
    </modified>
    <modified>
      <diff>@@ -14,7 +14,7 @@ module Cms
     
     def container(name)
       content = instance_variable_get(&quot;@content_for_#{name}&quot;)
-      if logged_in? &amp;&amp; @page &amp;&amp; @mode == &quot;edit&quot;
+      if logged_in? &amp;&amp; @page &amp;&amp; @mode == &quot;edit&quot; &amp;&amp; current_user.able_to_edit?(@page)
         render :partial =&gt; 'cms/pages/edit_container', :locals =&gt; {:name =&gt; name, :content =&gt; content}
       else
         content
@@ -65,4 +65,4 @@ module Cms
     end
             
   end
-end
\ No newline at end of file
+end</diff>
      <filename>app/helpers/cms/page_helper.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,5 +1,5 @@
 require 'digest/sha1'
-require 'ftools'
+require 'fileutils'
 
 class Attachment &lt; ActiveRecord::Base
 
@@ -116,7 +116,7 @@ class Attachment &lt; ActiveRecord::Base
     unless temp_file.blank?
       FileUtils.mkdir_p File.dirname(full_file_location)
       if temp_file.local_path
-        File.copy temp_file.local_path, full_file_location
+        FileUtils.copy temp_file.local_path, full_file_location
       else
         open(full_file_location, 'w') {|f| f &lt;&lt; temp_file.read }
       end</diff>
      <filename>app/models/attachment.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,5 +1,5 @@
 class Link &lt; ActiveRecord::Base
-  acts_as_content_block
+  acts_as_content_block :connectable =&gt; false
   
   named_scope :named, lambda{|name| {:conditions =&gt; ['links.name = ?', name]}}
   
@@ -32,4 +32,4 @@ class Link &lt; ActiveRecord::Base
     url
   end
 
-end
\ No newline at end of file
+end</diff>
      <filename>app/models/link.rb</filename>
    </modified>
    <modified>
      <diff>@@ -150,7 +150,18 @@ class Page &lt; ActiveRecord::Base
           
   def delete_connectors
     connectors.for_page_version(version).all.each{|c| c.destroy }
-  end        
+  end
+  
+  def connectables_by_connector
+    @connectables_by_connector ||= connectors.for_page_version(version).inject({}) do |mem, connector|
+      connectable = connector.connectable_with_deleted
+      if connectable.class.versioned?
+        connectable = connectable.as_of_version(connector.connectable_version)
+      end
+      mem[connector] = connectable
+      mem
+    end
+  end
          
   #This is done to let copy_connectors know which version to pull from
   #copy_connectors will get called later as an after_update callback</diff>
      <filename>app/models/page.rb</filename>
    </modified>
    <modified>
      <diff>@@ -75,7 +75,12 @@ class Section &lt; ActiveRecord::Base
     ancs = node ? node.ancestors : []
     options[:include_self] ? ancs + [self] : ancs
   end
-  
+
+  def with_ancestors(options = {})
+    options.merge! :include_self =&gt; true
+    self.ancestors(options)
+  end
+
   def move_to(section)
     if root?
       false
@@ -139,4 +144,4 @@ class Section &lt; ActiveRecord::Base
     end
   end
       
-end
\ No newline at end of file
+end</diff>
      <filename>app/models/section.rb</filename>
    </modified>
    <modified>
      <diff>@@ -11,8 +11,7 @@ class User &lt; ActiveRecord::Base
   validates_presence_of     :email
   #validates_length_of       :email,    :within =&gt; 6..100 #r@a.wk
   #validates_uniqueness_of   :email,    :case_sensitive =&gt; false
-  validates_format_of       :email,    :with =&gt; /[^@]{2,}@[^.]{2,}\..{2,}/, :message =&gt; &quot;should be an email address, ex. xx@xx.com&quot;
-
+  validates_format_of       :email,    :with =&gt; /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :message =&gt; &quot;should be an email address, ex. xx@xx.com&quot;
   attr_accessible :login, :email, :name, :first_name, :last_name, :password, :password_confirmation, :expires_at
 
   has_many :user_group_memberships
@@ -89,12 +88,12 @@ class User &lt; ActiveRecord::Base
     @viewable_sections ||= Section.find(:all, :include =&gt; {:groups =&gt; :users}, :conditions =&gt; [&quot;users.id = ?&quot;, id])
   end
 
-  def editable_sections
-    @editable_sections ||= Section.find(:all, :include =&gt; {:groups =&gt; [:group_type, :users]}, :conditions =&gt; [&quot;users.id = ? and group_types.cms_access = ?&quot;, id, true])
+  def modifiable_sections
+    @modifiable_sections ||= Section.find(:all, :include =&gt; {:groups =&gt; [:group_type, :users]}, :conditions =&gt; [&quot;users.id = ? and group_types.cms_access = ?&quot;, id, true])
   end
 
-  #Expects a list of names of Permissions
-  #true if the user has any of the permissions
+  # Expects a list of names of Permissions
+  # true if the user has any of the permissions
   def able_to?(*required_permissions)
     perms = required_permissions.map(&amp;:to_sym)
     permissions.any? do |p| 
@@ -102,24 +101,43 @@ class User &lt; ActiveRecord::Base
     end
   end
     
-  #Expects object to be an object or a section
-  #If it's a section, that will be used
-  #If it's not a section, it will call section on the object
-  #returns true if any of the sections of the groups the user is in matches the page's section.
+  # Expects object to be an object or a section
+  # If it's a section, that will be used
+  # If it's not a section, it will call section on the object
+  # returns true if any of the sections of the groups the user is in matches the page's section.
   def able_to_view?(object)
     section = object.is_a?(Section) ? object : object.section
-    !!(viewable_sections.include?(section) || groups.cms_access.count &gt; 0)
+    viewable_sections.include?(section) || groups.cms_access.count &gt; 0
+  end
+  
+  def able_to_modify?(object)
+    case object
+      when Section
+        modifiable_sections.include?(object)
+      when Page, Link
+        modifiable_sections.include?(object.section)
+      else
+        if object.class.respond_to?(:connectable?) &amp;&amp; object.class.connectable?
+          object.connected_pages.all? { |page| able_to_modify?(page) }
+        else
+          true
+        end
+    end
+  end
+  
+  # Expects node to be a Section, Page or Link
+  # Returns true if the specified node, or any of its ancestor sections, is editable by any of 
+  # the user's 'CMS User' groups.
+  def able_to_edit?(object)    
+    able_to?(:edit_content) &amp;&amp; able_to_modify?(object)
   end
   
-  #Expects section to be a Section
-  #Returns true if any of the sections of the groups that have group_type = 'CMS User' 
-  #that the user is in match the section.
-  def able_to_edit?(section)    
-    !!(editable_sections.include?(section) &amp;&amp; able_to?(:edit_content))
+  def able_to_publish?(object)
+    able_to?(:publish_content) &amp;&amp; able_to_modify?(object)
   end
   
   def able_to_edit_or_publish_content?
     able_to?(:edit_content, :publish_content)
   end
   
-end
\ No newline at end of file
+end</diff>
      <filename>app/models/user.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,7 +1,7 @@
 &lt;% able_to? :publish_content do -%&gt;
   &lt;% if @block.respond_to?(:live?) &amp;&amp; !@block.live? %&gt;
     &lt;%= link_to span_tag('Publish'), block_path(:publish), 
-      :class =&gt; &quot;http_put button left&quot;, 
+      :class =&gt; &quot;http_put button left#{' disabled' unless current_user.able_to_publish?(@block)}&quot;, 
       :id =&gt; &quot;publish_button&quot; %&gt;
   &lt;% else %&gt;
     &lt;%= link_to span_tag('Publish'), &quot;#&quot;, 
@@ -15,7 +15,7 @@
   :id =&gt; &quot;view_button&quot; %&gt;
   
 &lt;%= link_to span_tag('Edit Content'), block_path(:edit), 
-  :class =&gt; &quot;button right#{ ' off' if action_name == 'edit'}&quot;, 
+  :class =&gt; &quot;button right#{ ' off' if action_name == 'edit'}#{' disabled' unless current_user.able_to_edit?(@block)}&quot;, 
   :id =&gt; &quot;edit_button&quot; %&gt;
   
 &lt;%= link_to span_tag(&quot;Add New Content&quot;), new_block_path, 
@@ -33,6 +33,6 @@
 &lt;% end %&gt;
 
 &lt;%= link_to span_tag(&quot;&lt;span class=\&quot;delete_img\&quot;&gt;&amp;nbsp;&lt;/span&gt;Delete&quot;), block_path, 
-  :class =&gt; &quot;http_delete confirm_with_title button&quot;, 
+  :class =&gt; &quot;http_delete confirm_with_title button#{' disabled' unless current_user.able_to_publish?(@block)}&quot;, 
   :title =&gt; &quot;Are you sure you want to delete '#{@block.name}'?&quot;, 
   :id =&gt; &quot;delete_button&quot; %&gt;</diff>
      <filename>app/views/cms/blocks/_toolbar_for_member.html.erb</filename>
    </modified>
    <modified>
      <diff>@@ -11,12 +11,14 @@
         var match = this.id.match(/(.*)_(\d+)/) 
         var type = match[1]
         var id = match[2]
+        var editable = !$(this).hasClass(&quot;non-editable&quot;)
+        var publishable = !$(this).hasClass(&quot;non-publishable&quot;)
         $('table.data tbody tr').removeClass('selected')
         $(this).addClass('selected')
         $('#functions .button').addClass('disabled').attr('href','#')
         $('#add_button').removeClass('disabled').attr('href', '/cms/'+collectionName+'/new')
         $('#view_button').removeClass('disabled').attr('href', '/cms/'+collectionName+'/'+id)
-        $('#edit_button').removeClass('disabled').attr('href', '/cms/'+collectionName+'/'+id+'/edit')
+        if (editable) $('#edit_button').removeClass('disabled').attr('href', '/cms/'+collectionName+'/'+id+'/edit')
         &lt;% if content_type.model_class.versioned? %&gt;
           $('#revisions_button').removeClass('disabled').attr('href', '/cms/'+collectionName+'/'+id+'/versions')
         &lt;% else %&gt;
@@ -28,12 +30,14 @@
           $('#delete_button').addClass('disabled')
             .attr('title', $.trim(cannot_be_deleted_message.text()))
         } else {
-          $('#delete_button').removeClass('disabled')
-            .attr('href', '/cms/'+collectionName+'/'+id)  
-            .attr('title', 'Are You Sure You Want To Delete This Record?')
+          if (publishable) {
+            $('#delete_button').removeClass('disabled')
+              .attr('href', '/cms/'+collectionName+'/'+id)  
+              .attr('title', 'Are You Sure You Want To Delete This Record?')
+          }
         }        
         &lt;% able_to? :publish_content do -%&gt;
-          if($(this).hasClass('draft')) {
+          if($(this).hasClass('draft') &amp;&amp; publishable) {
             $('#publish_button').removeClass('disabled').attr('href', '/cms/'+collectionName+'/'+id+'/publish?_redirect_to='+location.href)        
           }
         &lt;% end %&gt;  
@@ -85,7 +89,7 @@
        col_ct += 1 if content_type.model_class.publishable? %&gt;
     &lt;% @blocks.each do |b| %&gt;
       &lt;% block = b.class.versioned? ? b.as_of_draft_version : b %&gt;
-      &lt;tr id=&quot;&lt;%= block.class.name.underscore %&gt;_&lt;%= block.id %&gt;&quot; class=&quot;&lt;%= block.class.name.underscore %&gt; &lt;%= block.class.publishable? &amp;&amp; !block.published? ? 'draft' : 'published' %&gt;&quot;&gt;
+      &lt;tr id=&quot;&lt;%= block.class.name.underscore %&gt;_&lt;%= block.id %&gt;&quot; class=&quot;&lt;%= block.class.name.underscore %&gt; &lt;%= block.class.publishable? &amp;&amp; !block.published? ? 'draft' : 'published' %&gt; &lt;%= 'non-editable' unless current_user.able_to_edit?(block) %&gt; &lt;%= 'non-publishable' unless current_user.able_to_publish?(block) %&gt;&quot;&gt;
 	&lt;td class=&quot;first&quot;&gt;&lt;/td&gt;
         &lt;% content_type.columns_for_index.each_with_index do |column, i| %&gt;
           &lt;td class=&quot;&lt;%= column[:label].gsub(' ', '').underscore %&gt;&quot;&gt;</diff>
      <filename>app/views/cms/blocks/index.html.erb</filename>
    </modified>
    <modified>
      <diff>@@ -12,9 +12,9 @@
   &lt;iframe src=&quot;&lt;%=h cms_toolbar_path(:page_id =&gt; @page.id, :page_version =&gt; @page.version, :mode =&gt; @mode, :page_toolbar =&gt; @show_page_toolbar ? 1 : 0) %&gt;&quot; width=&quot;100%&quot; height=&quot;&lt;%= @show_page_toolbar ? 159 : 100 %&gt;px&quot; frameborder=&quot;0&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; scrolling=&quot;no&quot; name=&quot;cms_toolbar&quot;&gt;&lt;/iframe&gt;
 &lt;% end %&gt;
 
-&lt;% @page.connectors.for_page_version(@page.version).each do |c| %&gt;
-  &lt;% content_for(c.container.to_sym) do %&gt;
-    &lt;%= render_connector c %&gt;
+&lt;% @page.connectables_by_connector.each_pair do |connector, connectable| %&gt;
+  &lt;% content_for(connector.container.to_sym) do %&gt;
+    &lt;%= render_connector_and_connectable(connector, connectable) %&gt;
   &lt;% end %&gt;
 &lt;% end %&gt; 
 </diff>
      <filename>app/views/cms/content/show.html.erb</filename>
    </modified>
    <modified>
      <diff>@@ -8,7 +8,7 @@
   &lt;table class=&quot;section_node &lt;%= node_type %&gt; &lt;%= &quot;movable&quot; if current_user.able_to?(:publish_content) %&gt;&quot; width=&quot;100%&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot;&gt;
     &lt;tr&gt;&lt;td colspan=&quot;4&quot; class=&quot;drop-before&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
     &lt;tr&lt;%= ' class=&quot;doubled&quot;' if access_icon &amp;&amp; hidden %&gt;&gt;
-      &lt;td id=&quot;&lt;%= node_type %&gt;_&lt;%= node.id %&gt;&quot; class=&quot;&lt;%= node_type == &quot;section&quot; &amp;&amp; node.root? ? 'root' : '' %&gt; &lt;%= node_type %&gt; node&quot;&gt;
+      &lt;td id=&quot;&lt;%= node_type %&gt;_&lt;%= node.id %&gt;&quot; class=&quot;&lt;%= node_type == &quot;section&quot; &amp;&amp; node.root? ? 'root' : '' %&gt; &lt;%= node_type %&gt; node &lt;%= 'non-editable' unless current_user.able_to_edit?(node) %&gt;&quot;&gt;
 	      &lt;%= icon %&gt;
 	      &lt;div&gt;&lt;%= h(node.name) %&gt;&lt;/div&gt;
       &lt;/td&gt;</diff>
      <filename>app/views/cms/section_nodes/_node.html.erb</filename>
    </modified>
    <modified>
      <diff>@@ -11,46 +11,48 @@
   &lt;/div&gt;
 &lt;/div&gt;
 
-&lt;div class=&quot;checkbox_group fields&quot; style=&quot;float: left; width: 100%&quot;&gt;
-  &lt;label&gt;Public Permissions&lt;/label&gt;
-  &lt;%= hidden_field_tag &quot;section[group_ids][]&quot;, &quot;&quot;, :id =&gt; nil %&gt;
-  &lt;div class=&quot;checkboxes&quot;&gt;
-    &lt;% for group in public_groups %&gt;
-    &lt;div class=&quot;checkbox_fields&quot;&gt;
-      &lt;%= check_box_tag &quot;section[group_ids][]&quot;, group.id,
-            @section.groups.include?(group), :class =&gt; &quot;public_group_ids&quot;, :id =&gt; &quot;public_group_ids_#{group.id}&quot;, :tabindex =&gt; next_tabindex %&gt;
-        &lt;label for=&quot;public_group_ids_&lt;%= group.id %&gt;&quot;&gt;&lt;%= group.name %&gt;&lt;/label&gt;
-    &lt;/div&gt;
-    &lt;% end %&gt;
-    &lt;div class=&quot;instructions&quot;&gt;Which &amp;ldquo;Public&amp;rdquo; groups can view pages in this section?&lt;/div&gt;
-    &lt;div class=&quot;check_uncheck&quot;&gt;
-      &lt;%= link_to_check_all 'input.public_group_ids' %&gt;, 
-      &lt;%= link_to_uncheck_all 'input.public_group_ids' %&gt;
+&lt;% able_to?(:administrate) do %&gt;
+  &lt;div class=&quot;checkbox_group fields&quot; style=&quot;float: left; width: 100%&quot;&gt;
+    &lt;label&gt;Public Permissions&lt;/label&gt;
+    &lt;%= hidden_field_tag &quot;section[group_ids][]&quot;, &quot;&quot;, :id =&gt; nil %&gt;
+    &lt;div class=&quot;checkboxes&quot;&gt;
+      &lt;% for group in public_groups %&gt;
+      &lt;div class=&quot;checkbox_fields&quot;&gt;
+        &lt;%= check_box_tag &quot;section[group_ids][]&quot;, group.id,
+              @section.groups.include?(group), :class =&gt; &quot;public_group_ids&quot;, :id =&gt; &quot;public_group_ids_#{group.id}&quot;, :tabindex =&gt; next_tabindex %&gt;
+          &lt;label for=&quot;public_group_ids_&lt;%= group.id %&gt;&quot;&gt;&lt;%= group.name %&gt;&lt;/label&gt;
+      &lt;/div&gt;
+      &lt;% end %&gt;
+      &lt;div class=&quot;instructions&quot;&gt;Which &amp;ldquo;Public&amp;rdquo; groups can view pages in this section?&lt;/div&gt;
+      &lt;div class=&quot;check_uncheck&quot;&gt;
+        &lt;%= link_to_check_all 'input.public_group_ids' %&gt;, 
+        &lt;%= link_to_uncheck_all 'input.public_group_ids' %&gt;
+      &lt;/div&gt;
     &lt;/div&gt;
   &lt;/div&gt;
-&lt;/div&gt;
 
-&lt;br clear=&quot;all&quot; /&gt;
+  &lt;br clear=&quot;all&quot; /&gt;
 
-&lt;div class=&quot;checkbox_group fields&quot; style=&quot;float: left; width: 100%&quot;&gt;
-  &lt;label&gt;CMS Permissions&lt;/label&gt;
-  &lt;%= hidden_field_tag &quot;section[group_ids][]&quot;, &quot;&quot;, :id =&gt; nil %&gt;
-  &lt;div class=&quot;checkboxes&quot;&gt;
-    &lt;% for group in cms_groups %&gt;
-    &lt;div class=&quot;checkbox_fields&quot;&gt;
-        &lt;%= check_box_tag &quot;section[group_ids][]&quot;, group.id,
-            @section.groups.include?(group), :class =&gt; &quot;cms_group_ids&quot;, :id =&gt; &quot;cms_group_ids_#{group.id}&quot;, :tabindex =&gt; next_tabindex %&gt;
-        &lt;label for=&quot;cms_group_ids_&lt;%= group.id %&gt;&quot;&gt;&lt;%= group.name %&gt;&lt;/label&gt;
-    &lt;/div&gt;
-    &lt;% end %&gt;
-    &lt;div class=&quot;instructions&quot;&gt;Which &amp;ldquo;CMS&amp;rdquo; groups can edit pages and content in this section?&lt;/div&gt;
-    &lt;div class=&quot;check_uncheck&quot;&gt;
-      &lt;%= link_to_check_all 'input.cms_group_ids' %&gt;, 
-      &lt;%= link_to_uncheck_all 'input.cms_group_ids' %&gt;
+  &lt;div class=&quot;checkbox_group fields&quot; style=&quot;float: left; width: 100%&quot;&gt;
+    &lt;label&gt;CMS Permissions&lt;/label&gt;
+    &lt;%= hidden_field_tag &quot;section[group_ids][]&quot;, &quot;&quot;, :id =&gt; nil %&gt;
+    &lt;div class=&quot;checkboxes&quot;&gt;
+      &lt;% for group in cms_groups %&gt;
+      &lt;div class=&quot;checkbox_fields&quot;&gt;
+          &lt;%= check_box_tag &quot;section[group_ids][]&quot;, group.id,
+              @section.groups.include?(group), :class =&gt; &quot;cms_group_ids&quot;, :id =&gt; &quot;cms_group_ids_#{group.id}&quot;, :tabindex =&gt; next_tabindex %&gt;
+          &lt;label for=&quot;cms_group_ids_&lt;%= group.id %&gt;&quot;&gt;&lt;%= group.name %&gt;&lt;/label&gt;
+      &lt;/div&gt;
+      &lt;% end %&gt;
+      &lt;div class=&quot;instructions&quot;&gt;Which &amp;ldquo;CMS&amp;rdquo; groups can edit pages and content in this section?&lt;/div&gt;
+      &lt;div class=&quot;check_uncheck&quot;&gt;
+        &lt;%= link_to_check_all 'input.cms_group_ids' %&gt;, 
+        &lt;%= link_to_uncheck_all 'input.cms_group_ids' %&gt;
+      &lt;/div&gt;
     &lt;/div&gt;
   &lt;/div&gt;
-&lt;/div&gt;
-&lt;br clear=&quot;all&quot; /&gt;
+  &lt;br clear=&quot;all&quot; /&gt;
+&lt;% end %&gt;
 
 &lt;div class=&quot;buttons&quot;&gt;
   &lt;%= lt_button_wrapper(f.submit(&quot;Save&quot;, :class =&gt; &quot;submit&quot;, :tabindex =&gt; next_tabindex)) %&gt;</diff>
      <filename>app/views/cms/sections/_form.html.erb</filename>
    </modified>
    <modified>
      <diff>@@ -1,10 +1,12 @@
 &lt;% @page_title = @toolbar_title = &quot;Set New Password&quot; %&gt;
-&lt;% content_for :toolbar_links do %&gt;
-  &lt;%= link_to(span_tag(&quot;List All&quot;), url_for(:controller =&gt; &quot;users&quot;, :action =&gt; &quot;index&quot;), :id =&gt; &quot;list_all_button&quot;, :class =&gt; &quot;button&quot;) %&gt;
-  &lt;%= link_to(span_tag(&quot;Edit User&quot;), url_for(:controller =&gt; &quot;users&quot;, :action =&gt; &quot;edit&quot;, :id =&gt; @user.id), :id =&gt; &quot;edit_user_button&quot;, :class =&gt; &quot;button&quot;) %&gt;
-&lt;% end %&gt;
+&lt;% able_to? :administrate do %&gt;
+  &lt;% content_for :toolbar_links do %&gt;
+    &lt;%= link_to(span_tag(&quot;List All&quot;), url_for(:controller =&gt; &quot;users&quot;, :action =&gt; &quot;index&quot;), :id =&gt; &quot;list_all_button&quot;, :class =&gt; &quot;button&quot;) %&gt;
+    &lt;%= link_to(span_tag(&quot;Edit User&quot;), url_for(:controller =&gt; &quot;users&quot;, :action =&gt; &quot;edit&quot;, :id =&gt; @user.id), :id =&gt; &quot;edit_user_button&quot;, :class =&gt; &quot;button&quot;) %&gt;
+  &lt;% end %&gt;
 
-&lt;%= content_for :functions, render(:partial =&gt; &quot;toolbar&quot;) %&gt;
+  &lt;%= content_for :functions, render(:partial =&gt; &quot;toolbar&quot;) %&gt;
+&lt;% end %&gt;
 
 &lt;% content_for :html_head do %&gt;
   &lt;%= stylesheet_link_tag('cms/form_layout') %&gt;
@@ -16,4 +18,4 @@
   &lt;div class=&quot;buttons&quot;&gt;
     &lt;%= lt_button_wrapper(f.submit(&quot;Save&quot;, :class =&gt; &quot;submit&quot;)) %&gt;
   &lt;/div&gt;
-&lt;% end %&gt;
\ No newline at end of file
+&lt;% end %&gt;</diff>
      <filename>app/views/cms/users/change_password.html.erb</filename>
    </modified>
    <modified>
      <diff>@@ -26,7 +26,7 @@
     &lt;% @users.each do |user|%&gt;
     &lt;tr&gt;
         &lt;td class=&quot;first&quot;&gt;&lt;/td&gt;
-        &lt;td&gt;&lt;div class=&quot;dividers&quot;&gt;&lt;%= link_to &quot;#{user.first_name} #{user.last_name}&quot;, [:cms, user] %&gt;&lt;/div&gt;&lt;/td&gt;
+        &lt;td&gt;&lt;div class=&quot;dividers&quot;&gt;&lt;%= link_to &quot;#{user.first_name} #{user.last_name}&quot;, edit_cms_user_path(user) %&gt;&lt;/div&gt;&lt;/td&gt;
 	      &lt;td&gt;&lt;div class=&quot;dividers&quot;&gt;&lt;%= link_to user.email, &quot;mailto:#{user.email}&quot; %&gt;&lt;/div&gt;&lt;/td&gt;
         &lt;td&gt;
             &lt;div class=&quot;dividers&quot;&gt;</diff>
      <filename>app/views/cms/users/index.html.erb</filename>
    </modified>
    <modified>
      <diff>@@ -8,7 +8,7 @@
         &lt;% able_to?(:administrate) do %&gt;&lt;li&gt;&lt;%= link_to image_tag(&quot;/images/cms/nav_admin#{'_on' if tab == :administration}.gif&quot;, :id =&gt; 'nav_admin_img'), cms_administration_path, :target =&gt; &quot;_top&quot; %&gt;&lt;/li&gt;&lt;% end %&gt;
       &lt;/ul&gt;
       &lt;ul id=&quot;userlinks&quot;&gt;
-	&lt;li id=&quot;user_info&quot;&gt;&lt;%= image_tag &quot;cms/icons/user.png&quot; %&gt;&lt;span&gt;&lt;%= current_user.full_name %&gt;&lt;/span&gt;&lt;/li&gt;
+	&lt;li id=&quot;user_info&quot;&gt;&lt;a href=&quot;&lt;%= current_user.able_to?(:administrate) ? edit_cms_user_path(current_user) : cms_user_path(current_user) %&gt;&quot; target=&quot;_top&quot;&gt;&lt;%= image_tag &quot;cms/icons/user.png&quot; %&gt; &lt;%= current_user.full_name %&gt;&lt;/a&gt;&lt;/li&gt;
       	&lt;li&gt;&lt;%= link_to &quot;Logout&quot;, cms_logout_path, :class =&gt; &quot;http_delete&quot;, :target =&gt; &quot;_top&quot; %&gt;&lt;/li&gt;
       &lt;/ul&gt;
       &lt;% flash_class, flash_message = flash.to_a.first %&gt;</diff>
      <filename>app/views/layouts/_cms_toolbar.html.erb</filename>
    </modified>
    <modified>
      <diff>@@ -50,30 +50,30 @@
   &lt;%= link_to &quot;&lt;span&gt;Publish&lt;/span&gt;&quot;, 
     @page.live? ? '#' : publish_cms_page_path(@page), 
     :id =&gt; &quot;publish_button&quot;,
-    :class =&gt; &quot;http_put button#{' disabled' if !current_user.able_to?(:publish_content) || @page.version != @page.draft.version || @page.live?} left&quot;,
+    :class =&gt; &quot;http_put button#{' disabled' if !current_user.able_to?(:publish_content) || !current_user.able_to_edit?(@page) || @page.version != @page.draft.version || @page.live?} left&quot;,
     :target =&gt; &quot;_top&quot; %&gt;
     
   &lt;%= link_to &quot;&lt;span&gt;Assign&lt;/span&gt;&quot;, 
     new_cms_page_task_path(@page), 
     :id =&gt; &quot;assign_button&quot;,
-    :class =&gt; &quot;button#{ ' disabled' if @page.assigned_to == current_user} middle&quot;,
+    :class =&gt; &quot;button#{ ' disabled' if @page.assigned_to == current_user || !current_user.able_to_edit?(@page) } middle&quot;,
     :target =&gt; &quot;_top&quot; %&gt;
     
   &lt;%= link_to &quot;&lt;span&gt;Complete Task&lt;/span&gt;&quot;, 
     @page.current_task ? complete_cms_task_path(@page.current_task) : '#', 
     :id =&gt; &quot;complete_task_button&quot;,
-    :class =&gt; &quot;http_put button#{ ' disabled' unless @page.assigned_to == current_user} right&quot;, 
+    :class =&gt; &quot;http_put button#{ ' disabled' if @page.assigned_to != current_user || !current_user.able_to_edit?(@page) } right&quot;, 
     :target =&gt; &quot;_top&quot; %&gt;
 
   &lt;%= link_to &quot;&lt;span&gt;Edit Properties&lt;/span&gt;&quot;, 
     [:edit, :cms, @page], 
     :id =&gt; &quot;edit_properties_button&quot;,
-    :class =&gt; &quot;spacer button&quot;, 
+    :class =&gt; &quot;spacer button#{ ' disabled' unless current_user.able_to_edit?(@page) }&quot;, 
     :target =&gt; &quot;_top&quot; %&gt;
   
   &lt;%= link_to &quot;&lt;span&gt;List Versions&lt;/span&gt;&quot;, 
     versions_cms_page_path(@page), 
-    :class =&gt; &quot;spacer button&quot;,
+    :class =&gt; &quot;spacer button#{ ' disabled' unless current_user.able_to_edit?(@page) }&quot;,
     :target =&gt; &quot;_top&quot; %&gt;
     
   &lt;% able_to? :publish_content do %&gt;
@@ -83,7 +83,7 @@
         :id =&gt; &quot;delete_button&quot;,
         :title =&gt; &quot;Are you sure you want to delete '#{@page.name}'?&quot;, 
         :target =&gt; &quot;_top&quot;, 
-        :class =&gt; &quot;spacer button confirm_with_title http_delete&quot; %&gt;
+        :class =&gt; &quot;spacer button confirm_with_title http_delete#{ ' disabled' unless current_user.able_to_publish?(@page) }&quot; %&gt;
     &lt;% else %&gt;
       &lt;%= link_to &quot;&lt;span&gt;Revert to this Version&lt;/span&gt;&quot;,
         revert_to_cms_page_path(@page, @page.version), 
@@ -98,7 +98,7 @@
         &lt;div class=&quot;visual_editor_label&quot;&gt;Visual Editor:&lt;/div&gt;
         &lt;div class=&quot;visual_editor_value_container&quot;&gt;
           &lt;% if @mode == &quot;edit&quot; %&gt;
-	    &lt;div&gt;&lt;span id=&quot;visual_editor_state&quot;&gt;ON&lt;/span&gt;&lt;/div&gt;
+	    &lt;div&gt;&lt;span id=&quot;visual_editor_state&quot;&lt;%= ' title=&quot;You don\'t have permission to edit this page&quot;' unless current_user.able_to_edit?(@page) %&gt;&gt;ON&lt;%= '*' unless current_user.able_to_edit?(@page) %&gt;&lt;/span&gt;&lt;/div&gt;
           &lt;% else %&gt;
 	    &lt;div&gt;&lt;span id=&quot;visual_editor_state&quot;&gt;OFF&lt;/span&gt;&lt;/div&gt;
           &lt;% end %&gt;</diff>
      <filename>app/views/layouts/_page_toolbar.html.erb</filename>
    </modified>
    <modified>
      <diff>@@ -7,18 +7,29 @@
   &lt;div id=&quot;wrapper&quot;&gt;
     &lt;%= render_cms_toolbar(:administration) %&gt;
     &lt;div id=&quot;main&quot;&gt;
-    &lt;div class=&quot;top_cap_menu&quot;&gt;&lt;/div&gt;
-      &lt;div id=&quot;menu&quot;&gt;
-        &lt;%= render :partial =&gt; 'cms/shared/admin_sidebar' %&gt;                      
-      &lt;/div&gt;
-      &lt;div id=&quot;contentwrap&quot;&gt;
+    
+    &lt;% if current_user.able_to?(:administrate) %&gt;
+      &lt;div class=&quot;top_cap_menu&quot;&gt;&lt;/div&gt;
+        &lt;div id=&quot;menu&quot;&gt;
+          &lt;%= render :partial =&gt; 'cms/shared/admin_sidebar' %&gt;                      
+        &lt;/div&gt;
+        &lt;div id=&quot;contentwrap&quot;&gt;
+    &lt;% else %&gt;
+      &lt;div class=&quot;top_cap&quot;&gt;&lt;/div&gt;
+      &lt;div id=&quot;contentwrapbig&quot;&gt;
+    &lt;% end %&gt;
+    
 	&lt;div id=&quot;functions&quot;&gt;
 	  &lt;h1&gt;&lt;%= @toolbar_title %&gt;&lt;/h1&gt;
 	  &lt;%= yield :functions %&gt;
 
 	&lt;/div&gt;
 	&lt;br clear=&quot;all&quot; /&gt;
-	&lt;div class=&quot;top_cap_content&quot;&gt;&lt;/div&gt;
+	
+	&lt;% able_to?(:administrate) do %&gt;
+	  &lt;div class=&quot;top_cap_content&quot;&gt;&lt;/div&gt;
+	&lt;% end %&gt;
+	
       &lt;div id=&quot;content&quot;&gt;
 	&lt;div class=&quot;pad&quot;&gt;
           &lt;%= yield %&gt;
@@ -26,7 +37,13 @@
       &lt;/div&gt;
     &lt;/div&gt;
     &lt;br clear=&quot;all&quot; /&gt;
-    &lt;div class=&quot;bottom_cap_content&quot;&gt;&lt;/div&gt;
+    
+    &lt;% if current_user.able_to?(:administrate) %&gt;
+      &lt;div class=&quot;bottom_cap_content&quot;&gt;&lt;/div&gt;
+    &lt;% else %&gt;
+      &lt;div class=&quot;bottom_cap&quot;&gt;&lt;/div&gt;
+    &lt;% end %&gt;
+    
     &lt;%= render :partial =&gt; 'layouts/cms/footer' %&gt;
   &lt;/div&gt;
 &lt;/div&gt;</diff>
      <filename>app/views/layouts/cms/administration.html.erb</filename>
    </modified>
    <modified>
      <diff>@@ -5,11 +5,11 @@
 
 Gem::Specification.new do |s|
   s.name = %q{browsercms}
-  s.version = &quot;3.0.2&quot;
+  s.version = &quot;3.0.3&quot;
 
   s.required_rubygems_version = Gem::Requirement.new(&quot;&gt;= 0&quot;) if s.respond_to? :required_rubygems_version=
   s.authors = [&quot;BrowserMedia&quot;]
-  s.date = %q{2009-10-06}
+  s.date = %q{2009-10-28}
   s.email = %q{github@browsermedia.com}
   s.extra_rdoc_files = [
     &quot;LICENSE.txt&quot;,
@@ -155,6 +155,8 @@ Gem::Specification.new do |s|
      &quot;app/views/cms/links/destroy.js.rjs&quot;,
      &quot;app/views/cms/links/edit.html.erb&quot;,
      &quot;app/views/cms/links/new.html.erb&quot;,
+     &quot;app/views/cms/menus/_menu.html.erb&quot;,
+     &quot;app/views/cms/menus/_menu_item.html.erb&quot;,
      &quot;app/views/cms/page_routes/_form.html.erb&quot;,
      &quot;app/views/cms/page_routes/edit.html.erb&quot;,
      &quot;app/views/cms/page_routes/index.html.erb&quot;,
@@ -191,6 +193,7 @@ Gem::Specification.new do |s|
      &quot;app/views/cms/shared/_pagination.html.erb&quot;,
      &quot;app/views/cms/shared/_version_conflict_diff.html.erb&quot;,
      &quot;app/views/cms/shared/_version_conflict_error.html.erb&quot;,
+     &quot;app/views/cms/shared/access_denied.html.erb&quot;,
      &quot;app/views/cms/shared/error.html.erb&quot;,
      &quot;app/views/cms/tags/_form.html.erb&quot;,
      &quot;app/views/cms/tags/render.html.erb&quot;,
@@ -205,6 +208,7 @@ Gem::Specification.new do |s|
      &quot;app/views/cms/users/edit.html.erb&quot;,
      &quot;app/views/cms/users/index.html.erb&quot;,
      &quot;app/views/cms/users/new.html.erb&quot;,
+     &quot;app/views/cms/users/show.html.erb&quot;,
      &quot;app/views/layouts/_cms_toolbar.html.erb&quot;,
      &quot;app/views/layouts/_page_toolbar.html.erb&quot;,
      &quot;app/views/layouts/application.html.erb&quot;,
@@ -1228,12 +1232,11 @@ Gem::Specification.new do |s|
      &quot;templates/demo.rb&quot;,
      &quot;templates/module.rb&quot;
   ]
-  s.has_rdoc = true
   s.homepage = %q{http://www.browsercms.org}
   s.rdoc_options = [&quot;--charset=UTF-8&quot;]
   s.require_paths = [&quot;lib&quot;]
   s.rubyforge_project = %q{browsercms}
-  s.rubygems_version = %q{1.3.1}
+  s.rubygems_version = %q{1.3.5}
   s.summary = %q{BrowserCMS is a general purpose, open source Web Content Management System (CMS), written in Ruby on Rails.}
   s.test_files = [
     &quot;test/functional/cms/file_blocks_controller_test.rb&quot;,
@@ -1248,6 +1251,7 @@ Gem::Specification.new do |s|
      &quot;test/functional/cms/links_controller_test.rb&quot;,
      &quot;test/functional/cms/dynamic_views_controller_test.rb&quot;,
      &quot;test/functional/cms/categories_controller_test.rb&quot;,
+     &quot;test/functional/cms/content_block_controller_test.rb&quot;,
      &quot;test/functional/cms/pages_controller_test.rb&quot;,
      &quot;test/functional/cms/connectors_controller_test.rb&quot;,
      &quot;test/functional/cms/home_controller_test.rb&quot;,
@@ -1303,7 +1307,7 @@ Gem::Specification.new do |s|
 
   if s.respond_to? :specification_version then
     current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
-    s.specification_version = 2
+    s.specification_version = 3
 
     if Gem::Version.new(Gem::RubyGemsVersion) &gt;= Gem::Version.new('1.2.0') then
     else</diff>
      <filename>browsercms.gemspec</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,6 @@
 # Settings specified here will take precedence over those in config/environment.rb
+config.gem &quot;mocha&quot;
+config.gem &quot;redgreen&quot;
 config.gem &quot;thoughtbot-factory_girl&quot;, :lib =&gt; &quot;factory_girl&quot;, :source =&gt; &quot;http://gems.github.com&quot;
 
 # The test environment is used exclusively to run your application's</diff>
      <filename>config/environments/test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,16 @@
-ActionController::Routing::Routes.draw do |map|
-  map.connect &quot;/__test__&quot;, :controller =&gt; &quot;cms/content&quot;, :action =&gt; &quot;show_page_route&quot;
-  map.routes_for_browser_cms
-end
\ No newline at end of file
+if RAILS_ENV == &quot;test&quot;
+  class ContentBlock
+    def self.versioned?; true; end
+    def self.publishable?; true; end
+    def self.connectable?; true; end
+    def self.searchable?; false; end
+  end
+end      
+
+# Only load these routes if this is the &quot;root&quot; application
+if RAILS_ROOT == File.expand_path(File.dirname(__FILE__) + &quot;/..&quot;)
+  ActionController::Routing::Routes.draw do |map|
+    map.connect &quot;/__test__&quot;, :controller =&gt; &quot;cms/content&quot;, :action =&gt; &quot;show_page_route&quot;
+    map.routes_for_browser_cms
+  end
+end</diff>
      <filename>config/routes.rb</filename>
    </modified>
    <modified>
      <diff>@@ -61,7 +61,7 @@ module ActsAsList
 
         #{scope_condition_method}
 
-        before_destroy :remove_from_list
+        before_destroy :remove_from_list_without_saving
         before_create  :add_to_list_bottom
       EOV
     end
@@ -118,13 +118,17 @@ module ActsAsList
     end
 
     # Removes the item from the list.
-    def remove_from_list
+    def remove_from_list(save = true)
       if in_list?
         decrement_positions_on_lower_items
-        update_attribute position_column, nil
+        update_attribute(position_column, nil) if save
       end
     end
 
+    def remove_from_list_without_saving
+      self.remove_from_list(false)
+    end
+
     # Increase the position of this item without adjusting the rest of the list.
     def increment_position
       return unless in_list?
@@ -249,4 +253,4 @@ module ActsAsList
         self.update_attribute(position_column, position)
       end
   end
-end
\ No newline at end of file
+end</diff>
      <filename>lib/acts_as_list.rb</filename>
    </modified>
    <modified>
      <diff>@@ -28,4 +28,4 @@ module Cms
       end
     end
   end
-end
\ No newline at end of file
+end</diff>
      <filename>lib/cms/acts/content_block.rb</filename>
    </modified>
    <modified>
      <diff>@@ -118,14 +118,14 @@ module Cms
 
         # Override this method if you would like to override the way the section is set
         def set_attachment_section
-          if new_record? &amp;&amp; !attachment_file.blank?
+          if !attachment_file.blank?
             attachment.section = Section.root.first
           end
         end
 
         # Override this method if you would like to override the way file_path is set
         def set_attachment_file_path
-          if new_record? &amp;&amp; !attachment_file.blank?
+          if !attachment_file.blank?
             attachment.file_path = &quot;/attachments/#{File.basename(attachment_file.original_filename).to_s.downcase}&quot;
           end
         end
@@ -181,4 +181,4 @@ module Cms
       end
     end
   end
-end
\ No newline at end of file
+end</diff>
      <filename>lib/cms/behaviors/attaching.rb</filename>
    </modified>
    <modified>
      <diff>@@ -23,7 +23,18 @@ module Cms
           after_save :publish_for_non_versioned
         
           named_scope :published, :conditions =&gt; {:published =&gt; true}
-          named_scope :unpublished, :conditions =&gt; {:published =&gt; false}        
+          named_scope :unpublished, lambda {
+            if versioned?
+              { :joins =&gt; :versions,
+                :conditions =&gt;
+                  &quot;#{connection.quote_table_name(version_table_name)}.#{connection.quote_column_name('version')} &gt; &quot; +
+                  &quot;#{connection.quote_table_name(table_name)}.#{connection.quote_column_name('version')}&quot;,
+                :select =&gt; &quot;distinct #{connection.quote_table_name(table_name)}.*&quot; }
+            else
+              { :conditions =&gt; { :published =&gt; false } }
+            end
+          }
+
         end
       end
       module ClassMethods</diff>
      <filename>lib/cms/behaviors/publishing.rb</filename>
    </modified>
    <modified>
      <diff>@@ -82,7 +82,7 @@ module Cms
   
     end
     module InstanceMethods
-      def perform_render(controller)
+      def prepare_to_render(controller)
         # Give this renderable a reference to the controller
         @controller = controller
 
@@ -90,12 +90,21 @@ module Cms
 
         # This gives the view a reference to this object
         instance_variable_set(self.class.instance_variable_name_for_view, self)
-
+    
         # This is like a controller action
         # We will call it if you have defined a render method
         # but if you haven't we won't
         render if respond_to?(:render)
+      end
     
+      def perform_render(controller)
+        return &quot;Exception: #{@render_exception}&quot; if @render_exception
+        unless @controller
+          # We haven't prepared to render. This should only happen when logged in, as we don't want
+          # errors to bubble up and prevent the page being edited in that case.
+          prepare_to_render(controller)
+        end
+        
         # Create, Instantiate and Initialize the view
         view_class  = Class.new(ActionView::Base)      
         action_view = view_class.new(@controller.view_paths, {}, @controller)
@@ -108,7 +117,7 @@ module Cms
         
         # We want content_for to be called on the controller's view, not this inner view
         def action_view.content_for(name, content=nil, &amp;block)
-          controller.instance_variable_get(&quot;@template&quot;).content_for(name, content, &amp;block)
+          @controller.instance_variable_get(&quot;@template&quot;).content_for(name, content, &amp;block)
         end
         
         # Copy instance variables from this renderable object to it's view
@@ -122,6 +131,10 @@ module Cms
         end
       end
   
+      def render_exception=(exception)
+        @render_exception = exception
+      end
+
       protected
         def copy_instance_variables_from_controller!
           if @controller.respond_to?(:instance_variables_for_rendering)
@@ -141,4 +154,4 @@ module Cms
       
     end
   end
-end
\ No newline at end of file
+end</diff>
      <filename>lib/cms/behaviors/rendering.rb</filename>
    </modified>
    <modified>
      <diff>@@ -110,7 +110,7 @@ module Cms
         def save(perform_validations=true)
           transaction do
             #logger.info &quot;..... Calling valid?&quot;
-            return false unless valid?            
+            return false unless !perform_validations || valid?            
             
             if changed?
               #logger.info &quot;..... Changes =&gt; #{changes.inspect}&quot;
@@ -172,7 +172,7 @@ module Cms
         end
 
         def save!(perform_validations=true)
-          save || raise(ActiveRecord::RecordNotSaved.new(errors.full_messages))
+          save(perform_validations) || raise(ActiveRecord::RecordNotSaved.new(errors.full_messages))
         end
 
         def draft</diff>
      <filename>lib/cms/behaviors/versioning.rb</filename>
    </modified>
    <modified>
      <diff>@@ -119,6 +119,10 @@ module Cms::Routes
         :enable =&gt; :put
       }
       
+      if RAILS_ENV == &quot;test&quot; &amp;&amp; File.expand_path(RAILS_ROOT) == File.expand_path(File.dirname(__FILE__) + &quot;/../..&quot;)
+        cms.content_blocks :content_block
+      end
+      
     end
 
     if PageRoute.table_exists?</diff>
      <filename>lib/cms/routes.rb</filename>
    </modified>
    <modified>
      <diff>@@ -9,24 +9,6 @@ end
 
 namespace :cms do
   
-  desc &quot;DEPRECATED&quot;
-  task :install do
-    puts &quot;This task has been deprecated, please use 'rake install' instead&quot;
-  end
-  
-  desc &quot;Bumps the build number in lib/cms/init.rb&quot;
-  task :bump_build_number do
-    init_file = Rails.root.join(&quot;lib/cms/init.rb&quot;)
-    s = File.read(init_file)
-    open(init_file, 'w') do |f| 
-      f &lt;&lt; s.sub(/def build_number; (\d+) end/) do |s|
-        new_build_number = $1.to_i + 1
-        puts &quot;Build number bumped to #{new_build_number}&quot;
-        &quot;def build_number; #{new_build_number} end&quot;
-      end
-    end
-  end
-  
   desc &quot;Generate guides for the CMS&quot;
   task :guides do
     require 'rubygems'</diff>
      <filename>lib/tasks/cms.rake</filename>
    </modified>
    <modified>
      <diff>@@ -187,15 +187,26 @@ jQuery(function($){
     }
     
     var enableButtonsForNode = function(node) {
-	var id = getId(node.id, /(section|page|link)_/)
-	if($(node).hasClass('section')) {
-	    enableButtonsForSection(id)
-	} else if($(node).hasClass('page')) {
-	    enableButtonsForPage(id)
-	} else if($(node).hasClass('link')) {
-	    enableButtonsForLink(id)
-	}  
-    }
+	var id = getId(node.id, /(section|page|link)_/);
+	if(!$(node).is(&quot;.non-editable&quot;)) {
+		if($(node).hasClass('section')) {
+		    enableButtonsForSection(id);
+		} else if($(node).hasClass('page')) {
+		    enableButtonsForPage(id);
+		} else if($(node).hasClass('link')) {
+		    enableButtonsForLink(id);
+		}  
+	}else if($(node).hasClass('page')) {
+	    $('#edit-button')
+	        .html('&lt;span&gt;View Page&lt;/span&gt;')
+		.removeClass('disabled')
+		.attr('href','/cms/pages/'+id)
+		.unbind('click')
+		.click(function(){return true});
+	} else {
+	    $('#properties-button').attr('href','/cms/sitemap');
+	}
+    };
     
     var enableButtonsForSection = function(id) {
 	$('#properties-button')
@@ -253,6 +264,7 @@ jQuery(function($){
     
     var enableButtonsForPage = function(id) {
 	$('#edit-button')
+	.html('&lt;span&gt;Edit Page&lt;/span&gt;')
 	.removeClass('disabled')
 	.attr('href','/cms/pages/'+id)
 	.unbind('click')</diff>
      <filename>public/javascripts/cms/sitemap.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,6 +1,6 @@
 @import url(/stylesheets/cms/selectbox.css);
 
-form {
+form, .faux_form {
 font-size: 10pt;
 font-family: &quot;Trebuchet MS&quot;, Helvetica, Verdana, Arial, sans-serif;
 color:#485561;
@@ -21,6 +21,19 @@ padding: 10px 0;
 background: url(/images/cms/dashed.gif) repeat-x 100% 100%;
 }
 
+/* Fake forms */
+.faux_form .fields {
+padding: 22px 0 10px 0;
+font-size: 12px;
+overflow: hidden;
+}
+.faux_form .fields .label {
+padding: 0 0 12px 0;
+float: left;
+width: 140px;
+font-weight: bold;
+}
+
 /* LABELS */
 .text_fields label,
 .textarea_fields label,
@@ -39,7 +52,8 @@ font-size: 12px;
 .select_fields label,
 .text_editor_fields label,
 .file_fields label,
-.checkboxes label
+.checkboxes label,
+.faux_label
  {
 font-weight: bold;
 font-size: 12px;</diff>
      <filename>public/stylesheets/cms/form_layout.css</filename>
    </modified>
    <modified>
      <diff>@@ -70,13 +70,14 @@ color: #666;
 font-weight: bold;
 }
 
-#nav ul#userlinks li a,	#nav ul#userlinks li span {
-padding: 8px 19px 11px 19px;
+#nav ul#userlinks li a {
+padding: 4px 19px 11px 19px;
 background: url(/images/cms/usercontrols_bg_cap.png) no-repeat 100% 0;
 color: #666;
 display: block;
 float: left;
 text-decoration: none;
+line-height: 18px;
 }
 
 #nav ul#userlinks li span {
@@ -88,7 +89,7 @@ padding: 9px 10px;
 }
 #nav ul#userlinks li#user_info img {
 float:left;
-margin: 4px 0 0 5px;
+margin: 0 5px 0 0;
 }
 
 #nav .cmssearch {</diff>
      <filename>public/stylesheets/cms/nav.css</filename>
    </modified>
    <modified>
      <diff>@@ -2,26 +2,26 @@ require File.join(File.dirname(__FILE__), '/../../test_helper')
 
 class Cms::ContentControllerTest &lt; ActionController::TestCase
   include Cms::ControllerTestHelper
-  
+
   def test_show_home_page
     get :show
     assert_response :success
     assert_select &quot;title&quot;, &quot;Home&quot;
   end
-  
+
   def test_show_another_page
     @page = Factory(:page, :section =&gt; root_section, :path =&gt; &quot;/about&quot;, :name =&gt; &quot;Test About&quot;, :template_file_name =&gt; &quot;default.html.erb&quot;, :publish_on_save =&gt; true)
     get :show, :path =&gt; [&quot;about&quot;]
     assert_select &quot;title&quot;, &quot;Test About&quot;
   end
-  
+
   def test_page_not_found_to_guest
     get :show, :path =&gt; [&quot;foo&quot;]
     assert_response :not_found
     assert_select &quot;title&quot;, &quot;Not Found&quot;
     assert_select &quot;h1&quot;, &quot;Page Not Found&quot;
   end
-  
+
   def test_page_not_found_to_cms_admin
     login_as_cms_admin
     get :show, :path =&gt; [&quot;foo&quot;]
@@ -29,25 +29,25 @@ class Cms::ContentControllerTest &lt; ActionController::TestCase
     assert_select &quot;title&quot;, &quot;Page Not Found&quot;
     assert_select &quot;h2&quot;, &quot;There is no page at /foo&quot;
   end
-  
+
   def test_show_protected_page_to_guest
     create_protected_page
-    
+
     get :show, :path =&gt; [&quot;secret&quot;]
     assert_response :forbidden
     assert_select &quot;title&quot;, &quot;Access Denied&quot;
   end
-  
+
   def test_show_protected_page_to_privileged_user
     create_protected_page
-    
+
     login_as @privileged_user
-    
+
     get :show, :path =&gt; [&quot;secret&quot;]
     assert_response :success
     assert_select &quot;title&quot;, &quot;Shhh... It's a Secret&quot;
   end
-  
+
   def test_show_archived_page_to_guest
     create_archived_page
 
@@ -67,14 +67,14 @@ class Cms::ContentControllerTest &lt; ActionController::TestCase
 
   def test_show_file
     create_file
-    
+
     get :show, :path =&gt; [&quot;test.txt&quot;]
-    
+
     assert_response :success
     assert_equal &quot;text/plain&quot;, @response.content_type
     assert_equal &quot;This is a test&quot;, streaming_file_contents
   end
-  
+
   def test_show_archived_file
     create_file
 
@@ -82,46 +82,46 @@ class Cms::ContentControllerTest &lt; ActionController::TestCase
     @file_block.update_attributes(:archived =&gt; true, :publish_on_save =&gt; true)
     reset(:file_block)
     assert @file_block.attachment.archived?
-    
+
     get :show, :path =&gt; [&quot;test.txt&quot;]
-    
+
     assert_response :not_found
     assert_select &quot;title&quot;, &quot;Not Found&quot;
   end
-  
+
   def test_show_protected_file_to_guest
     create_protected_file
-    
+
     get :show, :path =&gt; [&quot;test.txt&quot;]
-    
+
     assert_response :forbidden
     assert_select &quot;title&quot;, &quot;Access Denied&quot;
   end
-  
+
   def test_show_protected_file_to_privileged_user
     create_protected_file
     login_as @privileged_user
-    
+
     get :show, :path =&gt; [&quot;test.txt&quot;]
-    
+
     assert_response :success
     assert_equal &quot;text/plain&quot;, @response.content_type
     assert_equal &quot;This is a test&quot;, streaming_file_contents
   end
-  
+
   def test_show_page_route
     @page_template = Factory(:page_template, :name =&gt; &quot;test_show_page_route&quot;)
-    @page = Factory(:page, 
-      :section =&gt; root_section, 
+    @page = Factory(:page,
+      :section =&gt; root_section,
       :template_file_name =&gt; &quot;test_show_page_route.html.erb&quot;)
-    @portlet = DynamicPortlet.create!(:name =&gt; &quot;Test&quot;, 
+    @portlet = DynamicPortlet.create!(:name =&gt; &quot;Test&quot;,
       :template =&gt; &quot;&lt;h1&gt;&lt;%= @foo %&gt;&lt;/h1&gt;&quot;,
       :connect_to_page_id =&gt; @page.id, :connect_to_container =&gt; &quot;main&quot;)
     @page_route = @page.page_routes.create(:pattern =&gt; &quot;/foo&quot;, :code =&gt; &quot;@foo = params[:foo]&quot;)
 
     reset(:page)
     @page.publish!
-    
+
     get :show_page_route, :foo =&gt; &quot;42&quot;, :_page_route_id =&gt; @page_route.id
     assert_response :success
     assert_select &quot;h1&quot;, &quot;42&quot;
@@ -137,10 +137,10 @@ class Cms::ContentControllerTest &lt; ActionController::TestCase
   def test_show_draft_page_with_content_as_editor
     login_as_cms_admin
     create_page_with_content
-    
+
     @block.update_attributes(:content =&gt; &quot;&lt;h3&gt;I've been edited&lt;/h3&gt;&quot;)
     reset(:page, :block)
-    
+
     get :show, :path =&gt; [&quot;page_with_content&quot;]
     assert_response :success
     assert_select &quot;h3&quot;, &quot;I've been edited&quot;
@@ -154,42 +154,42 @@ class Cms::ContentControllerTest &lt; ActionController::TestCase
       @secret_group = Factory(:group, :name =&gt; &quot;Secret&quot;)
       @secret_group.sections &lt;&lt; @protected_section
       @privileged_user = Factory(:user, :login =&gt; &quot;privileged&quot;)
-      @privileged_user.groups &lt;&lt; @secret_group      
+      @privileged_user.groups &lt;&lt; @secret_group
     end
-  
+
     def create_protected_page
-      create_protected_user_section_group      
-      @page = Factory(:page, 
-        :section =&gt; @protected_section, 
-        :path =&gt; &quot;/secret&quot;, 
-        :name =&gt; &quot;Shhh... It's a Secret&quot;, 
-        :template_file_name =&gt; &quot;default.html.erb&quot;, 
+      create_protected_user_section_group
+      @page = Factory(:page,
+        :section =&gt; @protected_section,
+        :path =&gt; &quot;/secret&quot;,
+        :name =&gt; &quot;Shhh... It's a Secret&quot;,
+        :template_file_name =&gt; &quot;default.html.erb&quot;,
         :publish_on_save =&gt; true)
     end
-  
+
     def create_file
       @file = mock_file(:read =&gt; &quot;This is a test&quot;, :content_type =&gt; &quot;text/plain&quot;)
-      @file_block = Factory(:file_block, :attachment_section =&gt; root_section, :attachment_file =&gt; @file, :attachment_file_path =&gt; &quot;/test.txt&quot;, :publish_on_save =&gt; true)      
+      @file_block = Factory(:file_block, :attachment_section =&gt; root_section, :attachment_file =&gt; @file, :attachment_file_path =&gt; &quot;/test.txt&quot;, :publish_on_save =&gt; true)
     end
-    
+
     def create_protected_file
-      create_protected_user_section_group      
+      create_protected_user_section_group
       create_file
       reset(:file_block)
       @file_block.update_attributes(:attachment_section =&gt; @protected_section)
       reset(:file_block)
     end
-  
+
     def create_archived_page
-      @page = Factory(:page, 
-        :section =&gt; root_section, 
-        :path =&gt; &quot;/archived&quot;, 
-        :name =&gt; &quot;Archived&quot;, 
-        :archived =&gt; true, 
-        :template_file_name =&gt; &quot;default.html.erb&quot;, 
+      @page = Factory(:page,
+        :section =&gt; root_section,
+        :path =&gt; &quot;/archived&quot;,
+        :name =&gt; &quot;Archived&quot;,
+        :archived =&gt; true,
+        :template_file_name =&gt; &quot;default.html.erb&quot;,
         :publish_on_save =&gt; true)
     end
-  
+
     def create_page_with_content
       @page_template = Factory(:page_template, :name =&gt; &quot;testing_editting_content&quot;)
 
@@ -200,14 +200,14 @@ class Cms::ContentControllerTest &lt; ActionController::TestCase
 
       @block = HtmlBlock.create!(:name =&gt; &quot;Test&quot;,
         :content =&gt; &quot;&lt;h3&gt;TEST&lt;/h3&gt;&quot;,
-        :connect_to_page_id =&gt; @page.id, 
+        :connect_to_page_id =&gt; @page.id,
         :connect_to_container =&gt; &quot;main&quot;)
 
       reset(:page)
       @page.publish!
-      
+
     end
-  
+
 end
 
 # CMS Page Caching Enabled (Production Mode)
@@ -225,18 +225,18 @@ end
 class Cms::ContentCachingEnabledControllerTest &lt; ActionController::TestCase
   tests Cms::ContentController
   include Cms::ControllerTestHelper
-  
+
   def setup
     ActionController::Base.perform_caching = true
     @page = Factory(:page, :section =&gt; root_section, :name =&gt; &quot;Test Page&quot;, :path =&gt; &quot;/page&quot;, :publish_on_save =&gt; true)
     @registered_user = Factory(:user)
     @registered_user.groups &lt;&lt; Group.with_code(&quot;guest&quot;).first
   end
-  
+
   def teardown
     ActionController::Base.perform_caching = false
   end
-  
+
   def test_guest_user_views_page_on_public_site
     @request.host = &quot;mysite.com&quot;
     get :show, :path =&gt; [&quot;page&quot;]
@@ -253,9 +253,9 @@ class Cms::ContentCachingEnabledControllerTest &lt; ActionController::TestCase
   def test_registered_user_views_page_on_public_site
     login_as @registered_user
     @request.host = &quot;mysite.com&quot;
-    
+
     get :show, :path =&gt; [&quot;page&quot;]
-    
+
     assert_response :success
     assert_select &quot;title&quot;, &quot;Test Page&quot;
   end
@@ -263,19 +263,19 @@ class Cms::ContentCachingEnabledControllerTest &lt; ActionController::TestCase
   def test_registered_user_views_page_on_cms_site
     login_as @registered_user
     @request.host = &quot;cms.mysite.com&quot;
-    
+
     get :show, :path =&gt; [&quot;page&quot;]
-    
+
     assert_redirected_to &quot;http://mysite.com/page&quot;
   end
-  
+
   def test_cms_user_views_page_on_public_site
     login_as_cms_admin
     @request.session[:page_mode] = &quot;edit&quot;
     @request.host = &quot;mysite.com&quot;
-    
+
     get :show, :path =&gt; [&quot;page&quot;]
-    
+
     assert_response :success
     assert_select &quot;title&quot;, &quot;Test Page&quot;
     assert_select &quot;iframe&quot;, {:count =&gt; 0}
@@ -285,14 +285,14 @@ class Cms::ContentCachingEnabledControllerTest &lt; ActionController::TestCase
     login_as_cms_admin
     @request.session[:page_mode] = &quot;edit&quot;
     @request.host = &quot;cms.mysite.com&quot;
-    
+
     get :show, :path =&gt; [&quot;page&quot;]
-    
+
     assert_response :success
     assert_select &quot;title&quot;, &quot;Test Page&quot;
     assert_select &quot;iframe&quot;
-  end  
-  
+  end
+
 end
 
 # CMS Page Caching Disabled (Development Mode)
@@ -310,14 +310,14 @@ end
 class Cms::ContentCachingDisabledControllerTest &lt; ActionController::TestCase
   tests Cms::ContentController
   include Cms::ControllerTestHelper
-  
+
   def setup
     ActionController::Base.perform_caching = false
     @page = Factory(:page, :section =&gt; root_section, :name =&gt; &quot;Test Page&quot;, :path =&gt; &quot;/page&quot;, :publish_on_save =&gt; true)
     @registered_user = Factory(:user)
     @registered_user.groups &lt;&lt; Group.with_code(&quot;guest&quot;).first
   end
-  
+
   def test_guest_user_views_page_on_public_site
     @request.host = &quot;mysite.com&quot;
     get :show, :path =&gt; [&quot;page&quot;]
@@ -335,9 +335,9 @@ class Cms::ContentCachingDisabledControllerTest &lt; ActionController::TestCase
   def test_registered_user_views_page_on_public_site
     login_as @registered_user
     @request.host = &quot;mysite.com&quot;
-    
+
     get :show, :path =&gt; [&quot;page&quot;]
-    
+
     assert_response :success
     assert_select &quot;title&quot;, &quot;Test Page&quot;
   end
@@ -345,20 +345,20 @@ class Cms::ContentCachingDisabledControllerTest &lt; ActionController::TestCase
   def test_registered_user_views_page_on_cms_site
     login_as @registered_user
     @request.host = &quot;mysite.com&quot;
-    
+
     get :show, :path =&gt; [&quot;page&quot;]
-    
+
     assert_response :success
     assert_select &quot;title&quot;, &quot;Test Page&quot;
   end
-  
+
   def test_cms_user_views_page_on_public_site
     login_as_cms_admin
     @request.session[:page_mode] = &quot;edit&quot;
     @request.host = &quot;mysite.com&quot;
-    
+
     get :show, :path =&gt; [&quot;page&quot;]
-    
+
     assert_response :success
     assert_select &quot;title&quot;, &quot;Test Page&quot;
     assert_select &quot;iframe&quot;
@@ -368,12 +368,67 @@ class Cms::ContentCachingDisabledControllerTest &lt; ActionController::TestCase
     login_as_cms_admin
     @request.session[:page_mode] = &quot;edit&quot;
     @request.host = &quot;cms.mysite.com&quot;
-    
+
     get :show, :path =&gt; [&quot;page&quot;]
-    
+
     assert_response :success
     assert_select &quot;title&quot;, &quot;Test Page&quot;
     assert_select &quot;iframe&quot;
   end
-  
-end
\ No newline at end of file
+
+  def test_portlet_throw_access_denied_goes_to_access_denied_page
+    @page = Factory(:page, :section =&gt; root_section, :path =&gt; &quot;/about&quot;, :name =&gt; &quot;Test About&quot;, :template_file_name =&gt; &quot;default.html.erb&quot;, :publish_on_save =&gt; true)
+    @portlet_render = DynamicPortlet.create!(:name =&gt; &quot;Test&quot;, :connect_to_page_id =&gt; @page.id, :connect_to_container =&gt; &quot;main&quot;, :template =&gt; '&lt;p id=&quot;hi&quot;&gt;hello&lt;/p&gt;')
+    @portlet_raise_access_denied = DynamicPortlet.create!(:name =&gt; &quot;Test&quot;, :connect_to_page_id =&gt; @page.id, :connect_to_container =&gt; &quot;main&quot;, :code =&gt; 'raise Cms::Errors::AccessDenied')
+    reset(:page)
+
+    get :show, :path =&gt; [&quot;about&quot;]
+    assert_response :forbidden
+    assert_select &quot;title&quot;, &quot;Access Denied&quot;
+  end
+  def test_portlet_throw_not_found_goes_to_not_found_page
+    @page = Factory(:page, :section =&gt; root_section, :path =&gt; &quot;/about&quot;, :name =&gt; &quot;Test About&quot;, :template_file_name =&gt; &quot;default.html.erb&quot;, :publish_on_save =&gt; true)
+    @portlet_render = DynamicPortlet.create!(:name =&gt; &quot;Test&quot;, :connect_to_page_id =&gt; @page.id, :connect_to_container =&gt; &quot;main&quot;, :template =&gt; '&lt;p id=&quot;hi&quot;&gt;hello&lt;/p&gt;')
+    @portlet_raise_not_found = DynamicPortlet.create!(:name =&gt; &quot;Test&quot;, :connect_to_page_id =&gt; @page.id, :connect_to_container =&gt; &quot;main&quot;, :code =&gt; 'raise ActiveRecord::RecordNotFound')
+    reset(:page)
+
+    get :show, :path =&gt; [&quot;about&quot;]
+    assert_response :not_found
+    assert_select &quot;title&quot;, &quot;Not Found&quot;
+  end
+
+  def test_portlets_throw_multiple_goes_to_not_found
+    @page = Factory(:page, :section =&gt; root_section, :path =&gt; &quot;/about&quot;, :name =&gt; &quot;Test About&quot;, :template_file_name =&gt; &quot;default.html.erb&quot;, :publish_on_save =&gt; true)
+    @portlet_render = DynamicPortlet.create!(:name =&gt; &quot;Test&quot;, :connect_to_page_id =&gt; @page.id, :connect_to_container =&gt; &quot;main&quot;, :template =&gt; '&lt;p id=&quot;hi&quot;&gt;hello&lt;/p&gt;')
+    @portlet_raise_not_found = DynamicPortlet.create!(:name =&gt; &quot;Test&quot;, :connect_to_page_id =&gt; @page.id, :connect_to_container =&gt; &quot;main&quot;, :code =&gt; 'raise ActiveRecord::RecordNotFound')
+    @portlet_raise_access_denied = DynamicPortlet.create!(:name =&gt; &quot;Test&quot;, :connect_to_page_id =&gt; @page.id, :connect_to_container =&gt; &quot;main&quot;, :code =&gt; 'raise Cms::Errors::AccessDenied')   
+    @portlet_raise_generic = DynamicPortlet.create!(:name =&gt; &quot;Test&quot;, :connect_to_page_id =&gt; @page.id, :connect_to_container =&gt; &quot;main&quot;, :code =&gt; 'raise')
+    reset(:page)
+
+    get :show, :path =&gt; [&quot;about&quot;]
+    assert_response :not_found
+    assert_select &quot;title&quot;, &quot;Not Found&quot;
+  end
+
+  def test_portlets_throw_multiple_goes_to_access_denied
+    @page = Factory(:page, :section =&gt; root_section, :path =&gt; &quot;/about&quot;, :name =&gt; &quot;Test About&quot;, :template_file_name =&gt; &quot;default.html.erb&quot;, :publish_on_save =&gt; true)
+    @portlet_render = DynamicPortlet.create!(:name =&gt; &quot;Test&quot;, :connect_to_page_id =&gt; @page.id, :connect_to_container =&gt; &quot;main&quot;, :template =&gt; '&lt;p id=&quot;hi&quot;&gt;hello&lt;/p&gt;')
+    @portlet_raise_access_denied = DynamicPortlet.create!(:name =&gt; &quot;Test&quot;, :connect_to_page_id =&gt; @page.id, :connect_to_container =&gt; &quot;main&quot;, :code =&gt; 'raise Cms::Errors::AccessDenied')   
+    @portlet_raise_generic = DynamicPortlet.create!(:name =&gt; &quot;Test&quot;, :connect_to_page_id =&gt; @page.id, :connect_to_container =&gt; &quot;main&quot;, :code =&gt; 'raise')
+    reset(:page)
+
+    get :show, :path =&gt; [&quot;about&quot;]
+    assert_response :forbidden
+    assert_select &quot;title&quot;, &quot;Access Denied&quot;
+  end
+  def test_portlet_throw_generic_exception_still_render_page
+    @page = Factory(:page, :section =&gt; root_section, :path =&gt; &quot;/about&quot;, :name =&gt; &quot;Test About&quot;, :template_file_name =&gt; &quot;default.html.erb&quot;, :publish_on_save =&gt; true)
+    @portlet_render = DynamicPortlet.create!(:name =&gt; &quot;Test&quot;, :connect_to_page_id =&gt; @page.id, :connect_to_container =&gt; &quot;main&quot;, :template =&gt; '&lt;p id=&quot;hi&quot;&gt;hello&lt;/p&gt;')
+    @portlet_raise_generic = DynamicPortlet.create!(:name =&gt; &quot;Test&quot;, :connect_to_page_id =&gt; @page.id, :connect_to_container =&gt; &quot;main&quot;, :code =&gt; 'raise')
+    reset(:page)
+
+    get :show, :path =&gt; [&quot;about&quot;]
+    assert_select &quot;#hi&quot;, &quot;hello&quot;
+
+  end
+end</diff>
      <filename>test/functional/cms/content_controller_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -57,4 +57,92 @@ class Cms::LinksControllerTest &lt; ActionController::TestCase
       @link = Factory(:link, :section =&gt; root_section, :url =&gt; &quot;http://v1.example.com&quot;)
     end
 
-end
\ No newline at end of file
+end
+
+class Cms::LinksControllerPermissionsTest &lt; ActionController::TestCase
+  tests Cms::LinksController
+  include Cms::ControllerTestHelper
+  
+  def setup 
+    # DRYME copypaste from UserPermissionTest
+    @user = Factory(:user)
+    @group = Factory(:group, :name =&gt; &quot;Test&quot;, :group_type =&gt; Factory(:group_type, :name =&gt; &quot;CMS User&quot;, :cms_access =&gt; true))
+    @group.permissions &lt;&lt; create_or_find_permission_named(&quot;edit_content&quot;)
+    @group.permissions &lt;&lt; create_or_find_permission_named(&quot;publish_content&quot;)
+    @user.groups &lt;&lt; @group
+    
+    @editable_section = Factory(:section, :parent =&gt; root_section, :name =&gt; &quot;Editable&quot;)
+    @editable_subsection = Factory(:section, :parent =&gt; @editable_section, :name =&gt; &quot;Editable Subsection&quot;)
+    @group.sections &lt;&lt; @editable_section
+    @editable_page = Factory(:page, :section =&gt; @editable_section, :name =&gt; &quot;Editable Page&quot;)
+    @editable_subpage = Factory(:page, :section =&gt; @editable_subsection, :name =&gt; &quot;Editable SubPage&quot;)
+    @editable_link = Factory(:link, :section =&gt; @editable_section, :name =&gt; &quot;Editable Link&quot;)
+    @editable_sublink = Factory(:link, :section =&gt; @editable_subsection, :name =&gt; &quot;Editable SubLink&quot;)
+    
+    @noneditable_section = Factory(:section, :parent =&gt; root_section, :name =&gt; &quot;Not Editable&quot;)
+    @noneditable_page = Factory(:page, :section =&gt; @noneditable_section, :name =&gt; &quot;Non-Editable Page&quot;)
+    @noneditable_link = Factory(:link, :section =&gt; @noneditable_section, :name =&gt; &quot;Non-Editable Link&quot;)
+    
+    @noneditables = [@noneditable_section, @noneditable_page, @noneditable_link]
+    @editables = [@editable_section, @editable_subsection, 
+      @editable_page, @editable_subpage, 
+      @editable_link, @editable_sublink]
+  end
+
+  def test_new_permissions
+    login_as(@user)
+
+    get :new, :section_id =&gt; @editable_section
+    assert_response :success
+
+    get :new, :section_id =&gt; @noneditable_section
+    assert_response 403
+    assert_template &quot;cms/shared/access_denied&quot;
+  end
+
+  def test_create_permissions
+    login_as(@user)
+
+    post :create, :section_id =&gt; @editable_section, :name =&gt; &quot;Another editable link&quot;
+    assert_response :success
+
+    post :create, :section_id =&gt; @noneditable_section, :name =&gt; &quot;Another non-editable link&quot;
+    assert_response 403
+    assert_template &quot;cms/shared/access_denied&quot;
+  end
+
+  def test_edit_permissions
+    login_as(@user)
+
+    get :edit, :id =&gt; @editable_link
+    assert_response :success
+
+    get :edit, :id =&gt; @noneditable_link
+    assert_response 403
+    assert_template &quot;cms/shared/access_denied&quot;
+  end
+
+  def test_update_permissions
+    login_as(@user)
+
+    put :update, :id =&gt; @editable_link, :name =&gt; &quot;Modified editable link&quot;
+    assert_response :redirect
+
+    put :update, :id =&gt; @noneditable_link, :name =&gt; &quot;Modified non-editable link&quot;
+    assert_response 403
+    assert_template &quot;cms/shared/access_denied&quot;
+  end
+
+  def test_destroy_permissions
+    login_as(@user)
+
+    delete :destroy, :id =&gt; @editable_link
+    assert_response :redirect
+
+    delete :destroy, :id =&gt; @noneditable_link
+    assert_response 403
+    assert_template &quot;cms/shared/access_denied&quot;
+  end
+end
+
+</diff>
      <filename>test/functional/cms/links_controller_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -87,3 +87,134 @@ class Cms::PagesControllerTest &lt; ActionController::TestCase
     end
 
 end
+
+class Cms::PagesControllerPermissionsTest &lt; ActionController::TestCase
+  tests Cms::PagesController
+  include Cms::ControllerTestHelper
+  
+  def setup 
+    # DRYME copypaste from UserPermissionTest
+    @user = Factory(:user)
+    @group = Factory(:group, :name =&gt; &quot;Test&quot;, :group_type =&gt; Factory(:group_type, :name =&gt; &quot;CMS User&quot;, :cms_access =&gt; true))
+    @group.permissions &lt;&lt; create_or_find_permission_named(&quot;edit_content&quot;)
+    @group.permissions &lt;&lt; create_or_find_permission_named(&quot;publish_content&quot;)
+    @user.groups &lt;&lt; @group
+    
+    @editable_section = Factory(:section, :parent =&gt; root_section, :name =&gt; &quot;Editable&quot;)
+    @editable_subsection = Factory(:section, :parent =&gt; @editable_section, :name =&gt; &quot;Editable Subsection&quot;)
+    @group.sections &lt;&lt; @editable_section
+    @editable_page = Factory(:page, :section =&gt; @editable_section, :name =&gt; &quot;Editable Page&quot;)
+    @editable_subpage = Factory(:page, :section =&gt; @editable_subsection, :name =&gt; &quot;Editable SubPage&quot;)
+    @editable_link = Factory(:link, :section =&gt; @editable_section, :name =&gt; &quot;Editable Link&quot;)
+    @editable_sublink = Factory(:link, :section =&gt; @editable_subsection, :name =&gt; &quot;Editable SubLink&quot;)
+    
+    @noneditable_section = Factory(:section, :parent =&gt; root_section, :name =&gt; &quot;Not Editable&quot;)
+    @noneditable_page = Factory(:page, :section =&gt; @noneditable_section, :name =&gt; &quot;Non-Editable Page&quot;)
+    @noneditable_link = Factory(:link, :section =&gt; @noneditable_section, :name =&gt; &quot;Non-Editable Link&quot;)
+    
+    @noneditables = [@noneditable_section, @noneditable_page, @noneditable_link]
+    @editables = [@editable_section, @editable_subsection, 
+      @editable_page, @editable_subpage, 
+      @editable_link, @editable_sublink]
+  end
+
+  def test_new_permissions
+    login_as(@user)
+
+    get :new, :section_id =&gt; @editable_section
+    assert_response :success
+
+    get :new, :section_id =&gt; @noneditable_section
+    assert_response 403
+    assert_template &quot;cms/shared/access_denied&quot;
+  end
+
+  def test_create_permissions
+    login_as(@user)
+
+    post :create, :section_id =&gt; @editable_section, :name =&gt; &quot;Another editable page&quot;
+    assert_response :success
+
+    post :create, :section_id =&gt; @noneditable_section, :name =&gt; &quot;Another non-editable page&quot;
+    assert_response 403
+    assert_template &quot;cms/shared/access_denied&quot;
+  end
+
+  def test_edit_permissions
+    login_as(@user)
+
+    get :edit, :id =&gt; @editable_page
+    assert_response :success
+
+    get :edit, :id =&gt; @noneditable_page
+    assert_response 403
+    assert_template &quot;cms/shared/access_denied&quot;
+  end
+
+  def test_update_permissions
+    login_as(@user)
+
+    # Regular update
+    put :update, :id =&gt; @editable_page, :name =&gt; &quot;Modified editable page&quot;
+    assert_response :redirect
+
+    put :update, :id =&gt; @noneditable_page, :name =&gt; &quot;Modified non-editable page&quot;
+    assert_response 403
+    assert_template &quot;cms/shared/access_denied&quot;
+
+    # archive
+    put :archive, :id =&gt; @editable_page
+    assert_response :redirect
+
+    put :archive, :id =&gt; @noneditable_page
+    assert_response 403
+    assert_template &quot;cms/shared/access_denied&quot;
+
+    # hide
+    put :hide, :id =&gt; @editable_page
+    assert_response :redirect
+
+    put :hide, :id =&gt; @noneditable_page
+    assert_response 403
+    assert_template &quot;cms/shared/access_denied&quot;
+
+    # publish
+    put :publish, :id =&gt; @editable_page
+    assert_response :redirect
+
+    put :publish, :id =&gt; @noneditable_page
+    assert_response 403
+    assert_template &quot;cms/shared/access_denied&quot;
+
+    # publish many
+    put :publish, :page_ids =&gt; [@editable_page.id]
+    assert_response :redirect
+    
+    put :publish, :page_ids =&gt; [@noneditable_page.id]
+    assert_response 403
+    
+    put :publish, :page_ids =&gt; [@editable_page.id, @noneditable_page.id]
+    assert_response 403
+
+    # revert_to
+    # can't find route...
+#    put :revert_to, :id =&gt; @editable_page.id
+#    assert_response :redirect
+
+#    put :revert_to, :id =&gt; @noneditable_page.id
+#    assert_response :error # shouldn't it be 403?
+  end
+
+  def test_destroy_permissions
+    login_as(@user)
+
+    delete :destroy, :id =&gt; @editable_page
+    assert_response :redirect
+
+    delete :destroy, :id =&gt; @noneditable_page
+    assert_response 403
+    assert_template &quot;cms/shared/access_denied&quot;
+  end
+end
+
+</diff>
      <filename>test/functional/cms/pages_controller_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -3,11 +3,8 @@ require File.join(File.dirname(__FILE__), '/../../test_helper')
 class Cms::SectionNodesControllerTest &lt; ActionController::TestCase
   include Cms::ControllerTestHelper
   
-  def setup
+  def test_index_as_admin
     login_as_cms_admin
-  end
-  
-  def test_index
     @foo = Factory(:section, :name =&gt; &quot;Foo&quot;, :parent =&gt; root_section)
     @bar = Factory(:section, :name =&gt; &quot;Bar&quot;, :parent =&gt; @foo)
     @page = Factory(:page, :name =&gt; &quot;Test Page&quot;, :section =&gt; @bar)
@@ -39,6 +36,49 @@ class Cms::SectionNodesControllerTest &lt; ActionController::TestCase
     end
   end
   
+end
+
+class Cms::SectionNodesControllerPermissionsTest &lt; ActionController::TestCase
+  tests Cms::SectionNodesController
+  include Cms::ControllerTestHelper
   
+  def setup
+    # DRYME copypaste from UserPermissionTest
+    @user = Factory(:user)
+    login_as(@user)
+    @group = Factory(:group, :name =&gt; &quot;Test&quot;, :group_type =&gt; Factory(:group_type, :name =&gt; &quot;CMS User&quot;, :cms_access =&gt; true))
+    @group.permissions &lt;&lt; create_or_find_permission_named(&quot;edit_content&quot;)
+    @group.permissions &lt;&lt; create_or_find_permission_named(&quot;publish_content&quot;)
+    @user.groups &lt;&lt; @group
+    
+    @editable_section = Factory(:section, :parent =&gt; root_section, :name =&gt; &quot;Editable&quot;)
+    @group.sections &lt;&lt; @editable_section
+    @editable_page = Factory(:page, :section =&gt; @editable_section, :name =&gt; &quot;Editable Page&quot;)
+    @editable_link = Factory(:link, :section =&gt; @editable_section, :name =&gt; &quot;Editable Link&quot;)
+    
+    @noneditable_section = Factory(:section, :parent =&gt; root_section, :name =&gt; &quot;Not Editable&quot;)
+    @noneditable_page = Factory(:page, :section =&gt; @noneditable_section, :name =&gt; &quot;Non-Editable Page&quot;)
+    @noneditable_link = Factory(:link, :section =&gt; @noneditable_section, :name =&gt; &quot;Non-Editable Link&quot;)
+    
+    @noneditables = [@noneditable_section, @noneditable_page, @noneditable_link]
+    @editables = [@editable_section,
+      @editable_page, 
+      @editable_link,]
+  end
   
-end
\ No newline at end of file
+  def test_index_as_contributor_with_subsections
+    get :index
+    assert_response :success
+    
+    # Check that each non-editable has the non-editable class, and that each editable does not have
+    # the non-editable class
+    @noneditables.each do |ne|
+      assert_select &quot;td.node.non-editable div&quot;, ne.name
+    end
+    @editables.each do |e|
+      td = css_select(&quot;td##{e.class.to_s.underscore}_#{e.id}&quot;, e.name).first
+      assert !td.attributes[&quot;class&quot;].include?(&quot;non-editable&quot;)
+    end
+  end
+end
+</diff>
      <filename>test/functional/cms/section_nodes_controller_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -13,8 +13,15 @@ class Cms::SectionsControllerTest &lt; ActionController::TestCase
     assert_select &quot;input[name=?][value=?]&quot;, &quot;section[name]&quot;, root_section.name
   end
   
+  test &quot;GET new should set the groups to the parent section's groups by default&quot; do
+    @group = Factory(:group, :name =&gt; &quot;Test&quot;, :group_type =&gt; Factory(:group_type, :name =&gt; &quot;CMS User&quot;, :cms_access =&gt; true))
+    get :new, :section_id =&gt; root_section.to_param
+    assert_equal root_section.groups, assigns(:section).groups
+    assert !assigns(:section).groups.include?(@group)
+  end
+  
   def test_update
-    @section = Factory(:section, :name =&gt; &quot;V1&quot;, :parent =&gt; root_section)
+    @section = Factory(:section, :name =&gt; &quot;V1&quot;, :parent =&gt; root_section, :groups =&gt; root_section.groups)
     
     put :update, :id =&gt; @section.to_param, :section =&gt; {:name =&gt; &quot;V2&quot;}
     reset(:section)
@@ -76,3 +83,143 @@ class Cms::SectionFileBrowserControllerTest &lt; ActionController::TestCase
   end
   
 end
+
+class Cms::SectionsControllerPermissionsTest &lt; ActionController::TestCase
+  tests Cms::SectionsController
+  include Cms::ControllerTestHelper
+  
+  def setup
+    # DRYME copypaste from UserPermissionTest
+    @user = Factory(:user)
+    @group = Factory(:group, :name =&gt; &quot;Test&quot;, :group_type =&gt; Factory(:group_type, :name =&gt; &quot;CMS User&quot;, :cms_access =&gt; true))
+    @group.permissions &lt;&lt; create_or_find_permission_named(&quot;edit_content&quot;)
+    @group.permissions &lt;&lt; create_or_find_permission_named(&quot;publish_content&quot;)
+    @user.groups &lt;&lt; @group
+    
+    @editable_section = Factory(:section, :parent =&gt; root_section, :name =&gt; &quot;Editable&quot;)
+    @editable_subsection = Factory(:section, :parent =&gt; @editable_section, :name =&gt; &quot;Editable Subsection&quot;)
+    @group.sections &lt;&lt; @editable_section
+    @editable_page = Factory(:page, :section =&gt; @editable_section, :name =&gt; &quot;Editable Page&quot;)
+    @editable_subpage = Factory(:page, :section =&gt; @editable_subsection, :name =&gt; &quot;Editable SubPage&quot;)
+    @editable_link = Factory(:link, :section =&gt; @editable_section, :name =&gt; &quot;Editable Link&quot;)
+    @editable_sublink = Factory(:link, :section =&gt; @editable_subsection, :name =&gt; &quot;Editable SubLink&quot;)
+    
+    @noneditable_section = Factory(:section, :parent =&gt; root_section, :name =&gt; &quot;Not Editable&quot;)
+    @noneditable_page = Factory(:page, :section =&gt; @noneditable_section, :name =&gt; &quot;Non-Editable Page&quot;)
+    @noneditable_link = Factory(:link, :section =&gt; @noneditable_section, :name =&gt; &quot;Non-Editable Link&quot;)
+    
+    @noneditables = [@noneditable_section, @noneditable_page, @noneditable_link]
+    @editables = [@editable_section, @editable_subsection, 
+      @editable_page, @editable_subpage, 
+      @editable_link, @editable_sublink]
+  end
+
+  def test_new_permissions
+    login_as(@user)
+
+    get :new, :section_id =&gt; @editable_section
+    assert_response :success
+
+    get :new, :section_id =&gt; @noneditable_section
+    assert_response 403
+    assert_template &quot;cms/shared/access_denied&quot;
+  end
+  
+  test &quot;POST create should set the groups to the parent section's groups for non-admin user&quot; do
+    @group = Factory(:group, :name =&gt; &quot;Test&quot;, :group_type =&gt; Factory(:group_type, :name =&gt; &quot;CMS User&quot;, :cms_access =&gt; true))
+    login_as(@user)
+    get :new, :section_id =&gt; @editable_section
+    assert_equal @editable_section.groups, assigns(:section).groups
+    assert !assigns(:section).groups.include?(@group)
+  end
+
+  def test_create_permissions
+    login_as(@user)
+
+    post :create, :section_id =&gt; @editable_section, :name =&gt; &quot;Another editable subsection&quot;
+    assert_response :success
+
+    post :create, :section_id =&gt; @noneditable_section, :name =&gt; &quot;Another non-editable subsection&quot;
+    assert_response 403
+    assert_template &quot;cms/shared/access_denied&quot;
+  end
+
+  def test_edit_permissions
+    login_as(@user)
+
+    get :edit, :id =&gt; @editable_section
+    assert_response :success
+
+    get :edit, :id =&gt; @noneditable_section
+    assert_response 403
+    assert_template &quot;cms/shared/access_denied&quot;
+  end
+
+  def test_update_permissions
+    login_as(@user)
+
+    put :update, :id =&gt; @editable_section, :name =&gt; &quot;Modified editable subsection&quot;
+    assert_response :redirect
+
+    put :update, :id =&gt; @noneditable_section, :name =&gt; &quot;Modified non-editable subsection&quot;
+    assert_response 403
+    assert_template &quot;cms/shared/access_denied&quot;
+  end
+  
+  def test_update_permissions_of_subsection
+    login_as(@user)
+
+    put :update, :id =&gt; @editable_section, :name =&gt; &quot;Modified editable subsection&quot;
+    assert_response :redirect
+
+    put :update, :id =&gt; @editable_subsection, :name =&gt; &quot;Section below editable section&quot;
+    assert_response 403
+    assert_template &quot;cms/shared/access_denied&quot;
+  end
+  
+  test &quot;PUT update should leave groups alone for non-admin user&quot; do
+    @group2 = Factory(:group, :name =&gt; &quot;Test&quot;, :group_type =&gt; Factory(:group_type, :name =&gt; &quot;CMS User&quot;, :cms_access =&gt; true))
+    expected_groups = @editable_section.groups
+    login_as(@user)
+    put :update, :id =&gt; @editable_section
+    assert_response :redirect
+    assert_equal expected_groups, assigns(:section).groups
+    assert !assigns(:section).groups.include?(@group2)
+  end
+
+  test &quot;PUT update should leave groups alone for non-admin user even if hack url&quot; do
+    @group2 = Factory(:group, :name =&gt; &quot;Test&quot;, :group_type =&gt; Factory(:group_type, :name =&gt; &quot;CMS User&quot;, :cms_access =&gt; true))
+    expected_groups = @editable_section.groups
+    login_as(@user)
+    RAILS_DEFAULT_LOGGER.warn(&quot;starting...&quot;)
+    put :update, :id =&gt; @editable_section, :section =&gt; {:name =&gt; &quot;new name&quot;, :group_ids =&gt; [@group, @group2]}
+    assert_response :redirect
+    assert_equal expected_groups, assigns(:section).groups
+    assert_equal &quot;new name&quot;, assigns(:section).name
+    assert !assigns(:section).groups.include?(@group2)
+  end
+
+
+
+  test &quot;PUT update should add groups for admin user&quot; do
+# This step is unnecessary in the actual cms, as you can't stop the admin from doing anything
+    Group.find(:first, :conditions =&gt; &quot;code = 'cms-admin'&quot;).sections &lt;&lt; @editable_subsection
+    @group2 = Factory(:group, :name =&gt; &quot;Test&quot;, :group_type =&gt; Factory(:group_type, :name =&gt; &quot;CMS User&quot;, :cms_access =&gt; true))
+    expected_groups = [@group, @group2]
+    login_as_cms_admin
+    put :update, :id =&gt; @editable_subsection, :section =&gt; {:name =&gt; &quot;new name&quot;, :group_ids =&gt; [@group, @group2]}
+    assert_response :redirect
+    assert_equal expected_groups, assigns(:section).groups
+  end
+
+  def test_destroy_permissions
+    login_as(@user)
+
+    delete :destroy, :id =&gt; @editable_section
+    assert_response :redirect
+
+    delete :destroy, :id =&gt; @noneditable_section
+    assert_response 403
+    assert_template &quot;cms/shared/access_denied&quot;
+  end
+end</diff>
      <filename>test/functional/cms/sections_controller_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,6 +2,9 @@ require File.join(File.dirname(__FILE__), '/../../test_helper')
 
 class Cms::SessionsControllerTest &lt; ActionController::TestCase
   include Cms::ControllerTestHelper
+  def teardown
+    User.current = nil
+  end
   
   def test_not_redirected_to_cms_site_if_public_site
     @request.host = &quot;foo.com&quot;
@@ -19,6 +22,22 @@ class Cms::SessionsControllerTest &lt; ActionController::TestCase
     assert_select &quot;title&quot;, &quot;CMS Login&quot;
   end
   
+  def test_return_to
+    user = Factory(:user)
+    expected_url = &quot;/expected_url&quot;
+
+    post :create, {:success_url =&gt; &quot;&quot;, :login =&gt; user.login, :password =&gt; &quot;password&quot;}, {:return_to =&gt; expected_url }
+    assert_redirected_to(expected_url)
+  end
+  def test_success_url_overrides_return_to
+    user = Factory(:user)
+    expected_url = &quot;/expected_url&quot;
+
+    post :create, {:success_url =&gt; expected_url, :login =&gt; user.login, :password =&gt; &quot;password&quot;}, {:return_to =&gt; &quot;/somewhere_else&quot; }
+
+    assert_redirected_to(expected_url)
+  end
+  
 end
 
 class Cms::SessionsControllerCacheEnabledTest &lt; ActionController::TestCase
@@ -49,4 +68,4 @@ class Cms::SessionsControllerCacheEnabledTest &lt; ActionController::TestCase
     assert_select &quot;title&quot;, &quot;CMS Login&quot;
   end
   
-end
\ No newline at end of file
+end</diff>
      <filename>test/functional/cms/sessions_controller_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -6,7 +6,6 @@ class Cms::UsersControllerTest &lt; ActionController::TestCase
   def setup
     login_as_cms_admin
     @user = User.first
-    
   end
   
   def test_index
@@ -132,6 +131,11 @@ class Cms::UsersControllerTest &lt; ActionController::TestCase
     assert_select &quot;input#user_expires_at&quot;
   end
   
+  def test_show
+    get :show, :id =&gt; @user.id
+    assert_response :success
+  end
+  
   def test_update
     put :update, :id =&gt; @user.id, :user =&gt; { :first_name =&gt; &quot;First&quot;}
     reset(:user)
@@ -181,4 +185,47 @@ class Cms::UsersControllerTest &lt; ActionController::TestCase
       @user_with_login = Factory(:user, :login =&gt; &quot;mylogin&quot;)
     end
   
-end
\ No newline at end of file
+end
+
+class Cms::UsersControllerNonAdminTest &lt; ActionController::TestCase
+  tests Cms::UsersController
+  include Cms::ControllerTestHelper
+
+  def setup
+    @user = Factory.build(:user)
+    @user.groups = [groups(:group_3)]
+    @user.save!
+    login_as(@user)
+  end
+  
+  def test_show_self
+    get :show, :id =&gt; @user.id
+    assert_response :success
+  end
+  
+  def test_show_other
+    get :show, :id =&gt; Factory(:user).id
+    assert @response.body.include?(&quot;Access Denied&quot;)
+  end
+  
+  def test_change_password_self
+    get :change_password, :id =&gt; @user.id
+    assert_response :success
+  end
+  
+  def test_change_password_other
+    get :change_password, :id =&gt; Factory(:user).id
+    assert @response.body.include?(&quot;Access Denied&quot;)
+  end
+  
+  def test_update_password_self
+    put :update_password, :id =&gt; @user.id,
+        :user =&gt; {:password =&gt; &quot;something_else&quot;, :password_confirmation =&gt; &quot;something_else&quot;}
+    assert_redirected_to cms_user_path(@user)
+  end
+  
+  def test_update_password_other
+    put :update_password, :id =&gt; Factory(:user).id
+    assert @response.body.include?(&quot;Access Denied&quot;)
+  end
+end</diff>
      <filename>test/functional/cms/users_controller_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,6 +2,8 @@ ENV[&quot;RAILS_ENV&quot;] = &quot;test&quot;
 require File.expand_path(File.dirname(__FILE__) + &quot;/../config/environment&quot;)
 require 'test_help'
 require 'action_view/test_case'
+require 'mocha'
+require 'redgreen'
 
 class ActiveSupport::TestCase
   # Transactional fixtures accelerate your tests by wrapping each test method</diff>
      <filename>test/test_helper.rb</filename>
    </modified>
    <modified>
      <diff>@@ -81,6 +81,32 @@ class DefaultAttachableTest &lt; ActiveSupport::TestCase
     assert_equal &quot;/attachments/foo.jpg&quot;, @attachable.attachment_file_path
     assert @attachable.attachment.published?
   end  
+
+  def test_create_without_attachment_and_then_add_attachment_on_edit
+    @attachable = DefaultAttachable.new(:name =&gt; &quot;File Name&quot;, 
+      :attachment_file =&gt; nil, :publish_on_save =&gt; true)
+
+    assert_difference 'DefaultAttachable.count' do
+      assert_valid @attachable
+      @attachable.save!
+    end
+
+    assert_nil @attachable.attachment_file_path
+
+    reset(:attachable)
+
+    @attachable.attachment_file = @file
+    @attachable.save
+    @attachable.publish
+    assert_equal &quot;/attachments/foo.jpg&quot;, @attachable.attachment_file_path
+
+    reset(:attachable)
+
+    assert_equal @section, @attachable.attachment_section
+    assert_equal @section.id, @attachable.attachment_section_id
+    assert_equal &quot;/attachments/foo.jpg&quot;, @attachable.attachment_file_path
+    assert @attachable.attachment.published?
+  end
   
   
 end</diff>
      <filename>test/unit/behaviors/attaching_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,217 +2,96 @@ require File.join(File.dirname(__FILE__), '/../../test_helper')
 
 class Cms::MenuHelperTest &lt; ActionView::TestCase
   
-  def test_render_menu
+  def test_menu_items
     Page.first.update_attributes(:hidden =&gt; true, :publish_on_save =&gt; true)
     create_nfl_data
 
-    expected = &lt;&lt;HTML 
-&lt;div id=&quot;menu&quot; class=&quot;menu&quot;&gt;
-  &lt;ul&gt;
-    &lt;li id=&quot;section_#{@afc.id}&quot; class=&quot;depth-1 first open&quot;&gt;
-      &lt;a href=&quot;/buf&quot;&gt;AFC&lt;/a&gt;
-      &lt;ul&gt;
-        &lt;li id=&quot;section_#{@afc_east.id}&quot; class=&quot;depth-2 first&quot;&gt;
-          &lt;a href=&quot;/buf&quot;&gt;East&lt;/a&gt;
-        &lt;/li&gt;
-        &lt;li id=&quot;section_#{@afc_north.id}&quot; class=&quot;depth-2 open&quot;&gt;
-          &lt;a href=&quot;/bal&quot;&gt;North&lt;/a&gt;
-          &lt;ul&gt;
-            &lt;li id=&quot;page_#{@bal.id}&quot; class=&quot;depth-3 first on&quot;&gt;
-              &lt;a href=&quot;/bal&quot;&gt;Baltimore Ravens&lt;/a&gt;
-            &lt;/li&gt;
-            &lt;li id=&quot;page_#{@cin.id}&quot; class=&quot;depth-3&quot;&gt;
-              &lt;a href=&quot;/cin&quot;&gt;Cincinnati Bengals&lt;/a&gt;
-            &lt;/li&gt;
-            &lt;li id=&quot;page_#{@cle.id}&quot; class=&quot;depth-3&quot;&gt;
-              &lt;a href=&quot;/cle&quot;&gt;Cleveland Browns&lt;/a&gt;
-            &lt;/li&gt;
-            &lt;li id=&quot;page_#{@pit.id}&quot; class=&quot;depth-3 last&quot;&gt;
-              &lt;a href=&quot;/pit&quot;&gt;Pittsburgh Steelers&lt;/a&gt;
-            &lt;/li&gt;
-          &lt;/ul&gt;
-        &lt;/li&gt;
-        &lt;li id=&quot;section_#{@afc_south.id}&quot; class=&quot;depth-2&quot;&gt;
-          &lt;a href=&quot;/hou&quot;&gt;South&lt;/a&gt;
-        &lt;/li&gt;
-        &lt;li id=&quot;section_#{@afc_west.id}&quot; class=&quot;depth-2 last&quot;&gt;
-          &lt;a href=&quot;/den&quot;&gt;West&lt;/a&gt;
-        &lt;/li&gt;
-      &lt;/ul&gt;
-    &lt;/li&gt;
-    &lt;li id=&quot;section_#{@nfc.id}&quot; class=&quot;depth-1 last&quot;&gt;
-      &lt;a href=&quot;/dal&quot;&gt;NFC&lt;/a&gt;
-    &lt;/li&gt;
-  &lt;/ul&gt;
-&lt;/div&gt;
-HTML
-
-    assert_equal expected, render_menu(:page =&gt; @bal)
+    expected = [
+      { :id =&gt; &quot;section_#{@afc.id}&quot;, :url =&gt; &quot;/buf&quot;, :name =&gt; &quot;AFC&quot;, :children =&gt; [
+          { :id =&gt; &quot;section_#{@afc_east.id}&quot;, :url =&gt; &quot;/buf&quot;, :name =&gt; &quot;East&quot; },
+          { :id =&gt; &quot;section_#{@afc_north.id}&quot;, :url =&gt; &quot;/bal&quot;, :name =&gt; &quot;North&quot;, :children =&gt; [
+              { :id =&gt; &quot;page_#{@bal.id}&quot;, :selected =&gt; true, :url =&gt; &quot;/bal&quot;, :name =&gt; &quot;Baltimore Ravens&quot; },
+              { :id =&gt; &quot;page_#{@cin.id}&quot;, :url =&gt; &quot;/cin&quot;, :name =&gt; &quot;Cincinnati Bengals&quot; },
+              { :id =&gt; &quot;page_#{@cle.id}&quot;, :url =&gt; &quot;/cle&quot;, :name =&gt; &quot;Cleveland Browns&quot; },
+              { :id =&gt; &quot;page_#{@pit.id}&quot;, :url =&gt; &quot;/pit&quot;, :name =&gt; &quot;Pittsburgh Steelers&quot; }
+            ] },
+          { :id =&gt; &quot;section_#{@afc_south.id}&quot;, :url =&gt; &quot;/hou&quot;, :name =&gt; &quot;South&quot; },
+          { :id =&gt; &quot;section_#{@afc_west.id}&quot;, :url =&gt; &quot;/den&quot;, :name =&gt; &quot;West&quot; }
+        ] },
+      { :id =&gt; &quot;section_#{@nfc.id}&quot;, :url =&gt; &quot;/dal&quot;, :name =&gt; &quot;NFC&quot; }
+    ]
+    
+    assert_equal expected, menu_items(:page =&gt; @bal)
 
     @page = @bal
-    assert_equal expected, render_menu
-    assert_match /&lt;div id=\&quot;menu\&quot; class=\&quot;leftnav\&quot;&gt;/, render_menu(:class =&gt; &quot;leftnav&quot;)
-    
-    expected =  &lt;&lt;HTML 
-&lt;div id=&quot;menu&quot; class=&quot;menu&quot;&gt;
-  &lt;ul&gt;
-    &lt;li id=&quot;section_#{@afc.id}&quot; class=&quot;depth-1 first open&quot;&gt;
-      &lt;a href=&quot;/buf&quot;&gt;AFC&lt;/a&gt;
-      &lt;ul&gt;
-        &lt;li id=&quot;section_#{@afc_east.id}&quot; class=&quot;depth-2 first&quot;&gt;
-          &lt;a href=&quot;/buf&quot;&gt;East&lt;/a&gt;
-        &lt;/li&gt;
-        &lt;li id=&quot;section_#{@afc_north.id}&quot; class=&quot;depth-2 open&quot;&gt;
-          &lt;a href=&quot;/bal&quot;&gt;North&lt;/a&gt;
-        &lt;/li&gt;
-        &lt;li id=&quot;section_#{@afc_south.id}&quot; class=&quot;depth-2&quot;&gt;
-          &lt;a href=&quot;/hou&quot;&gt;South&lt;/a&gt;
-        &lt;/li&gt;
-        &lt;li id=&quot;section_#{@afc_west.id}&quot; class=&quot;depth-2 last&quot;&gt;
-          &lt;a href=&quot;/den&quot;&gt;West&lt;/a&gt;
-        &lt;/li&gt;
-      &lt;/ul&gt;
-    &lt;/li&gt;
-    &lt;li id=&quot;section_#{@nfc.id}&quot; class=&quot;depth-1 last&quot;&gt;
-      &lt;a href=&quot;/dal&quot;&gt;NFC&lt;/a&gt;
-    &lt;/li&gt;
-  &lt;/ul&gt;
-&lt;/div&gt;
-HTML
-    
-    assert_equal expected, render_menu(:depth =&gt; 2)
-
-    expected = &lt;&lt;HTML 
-&lt;div id=&quot;menu&quot; class=&quot;menu&quot;&gt;
-  &lt;ul&gt;
-    &lt;li id=&quot;section_#{@afc_east.id}&quot; class=&quot;depth-1 first&quot;&gt;
-      &lt;a href=&quot;/buf&quot;&gt;East&lt;/a&gt;
-    &lt;/li&gt;
-    &lt;li id=&quot;section_#{@afc_north.id}&quot; class=&quot;depth-1 open&quot;&gt;
-      &lt;a href=&quot;/bal&quot;&gt;North&lt;/a&gt;
-      &lt;ul&gt;
-        &lt;li id=&quot;page_#{@bal.id}&quot; class=&quot;depth-2 first on&quot;&gt;
-          &lt;a href=&quot;/bal&quot;&gt;Baltimore Ravens&lt;/a&gt;
-        &lt;/li&gt;
-        &lt;li id=&quot;page_#{@cin.id}&quot; class=&quot;depth-2&quot;&gt;
-          &lt;a href=&quot;/cin&quot;&gt;Cincinnati Bengals&lt;/a&gt;
-        &lt;/li&gt;
-        &lt;li id=&quot;page_#{@cle.id}&quot; class=&quot;depth-2&quot;&gt;
-          &lt;a href=&quot;/cle&quot;&gt;Cleveland Browns&lt;/a&gt;
-        &lt;/li&gt;
-        &lt;li id=&quot;page_#{@pit.id}&quot; class=&quot;depth-2 last&quot;&gt;
-          &lt;a href=&quot;/pit&quot;&gt;Pittsburgh Steelers&lt;/a&gt;
-        &lt;/li&gt;
-      &lt;/ul&gt;
-    &lt;/li&gt;
-    &lt;li id=&quot;section_#{@afc_south.id}&quot; class=&quot;depth-1&quot;&gt;
-      &lt;a href=&quot;/hou&quot;&gt;South&lt;/a&gt;
-    &lt;/li&gt;
-    &lt;li id=&quot;section_#{@afc_west.id}&quot; class=&quot;depth-1 last&quot;&gt;
-      &lt;a href=&quot;/den&quot;&gt;West&lt;/a&gt;
-    &lt;/li&gt;
-  &lt;/ul&gt;
-&lt;/div&gt;
-HTML
-    
-    assert_equal expected, render_menu(:from_top =&gt; 1, :depth =&gt; 2)
-    
-    expected = &lt;&lt;HTML 
-&lt;div id=&quot;menu&quot; class=&quot;menu&quot;&gt;
-  &lt;ul&gt;
-    &lt;li id=&quot;section_#{@afc.id}&quot; class=&quot;depth-1 first open&quot;&gt;
-      &lt;a href=&quot;/buf&quot;&gt;AFC&lt;/a&gt;
-      &lt;ul&gt;
-        &lt;li id=&quot;section_#{@afc_east.id}&quot; class=&quot;depth-2 first&quot;&gt;
-          &lt;a href=&quot;/buf&quot;&gt;East&lt;/a&gt;
-        &lt;/li&gt;
-        &lt;li id=&quot;section_#{@afc_north.id}&quot; class=&quot;depth-2 open&quot;&gt;
-          &lt;a href=&quot;/bal&quot;&gt;North&lt;/a&gt;
-        &lt;/li&gt;
-        &lt;li id=&quot;section_#{@afc_south.id}&quot; class=&quot;depth-2&quot;&gt;
-          &lt;a href=&quot;/hou&quot;&gt;South&lt;/a&gt;
-        &lt;/li&gt;
-        &lt;li id=&quot;section_#{@afc_west.id}&quot; class=&quot;depth-2 last&quot;&gt;
-          &lt;a href=&quot;/den&quot;&gt;West&lt;/a&gt;
-        &lt;/li&gt;
-      &lt;/ul&gt;
-    &lt;/li&gt;
-    &lt;li id=&quot;section_#{@nfc.id}&quot; class=&quot;depth-1 last&quot;&gt;
-      &lt;a href=&quot;/dal&quot;&gt;NFC&lt;/a&gt;
-      &lt;ul&gt;
-        &lt;li id=&quot;section_#{@nfc_east.id}&quot; class=&quot;depth-2 first&quot;&gt;
-          &lt;a href=&quot;/dal&quot;&gt;East&lt;/a&gt;
-        &lt;/li&gt;
-        &lt;li id=&quot;section_#{@nfc_north.id}&quot; class=&quot;depth-2&quot;&gt;
-          &lt;a href=&quot;/chi&quot;&gt;North&lt;/a&gt;
-        &lt;/li&gt;
-        &lt;li id=&quot;section_#{@nfc_south.id}&quot; class=&quot;depth-2&quot;&gt;
-          &lt;a href=&quot;/atl&quot;&gt;South&lt;/a&gt;
-        &lt;/li&gt;
-        &lt;li id=&quot;section_#{@nfc_west.id}&quot; class=&quot;depth-2 last&quot;&gt;
-          &lt;a href=&quot;/ari&quot;&gt;West&lt;/a&gt;
-        &lt;/li&gt;
-      &lt;/ul&gt;
-    &lt;/li&gt;
-  &lt;/ul&gt;
-&lt;/div&gt;
-HTML
-    
-    assert_equal expected, render_menu(:depth =&gt; 2, :show_all_siblings =&gt; true)
-    
-    expected = &lt;&lt;HTML 
-&lt;div id=&quot;menu&quot; class=&quot;menu&quot;&gt;
-  &lt;ul&gt;
-    &lt;li id=&quot;section_#{@afc.id}&quot; class=&quot;depth-1 first open&quot;&gt;
-      &lt;a href=&quot;/buf&quot;&gt;AFC&lt;/a&gt;
-    &lt;/li&gt;
-    &lt;li id=&quot;section_#{@nfc.id}&quot; class=&quot;depth-1 last&quot;&gt;
-      &lt;a href=&quot;/dal&quot;&gt;NFC&lt;/a&gt;
-    &lt;/li&gt;
-  &lt;/ul&gt;
-&lt;/div&gt;
-HTML
-    
-    assert_equal expected, render_menu(:depth =&gt; 1)
-    
-    expected = &lt;&lt;HTML 
-&lt;div id=&quot;menu&quot; class=&quot;menu&quot;&gt;
-  &lt;ul&gt;
-    &lt;li id=&quot;section_#{@afc_east.id}&quot; class=&quot;depth-1 first&quot;&gt;
-      &lt;a href=&quot;/buf&quot;&gt;East&lt;/a&gt;
-    &lt;/li&gt;
-    &lt;li id=&quot;section_#{@afc_north.id}&quot; class=&quot;depth-1 open&quot;&gt;
-      &lt;a href=&quot;/bal&quot;&gt;North&lt;/a&gt;
-    &lt;/li&gt;
-    &lt;li id=&quot;section_#{@afc_south.id}&quot; class=&quot;depth-1&quot;&gt;
-      &lt;a href=&quot;/hou&quot;&gt;South&lt;/a&gt;
-    &lt;/li&gt;
-    &lt;li id=&quot;section_#{@afc_west.id}&quot; class=&quot;depth-1 last&quot;&gt;
-      &lt;a href=&quot;/den&quot;&gt;West&lt;/a&gt;
-    &lt;/li&gt;
-  &lt;/ul&gt;
-&lt;/div&gt;
-HTML
-    
-    assert_equal expected, render_menu(:from_top =&gt; 1, :depth =&gt; 1)
-    
-    expected = &lt;&lt;HTML 
-&lt;div id=&quot;menu&quot; class=&quot;menu&quot;&gt;
-  &lt;ul&gt;
-    &lt;li id=&quot;section_#{@afc_east.id}&quot; class=&quot;depth-1 first&quot;&gt;
-      &lt;a href=&quot;/buf&quot;&gt;East&lt;/a&gt;
-    &lt;/li&gt;
-    &lt;li id=&quot;section_#{@afc_north.id}&quot; class=&quot;depth-1 open&quot;&gt;
-      &lt;a href=&quot;/bal&quot;&gt;North&lt;/a&gt;
-    &lt;/li&gt;
-    &lt;li id=&quot;section_#{@afc_south.id}&quot; class=&quot;depth-1 last&quot;&gt;
-      &lt;a href=&quot;/hou&quot;&gt;South&lt;/a&gt;
-    &lt;/li&gt;
-  &lt;/ul&gt;
-&lt;/div&gt;
-HTML
-
-    assert_equal expected, render_menu(:from_top =&gt; 1, :depth =&gt; 1, :limit =&gt; 3)    
+    assert_equal expected, menu_items
+    
+    expected = [
+      { :id =&gt; &quot;section_#{@afc.id}&quot;, :url =&gt; &quot;/buf&quot;, :name =&gt; &quot;AFC&quot;, :children =&gt; [
+          { :id =&gt; &quot;section_#{@afc_east.id}&quot;, :url =&gt; &quot;/buf&quot;, :name =&gt; &quot;East&quot; },
+          { :id =&gt; &quot;section_#{@afc_north.id}&quot;, :url =&gt; &quot;/bal&quot;, :name =&gt; &quot;North&quot; },
+          { :id =&gt; &quot;section_#{@afc_south.id}&quot;, :url =&gt; &quot;/hou&quot;, :name =&gt; &quot;South&quot; },
+          { :id =&gt; &quot;section_#{@afc_west.id}&quot;, :url =&gt; &quot;/den&quot;, :name =&gt; &quot;West&quot; }
+        ] },
+      { :id =&gt; &quot;section_#{@nfc.id}&quot;, :url =&gt; &quot;/dal&quot;, :name =&gt; &quot;NFC&quot; }
+    ]
+    
+    assert_equal expected, menu_items(:depth =&gt; 2)
+    
+    expected = [
+      { :id =&gt; &quot;section_#{@afc_east.id}&quot;, :url =&gt; &quot;/buf&quot;, :name =&gt; &quot;East&quot; },
+      { :id =&gt; &quot;section_#{@afc_north.id}&quot;, :url =&gt; &quot;/bal&quot;, :name =&gt; &quot;North&quot;, :children =&gt; [
+          { :id =&gt; &quot;page_#{@bal.id}&quot;, :selected =&gt; true, :url =&gt; &quot;/bal&quot;, :name =&gt; &quot;Baltimore Ravens&quot; },
+          { :id =&gt; &quot;page_#{@cin.id}&quot;, :url =&gt; &quot;/cin&quot;, :name =&gt; &quot;Cincinnati Bengals&quot; },
+          { :id =&gt; &quot;page_#{@cle.id}&quot;, :url =&gt; &quot;/cle&quot;, :name =&gt; &quot;Cleveland Browns&quot; },
+          { :id =&gt; &quot;page_#{@pit.id}&quot;, :url =&gt; &quot;/pit&quot;, :name =&gt; &quot;Pittsburgh Steelers&quot; }
+        ] },
+      { :id =&gt; &quot;section_#{@afc_south.id}&quot;, :url =&gt; &quot;/hou&quot;, :name =&gt; &quot;South&quot; },
+      { :id =&gt; &quot;section_#{@afc_west.id}&quot;, :url =&gt; &quot;/den&quot;, :name =&gt; &quot;West&quot; }
+    ]
+    
+    assert_equal expected, menu_items(:from_top =&gt; 1, :depth =&gt; 2)
+    
+    expected = [
+      { :id =&gt; &quot;section_#{@afc.id}&quot;, :url =&gt; &quot;/buf&quot;, :name =&gt; &quot;AFC&quot;, :children =&gt; [
+          { :id =&gt; &quot;section_#{@afc_east.id}&quot;, :url =&gt; &quot;/buf&quot;, :name =&gt; &quot;East&quot; },
+          { :id =&gt; &quot;section_#{@afc_north.id}&quot;, :url =&gt; &quot;/bal&quot;, :name =&gt; &quot;North&quot; },
+          { :id =&gt; &quot;section_#{@afc_south.id}&quot;, :url =&gt; &quot;/hou&quot;, :name =&gt; &quot;South&quot; },
+          { :id =&gt; &quot;section_#{@afc_west.id}&quot;, :url =&gt; &quot;/den&quot;, :name =&gt; &quot;West&quot; }
+        ] },
+      { :id =&gt; &quot;section_#{@nfc.id}&quot;, :url =&gt; &quot;/dal&quot;, :name =&gt; &quot;NFC&quot;, :children =&gt; [
+          { :id =&gt; &quot;section_#{@nfc_east.id}&quot;, :url =&gt; &quot;/dal&quot;, :name =&gt; &quot;East&quot; },
+          { :id =&gt; &quot;section_#{@nfc_north.id}&quot;, :url =&gt; &quot;/chi&quot;, :name =&gt; &quot;North&quot; },
+          { :id =&gt; &quot;section_#{@nfc_south.id}&quot;, :url =&gt; &quot;/atl&quot;, :name =&gt; &quot;South&quot; },
+          { :id =&gt; &quot;section_#{@nfc_west.id}&quot;, :url =&gt; &quot;/ari&quot;, :name =&gt; &quot;West&quot; }
+        ] }
+    ]
+    
+    assert_equal expected, menu_items(:depth =&gt; 2, :show_all_siblings =&gt; true)
+    
+    expected = [
+      { :id =&gt; &quot;section_#{@afc.id}&quot;, :url =&gt; &quot;/buf&quot;, :name =&gt; &quot;AFC&quot; },
+      { :id =&gt; &quot;section_#{@nfc.id}&quot;, :url =&gt; &quot;/dal&quot;, :name =&gt; &quot;NFC&quot; }
+    ]
+    
+    assert_equal expected, menu_items(:depth =&gt; 1)
+    
+    expected = [
+      { :id =&gt; &quot;section_#{@afc_east.id}&quot;, :url =&gt; &quot;/buf&quot;, :name =&gt; &quot;East&quot; },
+      { :id =&gt; &quot;section_#{@afc_north.id}&quot;, :url =&gt; &quot;/bal&quot;, :name =&gt; &quot;North&quot; },
+      { :id =&gt; &quot;section_#{@afc_south.id}&quot;, :url =&gt; &quot;/hou&quot;, :name =&gt; &quot;South&quot; },
+      { :id =&gt; &quot;section_#{@afc_west.id}&quot;, :url =&gt; &quot;/den&quot;, :name =&gt; &quot;West&quot; }
+    ]
+    
+    assert_equal expected, menu_items(:from_top =&gt; 1, :depth =&gt; 1)
+    
+    expected = [
+      { :id =&gt; &quot;section_#{@afc_east.id}&quot;, :url =&gt; &quot;/buf&quot;, :name =&gt; &quot;East&quot; },
+      { :id =&gt; &quot;section_#{@afc_north.id}&quot;, :url =&gt; &quot;/bal&quot;, :name =&gt; &quot;North&quot; },
+      { :id =&gt; &quot;section_#{@afc_south.id}&quot;, :url =&gt; &quot;/hou&quot;, :name =&gt; &quot;South&quot; }
+    ]
+    
+    assert_equal expected, menu_items(:from_top =&gt; 1, :depth =&gt; 1, :limit =&gt; 3)
     
   end
   
@@ -223,36 +102,20 @@ HTML
     @press_releases = Factory(:page, :section =&gt; @news, :name =&gt; &quot;Press Releases&quot;, :path =&gt; &quot;/press_releases&quot;, :publish_on_save =&gt; true)
     @corporate_news = Factory(:link, :section =&gt; @news, :name =&gt; &quot;Corporate News&quot;, :url =&gt; &quot;/news&quot;, :new_window =&gt; false, :publish_on_save =&gt; true)
     @cnn = Factory(:link, :section =&gt; @news, :name =&gt; &quot;CNN&quot;, :url =&gt; &quot;http://www.cnn.com&quot;, :new_window =&gt; true, :publish_on_save =&gt; true)
-    expected = &lt;&lt;HTML 
-&lt;div id=&quot;menu&quot; class=&quot;menu&quot;&gt;
-  &lt;ul&gt;
-    &lt;li id=&quot;section_#{@news.id}&quot; class=&quot;depth-1 first open&quot;&gt;
-      &lt;a href=&quot;/press_releases&quot;&gt;News&lt;/a&gt;
-      &lt;ul&gt;
-        &lt;li id=&quot;page_#{@press_releases.id}&quot; class=&quot;depth-2 first on&quot;&gt;
-          &lt;a href=&quot;/press_releases&quot;&gt;Press Releases&lt;/a&gt;
-        &lt;/li&gt;
-        &lt;li id=&quot;link_#{@corporate_news.id}&quot; class=&quot;depth-2&quot;&gt;
-          &lt;a href=&quot;/news&quot;&gt;Corporate News&lt;/a&gt;
-        &lt;/li&gt;
-        &lt;li id=&quot;link_#{@cnn.id}&quot; class=&quot;depth-2 last&quot;&gt;
-          &lt;a href=&quot;http://www.cnn.com&quot; target=&quot;_blank&quot;&gt;CNN&lt;/a&gt;
-        &lt;/li&gt;
-      &lt;/ul&gt;
-    &lt;/li&gt;
-  &lt;/ul&gt;
-&lt;/div&gt;
-HTML
     
-    @page = @press_releases
-    output = render_menu
-    
-    assert_equal expected, output
+    expected = [
+      { :id =&gt; &quot;section_#{@news.id}&quot;, :url =&gt; &quot;/press_releases&quot;, :name =&gt; &quot;News&quot;, :children =&gt; [
+          { :id =&gt; &quot;page_#{@press_releases.id}&quot;, :selected =&gt; true, :url =&gt; &quot;/press_releases&quot;, :name =&gt; &quot;Press Releases&quot; },
+          { :id =&gt; &quot;link_#{@corporate_news.id}&quot;, :url =&gt; &quot;/news&quot;, :name =&gt; &quot;Corporate News&quot; },
+          { :id =&gt; &quot;link_#{@cnn.id}&quot;, :url =&gt; &quot;http://www.cnn.com&quot;, :target =&gt; &quot;_blank&quot;, :name =&gt; &quot;CNN&quot; }
+        ] }
+    ]
     
-    assert_equal %Q{&lt;div id=&quot;menu&quot; class=&quot;menu&quot;&gt;\n&lt;/div&gt;\n}, 
-      render_menu(:from_top =&gt; 42)
+    @page = @press_releases
+    assert_equal expected, menu_items
+    assert_equal [], menu_items(:from_top =&gt; 42)
   end
-  
+
   def test_render_menu_does_not_show_unpublished_pages
     @section = Factory(:section, :name =&gt; &quot;Test&quot;, :path =&gt; &quot;/test&quot;)
     @page = Factory(:page, :section =&gt; @section, :name =&gt; &quot;Overview&quot;, :path =&gt; &quot;/test&quot;, :publish_on_save =&gt; true)
@@ -260,11 +123,13 @@ HTML
     @draft_page = Factory(:page, :section =&gt; @section, :name =&gt; &quot;Draft v1&quot;, :path =&gt; &quot;/draft&quot;, :publish_on_save =&gt; true)
     @draft_page.update_attributes(:name =&gt; &quot;Draft v2&quot;)    
     @never_published = Factory(:page, :section =&gt; @section, :name =&gt; &quot;Never Published&quot;, :path =&gt; &quot;/never_published&quot;)
-    output = render_menu(:from_top =&gt; 1)
-
-    assert output =~ /\/test/, &quot;Overview page should show up&quot;
-    assert output =~ /Draft v1/, &quot;Original version of draft page should show up&quot;
-    assert output !~ /\/never_published/, &quot;Never published should not show up&quot;
+    
+    expected = [
+      { :id =&gt; &quot;page_#{@page.id}&quot;, :name =&gt; &quot;Overview&quot;, :url =&gt; &quot;/test&quot;, :selected =&gt; true },
+      { :id =&gt; &quot;page_#{@draft_page.id}&quot;, :name =&gt; &quot;Draft v1&quot;, :url =&gt; &quot;/draft&quot; }
+    ]
+    
+    assert_equal expected, menu_items(:from_top =&gt; 1)
   end
   
   def test_render_menu_with_path
@@ -274,48 +139,23 @@ HTML
     @contact_us = Factory(:page, :section =&gt; @footer, :name =&gt; &quot;Contact Us&quot;, :path =&gt; &quot;/contact_us&quot;, :publish_on_save =&gt; true)
     @privacy_policy = Factory(:page, :section =&gt; @footer, :name =&gt; &quot;Privacy Policy&quot;, :path =&gt; &quot;/privacy_policy&quot;, :publish_on_save =&gt; true)
     
-    expected = &lt;&lt;HTML
-&lt;div id=&quot;menu&quot; class=&quot;menu&quot;&gt;
-  &lt;ul&gt;
-    &lt;li id=&quot;page_#{@about_us.id}&quot; class=&quot;depth-1 first&quot;&gt;
-      &lt;a href=&quot;/about_us&quot;&gt;About Us&lt;/a&gt;
-    &lt;/li&gt;
-    &lt;li id=&quot;page_#{@contact_us.id}&quot; class=&quot;depth-1&quot;&gt;
-      &lt;a href=&quot;/contact_us&quot;&gt;Contact Us&lt;/a&gt;
-    &lt;/li&gt;
-    &lt;li id=&quot;page_#{@privacy_policy.id}&quot; class=&quot;depth-1 last&quot;&gt;
-      &lt;a href=&quot;/privacy_policy&quot;&gt;Privacy Policy&lt;/a&gt;
-    &lt;/li&gt;
-  &lt;/ul&gt;
-&lt;/div&gt;
-HTML
-
-    #puts &quot;Expected:\n#{expected}&quot;
-    actual = render_menu(:page =&gt; @test, :path =&gt; &quot;/footer&quot;, :from_top =&gt; 1)
-    #puts &quot;Actual:\n#{actual}&quot;
+    expected = [
+      { :id =&gt; &quot;page_#{@about_us.id}&quot;, :url =&gt; &quot;/about_us&quot;, :name =&gt; &quot;About Us&quot; },
+      { :id =&gt; &quot;page_#{@contact_us.id}&quot;, :url =&gt; &quot;/contact_us&quot;, :name =&gt; &quot;Contact Us&quot; },
+      { :id =&gt; &quot;page_#{@privacy_policy.id}&quot;, :url =&gt; &quot;/privacy_policy&quot;, :name =&gt; &quot;Privacy Policy&quot; }
+    ]
+    
+    actual = menu_items(:page =&gt; @test, :path =&gt; &quot;/footer&quot;, :from_top =&gt; 1)
     assert_equal expected, actual
 
-    expected = &lt;&lt;HTML
-&lt;div id=&quot;menu&quot; class=&quot;menu&quot;&gt;
-  &lt;ul&gt;
-    &lt;li id=&quot;page_#{@about_us.id}&quot; class=&quot;depth-1 first&quot;&gt;
-      &lt;a href=&quot;/about_us&quot;&gt;About Us&lt;/a&gt;
-    &lt;/li&gt;
-    &lt;li id=&quot;page_#{@contact_us.id}&quot; class=&quot;depth-1 on&quot;&gt;
-      &lt;a href=&quot;/contact_us&quot;&gt;Contact Us&lt;/a&gt;
-    &lt;/li&gt;
-    &lt;li id=&quot;page_#{@privacy_policy.id}&quot; class=&quot;depth-1 last&quot;&gt;
-      &lt;a href=&quot;/privacy_policy&quot;&gt;Privacy Policy&lt;/a&gt;
-    &lt;/li&gt;
-  &lt;/ul&gt;
-&lt;/div&gt;
-HTML
-
-    #puts &quot;Expected:\n#{expected}&quot;
-    actual = render_menu(:page =&gt; @contact_us, :path =&gt; &quot;/footer&quot;, :from_top =&gt; 1)
-    #puts &quot;Actual:\n#{actual}&quot;
-    assert_equal expected, actual
+    expected = [
+      { :id =&gt; &quot;page_#{@about_us.id}&quot;, :url =&gt; &quot;/about_us&quot;, :name =&gt; &quot;About Us&quot; },
+      { :id =&gt; &quot;page_#{@contact_us.id}&quot;, :url =&gt; &quot;/contact_us&quot;, :name =&gt; &quot;Contact Us&quot;, :selected =&gt; true },
+      { :id =&gt; &quot;page_#{@privacy_policy.id}&quot;, :url =&gt; &quot;/privacy_policy&quot;, :name =&gt; &quot;Privacy Policy&quot; }
+    ]
     
+    actual = menu_items(:page =&gt; @contact_us, :path =&gt; &quot;/footer&quot;, :from_top =&gt; 1)
+    assert_equal expected, actual
   end
   
   protected
@@ -374,4 +214,4 @@ HTML
 
     end
   
-end
\ No newline at end of file
+end</diff>
      <filename>test/unit/helpers/menu_helper_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -16,7 +16,6 @@ class UserTest &lt; ActiveSupport::TestCase
     @user.disable!
     assert_nil User.authenticate(@user.login, @user.password)
   end
-  
   def test_expiration
     @user = Factory.build(:user)
     assert_nil @user.expires_at
@@ -32,7 +31,6 @@ class UserTest &lt; ActiveSupport::TestCase
     @user.expires_at = 1.day.ago
     assert @user.expired?
   end  
-
   def test_disable_enable
     @user = Factory(:user)
 
@@ -52,9 +50,26 @@ class UserTest &lt; ActiveSupport::TestCase
     assert !@user.expired?
     assert User.active.all.include?(@user)
   end
+  test &quot;email validation&quot; do 
+    @user = Factory(:user)
+    assert @user.valid?
+    
+    valid_emails = ['t@test.com', 'T@test.com', 'test@somewhere.mobi', 'test@somewhere.tv', 'joe_blow@somewhere.co.nz', 'joe_blow@somewhere.com.au', 't@t-t.co']
+    valid_emails.each do |email|
+      @user.email = email
+      assert @user.valid?
+    end
+
+    invalid_emails = ['', '@test.com', '@test', 'test@test', 'test@somewhere', 'test@somewhere.', 'test@somewhere.x', 'test@somewhere..']
+    invalid_emails.each do |email|
+      @user.email = email
+      assert !@user.valid?
+    end
+  end
+
 end
 
-class UserPermssionsTest &lt; ActiveSupport::TestCase
+class UserPermissionsTest &lt; ActiveSupport::TestCase
   def setup
     @user = Factory(:user)
     @guest_group = Group.first(:conditions =&gt; {:code =&gt; &quot;guest&quot;})    
@@ -75,36 +90,119 @@ class UserPermssionsTest &lt; ActiveSupport::TestCase
     assert !@user.able_to?(&quot;do something the group does not have permission to do&quot;)
   end
   
-  def test_cms_user_permissions
+  test &quot;cms user access to nodes&quot; do
+    @group = Factory(:group, :name =&gt; &quot;Test&quot;, :group_type =&gt; Factory(:group_type, :name =&gt; &quot;CMS User&quot;, :cms_access =&gt; true))
+    @user.groups &lt;&lt; @group
+    
+    @modifiable_section = Factory(:section, :parent =&gt; root_section, :name =&gt; &quot;Modifiable&quot;)
+    @non_modifiable_section = Factory(:section, :parent =&gt; root_section, :name =&gt; &quot;Not Modifiable&quot;)
+    
+    @group.sections &lt;&lt; @modifiable_section
+    
+    @modifiable_page = Factory(:page, :section =&gt; @modifiable_section)
+    @non_modifiable_page = Factory(:page, :section =&gt; @non_modifiable_section)
+    
+    @modifiable_link = Factory(:link, :section =&gt; @modifiable_section)
+    @non_modifiable_link = Factory(:link, :section =&gt; @non_modifiable_section)
+    
+    assert @user.able_to_modify?(@modifiable_section)
+    assert !@user.able_to_modify?(@non_modifiable_section)
+    
+    assert @user.able_to_modify?(@modifiable_page)
+    assert !@user.able_to_modify?(@non_modifiable_page)
+    
+    assert @user.able_to_modify?(@modifiable_link)
+    assert !@user.able_to_modify?(@non_modifiable_link)
+  end
+  
+  test &quot;cms user access to connectables&quot; do
     @group = Factory(:group, :name =&gt; &quot;Test&quot;, :group_type =&gt; Factory(:group_type, :name =&gt; &quot;CMS User&quot;, :cms_access =&gt; true))
-    @group.permissions &lt;&lt; create_or_find_permission_named(&quot;edit_content&quot;)
-    @group.permissions &lt;&lt; create_or_find_permission_named(&quot;publish_content&quot;)
     @user.groups &lt;&lt; @group
-    @editable_section = Factory(:section, :parent =&gt; root_section, :name =&gt; &quot;Editable&quot;)
-    @group.sections &lt;&lt; @editable_section
-    @noneditable_section = Factory(:section, :parent =&gt; root_section, :name =&gt; &quot;Not Editable&quot;)
-    @editable_page = Factory(:page, :section =&gt; @editable_section)
-    @noneditable_page = Factory(:page, :section =&gt; @noneditable_section)
-    
-    assert @user.able_to_edit?(@editable_section)
-    assert !@user.able_to_edit?(@noneditable_section)
-    assert @user.able_to_view?(@editable_page)
-    assert @user.able_to_view?(@noneditable_page)
+    
+    @modifiable_section = Factory(:section, :parent =&gt; root_section, :name =&gt; &quot;Modifiable&quot;)
+    @non_modifiable_section = Factory(:section, :parent =&gt; root_section, :name =&gt; &quot;Not Modifiable&quot;)
+    
+    @group.sections &lt;&lt; @modifiable_section
+    
+    @modifiable_page = Factory(:page, :section =&gt; @modifiable_section)
+    @non_modifiable_page = Factory(:page, :section =&gt; @non_modifiable_section)
+    
+    @all_modifiable_connectable = stub(
+      :class =&gt; stub(:content_block? =&gt; true, :connectable? =&gt; true),
+      :connected_pages =&gt; [@modifiable_page])
+    @some_modifiable_connectable = stub(
+      :class =&gt; stub(:content_block? =&gt; true, :connectable? =&gt; true),
+      :connected_pages =&gt; [@modifiable_page, @non_modifiable_page])
+    @none_modifiable_connectable = stub(
+      :class =&gt; stub(:content_block? =&gt; true, :connectable? =&gt; true),
+      :connected_pages =&gt; [@non_modifiable_page])
+    
+    assert @user.able_to_modify?(@all_modifiable_connectable)
+    assert !@user.able_to_modify?(@some_modifiable_connectable)
+    assert !@user.able_to_modify?(@none_modifiable_connectable)
+  end
+  
+  test &quot;cms user access to non-connectable content blocks&quot; do
+    @content_block = stub(:class =&gt; stub(:content_block? =&gt; true))
+    assert @user.able_to_modify?(@content_block)
   end
   
-  def test_non_cms_user_permissions
+  test &quot;non cms user access to nodes&quot; do
     @group = Factory(:group, :name =&gt; &quot;Test&quot;, :group_type =&gt; Factory(:group_type, :name =&gt; &quot;Registered User&quot;))
     @user.groups &lt;&lt; @group
-    @editable_section = Factory(:section, :parent =&gt; root_section, :name =&gt; &quot;Editable&quot;)
-    @group.sections &lt;&lt; @editable_section
-    @noneditable_section = Factory(:section, :parent =&gt; root_section, :name =&gt; &quot;Not Editable&quot;)
-    @editable_page = Factory(:page, :section =&gt; @editable_section)
-    @noneditable_page = Factory(:page, :section =&gt; @noneditable_section)
+    
+    @modifiable_section = Factory(:section, :parent =&gt; root_section, :name =&gt; &quot;Modifiable&quot;)
+    @group.sections &lt;&lt; @modifiable_section
+    @non_modifiable_section = Factory(:section, :parent =&gt; root_section, :name =&gt; &quot;Not Modifiable&quot;)
+    
+    @modifiable_page = Factory(:page, :section =&gt; @modifiable_section)
+    @non_modifiable_page = Factory(:page, :section =&gt; @non_modifiable_section)
 
-    assert !@user.able_to_edit?(@editable_section)
-    assert !@user.able_to_edit?(@noneditable_section)
-    assert @user.able_to_view?(@editable_page)
-    assert !@user.able_to_view?(@noneditable_page)
+    assert !@user.able_to_modify?(@modifiable_section)
+    assert !@user.able_to_modify?(@non_modifiable_section)
+    
+    assert @user.able_to_view?(@modifiable_page)
+    assert !@user.able_to_view?(@non_modifiable_page)
+  end
+  
+  test &quot;cms user with no permissions should still be able to view pages&quot; do
+    @group = Factory(:group, :name =&gt; &quot;Test&quot;, :group_type =&gt; Factory(:group_type, :name =&gt; &quot;CMS User&quot;, :cms_access =&gt; true))
+    @user.groups &lt;&lt; @group
+    
+    @page = Factory(:page)
+    assert @user.able_to_view?(@page)
+  end
+  
+  test &quot;cms user who can edit content&quot; do
+    @group = Factory(:group, :name =&gt; &quot;Test&quot;, :group_type =&gt; Factory(:group_type, :name =&gt; &quot;CMS User&quot;, :cms_access =&gt; true))
+    @group.permissions &lt;&lt; create_or_find_permission_named(&quot;edit_content&quot;)
+    @user.groups &lt;&lt; @group
+    
+    node = stub
+    
+    @user.stubs(:able_to_modify?).with(node).returns(true)
+    assert @user.able_to_edit?(node)
+    assert !@user.able_to_publish?(node)
+    
+    @user.stubs(:able_to_modify?).with(node).returns(false)
+    assert !@user.able_to_edit?(node)
+    assert !@user.able_to_publish?(node)
+  end
+  
+  test &quot;cms user who can publish content&quot; do
+    @group = Factory(:group, :name =&gt; &quot;Test&quot;, :group_type =&gt; Factory(:group_type, :name =&gt; &quot;CMS User&quot;, :cms_access =&gt; true))
+    @group.permissions &lt;&lt; create_or_find_permission_named(&quot;publish_content&quot;)
+    @user.groups &lt;&lt; @group
+    
+    node = stub
+    
+    @user.stubs(:able_to_modify?).with(node).returns(true)
+    assert !@user.able_to_edit?(node)
+    assert @user.able_to_publish?(node)
+    
+    @user.stubs(:able_to_modify?).with(node).returns(false)
+    assert !@user.able_to_edit?(node)
+    assert !@user.able_to_publish?(node)
   end
   
 end
@@ -127,4 +225,4 @@ class GuestUserTest &lt; ActiveSupport::TestCase
     assert !@user.able_to_view?(@protected_page)
   end
   
-end
\ No newline at end of file
+end</diff>
      <filename>test/unit/models/user_test.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>f457e772954dab427d922598db6f9ab5bfb033a7</id>
    </parent>
    <parent>
      <id>342e0ffcee051ffdc7430133cd8e2327d5101fdc</id>
    </parent>
  </parents>
  <author>
    <name>peakpg</name>
    <email>peakpg@gmail.com</email>
  </author>
  <url>http://github.com/browsermedia/browsercms/commit/1c393cefe71bf454241ced4ff1f56d9d629f9a4a</url>
  <id>1c393cefe71bf454241ced4ff1f56d9d629f9a4a</id>
  <committed-date>2009-11-03T13:53:08-08:00</committed-date>
  <authored-date>2009-11-03T13:53:08-08:00</authored-date>
  <message>Merge branch 'master' of git@github.com:browsermedia/browsercms</message>
  <tree>01d4706ca9bad2353f54d31dcbdf3816ea7b4996</tree>
  <committer>
    <name>peakpg</name>
    <email>peakpg@gmail.com</email>
  </committer>
</commit>
