browsercms 3.0.2 → 3.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/app/controllers/cms/content_block_controller.rb +25 -2
- data/app/controllers/cms/content_controller.rb +31 -2
- data/app/controllers/cms/dashboard_controller.rb +2 -1
- data/app/controllers/cms/error_handling.rb +9 -2
- data/app/controllers/cms/links_controller.rb +2 -0
- data/app/controllers/cms/pages_controller.rb +22 -18
- data/app/controllers/cms/section_nodes_controller.rb +1 -1
- data/app/controllers/cms/sections_controller.rb +12 -7
- data/app/controllers/cms/sessions_controller.rb +17 -10
- data/app/controllers/cms/users_controller.rb +8 -6
- data/app/helpers/cms/application_helper.rb +2 -6
- data/app/helpers/cms/menu_helper.rb +118 -146
- data/app/helpers/cms/page_helper.rb +2 -2
- data/app/models/attachment.rb +2 -2
- data/app/models/group.rb +13 -2
- data/app/models/guest_user.rb +9 -3
- data/app/models/link.rb +2 -2
- data/app/models/page.rb +1 -1
- data/app/models/section.rb +7 -2
- data/app/models/user.rb +35 -17
- data/app/views/cms/blocks/_toolbar_for_member.html.erb +3 -3
- data/app/views/cms/blocks/index.html.erb +11 -6
- data/app/views/cms/content/show.html.erb +3 -3
- data/app/views/cms/menus/_menu.html.erb +9 -0
- data/app/views/cms/menus/_menu_item.html.erb +11 -0
- data/app/views/cms/pages/_edit_connector.html.erb +1 -1
- data/app/views/cms/pages/_edit_container.html.erb +1 -1
- data/app/views/cms/section_nodes/_node.html.erb +1 -1
- data/app/views/cms/sections/_form.html.erb +36 -34
- data/app/views/cms/shared/access_denied.html.erb +3 -0
- data/app/views/cms/users/change_password.html.erb +8 -6
- data/app/views/cms/users/index.html.erb +1 -1
- data/app/views/cms/users/show.html.erb +50 -0
- data/app/views/layouts/_cms_toolbar.html.erb +1 -1
- data/app/views/layouts/_page_toolbar.html.erb +7 -7
- data/app/views/layouts/cms/administration.html.erb +24 -7
- data/browsercms.gemspec +13 -7
- data/lib/acts_as_list.rb +8 -4
- data/lib/cms/acts/content_block.rb +1 -1
- data/lib/cms/authentication/controller.rb +26 -7
- data/lib/cms/behaviors/attaching.rb +3 -3
- data/lib/cms/behaviors/publishing.rb +12 -1
- data/lib/cms/behaviors/rendering.rb +17 -4
- data/lib/cms/behaviors/versioning.rb +2 -2
- data/lib/cms/routes.rb +4 -0
- data/lib/tasks/cms.rake +0 -18
- data/public/javascripts/cms/content_library.js +36 -0
- data/public/javascripts/cms/sitemap.js +21 -9
- data/public/stylesheets/cms/form_layout.css +16 -2
- data/public/stylesheets/cms/nav.css +4 -3
- data/test/functional/cms/content_block_controller_test.rb +120 -0
- data/test/functional/cms/content_controller_test.rb +135 -80
- data/test/functional/cms/links_controller_test.rb +89 -1
- data/test/functional/cms/pages_controller_test.rb +138 -0
- data/test/functional/cms/section_nodes_controller_test.rb +45 -5
- data/test/functional/cms/sections_controller_test.rb +148 -1
- data/test/functional/cms/sessions_controller_test.rb +26 -2
- data/test/functional/cms/users_controller_test.rb +49 -2
- data/test/test_helper.rb +3 -1
- data/test/unit/behaviors/attaching_test.rb +26 -0
- data/test/unit/helpers/menu_helper_test.rb +118 -278
- data/test/unit/models/group_test.rb +6 -0
- data/test/unit/models/user_test.rb +127 -29
- metadata +12 -4
@@ -1,30 +1,63 @@
|
|
1
1
|
module Cms
|
2
2
|
module MenuHelper
|
3
|
-
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
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
|
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,
|
16
|
-
#
|
17
|
-
#
|
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
|
-
#
|
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>:
|
26
|
-
#
|
27
|
-
#
|
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
|
-
#
|
36
|
-
# # =>
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
#
|
42
|
-
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
#
|
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
|
-
#
|
76
|
-
# # =>
|
77
|
-
#
|
78
|
-
#
|
79
|
-
#
|
80
|
-
#
|
81
|
-
#
|
82
|
-
#
|
83
|
-
#
|
84
|
-
#
|
85
|
-
#
|
86
|
-
#
|
87
|
-
#
|
88
|
-
#
|
89
|
-
#
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
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 =
|
110
|
+
ancestors = selected_page.ancestors
|
126
111
|
end
|
127
112
|
|
128
|
-
|
129
|
-
|
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
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
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
|
-
|
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
|
-
#
|
164
|
-
|
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
|
-
|
170
|
-
|
171
|
-
|
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
|
-
|
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
|
-
|
183
|
-
|
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
|
data/app/models/attachment.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'digest/sha1'
|
2
|
-
require '
|
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
|
-
|
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
|
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
|
data/app/models/guest_user.rb
CHANGED
@@ -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 =>
|
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.
|
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
|
data/app/models/section.rb
CHANGED
@@ -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
|
data/app/models/user.rb
CHANGED
@@ -11,8 +11,7 @@ class User < ActiveRecord::Base
|
|
11
11
|
validates_presence_of :email
|
12
12
|
#validates_length_of :email, :within => 6..100 #r@a.wk
|
13
13
|
#validates_uniqueness_of :email, :case_sensitive => false
|
14
|
-
validates_format_of :email, :with =>
|
15
|
-
|
14
|
+
validates_format_of :email, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :message => "should be an email address, ex. xx@xx.com"
|
16
15
|
attr_accessible :login, :email, :name, :first_name, :last_name, :password, :password_confirmation, :expires_at
|
17
16
|
|
18
17
|
has_many :user_group_memberships
|
@@ -89,12 +88,12 @@ class User < ActiveRecord::Base
|
|
89
88
|
@viewable_sections ||= Section.find(:all, :include => {:groups => :users}, :conditions => ["users.id = ?", id])
|
90
89
|
end
|
91
90
|
|
92
|
-
def
|
93
|
-
@
|
91
|
+
def modifiable_sections
|
92
|
+
@modifiable_sections ||= Section.find(:all, :include => {:groups => [:group_type, :users]}, :conditions => ["users.id = ? and group_types.cms_access = ?", id, true])
|
94
93
|
end
|
95
94
|
|
96
|
-
#Expects a list of names of Permissions
|
97
|
-
#true if the user has any of the permissions
|
95
|
+
# Expects a list of names of Permissions
|
96
|
+
# true if the user has any of the permissions
|
98
97
|
def able_to?(*required_permissions)
|
99
98
|
perms = required_permissions.map(&:to_sym)
|
100
99
|
permissions.any? do |p|
|
@@ -102,24 +101,43 @@ class User < ActiveRecord::Base
|
|
102
101
|
end
|
103
102
|
end
|
104
103
|
|
105
|
-
#Expects object to be an object or a section
|
106
|
-
#If it's a section, that will be used
|
107
|
-
#If it's not a section, it will call section on the object
|
108
|
-
#returns true if any of the sections of the groups the user is in matches the page's section.
|
104
|
+
# Expects object to be an object or a section
|
105
|
+
# If it's a section, that will be used
|
106
|
+
# If it's not a section, it will call section on the object
|
107
|
+
# returns true if any of the sections of the groups the user is in matches the page's section.
|
109
108
|
def able_to_view?(object)
|
110
109
|
section = object.is_a?(Section) ? object : object.section
|
111
|
-
|
110
|
+
viewable_sections.include?(section) || groups.cms_access.count > 0
|
111
|
+
end
|
112
|
+
|
113
|
+
def able_to_modify?(object)
|
114
|
+
case object
|
115
|
+
when Section
|
116
|
+
modifiable_sections.include?(object)
|
117
|
+
when Page, Link
|
118
|
+
modifiable_sections.include?(object.section)
|
119
|
+
else
|
120
|
+
if object.class.respond_to?(:connectable?) && object.class.connectable?
|
121
|
+
object.connected_pages.all? { |page| able_to_modify?(page) }
|
122
|
+
else
|
123
|
+
true
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# Expects node to be a Section, Page or Link
|
129
|
+
# Returns true if the specified node, or any of its ancestor sections, is editable by any of
|
130
|
+
# the user's 'CMS User' groups.
|
131
|
+
def able_to_edit?(object)
|
132
|
+
able_to?(:edit_content) && able_to_modify?(object)
|
112
133
|
end
|
113
134
|
|
114
|
-
|
115
|
-
|
116
|
-
#that the user is in match the section.
|
117
|
-
def able_to_edit?(section)
|
118
|
-
!!(editable_sections.include?(section) && able_to?(:edit_content))
|
135
|
+
def able_to_publish?(object)
|
136
|
+
able_to?(:publish_content) && able_to_modify?(object)
|
119
137
|
end
|
120
138
|
|
121
139
|
def able_to_edit_or_publish_content?
|
122
140
|
able_to?(:edit_content, :publish_content)
|
123
141
|
end
|
124
142
|
|
125
|
-
end
|
143
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
<% able_to? :publish_content do -%>
|
2
2
|
<% if @block.respond_to?(:live?) && !@block.live? %>
|
3
3
|
<%= link_to span_tag('Publish'), block_path(:publish),
|
4
|
-
:class => "http_put button left",
|
4
|
+
:class => "http_put button left#{' disabled' unless current_user.able_to_publish?(@block)}",
|
5
5
|
:id => "publish_button" %>
|
6
6
|
<% else %>
|
7
7
|
<%= link_to span_tag('Publish'), "#",
|
@@ -15,7 +15,7 @@
|
|
15
15
|
:id => "view_button" %>
|
16
16
|
|
17
17
|
<%= link_to span_tag('Edit Content'), block_path(:edit),
|
18
|
-
:class => "button right#{ ' off' if action_name == 'edit'}",
|
18
|
+
:class => "button right#{ ' off' if action_name == 'edit'}#{' disabled' unless current_user.able_to_edit?(@block)}",
|
19
19
|
:id => "edit_button" %>
|
20
20
|
|
21
21
|
<%= link_to span_tag("Add New Content"), new_block_path,
|
@@ -33,6 +33,6 @@
|
|
33
33
|
<% end %>
|
34
34
|
|
35
35
|
<%= link_to span_tag("<span class=\"delete_img\"> </span>Delete"), block_path,
|
36
|
-
:class => "http_delete confirm_with_title button",
|
36
|
+
:class => "http_delete confirm_with_title button#{' disabled' unless current_user.able_to_publish?(@block)}",
|
37
37
|
:title => "Are you sure you want to delete '#{@block.name}'?",
|
38
38
|
:id => "delete_button" %>
|
@@ -1,4 +1,5 @@
|
|
1
1
|
<% content_for(:html_head) do %>
|
2
|
+
<%= javascript_include_tag "cms/content_library" %>
|
2
3
|
<% javascript_tag do %>
|
3
4
|
jQuery(function($){
|
4
5
|
var collectionName = '<%= content_type.model_class.name.underscore.pluralize %>'
|
@@ -11,12 +12,14 @@
|
|
11
12
|
var match = this.id.match(/(.*)_(\d+)/)
|
12
13
|
var type = match[1]
|
13
14
|
var id = match[2]
|
15
|
+
var editable = !$(this).hasClass("non-editable")
|
16
|
+
var publishable = !$(this).hasClass("non-publishable")
|
14
17
|
$('table.data tbody tr').removeClass('selected')
|
15
18
|
$(this).addClass('selected')
|
16
19
|
$('#functions .button').addClass('disabled').attr('href','#')
|
17
20
|
$('#add_button').removeClass('disabled').attr('href', '/cms/'+collectionName+'/new')
|
18
21
|
$('#view_button').removeClass('disabled').attr('href', '/cms/'+collectionName+'/'+id)
|
19
|
-
$('#edit_button').removeClass('disabled').attr('href', '/cms/'+collectionName+'/'+id+'/edit')
|
22
|
+
if (editable) $('#edit_button').removeClass('disabled').attr('href', '/cms/'+collectionName+'/'+id+'/edit')
|
20
23
|
<% if content_type.model_class.versioned? %>
|
21
24
|
$('#revisions_button').removeClass('disabled').attr('href', '/cms/'+collectionName+'/'+id+'/versions')
|
22
25
|
<% else %>
|
@@ -28,12 +31,14 @@
|
|
28
31
|
$('#delete_button').addClass('disabled')
|
29
32
|
.attr('title', $.trim(cannot_be_deleted_message.text()))
|
30
33
|
} else {
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
+
if (publishable) {
|
35
|
+
$('#delete_button').removeClass('disabled')
|
36
|
+
.attr('href', '/cms/'+collectionName+'/'+id)
|
37
|
+
.attr('title', 'Are You Sure You Want To Delete This Record?')
|
38
|
+
}
|
34
39
|
}
|
35
40
|
<% able_to? :publish_content do -%>
|
36
|
-
if($(this).hasClass('draft')) {
|
41
|
+
if($(this).hasClass('draft') && publishable) {
|
37
42
|
$('#publish_button').removeClass('disabled').attr('href', '/cms/'+collectionName+'/'+id+'/publish?_redirect_to='+location.href)
|
38
43
|
}
|
39
44
|
<% end %>
|
@@ -85,7 +90,7 @@
|
|
85
90
|
col_ct += 1 if content_type.model_class.publishable? %>
|
86
91
|
<% @blocks.each do |b| %>
|
87
92
|
<% block = b.class.versioned? ? b.as_of_draft_version : b %>
|
88
|
-
<tr id="<%= block.class.name.underscore %>_<%= block.id %>" class="<%= block.class.name.underscore %> <%= block.class.publishable? && !block.published? ? 'draft' : 'published' %>">
|
93
|
+
<tr id="<%= block.class.name.underscore %>_<%= block.id %>" class="<%= block.class.name.underscore %> <%= block.class.publishable? && !block.published? ? 'draft' : 'published' %> <%= 'non-editable' unless current_user.able_to_edit?(block) %> <%= 'non-publishable' unless current_user.able_to_publish?(block) %>">
|
89
94
|
<td class="first"></td>
|
90
95
|
<% content_type.columns_for_index.each_with_index do |column, i| %>
|
91
96
|
<td class="<%= column[:label].gsub(' ', '').underscore %>">
|
@@ -12,9 +12,9 @@
|
|
12
12
|
<iframe src="<%=h cms_toolbar_path(:page_id => @page.id, :page_version => @page.version, :mode => @mode, :page_toolbar => @show_page_toolbar ? 1 : 0) %>" width="100%" height="<%= @show_page_toolbar ? 159 : 100 %>px" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" name="cms_toolbar"></iframe>
|
13
13
|
<% end %>
|
14
14
|
|
15
|
-
<% @
|
16
|
-
<% content_for(
|
17
|
-
<%=
|
15
|
+
<% @_connectors.each_with_index do |connector, i| %>
|
16
|
+
<% content_for(connector.container.to_sym) do %>
|
17
|
+
<%= render_connector_and_connectable(connector, @_connectables[i]) %>
|
18
18
|
<% end %>
|
19
19
|
<% end %>
|
20
20
|
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<div id="<%= css_id %>" class="<%= css_class %>">
|
2
|
+
<% unless items.empty?
|
3
|
+
%> <ul>
|
4
|
+
<% items.each_with_index do |item, i|
|
5
|
+
%><%= render :partial => "/cms/menus/menu_item", :object => item, :locals => { :depth => 1, :position => i + 1, :item_count => items.length }
|
6
|
+
%><% end
|
7
|
+
%> </ul>
|
8
|
+
<% end
|
9
|
+
%></div>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<% indent = (depth - 1) * 4
|
2
|
+
%><%= " "*(indent + 4) %><li id="<%= menu_item[:id] %>" class="depth-<%= depth %><%= ' first' if position == 1 %><%= ' last' if position == item_count %><%= ' on' if menu_item[:selected] %><%= ' open' unless menu_item[:children].blank? %>">
|
3
|
+
<%= " "*(indent + 6) %><a href="<%= menu_item[:url] %>"<%= ' target=#{menu_item[:target]}' if menu_item[:target] %>><%= menu_item[:name] %></a>
|
4
|
+
<% unless menu_item[:children].blank?
|
5
|
+
%><%= " "*(indent + 6) %><ul>
|
6
|
+
<% menu_item[:children].each_with_index do |item, i|
|
7
|
+
%><%= render :partial => "/cms/menus/menu_item", :object => item, :locals => { :depth => depth + 1, :position => i + 1, :item_count => menu_item[:children].length }
|
8
|
+
%><% end
|
9
|
+
%><%= " "*(indent + 6) %></ul>
|
10
|
+
<% end
|
11
|
+
%><%= " "*(indent + 4) %></li>
|