1 require "highline"
2 require "forwardable"
3
4 module Radiant
5 class Setup
6
7 class << self
8 def bootstrap(config)
9 setup = new
10 setup.bootstrap(config)
11 setup
12 end
13 end
14
15 attr_accessor :config
16
17 def bootstrap(config)
18 @config = config
19 @admin = create_admin_user(config[:admin_name], config[:admin_username], config[:admin_password])
20 UserActionObserver.current_user = @admin
21 load_default_configuration
22 load_database_template(config[:database_template])
23 announce "Finished."
24 end
25
26 def create_admin_user(name, username, password)
27 unless name and username and password
28 announce "Create the admin user (press enter for defaults)."
29 name = prompt_for_admin_name unless name
30 username = prompt_for_admin_username unless username
31 password = prompt_for_admin_password unless password
32 end
33 attributes = {
34 :name => name,
35 :login => username,
36 :password => password,
37 :password_confirmation => password,
38 :admin => true
39 }
40 admin = User.find_by_login(username)
41 admin = User.new unless admin
42 admin.update_attributes(attributes)
43 admin
44 end
45
46 def load_default_configuration
47 feedback "\nInitializing configuration" do
48 step { Radiant::Config['admin.title' ] = 'Radiant CMS' }
49 step { Radiant::Config['admin.subtitle'] = 'Publishing for Small Teams' }
50 step { Radiant::Config['defaults.page.parts' ] = 'body, extended' }
51 step { Radiant::Config['defaults.page.status' ] = 'Draft' }
52 step { Radiant::Config['defaults.page.filter' ] = nil }
53 step { Radiant::Config['defaults.page.fields'] = 'Keywords, Description' }
54 step { Radiant::Config['session_timeout'] = 2.weeks }
55 step { Radiant::Config['default_locale'] = 'en' }
56 end
57 end
58
59 def load_database_template(filename)
60 template = nil
61 if filename
62 name = find_template_in_path(filename)
63 unless name
64 announce "Invalid template name: #{filename}"
65 filename = nil
66 else
67 template = load_template_file(name)
68 end
69 end
70 unless filename
71 templates = find_and_load_templates("#{RADIANT_ROOT}/db/templates/*.yml")
72 templates.concat find_and_load_templates("#{RADIANT_ROOT}/vendor/extensions/**/db/templates/*.yml")
73 Radiant::Extension.descendants.each do |d|
74 templates.concat find_and_load_templates(d.root + '/db/templates/*.yml')
75 end
76 templates.concat find_and_load_templates("#{Rails.root}/vendor/extensions/**/db/templates/*.yml")
77 templates.concat find_and_load_templates("#{Rails.root}/db/templates/*.yml")
78 templates.uniq!
79 choose do |menu|
80 menu.header = "\nSelect a database template"
81 menu.prompt = "[1-#{templates.size}]: "
82 menu.select_by = :index
83 templates.each { |t| menu.choice(t['name']) { template = t } }
84 end
85 end
86 create_records(template)
87 end
88
89 private
90
91 def prompt_for_admin_name
92 username = ask('Name (Administrator): ', String) do |q|
93 q.validate = /^.{0,100}$/
94 q.responses[:not_valid] = "Invalid name. Must be at less than 100 characters long."
95 q.whitespace = :strip
96 end
97 username = "Administrator" if username.blank?
98 username
99 end
100
101 def prompt_for_admin_username
102 username = ask('Username (admin): ', String) do |q|
103 q.validate = /^(|.{3,40})$/
104 q.responses[:not_valid] = "Invalid username. Must be at least 3 characters long."
105 q.whitespace = :strip
106 end
107 username = "admin" if username.blank?
108 username
109 end
110
111 def prompt_for_admin_password
112 password = ask('Password (radiant): ', String) do |q|
113 q.echo = false unless defined?(::JRuby) # JRuby doesn't support stty interaction
114 q.validate = /^(|.{5,40})$/
115 q.responses[:not_valid] = "Invalid password. Must be at least 5 characters long."
116 q.whitespace = :strip
117 end
118 password = "radiant" if password.blank?
119 password
120 end
121
122 def find_template_in_path(filename)
123 (
124 [
125 filename,
126 "#{RADIANT_ROOT}/#{filename}",
127 "#{RADIANT_ROOT}/db/templates/#{filename}",
128 "#{Rails.root}/#{filename}",
129 "#{Rails.root}/db/templates/#{filename}",
130 "#{Dir.pwd}/#{filename}",
131 "#{Dir.pwd}/db/templates/#{filename}"
132 ] +
133 Dir.glob("#{RADIANT_ROOT}/vendor/extensions/**/db/templates/#{filename}") +
134 Dir.glob("#{Rails.root}/vendor/extensions/**/db/templates/#{filename}") +
135 Radiant::Extension.descendants.inject([]) do |r, d|
136 r << "#{d.root}/db/templates/#{filename}"
137 end
138 ).find { |name| File.file?(name) }
139 end
140
141 def find_and_load_templates(glob)
142 templates = Dir[glob]
143 templates.map! { |template| load_template_file(template) }
144 templates.sort_by { |template| template['name'] }
145 end
146
147 def load_template_file(filename)
148 YAML.load_file(filename)
149 end
150
151 def create_records(template)
152 records = template['records']
153 if records
154 puts
155 records.keys.each do |key|
156 feedback "Creating #{key.to_s.underscore.humanize}" do
157 model = model(key)
158 model.reset_column_information
159 record_pairs = order_by_id(records[key])
160 step do
161 record_pairs.each do |id, record|
162 model.new(record).save
163 end
164 end
165 end
166 end
167 end
168 end
169
170 def model(model_name)
171 model_name.to_s.singularize.constantize
172 end
173
174 def order_by_id(records)
175 records.map { |name, record| [record['id'], record] }.sort { |a, b| a[0] <=> b[0] }
176 end
177
178 extend Forwardable
179 def_delegators :terminal, :agree, :ask, :choose, :say
180
181 def terminal
182 @terminal ||= HighLine.new
183 end
184
185 def output
186 terminal.instance_variable_get("@output")
187 end
188
189 def wrap(string)
190 string = terminal.send(:wrap, string) unless terminal.wrap_at.nil?
191 string
192 end
193
194 def print(string)
195 output.print(wrap(string))
196 output.flush
197 end
198
199 def puts(string = "\n")
200 say string
201 end
202
203 def announce(string)
204 puts "\n#{string}"
205 end
206
207 def feedback(process, &block)
208 print "#{process}..."
209 if yield
210 puts "OK"
211 true
212 else
213 puts "FAILED"
214 false
215 end
216 rescue Exception => e
217 puts "FAILED"
218 raise e
219 end
220
221 def step
222 yield if block_given?
223 print '.'
224 end
225
226 end
227 en