fae-rails 2.0.0 → 2.1.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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -1
  3. data/app/assets/javascripts/fae/form/_ajax.js +21 -1
  4. data/app/assets/javascripts/fae/form/_cancel.js +1 -0
  5. data/app/assets/javascripts/fae/form/_validator.js +22 -12
  6. data/app/assets/javascripts/fae/form/inputs/_color.js +2 -1
  7. data/app/assets/javascripts/fae/vendor/jqColorPicker.min.js +0 -1
  8. data/app/controllers/fae/application_controller.rb +15 -1
  9. data/app/controllers/fae/users_controller.rb +8 -0
  10. data/app/helpers/fae/form_helper.rb +13 -4
  11. data/app/helpers/fae/view_helper.rb +76 -31
  12. data/app/models/concerns/fae/user_concern.rb +10 -1
  13. data/app/models/fae/static_page.rb +7 -2
  14. data/app/models/fae/user.rb +2 -3
  15. data/app/uploaders/fae/file_uploader.rb +1 -1
  16. data/app/uploaders/fae/image_uploader.rb +1 -1
  17. data/app/views/fae/application/_file_uploader.html.slim +4 -1
  18. data/app/views/fae/application/_global_search_results.html.slim +1 -1
  19. data/app/views/fae/application/_header.slim +8 -8
  20. data/app/views/fae/application/_markdown_helper.slim +17 -17
  21. data/app/views/fae/application/_mobilenav.slim +6 -6
  22. data/app/views/fae/application/_user_log.html.slim +2 -2
  23. data/app/views/fae/images/_image_uploader.html.slim +1 -1
  24. data/app/views/fae/options/_form.html.slim +6 -6
  25. data/app/views/fae/pages/activity_log.html.slim +11 -11
  26. data/app/views/fae/pages/disabled_environment.html.slim +2 -2
  27. data/app/views/fae/pages/error404.html.slim +5 -3
  28. data/app/views/fae/pages/home.html.slim +9 -8
  29. data/app/views/fae/setup/first_user.html.slim +3 -3
  30. data/app/views/fae/shared/_form_header.html.slim +3 -3
  31. data/app/views/fae/shared/_index_header.html.slim +3 -2
  32. data/app/views/fae/shared/_nested_table.html.slim +1 -1
  33. data/app/views/fae/shared/_recent_changes.html.slim +6 -6
  34. data/app/views/fae/shared/_shared_nested_table.html.slim +1 -1
  35. data/app/views/fae/static_pages/index.html.slim +2 -2
  36. data/app/views/fae/users/_form.html.slim +8 -12
  37. data/app/views/fae/users/index.html.slim +6 -6
  38. data/config/locales/devise.cs.yml +59 -0
  39. data/config/locales/fae.cs.yml +125 -0
  40. data/config/locales/fae.en.yml +109 -0
  41. data/config/locales/fae.zh-CN.yml +109 -0
  42. data/lib/fae/version.rb +1 -1
  43. data/lib/generators/fae/base_generator.rb +46 -3
  44. data/lib/generators/fae/nested_index_scaffold_generator.rb +1 -0
  45. data/lib/generators/fae/nested_scaffold_generator.rb +1 -0
  46. data/lib/generators/fae/page_generator.rb +8 -0
  47. data/lib/generators/fae/scaffold_generator.rb +1 -0
  48. data/lib/generators/fae/templates/graphql/graphql_page_type.rb +17 -0
  49. data/lib/generators/fae/templates/graphql/graphql_type.rb +13 -0
  50. metadata +9 -5
@@ -1 +1,10 @@
1
- module Fae::UserConcern; end
1
+ module Fae
2
+ module UserConcern
3
+ extend ActiveSupport::Concern
4
+ module ClassMethods
5
+ def available_languages
6
+ [:en]
7
+ end
8
+ end
9
+ end
10
+ end
@@ -42,7 +42,7 @@ module Fae
42
42
  languages.each do |lang|
43
43
  # Save with suffix for form fields
44
44
  define_association("#{name}_#{lang}", type)
45
- define_validations("#{name}_#{lang}", type, value[:validates]) if value.try(:[], :validates).present?
45
+ define_validations("#{name}_#{lang}", type, value[:validates]) if supports_validation(type, value)
46
46
  end
47
47
  # Save with lookup to have default language return in front-end use (don't need to worry about validations here)
48
48
  default_language = Rails.application.config.i18n.default_locale || languages.first
@@ -50,7 +50,7 @@ module Fae
50
50
  else
51
51
  # Normal content_blocks
52
52
  define_association(name, type)
53
- define_validations(name, type, value[:validates]) if value.try(:[], :validates).present?
53
+ define_validations(name, type, value[:validates]) if supports_validation(type, value)
54
54
  end
55
55
  end
56
56
 
@@ -75,6 +75,11 @@ module Fae
75
75
  end
76
76
  end
77
77
 
78
+ def self.supports_validation(type, value)
79
+ # validations are only supported on Fae::TextField and Fae::TextArea
80
+ poly_sym(type) == :contentable && value.try(:[], :validates).present?
81
+ end
82
+
78
83
  def self.poly_sym(assoc)
79
84
  case assoc.name
80
85
  when 'Fae::TextField', 'Fae::TextArea'
@@ -16,9 +16,8 @@ module Fae
16
16
  presence: true,
17
17
  uniqueness: { message: 'That email address is already in use. Give another one a go.' },
18
18
  format: {
19
- with: /^\s*(([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})[\s\/,;]*)+$/i,
20
- message: 'is invalid',
21
- multiline: true
19
+ with: Fae.validation_helpers.email_regex,
20
+ message: 'is invalid'
22
21
  }
23
22
  validates :password,
24
23
  presence: { on: :create },
@@ -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_white_list
21
+ def extension_whitelist
22
22
  %w(jpg jpeg gif png pdf)
23
23
  end
24
24
 
@@ -10,7 +10,7 @@ module Fae
10
10
  model.file_size = file.size
11
11
  end
12
12
 
13
- def extension_white_list
13
+ def extension_whitelist
14
14
  %w(jpg jpeg gif png ico)
15
15
  end
16
16
 
@@ -28,7 +28,10 @@ ruby:
28
28
  .asset-actions.-files
29
29
  a.asset-title href=the_file.asset.url target="_blank" = the_file.asset.file.filename
30
30
 
31
- = link_to '', fae.delete_file_path(the_file.id), class: 'asset-delete js-asset-delete', remote: true, method: :delete, data: { confirm: t('fae.delete_confirmation') }
31
+ - if the_file.id.present?
32
+ = link_to '', fae.delete_file_path(the_file.id), class: 'asset-delete js-asset-delete', remote: true, method: :delete, data: { confirm: t('fae.delete_confirmation') }
33
+ - else
34
+ a.js-file-clear.asset-delete.js-asset-delete
32
35
 
33
36
  .asset-inputs style="#{'display: none;' unless the_file.asset.blank?}"
34
37
  = i.input :asset, as: :file, label: false, input_html: { data: { limit: Fae.max_file_upload_size, exceeded: t('fae.exceeded_upload_limit') } }
@@ -34,4 +34,4 @@ ul.search-results.js-search-results
34
34
 
35
35
  - else
36
36
 
37
- li No results
37
+ li = t('fae.navbar.no_results')
@@ -19,22 +19,22 @@ header.main-header#js-main-header
19
19
  a.grabatar-link href="#"
20
20
  img.grabatar src=fae_avatar alt=current_user.full_name
21
21
  ul
22
- li: a href=fae.destroy_user_session_path Log Out
23
- li: a href=fae.settings_path Your Settings
22
+ li: a href=fae.destroy_user_session_path = t('fae.navbar.logout')
23
+ li: a href=fae.settings_path = t('fae.navbar.your_settings')
24
24
 
25
25
  li.utility-dropdown.js-utility-dropdown
26
26
  a href="#"
27
27
  i.icon-settings
28
28
  ul
29
29
  - if current_user.super_admin_or_admin?
30
- li: a href=fae.users_path Users
31
- li: a href=fae.activity_log_path Activity Log
30
+ li: a href=fae.users_path = t('fae.navbar.users')
31
+ li: a href=fae.activity_log_path = t('fae.navbar.activity_log')
32
32
  - if current_user.super_admin?
33
- li: a href=fae.option_path Root Settings
33
+ li: a href=fae.option_path = t('fae.navbar.root_settings')
34
34
  - if @option.live_url.present?
35
- li: a href=@option.live_url target="_blank" Live Site
35
+ li: a href=@option.live_url target="_blank" = t('fae.application.live_site')
36
36
  - if @option.stage_url.present?
37
- li: a href=@option.stage_url target="_blank" Stage Site
37
+ li: a href=@option.stage_url target="_blank" = t('fae.application.stage_site')
38
38
 
39
39
  li
40
40
  a href=fae.help_path
@@ -45,6 +45,6 @@ header.main-header#js-main-header
45
45
 
46
46
  .utility-search-hit-area#js-utility-search-wrapper
47
47
  .utility-search-wrapper
48
- input#js-global-search type="text" placeholder="Jump to..."
48
+ input#js-global-search type="text" placeholder=t('fae.navbar.jump_to')
49
49
 
50
50
  == render 'global_search_results', show_nav: true
@@ -1,41 +1,41 @@
1
1
  .markdown-hint
2
2
  .markdown-hint-wrapper
3
- h3 Markdown Options
3
+ h3 = t('fae.application.markdown_opts')
4
4
 
5
- h4 Links
5
+ h4 = t('fae.application.links')
6
6
  p
7
- | To link text, place a [bracket] around the link title and use a (/url) to house the url.
7
+ = t('fae.application.links_help')
8
8
  br
9
9
  | Internal Link: [link to about](/about)
10
10
  br
11
11
  | External link: [link to about](http://www.google.com/about)
12
12
 
13
- h4 Formatting
13
+ h4 = t('fae.application.formatting')
14
14
  p
15
- | Emphasize text in a variety of ways by placing **asterisks** to bold, _underscores_ to italicize.
15
+ = t('fae.application.formatting_help')
16
16
  br
17
- | Bold **bold**
17
+ = t('fae.application.bold')
18
18
  br
19
- | Italicize _italic
19
+ = t('fae.application.italic')
20
20
 
21
- h4 Headers
21
+ h4 = t('fae.application.headers')
22
22
  p
23
- | Use up to six hashtags to identify the importance of the section header.
23
+ = t('fae.application.headers_help')
24
24
  br
25
- | Page Header: # Page Header
25
+ = t('fae.apllication.headers_example')
26
26
  br
27
- | Sub Header: ## Sub Header
27
+ = t('fae.application.subheaders_example')
28
28
 
29
- h4 List
29
+ h4 = t('fae.application.list')
30
30
  p
31
- | Format lists by swapping out the characters that lead the list item.
31
+ = t('fae.application.list_help')
32
32
  br
33
- span Bulleted List:
33
+ span = t('fae.application.bulleted_list')
34
34
  br
35
35
  == ['* bullet', '* bullet 2'].join('<br />')
36
36
  br
37
- span Numbered List
37
+ span = t('fae.application.numbered_list')
38
38
  == ['1. line item', '2. line item'].join('<br />')
39
39
 
40
- h4 Paragraph break
41
- p Adding a blank line in between your paragraphs makes a paragraph break.
40
+ h4 = t('fae.application.paragraph_break')
41
+ p = t('fae.application.paragraph_break_help')
@@ -55,12 +55,12 @@ nav.nav.mobilenav
55
55
  - if @option.stage_url.present? || @option.live_url.present?
56
56
  li class=view_site_class
57
57
  - if site_urls_are_present
58
- a href="#" View Site
58
+ a href="#" = t('fae.application.view_site')
59
59
  ul.subnav.js-subnav
60
- li: span.mobilenav-subheader.js-mobile-back View Site
61
- li: a.-viewsite href=@option.stage_url target="_blank" Preview Site
62
- li: a.-viewsite href=@option.live_url target="_blank" Live Site
60
+ li: span.mobilenav-subheader.js-mobile-back = t('fae.application.view_site')
61
+ li: a.-viewsite href=@option.stage_url target="_blank" = t('fae.application.preview_site')
62
+ li: a.-viewsite href=@option.live_url target="_blank" = t('fae.application.live_site')
63
63
  - elsif @option.stage_url.present?
64
- a.-viewsite href=@option.stage_url target="_blank" Preview Site
64
+ a.-viewsite href=@option.stage_url target="_blank" = t('fae.application.preview_site')
65
65
  - elsif @option.live_url.present?
66
- a.-viewsite href=@option.live_url target="_blank" Live Site
66
+ a.-viewsite href=@option.live_url target="_blank" = t('fae.application.live_site')
@@ -28,9 +28,9 @@
28
28
  - if changed.present?
29
29
  tr
30
30
  td = version.created_at
31
- td = version.user.present? ? version.user.full_name : content_tag(:em, "user no longer exists")
31
+ td = version.user.present? ? version.user.full_name : content_tag(:em, t('fae.application.user_absent'))
32
32
  td = changed.join(', ')
33
33
 
34
34
  - else
35
35
  tr
36
- td colspan="3" No logs available.
36
+ td colspan="3" = t('fae.application.no_logs')
@@ -7,7 +7,7 @@ ruby:
7
7
  alt_label ||= "#{label} Alt Text"
8
8
  caption_label ||= "#{label} Caption"
9
9
  helper_text ||= ""
10
- alt_helper_text ||= 'Brief description of image contents, used in hover text and by visually impaired users.'
10
+ alt_helper_text ||= t('fae.images.alt_helper')
11
11
  caption_helper_text ||= ""
12
12
  show_alt = true if show_alt.nil?
13
13
  show_caption ||= false
@@ -2,15 +2,15 @@
2
2
  header.content-header.js-content-header
3
3
  h1 Root Settings
4
4
  .content-header-buttons
5
- a.button#js-header-cancel href=fae.root_path(cancelled: true) Cancel
6
- = f.submit 'Save'
5
+ a.button#js-header-cancel href=fae.root_path(cancelled: true) = t('fae.form.cancel')
6
+ = f.submit t('fae.form.save')
7
7
 
8
8
  == render 'flash_messages'
9
9
 
10
10
  section.content
11
- = fae_input f, :title, label: 'Project Name'
11
+ = fae_input f, :title, label: t('fae.options.project.name')
12
12
  = fae_input f, :time_zone, wrapper_class: 'select'
13
- = fae_input f, :live_url, label: 'Live URL', helper_text: 'Include http:// for external links.'
14
- = fae_input f, :stage_url, label: 'Stage URL', helper_text: 'Include http:// for external links.'
15
- = fae_image_form f, :logo, helper_text: 'Used on login page, 300x300px .jpg'
13
+ = fae_input f, :live_url, label: t('fae.options.live_url'), helper_text: t('fae.options.url_helper')
14
+ = fae_input f, :stage_url, label: t('fae.options.stage_url'), helper_text: t('fae.options.url_helper')
15
+ = fae_image_form f, :logo, helper_text: t('fae.options.logo_helper')
16
16
  = fae_image_form f, :favicon
@@ -2,20 +2,20 @@
2
2
 
3
3
  main.content
4
4
 
5
- == fae_filter_form title: 'Search Changes', action: fae.activity_log_filter_path do
6
- == fae_filter_select :user, options: Fae::User.all.map { |u| [u.full_name, u.id] }
7
- == fae_filter_select :model, options: Fae::Change.unique_changeable_types
8
- == fae_filter_select :type, options: ['created', 'updated', 'deleted']
9
- == fae_filter_select :date, options: ['Last Hour', 'Last Day', 'Last Week', 'Last Month'], placeholder: 'All Time'
5
+ == fae_filter_form title: t('fae.changes.title'), action: fae.activity_log_filter_path do
6
+ == fae_filter_select :user, options: Fae::User.all.map { |u| [u.full_name, u.id] }, translation_path: 'fae.changes.models'
7
+ == fae_filter_select :model, options: Fae::Change.unique_changeable_types, translation_path: 'fae.changes.models'
8
+ == fae_filter_select :type, options: [t('fae.changes.created'), t('fae.changes.updated'), t('fae.changes.deleted')], translation_path: 'fae.changes.models'
9
+ == fae_filter_select :date, options: [t('fae.changes.last_hour'), t('fae.changes.last_day'), t('fae.changes.last_week'), t('fae.changes.last_month')], placeholder: t('fae.changes.all_time')
10
10
 
11
11
  table.js-results-table
12
12
  thead
13
13
  tr
14
- th data-sort="user.first_name" User
15
- th data-sort="changeable_type" Item
16
- th data-sort="change_type" Type
17
- th Attribute(s) Updated
18
- th data-sort="updated_at" Modified
14
+ th data-sort="user.first_name" = t('fae.changes.user')
15
+ th data-sort="changeable_type" = t('fae.changes.item')
16
+ th data-sort="change_type" = t('fae.changes.type')
17
+ th = t('fae.changes.attrs')
18
+ th data-sort="updated_at" = t('fae.changes.modified')
19
19
  tbody
20
20
  - if @items.present?
21
21
  - @items.each do |change|
@@ -27,6 +27,6 @@ main.content
27
27
  td = fae_datetime_format(change.updated_at)
28
28
  - else
29
29
  tr
30
- td colspan=5 No changes recorded
30
+ td colspan=5 = t('fae.changes.no_changes')
31
31
 
32
32
  == fae_paginate @items
@@ -1,4 +1,4 @@
1
1
  .center
2
2
  .sad_face
3
- h1 Disabled Admin
4
- p The admin on this domain has been disabled. This usually occurs on a staging domain where content is managed in production. Contact application support for more information.
3
+ h1 = t('fae.page.disabled_title')
4
+ p = t('fae.page.disabled_text')
@@ -2,6 +2,8 @@
2
2
  .sad_face
3
3
  h1 Oops!
4
4
  p
5
- | The page you requested can not be found. Please&nbsp;
6
- a href=fae.root_path click here
7
- | &nbsp;to be taken to our home page.
5
+ = t('fae.page.error_start')
6
+ |&nbsp;
7
+ a href=fae.root_path = t('fae.page.click')
8
+ | &nbsp;
9
+ = t('fae.page.error_end')
@@ -1,7 +1,8 @@
1
1
  header.content-header.js-content-header.-dashboard
2
2
  div
3
3
  i.icon-home>
4
- | &nbsp;&nbsp;Hello
4
+ | &nbsp;&nbsp;
5
+ = t('fae.page.hello')
5
6
  strong< = current_user.full_name
6
7
 
7
8
  == render 'flash_messages'
@@ -11,9 +12,9 @@ header.content-header.js-content-header.-dashboard
11
12
  table.js-sort-column
12
13
  thead
13
14
  tr
14
- th Name
15
- th Type
16
- th.-action-wide Modified
15
+ th = t('fae.common.name')
16
+ th = t('fae.changes.type')
17
+ th.-action-wide = t('fae.changes.modified')
17
18
  tbody
18
19
  - @list.each do |item|
19
20
  - begin
@@ -28,9 +29,9 @@ header.content-header.js-content-header.-dashboard
28
29
  - else
29
30
  section.help-section
30
31
  article.content
31
- h2 Welcome to Fae
32
+ h2 = t('fae.page.welcome')
32
33
  p
33
- ' You don'thave any objects setup to manage. If you need help getting started, check out the
34
+ = t('fae.page.no_objs_start')
34
35
  br
35
- a href="https://www.faecms.com/documentation/quickstart-guide" target="_blank" Quickstart Guide and Documentation site
36
- ' .
36
+ a href="https://www.faecms.com/documentation/quickstart-guide" target="_blank"
37
+ = t('fae.page.no_objs.end')
@@ -1,11 +1,11 @@
1
1
  .first_user-form
2
- p Please create your super admin user.
2
+ p = t('fae.setup.prompt')
3
3
 
4
4
  = simple_form_for(@user, html: { multipart: true, novalidate: true }, url: fae.first_user_path) do |f|
5
5
  = fae_input f, :first_name
6
6
  = fae_input f, :last_name
7
7
  = fae_input f, :email
8
- = fae_input f, :password, helper_text: 'Passwords must contain at least 8 characters.'
8
+ = fae_input f, :password, helper_text: t('fae.setup.password')
9
9
  = fae_input f, :password_confirmation
10
10
  .login-form-actions
11
- = f.button :submit, "Submit"
11
+ input name="commit" type="submit" value=t('fae.form.save')
@@ -1,9 +1,9 @@
1
1
  ruby:
2
2
  require_locals ['header'], local_assigns
3
- save_button_text ||= 'Save'
4
- cancel_button_text ||= 'Cancel'
3
+ save_button_text ||= t('fae.form.save')
4
+ cancel_button_text ||= t('fae.form.cancel')
5
5
  cloneable ||= false
6
- clone_button_text ||= 'Clone'
6
+ clone_button_text ||= t('fae.form.clone')
7
7
  subnav ||= []
8
8
  header_as_string = header.is_a?(String) ? header : header.class.name.split('::').last
9
9
  header_as_string = header_as_string.singularize
@@ -3,7 +3,8 @@ ruby:
3
3
  title ||= @klass_humanized.pluralize.titleize
4
4
  new_button = !local_assigns[:new_button].eql?(false)
5
5
  new_path ||= "new_#{fae_path}_#{@klass_singular}_path"
6
- button_text ||= "Add #{title.singularize}"
6
+ button_text ||= t('fae.common.add', title: title.singularize)
7
+ export_text ||= t('fae.common.csv_export')
7
8
  csv ||= false
8
9
  breadcrumb = !local_assigns[:breadcrumbs].eql?(false)
9
10
 
@@ -15,7 +16,7 @@ header.content-header.js-content-header
15
16
 
16
17
  .content-header-buttons
17
18
  - if csv && @items.present?
18
- a.button href="#{@index_path}.csv" Export to CSV
19
+ a.button href="#{@index_path}.csv" = export_text
19
20
 
20
21
  - if new_button
21
22
  - if nested
@@ -10,7 +10,7 @@ ruby:
10
10
  # optional locals
11
11
  assoc_name ||= assoc.to_s # 'restaurant_bars'
12
12
  title ||= assoc_name.titleize # 'Restaurant Bars'
13
- add_button_text ||= "Add #{title.singularize}"
13
+ add_button_text ||= t('fae.common.add', title: title.singularize)
14
14
  ordered ||= false
15
15
  has_thumb ||= false
16
16
  edit_column ||= false
@@ -1,14 +1,14 @@
1
1
  - unless params[:action] == 'new'
2
2
  .content#recent_changes
3
- h2 Recent Changes
3
+ h2 = t('fae.changes.recent')
4
4
  section.addedit-form.js-addedit-form
5
5
  table
6
6
  thead
7
7
  tr
8
- th User
9
- th Type
10
- th Attribute(s) Updated
11
- th.-action-wide Modified
8
+ th = t('fae.changes.user')
9
+ th = t('fae.changes.type')
10
+ th = t('fae.changes.attrs')
11
+ th.-action-wide t('fea.changes.modified')
12
12
  tbody
13
13
  - if @item.try(:tracked_changes).present?
14
14
  - @item.tracked_changes.each do |change|
@@ -19,4 +19,4 @@
19
19
  td = fae_datetime_format(change.updated_at)
20
20
  - else
21
21
  tr
22
- td colspan="4" No changes recorded
22
+ td colspan="4" = t('fae.changes.no_changes')
@@ -33,7 +33,7 @@ table class=table_class
33
33
  = td_columns(options_for_td)
34
34
  - if edit_column
35
35
  td
36
- a.js-edit-link.js-tooltip.table-action title="Edit" href=self.send(edit_path, item)
36
+ a.js-edit-link.js-tooltip.table-action title=t('fae.common.edit') href=self.send(edit_path, item)
37
37
  i.icon-edit
38
38
  td = fae_delete_button item, "/#{fae_path}/#{assoc_name}/#{item.id.to_s}", class: 'js-tooltip table-action js-delete-link', remote: true
39
39