browsercms 3.0.2 → 3.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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>
|