wcc-contentful-app 0.4.0.pre.alpha → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. checksums.yaml +5 -5
  2. data/.rspec +2 -2
  3. data/README.md +11 -0
  4. data/Rakefile +125 -0
  5. data/app/assets/config/manifest.js +5 -0
  6. data/app/assets/images/down-arrow-primary.svg +3 -0
  7. data/app/assets/images/up-arrow-primary.svg +3 -0
  8. data/app/assets/javascripts/wcc/contentful/app/contact-form.js +70 -0
  9. data/app/assets/javascripts/wcc/contentful/app/index.js +1 -0
  10. data/app/assets/stylesheets/_wcc-contentful-app.scss +7 -0
  11. data/app/assets/stylesheets/components/_menu-item.scss +5 -0
  12. data/app/assets/stylesheets/sections/_faq.scss +39 -0
  13. data/app/controllers/wcc/contentful/app/contact_form_controller.rb +39 -0
  14. data/app/controllers/wcc/contentful/app/pages_controller.rb +48 -0
  15. data/app/helpers/wcc/contentful/app/menu_helper.rb +85 -0
  16. data/app/helpers/wcc/contentful/app/section_helper.rb +135 -0
  17. data/app/mailers/wcc/contentful/app/contact_mailer.rb +11 -0
  18. data/app/models/concerns/wcc/contentful/app/preview_password.rb +19 -0
  19. data/app/models/wcc/contentful/app/contact_form_submission.rb +9 -0
  20. data/app/models/wcc/contentful/app/custom_markdown_render.rb +43 -0
  21. data/app/views/components/_faq_row.html.erb +21 -0
  22. data/app/views/components/_menu-item.html.erb +41 -0
  23. data/app/views/components/_other-menu-item.html.erb +4 -0
  24. data/app/views/components/_section.html.erb +11 -0
  25. data/app/views/layouts/mailer.html.erb +9 -0
  26. data/app/views/layouts/mailer.text.erb +1 -0
  27. data/app/views/pages/show.html.erb +4 -0
  28. data/app/views/sections/_block_text.html.erb +5 -0
  29. data/app/views/sections/_code_widget.html.erb +3 -0
  30. data/app/views/sections/_contact_form.html.erb +53 -0
  31. data/app/views/sections/_faq.html.erb +36 -0
  32. data/app/views/sections/_http_error.html.erb +13 -0
  33. data/app/views/sections/_marquee_text.html.erb +12 -0
  34. data/app/views/sections/_testimonials.html.erb +38 -0
  35. data/app/views/sections/_video.html.erb +12 -0
  36. data/app/views/sections/_video_highlight.html.erb +18 -0
  37. data/app/views/wcc/contentful/app/contact_mailer/contact_form_email.html.erb +7 -0
  38. data/bin/rails +14 -0
  39. data/config/routes.rb +7 -0
  40. data/doc +1 -0
  41. data/lib/generators/wcc/model_generator.rb +16 -6
  42. data/lib/generators/wcc/templates/menu/migrations/generated_add_menus.ts +285 -0
  43. data/lib/generators/wcc/templates/menu/models/dropdown_menu.rb +19 -0
  44. data/lib/generators/wcc/templates/menu/models/menu.rb +0 -4
  45. data/lib/generators/wcc/templates/menu/models/menu_button.rb +0 -4
  46. data/lib/generators/wcc/templates/page/migrations/generated_add_pages.ts +147 -0
  47. data/lib/generators/wcc/templates/page/models/page.rb +0 -4
  48. data/lib/generators/wcc/templates/page/models/redirect.rb +19 -0
  49. data/lib/generators/wcc/templates/section-block-text/migrations/generated_add_section-block-texts.ts +40 -0
  50. data/lib/generators/wcc/templates/section-block-text/models/section_block_text.rb +19 -0
  51. data/lib/generators/wcc/templates/section-code-widget/migrations/generated_add_section-code-widget.ts +90 -0
  52. data/lib/generators/wcc/templates/section-code-widget/models/section_code_widget.rb +23 -0
  53. data/lib/generators/wcc/templates/section-contact-form/migrations/create_wcc_contentful_app_contact_form_submissions.rb +12 -0
  54. data/lib/generators/wcc/templates/section-contact-form/migrations/generated_add_section-contact-forms.ts +147 -0
  55. data/lib/generators/wcc/templates/section-contact-form/models/form_field.rb +19 -0
  56. data/lib/generators/wcc/templates/section-contact-form/models/section_contact_form.rb +19 -0
  57. data/lib/generators/wcc/templates/section-faq/migrations/generated_add_section-faqs.ts +148 -0
  58. data/lib/generators/wcc/templates/section-faq/models/section_faq.rb +19 -0
  59. data/lib/generators/wcc/templates/section-http-error/migrations/generated_add_section-http-errors.ts +87 -0
  60. data/lib/generators/wcc/templates/section-http-error/models/section_http_error.rb +19 -0
  61. data/lib/generators/wcc/templates/section-marquee-text/migrations/generated_add_section-marquee-texts.ts +64 -0
  62. data/lib/generators/wcc/templates/section-marquee-text/models/section_marquee_text.rb +19 -0
  63. data/lib/generators/wcc/templates/section-testimonial/migrations/generated_add_section-testimonials.ts +182 -0
  64. data/lib/generators/wcc/templates/section-testimonial/models/section_testimonial.rb +19 -0
  65. data/lib/generators/wcc/templates/section-video-highlight/migrations/generated_add_section-video-highlights.ts +80 -0
  66. data/lib/generators/wcc/templates/section-video-highlight/models/section_video_highlight.rb +19 -0
  67. data/lib/generators/wcc/templates/section-video/migrations/generated_add_section-videos.ts +77 -0
  68. data/lib/generators/wcc/templates/section-video/models/section_video.rb +19 -0
  69. data/lib/generators/wcc/templates/site-config/migrations/generated_add_site-configs.ts +97 -0
  70. data/lib/generators/wcc/templates/site-config/models/site_config.rb +19 -0
  71. data/lib/generators/wcc/templates/wcc_contentful.rb +1 -1
  72. data/lib/wcc/contentful/app.rb +46 -3
  73. data/lib/wcc/contentful/app/configuration.rb +60 -0
  74. data/lib/wcc/contentful/app/engine.rb +15 -0
  75. data/lib/wcc/contentful/app/exceptions.rb +9 -0
  76. data/lib/wcc/contentful/app/rails.rb +6 -0
  77. data/lib/wcc/contentful/app/version.rb +1 -1
  78. data/lib/wcc/contentful/model/dropdown_menu.rb +0 -3
  79. data/lib/wcc/contentful/model/form_field.rb +4 -0
  80. data/lib/wcc/contentful/model/menu.rb +0 -2
  81. data/lib/wcc/contentful/model/menu_button.rb +18 -5
  82. data/lib/wcc/contentful/model/page.rb +0 -4
  83. data/lib/wcc/contentful/model/redirect.rb +21 -11
  84. data/lib/wcc/contentful/model/section_contact_form.rb +42 -0
  85. data/lib/wcc/contentful/model/site_config.rb +7 -0
  86. data/wcc-contentful-app.gemspec +23 -15
  87. metadata +169 -106
  88. data/Gemfile +0 -8
  89. data/lib/generators/wcc/templates/menu/generated_add_menus.ts +0 -192
  90. data/lib/generators/wcc/templates/page/generated_add_pages.ts +0 -50
  91. data/lib/tasks/validate.rake +0 -20
  92. data/lib/wcc/contentful/app/model_validators.rb +0 -121
  93. data/lib/wcc/contentful/app/model_validators/dsl.rb +0 -166
  94. data/lib/wcc/contentful/ext/model.rb +0 -5
@@ -0,0 +1,135 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'redcarpet'
4
+
5
+ module WCC::Contentful::App::SectionHelper
6
+ extend self
7
+
8
+ def render_section(section, index)
9
+ render('components/section', section: section, index: index)
10
+ end
11
+
12
+ def section_template_name(section)
13
+ section.class.name.demodulize.underscore.sub('section_', '')
14
+ end
15
+
16
+ def section_css_name(section)
17
+ section_template_name(section).dasherize
18
+ end
19
+
20
+ def section_styles(section)
21
+ section_styles = ['section-' + section_css_name(section)]
22
+ if styles = section.try(:styles)
23
+ section_styles.push(styles.map { |style| style.downcase.gsub(/[^\w]/, '-') })
24
+ elsif style = section.try(:style)
25
+ section_styles.push(style.downcase.gsub(/[^\w]/, '-'))
26
+ else
27
+ section_styles.push('default')
28
+ end
29
+ section_styles
30
+ end
31
+
32
+ def section_id(section)
33
+ title = section.try(:bookmark_title) || section.try(:title)
34
+ CGI.escape(title.gsub(/\W+/, '-')) if title.present?
35
+ end
36
+
37
+ def markdown(text)
38
+ raise ArgumentError, 'markdown method requires text' unless text
39
+
40
+ markdown_links = links_within_markdown(text)
41
+ links_with_classes, raw_classes = gather_links_with_classes_data(markdown_links)
42
+
43
+ options = {
44
+ filter_html: true,
45
+ hard_wrap: true,
46
+ link_attributes: { target: '_blank' },
47
+ space_after_headers: true,
48
+ fenced_code_blocks: true,
49
+ links_with_classes: links_with_classes
50
+ }
51
+
52
+ extensions = {
53
+ autolink: true,
54
+ superscript: true,
55
+ disable_indented_code_blocks: true,
56
+ tables: true
57
+ }
58
+
59
+ renderer = ::WCC::Contentful::App::CustomMarkdownRender.new(options)
60
+ markdown = ::Redcarpet::Markdown.new(renderer, extensions)
61
+ html_to_render = markdown.render(remove_markdown_href_class_syntax(raw_classes, text))
62
+
63
+ content_tag(:div,
64
+ CGI.unescapeHTML(html_to_render).html_safe,
65
+ class: 'formatted-content')
66
+ end
67
+
68
+ def links_within_markdown(text)
69
+ text.scan(/(\[(.*?)\]\((.*?)\)(\{\:.*?\})?)/)
70
+ end
71
+
72
+ def gather_links_with_classes_data(markdown_links)
73
+ links_with_classes_arr = []
74
+ raw_classes_arr = []
75
+ markdown_links.each do |markdown_link_arr|
76
+ next unless markdown_link_arr.last.present?
77
+
78
+ raw_class = markdown_link_arr[3]
79
+ url, title = url_and_title(markdown_link_arr[2])
80
+ content = markdown_link_arr[1]
81
+ classes = capture_individual_classes(raw_class)
82
+ link_class = combine_individual_classes_to_one_string(classes)
83
+
84
+ raw_classes_arr << raw_class
85
+ links_with_classes_arr << [url, title, content, link_class]
86
+ end
87
+
88
+ [links_with_classes_arr, raw_classes_arr]
89
+ end
90
+
91
+ def remove_markdown_href_class_syntax(raw_classes, text)
92
+ text_without_markdown_class_syntax = text.dup
93
+ raw_classes.each { |klass| text_without_markdown_class_syntax.slice!(klass) }
94
+ text_without_markdown_class_syntax
95
+ end
96
+
97
+ def url_and_title(markdown_link_and_title)
98
+ match =
99
+ markdown_link_and_title.scan(
100
+ /(\s|^)(https?:\/\/\S*|^\/\S*\/*\S*|^#\S*|mailto:\S*)(?=\s|$)|(\".*?\")/
101
+ )
102
+ url = match[0][1]
103
+ title = match[1] ? match[1][2] : nil
104
+ [url, title]
105
+ end
106
+
107
+ def capture_individual_classes(classes)
108
+ classes.scan(/\.[^\.\}\s]*/)
109
+ end
110
+
111
+ def combine_individual_classes_to_one_string(classes)
112
+ class_string = ''
113
+ classes.each do |klass|
114
+ class_string += klass.tr('.', '') + ' '
115
+ end
116
+ class_string
117
+ end
118
+
119
+ def safe_line_break(text, options = {})
120
+ return unless text.present?
121
+
122
+ text = CGI.escapeHTML(text)
123
+ text = text.gsub(/\&amp;(nbsp|vert|\#\d+);/, '&\1;')
124
+ .gsub(/\&lt;br\/?\&gt;/, '<br/>')
125
+ content_tag(:span, text.html_safe, {
126
+ class: 'safe-line-break'
127
+ }.merge(options))
128
+ end
129
+
130
+ def split_content_for_mobile_view(visible_count, speakers)
131
+ visible_count = visible_count.to_i
132
+ speakers = [*speakers].compact
133
+ [speakers.shift(visible_count), speakers]
134
+ end
135
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WCC::Contentful::App
4
+ class ContactMailer < ::ApplicationMailer
5
+ def contact_form_email(to_email, data)
6
+ @form_data = data
7
+
8
+ mail(from: @form_data[:Email], to: to_email, subject: "#{@form_data[:internal_title]} Submission")
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WCC::Contentful::App::PreviewPassword
4
+ def preview?
5
+ # check ApplicationController for a :preview? method
6
+ return super if defined?(super)
7
+
8
+ @preview ||=
9
+ if preview_password.present?
10
+ params[:preview]&.chomp == preview_password.chomp
11
+ else
12
+ false
13
+ end
14
+ end
15
+
16
+ def preview_password
17
+ WCC::Contentful::App.configuration.preview_password
18
+ end
19
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WCC::Contentful::App
4
+ if defined?(::ActiveRecord)
5
+ class ContactFormSubmission < ::ActiveRecord::Base
6
+ self.table_name = 'wcc_contentful_app_contact_form_submissions'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WCC::Contentful::App
4
+ class CustomMarkdownRender < Redcarpet::Render::HTML
5
+ def initialize(options)
6
+ super
7
+ @options = options
8
+ end
9
+
10
+ def link(link, title, content)
11
+ link_with_class_data =
12
+ @options[:links_with_classes]&.find do |link_with_class|
13
+ link_with_class[0] == link &&
14
+ link_with_class[2] == CGI.unescape_html(content)
15
+ end
16
+
17
+ link_class = link_with_class_data ? link_with_class_data[3] : nil
18
+ ActionController::Base.helpers.link_to(
19
+ content,
20
+ link,
21
+ hyperlink_attributes(title, link, link_class)
22
+ )
23
+ end
24
+
25
+ def hyperlink_attributes(title, url, link_class = nil)
26
+ link_attrs = { title: title, class: link_class }
27
+
28
+ link_attrs[:target] = use_target_blank?(url) ? '_blank' : nil
29
+
30
+ return link_attrs unless @options[:link_attributes]
31
+
32
+ @options[:link_attributes].merge(link_attrs)
33
+ end
34
+
35
+ def use_target_blank?(url)
36
+ url.scan(/(\s|^)(https?:\/\/\S*)/).present?
37
+ end
38
+
39
+ def table(header, body)
40
+ "<table class=\"table\">#{header}#{body}</table>"
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,21 @@
1
+ <div class="row justify-content-center section-faq__row">
2
+ <div class="col-md-6 col-xs-8 section-faq__faq"
3
+ data-toggle="collapse" data-target="#faq-section-<%= faq.id %>">
4
+ <div class="section-faq__faq-question safe-line-break"><%= safe_line_break(faq.questions) %></div>
5
+
6
+ <div class="section-faq__faq-answer collapse" id="faq-section-<%= faq.id %>">
7
+ <%= markdown(faq.answers) if faq.answers.present? %>
8
+ </div>
9
+ </div>
10
+ <div class="col-md-1 col-xs-1 section-faq__expander">
11
+ <a href="#faq-section-<%= faq.id %>" id="faq-section-<%= faq.id %>-expander"
12
+ role="button"
13
+ data-toggle="collapse"
14
+ aria-expanded="false"></a>
15
+ </div>
16
+ </div>
17
+ <div class="row justify-content-center">
18
+ <div class="col-md-8 col-xs-10">
19
+ <div class="horizontal-line"></div>
20
+ </div>
21
+ </div>
@@ -0,0 +1,41 @@
1
+ <% return '' unless item
2
+
3
+ classes ||= ['nav-item']
4
+ classes.push('active') if item_active?(item)
5
+ classes.push('dropdown') if dropdown?(item)
6
+
7
+ tabindex ||= 0 %>
8
+ <%= content_tag(:li, class: classes) do %>
9
+ <% if dropdown?(item) %>
10
+ <% if item.label %>
11
+ <%= render_button(item.label,
12
+ class: 'nav-link dropdown-toggle',
13
+ data: { toggle: 'dropdown' },
14
+ role: 'button',
15
+ tabindex: tabindex) %>
16
+ <% else %>
17
+ <%= content_tag(:a, item.name,
18
+ class: 'nav-link dropdown-toggle',
19
+ data: { toggle: 'dropdown' },
20
+ role: 'button',
21
+ tabindex: tabindex) %>
22
+ <% end %>
23
+
24
+ <div class='dropdown-menu'>
25
+ <% item.items.each do |dropdown_item|
26
+ next unless dropdown_item %>
27
+ <% if menu_button?(dropdown_item) %>
28
+ <%= render_button(dropdown_item,
29
+ class: 'dropdown-item',
30
+ tabindex: tabindex) %>
31
+ <% else %>
32
+ <%= render 'components/other-menu-item', item: dropdown_item, tabindex: tabindex %>
33
+ <% end %>
34
+ <% end %>
35
+ </div>
36
+ <% elsif menu_button?(item) %>
37
+ <%= render_button(item, class: 'nav-link', tabindex: tabindex) %>
38
+ <% else %>
39
+ <%= render 'components/other-menu-item', item: item, tabindex: tabindex %>
40
+ <% end %>
41
+ <% end %>
@@ -0,0 +1,4 @@
1
+ <span>
2
+ Unknown menu item: <%= item.class.name %>,
3
+ Please implement <code>components/_other-menu-item.html.erb</code>
4
+ </span>
@@ -0,0 +1,11 @@
1
+ <% styles ||= []
2
+ styles.push(*section_styles(section))
3
+ section_prefixes ||= nil
4
+ section_name = section_template_name(section) %>
5
+ <%= content_tag(:section, class: styles, id: section_id(section)) do %>
6
+ <% if section_prefixes && lookup_context.exists?(section_name, [*section_prefixes], true) %>
7
+ <%= render([*section_prefixes, section_name].join('/'), section: section) %>
8
+ <% else %>
9
+ <%= render("sections/#{section_name}", section: section) %>
10
+ <% end %>
11
+ <% end %>
@@ -0,0 +1,9 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
5
+ </head>
6
+ <body>
7
+ <%= yield %>
8
+ </body>
9
+ </html>
@@ -0,0 +1 @@
1
+ <%= content_for?(:content) ? yield(:content) : yield %>
@@ -0,0 +1,4 @@
1
+ <% @page.sections&.each_with_index do |section, index|
2
+ next if section.nil? %>
3
+ <%= render_section(section, index) %>
4
+ <% end %>
@@ -0,0 +1,5 @@
1
+ <div class="row justify-content-center">
2
+ <div class="col col-md-10 col-lg-8">
3
+ <%= markdown(section.body) if section.body.present? %>
4
+ </div>
5
+ </div>
@@ -0,0 +1,3 @@
1
+ <div class="widget-<%= section.view %>">
2
+ <%= render "sections/code_widget/#{section.view}", section: section %>
3
+ </div>
@@ -0,0 +1,53 @@
1
+ <div class="row justify-content-center">
2
+ <div class="col-xs-12 col-md-10">
3
+ <div class="contact-panel__header">
4
+ <%= markdown(section.text) %>
5
+ </div><!--contact-panel__header-->
6
+
7
+ <div class="contact-panel__form">
8
+ <%= form_tag wcc_contentful_app_engine.contact_form_path,
9
+ remote: true,
10
+ data: { contact_form: true },
11
+ method: 'post' do %>
12
+ <input type="hidden" name="id" value="<%= section.id %>">
13
+ <%= hidden_field_tag :internal_title, section.internal_title %>
14
+ <% if defined?(email_object_id) %>
15
+ <input
16
+ id="email-object-id"
17
+ type="hidden"
18
+ name="email_object_id"
19
+ value="<%= email_object_id %>">
20
+ <% end %>
21
+
22
+ <% section.fields&.each do |field| %>
23
+ <div class="form-group contact-panel__form-field">
24
+ <label for="" class="contact-panel__form-field-label"><%= field.title %></label>
25
+ <%= case field.input_type
26
+ when 'email'
27
+ content_tag(:input,
28
+ nil,
29
+ type: 'email',
30
+ name: field.title,
31
+ class: 'form-control',
32
+ required: true)
33
+ when 'textarea'
34
+ content_tag(:textarea,
35
+ nil,
36
+ rows: 8,
37
+ columnns: 80,
38
+ name: field.title,
39
+ class: 'form-control',
40
+ minlength: 5,
41
+ required: true)
42
+ else
43
+ content_tag(:input, nil, type: 'text', name: field.title, class: 'form-control')
44
+ end %>
45
+ </div>
46
+ <% end %>
47
+ <%= submit_tag section.submit_button_text || 'Submit',
48
+ class: 'btn btn-primary', data: { disable_with: 'Submitting...' } %>
49
+ <% end %>
50
+
51
+ </div><!--contact-panel__content-->
52
+ </div><!--column-->
53
+ </div><!--row-->
@@ -0,0 +1,36 @@
1
+ <div class="row justify-content-center">
2
+ <div class="col-md-6 col-xs-8">
3
+ <h2 class="section-faq__header float-right safe-line-break"><%= safe_line_break(section.title) %></h2>
4
+ </div>
5
+ </div>
6
+
7
+ <% visible, hidden =
8
+ split_content_for_mobile_view(section.number_of_faqs_before_fold || 3, section.faqs) %>
9
+ <% visible.each_with_index do |faq, index| %>
10
+ <%= render partial: 'components/faq_row', locals: { faq: faq } %>
11
+ <% end %>
12
+ <div class="collapse" id="faq-hidden-collapse">
13
+ <% hidden.each_with_index do |faq, index| %>
14
+ <%= render partial: 'components/faq_row', locals: { faq: faq } %>
15
+ <% end %>
16
+ </div>
17
+
18
+ <% unless section.number_of_faqs_before_fold.nil? %>
19
+ <% if section.faqs && section.faqs.count > section.number_of_faqs_before_fold %>
20
+ <div class="row justify-content-md-end section-faq__show-more">
21
+ <div class="col-md-3 col-xs-12">
22
+ <a class="section-faq__show-more-button"
23
+ data-toggle="collapse" href="#faq-hidden-collapse"
24
+ role="button"
25
+ aria-expanded="false">
26
+ <span class="section-faq__show-more-button__expanded">
27
+ <%= section.fold_button_hide_text || 'SEE LESS' %> <%= image_tag('up-arrow-primary.svg') %>
28
+ </span>
29
+ <span class="section-faq__show-more-button__collapsed">
30
+ <%= section.fold_button_show_text || 'SEE MORE' %> <%= image_tag('down-arrow-primary.svg') %>
31
+ </span>
32
+ </a>
33
+ </div>
34
+ </div>
35
+ <% end %>
36
+ <% end %>
@@ -0,0 +1,13 @@
1
+ <div class="section-error-page" style="background-image: url(<%= section.background_image&.file&.url %>);">
2
+ <div class="row">
3
+ <div class="col">
4
+ <div class="error-page__content">
5
+ <h2 class="error-page__content__title"><%= section.errorCode %></h2>
6
+ <p class="error-page__content__text"><%= markdown(section.text) if section.text.present? %></p>
7
+ <% section.actionButton&.each do |button| %>
8
+ <%= render_button(button, class: 'btn btn-outline-light') if button %>
9
+ <% end %>
10
+ </div><!--error-page__content-->
11
+ </div><!--column-->
12
+ </div><!--row-->
13
+ </div>