1 require 'simpleton'
2 require 'ostruct'
3
4 module Radiant
5 class AdminUI
6 # This may be loaded before ActiveSupport, so do an explicit require
7 require 'radiant/admin_ui/region_set'
8
9 class DuplicateTabNameError < StandardError; end
10
11 # The NavTab Class holds the structure of a navigation tab (including
12 # its sub-nav items).
13 class NavTab < Array
14 attr_reader :name
15
16 def initialize(name)
17 @name = name
18 end
19
20 def [](id)
21 unless id.kind_of? Fixnum
22 self.find {|subnav_item| subnav_item.name.to_s.titleize == id.to_s.titleize }
23 else
24 super
25 end
26 end
27
28 def <<(*args)
29 options = args.extract_options!
30 item = args.size > 1 ? deprecated_add(*(args << caller)) : args.first
31 raise DuplicateTabNameError.new("duplicate tab name `#{item.name}'") if self[item.name]
32 item.tab = self if item.respond_to?(:tab=)
33 if options.empty?
34 super(item)
35 else
36 options.symbolize_keys!
37 before = options.delete(:before)
38 after = options.delete(:after)
39 tab_name = before || after
40 if self[tab_name]
41 _index = index(self[tab_name])
42 _index += 1 unless before
43 insert(_index, item)
44 else
45 super(item)
46 end
47 end
48 end
49
50 alias :add :<<
51
52 def add_item(*args)
53 options = args.extract_options!
54 options.symbolize_keys!
55 before = options.delete(:before)
56 after = options.delete(:after)
57 tab_name = before || after
58 if self[tab_name]
59 _index = index(self[tab_name])
60 _index += 1 unless before
61 insert(_index, NavSubItem.new(args.first, args.second))
62 else
63 add NavSubItem.new(args.first, args.second)
64 end
65 end
66
67 def visible?(user)
68 any? { |sub_item| sub_item.visible?(user) }
69 end
70
71 def deprecated_add(name, url, caller)
72 ActiveSupport::Deprecation.warn("admin.tabs.add is no longer supported in Radiant 0.9.x. Please update your code to use: \ntab \"Content\" do\n\tadd_item(...)\nend", caller)
73 NavSubItem.new(name, url)
74 end
75 end
76
77 # Simple structure for storing the properties of a tab's sub items.
78 class NavSubItem
79 attr_reader :name, :url
80 attr_accessor :tab
81
82 def initialize(name, url = "#")
83 @name, @url = name, url
84 end
85
86 def visible?(user)
87 visible_by_controller?(user)
88 end
89
90 def relative_url
91 File.join(ActionController::Base.relative_url_root || '', url)
92 end
93
94 private
95 def visible_by_controller?(user)
96 params = ActionController::Routing::Routes.recognize_path(url, :method => :get)
97 if params && params[:controller]
98 klass = "#{params[:controller].camelize}Controller".constantize
99 klass.user_has_access_to_action?(user, params[:action])
100 else
101 false
102 end
103 end
104 end
105
106 include Simpleton
107
108 attr_accessor :nav
109
110 def nav_tab(*args)
111 NavTab.new(*args)
112 end
113
114 def nav_item(*args)
115 NavSubItem.new(*args)
116 end
117
118 def tabs
119 nav['Content']
120 end
121
122 # Region sets
123 %w{page snippet layout user configuration extension}.each do |controller|
124 attr_accessor controller
125 alias_method "#{controller}s", controller
126 end
127
128 def initialize
129 initialize_nav
130 load_default_regions
131 end
132
133 def initialize_nav
134 @nav = NavTab.new("Tab Container")
135 load_default_nav
136 end
137
138 def load_default_nav
139 content = nav_tab("Content")
140 content << nav_item("Pages", "/admin/pages")
141 nav << content
142
143 design = nav_tab("Design")
144 design << nav_item("Layouts", "/admin/layouts")
145 design << nav_item("Snippets", "/admin/snippets")
146 nav << design
147
148 settings = nav_tab("Settings")
149 settings << nav_item("General", "/admin/configuration")
150 settings << nav_item("Personal", "/admin/preferences")
151 settings << nav_item("Users", "/admin/users")
152 settings << nav_item("Extensions", "/admin/extensions")
153 nav << settings
154 end
155
156 def load_default_regions
157 @page = load_default_page_regions
158 @snippet = load_default_snippet_regions
159 @layout = load_default_layout_regions
160 @user = load_default_user_regions
161 @configuration = load_default_configuration_regions
162 @extension = load_default_extension_regions
163 end
164
165 private
166
167 def load_default_page_regions
168 OpenStruct.new.tap do |page|
169 page.edit = RegionSet.new do |edit|
170 edit.main.concat %w{edit_header edit_form edit_popups}
171 edit.form.concat %w{edit_title edit_extended_metadata edit_page_parts}
172 edit.layout.concat %w{edit_layout edit_type edit_status edit_published_at}
173 edit.form_bottom.concat %w{edit_buttons edit_timestamp}
174 end
175 page.index = RegionSet.new do |index|
176 index.sitemap_head.concat %w{title_column_header status_column_header actions_column_header}
177 index.node.concat %w{title_column status_column actions_column}
178 end
179 page.remove = page.children = page.index
180 page.new = page._part = page.edit
181 end
182 end
183
184 def load_default_user_regions
185 OpenStruct.new.tap do |user|
186 user.preferences = RegionSet.new do |preferences|
187 preferences.main.concat %w{edit_header edit_form}
188 preferences.form.concat %w{edit_name edit_email edit_username edit_password edit_locale}
189 preferences.form_bottom.concat %w{edit_buttons}
190 end
191 user.edit = RegionSet.new do |edit|
192 edit.main.concat %w{edit_header edit_form}
193 edit.form.concat %w{edit_name edit_email edit_username edit_password
194 edit_roles edit_locale edit_notes}
195 edit.form_bottom.concat %w{edit_buttons edit_timestamp}
196 end
197 user.index = RegionSet.new do |index|
198 index.thead.concat %w{title_header roles_header actions_header}
199 index.tbody.concat %w{title_cell roles_cell actions_cell}
200 index.bottom.concat %w{new_button}
201 end
202 user.new = user.edit
203 end
204 end
205
206 def load_default_snippet_regions
207 OpenStruct.new.tap do |snippet|
208 snippet.edit = RegionSet.new do |edit|
209 edit.main.concat %w{edit_header edit_form}
210 edit.form.concat %w{edit_title edit_content edit_filter}
211 edit.form_bottom.concat %w{edit_buttons edit_timestamp}
212 end
213 snippet.index = RegionSet.new do |index|
214 index.top.concat %w{}
215 index.thead.concat %w{title_header actions_header}
216 index.tbody.concat %w{title_cell actions_cell}
217 index.bottom.concat %w{new_button}
218 end
219 snippet.new = snippet.edit
220 end
221 end
222
223 def load_default_layout_regions
224 OpenStruct.new.tap do |layout|
225 layout.edit = RegionSet.new do |edit|
226 edit.main.concat %w{edit_header edit_form}
227 edit.form.concat %w{edit_title edit_extended_metadata edit_content}
228 edit.form_bottom.concat %w{reference_links edit_buttons edit_timestamp}
229 end
230 layout.index = RegionSet.new do |index|
231 index.top.concat %w{}
232 index.thead.concat %w{title_header actions_header}
233 index.tbody.concat %w{title_cell actions_cell}
234 index.bottom.concat %w{new_button}
235 end
236 layout.new = layout.edit
237 end
238 end
239
240 def load_default_configuration_regions
241 OpenStruct.new.tap do |configuration|
242 configuration.show = RegionSet.new do |show|
243 show.user.concat %w{preferences}
244 show.config.concat %w{site defaults users}
245 end
246 configuration.edit = RegionSet.new do |edit|
247 edit.main.concat %w{edit_header edit_form}
248 edit.form.concat %w{edit_site edit_defaults edit_users}
249 edit.form_bottom.concat %w{edit_buttons}
250 end
251 end
252 end
253
254 def load_default_extension_regions
255 OpenStruct.new.tap do |extension|
256 extension.index = RegionSet.new do |index|
257 index.thead.concat %w{title_header website_header version_header}
258 index.tbody.concat %w{title_cell website_cell version_cell}
259 end
260 end
261 end
262 end
263 end