we5-browsercms 3.0.2 → 3.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. data/README.markdown +1 -0
  2. data/app/controllers/cms/content_block_controller.rb +25 -2
  3. data/app/controllers/cms/content_controller.rb +31 -2
  4. data/app/controllers/cms/dashboard_controller.rb +2 -1
  5. data/app/controllers/cms/error_handling.rb +9 -2
  6. data/app/controllers/cms/links_controller.rb +2 -0
  7. data/app/controllers/cms/pages_controller.rb +22 -18
  8. data/app/controllers/cms/section_nodes_controller.rb +1 -1
  9. data/app/controllers/cms/sections_controller.rb +12 -7
  10. data/app/controllers/cms/sessions_controller.rb +17 -10
  11. data/app/controllers/cms/users_controller.rb +8 -6
  12. data/app/helpers/cms/application_helper.rb +2 -6
  13. data/app/helpers/cms/menu_helper.rb +118 -146
  14. data/app/helpers/cms/page_helper.rb +2 -2
  15. data/app/models/attachment.rb +2 -2
  16. data/app/models/forgot_password_mailer.rb +12 -0
  17. data/app/models/group.rb +13 -2
  18. data/app/models/guest_user.rb +9 -3
  19. data/app/models/link.rb +2 -2
  20. data/app/models/page.rb +1 -1
  21. data/app/models/section.rb +7 -2
  22. data/app/models/user.rb +35 -17
  23. data/app/portlets/forgot_password_portlet.rb +27 -0
  24. data/app/portlets/reset_password_portlet.rb +28 -0
  25. data/app/views/cms/blocks/_toolbar_for_member.html.erb +3 -3
  26. data/app/views/cms/blocks/index.html.erb +11 -6
  27. data/app/views/cms/content/show.html.erb +3 -3
  28. data/app/views/cms/forgot_password_mailer/reset_password.text.html.erb +3 -0
  29. data/app/views/cms/forgot_password_mailer/reset_password.text.plain.erb +3 -0
  30. data/app/views/cms/menus/_menu.html.erb +9 -0
  31. data/app/views/cms/menus/_menu_item.html.erb +11 -0
  32. data/app/views/cms/pages/_edit_connector.html.erb +1 -1
  33. data/app/views/cms/pages/_edit_container.html.erb +1 -1
  34. data/app/views/cms/section_nodes/_node.html.erb +1 -1
  35. data/app/views/cms/sections/_form.html.erb +36 -34
  36. data/app/views/cms/shared/access_denied.html.erb +3 -0
  37. data/app/views/cms/users/change_password.html.erb +8 -6
  38. data/app/views/cms/users/index.html.erb +1 -1
  39. data/app/views/cms/users/show.html.erb +50 -0
  40. data/app/views/layouts/_cms_toolbar.html.erb +1 -1
  41. data/app/views/layouts/_page_toolbar.html.erb +7 -7
  42. data/app/views/layouts/cms/administration.html.erb +24 -7
  43. data/app/views/portlets/forgot_password/_form.html.erb +5 -0
  44. data/app/views/portlets/forgot_password/render.html.erb +14 -0
  45. data/app/views/portlets/reset_password/_form.html.erb +3 -0
  46. data/app/views/portlets/reset_password/render.html.erb +24 -0
  47. data/{we5-browsercms.gemspec → browsercms.gemspec} +72 -54
  48. data/db/migrate/20091109175123_browsercms_3_0_5.rb +9 -0
  49. data/lib/acts_as_list.rb +8 -4
  50. data/lib/cms/acts/content_block.rb +1 -1
  51. data/lib/cms/authentication/controller.rb +26 -7
  52. data/lib/cms/behaviors/attaching.rb +3 -3
  53. data/lib/cms/behaviors/publishing.rb +12 -1
  54. data/lib/cms/behaviors/rendering.rb +17 -4
  55. data/lib/cms/behaviors/versioning.rb +2 -2
  56. data/lib/cms/routes.rb +4 -0
  57. data/lib/tasks/cms.rake +0 -18
  58. data/public/javascripts/cms/content_library.js +36 -0
  59. data/public/javascripts/cms/sitemap.js +21 -9
  60. data/public/stylesheets/cms/form_layout.css +16 -2
  61. data/public/stylesheets/cms/nav.css +4 -3
  62. data/test/functional/cms/content_block_controller_test.rb +120 -0
  63. data/test/functional/cms/content_controller_test.rb +135 -80
  64. data/test/functional/cms/links_controller_test.rb +89 -1
  65. data/test/functional/cms/pages_controller_test.rb +138 -0
  66. data/test/functional/cms/section_nodes_controller_test.rb +45 -5
  67. data/test/functional/cms/sections_controller_test.rb +148 -1
  68. data/test/functional/cms/sessions_controller_test.rb +26 -2
  69. data/test/functional/cms/users_controller_test.rb +49 -2
  70. data/test/integration/cms/password_management_test.rb +57 -0
  71. data/test/test_helper.rb +3 -1
  72. data/test/unit/behaviors/attaching_test.rb +26 -0
  73. data/test/unit/helpers/menu_helper_test.rb +118 -278
  74. data/test/unit/models/group_test.rb +6 -0
  75. data/test/unit/models/user_test.rb +127 -29
  76. metadata +20 -3
@@ -24,12 +24,8 @@ module Cms
24
24
  text
25
25
  end
26
26
 
27
- def render_connector(connector)
28
- connectable = connector.connectable_with_deleted
29
- if logged_in? && @mode == "edit"
30
- if connectable.class.versioned?
31
- connectable = connectable.as_of_version(connector.connectable_version)
32
- end
27
+ def render_connector_and_connectable(connector, connectable)
28
+ if logged_in? && @mode == "edit" && current_user.able_to_edit?(connector.page)
33
29
  render :partial => 'cms/pages/edit_connector',
34
30
  :locals => { :connector => connector, :connectable => connectable}
35
31
  else
@@ -1,30 +1,63 @@
1
1
  module Cms
2
2
  module MenuHelper
3
- # This will render a menu based on the page
4
- # With no options passed, it will render a menu that shows all the child sections of the root
5
- # and then it will show the path of decendent sections all the way to the current page.
6
- # 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,
7
- # the HREF of the A will be the URL of the first non-archived page that is a direct child of that Section.
8
- # Hidden pages will not show up, but if the first page in a Section is hidden, it will be used as the URL
9
- # for that Section. This is commonly done to have a page for a Section and avoid having duplicates in the
10
- # navigation.
3
+ # Renders a menu. There are two options, neither are required:
4
+ #
5
+ # ==== Options
6
+ # * <tt>:items</tt> - The items which should appear in the menu. This defaults to calling
7
+ # menu_items which generates items automatically based on the current page. But you can use
8
+ # this option to pass in a custom menu structure.
9
+ # * <tt>:partial</tt> - The partial used to render the menu. By default this is "partials/menu",
10
+ # which can be customised through the CMS. The partial gets a local variable <tt>items</tt>.
11
+ #
12
+ # ==== Structure of items
11
13
  #
14
+ # The items should be an array of hashes, in a tree. Each hash can have the following keys (name
15
+ # and url are required, others are optional):
16
+ #
17
+ # * <tt>:name</tt> - The name which appears in the menu
18
+ # * <tt>:url</tt> - The URL to link to
19
+ # * <tt>:id</tt> - The id for the menu item
20
+ # * <tt>:selected</tt> - Boolean value to indicate whether the menu item is the current page
21
+ # * <tt>:target</tt> - The target attribute for the link
22
+ # * <tt>:children</tt> - An array of hashes containing the child menu items. This is where the
23
+ # tree structure comes in.
24
+ def render_menu(options = {})
25
+ options[:items] ||= menu_items(options)
26
+ options[:partial] ||= "cms/menus/menu"
27
+ options[:id] ||= "menu"
28
+ options[:class] ||= "menu"
29
+ render :partial => options[:partial], :locals => { :items => options[:items], :css_id => options[:id], :css_class => options[:class] }
30
+ end
31
+
32
+ # This will render generate an array-of-hashes tree structure based on the page, which can be
33
+ # passed to render_menu in order to generate a menu.
34
+ #
35
+ # With no options passed, it will generate a structure that includes all the child sections of
36
+ # the root and then it will include the path of decendent sections all the way to the current
37
+ # page.
38
+ #
39
+ # Hidden pages will not be included, but if the first page in a Section is hidden, it will be
40
+ # used as the URL for that Section. This is commonly done to have a page for a Section and avoid
41
+ # having duplicates in the navigation.
42
+ #
12
43
  # You can change the behavior with the following options, all of these are optional:
13
44
  #
14
45
  # ==== Options
15
- # * <tt>:page</tt> - What page should be used as the current page. If this value is omitted, the value in @page will be used.
16
- # * <tt>:path</tt> - This will be used to look up a section and that section will used to generate the menu. The current page will
17
- # 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.
46
+ # * <tt>:page</tt> - What page should be used as the current page. If this value is omitted,
47
+ # the value in @page will be used.
48
+ # * <tt>:path</tt> - This will be used to look up a section and that section will used to
49
+ # generate the menu structure. The current page will still be the value of the page option or
50
+ # @page. Note that this is the path to a section, not a path to a page.
18
51
  # * <tt>:from_top</tt> - How many below levels from the root the tree should start at.
19
52
  # All sections at this level will be shown. The default is 0, which means show all
20
- # section that are direct children of the root
53
+ # nodes that are direct children of the root
21
54
  # * <tt>:depth</tt> - How many levels deep should the tree go, relative to from_top.
22
55
  # If no value is supplied, the tree will go all the way down to the current page.
23
56
  # If a value is supplied, the tree will be that many levels underneath from_top deep.
24
57
  # * <tt>:limit</tt> - Limits the number of top-level elements that will be included in the list
25
- # * <tt>:class</tt> - The CSS Class that will be applied to the div. The default value is "menu".
26
- # * <tt>:show_all_siblings</tt> - Passing true for this option will make all sibilings appear in the tree.
27
- # the default is false, in which case only the siblings of nodes within the open path will appear.
58
+ # * <tt>:show_all_siblings</tt> - Passing true for this option will make all sibilings appear in
59
+ # the tree. The default is false, in which case only the siblings of nodes within the open
60
+ # path will appear.
28
61
  #
29
62
  # ==== Examples
30
63
  #
@@ -32,89 +65,41 @@ module Cms
32
65
  # with teams being a Page, everything else a Section. Also, assume we are on the
33
66
  # Baltimore Ravens page. If you're not a footbal fan, see http://sports.yahoo.com/nfl/teams
34
67
  #
35
- # render_menu
36
- # # => <div class="menu">
37
- # <ul>
38
- # <li id="section_2" class="first open">
39
- # <a href="/buf">AFC</a>
40
- # <ul>
41
- # <li id="section_3" class="first">
42
- # <a href="/buf">East</a>
43
- # </li>
44
- # <li id="section_4" class="open">
45
- # <a href="/bal">North</a>
46
- # <ul>
47
- # <li id="page_5" class="first on">
48
- # <a href="/bal">Baltimore Ravens</a>
49
- # </li>
50
- # <li id="page_6">
51
- # <a href="/cin">Cincinnati Bengals</a>
52
- # </li>
53
- # <li id="page_7">
54
- # <a href="/cle">Cleveland Browns</a>
55
- # </li>
56
- # <li id="page_8" class="last">
57
- # <a href="/pit">Pittsburgh Steelers</a>
58
- # </li>
59
- # </ul>
60
- # </li>
61
- # <li id="section_5">
62
- # <a href="/hou">South</a>
63
- # </li>
64
- # <li id="section_6" class="last">
65
- # <a href="/den">West</a>
66
- # </li>
67
- # </ul>
68
- # </li>
69
- # <li id="section_7" class="last">
70
- # <a href="/dal">NFC</a>
71
- # </li>
72
- # </ul>
73
- # </div>
68
+ # menu_items
69
+ # # => [
70
+ # { :id => "section_2", :url => "/buf", :name => "AFC", :children => [
71
+ # { :id => "section_3", :url => "/buf", :name => "East" },
72
+ # { :id => "section_4", :url => "/bal", :name => "North", :children => [
73
+ # { :id => "page_5", :selected => true, :url => "/bal", :name => "Baltimore Ravens" },
74
+ # { :id => "page_6", :url => "/cin", :name => "Cincinnati Bengals" },
75
+ # { :id => "page_7", :url => "/cle", :name => "Cleveland Browns" },
76
+ # { :id => "page_8", :url => "/pit", :name => "Pittsburgh Steelers" }
77
+ # ] },
78
+ # { :id => "section_9", :url => "/hou", :name => "South" },
79
+ # { :id => "section_10}", :url => "/den", :name => "West" }
80
+ # ] },
81
+ # { :id => "section_11", :url => "/dal", :name => "NFC" }
82
+ # ]
74
83
  #
75
- # render_menu(:depth => 2, :show_all_siblings => true)
76
- # # => <div class="menu">
77
- # <ul>
78
- # <li id="section_2" class="first open">
79
- # <a href="/buf">AFC</a>
80
- # <ul>
81
- # <li id="section_3" class="first">
82
- # <a href="/buf">East</a>
83
- # </li>
84
- # <li id="section_4" class="open">
85
- # <a href="/bal">North</a>
86
- # </li>
87
- # <li id="section_5">
88
- # <a href="/hou">South</a>
89
- # </li>
90
- # <li id="section_6" class="last">
91
- # <a href="/den">West</a>
92
- # </li>
93
- # </ul>
94
- # </li>
95
- # <li id="section_7" class="last">
96
- # <a href="/dal">NFC</a>
97
- # <ul>
98
- # <li id="section_8" class="first">
99
- # <a href="/dal">East</a>
100
- # </li>
101
- # <li id="section_9">
102
- # <a href="/chi">North</a>
103
- # </li>
104
- # <li id="section_10">
105
- # <a href="/atl">South</a>
106
- # </li>
107
- # <li id="section_11" class="last">
108
- # <a href="/ari">West</a>
109
- # </li>
110
- # </ul>
111
- # </li>
112
- # </ul>
113
- # </div>
114
- def render_menu(options={})
115
- #Intialize parameters
116
- page = options[:page] || @page
117
- return nil unless page
84
+ # menu_items(:depth => 2, :show_all_siblings => true)
85
+ # # => [
86
+ # { :id => "section_2", :url => "/buf", :name => "AFC", :children => [
87
+ # { :id => "section_3", :url => "/buf", :name => "East" },
88
+ # { :id => "section_4", :url => "/bal", :name => "North" },
89
+ # { :id => "section_5", :url => "/hou", :name => "South" },
90
+ # { :id => "section_6", :url => "/den", :name => "West" }
91
+ # ] },
92
+ # { :id => "section_7", :url => "/dal", :name => "NFC", :children => [
93
+ # { :id => "section_8", :url => "/dal", :name => "East" },
94
+ # { :id => "section_9", :url => "/chi", :name => "North" },
95
+ # { :id => "section_10", :url => "/atl", :name => "South" },
96
+ # { :id => "section_11", :url => "/ari", :name => "West" }
97
+ # ] }
98
+ # ]
99
+ def menu_items(options = {})
100
+ # Intialize parameters
101
+ selected_page = options[:page] || @page
102
+ return nil unless selected_page
118
103
 
119
104
  # Path to the section
120
105
  if options.has_key?(:path)
@@ -122,65 +107,52 @@ module Cms
122
107
  raise "Could not find section for path '#{options[:path]}'" unless section_for_path
123
108
  ancestors = section_for_path.ancestors(:include_self => true)
124
109
  else
125
- ancestors = page.ancestors
110
+ ancestors = selected_page.ancestors
126
111
  end
127
112
 
128
- from_top = options.has_key?(:from_top) ? options[:from_top].to_i : 0
129
- depth = options.has_key?(:depth) ? options[:depth].to_i : 1.0/0
130
- id = options[:id] || "menu"
131
- css_class = options[:class] || "menu"
132
- show_all_siblings = !!(options[:show_all_siblings])
133
- limit = options[:limit]
134
-
135
- html = "<div id=\"#{id}\" class=\"#{css_class}\">\n"
136
-
137
- if from_top > ancestors.size
138
- return html << "</div>\n"
139
- else
140
- ancestors = ancestors[from_top..-1]
113
+ if options.has_key?(:from_top)
114
+ ancestors = ancestors[options[:from_top].to_i..-1] || []
141
115
  end
142
116
 
143
- #We are defining a recursive lambda that takes the top-level sections
144
- #d is the current depth
145
- fn = lambda do |nodes, d|
146
- indent = (d-1)*4
147
- html << "<ul>\n".indent(indent+2)
148
- nodes.each_with_index do |sn, i|
149
-
150
- #Construct the CSS classes that the LI should have
151
- classes = ["depth-#{d}"]
152
- if i == 0
153
- classes << "first"
154
- elsif i == nodes.size-1
155
- classes << "last"
156
- end
157
- classes << "open" if ancestors.include?(sn.node)
158
- classes << "on" if page == sn.node
159
- cls = classes.empty? ? nil : classes.join(" ")
117
+ depth = options.has_key?(:depth) ? options[:depth].to_i : 1.0/0
118
+ show_all_siblings = options[:show_all_siblings] || false
119
+
120
+ # We are defining a recursive lambda that takes the top-level sections
121
+ fn = lambda do |section_nodes, current_depth|
122
+ section_nodes.map do |section_node|
123
+ node = section_node.node
160
124
 
161
- html << %Q{<li id="#{sn.node_type.underscore}_#{sn.node.id}"#{cls ? " class=\"#{cls}\"" : ''}>\n}.indent(indent+4)
125
+ item = {}
126
+ item[:selected] = true if selected_page == node
127
+ item[:id] = "#{section_node.node_type.underscore}_#{section_node.node_id}"
162
128
 
163
- #Figure out what this link for this node should be
164
- #If it is a page, then the page will simply be used
165
- #But if is a page, we call the first_page_or_link method
166
- p = sn.node_type == "Section" ? sn.node.first_page_or_link : sn.node
167
- html << %Q{<a href="#{p ? p.path : '#'}"#{(p.respond_to?(:new_window) && p.new_window?) ? ' target="_blank"' : ''}>#{sn.node.name}</a>\n}.indent(indent+6)
129
+ # If we are showing a section item, we want to use the path for the first page
130
+ page = section_node.section? ? node.first_page_or_link : node
168
131
 
169
- #Now if this is a section, we do the child nodes,
170
- #but only if the show_all_siblings parameter is true,
171
- #or if this section is one of the current page's ancestors
172
- #and also if the current depth is less than the target depth
173
- if sn.node_type == "Section" && (show_all_siblings || ancestors.include?(sn.node)) && d < depth
174
- fn.call(sn.node.visible_child_nodes, d+1)
175
- end
132
+ item[:url] = page && page.path || '#'
133
+ item[:name] = node.name
134
+ item[:target] = "_blank" if page.respond_to?(:new_window?) && page.new_window?
176
135
 
177
- html << %Q{</li>\n}.indent(indent+4)
136
+ # Now if this is a section, we do the child nodes,
137
+ # but only if the show_all_siblings parameter is true,
138
+ # or if this section is one of the current page's ancestors
139
+ # and also if the current depth is less than the target depth
140
+ if section_node.section? &&
141
+ current_depth < depth &&
142
+ (show_all_siblings || ancestors.include?(node)) &&
143
+ !node.visible_child_nodes.empty?
144
+ item[:children] = fn.call(node.visible_child_nodes, current_depth + 1)
145
+ end
178
146
 
147
+ item
179
148
  end
180
- html << "</ul>\n".indent(indent+2)
181
149
  end
182
- fn.call(ancestors.first.visible_child_nodes(:limit => limit), 1) unless ancestors.first.blank?
183
- html << "</div>\n"
150
+
151
+ if ancestors.empty?
152
+ []
153
+ else
154
+ fn.call(ancestors.first.visible_child_nodes(:limit => options[:limit]), 1)
155
+ end
184
156
  end
185
157
  end
186
- end
158
+ end
@@ -14,7 +14,7 @@ module Cms
14
14
 
15
15
  def container(name)
16
16
  content = instance_variable_get("@content_for_#{name}")
17
- if logged_in? && @page && @mode == "edit"
17
+ if logged_in? && @page && @mode == "edit" && current_user.able_to_edit?(@page)
18
18
  render :partial => 'cms/pages/edit_container', :locals => {:name => name, :content => content}
19
19
  else
20
20
  content
@@ -65,4 +65,4 @@ module Cms
65
65
  end
66
66
 
67
67
  end
68
- end
68
+ end
@@ -1,5 +1,5 @@
1
1
  require 'digest/sha1'
2
- require 'ftools'
2
+ require 'fileutils'
3
3
 
4
4
  class Attachment < ActiveRecord::Base
5
5
 
@@ -116,7 +116,7 @@ class Attachment < ActiveRecord::Base
116
116
  unless temp_file.blank?
117
117
  FileUtils.mkdir_p File.dirname(full_file_location)
118
118
  if temp_file.local_path
119
- File.copy temp_file.local_path, full_file_location
119
+ FileUtils.copy temp_file.local_path, full_file_location
120
120
  else
121
121
  open(full_file_location, 'w') {|f| f << temp_file.read }
122
122
  end
@@ -0,0 +1,12 @@
1
+ class ForgotPasswordMailer < ActionMailer::Base
2
+
3
+ def reset_password(link, email)
4
+ @subject = "Account Management"
5
+ @body[:url] = link
6
+ @recipients = email
7
+ @from = 'do_not_reply@domain.com'
8
+ @sent_on = Time.now
9
+ template "cms/forgot_password_mailer/reset_password"
10
+ end
11
+
12
+ end
data/app/models/group.rb CHANGED
@@ -1,5 +1,11 @@
1
+ #
2
+ # A group represents a collection of permissions. Each User can be assigned to one or more groups, and the sum of
3
+ # their permissions from all groups combined represents what they can do.
4
+ #
1
5
  class Group < ActiveRecord::Base
2
-
6
+
7
+ GUEST_CODE = "guest"
8
+
3
9
  has_many :user_group_memberships
4
10
  has_many :users, :through => :user_group_memberships
5
11
 
@@ -26,5 +32,10 @@ class Group < ActiveRecord::Base
26
32
  def cms_access?
27
33
  group_type && group_type.cms_access?
28
34
  end
29
-
35
+
36
+ # Finds the guest group, which is a special group that represents public non-logged in users.
37
+ def self.guest
38
+ with_code(GUEST_CODE).first
39
+ end
40
+
30
41
  end
@@ -1,7 +1,13 @@
1
+ #
2
+ # Guests are a special user that represents a non-logged in user. The main reason to create an explicit
3
+ # instance of this type of user is so that the permissions a Guest user can have can be set via the Admin interface.
4
+ #
5
+ # Every request that a non-logged in user makes will use this User's permissions to determine what they can/can't do.
6
+ #
1
7
  class GuestUser < User
2
-
8
+
3
9
  def initialize(attributes={})
4
- super({:login => "guest", :first_name => "Anonymous", :last_name => "User"}.merge(attributes))
10
+ super({:login => Group::GUEST_CODE, :first_name => "Anonymous", :last_name => "User"}.merge(attributes))
5
11
  @guest = true
6
12
  end
7
13
 
@@ -18,7 +24,7 @@ class GuestUser < User
18
24
  end
19
25
 
20
26
  def group
21
- @group ||= Group.find_by_code("guest")
27
+ @group ||= Group.guest
22
28
  end
23
29
 
24
30
  def groups
data/app/models/link.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  class Link < ActiveRecord::Base
2
- acts_as_content_block
2
+ acts_as_content_block :connectable => false
3
3
 
4
4
  named_scope :named, lambda{|name| {:conditions => ['links.name = ?', name]}}
5
5
 
@@ -32,4 +32,4 @@ class Link < ActiveRecord::Base
32
32
  url
33
33
  end
34
34
 
35
- end
35
+ end
data/app/models/page.rb CHANGED
@@ -150,7 +150,7 @@ class Page < ActiveRecord::Base
150
150
 
151
151
  def delete_connectors
152
152
  connectors.for_page_version(version).all.each{|c| c.destroy }
153
- end
153
+ end
154
154
 
155
155
  #This is done to let copy_connectors know which version to pull from
156
156
  #copy_connectors will get called later as an after_update callback
@@ -75,7 +75,12 @@ class Section < ActiveRecord::Base
75
75
  ancs = node ? node.ancestors : []
76
76
  options[:include_self] ? ancs + [self] : ancs
77
77
  end
78
-
78
+
79
+ def with_ancestors(options = {})
80
+ options.merge! :include_self => true
81
+ self.ancestors(options)
82
+ end
83
+
79
84
  def move_to(section)
80
85
  if root?
81
86
  false
@@ -139,4 +144,4 @@ class Section < ActiveRecord::Base
139
144
  end
140
145
  end
141
146
 
142
- end
147
+ end