maestrano-connector-rails 0.2.16 → 0.2.17
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +3 -0
- data/Gemfile.lock +27 -0
- data/VERSION +1 -1
- data/app/helpers/maestrano/connector/rails/session_helper.rb +5 -1
- data/app/models/maestrano/connector/rails/complex_entity.rb +14 -33
- data/app/models/maestrano/connector/rails/concerns/entity.rb +161 -73
- data/app/models/maestrano/connector/rails/sub_entity_base.rb +44 -4
- data/lib/generators/connector/install_generator.rb +20 -24
- data/lib/generators/connector/templates/home_controller.rb +33 -10
- data/lib/generators/connector/templates/home_controller_spec.rb +141 -0
- data/lib/generators/connector/templates/home_index.haml +103 -0
- data/lib/generators/connector/templates/layouts.haml +45 -0
- data/lib/generators/connector/templates/oauth_controller.rb +3 -6
- data/lib/generators/connector/templates/shared_entities_controller.rb +7 -0
- data/lib/generators/connector/templates/shared_entities_controller_spec.rb +23 -0
- data/lib/generators/connector/templates/shared_entities_index.haml +41 -0
- data/lib/generators/connector/templates/stylesheets/application.sass +24 -0
- data/lib/generators/connector/templates/stylesheets/banners.sass +59 -0
- data/lib/generators/connector/templates/stylesheets/home.sass +25 -0
- data/lib/generators/connector/templates/stylesheets/layout.sass +125 -0
- data/lib/generators/connector/templates/stylesheets/spacers.sass +46 -0
- data/lib/generators/connector/templates/stylesheets/variables.sass +57 -0
- data/lib/generators/connector/templates/sychronizations_controller_spec.rb +22 -0
- data/lib/generators/connector/templates/synchronizations_controller.rb +7 -0
- data/lib/generators/connector/templates/synchronizations_index.haml +42 -0
- data/maestrano-connector-rails.gemspec +29 -6
- data/pkg/maestrano-connector-rails-0.2.16.gem +0 -0
- data/spec/models/complex_entity_spec.rb +46 -12
- data/spec/models/entity_spec.rb +212 -113
- data/spec/models/sub_entity_base_spec.rb +59 -0
- data/template/maestrano-connector-template.rb +4 -3
- data/template/routes.rb +14 -0
- metadata +61 -5
- data/lib/generators/connector/templates/admin_controller.rb +0 -58
- data/lib/generators/connector/templates/admin_index.html.erb +0 -55
- data/lib/generators/connector/templates/home_index.html.erb +0 -48
@@ -14,19 +14,59 @@ module Maestrano::Connector::Rails
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def external_entity_name
|
17
|
-
if
|
18
|
-
|
17
|
+
if external?
|
18
|
+
entity_name
|
19
19
|
else
|
20
20
|
raise "Forbidden call"
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
24
|
def connec_entity_name
|
25
|
-
if
|
25
|
+
if external?
|
26
26
|
raise "Forbidden call"
|
27
27
|
else
|
28
|
-
|
28
|
+
entity_name
|
29
29
|
end
|
30
30
|
end
|
31
|
+
|
32
|
+
def names_hash
|
33
|
+
if external?
|
34
|
+
{external_entity: entity_name.downcase}
|
35
|
+
else
|
36
|
+
{connec_entity: entity_name.downcase}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def create_idmap_from_external_entity(entity, connec_entity_name, organization)
|
41
|
+
if external?
|
42
|
+
h = names_hash.merge({
|
43
|
+
external_id: get_id_from_external_entity_hash(entity),
|
44
|
+
name: object_name_from_external_entity_hash(entity),
|
45
|
+
connec_entity: connec_entity_name.downcase,
|
46
|
+
organization_id: organization.id
|
47
|
+
})
|
48
|
+
Maestrano::Connector::Rails::IdMap.create(h)
|
49
|
+
else
|
50
|
+
raise 'Forbidden call'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def create_idmap_from_connec_entity(entity, external_entity_name, organization)
|
55
|
+
if external?
|
56
|
+
raise 'Forbidden call'
|
57
|
+
else
|
58
|
+
h = names_hash.merge({
|
59
|
+
connec_id: entity['id'],
|
60
|
+
name: object_name_from_connec_entity_hash(entity),
|
61
|
+
external_entity: external_entity_name.downcase,
|
62
|
+
organization_id: organization.id
|
63
|
+
})
|
64
|
+
Maestrano::Connector::Rails::IdMap.create(h)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def map_external_entity_with_idmap(external_entity, connec_entity_name, idmap, organization)
|
69
|
+
{entity: map_to(connec_entity_name, external_entity, organization), idmap: idmap}
|
70
|
+
end
|
31
71
|
end
|
32
72
|
end
|
@@ -21,25 +21,6 @@ module Connector
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
def include_routes
|
25
|
-
sentinel = 'Rails.application.routes.draw do'
|
26
|
-
code_lines = [
|
27
|
-
"mount Maestrano::Connector::Rails::Engine, at: '/'\n",
|
28
|
-
"root 'home#index'",
|
29
|
-
"get 'home/index' => 'home#index'",
|
30
|
-
"get 'admin/index' => 'admin#index'",
|
31
|
-
"put 'admin/update' => 'admin#update'",
|
32
|
-
"post 'admin/synchronize' => 'admin#synchronize'",
|
33
|
-
"put 'admin/toggle_sync' => 'admin#toggle_sync'"
|
34
|
-
]
|
35
|
-
|
36
|
-
in_root do
|
37
|
-
gsub_file 'config/routes.rb', /(#{Regexp.escape(sentinel)})/mi do |match|
|
38
|
-
"#{match}\n #{code_lines.join("\n ")}\n"
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
24
|
def copy_entity
|
44
25
|
copy_file 'entity.rb', 'app/models/maestrano/connector/rails/entity.rb'
|
45
26
|
end
|
@@ -52,14 +33,29 @@ module Connector
|
|
52
33
|
copy_file 'example_entity.rb', 'app/models/entities/example_entitiy.rb'
|
53
34
|
end
|
54
35
|
|
55
|
-
def
|
36
|
+
def copy_controllers_and_views
|
56
37
|
copy_file 'home_controller.rb', 'app/controllers/home_controller.rb'
|
57
|
-
copy_file '
|
38
|
+
copy_file 'home_controller_spec.rb', 'spec/controllers/home_controller_spec.rb'
|
39
|
+
copy_file 'home_index.haml', 'app/views/home/index.html.haml'
|
40
|
+
|
41
|
+
copy_file 'synchronizations_controller.rb', 'app/controllers/synchronizations_controller.rb'
|
42
|
+
copy_file 'synchronizations_controller_spec.rb', 'spec/controllers/synchronizations_controller_spec.rb'
|
43
|
+
copy_file 'synchronizations_index.haml', 'app/views/synchronizations/index.html.haml'
|
44
|
+
|
45
|
+
copy_file 'shared_entities_controller.rb', 'app/controllers/shared_entities_controller.rb'
|
46
|
+
copy_file 'shared_entities_controller_spec.rb', 'spec/controllers/shared_entities_controller_spec.rb'
|
47
|
+
copy_file 'shared_entities_index.haml', 'app/views/shared_entities/index.html.haml'
|
48
|
+
|
49
|
+
copy_file 'layouts.haml', 'app/views/layouts/application.html.haml'
|
58
50
|
end
|
59
51
|
|
60
|
-
def
|
61
|
-
copy_file '
|
62
|
-
copy_file '
|
52
|
+
def copy_stylesheets
|
53
|
+
copy_file 'stylesheets/application.sass', 'app/assets/stylesheets/application.sass'
|
54
|
+
copy_file 'stylesheets/banners.sass', 'app/assets/stylesheets/banners.sass'
|
55
|
+
copy_file 'stylesheets/home.sass', 'app/assets/stylesheets/home.sass'
|
56
|
+
copy_file 'stylesheets/layout.sass', 'app/assets/stylesheets/layout.sass'
|
57
|
+
copy_file 'stylesheets/spacers.sass', 'app/assets/stylesheets/spacers.sass'
|
58
|
+
copy_file 'stylesheets/variables.sass', 'app/assets/stylesheets/variables.sass'
|
63
59
|
end
|
64
60
|
|
65
61
|
def copy_oauth_controller
|
@@ -1,17 +1,40 @@
|
|
1
|
-
# TODO
|
2
|
-
# This controller is given as an example of a possible home
|
3
|
-
# Home funcitons :
|
4
|
-
# * Display generic information about the connector (mostly for not connected users)
|
5
|
-
# * Link the connector to a Maestrano organization
|
6
|
-
# * Acces the last synchronization and a synchronization history (with their status)
|
7
1
|
class HomeController < ApplicationController
|
8
2
|
def index
|
9
|
-
|
10
|
-
|
3
|
+
@organization = current_organization
|
4
|
+
end
|
5
|
+
|
6
|
+
def update
|
7
|
+
organization = Maestrano::Connector::Rails::Organization.find_by_id(params[:id])
|
11
8
|
|
12
|
-
|
13
|
-
|
9
|
+
if organization && is_admin?(current_user, organization)
|
10
|
+
organization.synchronized_entities.keys.each do |entity|
|
11
|
+
if !!params["#{entity}"]
|
12
|
+
organization.synchronized_entities[entity] = true
|
13
|
+
else
|
14
|
+
organization.synchronized_entities[entity] = false
|
15
|
+
end
|
14
16
|
end
|
17
|
+
organization.save
|
18
|
+
end
|
19
|
+
|
20
|
+
redirect_to(:back)
|
21
|
+
end
|
22
|
+
|
23
|
+
def synchronize
|
24
|
+
if is_admin
|
25
|
+
Maestrano::Connector::Rails::SynchronizationJob.perform_later(current_organization, params['opts'] || {})
|
26
|
+
flash[:info] = 'Synchronization requested'
|
15
27
|
end
|
28
|
+
|
29
|
+
redirect_to(:back)
|
30
|
+
end
|
31
|
+
|
32
|
+
def toggle_sync
|
33
|
+
if is_admin
|
34
|
+
current_organization.update(sync_enabled: !current_organization.sync_enabled)
|
35
|
+
flash[:info] = current_organization.sync_enabled ? 'Synchronization enabled' : 'Synchronization disabled'
|
36
|
+
end
|
37
|
+
|
38
|
+
redirect_to(:back)
|
16
39
|
end
|
17
40
|
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe HomeController, :type => :controller do
|
4
|
+
let(:back_path) { home_index_path }
|
5
|
+
before(:each) do
|
6
|
+
request.env["HTTP_REFERER"] = back_path
|
7
|
+
end
|
8
|
+
|
9
|
+
describe 'index' do
|
10
|
+
subject { get :index }
|
11
|
+
|
12
|
+
it { expect(subject).to be_success }
|
13
|
+
end
|
14
|
+
|
15
|
+
describe 'update' do
|
16
|
+
let(:organization) { create(:organization, synchronized_entities: {'people' => false, 'item' => true}) }
|
17
|
+
subject { put :update, id: organization.id, 'people' => true, 'item' => false, 'lala' => true }
|
18
|
+
|
19
|
+
|
20
|
+
context 'when user is not admin' do
|
21
|
+
before {
|
22
|
+
allow_any_instance_of(Maestrano::Connector::Rails::SessionHelper).to receive(:is_admin?).and_return(false)
|
23
|
+
}
|
24
|
+
|
25
|
+
it { expect(subject).to redirect_to back_path }
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'when user is admin' do
|
29
|
+
before {
|
30
|
+
allow_any_instance_of(Maestrano::Connector::Rails::SessionHelper).to receive(:is_admin?).and_return(true)
|
31
|
+
}
|
32
|
+
|
33
|
+
it { expect(subject).to redirect_to back_path }
|
34
|
+
|
35
|
+
it 'updates organization' do
|
36
|
+
subject
|
37
|
+
organization.reload
|
38
|
+
expect(organization.synchronized_entities).to eq({'people' => true, 'item' => false})
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe 'synchronize' do
|
44
|
+
subject { post :synchronize }
|
45
|
+
let(:organization) { create(:organization) }
|
46
|
+
before {
|
47
|
+
allow_any_instance_of(Maestrano::Connector::Rails::SessionHelper).to receive(:current_organization).and_return(organization)
|
48
|
+
}
|
49
|
+
|
50
|
+
context 'when user is not admin' do
|
51
|
+
before {
|
52
|
+
allow_any_instance_of(ApplicationHelper).to receive(:is_admin).and_return(false)
|
53
|
+
}
|
54
|
+
|
55
|
+
it { expect(subject).to redirect_to back_path }
|
56
|
+
|
57
|
+
it 'does nothing' do
|
58
|
+
expect(Maestrano::Connector::Rails::SynchronizationJob).to_not receive(:perform_later)
|
59
|
+
subject
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'when user is admin' do
|
64
|
+
before {
|
65
|
+
allow_any_instance_of(ApplicationHelper).to receive(:is_admin).and_return(true)
|
66
|
+
}
|
67
|
+
|
68
|
+
it { expect(subject).to redirect_to back_path }
|
69
|
+
|
70
|
+
context 'with opts' do
|
71
|
+
subject { post :synchronize, opts: 'opts' }
|
72
|
+
|
73
|
+
it 'calls perform_later with opts' do
|
74
|
+
expect(Maestrano::Connector::Rails::SynchronizationJob).to receive(:perform_later).with(organization, 'opts')
|
75
|
+
subject
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context 'without opts' do
|
80
|
+
subject { post :synchronize}
|
81
|
+
|
82
|
+
it 'calls perform_later with empty opts hash' do
|
83
|
+
expect(Maestrano::Connector::Rails::SynchronizationJob).to receive(:perform_later).with(organization, {})
|
84
|
+
subject
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe 'toggle_sync' do
|
90
|
+
subject { put :toggle_sync }
|
91
|
+
let(:organization) { create(:organization, sync_enabled: true) }
|
92
|
+
before {
|
93
|
+
allow_any_instance_of(Maestrano::Connector::Rails::SessionHelper).to receive(:current_organization).and_return(organization)
|
94
|
+
}
|
95
|
+
|
96
|
+
context 'when user is not an admin' do
|
97
|
+
before {
|
98
|
+
allow_any_instance_of(ApplicationHelper).to receive(:is_admin).and_return(false)
|
99
|
+
}
|
100
|
+
it { expect(subject).to redirect_to back_path }
|
101
|
+
|
102
|
+
it 'does nothing' do
|
103
|
+
expect{ subject }.to_not change{ organization.sync_enabled }
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context 'when user is admin' do
|
108
|
+
before {
|
109
|
+
allow_any_instance_of(ApplicationHelper).to receive(:is_admin).and_return(true)
|
110
|
+
}
|
111
|
+
it { expect(subject).to redirect_to back_path }
|
112
|
+
|
113
|
+
it 'change sync_enabled from true to false' do
|
114
|
+
expect{ subject }.to change{ organization.sync_enabled }.from(true).to(false)
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'change sync_enabled from false to true' do
|
118
|
+
organization.update(sync_enabled: false)
|
119
|
+
expect{ subject }.to change{ organization.sync_enabled }.from(false).to(true)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
describe 'redirect_to_external' do
|
126
|
+
subject { get :redirect_to_external }
|
127
|
+
|
128
|
+
context 'when organization has a redirect url' do
|
129
|
+
let(:organization) { create(:organization, instance_url: 'url') }
|
130
|
+
before {
|
131
|
+
allow_any_instance_of(Maestrano::Connector::Rails::SessionHelper).to receive(:current_organization).and_return(organization)
|
132
|
+
}
|
133
|
+
|
134
|
+
it {expect(subject).to redirect_to('url')}
|
135
|
+
end
|
136
|
+
|
137
|
+
context 'otherwise' do
|
138
|
+
it {expect(subject).to redirect_to('https://login.salesforce.com')}
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
/ TODO
|
2
|
+
/ Replace ApplicationName
|
3
|
+
.home
|
4
|
+
.banners
|
5
|
+
.row
|
6
|
+
.col-md-10.col-md-offset-2
|
7
|
+
%h2 ApplicationName Connector
|
8
|
+
%p
|
9
|
+
Link your Maestrano account to ApplicationName to get your business in synch. Check the status of your connection on this screen.
|
10
|
+
|
11
|
+
.container
|
12
|
+
- if current_user
|
13
|
+
- unless is_admin
|
14
|
+
.row
|
15
|
+
.col-md-12.alert.alert-warning
|
16
|
+
Only administrators can modify the application settings
|
17
|
+
|
18
|
+
.row.link-step
|
19
|
+
.col-md-1.text-center.link-step-number
|
20
|
+
%span.badge.link-step-badge
|
21
|
+
1
|
22
|
+
.col-md-3.text-center
|
23
|
+
=image_tag "logos/default-white.png", class: 'logo'
|
24
|
+
.col-md-6.link-step-description
|
25
|
+
%h
|
26
|
+
Your Maestrano company <strong>#{@organization.name} (#{@organization.uid})</strong> is currently linked
|
27
|
+
.col-md-2.text-center.link-step-action
|
28
|
+
= link_to "Disconnect", Maestrano::Connector::Rails::Engine.routes.url_helpers.maestrano_signout_path, class: "btn btn-warning btn-lg #{is_admin ? '' : 'disabled'}"
|
29
|
+
|
30
|
+
.spacer1
|
31
|
+
|
32
|
+
.row.link-step
|
33
|
+
.col-md-1.text-center.link-step-number
|
34
|
+
%span.badge.link-step-badge
|
35
|
+
2
|
36
|
+
.col-md-3.text-center
|
37
|
+
=image_tag "logos/external.png", class: 'logo'
|
38
|
+
.col-md-6.link-step-description
|
39
|
+
%h
|
40
|
+
- if @organization.oauth_uid
|
41
|
+
Your ApplicationName account <strong>#{@organization.oauth_name} (#{@organization.oauth_uid})</strong> is currently linked
|
42
|
+
- else
|
43
|
+
Your ApplicationName account is not linked
|
44
|
+
.col-md-2.text-center.link-step-action
|
45
|
+
- if @organization.oauth_uid
|
46
|
+
/ TODO
|
47
|
+
= link_to "Disconnect", root_path, class: "btn btn-warning btn-lg #{is_admin ? '' : 'disabled'}"
|
48
|
+
- else
|
49
|
+
/ TODO
|
50
|
+
= link_to "Link to ApplicationName", root_path, class: "btn btn-warning btn-lg #{is_admin ? '' : 'disabled'}"
|
51
|
+
|
52
|
+
.spacer1
|
53
|
+
|
54
|
+
.row.link-step
|
55
|
+
.col-md-1.text-center.link-step-number
|
56
|
+
%span.badge.link-step-badge
|
57
|
+
3
|
58
|
+
.col-md-3.text-center
|
59
|
+
=image_tag "logos/sync.png", class: 'logo'
|
60
|
+
.col-md-6.link-step-description
|
61
|
+
%h
|
62
|
+
- if @organization.sync_enabled
|
63
|
+
Synchronizations are enabled
|
64
|
+
- else
|
65
|
+
Synchronizations are disabled
|
66
|
+
.col-md-2.text-center.link-step-action
|
67
|
+
= link_to "Toggle #{@organization.sync_enabled ? 'off' : 'on'}", home_toggle_sync_path, method: :put, class: "btn btn-warning btn-lg #{is_admin ? '' : 'disabled'}"
|
68
|
+
|
69
|
+
- else
|
70
|
+
.row
|
71
|
+
.col-md-4.col-md-offset-4.center
|
72
|
+
= link_to "Link your Maestrano account", Maestrano::Connector::Rails::Engine.routes.url_helpers.default_maestrano_auth_saml_index_path(tenant: :default), class: 'btn btn-warning'
|
73
|
+
|
74
|
+
- if @organization
|
75
|
+
.spacer2
|
76
|
+
.row
|
77
|
+
.col-md-12
|
78
|
+
%h1
|
79
|
+
Synchronized entities
|
80
|
+
%small You can customize which entities are synchronized by the connector
|
81
|
+
|
82
|
+
.spacer1
|
83
|
+
.row
|
84
|
+
.col-md-10.col-md-offset-1
|
85
|
+
= form_tag home_update_path(id: @organization.id), method: :put do
|
86
|
+
- @organization.synchronized_entities.each do |k, v|
|
87
|
+
.row.sync-entity
|
88
|
+
.col-md-1.link-step-action
|
89
|
+
%input{type: "checkbox", id: "#{k}", name: "#{k}", checked: v}
|
90
|
+
.col-md-7{style: 'padding-top: 5px;'}
|
91
|
+
%label{:for => "#{k}"} #{k.to_s.humanize}
|
92
|
+
-if is_admin
|
93
|
+
.col-md-4.text-right
|
94
|
+
- if v && @organization.oauth_uid
|
95
|
+
- if @organization.sync_enabled
|
96
|
+
= link_to "Force a synchronization for #{k.to_s.humanize.pluralize} only", home_synchronize_path(opts: {only_entities: [k.to_s]}), method: :post, class: "btn btn-warning btn-sm #{is_admin && @organization.sync_enabled ? '' : 'disabled'}"
|
97
|
+
- else
|
98
|
+
.btn.btn-warning.btn-sm.disabled= "Force a synchronization for #{k.to_s.humanize.pluralize} only"
|
99
|
+
-if is_admin
|
100
|
+
.spacer1
|
101
|
+
.row
|
102
|
+
.col-md-4.col-md-offset-8.text-right
|
103
|
+
=submit_tag :Save, class: "btn btn-primary"
|
@@ -0,0 +1,45 @@
|
|
1
|
+
/ TODO
|
2
|
+
/ Replace ApplicationName
|
3
|
+
!!!
|
4
|
+
%html
|
5
|
+
%head
|
6
|
+
%title ApplicationName Connector for Connec!™
|
7
|
+
= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true
|
8
|
+
= javascript_include_tag 'application', 'data-turbolinks-track' => true
|
9
|
+
= csrf_meta_tags
|
10
|
+
%body
|
11
|
+
.navbar.navbar-fixed-top.navbar-inverse
|
12
|
+
.container-fluid
|
13
|
+
.collapse.navbar-collapse#maestrano-navbar-collapse
|
14
|
+
%ul.nav.navbar-nav
|
15
|
+
=image_tag "logos/default.png", class: 'external-logo'
|
16
|
+
|
17
|
+
%ul.nav.navbar-nav
|
18
|
+
%li=link_to "Status", root_path
|
19
|
+
%li=link_to "History", synchronizations_index_path
|
20
|
+
-if is_admin
|
21
|
+
%li=link_to "Mapping", shared_entities_index_path
|
22
|
+
|
23
|
+
-if current_user
|
24
|
+
%ul.nav.navbar-nav.navbar-right
|
25
|
+
%li.dropdown
|
26
|
+
%a{:href=>"#", :class=>"dropdown-toggle", :'data-toggle'=>"dropdown", :role=>"button"}
|
27
|
+
Hi #{current_user.first_name} #{current_user.last_name}
|
28
|
+
|
29
|
+
.padding-navbar
|
30
|
+
|
31
|
+
.wrapper{style: 'margin-bottom: 15px; '}
|
32
|
+
- if flash.any?
|
33
|
+
- flash.each do |flash_key, message|
|
34
|
+
.alert{:class => "alert-#{flash_key}"}
|
35
|
+
%strong= flash.discard(flash_key)
|
36
|
+
|
37
|
+
= yield
|
38
|
+
|
39
|
+
.footer
|
40
|
+
.container
|
41
|
+
.row
|
42
|
+
.col-xs-12
|
43
|
+
.copyright
|
44
|
+
© Copyright #{Time.now.year} Maestrano Pty Ltd (ABN: 80 152 564 424).
|
45
|
+
All rights reserved. Various trademarks held by their respective owners.
|