chaltron 0.3.2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +2 -1
- data/app/assets/javascripts/chaltron.js +7 -5
- data/app/assets/javascripts/chaltron/datatables.js.coffee +2 -2
- data/app/assets/javascripts/chaltron/navbar.js.coffee +46 -0
- data/app/assets/javascripts/dataTables/dataTables.bootstrap4.min.js +8 -0
- data/app/assets/stylesheets/chaltron.scss +9 -9
- data/app/assets/stylesheets/chaltron/layout.scss +3 -29
- data/app/assets/stylesheets/chaltron/logs.scss +5 -0
- data/app/controllers/chaltron/ldap_controller.rb +5 -5
- data/app/controllers/chaltron/omniauth_callbacks_controller.rb +0 -1
- data/app/controllers/chaltron/sessions_controller.rb +0 -3
- data/app/controllers/chaltron/users_controller.rb +1 -3
- data/app/datatables/log_datatable.rb +3 -2
- data/app/helpers/chaltron/ldap_helper.rb +3 -3
- data/app/helpers/chaltron/logs_helper.rb +4 -13
- data/app/helpers/chaltron/users_helper.rb +20 -2
- data/app/helpers/chaltron_helper.rb +0 -16
- data/app/models/authorizable.rb +1 -1
- data/app/models/log.rb +10 -12
- data/app/models/user.rb +1 -5
- data/app/views/chaltron/ldap/multi_create.html.erb +39 -35
- data/app/views/chaltron/ldap/multi_new.html.erb +36 -30
- data/app/views/chaltron/ldap/search.html.erb +13 -10
- data/app/views/chaltron/logs/index.html.erb +22 -28
- data/app/views/chaltron/logs/show.html.erb +6 -6
- data/app/views/chaltron/users/_form.html.erb +8 -5
- data/app/views/chaltron/users/_side_filters.html.erb +10 -26
- data/app/views/chaltron/users/edit.html.erb +1 -1
- data/app/views/chaltron/users/index.html.erb +29 -31
- data/app/views/chaltron/users/new.html.erb +1 -1
- data/app/views/chaltron/users/self_edit.html.erb +27 -20
- data/app/views/chaltron/users/self_show.html.erb +8 -8
- data/app/views/chaltron/users/show.html.erb +10 -10
- data/app/views/devise/passwords/edit.html.erb +8 -7
- data/app/views/devise/passwords/new.html.erb +10 -7
- data/app/views/devise/sessions/_new_ldap.html.erb +17 -14
- data/app/views/devise/sessions/_new_local.html.erb +10 -7
- data/app/views/devise/sessions/new.html.erb +30 -25
- data/app/views/locales/en.yml +4 -4
- data/app/views/locales/it.yml +2 -2
- data/config/chaltron_navigation.rb +15 -23
- data/config/locales/en.yml +1 -6
- data/config/locales/it.yml +1 -6
- data/config/routes.rb +4 -5
- data/lib/chaltron.rb +1 -24
- data/lib/chaltron/bootstrap_form.rb +1 -1
- data/lib/chaltron/engine.rb +2 -3
- data/lib/chaltron/ldap/connection.rb +34 -93
- data/lib/chaltron/ldap/person.rb +3 -13
- data/lib/chaltron/ldap/user.rb +1 -5
- data/lib/chaltron/version.rb +1 -1
- data/lib/generators/chaltron/install_generator.rb +9 -3
- data/lib/generators/chaltron/templates/app/assets/stylesheets/home.scss +2 -20
- data/lib/generators/chaltron/templates/app/views/home/_carousel.html.erb +18 -18
- data/lib/generators/chaltron/templates/app/views/home/_faq.html.erb +10 -8
- data/lib/generators/chaltron/templates/app/views/home/_panel.html.erb +5 -7
- data/lib/generators/chaltron/templates/app/views/home/index.html.erb +12 -10
- data/lib/generators/chaltron/templates/app/views/home/test.html.erb +3 -5
- data/lib/generators/chaltron/templates/app/views/layouts/_footer.html.erb +7 -9
- data/lib/generators/chaltron/templates/app/views/layouts/_navbar.html.erb +8 -15
- data/lib/generators/chaltron/templates/config/initializers/chaltron.rb +4 -57
- data/lib/generators/chaltron/templates/config/navigation.rb +11 -10
- data/lib/templates/erb/scaffold/_form.html.erb +8 -5
- data/lib/templates/erb/scaffold/edit.html.erb +8 -8
- data/lib/templates/erb/scaffold/index.html.erb +15 -21
- data/lib/templates/erb/scaffold/new.html.erb +2 -6
- data/lib/templates/erb/scaffold/show.html.erb +9 -9
- metadata +24 -25
- data/app/assets/javascripts/dataTables/extras/dataTables.select.min.js +0 -23
- data/app/assets/stylesheets/dataTables/dataTables.bootstrap.css +0 -167
- data/app/assets/stylesheets/dataTables/extras/select.dataTables.css +0 -100
- data/app/models/application_record.rb +0 -3
- data/app/views/chaltron/logs/_log.html.erb +0 -14
data/lib/chaltron/engine.rb
CHANGED
@@ -2,7 +2,7 @@ require 'devise'
|
|
2
2
|
require 'cancancan'
|
3
3
|
require 'omniauth'
|
4
4
|
require 'omniauth-ldap'
|
5
|
-
require 'bootstrap
|
5
|
+
require 'bootstrap'
|
6
6
|
require 'autoprefixer-rails'
|
7
7
|
require 'font-awesome-sass'
|
8
8
|
require 'simple-navigation'
|
@@ -11,9 +11,8 @@ require 'bootstrap_form'
|
|
11
11
|
require 'nprogress-rails'
|
12
12
|
require 'rails-i18n'
|
13
13
|
require 'jquery-rails'
|
14
|
-
require 'jquery-datatables
|
14
|
+
require 'jquery-datatables'
|
15
15
|
|
16
|
-
require 'simple_navigation_bootstrap'
|
17
16
|
SimpleNavigation.config_file_paths << File.expand_path('../../../config', __FILE__)
|
18
17
|
|
19
18
|
module Chaltron
|
@@ -4,12 +4,6 @@ require 'chaltron/ldap/person'
|
|
4
4
|
module Chaltron
|
5
5
|
module LDAP
|
6
6
|
class Connection
|
7
|
-
NET_LDAP_ENCRYPTION_METHOD = {
|
8
|
-
simple_tls: :simple_tls,
|
9
|
-
start_tls: :start_tls,
|
10
|
-
plain: nil
|
11
|
-
}.freeze
|
12
|
-
|
13
7
|
attr_reader :ldap
|
14
8
|
|
15
9
|
def initialize(params = {})
|
@@ -22,9 +16,7 @@ module Chaltron
|
|
22
16
|
end
|
23
17
|
|
24
18
|
def find_by_uid(id)
|
25
|
-
|
26
|
-
opts[uid.to_sym] = id
|
27
|
-
ret = find_user(opts)
|
19
|
+
find_user(uid: id)
|
28
20
|
end
|
29
21
|
|
30
22
|
def find_user(*args)
|
@@ -55,31 +47,35 @@ module Chaltron
|
|
55
47
|
scope: Net::LDAP::SearchScope_BaseObject
|
56
48
|
}
|
57
49
|
else
|
58
|
-
filters =
|
59
|
-
|
60
|
-
Net::LDAP::Filter.eq(
|
50
|
+
filters = []
|
51
|
+
fields.each do |field|
|
52
|
+
filters << Net::LDAP::Filter.eq(field, args[field])
|
61
53
|
end
|
62
54
|
options = {
|
63
55
|
base: base,
|
64
56
|
filter: filters.inject { |sum, n| Net::LDAP::Filter.join(sum, n) }
|
65
57
|
}
|
66
58
|
end
|
67
|
-
options.merge!(size: limit) unless limit.nil?
|
68
|
-
ldap_search(options).map do |entry|
|
69
|
-
Chaltron::LDAP::Person.new(entry, uid) if entry.respond_to? uid
|
70
|
-
end.compact
|
71
|
-
end
|
72
59
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
filter:
|
77
|
-
|
78
|
-
|
79
|
-
|
60
|
+
# if config.user_filter.present?
|
61
|
+
# user_filter = Net::LDAP::Filter.construct(config.user_filter)
|
62
|
+
|
63
|
+
# options[:filter] = if options[:filter]
|
64
|
+
# Net::LDAP::Filter.join(options[:filter], user_filter)
|
65
|
+
# else
|
66
|
+
# user_filter
|
67
|
+
# end
|
68
|
+
# end
|
80
69
|
|
81
|
-
|
82
|
-
|
70
|
+
options.merge!(size: limit) if limit.present?
|
71
|
+
|
72
|
+
entries = ldap_search(options).select do |entry|
|
73
|
+
entry.respond_to? uid
|
74
|
+
end
|
75
|
+
|
76
|
+
entries.map do |entry|
|
77
|
+
Chaltron::LDAP::Person.new(entry, uid)
|
78
|
+
end
|
83
79
|
end
|
84
80
|
|
85
81
|
private
|
@@ -88,21 +84,15 @@ module Chaltron
|
|
88
84
|
Devise.omniauth_configs[:ldap].options
|
89
85
|
end
|
90
86
|
|
91
|
-
def translate_field field
|
92
|
-
return uid if field.to_sym == :uid
|
93
|
-
return Chaltron.ldap_field_mappings[field.to_sym] unless Chaltron.ldap_field_mappings[field.to_sym].nil?
|
94
|
-
field
|
95
|
-
end
|
96
|
-
|
97
87
|
def adapter_options
|
98
|
-
|
88
|
+
{
|
99
89
|
host: options[:host],
|
100
90
|
port: options[:port],
|
101
|
-
encryption:
|
91
|
+
encryption: encryption,
|
102
92
|
verbose: true
|
103
|
-
}
|
104
|
-
|
105
|
-
|
93
|
+
}.tap do |options|
|
94
|
+
options.merge!(auth_options) if has_auth?
|
95
|
+
end
|
106
96
|
end
|
107
97
|
|
108
98
|
def base
|
@@ -113,64 +103,15 @@ module Chaltron
|
|
113
103
|
options[:uid]
|
114
104
|
end
|
115
105
|
|
116
|
-
def
|
117
|
-
method
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
}
|
123
|
-
end
|
124
|
-
|
125
|
-
def translate_method
|
126
|
-
NET_LDAP_ENCRYPTION_METHOD[options[:encryption]&.to_sym]
|
127
|
-
end
|
128
|
-
|
129
|
-
def tls_options
|
130
|
-
return @tls_options if defined?(@tls_options)
|
131
|
-
|
132
|
-
method = translate_method
|
133
|
-
return unless method
|
134
|
-
|
135
|
-
opts = if options[:disable_verify_certificates]
|
136
|
-
# It is important to explicitly set verify_mode for two reasons:
|
137
|
-
# 1. The behavior of OpenSSL is undefined when verify_mode is not set.
|
138
|
-
# 2. The net-ldap gem implementation verifies the certificate hostname
|
139
|
-
# unless verify_mode is set to VERIFY_NONE.
|
140
|
-
{ verify_mode: OpenSSL::SSL::VERIFY_NONE }
|
106
|
+
def encryption
|
107
|
+
case options[:method].to_s
|
108
|
+
when 'ssl'
|
109
|
+
:simple_tls
|
110
|
+
when 'tls'
|
111
|
+
:start_tls
|
141
112
|
else
|
142
|
-
|
143
|
-
OpenSSL::SSL::SSLContext::DEFAULT_PARAMS.dup
|
144
|
-
end
|
145
|
-
|
146
|
-
opts.merge!(custom_tls_options)
|
147
|
-
|
148
|
-
@tls_options = opts
|
149
|
-
end
|
150
|
-
|
151
|
-
def custom_tls_options
|
152
|
-
return {} unless options['tls_options']
|
153
|
-
|
154
|
-
# Dup so we don't overwrite the original value
|
155
|
-
custom_options = options['tls_options'].dup.delete_if { |_, value| value.nil? || value.blank? }
|
156
|
-
custom_options.symbolize_keys!
|
157
|
-
|
158
|
-
if custom_options[:cert]
|
159
|
-
begin
|
160
|
-
custom_options[:cert] = OpenSSL::X509::Certificate.new(custom_options[:cert])
|
161
|
-
rescue OpenSSL::X509::CertificateError => e
|
162
|
-
Rails.logger.error "LDAP TLS Options 'cert' is invalid for provider #{provider}: #{e.message}"
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
|
-
if custom_options[:key]
|
167
|
-
begin
|
168
|
-
custom_options[:key] = OpenSSL::PKey.read(custom_options[:key])
|
169
|
-
rescue OpenSSL::PKey::PKeyError => e
|
170
|
-
Rails.logger.error "LDAP TLS Options 'key' is invalid for provider #{provider}: #{e.message}"
|
171
|
-
end
|
113
|
+
nil
|
172
114
|
end
|
173
|
-
custom_options
|
174
115
|
end
|
175
116
|
|
176
117
|
def auth_options
|
data/lib/chaltron/ldap/person.rb
CHANGED
@@ -44,17 +44,11 @@ module Chaltron
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def department
|
47
|
-
entry.
|
47
|
+
entry.department.first rescue nil
|
48
48
|
end
|
49
49
|
|
50
50
|
def name
|
51
|
-
|
52
|
-
first_name = entry.send(Chaltron.ldap_field_mappings[:first_name]).first
|
53
|
-
last_name = entry.send(Chaltron.ldap_field_mappings[:last_name]).first
|
54
|
-
"#{first_name} #{last_name}"
|
55
|
-
else
|
56
|
-
entry.send(Chaltron.ldap_field_mappings[:full_name]).first
|
57
|
-
end
|
51
|
+
entry.cn.first
|
58
52
|
end
|
59
53
|
|
60
54
|
def uid
|
@@ -66,7 +60,7 @@ module Chaltron
|
|
66
60
|
end
|
67
61
|
|
68
62
|
def email
|
69
|
-
entry.
|
63
|
+
entry.mail.first rescue nil
|
70
64
|
end
|
71
65
|
|
72
66
|
def dn
|
@@ -77,10 +71,6 @@ module Chaltron
|
|
77
71
|
'ldap'
|
78
72
|
end
|
79
73
|
|
80
|
-
def ldap_groups
|
81
|
-
self.class.ldap.find_groups_by_member(self)
|
82
|
-
end
|
83
|
-
|
84
74
|
private
|
85
75
|
|
86
76
|
def self.ldap
|
data/lib/chaltron/ldap/user.rb
CHANGED
@@ -20,11 +20,7 @@ module Chaltron
|
|
20
20
|
entry = Chaltron::LDAP::Person.find_by_uid(username)
|
21
21
|
if user.nil? and create
|
22
22
|
# create user
|
23
|
-
|
24
|
-
roles = entry.ldap_groups.map do |e|
|
25
|
-
Chaltron.ldap_role_mappings[e.dn]
|
26
|
-
end.compact if !Chaltron.ldap_role_mappings.blank?
|
27
|
-
user = entry.create_user(roles)
|
23
|
+
user = entry.create_user Chaltron.default_roles
|
28
24
|
end
|
29
25
|
update_ldap_attributes(user, entry) unless user.nil?
|
30
26
|
user
|
data/lib/chaltron/version.rb
CHANGED
@@ -15,6 +15,15 @@ module Chaltron
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
+
def gemfile
|
19
|
+
gem 'bootstrap_form',
|
20
|
+
git: 'https://github.com/bootstrap-ruby/rails-bootstrap-forms.git',
|
21
|
+
branch: 'master'
|
22
|
+
Bundler.with_clean_env do
|
23
|
+
run 'bundle install'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
18
27
|
def db_migrations
|
19
28
|
rake 'chaltron_engine:install:migrations'
|
20
29
|
end
|
@@ -42,9 +51,6 @@ RUBY
|
|
42
51
|
# javascript
|
43
52
|
inject_into_file 'app/assets/javascripts/application.js',
|
44
53
|
"//= require chaltron\n", before: '//= require_tree .'
|
45
|
-
# css
|
46
|
-
inject_into_file 'app/assets/stylesheets/application.css',
|
47
|
-
" *= require chaltron\n", before: ' *= require_self'
|
48
54
|
end
|
49
55
|
|
50
56
|
def create_index_controller
|
@@ -2,29 +2,11 @@
|
|
2
2
|
// They will automatically be included in application.css.
|
3
3
|
// You can use Sass (SCSS) here: http://sass-lang.com/
|
4
4
|
|
5
|
+
@import 'chaltron';
|
6
|
+
|
5
7
|
#main-carousel {
|
6
8
|
img {
|
7
9
|
height: 400px;
|
8
10
|
width: 100%;
|
9
11
|
}
|
10
|
-
margin-bottom: 60px;
|
11
|
-
}
|
12
|
-
|
13
|
-
.marketing {
|
14
|
-
img.img-circle {
|
15
|
-
height: 140px;
|
16
|
-
width: 140px;
|
17
|
-
}
|
18
|
-
.col-lg-4 {
|
19
|
-
margin-bottom: 20px;
|
20
|
-
text-align: center;
|
21
|
-
}
|
22
|
-
}
|
23
|
-
|
24
|
-
.featurette-divider {
|
25
|
-
margin: 80px 0;
|
26
|
-
}
|
27
|
-
|
28
|
-
.featurette-heading {
|
29
|
-
font-size: 50px;
|
30
12
|
}
|
@@ -1,39 +1,39 @@
|
|
1
|
-
<div id='main-carousel' class='carousel slide' data-ride='carousel'>
|
2
|
-
|
3
|
-
<ol class=
|
4
|
-
<li data-target=
|
5
|
-
<li data-target=
|
6
|
-
<li data-target=
|
1
|
+
<div id='main-carousel' class='carousel slide border-bottom border-primary' data-ride='carousel'>
|
2
|
+
|
3
|
+
<ol class="carousel-indicators">
|
4
|
+
<li data-target="#main-carousel" data-slide-to="0" class="active"></li>
|
5
|
+
<li data-target="#main-carousel" data-slide-to="1"></li>
|
6
|
+
<li data-target="#main-carousel" data-slide-to="2"></li>
|
7
7
|
</ol>
|
8
8
|
|
9
|
-
<!-- Wrapper for slides -->
|
10
9
|
<div class='carousel-inner'>
|
11
|
-
<div class='item active'>
|
10
|
+
<div class='carousel-item active'>
|
12
11
|
<%= image_tag('slide1.gif') %>
|
13
|
-
<div class='carousel-caption'>
|
12
|
+
<div class='carousel-caption d-none d-md-block'>
|
14
13
|
<h1>Chaltron rulez!</h1>
|
15
14
|
</div>
|
16
15
|
</div>
|
17
|
-
<div class='item'>
|
16
|
+
<div class='carousel-item'>
|
18
17
|
<%= image_tag('slide2.gif') %>
|
19
|
-
<div class='carousel-caption'>
|
18
|
+
<div class='carousel-caption d-none d-md-block'>
|
20
19
|
<h1>Wow!</h1>
|
21
20
|
<p>Looking good</p>
|
22
21
|
</div>
|
23
22
|
</div>
|
24
|
-
<div class='item'>
|
23
|
+
<div class='carousel-item'>
|
25
24
|
<%= image_tag('slide3.gif') %>
|
26
|
-
<div class='carousel-caption'>
|
25
|
+
<div class='carousel-caption d-none d-md-block'>
|
27
26
|
<h1>Not bad!</h1>
|
28
27
|
</div>
|
29
28
|
</div>
|
30
29
|
</div>
|
31
30
|
|
32
|
-
|
33
|
-
|
34
|
-
<span class=
|
31
|
+
<a class="carousel-control-prev" href="#main-carousel" role="button" data-slide="prev">
|
32
|
+
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
|
33
|
+
<span class="sr-only">Previous</span>
|
35
34
|
</a>
|
36
|
-
<a class=
|
37
|
-
<span class=
|
35
|
+
<a class="carousel-control-next" href="#main-carousel" role="button" data-slide="next">
|
36
|
+
<span class="carousel-control-next-icon" aria-hidden="true"></span>
|
37
|
+
<span class="sr-only">Next</span>
|
38
38
|
</a>
|
39
39
|
</div>
|
@@ -1,11 +1,13 @@
|
|
1
|
-
<div class=
|
2
|
-
|
3
|
-
<
|
4
|
-
<%= link_to raw(faq[:question]), "#collapse#{faq_counter}", data: {toggle: 'collapse'
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
1
|
+
<div class='card'>
|
2
|
+
<%= content_tag :div, id: "heading#{faq_counter}", class: 'card-header', role: 'tab' do %>
|
3
|
+
<h5 class='mb-0'>
|
4
|
+
<%= link_to raw(faq[:question]), "#collapse#{faq_counter}", data: {toggle: 'collapse'},
|
5
|
+
role: 'button', aria: {expanded: false, controls: "collapse#{faq_counter}"}, class: 'collapsed' %>
|
6
|
+
</h5>
|
7
|
+
<% end %>
|
8
|
+
<%= content_tag :div, id: "collapse#{faq_counter}", class: 'collapse', role: 'tabpanel',
|
9
|
+
data: { parent: '#accordion'}, aria: {labelledby: "heading#{faq_counter}"} do %>
|
10
|
+
<div class="card-body">
|
9
11
|
<%= raw faq[:answer] %>
|
10
12
|
</div>
|
11
13
|
<% end %>
|
@@ -4,15 +4,13 @@
|
|
4
4
|
link_text ||= 'Learn more'
|
5
5
|
%>
|
6
6
|
|
7
|
-
<div class=
|
8
|
-
<div class=
|
9
|
-
<
|
10
|
-
<h4>
|
7
|
+
<div class='col-md-4'>
|
8
|
+
<div class='card'>
|
9
|
+
<h5 class='card-header text-white bg-primary'>
|
11
10
|
<%= icon(icon) %>
|
12
11
|
<%= title %>
|
13
|
-
|
14
|
-
|
15
|
-
<div class="panel-body">
|
12
|
+
</h5>
|
13
|
+
<div class='card-body'>
|
16
14
|
<p><%= raw body %> </p>
|
17
15
|
<%= link_to link_text, link, class: 'btn btn-primary' %>
|
18
16
|
</div>
|
@@ -1,13 +1,12 @@
|
|
1
1
|
<%= render 'carousel' %>
|
2
|
-
|
3
2
|
<div class='container-fluid'>
|
4
3
|
<div class='row'>
|
5
4
|
<div class='col-lg-12'>
|
6
|
-
<h1 class='
|
5
|
+
<h1 class='pt-4'>
|
7
6
|
Welcome to Chaltron! <small> aka Muffaster reloaded</small>
|
8
7
|
</h1>
|
9
8
|
</div>
|
10
|
-
<%= render 'panel', title: 'Bootstrap
|
9
|
+
<%= render 'panel', title: 'Bootstrap v4',
|
11
10
|
link: 'http://getbootstrap.com', icon: 'check',
|
12
11
|
body: 'Bootstrap rocks! And there are lots of good template ready to ' \
|
13
12
|
'use, and free. Just as this one ;-)'
|
@@ -23,9 +22,11 @@
|
|
23
22
|
%>
|
24
23
|
</div>
|
25
24
|
|
25
|
+
<hr>
|
26
|
+
|
26
27
|
<div class='row'>
|
27
28
|
<div class='col-lg-12'>
|
28
|
-
<h2 class='
|
29
|
+
<h2 class='pt-4'>Main features</h2>
|
29
30
|
</div>
|
30
31
|
<div class='col-md-6'>
|
31
32
|
<p>Chaltron provides:</p>
|
@@ -33,7 +34,7 @@
|
|
33
34
|
<li>
|
34
35
|
<%= icon('check-square', '', class: 'fa-li') %>
|
35
36
|
Compatibility with
|
36
|
-
<strong><%= link_to 'Bootstrap
|
37
|
+
<strong><%= link_to 'Bootstrap v4', 'http://getbootstrap.com' %></strong> and
|
37
38
|
<strong><%= link_to 'Font Awesome v4', 'http://fortawesome.github.io/Font-Awesome/' %></strong>
|
38
39
|
</li>
|
39
40
|
<li>
|
@@ -54,13 +55,15 @@
|
|
54
55
|
taste <strong>L</strong>ight <strong>S</strong>peed <strong>A</strong>pplication <strong>D</strong>evelopment!</p>
|
55
56
|
</div>
|
56
57
|
<div class='col-md-6'>
|
57
|
-
<%= image_tag('700x300.gif', class: 'img-responsive img-thumbnail') %>
|
58
|
+
<%= image_tag('700x300.gif', class: 'img-responsive img-thumbnail border border-primary') %>
|
58
59
|
</div>
|
59
60
|
</div>
|
60
61
|
|
62
|
+
<hr>
|
63
|
+
|
61
64
|
<div class='row'%>
|
62
65
|
<div class='col-lg-12'>
|
63
|
-
<h2 class='
|
66
|
+
<h2 class='pt-4'>FAQ</h2>
|
64
67
|
</div>
|
65
68
|
</div>
|
66
69
|
|
@@ -85,11 +88,10 @@
|
|
85
88
|
}
|
86
89
|
%>
|
87
90
|
|
88
|
-
<div class='row'
|
91
|
+
<div class='row'>
|
89
92
|
<div class='col-lg-12'>
|
90
|
-
<div
|
93
|
+
<div id='accordion' role='tablist'>
|
91
94
|
<%= render partial: 'faq', collection: faqs %>
|
92
|
-
</div>
|
93
95
|
</div>
|
94
96
|
</div>
|
95
97
|
|