mumuki-laboratory 9.3.0 → 9.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/javascripts/mumuki_laboratory/application/discussions.js +15 -0
- data/app/assets/javascripts/mumuki_laboratory/application/faqs.js +6 -6
- data/app/assets/javascripts/mumuki_laboratory/application/toast.js +12 -1
- data/app/assets/stylesheets/mumuki_laboratory/application/modules/_discussion.scss +0 -25
- data/app/assets/stylesheets/mumuki_laboratory/application/modules/_faqs.scss +4 -3
- data/app/controllers/discussions_controller.rb +29 -2
- data/app/controllers/exam_authorization_requests_controller.rb +7 -1
- data/app/helpers/discussions_helper.rb +26 -2
- data/app/views/discussions/_description_message.html.erb +1 -1
- data/app/views/discussions/_message.html.erb +1 -1
- data/app/views/discussions/_new_message.html.erb +1 -1
- data/app/views/discussions/new.html.erb +1 -1
- data/app/views/discussions/show.html.erb +6 -0
- data/app/views/exam_authorization_requests/_approved.html.erb +1 -1
- data/app/views/exam_authorization_requests/_pending.html.erb +2 -2
- data/app/views/exam_registrations/show.html.erb +3 -2
- data/app/views/exercises/_read_only.html.erb +5 -6
- data/app/views/faqs/index.html.erb +1 -1
- data/app/views/layouts/_discussions.html.erb +3 -3
- data/app/views/layouts/_main.html.erb +4 -10
- data/app/views/layouts/_toast.html.erb +7 -0
- data/app/views/users/activity.html.erb +8 -6
- data/app/views/users/exam_authorizations.html.erb +1 -1
- data/config/routes.rb +2 -0
- data/lib/mumuki/laboratory/locales/en.yml +13 -5
- data/lib/mumuki/laboratory/locales/es-CL.yml +12 -3
- data/lib/mumuki/laboratory/locales/es.yml +12 -4
- data/lib/mumuki/laboratory/locales/pt.yml +12 -4
- data/lib/mumuki/laboratory/version.rb +1 -1
- data/lib/tasks/exams.rake +15 -0
- data/spec/controllers/discussions_controller_spec.rb +42 -0
- data/spec/dummy/db/schema.rb +3 -3
- data/spec/features/discussion_flow_spec.rb +38 -14
- data/spec/features/user_activity_flow_spec.rb +30 -10
- data/spec/helpers/with_navigation_spec.rb +22 -0
- metadata +114 -112
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 37091ade7a9c0bad80f16a5276b2748692a49cc11708613036dacd1042b9cdc2
|
4
|
+
data.tar.gz: 949f97b3a7e9e920f47cb1748a66d0f6951f4d45a2021cacad43e4a1122b4a8f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 34d25e182a43788fc032bd77c6472c497a55d08cb6d776575441aee4b5ec11aa55a3f95a7b3d1300a47e43006764494dab809f1a8114076d50cadeb0b59d1021
|
7
|
+
data.tar.gz: c9dd87e5ac8d17873aaa3e28a442b4c87225bb31b4573385e0906e852fa58b9c778ad61cf2e0c8e48ca2c90edba9844064ed260ca276ff95511c69ba4af5f44a
|
@@ -1,6 +1,7 @@
|
|
1
1
|
mumuki.load(() => {
|
2
2
|
var $subscriptionButtons = $('.discussion-subscription > button');
|
3
3
|
var $upvoteButtons = $('.discussion-upvote > button');
|
4
|
+
var $responsibleButton = $('.discussion-responsible > button');
|
4
5
|
let $messagePreviewButton = $('.discussion-new-message-preview-button.preview');
|
5
6
|
let $messageEditButton = $('.discussion-new-message-preview-button.edit');
|
6
7
|
let $newMessageContent = $('.discussion-new-message-content');
|
@@ -53,9 +54,23 @@ mumuki.load(() => {
|
|
53
54
|
discussionUpvote: function (url) {
|
54
55
|
Forum.discussionPostAndToggle(url, $upvoteButtons);
|
55
56
|
},
|
57
|
+
discussionResponsible: function (url) {
|
58
|
+
Forum.discussionPostToggleAndRenderToast(url, $responsibleButton);
|
59
|
+
$('.responsible-moderator-badge').toggleClass('d-none');
|
60
|
+
},
|
56
61
|
discussionPostAndToggle: function (url, elem) {
|
57
62
|
Forum.discussionPost(url).done(Forum.toggleButton(elem));
|
58
63
|
},
|
64
|
+
discussionPostToggleAndRenderToast: function (url, elem) {
|
65
|
+
Forum.discussionPost(url)
|
66
|
+
.done(function (response) {
|
67
|
+
Forum.toggleButton(elem);
|
68
|
+
mumuki.toast.addToast(response);
|
69
|
+
})
|
70
|
+
.fail(function (response) {
|
71
|
+
mumuki.toast.addToast(response.responseText);
|
72
|
+
});
|
73
|
+
},
|
59
74
|
discussionMessageToggleApprove: function (url, elem) {
|
60
75
|
Forum.discussionPost(url).done(function () {
|
61
76
|
elem.toggleClass("selected");
|
@@ -25,9 +25,9 @@ mumuki.faqs = class {
|
|
25
25
|
_createNavbar() {
|
26
26
|
const $faqsNavbar = $(".mu-faqs-navbar nav ul");
|
27
27
|
$('.mu-faqs-group').each((_index, faqGroup) => {
|
28
|
-
const $navItem = this._createNavbarItem($faqsNavbar, faqGroup)
|
28
|
+
const $navItem = this._createNavbarItem($faqsNavbar, faqGroup);
|
29
29
|
const $faqGroup = $(faqGroup);
|
30
|
-
this._configureClickFor($navItem, $faqGroup, $faqsNavbar)
|
30
|
+
this._configureClickFor($navItem, $faqGroup, $faqsNavbar);
|
31
31
|
});
|
32
32
|
}
|
33
33
|
|
@@ -56,12 +56,12 @@ mumuki.faqs = class {
|
|
56
56
|
}
|
57
57
|
|
58
58
|
_createFaqsIcons() {
|
59
|
-
const $faqIcon = $('<i class="mu-faqs-group-icon
|
59
|
+
const $faqIcon = $('<i class="mu-faqs-group-icon fas fa-chevron-down">');
|
60
60
|
$faqIcon.click(function(e){
|
61
61
|
const $elem = $(this);
|
62
|
-
$elem.toggleClass('fa-
|
62
|
+
$elem.toggleClass('fa-chevron-down fa-chevron-up');
|
63
63
|
$elem.closest('.mu-faqs-group').toggleClass('active');
|
64
|
-
})
|
64
|
+
});
|
65
65
|
$('.mu-faqs-group').prepend($faqIcon);
|
66
66
|
}
|
67
67
|
|
@@ -75,7 +75,7 @@ mumuki.faqs = class {
|
|
75
75
|
newGroup = [];
|
76
76
|
}
|
77
77
|
newGroup.push(elem);
|
78
|
-
previousNodeName = elem.nodeName
|
78
|
+
previousNodeName = elem.nodeName;
|
79
79
|
});
|
80
80
|
|
81
81
|
elemsGroups.push(newGroup);
|
@@ -1,3 +1,14 @@
|
|
1
|
+
mumuki.toast = {
|
2
|
+
load() {
|
3
|
+
document.querySelectorAll('.toast').forEach((toast) => new bootstrap.Toast(toast).show());
|
4
|
+
},
|
5
|
+
|
6
|
+
addToast(content) {
|
7
|
+
$('.toast-container').html(content);
|
8
|
+
mumuki.toast.load();
|
9
|
+
}
|
10
|
+
};
|
11
|
+
|
1
12
|
mumuki.load(() => {
|
2
|
-
|
13
|
+
mumuki.toast.load();
|
3
14
|
});
|
@@ -394,31 +394,6 @@ $moderator-badge-color: #dd9900;
|
|
394
394
|
}
|
395
395
|
}
|
396
396
|
|
397
|
-
$statuses: (
|
398
|
-
closed: ($danger white $danger),
|
399
|
-
opened: (white #333333 #eaeaea),
|
400
|
-
solved: ($success white $success),
|
401
|
-
pending_review: ($success white $success)
|
402
|
-
);
|
403
|
-
|
404
|
-
@each $status, $style in $statuses {
|
405
|
-
$background-color: nth($style, 1);
|
406
|
-
$font-color: nth($style, 2);
|
407
|
-
$border-color: nth($style, 3);
|
408
|
-
|
409
|
-
.btn-discussion-#{$status} {
|
410
|
-
background-color: $background-color;
|
411
|
-
color: $font-color;
|
412
|
-
border-color: $border-color;
|
413
|
-
margin-left: 5px;
|
414
|
-
&:hover {
|
415
|
-
color: $font-color;
|
416
|
-
border-color: darken($border-color, 3%);
|
417
|
-
background-color: darken($background-color, 3%);
|
418
|
-
}
|
419
|
-
}
|
420
|
-
}
|
421
|
-
|
422
397
|
.discussion-requires-attention {
|
423
398
|
margin-right: 20px;
|
424
399
|
label {
|
@@ -8,7 +8,7 @@
|
|
8
8
|
|
9
9
|
width: 100%;
|
10
10
|
|
11
|
-
@media
|
11
|
+
@include media-breakpoint-up(md) {
|
12
12
|
width: 75%;
|
13
13
|
float: right;
|
14
14
|
}
|
@@ -17,8 +17,9 @@
|
|
17
17
|
padding: 32px;
|
18
18
|
box-shadow: 0 0.375em 2.8125em 0 #d2d5d9;
|
19
19
|
margin-bottom: 32px;
|
20
|
+
word-wrap: anywhere;
|
20
21
|
|
21
|
-
@media
|
22
|
+
@include media-breakpoint-down(md) {
|
22
23
|
&:not(.active) {
|
23
24
|
h3, p {
|
24
25
|
display: none
|
@@ -44,7 +45,7 @@
|
|
44
45
|
margin-top: 10px;
|
45
46
|
cursor: pointer;
|
46
47
|
display: none;
|
47
|
-
@media
|
48
|
+
@include media-breakpoint-down(md) {
|
48
49
|
display: unset;
|
49
50
|
}
|
50
51
|
}
|
@@ -2,10 +2,11 @@ class DiscussionsController < ApplicationController
|
|
2
2
|
include Mumuki::Laboratory::Controllers::Content
|
3
3
|
include WithUserDiscussionValidation
|
4
4
|
|
5
|
-
before_action :set_debatable, except: [:subscription]
|
5
|
+
before_action :set_debatable, except: [:subscription, :responsible]
|
6
6
|
before_action :authenticate!, only: [:update, :create]
|
7
7
|
before_action :discussion_filter_params, only: :index
|
8
8
|
before_action :read_discussion, only: :show
|
9
|
+
before_action :authorize_moderator!, only: [:responsible]
|
9
10
|
|
10
11
|
helper_method :discussion_filter_params
|
11
12
|
|
@@ -19,7 +20,6 @@ class DiscussionsController < ApplicationController
|
|
19
20
|
end
|
20
21
|
|
21
22
|
def show
|
22
|
-
@discussion.update_last_moderator_access! current_user
|
23
23
|
end
|
24
24
|
|
25
25
|
def update
|
@@ -37,6 +37,20 @@ class DiscussionsController < ApplicationController
|
|
37
37
|
head :ok
|
38
38
|
end
|
39
39
|
|
40
|
+
def responsible
|
41
|
+
if subject.can_toggle_responsible? current_user
|
42
|
+
subject.toggle_responsible! current_user
|
43
|
+
|
44
|
+
set_flash_responsible_confirmation!
|
45
|
+
status = :ok
|
46
|
+
else
|
47
|
+
set_flash_responsible_alert!
|
48
|
+
status = :conflict
|
49
|
+
end
|
50
|
+
|
51
|
+
render :partial => 'layouts/toast', status: status
|
52
|
+
end
|
53
|
+
|
40
54
|
def create
|
41
55
|
discussion = @debatable.discuss! current_user, discussion_params
|
42
56
|
redirect_to [@debatable, discussion]
|
@@ -57,6 +71,19 @@ class DiscussionsController < ApplicationController
|
|
57
71
|
@debatable = Discussion.debatable_for(@debatable_class, params)
|
58
72
|
end
|
59
73
|
|
74
|
+
def set_flash_responsible_confirmation!
|
75
|
+
subject.any_responsible? ?
|
76
|
+
flash.now.notice = I18n.t('moderator_take_care.you_will_confirmation') :
|
77
|
+
flash.now.notice = I18n.t('moderator_take_care.you_wont_confirmation')
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
def set_flash_responsible_alert!
|
82
|
+
subject.any_responsible? ?
|
83
|
+
flash.now.alert = I18n.t('moderator_take_care.someone_else_will') :
|
84
|
+
flash.now.alert = I18n.t('moderator_take_care.status_changed')
|
85
|
+
end
|
86
|
+
|
60
87
|
def subject
|
61
88
|
@discussion ||= Discussion.find_by(id: params[:id])
|
62
89
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
class ExamAuthorizationRequestsController < ApplicationController
|
2
2
|
def create
|
3
|
-
authorization_request = ExamAuthorizationRequest.
|
3
|
+
authorization_request = ExamAuthorizationRequest.find_or_create_by! create_authorization_request_params do |it|
|
4
|
+
it.assign_attributes authorization_request_params
|
5
|
+
end
|
4
6
|
current_user.read_notification! authorization_request.exam_registration
|
5
7
|
flash.notice = I18n.t :exam_authorization_request_created
|
6
8
|
redirect_to root_path
|
@@ -14,6 +16,10 @@ class ExamAuthorizationRequestsController < ApplicationController
|
|
14
16
|
|
15
17
|
private
|
16
18
|
|
19
|
+
def create_authorization_request_params
|
20
|
+
authorization_request_params.slice :exam_registration_id, :user, :organization
|
21
|
+
end
|
22
|
+
|
17
23
|
def authorization_request_params
|
18
24
|
params
|
19
25
|
.require(:exam_authorization_request).permit(:exam_id, :exam_registration_id)
|
@@ -83,10 +83,14 @@ module DiscussionsHelper
|
|
83
83
|
def discussion_update_status_button(status)
|
84
84
|
button_to t("to_#{status}"),
|
85
85
|
item_discussion_path(@discussion, {status: status}),
|
86
|
-
class: "btn btn
|
86
|
+
class: "btn btn-#{btn_type_for_discussion_statuses[status.to_sym]}",
|
87
87
|
method: :put
|
88
88
|
end
|
89
89
|
|
90
|
+
def btn_type_for_discussion_statuses
|
91
|
+
{ closed: 'danger', solved: 'success', opened: 'light' }
|
92
|
+
end
|
93
|
+
|
90
94
|
def new_discussion_link(teaser_text, link_text)
|
91
95
|
%Q{
|
92
96
|
<h4>
|
@@ -173,7 +177,7 @@ module DiscussionsHelper
|
|
173
177
|
end
|
174
178
|
|
175
179
|
def discussion_info(discussion)
|
176
|
-
"#{t(:time_since, time: time_ago_in_words(discussion.created_at))} · #{t(:
|
180
|
+
"#{t(:time_since, time: time_ago_in_words(discussion.created_at))} · #{t(:reply_count, count: discussion.visible_messages.size)}"
|
177
181
|
end
|
178
182
|
|
179
183
|
def discussion_filter_params_without_page
|
@@ -188,6 +192,26 @@ module DiscussionsHelper
|
|
188
192
|
user.abbreviated_name
|
189
193
|
end
|
190
194
|
|
195
|
+
def linked_discussion_user_name(user)
|
196
|
+
content_tag :a, discussion_user_name(user)
|
197
|
+
end
|
198
|
+
|
199
|
+
def responsible_moderator_text_for(discussion, user)
|
200
|
+
if discussion.responsible?(user)
|
201
|
+
t('moderator_take_care.you_are')
|
202
|
+
else
|
203
|
+
t('moderator_take_care.moderator_is', moderator: discussion_user_name(@discussion.responsible_moderator_by))
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def responsible_icon
|
208
|
+
fa_icon 'hand-paper', text: t('moderator_take_care.i_will')
|
209
|
+
end
|
210
|
+
|
211
|
+
def not_responsible_icon
|
212
|
+
fa_icon 'hand-rock', type: :regular, text: t('moderator_take_care.i_wont')
|
213
|
+
end
|
214
|
+
|
191
215
|
def subscription_icon
|
192
216
|
fa_icon :bell, text: t(:subscribe)
|
193
217
|
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
<div class="discussion-message-bubble">
|
3
3
|
<div class="discussion-message-bubble-header">
|
4
4
|
<div class="discussion-message-bubble-title">
|
5
|
-
<%=
|
5
|
+
<%= linked_discussion_user_name(discussion.initiator) %>
|
6
6
|
<span class="message-date">
|
7
7
|
<%= t(:time_since, time: time_ago_in_words(discussion.created_at)) %>
|
8
8
|
</span>
|
@@ -2,7 +2,7 @@
|
|
2
2
|
<div class="discussion-message-bubble">
|
3
3
|
<div class="discussion-message-bubble-header">
|
4
4
|
<div class="discussion-message-bubble-title">
|
5
|
-
<%=
|
5
|
+
<%= linked_discussion_user_name user %>
|
6
6
|
<% if user.moderator_here? %>
|
7
7
|
<span class="moderator-badge"><%= t(:moderation) %></span>
|
8
8
|
<% end %>
|
@@ -15,7 +15,7 @@
|
|
15
15
|
<div class="discussion-message-bubble" id="new-discussion-description-container">
|
16
16
|
<div class="discussion-message-bubble-header">
|
17
17
|
<div class="discussion-message-bubble-title">
|
18
|
-
<%= @discussion.initiator
|
18
|
+
<%= discussion_user_name @discussion.initiator %>
|
19
19
|
</div>
|
20
20
|
</div>
|
21
21
|
<div class="discussion-message-bubble-content">
|
@@ -15,6 +15,12 @@
|
|
15
15
|
<h3 class="flex-grow-1 me-3"><%= t :messages %></h3>
|
16
16
|
<% if current_user && @discussion.persisted? %>
|
17
17
|
<span class="d-flex">
|
18
|
+
<% if @discussion.can_toggle_responsible?(current_user) %>
|
19
|
+
<div class="discussion-responsible me-1">
|
20
|
+
<%= btn_toggle responsible_icon, not_responsible_icon, @discussion.any_responsible?, class: 'btn-sm',
|
21
|
+
onclick: "mumuki.Forum.discussionResponsible('#{responsible_discussion_url(@discussion)}')" %>
|
22
|
+
</div>
|
23
|
+
<% end %>
|
18
24
|
<% if @discussion.subscribable? %>
|
19
25
|
<div class="discussion-subscription me-1">
|
20
26
|
<%= btn_toggle subscription_icon, unsubscription_icon, current_user.subscribed_to?(@discussion), class: 'btn-sm',
|
@@ -1 +1 @@
|
|
1
|
-
<%= t :exam_authorization_request_approved_html, date: l(authorization_request.exam.start_time, format: :long) %>
|
1
|
+
<%= t :exam_authorization_request_approved_html, date: l(authorization_request.exam.start_time.in_time_zone(-3), format: :long) %>
|
@@ -1,4 +1,4 @@
|
|
1
1
|
<%= t :exam_authorization_pending_explanation_html,
|
2
|
-
date: l(authorization_request.exam.start_time, format: :long),
|
3
|
-
end_time: l(authorization_request.exam_registration.end_time, format: :long),
|
2
|
+
date: l(authorization_request.exam.start_time.in_time_zone(-3), format: :long),
|
3
|
+
end_time: l(authorization_request.exam_registration.end_time.in_time_zone(-3), format: :long),
|
4
4
|
edit_path: url_for(authorization_request.exam_registration) %>
|
@@ -17,7 +17,7 @@
|
|
17
17
|
<strong><%= fa_icon :info_circle %> <%= t :important_info %></strong>
|
18
18
|
</h4>
|
19
19
|
<p>
|
20
|
-
<%= t :exam_registration_explanation_html, date: l(@registration.end_time, format: :long) %>
|
20
|
+
<%= t :exam_registration_explanation_html, date: l(@registration.end_time.in_time_zone(-3), format: :long) %>
|
21
21
|
</p>
|
22
22
|
</div>
|
23
23
|
<%= form_for @authorization_request, html: {class: 'mu-form'} do |f| %>
|
@@ -27,10 +27,11 @@
|
|
27
27
|
<div class="form-check">
|
28
28
|
<%= f.radio_button(:exam_id, exam.id, id: exam.id, required: true, class: 'form-check-input mu-read-only-input',
|
29
29
|
checked: @authorization_request.exam_id == exam.id) %>
|
30
|
-
<%= label_tag exam.id, l(exam.start_time, format: :long), class: 'form-check-label' %>
|
30
|
+
<%= label_tag exam.id, l(exam.start_time.in_time_zone(-3), format: :long), class: 'form-check-label' %>
|
31
31
|
</div>
|
32
32
|
<% end %>
|
33
33
|
<button class="btn btn-complementary"> <%= t :save %> </button>
|
34
34
|
<% end %>
|
35
35
|
</div>
|
36
36
|
</div>
|
37
|
+
|
@@ -26,16 +26,15 @@
|
|
26
26
|
<%= label_for_contextualization(@discussion, class: 'd-none d-sm-inline') %> ·
|
27
27
|
<span class="discussion-info">
|
28
28
|
<span class="discussion-initiator-name">
|
29
|
-
<%= @discussion.initiator
|
29
|
+
<%= discussion_user_name @discussion.initiator %>
|
30
30
|
</span>
|
31
|
-
<span
|
31
|
+
<span><%= discussion_info(@discussion) unless @discussion.new_record? %></span>
|
32
32
|
</span>
|
33
33
|
</div>
|
34
|
-
<% if @discussion.
|
34
|
+
<% if @discussion.current_responsible_visible_for?(current_user) %>
|
35
35
|
<h5 class="my-2 me-3">
|
36
|
-
<span class="badge bg-primary text-wrap">
|
37
|
-
<%=
|
38
|
-
<%= t :time_since, time: time_ago_in_words(@discussion.last_moderator_access_at) %>
|
36
|
+
<span class="badge bg-primary text-wrap responsible-moderator-badge">
|
37
|
+
<%= responsible_moderator_text_for(@discussion, current_user) %>
|
39
38
|
</span>
|
40
39
|
</h5>
|
41
40
|
<% end %>
|
@@ -58,11 +58,11 @@
|
|
58
58
|
</span>
|
59
59
|
<span class="d-none d-lg-inline"><%= discussion.exercise.guide.name %> -</span>
|
60
60
|
<span><%= discussion.exercise.name %></span>
|
61
|
-
<% if discussion.
|
61
|
+
<% if discussion.current_responsible_visible_for?(current_user) %>
|
62
62
|
<div class="float-end discussion-moderator-access" >
|
63
|
-
<%= profile_picture_for(discussion.
|
63
|
+
<%= profile_picture_for(discussion.responsible_moderator_by, height: 32) %>
|
64
64
|
<span class="moderator-initials">
|
65
|
-
<%= discussion.
|
65
|
+
<%= discussion.responsible_moderator_by.name_initials %>
|
66
66
|
</span>
|
67
67
|
</div>
|
68
68
|
<% end %>
|
@@ -28,17 +28,11 @@
|
|
28
28
|
|
29
29
|
<%= yield :navbar %>
|
30
30
|
|
31
|
-
<div class="
|
32
|
-
|
33
|
-
|
34
|
-
<%= toast_notice(notice) %>
|
35
|
-
<% elsif alert %>
|
36
|
-
<%= toast_alert(alert) %>
|
37
|
-
<% elsif info %>
|
38
|
-
<%= toast_info(info) %>
|
39
|
-
<% end %>
|
40
|
-
</div>
|
31
|
+
<div class="toast-container bottom-0 start-0 p-3">
|
32
|
+
<%= render partial: 'layouts/toast' %>
|
33
|
+
</div>
|
41
34
|
|
35
|
+
<div class="<%= exercise_container_type %><%= ' mb-5' unless input_kids? %>" id="wrap">
|
42
36
|
<%= yield %>
|
43
37
|
</div>
|
44
38
|
|