qti 2.13.1 → 2.14.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 012e43c78ded5cf4f1ec586f8bbd321cadb532facc73e3b06bf9ecd884ab724f
4
- data.tar.gz: d95a01adb90683fa13c2bd769581edc53771c8a8157c692954090d38936fc318
3
+ metadata.gz: ce025f977e8afee917a58d479c15bc56fc7320596b1bfb0c28da5e94ad19602a
4
+ data.tar.gz: 642c6b5ba04eb180d5ff363d4aec28bdfc1807cb27fc8d8e99ab0d4b78c36ad0
5
5
  SHA512:
6
- metadata.gz: bfd79edf504cedad28c37fb5c34ce67b54e2421ba8db324e25d905d23881c66d44f64f700d6803c8853f70b68864f249eb9725d47b3f3b232c66d3a9b2217f39
7
- data.tar.gz: 197b13f906c3604df179ab2276d69fcd8e0183f8988046bcd73df35653c312c02f0e37127874d6d371d7320e7e9d0b2bea5a6bbc324d419d1c3067907fa8e1d9
6
+ metadata.gz: c96870e3a861ce1e635a5a2f09a8be85820528cc703303bdea362468c0107704be4cf87e425ed4f11d892e3db3453450d4058c5d102442e43c5caa71f3b21b4a
7
+ data.tar.gz: d0eac68354d0ffeeae4b1098e90bded2907462f0671ec21377de1d0fa596695fb4ae417f3f6770909c23edf108634f050b1452700b0324fd305833aedb536997
@@ -18,7 +18,7 @@ module Qti
18
18
  node = @doc.dup
19
19
  presentation = node.at_xpath('.//xmlns:presentation')
20
20
  mattext = presentation.at_xpath('.//xmlns:mattext')
21
- prompt = return_inner_content!(mattext)
21
+ prompt = sanitize_attributes(return_inner_content!(mattext))
22
22
  sanitize_content!(prompt)
23
23
  end
24
24
  end
@@ -83,11 +83,15 @@ module Qti
83
83
  end
84
84
 
85
85
  def feedback
86
- @feedback ||= interaction_model.canvas_item_feedback
86
+ @feedback ||= interaction_model.canvas_item_feedback&.transform_values { |v| sanitize_content!(v) }
87
87
  end
88
88
 
89
89
  def answer_feedback
90
- @answer_feedback ||= interaction_model.answer_feedback
90
+ @answer_feedback ||= interaction_model.answer_feedback&.map do |answer|
91
+ duped = answer.dup
92
+ duped[:feedback] = sanitize_content!(duped[:feedback])
93
+ duped
94
+ end
91
95
  end
92
96
  end
93
97
  end
@@ -2,6 +2,8 @@ module Qti
2
2
  module V1
3
3
  module Models
4
4
  class Base < Qti::Models::Base
5
+ CANVAS_BLANK_REGEX ||= /(\[[A-Za-z0-9_\-.]+\])/.freeze
6
+
5
7
  def qti_version
6
8
  1
7
9
  end
@@ -12,6 +14,20 @@ module Qti
12
14
  node.inner_html
13
15
  end
14
16
 
17
+ def sanitize_attributes(html)
18
+ node = Nokogiri::HTML.fragment(html)
19
+ sanitize_attributes_by_node(node)
20
+ node.to_html
21
+ end
22
+
23
+ def sanitize_attributes_by_node(node)
24
+ node.attribute_nodes.each do |a|
25
+ matches = a.value.match(CANVAS_BLANK_REGEX) || []
26
+ a.value = a.value.gsub!('[', '&#91;').gsub!(']', '&#93;') if matches.length.positive?
27
+ end
28
+ node.children.each { |c| sanitize_attributes_by_node(c) }
29
+ end
30
+
15
31
  private
16
32
 
17
33
  def text_node?(node)
@@ -3,10 +3,9 @@ module Qti
3
3
  module Models
4
4
  module Interactions
5
5
  class BaseFillBlankInteraction < BaseInteraction
6
- CANVAS_REGEX ||= /(\[[A-Za-z0-9_\-.]+\])/.freeze
7
-
8
6
  def canvas_stem_items(item_prompt)
9
- item_prompt.split(CANVAS_REGEX).map.with_index do |stem_item, index|
7
+ item_prompt = sanitize_attributes(item_prompt)
8
+ item_prompt.split(CANVAS_BLANK_REGEX).map.with_index do |stem_item, index|
10
9
  if canvas_fib_response_ids.include?(stem_item)
11
10
  # Strip the brackets before searching
12
11
  value = stem_item[1..-2]
data/lib/qti/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Qti
2
- VERSION = '2.13.1'.freeze
2
+ VERSION = '2.14.0'.freeze
3
3
  end
@@ -3,6 +3,18 @@
3
3
  <assessment title="1.2 Import Quiz" ident="A1001">
4
4
  <section title="Main" ident="S1002">
5
5
  <item title="Grading - specific - 3 pt score" ident="QUE_1003">
6
+ <itemmetadata>
7
+ <qtimetadata>
8
+ <qtimetadatafield>
9
+ <fieldlabel>question_type</fieldlabel>
10
+ <fieldentry>true_false_question</fieldentry>
11
+ </qtimetadatafield>
12
+ <qtimetadatafield>
13
+ <fieldlabel>assessment_question_identifierref</fieldlabel>
14
+ <fieldentry>iff205c7405d3b36bdca9b75b51198932</fieldentry>
15
+ </qtimetadatafield>
16
+ </qtimetadata>
17
+ </itemmetadata>
6
18
  <presentation>
7
19
  <material>
8
20
  <mattext texttype="text/html"><![CDATA[If I get a 3, I must have done something wrong. <img data-equation-content="sample equation" script="alert('bad')" align="bottom" alt="image.png" src="org0/images/image.png" border="0"/> <img script="alert('bad')" align="bottom" alt="image.png" src="org0/images/image.png" border="0"/>]]></mattext>
@@ -37,8 +49,37 @@
37
49
  <varequal respident="QUE_1004_RL">QUE_1006_A2</varequal>
38
50
  </conditionvar>
39
51
  <setvar varname="que_score" action="Set">10.00</setvar>
52
+ <displayfeedback feedbacktype="Response" linkrefid="fb_QUE_1006_A2"/>
40
53
  </respcondition>
41
54
  </resprocessing>
55
+ <itemfeedback ident="general_fb">
56
+ <flow_mat>
57
+ <material>
58
+ <mattext texttype="text/html">&lt;p&gt;Neutral&lt;/p&gt;&lt;img script="alert('bad')" alt="image.png" src="org0/images/image.png"/&gt;</mattext>
59
+ </material>
60
+ </flow_mat>
61
+ </itemfeedback>
62
+ <itemfeedback ident="correct_fb">
63
+ <flow_mat>
64
+ <material>
65
+ <mattext texttype="text/html">&lt;p&gt;Correct&lt;/p&gt;&lt;img script="alert('bad')" alt="image.png" src="org0/images/image.png"/&gt;</mattext>
66
+ </material>
67
+ </flow_mat>
68
+ </itemfeedback>
69
+ <itemfeedback ident="general_incorrect_fb">
70
+ <flow_mat>
71
+ <material>
72
+ <mattext texttype="text/html">&lt;p&gt;Incorrect&lt;/p&gt;&lt;img script="alert('bad')" alt="image.png" src="org0/images/image.png"/&gt;</mattext>
73
+ </material>
74
+ </flow_mat>
75
+ </itemfeedback>
76
+ <itemfeedback ident="fb_QUE_1006_A2">
77
+ <flow_mat>
78
+ <material>
79
+ <mattext texttype="text/html">&lt;p&gt;Answer was Correct&lt;/p&gt;&lt;img script="alert('bad')" alt="image.png" src="org0/images/image.png"/&gt;</mattext>
80
+ </material>
81
+ </flow_mat>
82
+ </itemfeedback>
42
83
  </item>
43
84
  </section>
44
85
  </assessment>
@@ -10,6 +10,37 @@ describe Qti::V1::Models::AssessmentItem do
10
10
  end
11
11
  end
12
12
 
13
+ context 'feedback', focus: true do
14
+ let(:file_path) { File.join('spec', 'fixtures', 'items_1.2', 'true_false.xml') }
15
+ let(:test_object) { Qti::V1::Models::Assessment.from_path!(file_path) }
16
+ let(:assessment_item_refs) { test_object.assessment_items }
17
+ let(:loaded_class) { described_class.new(assessment_item_refs) }
18
+
19
+ it 'sanitizes general neutral feedback' do
20
+ expect(loaded_class.feedback[:neutral]).to include '<p>Neutral'
21
+ expect(loaded_class.feedback[:neutral]).to include '<img'
22
+ expect(loaded_class.feedback[:neutral]).not_to include 'script="alert(\'bad\')"'
23
+ end
24
+
25
+ it 'sanitizes general correct feedback' do
26
+ expect(loaded_class.feedback[:correct]).to include '<p>Correct'
27
+ expect(loaded_class.feedback[:correct]).to include '<img'
28
+ expect(loaded_class.feedback[:correct]).not_to include 'script="alert(\'bad\')"'
29
+ end
30
+
31
+ it 'sanitizes general incorrect feedback' do
32
+ expect(loaded_class.feedback[:incorrect]).to include '<p>Incorrect'
33
+ expect(loaded_class.feedback[:incorrect]).to include '<img'
34
+ expect(loaded_class.feedback[:incorrect]).not_to include 'script="alert(\'bad\')"'
35
+ end
36
+
37
+ it 'sanitizes answer feedback' do
38
+ expect(loaded_class.answer_feedback.first[:feedback]).to include '<p>Answer was Correc'
39
+ expect(loaded_class.answer_feedback.first[:feedback]).to include '<img'
40
+ expect(loaded_class.answer_feedback.first[:feedback]).not_to include 'script="alert(\'bad\')"'
41
+ end
42
+ end
43
+
13
44
  context 'quiz.xml' do
14
45
  let(:file_path) { File.join('spec', 'fixtures', 'items_1.2', 'true_false.xml') }
15
46
  let(:test_object) { Qti::V1::Models::Assessment.from_path!(file_path) }
@@ -0,0 +1,22 @@
1
+ describe Qti::V1::Models::Base do
2
+ context 'specified as single content node matching helpers' do
3
+ let(:loaded_class) do
4
+ fixtures_path = File.join('spec', 'fixtures')
5
+ path = File.join(fixtures_path, 'test_qti_1.2', 'quiz.xml')
6
+ described_class.from_path!(path)
7
+ end
8
+
9
+ describe '.sanitize_attributes' do
10
+ it 'respects valid content' do
11
+ source = 'fill in the [blank]'
12
+ expect(loaded_class.sanitize_attributes(source)).to eq source
13
+ end
14
+
15
+ it 'translates invalid content' do
16
+ source = '<span title="[x]">fill in the [blank]</span>'
17
+ expected = '<span title="&amp;#91;x&amp;#93;">fill in the [blank]</span>'
18
+ expect(loaded_class.sanitize_attributes(source)).to eq expected
19
+ end
20
+ end
21
+ end
22
+ end
@@ -39,7 +39,7 @@ describe Qti::V1::Models::Interactions::ChoiceInteraction do
39
39
  let(:file_path) { File.join(fixtures_path, 'true_false.xml') }
40
40
  let(:shuffle_value) { true }
41
41
  let(:answer_choices_count) { 2 }
42
- let(:meta_type) { nil }
42
+ let(:meta_type) { 'true_false_question' }
43
43
 
44
44
  include_examples 'shuffled?'
45
45
  include_examples 'answers'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: qti
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.13.1
4
+ version: 2.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adrian Diaz
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2023-04-24 00:00:00.000000000 Z
15
+ date: 2023-05-11 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: actionview
@@ -665,6 +665,7 @@ files:
665
665
  - spec/lib/qti/sanitizer_spec.rb
666
666
  - spec/lib/qti/v1/models/assessment_item_spec.rb
667
667
  - spec/lib/qti/v1/models/assessment_spec.rb
668
+ - spec/lib/qti/v1/models/base_spec.rb
668
669
  - spec/lib/qti/v1/models/choices/fill_blank_choice_spec.rb
669
670
  - spec/lib/qti/v1/models/choices/logical_identifier_choice_spec.rb
670
671
  - spec/lib/qti/v1/models/interactions/base_fill_blank_interaction_spec.rb
@@ -1022,6 +1023,7 @@ test_files:
1022
1023
  - spec/lib/qti/sanitizer_spec.rb
1023
1024
  - spec/lib/qti/v1/models/assessment_item_spec.rb
1024
1025
  - spec/lib/qti/v1/models/assessment_spec.rb
1026
+ - spec/lib/qti/v1/models/base_spec.rb
1025
1027
  - spec/lib/qti/v1/models/choices/fill_blank_choice_spec.rb
1026
1028
  - spec/lib/qti/v1/models/choices/logical_identifier_choice_spec.rb
1027
1029
  - spec/lib/qti/v1/models/interactions/base_fill_blank_interaction_spec.rb