lcms-engine 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (114) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +1 -1
  3. data/.env.docker +1 -1
  4. data/.rubocop.yml +8 -7
  5. data/.ruby-version +1 -1
  6. data/CHANGELOG.md +16 -1
  7. data/Dockerfile +1 -1
  8. data/Gemfile.lock +223 -184
  9. data/README.md +3 -2
  10. data/app/controllers/concerns/lcms/engine/reimportable.rb +11 -1
  11. data/app/controllers/lcms/engine/admin/admin_controller.rb +1 -1
  12. data/app/controllers/lcms/engine/admin/documents_controller.rb +19 -4
  13. data/app/controllers/lcms/engine/admin/materials_controller.rb +12 -3
  14. data/app/controllers/lcms/engine/admin/standards_controller.rb +6 -1
  15. data/app/controllers/lcms/engine/admin/users_controller.rb +6 -1
  16. data/app/entities/lcms/engine/grades.rb +17 -6
  17. data/app/entities/lcms/engine/media_embed.rb +1 -1
  18. data/app/entities/lcms/engine/roman_numerals.rb +1 -1
  19. data/app/forms/lcms/engine/document_form.rb +19 -14
  20. data/app/forms/lcms/engine/material_form.rb +3 -2
  21. data/app/helpers/admin/components_helper.rb +2 -2
  22. data/app/helpers/lcms/engine/view_helper.rb +1 -1
  23. data/app/interactors/lcms/engine/explore_curriculum_interactor.rb +1 -1
  24. data/app/javascript/components/admin/ImportStatus.jsx +7 -3
  25. data/app/javascript/components/admin/MultiSelectedOperation.jsx +1 -0
  26. data/app/javascript/components/admin/association-picker/AssociationPicker.jsx +1 -0
  27. data/app/javascript/components/admin/association-picker/AssociationPickerResults.jsx +1 -0
  28. data/app/javascript/components/admin/association-picker/AssociationPickerWindow.jsx +1 -0
  29. data/app/javascript/components/admin/curriculum/CurriculumEditor.jsx +1 -0
  30. data/app/javascript/components/admin/resource-picker/ResourcePicker.jsx +1 -0
  31. data/app/jobs/concerns/lcms/engine/nested_resque_job.rb +1 -4
  32. data/app/jobs/lcms/engine/document_generate_gdoc_job.rb +2 -2
  33. data/app/jobs/lcms/engine/document_generate_job.rb +1 -1
  34. data/app/jobs/lcms/engine/document_parse_job.rb +1 -1
  35. data/app/models/lcms/engine/component.rb +3 -3
  36. data/app/models/lcms/engine/download.rb +1 -1
  37. data/app/models/lcms/engine/search/document.rb +4 -3
  38. data/app/presenters/lcms/engine/document_presenter.rb +2 -2
  39. data/app/queries/lcms/engine/admin_documents_query.rb +1 -1
  40. data/app/serializers/lcms/engine/previews_material_serializer.rb +1 -0
  41. data/app/services/lcms/engine/document_build_service.rb +4 -0
  42. data/app/services/lcms/engine/html_sanitizer.rb +5 -5
  43. data/app/services/lcms/engine/lessons_gdoc_bundler.rb +1 -1
  44. data/app/services/lcms/engine/material_build_service.rb +6 -2
  45. data/app/services/lcms/engine/material_preview_generator.rb +2 -2
  46. data/app/services/lcms/engine/react_materials_resolver.rb +3 -2
  47. data/app/views/lcms/engine/admin/batch_reimports/_search_form.html.erb +1 -1
  48. data/app/views/lcms/engine/admin/curriculums/edit.html.erb +1 -3
  49. data/app/views/lcms/engine/admin/documents/_materials_links.html.erb +2 -2
  50. data/app/views/lcms/engine/admin/documents/_search_form.html.erb +1 -1
  51. data/app/views/lcms/engine/admin/documents/index.html.erb +2 -2
  52. data/app/views/lcms/engine/admin/documents/new.html.erb +1 -1
  53. data/app/views/lcms/engine/admin/materials/_search_form.html.erb +1 -1
  54. data/app/views/lcms/engine/admin/materials/index.html.erb +4 -4
  55. data/app/views/lcms/engine/admin/resource_bulk_edits/new.html.erb +1 -1
  56. data/app/views/lcms/engine/admin/resources/_fields.html.erb +3 -0
  57. data/app/views/lcms/engine/admin/resources/_search_form.html.erb +1 -1
  58. data/app/views/lcms/engine/documents/gdoc/_agenda.html.erb +1 -1
  59. data/app/views/lcms/engine/documents/show.html.erb +1 -1
  60. data/app/views/lcms/engine/materials/show.html.erb +1 -1
  61. data/app/views/lcms/engine/resources/_download.html.erb +2 -2
  62. data/app/views/lcms/engine/resources/_unit_bundles.html.erb +2 -2
  63. data/config/locales/admin/en.yml +1 -1
  64. data/db/schema.rb +1 -1
  65. data/lcms-engine.gemspec +12 -10
  66. data/lib/doc_template.rb +1 -1
  67. data/lib/doc_template/objects/agenda_metadata.rb +1 -1
  68. data/lib/doc_template/objects/metadata_helpers.rb +1 -1
  69. data/lib/doc_template/objects/toc_metadata.rb +2 -2
  70. data/lib/doc_template/tables/base.rb +3 -2
  71. data/lib/doc_template/tags/activity_metadata_type_tag.rb +1 -1
  72. data/lib/doc_template/tags/answer_space_tag.rb +1 -1
  73. data/lib/doc_template/tags/base_tag.rb +3 -3
  74. data/lib/doc_template/tags/columns_tag.rb +1 -1
  75. data/lib/doc_template/tags/def_tag.rb +1 -1
  76. data/lib/doc_template/tags/expand_tag.rb +1 -0
  77. data/lib/doc_template/tags/heading_tag.rb +1 -1
  78. data/lib/doc_template/tags/inset_tag.rb +2 -2
  79. data/lib/doc_template/tags/latex_tag.rb +1 -1
  80. data/lib/doc_template/tags/page_break_tag.rb +1 -1
  81. data/lib/doc_template/tags/pd_tag.rb +4 -4
  82. data/lib/doc_template/tags/section_tag.rb +2 -2
  83. data/lib/doc_template/tags/standard_tag.rb +3 -3
  84. data/lib/doc_template/tags/table_preserve_alignment_tag.rb +1 -1
  85. data/lib/document_exporter/gdoc/base.rb +1 -1
  86. data/lib/document_renderer/part.rb +3 -3
  87. data/lib/elasticsearch/persistence/repository/response/results.rb +1 -1
  88. data/lib/lcms/engine/engine.rb +1 -1
  89. data/lib/lcms/engine/version.rb +1 -1
  90. data/lib/lt/lcms/metadata/base_service.rb +2 -1
  91. data/lib/lt/lcms/metadata/context.rb +2 -2
  92. data/lib/lt/lcms/metadata/service.rb +3 -1
  93. data/lib/resque_job.rb +3 -6
  94. data/lib/standard_importer.rb +4 -4
  95. data/lib/tasks/cloud66.rake +6 -4
  96. data/lib/tasks/document.rake +3 -3
  97. data/lib/tasks/google.rake +1 -1
  98. data/package.json +1 -0
  99. data/spec/controllers/admin/association_picker_controller_spec.rb +6 -8
  100. data/spec/controllers/admin/documents_controller_spec.rb +3 -1
  101. data/spec/controllers/admin/materials_controller_spec.rb +1 -1
  102. data/spec/controllers/admin/resources_controller_spec.rb +2 -2
  103. data/spec/dummy/.env.docker +5 -0
  104. data/spec/dummy/config/environments/production.rb +1 -1
  105. data/spec/dummy/config/puma.rb +3 -3
  106. data/spec/entities/grades_spec.rb +12 -0
  107. data/spec/features/admin/lessons/add_lesson_spec.rb +3 -3
  108. data/spec/features/admin/materials/add_material_spec.rb +3 -3
  109. data/spec/forms/document_form_spec.rb +11 -1
  110. data/spec/lib/doc_template/tables/shared_examples/remove_table.rb +1 -1
  111. data/spec/rails_helper.rb +19 -2
  112. data/spec/services/document_build_service_spec.rb +1 -1
  113. data/yarn.lock +145 -170
  114. metadata +81 -34
data/README.md CHANGED
@@ -13,8 +13,9 @@ separately, simplifying the client applications in the process.
13
13
 
14
14
  |Branch|Rails version|
15
15
  |------|-------------|
16
- |master|Rails 5.2.4.2|
17
- |0.1.x|Rails 4.2.11|
16
+ |master|Rails 6|
17
+ |0.3.x|Rails 5.2.4.5|
18
+ |0.1.x|Rails 4.2.11.3|
18
19
 
19
20
  This is still a [work in progress](https://github.com/learningtapestry/lcms-engine/issues/3). The
20
21
  initial phase of the project consisted in extracting as much code as possible from the client
@@ -19,10 +19,20 @@ module Lcms
19
19
  status = job_class.status(jid)
20
20
  obj[jid] = {
21
21
  status: status,
22
- result: (status == :done ? job_class.fetch_result(jid) : nil)
22
+ result: (status == :done ? prepare_result(job_class, jid) : nil)
23
23
  }.compact
24
24
  end
25
25
  end
26
+
27
+ def prepare_result(job_class, jid)
28
+ jid_res = job_class.fetch_result(jid)
29
+ return jid_res if jid_res['ok']
30
+
31
+ {
32
+ ok: false,
33
+ errors: Array.wrap("<a href=\"#{jid_res['link']}\">Source</a>: #{jid_res['errors'].join(', ')}")
34
+ }
35
+ end
26
36
  end
27
37
  end
28
38
  end
@@ -11,7 +11,7 @@ module Lcms
11
11
  materials_query: Lcms::Engine::AdminMaterialsQuery
12
12
  }.freeze
13
13
 
14
- RE_GOOGLE_FOLDER = %r{/drive/(.*/)?folders/}
14
+ RE_GOOGLE_FOLDER = %r{/drive/(.*/)?folders/}.freeze
15
15
 
16
16
  layout :customized_layout
17
17
 
@@ -10,9 +10,10 @@ module Lcms
10
10
  include Reimportable
11
11
 
12
12
  before_action :find_selected, only: %i(destroy_selected reimport_selected)
13
+ before_action :set_query_params
13
14
 
14
15
  def index
15
- @query = OpenStruct.new(params[:query])
16
+ @query = OpenStruct.new @query_params
16
17
  @documents = DocTemplate.config['queries']['document'].constantize.call(@query, page: params[:page])
17
18
  render_customized_view
18
19
  end
@@ -23,7 +24,7 @@ module Lcms
23
24
  @document = reimport_lesson
24
25
  if @document.save
25
26
  redirect_to AdminController.document_path(@document.document),
26
- notice: t('.success', name: @document.document.name)
27
+ notice: t('.success', name: @document.document.name, errors: collect_errors)
27
28
  else
28
29
  render :new
29
30
  end
@@ -32,12 +33,12 @@ module Lcms
32
33
  def destroy
33
34
  @document = Document.find(params[:id])
34
35
  @document.destroy
35
- redirect_to admin_documents_path(query: params[:query]), notice: t('.success')
36
+ redirect_to admin_documents_path(query: @query_params), notice: t('.success')
36
37
  end
37
38
 
38
39
  def destroy_selected
39
40
  count = @documents.destroy_all.count
40
- redirect_to admin_documents_path(query: params[:query]), notice: t('.success', count: count)
41
+ redirect_to admin_documents_path(query: @query_params), notice: t('.success', count: count)
41
42
  end
42
43
 
43
44
  def import_status
@@ -67,6 +68,12 @@ module Lcms
67
68
  @props = { jobs: jobs, type: :documents, links: view_links }
68
69
  end
69
70
 
71
+ def collect_errors
72
+ return if @document.service_errors.empty?
73
+
74
+ "Errors: #{@document.service_errors.join(' ')}"
75
+ end
76
+
70
77
  def find_selected
71
78
  return head(:bad_request) unless params[:selected_ids].present?
72
79
 
@@ -105,6 +112,14 @@ module Lcms
105
112
  MaterialForm.new({ link: material.file_url, source_type: material.source_type }, google_credentials).save
106
113
  end
107
114
  end
115
+
116
+ def set_query_params
117
+ @query_params = params[:query]
118
+ &.permit(
119
+ :broken_materials, :course, :grade, :inactive, :locale, :module, :only_failed, :reimport_required,
120
+ :search_term, :sort_by
121
+ ) || {}
122
+ end
108
123
  end
109
124
  end
110
125
  end
@@ -8,9 +8,10 @@ module Lcms
8
8
  include Reimportable
9
9
 
10
10
  before_action :find_selected, only: %i(destroy_selected reimport_selected)
11
+ before_action :set_query_params
11
12
 
12
13
  def index
13
- @query = OpenStruct.new params[:query]
14
+ @query = OpenStruct.new @query_params
14
15
  @materials = DocTemplate.config['queries']['material'].constantize.call(@query, page: params[:page])
15
16
  render_customized_view
16
17
  end
@@ -32,12 +33,12 @@ module Lcms
32
33
  def destroy
33
34
  material = Material.find(params[:id])
34
35
  material.destroy
35
- redirect_to admin_materials_path(query: params[:query]), notice: t('.success')
36
+ redirect_to admin_materials_path(query: @query_params), notice: t('.success')
36
37
  end
37
38
 
38
39
  def destroy_selected
39
40
  count = @materials.destroy_all.count
40
- redirect_to admin_materials_path(query: params[:query]), notice: t('.success', count: count)
41
+ redirect_to admin_materials_path(query: @query_params), notice: t('.success', count: count)
41
42
  end
42
43
 
43
44
  def import_status
@@ -89,6 +90,14 @@ module Lcms
89
90
  .map { |id| ::Lt::Lcms::Lesson::Downloader::Gdoc.gdoc_file_url(id) }
90
91
  end
91
92
  end
93
+
94
+ def set_query_params
95
+ @query_params = params[:query]
96
+ &.permit(
97
+ :course, :lesson, :name_date, :orientation, :search_term, :search_file_name, :sort_by, :title, :type,
98
+ :unit
99
+ ) || {}
100
+ end
92
101
  end
93
102
  end
94
103
  end
@@ -5,11 +5,12 @@ module Lcms
5
5
  module Admin
6
6
  class StandardsController < AdminController
7
7
  before_action :find_standard, except: [:index]
8
+ before_action :set_query_params
8
9
 
9
10
  def edit; end
10
11
 
11
12
  def index
12
- @query = OpenStruct.new params[:query]
13
+ @query = OpenStruct.new @query_params
13
14
 
14
15
  scope = Standard.order(:id)
15
16
  scope = scope.search_by_name(@query.name) if @query.name.present?
@@ -31,6 +32,10 @@ module Lcms
31
32
  @standard = Standard.find(params[:id])
32
33
  end
33
34
 
35
+ def set_query_params
36
+ @query_params = params[:query]&.permit(:name) || {}
37
+ end
38
+
34
39
  def standard_params
35
40
  params.require(:standard).permit(:description)
36
41
  end
@@ -5,9 +5,10 @@ module Lcms
5
5
  module Admin
6
6
  class UsersController < AdminController
7
7
  before_action :find_user, except: %i(index new create)
8
+ before_action :set_query_params
8
9
 
9
10
  def index
10
- @query = OpenStruct.new(params[:query])
11
+ @query = OpenStruct.new @query_params
11
12
  @users = users(@query)
12
13
  end
13
14
 
@@ -52,6 +53,10 @@ module Lcms
52
53
  @user = User.find(params[:id])
53
54
  end
54
55
 
56
+ def set_query_params
57
+ @query_params = params[:query]&.permit(:access_code, :email) || {}
58
+ end
59
+
55
60
  def user_params
56
61
  params.require(:user).permit(:access_code, :email, :name, :role)
57
62
  end
@@ -12,31 +12,42 @@ module Lcms
12
12
 
13
13
  attr_reader :model
14
14
 
15
+ class << self
16
+ def grades
17
+ ::Lcms::Engine::Grades::GRADES
18
+ end
19
+
20
+ def grades_abbrevs
21
+ ::Lcms::Engine::Grades::GRADES_ABBR
22
+ end
23
+ end
24
+
15
25
  def initialize(model)
16
26
  @model = model
17
27
  end
18
28
 
19
29
  def list
20
- @list ||= if model.is_a?(Resource)
30
+ @list ||= case model
31
+ when Resource
21
32
  Array.wrap model.metadata['grade']
22
- elsif model.is_a?(Search::Document)
33
+ when Search::Document
23
34
  Array.wrap model.grade.presence
24
35
  else
25
36
  model.grade_list
26
- end.sort_by { |g| GRADES.index(g) }
37
+ end.sort_by { |g| self.class.grades.index(g) }
27
38
  end
28
39
 
29
40
  def average(abbr: true)
30
41
  return nil if average_number.nil?
31
42
 
32
- avg = GRADES[average_number]
43
+ avg = self.class.grades[average_number]
33
44
  abbr ? (grade_abbr(avg) || 'base') : avg
34
45
  end
35
46
 
36
47
  def average_number
37
48
  return nil if list.empty?
38
49
 
39
- list.map { |g| GRADES.index(g) }.sum / (list.size.nonzero? || 1)
50
+ list.map { |g| self.class.grades.index(g) }.sum / (list.size.nonzero? || 1)
40
51
  end
41
52
 
42
53
  def grade_abbr(abbr)
@@ -62,7 +73,7 @@ module Lcms
62
73
  abbr = grade_abbr(g).upcase
63
74
 
64
75
  # if the current grade is subsequent we store on the same chain
65
- if idx.zero? || GRADES.index(g) == GRADES.index(prev) + 1
76
+ if idx.zero? || self.class.grades.index(g) == self.class.grades.index(prev) + 1
66
77
  chain << abbr
67
78
  else
68
79
  # the grade is not subsequent, so we store the current chain, and create a new one
@@ -26,7 +26,7 @@ module Lcms
26
26
  when /vimeo\.com/
27
27
  url.match(%r{https?://(www\.)?vimeo.com/(\d+)}).try(:[], 2)
28
28
  when /youtu\.be/
29
- url.match(%r{https?://(www\.)?youtu\.be/([^"&?\/]+)}).try(:[], 2)
29
+ url.match(%r{https?://(www\.)?youtu\.be/([^"&?/]+)}).try(:[], 2)
30
30
  end
31
31
  end
32
32
  end
@@ -3,7 +3,7 @@
3
3
  module Lcms
4
4
  module Engine
5
5
  class RomanNumerals
6
- ROMAN_NUMERALS_RE = /^(M|CM|D|CD|C|XC|L|XL|X|IX|V|IV|I)/
6
+ ROMAN_NUMERALS_RE = /^(M|CM|D|CD|C|XC|L|XL|X|IX|V|IV|I)/.freeze
7
7
  SYMBOLS = [
8
8
  [1000, 'M'],
9
9
  [900, 'CM'],
@@ -13,7 +13,7 @@ module Lcms
13
13
  validates_presence_of :link, if: -> { link_fs.blank? }
14
14
  validates_presence_of :link_fs, if: -> { link.blank? }
15
15
 
16
- attr_reader :document
16
+ attr_reader :document, :service_errors
17
17
 
18
18
  def initialize(attributes = {}, opts = {})
19
19
  @is_reimport = attributes.delete(:reimport).present? || false
@@ -29,7 +29,7 @@ module Lcms
29
29
  @document.update(reimported: true)
30
30
  rescue StandardError => e
31
31
  @document&.update(reimported: false)
32
- Rails.logger.error e.message + "\n " + e.backtrace.join("\n ")
32
+ Rails.logger.error "#{e.message}\n #{e.backtrace.join("\n ")}"
33
33
  errors.add(:link, e.message)
34
34
  false
35
35
  end
@@ -45,18 +45,23 @@ module Lcms
45
45
  def build_document
46
46
  service = document_build_service
47
47
 
48
- if is_reimport
49
- doc = service.build_for(link)
50
- doc = service.build_for(link_fs, expand: true) if link_fs.present?
51
- doc
52
- elsif (full_doc = find_full_document)
53
- # if there is a document with the same file_id or foundational_file_id
54
- # we need to make full re-import to correctly handle expand process
55
- service.build_for(full_doc.file_url)
56
- service.build_for(full_doc.file_fs_url, expand: true)
57
- else
58
- service.build_for link
59
- end
48
+ result =
49
+ if is_reimport
50
+ doc = service.build_for(link)
51
+ doc = service.build_for(link_fs, expand: true) if link_fs.present?
52
+ doc
53
+ elsif (full_doc = find_full_document)
54
+ # if there is a document with the same file_id or foundational_file_id
55
+ # we need to make full re-import to correctly handle expand process
56
+ service.build_for(full_doc.file_url)
57
+ service.build_for(full_doc.file_fs_url, expand: true)
58
+ else
59
+ service.build_for link
60
+ end
61
+
62
+ @service_errors = service.errors
63
+
64
+ result
60
65
  end
61
66
 
62
67
  def document_build_service
@@ -11,7 +11,7 @@ module Lcms
11
11
  attribute :source_type, String
12
12
  validates :link, presence: true
13
13
 
14
- attr_accessor :material
14
+ attr_accessor :material, :service_errors
15
15
 
16
16
  def initialize(attributes = {}, opts = {})
17
17
  super(attributes)
@@ -28,12 +28,13 @@ module Lcms
28
28
  }.compact
29
29
  service = MaterialBuildService.new google_credentials, params
30
30
  @material = service.build link
31
+ @service_errors = service.errors
31
32
 
32
33
  material.update preview_links: {}
33
34
  after_reimport_hook
34
35
  true
35
36
  rescue StandardError => e
36
- Rails.logger.error e.message + "\n " + e.backtrace.join("\n ")
37
+ Rails.logger.error "#{e.message}\n #{e.backtrace.join("\n ")}"
37
38
  errors.add(:link, e.message)
38
39
  false
39
40
  end
@@ -2,9 +2,9 @@
2
2
 
3
3
  module Admin
4
4
  module ComponentsHelper
5
- HIDDEN_FIELD_RE = /\b(?<=name=")[^"]+(?=")/
5
+ HIDDEN_FIELD_RE = /\b(?<=name=")[^"]+(?=")/.freeze
6
6
 
7
- def resource_picker_field(form, collection, allow_multiple: true, path:, name:)
7
+ def resource_picker_field(form, collection, path:, name:, allow_multiple: true)
8
8
  path = path.to_s
9
9
 
10
10
  base_name = form.hidden_field(name).scan(HIDDEN_FIELD_RE)[0]
@@ -116,7 +116,7 @@ module Lcms
116
116
  return unless selected_ids.present?
117
117
 
118
118
  case selected_ids
119
- when Array then
119
+ when Array
120
120
  selected_ids.include?(id.to_s)
121
121
  else
122
122
  selected_ids.split(',').include?(id.to_s)
@@ -70,7 +70,7 @@ module Lcms
70
70
 
71
71
  def slug_param
72
72
  slug = params[:p]
73
- (slug.start_with?('/') ? slug[1..-1] : slug) if slug.present?
73
+ (slug.start_with?('/') ? slug[1..] : slug) if slug.present?
74
74
  end
75
75
 
76
76
  def expanded?
@@ -1,4 +1,5 @@
1
1
  import React from 'react'
2
+ import _ from 'lodash'
2
3
 
3
4
  class ImportStatus extends React.Component {
4
5
  constructor(props) {
@@ -53,7 +54,8 @@ class ImportStatus extends React.Component {
53
54
  if (this.withPdf) {
54
55
  return (
55
56
  <a href={job.link}
56
- className="o-adm-materials__resource ub-icon ub-file-pdf button primary u-margin-left--small u-margin-bottom--zero" target="_blank">
57
+ className="o-adm-materials__resource button primary u-margin-left--small u-margin-bottom--zero" target="_blank">
58
+ <i className="far fa-file-pdf"></i>
57
59
  </a>
58
60
  )
59
61
  }
@@ -69,7 +71,8 @@ class ImportStatus extends React.Component {
69
71
  return _.map(this.links, (link, idx) => (
70
72
  <a key={`pl-${idx}`}
71
73
  href={linkWithParams(link, { id: job.model.id })}
72
- className="o-adm-materials__resource ub-icon ub-eye button primary u-margin-left--small u-margin-bottom--zero" target="_blank">
74
+ className="o-adm-materials__resource button primary u-margin-left--small u-margin-bottom--zero" target="_blank">
75
+ <i className="fas fa-eye"></i>
73
76
  </a>
74
77
  ))
75
78
  }
@@ -99,7 +102,8 @@ class ImportStatus extends React.Component {
99
102
  {job.status !== 'done' ? this.spinner() : null}
100
103
  {job.status === 'done' && job.ok ? <span>{this.resourceButton(job)}</span> : null}
101
104
  </div>
102
- {job.errors ? (<p dangerouslySetInnerHTML={{__html: _.join(job.errors, '<br/>')}}></p>) : null}
105
+ {!(_.isEmpty(job.errors)) ? (<p dangerouslySetInnerHTML={{__html: _.join(job.errors, '<br/>')}}></p>) : null}
106
+ {!(_.isEmpty(job.warnings)) ? (<p dangerouslySetInnerHTML={{ __html: _.join(job.warnings, '<br/>') }}></p>) : null}
103
107
  </li>
104
108
  )
105
109
  })
@@ -1,5 +1,6 @@
1
1
  import React from 'react'
2
2
  import ReactDOM from 'react-dom'
3
+ import _ from 'lodash'
3
4
 
4
5
  class MultiSelectedOperation extends React.Component {
5
6
  componentDidMount() {
@@ -1,5 +1,6 @@
1
1
  import React from 'react'
2
2
  import ReactDOM from 'react-dom'
3
+ import _ from 'lodash'
3
4
  // eslint-disable-next-line no-unused-vars
4
5
  import AssociationPickerItem from './AssociationPickerItem'
5
6
  import AssociationPickerWindow from './AssociationPickerWindow'
@@ -1,4 +1,5 @@
1
1
  import React from 'react';
2
+ import _ from 'lodash'
2
3
 
3
4
  function AssociationPickerResults(props) {
4
5
  let items