mumuki-laboratory 5.7.0 → 5.8.0
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/application/codemirror-builder.js +16 -8
- data/app/assets/javascripts/application/codemirror.js +7 -9
- data/app/assets/javascripts/application/discussions.js +20 -12
- data/app/assets/javascripts/application/multiple-files.js +222 -0
- data/app/assets/javascripts/application/submission.js +1 -0
- data/app/assets/stylesheets/application/modules/_discussion.scss +12 -0
- data/app/assets/stylesheets/application/modules/_editor.scss +13 -0
- data/app/controllers/discussions_controller.rb +10 -0
- data/app/controllers/discussions_messages_controller.rb +15 -3
- data/app/helpers/application_helper.rb +1 -1
- data/app/helpers/discussions_helper.rb +13 -5
- data/app/helpers/multiple_file_editor_helper.rb +9 -0
- data/app/models/application_record.rb +5 -0
- data/app/models/concerns/with_assignments.rb +3 -21
- data/app/models/discussion.rb +1 -1
- data/app/models/exam.rb +12 -11
- data/app/models/exercise.rb +1 -1
- data/app/models/guide.rb +1 -1
- data/app/models/message.rb +5 -1
- data/app/models/stats.rb +4 -29
- data/app/models/user.rb +6 -0
- data/app/views/discussions/_message.html.erb +3 -0
- data/app/views/errors/forbidden.html.erb +1 -1
- data/app/views/layouts/_authoring.html.erb +5 -0
- data/app/views/layouts/_copyright.html.erb +2 -0
- data/app/views/layouts/_social_media.html.erb +4 -0
- data/app/views/layouts/application.html.erb +4 -6
- data/app/views/layouts/embedded.html.erb +27 -0
- data/app/views/layouts/exercise_inputs/editors/_multiple_files.html.erb +8 -3
- data/config/routes.rb +3 -1
- data/db/migrate/20180802190437_add_approved_to_messages.rb +5 -0
- data/lib/mumuki/laboratory/controllers/dynamic_errors.rb +6 -1
- data/lib/mumuki/laboratory/controllers/notifications.rb +3 -2
- data/lib/mumuki/laboratory/exceptions.rb +1 -0
- data/lib/mumuki/laboratory/exceptions/blocked_forum_error.rb +2 -0
- data/lib/mumuki/laboratory/locales/en.yml +3 -1
- data/lib/mumuki/laboratory/locales/es.yml +3 -2
- data/lib/mumuki/laboratory/locales/pt.yml +3 -2
- data/lib/mumuki/laboratory/mumukit/directives.rb +6 -5
- data/lib/mumuki/laboratory/status/submission/pending.rb +1 -5
- data/lib/mumuki/laboratory/status/submission/running.rb +1 -1
- data/lib/mumuki/laboratory/status/submission/submission.rb +0 -1
- data/lib/mumuki/laboratory/version.rb +1 -1
- data/spec/controllers/discussions_controller_spec.rb +1 -0
- data/spec/controllers/exercise_solutions_controller_spec.rb +1 -1
- data/spec/controllers/organizations_api_controller_spec.rb +1 -1
- data/spec/dummy/db/schema.rb +2 -1
- data/spec/factories/api_client_factory.rb +3 -3
- data/spec/factories/assignments_factory.rb +1 -1
- data/spec/factories/chapter_factory.rb +1 -1
- data/spec/factories/course_factory.rb +5 -5
- data/spec/factories/discussion_factory.rb +2 -2
- data/spec/factories/exercise_factory.rb +24 -26
- data/spec/factories/guide_factory.rb +3 -3
- data/spec/factories/login_settings_factory.rb +1 -1
- data/spec/factories/message_factory.rb +1 -1
- data/spec/factories/organization_factory.rb +9 -9
- data/spec/factories/topic_factory.rb +1 -1
- data/spec/features/choose_organization_spec.rb +49 -42
- data/spec/models/exercise_spec.rb +3 -27
- data/spec/models/query_spec.rb +1 -1
- data/spec/models/question_spec.rb +2 -2
- data/spec/models/stats_spec.rb +2 -9
- data/spec/models/user_spec.rb +13 -0
- metadata +8 -3
- data/lib/mumuki/laboratory/status/submission/unknown.rb +0 -11
@@ -1,5 +1,6 @@
|
|
1
1
|
class DiscussionsMessagesController < AjaxController
|
2
2
|
before_action :set_discussion!, only: [:create, :destroy]
|
3
|
+
before_action :authorize!, only: [:destroy, :approve]
|
3
4
|
|
4
5
|
def create
|
5
6
|
@discussion.submit_message! message_params, current_user
|
@@ -7,18 +8,29 @@ class DiscussionsMessagesController < AjaxController
|
|
7
8
|
end
|
8
9
|
|
9
10
|
def destroy
|
10
|
-
|
11
|
-
message.authorize! current_user
|
12
|
-
message.destroy!
|
11
|
+
current_message.destroy!
|
13
12
|
redirect_back(fallback_location: root_path)
|
14
13
|
end
|
15
14
|
|
15
|
+
def approve
|
16
|
+
current_message.toggle_approved!
|
17
|
+
head :ok
|
18
|
+
end
|
19
|
+
|
16
20
|
private
|
17
21
|
|
18
22
|
def set_discussion!
|
19
23
|
@discussion ||= Discussion.find_by(id: params[:discussion_id])
|
20
24
|
end
|
21
25
|
|
26
|
+
def authorize!
|
27
|
+
current_message.authorize! current_user
|
28
|
+
end
|
29
|
+
|
30
|
+
def current_message
|
31
|
+
@message ||= Message.find(params[:id])
|
32
|
+
end
|
33
|
+
|
22
34
|
def message_params
|
23
35
|
params.require(:message).permit(:content)
|
24
36
|
end
|
@@ -16,7 +16,7 @@ module ApplicationHelper
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def profile_picture
|
19
|
-
image_tag(
|
19
|
+
image_tag(current_user.image_url, height: 40, class: 'img-circle', onError: "this.onerror = null; this.src = '#{image_url('user_shape.png')}'")
|
20
20
|
end
|
21
21
|
|
22
22
|
def paginate(object, options={})
|
@@ -1,18 +1,26 @@
|
|
1
1
|
module DiscussionsHelper
|
2
2
|
def read_discussions_link(item)
|
3
|
-
discussions_link t(:solve_your_doubts), item_discussions_path(item, default_discussions_params)
|
3
|
+
discussions_link others_discussions_icon(t(:solve_your_doubts)), item_discussions_path(item, default_discussions_params)
|
4
4
|
end
|
5
5
|
|
6
6
|
def solve_discussions_link
|
7
|
-
discussions_link t(:solve_doubts), discussions_path(solve_discussion_params_for(current_user))
|
7
|
+
discussions_link others_discussions_icon(t(:solve_doubts)), discussions_path(solve_discussion_params_for(current_user))
|
8
8
|
end
|
9
9
|
|
10
|
-
def
|
10
|
+
def user_discussions_link
|
11
|
+
discussions_link user_discussions_icon(t(:my_doubts)), user_path(anchor: 'discussions') if current_user.watched_discussions.present?
|
12
|
+
end
|
13
|
+
|
14
|
+
def others_discussions_icon(text)
|
11
15
|
fixed_fa_icon 'comments', text: text
|
12
16
|
end
|
13
17
|
|
14
|
-
def
|
15
|
-
|
18
|
+
def user_discussions_icon(text)
|
19
|
+
fixed_fa_icon 'comment', text: text
|
20
|
+
end
|
21
|
+
|
22
|
+
def discussions_link(item, path, organization=Organization.current)
|
23
|
+
link_to item, path if organization.forum_enabled?
|
16
24
|
end
|
17
25
|
|
18
26
|
def item_discussion_path(discussion, params={})
|
@@ -1,6 +1,8 @@
|
|
1
1
|
class ApplicationRecord < ActiveRecord::Base
|
2
2
|
self.abstract_class = true
|
3
3
|
|
4
|
+
delegate :whitelist_attributes, to: :class
|
5
|
+
|
4
6
|
def self.defaults(&block)
|
5
7
|
after_initialize :defaults, if: :new_record?
|
6
8
|
define_method :defaults, &block
|
@@ -77,4 +79,7 @@ class ApplicationRecord < ActiveRecord::Base
|
|
77
79
|
obj
|
78
80
|
end
|
79
81
|
|
82
|
+
def self.whitelist_attributes(a_hash, options={})
|
83
|
+
a_hash.slice(*attribute_names).except(*options[:except])
|
84
|
+
end
|
80
85
|
end
|
@@ -6,15 +6,13 @@ module WithAssignments
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def current_content_for(user)
|
9
|
-
assignment_for(user)
|
10
|
-
default_content_for(user)
|
9
|
+
assignment_for(user)&.solution || default_content_for(user)
|
11
10
|
end
|
12
11
|
|
13
12
|
def files_for(user)
|
14
13
|
language
|
15
14
|
.directives_sections
|
16
|
-
.split_sections(
|
17
|
-
.except('content')
|
15
|
+
.split_sections(current_content_for user)
|
18
16
|
.map { |name, content| Mumuki::Laboratory::File.new name, content }
|
19
17
|
end
|
20
18
|
|
@@ -55,24 +53,8 @@ module WithAssignments
|
|
55
53
|
assignments.find_by(submitter: user)
|
56
54
|
end
|
57
55
|
|
58
|
-
def solved_by?(user)
|
59
|
-
!!assignment_for(user).try(&:passed?)
|
60
|
-
end
|
61
|
-
|
62
|
-
def assigned_to?(user)
|
63
|
-
assignments.exists?(submitter: user)
|
64
|
-
end
|
65
|
-
|
66
56
|
def status_for(user)
|
67
|
-
assignment_for(user).defaulting(Mumuki::Laboratory::Status::Submission::
|
68
|
-
end
|
69
|
-
|
70
|
-
def last_submission_date_for(user)
|
71
|
-
assignment_for(user).try(&:updated_at)
|
72
|
-
end
|
73
|
-
|
74
|
-
def submissions_count_for(user)
|
75
|
-
assignment_for(user).try(&:submissions_count) || 0
|
57
|
+
assignment_for(user).defaulting(Mumuki::Laboratory::Status::Submission::Pending, &:status) if user
|
76
58
|
end
|
77
59
|
|
78
60
|
def find_or_init_assignment_for(user)
|
data/app/models/discussion.rb
CHANGED
@@ -2,7 +2,7 @@ class Discussion < ApplicationRecord
|
|
2
2
|
include WithDiscussionStatus, ParentNavigation, WithScopedQueries, Contextualization
|
3
3
|
|
4
4
|
belongs_to :item, polymorphic: true
|
5
|
-
has_many :messages
|
5
|
+
has_many :messages, -> { order(:created_at) }
|
6
6
|
belongs_to :initiator, class_name: 'User'
|
7
7
|
belongs_to :exercise, foreign_type: :exercise, foreign_key: 'item_id'
|
8
8
|
has_many :subscriptions
|
data/app/models/exam.rb
CHANGED
@@ -30,6 +30,10 @@ class Exam < ApplicationRecord
|
|
30
30
|
enabled_range_for(user).cover? DateTime.now
|
31
31
|
end
|
32
32
|
|
33
|
+
def in_progress_for?(user)
|
34
|
+
accessible_for?(user) && started?(user)
|
35
|
+
end
|
36
|
+
|
33
37
|
def validate_accessible_for!(user)
|
34
38
|
if user.present?
|
35
39
|
raise Mumuki::Laboratory::ForbiddenError unless authorized?(user)
|
@@ -97,25 +101,22 @@ class Exam < ApplicationRecord
|
|
97
101
|
end
|
98
102
|
|
99
103
|
def self.import_from_json!(json)
|
100
|
-
json.
|
101
|
-
organization = Organization.find_by!(name:
|
104
|
+
exam_data = json.with_indifferent_access
|
105
|
+
organization = Organization.find_by!(name: exam_data[:organization])
|
102
106
|
organization.switch!
|
103
|
-
exam_data
|
107
|
+
adapt_json_values exam_data
|
104
108
|
remove_previous_version exam_data[:eid], exam_data[:guide_id]
|
105
|
-
|
106
|
-
exam
|
107
|
-
exam.process_users users
|
109
|
+
exam = where(classroom_id: exam_data[:eid]).update_or_create!(whitelist_attributes(exam_data))
|
110
|
+
exam.process_users exam_data[:users]
|
108
111
|
exam.index_usage! organization
|
109
112
|
exam
|
110
113
|
end
|
111
114
|
|
112
|
-
def self.
|
113
|
-
exam =
|
114
|
-
exam[:guide_id] = Guide.find_by(slug: exam.delete(:slug)).id
|
115
|
+
def self.adapt_json_values(exam)
|
116
|
+
exam[:guide_id] = Guide.find_by(slug: exam[:slug]).id
|
115
117
|
exam[:organization_id] = Organization.current.id
|
116
|
-
exam[:users] = exam
|
118
|
+
exam[:users] = exam[:uids].map { |uid| User.find_by(uid: uid) }.compact
|
117
119
|
[:start_time, :end_time].each { |param| exam[param] = exam[param].to_time }
|
118
|
-
exam
|
119
120
|
end
|
120
121
|
|
121
122
|
def self.remove_previous_version(eid, guide_id)
|
data/app/models/exercise.rb
CHANGED
@@ -82,7 +82,7 @@ class Exercise < ApplicationRecord
|
|
82
82
|
|
83
83
|
reset!
|
84
84
|
|
85
|
-
attrs = json
|
85
|
+
attrs = whitelist_attributes(json, except: %w(type id))
|
86
86
|
attrs['choices'] = json['choices'].map { |choice| choice['value'] } if json['choices'].present?
|
87
87
|
attrs['bibliotheca_id'] = json['id']
|
88
88
|
attrs['number'] = number
|
data/app/models/guide.rb
CHANGED
@@ -58,7 +58,7 @@ class Guide < Content
|
|
58
58
|
end
|
59
59
|
|
60
60
|
def import_from_json!(json)
|
61
|
-
self.assign_attributes json
|
61
|
+
self.assign_attributes whitelist_attributes(json, except: ['id'])
|
62
62
|
self.language = Language.for_name(json['language'])
|
63
63
|
self.save!
|
64
64
|
|
data/app/models/message.rb
CHANGED
@@ -33,7 +33,7 @@ class Message < ApplicationRecord
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def as_platform_json
|
36
|
-
as_json(except: [:id, :type, :discussion_id],
|
36
|
+
as_json(except: [:id, :type, :discussion_id, :approved],
|
37
37
|
include: {exercise: {only: [:bibliotheca_id]}})
|
38
38
|
.merge(organization: Organization.current.name)
|
39
39
|
end
|
@@ -42,6 +42,10 @@ class Message < ApplicationRecord
|
|
42
42
|
update! read: true
|
43
43
|
end
|
44
44
|
|
45
|
+
def toggle_approved!
|
46
|
+
toggle! :approved
|
47
|
+
end
|
48
|
+
|
45
49
|
def self.parse_json(json)
|
46
50
|
message = json.delete 'message'
|
47
51
|
json
|
data/app/models/stats.rb
CHANGED
@@ -1,49 +1,24 @@
|
|
1
1
|
class Stats
|
2
2
|
include ActiveModel::Model
|
3
3
|
|
4
|
-
attr_accessor :passed, :passed_with_warnings, :failed, :
|
5
|
-
|
6
|
-
def total
|
7
|
-
submitted + unknown
|
8
|
-
end
|
4
|
+
attr_accessor :passed, :passed_with_warnings, :failed, :pending
|
9
5
|
|
10
6
|
def submitted
|
11
|
-
|
12
|
-
end
|
13
|
-
|
14
|
-
def pending
|
15
|
-
failed + unknown
|
16
|
-
end
|
17
|
-
|
18
|
-
def resolved
|
19
|
-
passed + passed_with_warnings
|
7
|
+
passed + passed_with_warnings + failed
|
20
8
|
end
|
21
9
|
|
22
10
|
def done?
|
23
|
-
pending == 0
|
11
|
+
failed + pending == 0
|
24
12
|
end
|
25
13
|
|
26
14
|
def started?
|
27
15
|
submitted > 0
|
28
16
|
end
|
29
17
|
|
30
|
-
def to_h(&key)
|
31
|
-
{key.call(:passed) => passed,
|
32
|
-
key.call(:passed_with_warnings) => passed_with_warnings,
|
33
|
-
key.call(:failed) => failed,
|
34
|
-
key.call(:unknown) => unknown}
|
35
|
-
end
|
36
|
-
|
37
18
|
def self.from_statuses(statuses)
|
38
|
-
Stats.new(statuses.inject({passed: 0, passed_with_warnings: 0, failed: 0,
|
19
|
+
Stats.new(statuses.inject({passed: 0, passed_with_warnings: 0, failed: 0, pending: 0}) do |accum, status|
|
39
20
|
accum[status.group.to_sym] += 1
|
40
21
|
accum
|
41
22
|
end)
|
42
23
|
end
|
43
|
-
|
44
|
-
private
|
45
|
-
|
46
|
-
def ratio(x)
|
47
|
-
(100 * x / total.to_f).round(2)
|
48
|
-
end
|
49
24
|
end
|
data/app/models/user.rb
CHANGED
@@ -26,6 +26,8 @@ class User < ApplicationRecord
|
|
26
26
|
|
27
27
|
has_many :exam_authorizations
|
28
28
|
|
29
|
+
has_many :exams, through: :exam_authorizations
|
30
|
+
|
29
31
|
after_initialize :init
|
30
32
|
|
31
33
|
before_validation :set_uid!
|
@@ -135,6 +137,10 @@ class User < ApplicationRecord
|
|
135
137
|
assignments.each { |it| it.notify! rescue nil }
|
136
138
|
end
|
137
139
|
|
140
|
+
def currently_in_exam?
|
141
|
+
exams.any? { |e| e.in_progress_for? self }
|
142
|
+
end
|
143
|
+
|
138
144
|
private
|
139
145
|
|
140
146
|
def set_uid!
|
@@ -11,6 +11,9 @@
|
|
11
11
|
<% end %>
|
12
12
|
<% if message.authorized? current_user %>
|
13
13
|
<span class="actions">
|
14
|
+
<a class="discussion-message-approved <%= 'selected' if message.approved? %>" onclick="mumuki.Forum.discussionMessageToggleApprove('<%= approve_discussion_message_url(@discussion, message) %>', $(this))">
|
15
|
+
<%= fa_icon(:check, class: 'fa-xs') %>
|
16
|
+
</a>
|
14
17
|
<%= link_to fa_icon('trash-o'), discussion_message_path(@discussion, message), method: :delete, data: { confirm: t(:are_you_sure, action: t(:destroy_message)) } %>
|
15
18
|
</span>
|
16
19
|
<% end %>
|
@@ -7,7 +7,7 @@
|
|
7
7
|
<%= t(:error_description, error: link_to_status_codes(403)).html_safe %>
|
8
8
|
</p>
|
9
9
|
<p>
|
10
|
-
<%= Organization.current.explain_error(403,
|
10
|
+
<%= Organization.current.explain_error(403, explanation).html_safe %>
|
11
11
|
</p>
|
12
12
|
<p>
|
13
13
|
<%= t(:contact_administrator, link: mail_to_administrator).html_safe %>
|
@@ -1,6 +1,11 @@
|
|
1
1
|
<% if guide.authors.present? %>
|
2
2
|
<%= content_for :authoring do %>
|
3
3
|
<p class="small">
|
4
|
+
<% if embedded_mode? %>
|
5
|
+
<span class="hidden-md hidden-lg hidden-xl">
|
6
|
+
<%= render partial: 'layouts/copyright' %> -
|
7
|
+
</span>
|
8
|
+
<% end %>
|
4
9
|
<%= raw t :authoring_note, authors: guide.authors, collaborators: "https://raw.githubusercontent.com/#{guide.slug}/master/COLLABORATORS.txt" %>
|
5
10
|
</p>
|
6
11
|
<% end %>
|
@@ -0,0 +1,4 @@
|
|
1
|
+
<a class="fa fa-facebook social-icon" aria-label="Facebook" href="https://www.facebook.com/MumukiProject" target="_blank"></a>
|
2
|
+
<a class="fa fa-twitter social-icon" aria-label="Twitter" href="https://twitter.com/MumukiProject" target="_blank"></a>
|
3
|
+
<a class="fa fa-github social-icon" aria-label="Github" href="https://github.com/mumuki" target="_blank"></a>
|
4
|
+
<a class="fa fa-linkedin social-icon" aria-label="LinkedIn" href="https://www.linkedin.com/company/mumuki-project" target="_blank"></a>
|
@@ -37,6 +37,7 @@
|
|
37
37
|
<li><%= link_to_classroom %></li>
|
38
38
|
<li><%= link_to_bibliotheca %></li>
|
39
39
|
<li><%= solve_discussions_link %></li>
|
40
|
+
<li><%= user_discussions_link %></li>
|
40
41
|
<li class="divider"></li>
|
41
42
|
<li><%= link_to(t(:sign_out), logout_path(origin: url_for), role: 'menuitem') %></li>
|
42
43
|
</ul>
|
@@ -62,8 +63,8 @@
|
|
62
63
|
|
63
64
|
<div id="footer-copyright" class="row">
|
64
65
|
<div class="col-md-4 text-left">
|
65
|
-
<p
|
66
|
-
|
66
|
+
<p>
|
67
|
+
<%= render partial: 'layouts/copyright' %>
|
67
68
|
</p>
|
68
69
|
</div>
|
69
70
|
|
@@ -72,10 +73,7 @@
|
|
72
73
|
</div>
|
73
74
|
|
74
75
|
<div id="footer-social" class="col-md-4 text-right" lang="en">
|
75
|
-
|
76
|
-
<a class="fa fa-twitter social-icon" aria-label="Twitter" href="https://twitter.com/MumukiProject" target="_blank"></a>
|
77
|
-
<a class="fa fa-github social-icon" aria-label="Github" href="https://github.com/mumuki" target="_blank"></a>
|
78
|
-
<a class="fa fa-linkedin social-icon" aria-label="LinkedIn" href="https://www.linkedin.com/company/mumuki-project" target="_blank"></a>
|
76
|
+
<%= render partial: 'layouts/social_media' %>
|
79
77
|
</div>
|
80
78
|
</div>
|
81
79
|
</div>
|
@@ -1 +1,28 @@
|
|
1
|
+
<% content_for :footer do %>
|
2
|
+
<footer class="footer">
|
3
|
+
<div class="<%= exercise_container_type %>">
|
4
|
+
<hr>
|
5
|
+
|
6
|
+
<div class="row">
|
7
|
+
<div class="col-md-12">
|
8
|
+
<%= yield :authoring %>
|
9
|
+
</div>
|
10
|
+
</div>
|
11
|
+
|
12
|
+
<div id="footer-copyright" class="row hidden-xs hidden-sm">
|
13
|
+
<div class="col-sm-8 text-left">
|
14
|
+
<p>
|
15
|
+
<%= render partial: 'layouts/copyright' %>
|
16
|
+
</p>
|
17
|
+
</div>
|
18
|
+
|
19
|
+
<div id="footer-social" class="col-sm-4 text-right" lang="en">
|
20
|
+
<%= render partial: 'layouts/social_media' %>
|
21
|
+
</div>
|
22
|
+
</div>
|
23
|
+
</div>
|
24
|
+
</footer>
|
25
|
+
<% end %>
|
26
|
+
|
27
|
+
|
1
28
|
<%= render partial: 'layouts/main' %>
|
@@ -4,15 +4,20 @@
|
|
4
4
|
<span class="files-tabs">
|
5
5
|
<ul class="nav nav-tabs">
|
6
6
|
<% @files.each_with_index do |file, index| %>
|
7
|
-
<li role="presentation" class="<%= 'active' if index == 0 %>" data-target="#editor-file-<%= index %>" tabindex='0' data-toggle='tab'>
|
8
|
-
<a href="#"><%= file.name %></a>
|
7
|
+
<li role="presentation" class="file-tab <%= 'active' if index == 0 %>" data-target="#editor-file-<%= index %>" tabindex='0' data-toggle='tab'>
|
8
|
+
<a class="file-name" href="#"><%= file.name %></a> <i class="delete-file-button fa fa-times"></i>
|
9
9
|
</li>
|
10
10
|
<% end %>
|
11
11
|
</ul>
|
12
|
+
<i class="add-file-button fa fa-plus"></i>
|
12
13
|
</span>
|
14
|
+
|
15
|
+
<input id="highlight-modes" type="hidden" value="<%=highlight_modes.to_json%>" />
|
16
|
+
<input id="multifile-locales" type="hidden" value="<%=multifile_locales.to_json%>" />
|
17
|
+
|
13
18
|
<div class="tab-content">
|
14
19
|
<% @files.each_with_index do |file, index| %>
|
15
|
-
<div role="tabpanel" class="tab-pane mu-input-panel <%= 'fade in active' if index == 0 %>" id="editor-file-<%= index %>">
|
20
|
+
<div role="tabpanel" class="file-editor tab-pane mu-input-panel <%= 'fade in active' if index == 0 %>" id="editor-file-<%= index %>">
|
16
21
|
<%= form.editor "content[#{file.name}]", file.highlight_mode,
|
17
22
|
placeholder: t(:editor_placeholder),
|
18
23
|
class: 'form-control editor',
|