fae-rails 2.1.0 → 3.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 (137) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -2
  3. data/app/assets/config/fae/manifest.js +2 -0
  4. data/app/assets/javascripts/fae/_contrast.js +50 -0
  5. data/app/assets/javascripts/fae/_deploy.js +198 -0
  6. data/app/assets/javascripts/fae/_modals.js +94 -0
  7. data/app/assets/javascripts/fae/application.js +7 -1
  8. data/app/assets/javascripts/fae/form/_ajax.js +17 -5
  9. data/app/assets/javascripts/fae/form/_filtering.js +34 -0
  10. data/app/assets/javascripts/fae/form/_form.js +5 -2
  11. data/app/assets/javascripts/fae/form/_form_manager.js +295 -0
  12. data/app/assets/javascripts/fae/form/_slugger.js.erb +2 -2
  13. data/app/assets/javascripts/fae/form/_validator.js +224 -55
  14. data/app/assets/javascripts/fae/form/drag_drop.js +109 -0
  15. data/app/assets/javascripts/fae/form/inputs/_select.js +10 -4
  16. data/app/assets/javascripts/fae/form/inputs/_text.js +23 -9
  17. data/app/assets/javascripts/fae/vendor/simplemde/codemirror-4.inline-attachment.js +95 -0
  18. data/app/assets/javascripts/fae/vendor/simplemde/inline-attachment.js +405 -0
  19. data/app/assets/stylesheets/fae/application.css +1 -0
  20. data/app/assets/stylesheets/fae/base.scss +7 -3
  21. data/app/assets/stylesheets/fae/globals/_tags.scss +1 -1
  22. data/app/assets/stylesheets/fae/globals/imports/_variables.scss +1 -0
  23. data/app/assets/stylesheets/fae/globals/layout/_base.scss +9 -4
  24. data/app/assets/stylesheets/fae/globals/layout/_content-header.scss +14 -4
  25. data/app/assets/stylesheets/fae/globals/legacy/_pre-1.3.scss +1 -1
  26. data/app/assets/stylesheets/fae/globals/navigation/_footer.scss +1 -1
  27. data/app/assets/stylesheets/fae/globals/navigation/_header.scss +3 -3
  28. data/app/assets/stylesheets/fae/globals/navigation/_multi-col-subnav.scss +50 -0
  29. data/app/assets/stylesheets/fae/globals/navigation/_sidenav.scss +2 -2
  30. data/app/assets/stylesheets/fae/globals/navigation/_utility.scss +1 -1
  31. data/app/assets/stylesheets/fae/modules/_buttons.scss +11 -0
  32. data/app/assets/stylesheets/fae/modules/_deploy.scss +25 -0
  33. data/app/assets/stylesheets/fae/modules/_errors-bar.scss +19 -0
  34. data/app/assets/stylesheets/fae/modules/_modal.scss +25 -0
  35. data/app/assets/stylesheets/fae/modules/_toggles.scss +39 -23
  36. data/app/assets/stylesheets/fae/modules/forms/_asset-actions.scss +1 -1
  37. data/app/assets/stylesheets/fae/modules/forms/_base.scss +14 -1
  38. data/app/assets/stylesheets/fae/modules/forms/_checkbox.scss +1 -1
  39. data/app/assets/stylesheets/fae/modules/forms/_date.scss +20 -4
  40. data/app/assets/stylesheets/fae/modules/forms/_form-manager.scss +82 -0
  41. data/app/assets/stylesheets/fae/modules/forms/_hints.scss +1 -1
  42. data/app/assets/stylesheets/fae/modules/forms/_label.scss +1 -1
  43. data/app/assets/stylesheets/fae/modules/forms/_radio.scss +1 -1
  44. data/app/assets/stylesheets/fae/modules/forms/_select.scss +9 -8
  45. data/app/assets/stylesheets/fae/modules/forms/_simple-mde.scss +72 -1
  46. data/app/assets/stylesheets/fae/modules/forms/_textarea.scss +1 -1
  47. data/app/assets/stylesheets/fae/modules/tables/_base.scss +1 -1
  48. data/app/assets/stylesheets/fae/modules/tables/_filters.scss +4 -0
  49. data/app/assets/stylesheets/fae/modules/tables/_pagination.scss +2 -2
  50. data/app/assets/stylesheets/fae/pages/_error.scss +1 -1
  51. data/app/assets/stylesheets/fae/pages/_login.scss +5 -1
  52. data/app/assets/stylesheets/fae/simplemde_override.scss +32 -0
  53. data/app/controllers/fae/application_controller.rb +9 -2
  54. data/app/controllers/fae/base_controller.rb +6 -2
  55. data/app/controllers/fae/deploy_controller.rb +24 -0
  56. data/app/controllers/fae/deploy_hooks_controller.rb +71 -0
  57. data/app/controllers/fae/form_managers_controller.rb +19 -0
  58. data/app/controllers/fae/options_controller.rb +1 -0
  59. data/app/controllers/fae/setup_controller.rb +2 -2
  60. data/app/controllers/fae/static_pages_controller.rb +6 -1
  61. data/app/controllers/fae/users_controller.rb +11 -1
  62. data/app/controllers/fae/utilities_controller.rb +18 -6
  63. data/app/helpers/fae/application_helper.rb +36 -2
  64. data/app/helpers/fae/form_helper.rb +26 -2
  65. data/app/helpers/fae/view_helper.rb +26 -9
  66. data/app/models/concerns/fae/base_model_concern.rb +17 -0
  67. data/app/models/concerns/fae/seo_set_concern.rb +1 -0
  68. data/app/models/fae/change.rb +20 -6
  69. data/app/models/fae/deploy_hook.rb +12 -0
  70. data/app/models/fae/form_manager.rb +24 -0
  71. data/app/models/fae/option.rb +1 -0
  72. data/app/models/fae/seo_set.rb +14 -0
  73. data/app/models/fae/user.rb +2 -2
  74. data/app/services/fae/netlify_api.rb +213 -0
  75. data/app/uploaders/fae/file_uploader.rb +1 -1
  76. data/app/uploaders/fae/image_uploader.rb +2 -2
  77. data/app/views/devise/unlocks/new.html.slim +5 -9
  78. data/app/views/fae/application/_content_form.html.slim +16 -11
  79. data/app/views/fae/application/_file_uploader.html.slim +7 -2
  80. data/app/views/fae/application/_global_search_results.html.slim +1 -1
  81. data/app/views/fae/application/_header.slim +4 -1
  82. data/app/views/fae/application/_mobilenav.slim +3 -0
  83. data/app/views/fae/application/_seo_set_form.html.slim +12 -0
  84. data/app/views/fae/deploy/index.html.slim +40 -0
  85. data/app/views/fae/deploy_hooks/_form.html.slim +18 -0
  86. data/app/views/fae/deploy_hooks/_table.html.slim +28 -0
  87. data/app/views/fae/deploy_hooks/edit.html.slim +3 -0
  88. data/app/views/fae/deploy_hooks/new.html.slim +3 -0
  89. data/app/views/fae/images/_image_uploader.html.slim +12 -3
  90. data/app/views/fae/options/_form.html.slim +6 -2
  91. data/app/views/fae/pages/activity_log.html.slim +7 -3
  92. data/app/views/fae/pages/home.html.slim +3 -4
  93. data/app/views/fae/shared/_errors.slim +0 -3
  94. data/app/views/fae/shared/_form_header.html.slim +20 -12
  95. data/app/views/fae/shared/_nested_table.html.slim +5 -2
  96. data/app/views/fae/shared/_recent_changes.html.slim +1 -1
  97. data/app/views/fae/shared/_shared_nested_table.html.slim +9 -3
  98. data/app/views/layouts/fae/application.html.slim +2 -1
  99. data/config/deploy.rb +3 -1
  100. data/config/initializers/carrierwave.rb +41 -2
  101. data/config/initializers/devise.rb +6 -6
  102. data/config/initializers/fae_judge.rb +4 -2
  103. data/config/locales/fae.en.yml +49 -4
  104. data/config/locales/fae.zh-CN.yml +2 -2
  105. data/config/puma.rb +82 -0
  106. data/config/routes.rb +9 -1
  107. data/db/migrate/20140809222030_add_user_table.rb +1 -1
  108. data/db/migrate/20190925153222_create_fae_form_managers.rb +11 -0
  109. data/db/migrate/20220118192729_create_fae_publish_hooks.rb +10 -0
  110. data/db/migrate/20220128133730_rename_publish_hooks.rb +5 -0
  111. data/db/migrate/20220202153607_add_position_to_deploy_hooks.rb +6 -0
  112. data/db/migrate/20221118161833_create_fae_seo_sets.rb +13 -0
  113. data/lib/fae/concerns/models/base.rb +2 -0
  114. data/lib/fae/engine.rb +3 -3
  115. data/lib/fae/options.rb +18 -18
  116. data/lib/fae/version.rb +1 -1
  117. data/lib/generators/fae/base_generator.rb +28 -5
  118. data/lib/generators/fae/controller_generator.rb +0 -1
  119. data/lib/generators/fae/install_generator.rb +1 -1
  120. data/lib/generators/fae/model_generator.rb +1 -2
  121. data/lib/generators/fae/nested_index_scaffold_generator.rb +1 -2
  122. data/lib/generators/fae/nested_scaffold_generator.rb +23 -2
  123. data/lib/generators/fae/page_generator.rb +1 -2
  124. data/lib/generators/fae/scaffold_generator.rb +1 -1
  125. data/lib/generators/fae/templates/assets/fae.js +1 -1
  126. data/lib/generators/fae/templates/controllers/nested_scaffold_controller.rb +13 -1
  127. data/lib/generators/fae/templates/controllers/scaffold_controller.rb +7 -0
  128. data/lib/generators/fae/templates/initializers/fae.rb +16 -1
  129. data/lib/generators/fae/templates/views/_form.html.slim +12 -1
  130. data/lib/generators/fae/templates/views/_form_index_nested.html.slim +15 -1
  131. data/lib/generators/fae/templates/views/_form_nested.html.slim +22 -2
  132. data/lib/generators/fae/templates/views/static_page_form.html.slim +13 -1
  133. metadata +53 -24
  134. data/config/deploy/dev.rb +0 -19
  135. data/config/deploy/prod.rb +0 -19
  136. data/config/deploy/stage.rb +0 -19
  137. /data/app/assets/javascripts/fae/vendor/{simplemde.min.js → simplemde/simplemde.min.js} +0 -0
@@ -13,16 +13,20 @@ module Fae
13
13
  Rails.application.routes.url_helpers.fae_path[1..-1]
14
14
  end
15
15
 
16
- def fae_image_form(f, image_name, label: nil, alt_label: nil, caption_label: nil, show_alt: nil, show_caption: nil, required: nil, helper_text: nil, alt_helper_text: nil, caption_helper_text: nil, attached_as: nil)
17
- render 'fae/images/image_uploader', f: f, image_name: image_name, label: label, alt_label: alt_label, caption_label: caption_label, show_alt: show_alt, show_caption: show_caption, required: required, helper_text: helper_text, alt_helper_text: alt_helper_text, caption_helper_text: caption_helper_text, attached_as: attached_as
16
+ def fae_image_form(f, image_name, label: nil, alt_label: nil, caption_label: nil, show_alt: nil, show_caption: nil, required: nil, helper_text: nil, alt_helper_text: nil, caption_helper_text: nil, attached_as: nil, show_form_manager: true)
17
+ render 'fae/images/image_uploader', f: f, image_name: image_name, label: label, alt_label: alt_label, caption_label: caption_label, show_alt: show_alt, show_caption: show_caption, required: required, helper_text: helper_text, alt_helper_text: alt_helper_text, caption_helper_text: caption_helper_text, attached_as: attached_as, show_form_manager: show_form_manager
18
18
  end
19
19
 
20
- def fae_file_form(f, file_name, label: nil, helper_text: nil, required: nil)
21
- render 'fae/application/file_uploader', f: f, file_name: file_name, label: label, required: required, helper_text: helper_text
20
+ def fae_file_form(f, file_name, label: nil, helper_text: nil, required: nil, show_form_manager: true)
21
+ render 'fae/application/file_uploader', f: f, file_name: file_name, label: label, required: required, helper_text: helper_text, show_form_manager: show_form_manager
22
22
  end
23
23
 
24
- def fae_content_form(f, attribute, label: nil, hint: nil, helper_text: nil, markdown: nil, markdown_supported: nil, input_options: nil)
25
- render 'fae/application/content_form', f: f, attribute: attribute, label: label, hint: hint, helper_text: helper_text, markdown: markdown, markdown_supported: markdown_supported, input_options: input_options
24
+ def fae_content_form(f, attribute, label: nil, hint: nil, helper_text: nil, markdown: nil, markdown_supported: nil, input_options: nil, show_form_manager: true)
25
+ render 'fae/application/content_form', f: f, attribute: attribute, label: label, hint: hint, helper_text: helper_text, markdown: markdown, markdown_supported: markdown_supported, input_options: input_options, show_form_manager: show_form_manager
26
+ end
27
+
28
+ def fae_seo_set_form(f, seo_set_name)
29
+ render 'fae/application/seo_set_form', f: f, seo_set_name: seo_set_name
26
30
  end
27
31
 
28
32
  def fae_index_image(image, path = nil)
@@ -40,9 +44,9 @@ module Fae
40
44
 
41
45
  link_to url, class: "slider-wrapper #{link_class}", method: :post, remote: true do
42
46
  '<div class="slider-options">
43
- <div class="slider-option slider-option-yes">Yes</div>
47
+ <div class="slider-option slider-option-yes" aria-live="polite">Yes</div>
44
48
  <div class="slider-option-selector"></div>
45
- <div class="slider-option slider-option-no">No</div>
49
+ <div class="slider-option slider-option-no" aria-live="polite">No</div>
46
50
  </div>'.html_safe
47
51
  end
48
52
  end
@@ -56,7 +60,11 @@ module Fae
56
60
 
57
61
  def fae_delete_button(item, delete_path = nil, *custom_attrs)
58
62
  return if item.blank?
59
- delete_path ||= polymorphic_path([main_app, fae_scope, item.try(:fae_parent), item])
63
+
64
+ delete_path ||= polymorphic_path(
65
+ [main_app, fae_scope.to_sym, item.try(:fae_parent), item]
66
+ )
67
+
60
68
  attrs = { method: :delete, title: 'Delete', class: 'js-tooltip table-action', data: { confirm: t('fae.delete_confirmation') } }
61
69
  attrs.deep_merge!(custom_attrs[0]) if custom_attrs.present?
62
70
  link_to delete_path, attrs do
@@ -115,6 +123,15 @@ module Fae
115
123
  end
116
124
  end
117
125
 
126
+ def fae_filter_datepicker(attribute, options={})
127
+ options[:label] ||= attribute.to_s.titleize
128
+ options[:placeholder] = options[:label] if options[:placeholder].nil?
129
+ content_tag :div, class: 'table-filter-group text-input datepicker' do
130
+ concat label_tag "filter[#{attribute}]", options[:label]
131
+ concat text_field_tag "filter[#{attribute}]", nil, placeholder: options[:placeholder]
132
+ end
133
+ end
134
+
118
135
  def fae_avatar(user = current_user)
119
136
  hash = Digest::MD5.hexdigest(user.email.downcase)
120
137
  "https://secure.gravatar.com/avatar/#{hash}?s=80&d=mm"
@@ -27,6 +27,15 @@ module Fae
27
27
  "#{fae_nested_parent}_id"
28
28
  end
29
29
 
30
+ def fae_form_manager_model_name
31
+ return 'Fae::StaticPage' if self.class.name.constantize.superclass.name == 'Fae::StaticPage'
32
+ self.class.name
33
+ end
34
+
35
+ def fae_form_manager_model_id
36
+ self.id
37
+ end
38
+
30
39
  module ClassMethods
31
40
  def for_fae_index
32
41
  order(order_method)
@@ -91,6 +100,14 @@ module Fae
91
100
  accepts_nested_attributes_for file_name_symbol, allow_destroy: true
92
101
  end
93
102
 
103
+ def has_fae_seo_set(set_name_symbol)
104
+ has_one set_name_symbol,
105
+ as: :seo_setable,
106
+ class_name: '::Fae::SeoSet',
107
+ dependent: :destroy
108
+ accepts_nested_attributes_for set_name_symbol, allow_destroy: true
109
+ end
110
+
94
111
  end
95
112
 
96
113
  private
@@ -0,0 +1 @@
1
+ module Fae::SeoSetConcern; end
@@ -9,6 +9,7 @@ module Fae
9
9
  serialize :updated_attributes
10
10
 
11
11
  class << self
12
+
12
13
  # writing current_user to thread for thread safety
13
14
  def current_user=(user)
14
15
  Thread.current[:current_user] = user
@@ -28,12 +29,25 @@ module Fae
28
29
  conditions[:user_id] = params['user'] if params['user'].present?
29
30
  conditions[:changeable_type] = params['model'] if params['model'].present?
30
31
  conditions[:change_type] = params['type'] if params['type'].present?
32
+ params['date'] ||= ''
31
33
 
32
- date_scope = case params['date']
33
- when 'Last Hour' then ['fae_changes.updated_at >= ?', 60.minutes.ago]
34
- when 'Last Day' then ['fae_changes.updated_at >= ?', 1.day.ago]
35
- when 'Last Week' then ['fae_changes.updated_at >= ?', 1.week.ago]
36
- when 'Last Month' then ['fae_changes.updated_at >= ?', 1.month.ago]
34
+ date_scope = []
35
+ if params['start_date'].present? || params['end_date'].present?
36
+ start_date = params['start_date'].present? ? CGI.unescape(params['start_date']).to_date : nil
37
+ end_date = params['end_date'].present? ? CGI.unescape(params['end_date']).to_date : nil
38
+ date_scope = ['fae_changes.updated_at >= ?', start_date] if start_date.present?
39
+ date_scope = ['fae_changes.updated_at <= ?', end_date] if end_date.present?
40
+ date_scope = ['fae_changes.updated_at >= ? AND fae_changes.updated_at <= ?', start_date, end_date] if start_date.present? && end_date.present?
41
+ else
42
+ # normalize url param strings because "Last Hour" != "Last%20Hour"
43
+ date_scope = case params['date'].downcase.gsub('%20', '-')
44
+ when 'last-hour' then ['fae_changes.updated_at >= ?', 60.minutes.ago]
45
+ when 'last-day' then ['fae_changes.updated_at >= ?', 1.day.ago]
46
+ when 'last-week' then ['fae_changes.updated_at >= ?', 1.week.ago]
47
+ when 'last-month' then ['fae_changes.updated_at >= ?', 1.month.ago]
48
+ else
49
+ []
50
+ end
37
51
  end
38
52
 
39
53
  # use good 'ol MySQL to search if search param is present
@@ -45,7 +59,7 @@ module Fae
45
59
  # apply conditions and search from above to our scope
46
60
  order(id: :desc)
47
61
  .includes(:user).references(:user)
48
- .where(Arel.sql(date_scope)).where(conditions).where(Arel.sql(search))
62
+ .where(date_scope).where(conditions).where(search)
49
63
  end
50
64
 
51
65
  end
@@ -0,0 +1,12 @@
1
+ module Fae
2
+ class DeployHook < ApplicationRecord
3
+ include Fae::BaseModelConcern
4
+
5
+ acts_as_list add_new_at: :top
6
+ default_scope { order(:position) }
7
+
8
+ default_scope { order(:environment) }
9
+
10
+ validates :url, :environment, presence: true
11
+ end
12
+ end
@@ -0,0 +1,24 @@
1
+ module Fae
2
+ class FormManager < ActiveRecord::Base
3
+
4
+ include Fae::BaseModelConcern
5
+
6
+ def fae_display_field
7
+ model_name
8
+ end
9
+
10
+ class << self
11
+
12
+ def for_model(params, item)
13
+ if item.present? && item.class.superclass.name == 'Fae::StaticPage'
14
+ conditions = {form_manager_model_name: 'Fae::StaticPage', form_manager_model_id: item.fae_form_manager_model_id}
15
+ else
16
+ conditions = {form_manager_model_name: params[:controller].gsub('admin/','').classify}
17
+ end
18
+ where(conditions).first
19
+ end
20
+
21
+ end
22
+
23
+ end
24
+ end
@@ -6,6 +6,7 @@ module Fae
6
6
 
7
7
  validates_inclusion_of :singleton_guard, :in => [0]
8
8
  validates_presence_of :title, :time_zone, :live_url
9
+ validates :colorway, format: { with: /(^[0-9a-fA-F]{6}$)|(^$)/, message: "Invalid hex code or doesn't meet length requirement of exactly 6 characters" }
9
10
 
10
11
  has_one :logo, -> { where(attached_as: 'logo' ) },
11
12
  as: :imageable,
@@ -0,0 +1,14 @@
1
+ module Fae
2
+ class SeoSet < ActiveRecord::Base
3
+ include Fae::BaseModelConcern
4
+ include Fae::SeoSetConcern
5
+
6
+ has_fae_image :social_media_image
7
+ belongs_to :seo_setable, polymorphic: true, touch: true, optional: true
8
+
9
+ def fae_display_field
10
+ seo_title
11
+ end
12
+
13
+ end
14
+ end
@@ -5,9 +5,9 @@ module Fae
5
5
  include Fae::UserConcern
6
6
 
7
7
  # Include default devise modules. Others available are:
8
- # :registerable, :confirmable, :lockable, :timeoutable and :omniauthable
8
+ # :registerable, :confirmable, :timeoutable and :omniauthable
9
9
  devise :database_authenticatable,
10
- :recoverable, :rememberable, :trackable
10
+ :recoverable, :rememberable, :trackable, :lockable
11
11
 
12
12
  belongs_to :role
13
13
 
@@ -0,0 +1,213 @@
1
+ require 'net/http'
2
+
3
+ module Fae
4
+ class NetlifyApi
5
+
6
+ def initialize()
7
+ @netlify_api_user = Fae.netlify[:api_user]
8
+ @netlify_api_token = Fae.netlify[:api_token]
9
+ @site = Fae.netlify[:site]
10
+ @site_id = Fae.netlify[:site_id]
11
+ @endpoint_base = Fae.netlify[:api_base]
12
+ @logger = Logger.new(Rails.root.join('log', 'netlify_api.log'))
13
+ end
14
+
15
+ def get_deploys
16
+ path = "sites/#{@site_id}/deploys?per_page=15"
17
+ get_deploys_env_response(path)
18
+ end
19
+
20
+ def run_deploy(deploy_hook_type, current_user)
21
+ hook = Fae::DeployHook.find_by_environment(deploy_hook_type)
22
+ if hook.present?
23
+ post("#{hook.url}?trigger_title=#{current_user.full_name.gsub(' ', '+')}+triggered+a+#{deploy_hook_type.titleize}+deploy")
24
+ return true
25
+ end
26
+ false
27
+ end
28
+
29
+ private
30
+
31
+ def get(endpoint)
32
+ begin
33
+ uri = URI.parse(endpoint)
34
+ request = Net::HTTP::Get.new(uri)
35
+ set_headers(request)
36
+ response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) {|http|
37
+ http.request(request)
38
+ }
39
+ if response.is_a?(Net::HTTPSuccess)
40
+ return JSON.parse(response.body) if response.body.present?
41
+ else
42
+ @logger.info "\n"
43
+ @logger.info "Get returned non-success code: #{response.code}"
44
+ @logger.info "Endpoint: #{endpoint}"
45
+ @logger.info "Body: #{response.body}" if response.body.present?
46
+ end
47
+ rescue Exception => e
48
+ @logger.info "\n"
49
+ @logger.info "Get failed"
50
+ @logger.info "Endpoint: #{endpoint}"
51
+ @logger.info "Reason: #{e}"
52
+ end
53
+ end
54
+
55
+ def post(endpoint, params = nil)
56
+ begin
57
+ uri = URI.parse(endpoint)
58
+ request = Net::HTTP::Post.new(uri)
59
+ set_headers(request)
60
+ request.body = params.to_json
61
+ response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) {|http|
62
+ http.request(request)
63
+ }
64
+ if response.is_a?(Net::HTTPSuccess)
65
+ return JSON.parse(response.body) if response.body.present?
66
+ else
67
+ @logger.info "\n"
68
+ @logger.info "Post returned non-success code: #{response.code}"
69
+ @logger.info "Endpoint: #{endpoint}"
70
+ @logger.info "Params: #{params}"
71
+ @logger.info "Body: #{response.body}" if response.body.present?
72
+ end
73
+ rescue Exception => e
74
+ @logger.info "\n"
75
+ @logger.info "Post failed"
76
+ @logger.info "Endpoint: #{endpoint}"
77
+ @logger.info "Params: #{params}"
78
+ @logger.info "Reason: #{e}"
79
+ end
80
+ end
81
+
82
+ def set_headers(request)
83
+ request['User-Agent'] = "#{@site} (#{@netlify_api_user})"
84
+ request['Authorization'] = "Bearer #{@netlify_api_token}"
85
+ end
86
+
87
+ def get_deploys_env_response(path)
88
+ return get "#{@endpoint_base}#{path}" unless Rails.env.test?
89
+ [
90
+ {
91
+ "state"=>"building",
92
+ "name"=>"building-test",
93
+ "created_at"=>"2021-10-22T14:56:18.163Z",
94
+ "updated_at"=>"2021-10-22T14:57:55.905Z",
95
+ "error_message"=>nil,
96
+ "commit_ref"=>nil,
97
+ "branch"=>"staging",
98
+ "title"=>"Staging building",
99
+ "review_url"=>nil,
100
+ "published_at"=>nil,
101
+ "context"=>"branch-deploy",
102
+ "deploy_time"=>93,
103
+ "committer"=>nil,
104
+ "skipped_log"=>nil,
105
+ "manual_deploy"=>false,
106
+ },
107
+ {
108
+ "state"=>"processing",
109
+ "name"=>"processing-test",
110
+ "created_at"=>"2021-10-22T14:56:18.163Z",
111
+ "updated_at"=>"2021-10-22T14:57:55.905Z",
112
+ "error_message"=>nil,
113
+ "commit_ref"=>nil,
114
+ "branch"=>"staging",
115
+ "title"=>"Staging processing",
116
+ "review_url"=>nil,
117
+ "published_at"=>nil,
118
+ "context"=>"branch-deploy",
119
+ "deploy_time"=>93,
120
+ "committer"=>nil,
121
+ "skipped_log"=>nil,
122
+ "manual_deploy"=>false,
123
+ },
124
+ {
125
+ "state"=>"ready",
126
+ "name"=>"complete-test",
127
+ "created_at"=>"2021-10-22T14:56:18.163Z",
128
+ "updated_at"=>"2021-10-22T14:57:55.905Z",
129
+ "error_message"=>nil,
130
+ "commit_ref"=>'string',
131
+ "branch"=>"staging",
132
+ "title"=>"Staging complete",
133
+ "review_url"=>nil,
134
+ "published_at"=>nil,
135
+ "context"=>"branch-deploy",
136
+ "deploy_time"=>93,
137
+ "committer"=>nil,
138
+ "skipped_log"=>nil,
139
+ "manual_deploy"=>false,
140
+ },
141
+ {
142
+ "state"=>"ready",
143
+ "name"=>"admin-test",
144
+ "created_at"=>"2021-10-22T14:56:18.163Z",
145
+ "updated_at"=>"2021-10-22T14:57:55.905Z",
146
+ "error_message"=>nil,
147
+ "commit_ref"=>nil,
148
+ "branch"=>"staging",
149
+ "title"=>"Staging admin complete",
150
+ "review_url"=>nil,
151
+ "published_at"=>nil,
152
+ "context"=>"branch-deploy",
153
+ "deploy_time"=>93,
154
+ "committer"=>nil,
155
+ "skipped_log"=>nil,
156
+ "manual_deploy"=>false,
157
+ },
158
+ {
159
+ "state"=>"error",
160
+ "name"=>"error-test",
161
+ "created_at"=>"2021-10-22T14:56:18.163Z",
162
+ "updated_at"=>"2021-10-22T14:57:55.905Z",
163
+ "error_message"=>'Error!',
164
+ "commit_ref"=>nil,
165
+ "branch"=>"staging",
166
+ "title"=>"FINE admin triggered a Staging build",
167
+ "review_url"=>nil,
168
+ "published_at"=>nil,
169
+ "context"=>"branch-deploy",
170
+ "deploy_time"=>93,
171
+ "committer"=>nil,
172
+ "skipped_log"=>nil,
173
+ "manual_deploy"=>false,
174
+ },
175
+ {
176
+ "state"=>"ready",
177
+ "name"=>"fae-dummy",
178
+ "created_at"=>"2021-10-22T14:56:18.163Z",
179
+ "updated_at"=>"2021-10-22T14:57:55.905Z",
180
+ "error_message"=>nil,
181
+ "commit_ref"=>nil,
182
+ "branch"=>"master",
183
+ "title"=>"A production build",
184
+ "review_url"=>nil,
185
+ "published_at"=>nil,
186
+ "context"=>"production",
187
+ "deploy_time"=>93,
188
+ "committer"=>nil,
189
+ "skipped_log"=>nil,
190
+ "manual_deploy"=>false,
191
+ },
192
+ {
193
+ "state"=>"ready",
194
+ "name"=>"fae-dummy",
195
+ "created_at"=>"2021-10-22T14:56:18.163Z",
196
+ "updated_at"=>"2021-10-22T14:57:55.905Z",
197
+ "error_message"=>nil,
198
+ "commit_ref"=>nil,
199
+ "branch"=>"master",
200
+ "title"=>"Another production build",
201
+ "review_url"=>nil,
202
+ "published_at"=>nil,
203
+ "context"=>"production",
204
+ "deploy_time"=>93,
205
+ "committer"=>nil,
206
+ "skipped_log"=>nil,
207
+ "manual_deploy"=>false,
208
+ },
209
+ ]
210
+ end
211
+
212
+ end
213
+ end
@@ -18,7 +18,7 @@ module Fae
18
18
 
19
19
  # Add a white list of extensions which are allowed to be uploaded.
20
20
  # For images you might use something like this:
21
- def extension_whitelist
21
+ def extension_allowlist
22
22
  %w(jpg jpeg gif png pdf)
23
23
  end
24
24
 
@@ -10,8 +10,8 @@ module Fae
10
10
  model.file_size = file.size
11
11
  end
12
12
 
13
- def extension_whitelist
14
- %w(jpg jpeg gif png ico)
13
+ def extension_allowlist
14
+ %w(jpg jpeg gif png ico svg)
15
15
  end
16
16
 
17
17
  # Override the directory where uploaded files will be stored.
@@ -1,13 +1,9 @@
1
- h2 Resend unlock instructions
1
+ .login-form
2
+ h2 Resend unlock instructions
2
3
 
3
- = simple_form_for(resource, :as => resource_name, :url => unlock_path(resource_name), :html => { :method => :post }) do |f|
4
- = f.error_notification
5
- = f.full_error :unlock_token
4
+ = simple_form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post, id: 'login_form' }) do |f|
6
5
 
7
- .form-inputs
8
6
  = f.input :email, :required => true, :autofocus => true
9
7
 
10
- .form-actions
11
- = f.button :submit, "Resend unlock instructions"
12
-
13
- == render "devise/shared/links"
8
+ .login-form-actions
9
+ = f.button :submit, "Resend unlock instructions", class: "login-form-actions-submit"
@@ -1,20 +1,21 @@
1
1
  ruby:
2
- require_locals ['attribute', 'f'], local_assigns
3
- has_label = ![false, ''].include?(local_assigns[:label])
4
- label ||= attribute.to_s.titleize
5
- required ||= f.object.send(attribute).is_required?(f.object.class)
6
- helper_text ||= attempt_common_helper_text(attribute)
7
- markdown ||= false
8
- markdown_supported ||= false
9
- hint ||= nil
10
- options ||= {}
11
- input_options ||= {}
2
+ require_locals ['attribute', 'f'], local_assigns
3
+ has_label = ![false, ''].include?(local_assigns[:label])
4
+ label ||= attribute.to_s.titleize
5
+ required ||= f.object.send(attribute).is_required?(f.object.class)
6
+ helper_text ||= attempt_common_helper_text(attribute)
7
+ markdown ||= false
8
+ markdown_supported ||= false
9
+ hint ||= nil
10
+ options ||= {}
11
+ input_options ||= {}
12
+ show_form_manager ||= true
12
13
 
13
14
  if has_label
14
15
  label_adjusted = required ? '<abbr title="required">*</abbr> ' : ''
15
16
  label_adjusted += label
16
17
  if markdown_supported.present? || helper_text.present?
17
- label_adjusted += content_tag :h6, class: 'helper_text' do
18
+ label_adjusted += content_tag :h6, class: 'helper_text helper_text_text' do
18
19
  concat(helper_text) if helper_text.present?
19
20
  concat(content_tag(:span, 'Markdown Supported', class: 'markdown-support')) if markdown_supported.present?
20
21
  end
@@ -28,6 +29,8 @@ ruby:
28
29
  options.merge! input_options if (input_options.keys).any?
29
30
  options.merge! wrapper_html: {} if input_options[:wrapper_html].blank?
30
31
 
32
+ options.merge! wrapper_html: { data: { form_manager_id: "#{f.object.class.name}_#{attribute}" } } if show_form_manager
33
+
31
34
  if options[:input_html].present? && options[:input_html][:class].present? && markdown.present?
32
35
  options[:input_html][:class] = options[:input_html][:class] + ' js-markdown-editor'
33
36
  elsif options[:input_html].blank?
@@ -45,6 +48,8 @@ ruby:
45
48
  - options.deep_merge! wrapper_html: { data: { language: lang } }
46
49
  - options[:label] = orig_label.gsub(label, "#{label} (#{lang.to_s})").html_safe
47
50
 
51
+ - options.deep_merge! wrapper_html: { data: { form_manager_id: "#{f.object.class.name}_#{attribute}_#{lang}" } } if show_form_manager
52
+
48
53
  = f.simple_fields_for "#{attribute}_#{lang}".to_sym, defaults: { wrapper_html: options[:wrapper_html] } do |i|
49
54
  - options[:input_html].merge! data: { validate: i.object.validation_json(f.object.class) }
50
55
  = i.input :content, options
@@ -8,21 +8,26 @@ ruby:
8
8
  required ||= false
9
9
  languages = f.object.class.try(:fae_fields).try(:[], file_name).try(:[], :languages)
10
10
  language ||= nil
11
+ show_form_manager ||= true
11
12
 
12
13
  - if languages.present?
13
14
  - languages.each do |lang|
14
15
  = render 'fae/application/file_uploader', f: f, item: item, file_name: "#{file_name}_#{lang}".to_sym, label: "#{label} (#{lang.to_s})", helper_text: helper_text, required: required, language: lang
15
16
 
16
17
  - else
18
+ ruby:
19
+ form_manager_id = show_form_manager ? "#{item.class.name}_#{file_name}" : nil
20
+ form_manager_id += "_#{language}" if language.present?
17
21
  - wrapper_html = language.present? ? { data: { language: language } } : {}
18
22
  = f.simple_fields_for file_name, defaults: { wrapper_html: wrapper_html } do |i|
19
- .input.field
23
+ .input.field class="#{file_name}" data-form-manager-id=form_manager_id
20
24
  = content_tag :label, class: ('required' if required) do
21
25
  - if required
22
26
  abbr title="required" *
23
27
  = label.html_safe
24
28
  - if helper_text.present?
25
- h6.helper_text = helper_text
29
+ h6.helper_text
30
+ span.helper_text_text = helper_text
26
31
 
27
32
  - if the_file.asset.present?
28
33
  .asset-actions.-files
@@ -25,7 +25,7 @@ ul.search-results.js-search-results
25
25
  - records.each do |item|
26
26
  - begin
27
27
  - parent = item.respond_to?(:fae_parent) ? item.fae_parent : nil
28
- - edit_path = edit_polymorphic_path([main_app, fae_scope, parent, item])
28
+ - edit_path = edit_polymorphic_path([main_app, fae_scope.to_sym, parent, item])
29
29
  li
30
30
  a href=edit_path
31
31
  span.search-record-label = item.class.name
@@ -9,12 +9,15 @@ header.main-header#js-main-header
9
9
  li class=nav_active_class(item[:class_name], 0, idx)
10
10
  a href=item[:nested_path] = item[:text]
11
11
  - if item[:subitems].present?
12
- ul
12
+ ul class=multi_column_nav_ul_class(item[:subitems])
13
13
  - item[:subitems].each_with_index do |subitem, sub_idx|
14
14
  li: a href=subitem[:nested_path] class=nav_active_class(subitem[:class_name], 1, sub_idx, idx) = subitem[:text]
15
15
 
16
16
  - if user_signed_in?
17
17
  ul.utility-nav#js-utility-nav
18
+ - if current_user.super_admin_or_admin? && netlify_enabled?
19
+ li class=deployments_active_class
20
+ a.deployments-nav-link href=fae.deploy_path = t('fae.navbar.deployments')
18
21
  li.utility-dropdown.js-utility-dropdown
19
22
  a.grabatar-link href="#"
20
23
  img.grabatar src=fae_avatar alt=current_user.full_name
@@ -31,6 +31,9 @@ nav.nav.mobilenav
31
31
  - thirditem[:path] ||= '#'
32
32
  li: a href=thirditem[:path] = thirditem[:text]
33
33
 
34
+ - if current_user.super_admin_or_admin? && netlify_enabled?
35
+ li: a href=fae.deploy_path = t('fae.navbar.deployments')
36
+
34
37
  - if current_user.super_admin_or_admin?
35
38
  li.mobilenav-accordion.js-accordion
36
39
  a href="#" Admin
@@ -0,0 +1,12 @@
1
+ ruby:
2
+ require_locals ['f'], local_assigns
3
+ item ||= f.object
4
+ seo_set_name ||= :seo_set
5
+ the_seo_set = item.send(seo_set_name)
6
+
7
+ = f.simple_fields_for seo_set_name do |i|
8
+ = fae_input i, :seo_title, label: 'SEO Title'
9
+ = fae_input i, :seo_description, label: 'SEO Description'
10
+ = fae_input i, :social_media_title
11
+ = fae_input i, :social_media_description
12
+ = fae_image_form i, :social_media_image, helper_text: '1200 x 630 px., JPG'
@@ -0,0 +1,40 @@
1
+ header.content-header.js-content-header
2
+ h1 = t('fae.deploy.page.heading')
3
+
4
+ == render 'flash_messages'
5
+
6
+ main.content
7
+ - if @deploy_hooks.present?
8
+ - @deploy_hooks.each do |deploy_hook|
9
+ button.button.deploy-button.js-run-deploy data-build-hook-type=deploy_hook.environment href="#" = "#{t('fae.deploy.ctas.deploy')} #{deploy_hook.environment.to_s.titleize}"
10
+
11
+ section.content
12
+ p = t('fae.deploy.table.deploying_helper').html_safe
13
+
14
+ section.content#js-deploying-section
15
+ h2.deploying-heading = t('fae.deploy.table.deploying_heading')
16
+ .js-deploys-list.running
17
+ table
18
+ thead
19
+ tr
20
+ th = t('fae.deploy.table.title')
21
+ th = t('fae.deploy.table.deployed')
22
+ th = t('fae.deploy.table.duration')
23
+ th = t('fae.deploy.table.context')
24
+ th = t('fae.deploy.table.error_msg')
25
+ tbody
26
+
27
+ section.content
28
+ h2 = t('fae.deploy.table.past_heading')
29
+ .js-deploys-list.past
30
+ table
31
+ thead
32
+ tr
33
+ th = t('fae.deploy.table.title')
34
+ th = t('fae.deploy.table.deployed')
35
+ th = t('fae.deploy.table.duration')
36
+ th = t('fae.deploy.table.context')
37
+ th = t('fae.deploy.table.error_msg')
38
+ tbody
39
+
40
+