rocket_cms 0.1.13

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 (99) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +1 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +2 -0
  6. data/.travis.yml +19 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +36 -0
  10. data/Rakefile +1 -0
  11. data/app/assets/javascripts/head.load.js +707 -0
  12. data/app/assets/javascripts/jquery.placeholder.js +157 -0
  13. data/app/assets/javascripts/rocket_cms.js.coffee +10 -0
  14. data/app/assets/javascripts/rocket_cms/flash.js.coffee +3 -0
  15. data/app/assets/javascripts/rocket_cms/map.js.coffee +22 -0
  16. data/app/assets/stylesheets/rocket_cms.css.sass +2 -0
  17. data/app/assets/stylesheets/rocket_cms/flash.css.sass +66 -0
  18. data/app/assets/stylesheets/rocket_cms/normalize.css.scss +406 -0
  19. data/app/controllers/concerns/no_cache.rb +12 -0
  20. data/app/controllers/concerns/rs_errors.rb +58 -0
  21. data/app/controllers/concerns/rs_menu.rb +45 -0
  22. data/app/controllers/concerns/rs_pages.rb +41 -0
  23. data/app/controllers/contacts_controller.rb +29 -0
  24. data/app/controllers/news_controller.rb +22 -0
  25. data/app/controllers/pages_controller.rb +12 -0
  26. data/app/controllers/search_controller.rb +25 -0
  27. data/app/mailers/contact_mailer.rb +15 -0
  28. data/app/models/ckeditor/asset.rb +5 -0
  29. data/app/models/ckeditor/attachment_file.rb +15 -0
  30. data/app/models/ckeditor/picture.rb +16 -0
  31. data/app/models/concerns/boolean_field.rb +9 -0
  32. data/app/models/concerns/enableable.rb +8 -0
  33. data/app/models/concerns/geocodeable.rb +4 -0
  34. data/app/models/concerns/manual_slug.rb +38 -0
  35. data/app/models/concerns/mappable.rb +77 -0
  36. data/app/models/concerns/seoable.rb +35 -0
  37. data/app/models/concerns/sort_field.rb +12 -0
  38. data/app/models/concerns/sortable.rb +8 -0
  39. data/app/models/concerns/trackable.rb +8 -0
  40. data/app/models/contact_message.rb +6 -0
  41. data/app/models/menu.rb +6 -0
  42. data/app/models/news.rb +5 -0
  43. data/app/models/page.rb +6 -0
  44. data/app/views/contact_mailer/new_message_email.html.haml +15 -0
  45. data/app/views/contacts/new.html.haml +10 -0
  46. data/app/views/contacts/sent.html.haml +4 -0
  47. data/app/views/errors/_base.html.haml +3 -0
  48. data/app/views/errors/error_403.html.haml +1 -0
  49. data/app/views/errors/error_404.html.haml +1 -0
  50. data/app/views/errors/error_500.html.haml +1 -0
  51. data/app/views/news/index.html.haml +8 -0
  52. data/app/views/news/show.html.haml +8 -0
  53. data/app/views/pages/show.html.haml +1 -0
  54. data/app/views/rails_admin/main/_check_boxes.html.haml +27 -0
  55. data/app/views/rails_admin/main/_form_raw.html.haml +1 -0
  56. data/app/views/search/index.html.haml +19 -0
  57. data/app/views/shared/_admin_link.html.haml +3 -0
  58. data/app/views/shared/_messages.html.haml +7 -0
  59. data/app/views/shared/_meta.html.haml +6 -0
  60. data/app/views/shared/_obj.html.haml +14 -0
  61. data/app/views/shared/_og.html.haml +4 -0
  62. data/config/locales/en.rocket_admin.yml +6 -0
  63. data/config/locales/en.rs.yml +17 -0
  64. data/config/locales/ru.cancan.yml +4 -0
  65. data/config/locales/ru.devise.yml +65 -0
  66. data/config/locales/ru.kaminari.yml +17 -0
  67. data/config/locales/ru.models.yml +78 -0
  68. data/config/locales/ru.mongoid.yml +450 -0
  69. data/config/locales/ru.rails_admin.yml +147 -0
  70. data/config/locales/ru.rocket_admin.yml +6 -0
  71. data/config/locales/ru.rs.yml +17 -0
  72. data/config/locales/ru.simple_captcha.yml +3 -0
  73. data/config/locales/ru.simple_form.yml +9 -0
  74. data/lib/filename_to_slug.rb +32 -0
  75. data/lib/generators/rocket_cms/admin_generator.rb +20 -0
  76. data/lib/generators/rocket_cms/templates/ability.erb +17 -0
  77. data/lib/generators/rocket_cms/templates/admin.erb +71 -0
  78. data/lib/generators/rocket_cms/utils.rb +22 -0
  79. data/lib/history_tracker.rb +4 -0
  80. data/lib/rails_admin/custom_show_in_app.rb +39 -0
  81. data/lib/rocket_cms.rb +57 -0
  82. data/lib/rocket_cms/admin.rb +128 -0
  83. data/lib/rocket_cms/configuration.rb +54 -0
  84. data/lib/rocket_cms/controller.rb +24 -0
  85. data/lib/rocket_cms/elastic_search.rb +32 -0
  86. data/lib/rocket_cms/engine.rb +4 -0
  87. data/lib/rocket_cms/model.rb +16 -0
  88. data/lib/rocket_cms/models/contact_message.rb +37 -0
  89. data/lib/rocket_cms/models/menu.rb +16 -0
  90. data/lib/rocket_cms/models/news.rb +61 -0
  91. data/lib/rocket_cms/models/page.rb +86 -0
  92. data/lib/rocket_cms/patch.rb +58 -0
  93. data/lib/rocket_cms/rails_admin_menu.rb +137 -0
  94. data/lib/rocket_cms/railtie.rb +39 -0
  95. data/lib/rocket_cms/tasks.rb +14 -0
  96. data/lib/rocket_cms/version.rb +3 -0
  97. data/lib/smart_excerpt.rb +98 -0
  98. data/rocket_cms.gemspec +50 -0
  99. metadata +533 -0
@@ -0,0 +1,35 @@
1
+ module Seoable
2
+ extend ActiveSupport::Concern
3
+ include Mongoid::Paperclip
4
+
5
+ included do
6
+ field :name, type: String
7
+ field :h1, type: String
8
+
9
+ field :title, type: String
10
+ field :keywords, type: String
11
+ field :description, type: String
12
+ field :robots, type: String
13
+
14
+ field :og_title, type: String
15
+ has_mongoid_attached_file :og_image, styles: {thumb: "800x600>"}
16
+ end
17
+
18
+ def page_title
19
+ title.blank? ? name : title
20
+ end
21
+
22
+ def get_og_title
23
+ og_title.blank? ? name : og_title
24
+ end
25
+
26
+ def self.admin
27
+ RocketCMS.seo_config
28
+ end
29
+
30
+ # deprecated
31
+ def self.seo_config
32
+ RocketCMS.seo_config
33
+ end
34
+ end
35
+
@@ -0,0 +1,12 @@
1
+ module SortField
2
+ extend ActiveSupport::Concern
3
+
4
+ module ClassMethods
5
+ def sort_field(prefix = '')
6
+ prefix = "#{prefix}_" unless prefix == ''
7
+
8
+ field "#{prefix}sort".to_sym, type: Integer
9
+ scope "#{prefix}sorted".to_sym, -> { asc("#{prefix}sort".to_sym) }
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,8 @@
1
+ module Sortable
2
+ extend ActiveSupport::Concern
3
+ include SortField
4
+
5
+ included do
6
+ sort_field
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ module Trackable
2
+ extend ActiveSupport::Concern
3
+ include Mongoid::Audit::Trackable
4
+ included do
5
+ track_history track_create: true, track_destroy: true
6
+ end
7
+ end
8
+
@@ -0,0 +1,6 @@
1
+ class ContactMessage
2
+ include RocketCMS::Models::ContactMessage
3
+ RocketCMS.apply_patches self
4
+ rails_admin &RocketCMS.contact_message_config
5
+ end
6
+
@@ -0,0 +1,6 @@
1
+ class Menu
2
+ include RocketCMS::Models::Menu
3
+ RocketCMS.apply_patches self
4
+ rails_admin &RocketCMS.menu_config
5
+ end
6
+
@@ -0,0 +1,5 @@
1
+ class News
2
+ include RocketCMS::Models::News
3
+ RocketCMS.apply_patches self
4
+ rails_admin &RocketCMS.news_config
5
+ end
@@ -0,0 +1,6 @@
1
+ class Page
2
+ include RocketCMS::Models::Page
3
+ RocketCMS.apply_patches self
4
+ rails_admin &RocketCMS.page_config
5
+ end
6
+
@@ -0,0 +1,15 @@
1
+
2
+ - excluded_column_names = %w[id created_at updated_at _id _type enabled attachment c_at u_at captcha captcha_key version modifier modifier_id]
3
+
4
+ %h3 Сообщение из формы связи:
5
+
6
+ %table
7
+ %tr
8
+ %th(style='padding: 2px 3px') Поле
9
+ %th(style='padding: 2px 3px') Значение
10
+ - ContactMessage.fields.keys.reject{|c| excluded_column_names.include?(c) }.each do |c|
11
+ %tr
12
+ %td(style='padding: 2px 3px')
13
+ = ContactMessage.human_attribute_name(c)
14
+ %td(style='padding: 2px 3px')
15
+ = @message.send(c.to_sym)
@@ -0,0 +1,10 @@
1
+ = render 'shared/obj', obj: @seo_page
2
+
3
+ #rocket_cms_contact_form
4
+ = simple_form_for @contact_message do |f|
5
+ = f.input :name
6
+ = f.input :email
7
+ = f.input :phone
8
+ = f.input :content
9
+ = f.input :captcha
10
+ = f.submit t('rs.send')
@@ -0,0 +1,4 @@
1
+ #rocket_cms_contact_sent
2
+ %h1 Обратная связь
3
+ %h2 Ваше сообщение отправлено
4
+ %div Если оно требует ответа, мы постараемся ответить в ближайшее время
@@ -0,0 +1,3 @@
1
+ #rocket_cms_error{class: "error-#{code}"}
2
+ %h1 Ошибка #{code}
3
+ %p #{text}
@@ -0,0 +1 @@
1
+ = render 'errors/base', code: 403, text: 'Доступ запрещен'
@@ -0,0 +1 @@
1
+ = render 'errors/base', code: 404, text: 'Запрашиваемая страница была удалена, или введён некорректный адрес'
@@ -0,0 +1 @@
1
+ = render 'errors/base', code: 500, text: 'Внутренняя ошибка сервера'
@@ -0,0 +1,8 @@
1
+ .rs-news-index
2
+ %h1= t('rs.news')
3
+ - @news.each do |news|
4
+ .rs-news-date= news.time.strftime('%Y-%m-%d')
5
+ - unless RocketCMS.configuration.news_image_styles.nil?
6
+ .rs-news-image= image_tag news.image.url(:full)
7
+ .rs-news-excerpt = news.excerpt
8
+ = paginate @news
@@ -0,0 +1,8 @@
1
+ .rs-news-show
2
+ %h1.rs-news-title= @news.name
3
+ .rs-news-date= @news.time.strftime('%Y-%m-%d')
4
+ .rs-news-text = @news.excerpt
5
+ - unless RocketCMS.configuration.news_image_styles.nil?
6
+ .rs-news-image= image_tag @news.image.url(:full)
7
+ .rs-news-content
8
+ = render 'shared/obj', obj: @news
@@ -0,0 +1 @@
1
+ = render 'shared/obj', obj: @seo_page
@@ -0,0 +1,27 @@
1
+ - if params[:associations].nil?
2
+ = form.collection_check_boxes field.method_name, form.object.class.send(field.method_name).values.map { |v| [t("enumerize.#{form.object.class.name.downcase}.#{field.method_name}.#{v}"), v] }, :last, :first, {}, {} { |i| i.label( class: 'checkbox' ) { i.check_box + i.text } }
3
+ - else
4
+ :ruby
5
+ related_id = params[:associations] && params[:associations][field.name.to_s]
6
+ config = field.associated_model_config
7
+ source_abstract_model = RailsAdmin.config(form.object.class).abstract_model
8
+
9
+ if form.object.new_record? && related_id.present? && related_id != 'new'
10
+ selected = [config.abstract_model.get(related_id)]
11
+ else
12
+ selected = form.object.send(field.name)
13
+ end
14
+ selected_ids = selected.map{|s| s.send(field.associated_primary_key)}
15
+
16
+ current_action = params[:action].in?(['create', 'new']) ? 'create' : 'update'
17
+
18
+ xhr = !field.associated_collection_cache_all
19
+
20
+ collection = if xhr
21
+ selected.map { |o| [o.send(field.associated_object_label_method), o.send(field.associated_primary_key)] }
22
+ else
23
+ i = 0
24
+ controller.list_entries(config, :index, field.associated_collection_scope, false).map { |o| [o.send(field.associated_object_label_method), o.send(field.associated_primary_key)] }.sort_by {|a| [selected_ids.index(a[1]) || selected_ids.size, i+=1] }
25
+ end
26
+
27
+ = form.collection_check_boxes field.method_name, collection, :last, :first, {}, {} { |i| i.label( class: 'checkbox' ) { i.check_box + i.text } }
@@ -0,0 +1 @@
1
+ = field.pretty_value
@@ -0,0 +1,19 @@
1
+ .rs-search-results
2
+ %h1 Результаты поиска
3
+ = form_tag search_path, method: :get, class: 'nav_search' do
4
+ = text_field_tag 'query', params[:query], placeholder: "Поиск"
5
+ = submit_tag 'Найти'
6
+ %ol
7
+ - any = false
8
+ - @results.each do |r|
9
+ - any = true
10
+ %li
11
+ .title= link_to (r._highlight.nil? || r._highlight['name'].nil?) ? r.name : r._highlight['name'].join(' ... ').html_safe, url_for(r)
12
+ .text
13
+ = raw (r._highlight.nil? || r._highlight['content'].nil?) ? (r.content.blank? ? '' : SmartExcerpt.truncate(r.content)) : r._highlight['content'].join(' ... ')
14
+ %span.more= link_to '', url_for(r)
15
+
16
+ - unless any
17
+ .rs-search-no-results К сожалению, ничего не найдено
18
+
19
+ = paginate @results if any
@@ -0,0 +1,3 @@
1
+ - if user_signed_in? && respond_to?(:can?) && can?(:access, :rails_admin) && can?(:edit, obj)
2
+ %div.admin_link
3
+ = link_to t('rs.edit'), rails_admin.edit_path(obj.class.model_name.i18n_key.to_s.gsub('/', '~'), obj.id), 'data-no-turbolink' => true
@@ -0,0 +1,7 @@
1
+ - unless flash.empty?
2
+ .flash
3
+ - flash.each do |type, message|
4
+ - next if message.blank?
5
+ %div{class: "message #{type}"}
6
+ %a.close_flash ×
7
+ = simple_format(message.strip)
@@ -0,0 +1,6 @@
1
+ - unless !obj.respond_to?(:keywords) || obj.keywords.blank?
2
+ %meta{name: "keywords", content: obj.keywords}/
3
+ - unless !obj.respond_to?(:description) || obj.description.blank?
4
+ %meta{name: "description", content: obj.description}/
5
+ - unless !obj.respond_to?(:robots) || obj.robots.blank?
6
+ %meta{name: "robots", content: obj.robots}/
@@ -0,0 +1,14 @@
1
+ - unless obj.nil?
2
+ .text_content
3
+ - unless obj.h1.blank?
4
+ %h1= obj.h1
5
+ - if obj.content.blank?
6
+ = raw @seo_page.page_content unless @seo_page.nil?
7
+ = render 'shared/admin_link', obj: @seo_page
8
+ - else
9
+ = raw obj.content
10
+ = render 'shared/admin_link', obj: obj
11
+
12
+ - content_for :meta do
13
+ = render 'shared/meta', obj: obj
14
+ = render 'shared/og', title: obj.get_og_title, image: obj.og_image
@@ -0,0 +1,4 @@
1
+ - if defined?(title) && !title.blank?
2
+ %meta{property: 'og:title', content: title}
3
+ - if defined?(image) && !image.blank?
4
+ %meta{property: 'og:image', content: "http://#{request.host}#{image}"}
@@ -0,0 +1,6 @@
1
+ en:
2
+ admin:
3
+ actions:
4
+ custom_show_in_app:
5
+ title: 'Show in app'
6
+ menu: "Show in app"
@@ -0,0 +1,17 @@
1
+ en:
2
+ rs:
3
+ cms: "CMS"
4
+ menu: "Menu"
5
+ settings: 'Settings'
6
+ news: 'News'
7
+ with_final_slash: "should begin with a slash"
8
+ page_url_regex: "Regular expression to check if page is current"
9
+ final_in_menu: "This link is also displayed in menu"
10
+ no_contact_info: "Please enter your phone or email so we could contact you."
11
+ map: 'Map'
12
+ edit: Edit
13
+ m:
14
+ enabled: 'added to menu "%{menu}"'
15
+ disabled: 'deleted from menu "%{menu}"'
16
+ error: "Error: %{err}"
17
+ no_id: "No ID"
@@ -0,0 +1,4 @@
1
+ en:
2
+ unauthorized:
3
+ manage:
4
+ all: "У вас нет прав на действие %{action} над %{subject}."
@@ -0,0 +1,65 @@
1
+ ru:
2
+ devise:
3
+ confirmations:
4
+ confirmed: Ваша учётная запись подтверждена. Теперь вы вошли в систему.
5
+ send_instructions: В течение нескольких минут вы получите письмо с инструкциями по подтверждению вашей учётной записи.
6
+ send_paranoid_instructions: Если ваш адрес e-mail есть в нашей базе данных, то в течение нескольких минут вы получите письмо с инструкциями по подтверждению вашей учётной записи.
7
+ failure:
8
+ already_authenticated: Вы уже вошли в систему.
9
+ inactive: Ваша учётная запись ещё не активирована.
10
+ invalid: Неверный адрес e-mail или пароль.
11
+ invalid_token: Неверный ключ аутентификации.
12
+ locked: Ваша учётная запись заблокирована.
13
+ not_found_in_database:
14
+ timeout: Ваш сеанс закончился. Пожалуйста, войдите в систему снова.
15
+ unauthenticated: Вам необходимо войти в систему или зарегистрироваться.
16
+ unconfirmed: Вы должны подтвердить вашу учётную запись.
17
+ mailer:
18
+ confirmation_instructions:
19
+ subject: Инструкции по подтверждению учётной записи
20
+ reset_password_instructions:
21
+ subject: Инструкции по восстановлению пароля
22
+ unlock_instructions:
23
+ subject: Инструкции по разблокировке учётной записи
24
+ omniauth_callbacks:
25
+ failure: Вы не можете войти в систему с учётной записью из %{kind}, т.к. "%{reason}".
26
+ success: Вход в систему выполнен с учётной записью из %{kind}.
27
+ passwords:
28
+ no_token: Доступ к этой странице возможен только по ссылке из письма о восстановлении пароля. Если Вы пришли по такой ссылке, пожалуйста убедитесь что Вы скопировали всю ссылку целиком.
29
+ send_instructions: В течение нескольких минут вы получите письмо с инструкциями по восстановлению вашего пароля.
30
+ send_paranoid_instructions: Если ваш адрес e-mail есть в нашей базе данных, то в течение нескольких минут вы получите письмо с инструкциями по восстановлению вашего пароля.
31
+ updated: Ваш пароль изменён. Теперь вы вошли в систему.
32
+ updated_not_active: Ваш пароль изменен.
33
+ registrations:
34
+ destroyed: До свидания! Ваша учётная запись удалена. Надеемся снова увидеть вас.
35
+ signed_up: Добро пожаловать! Вы успешно зарегистрировались.
36
+ signed_up_but_inactive: Вы успешно зарегистрированы. Однако, вы не можете войти в систему, потому что ваша учетная запись не активирована.
37
+ signed_up_but_locked: Вы успешно зарегистрированы. Однако, вы не можете войти в систему, потому что ваша учетная запись заблокирована.
38
+ signed_up_but_unconfirmed: Письмо со ссылкой для подтверждения было отправлено на ваш e-mail. Пожалуйста, перейдите по ссылке, чтобы подтвердить вашу учетную запись.
39
+ update_needs_confirmation: Вы успешно обновили данные вашей учетной записи, но нам нужно проверить ваш новый адрес e-mail. Пожалуйста, проверьте ваш почтовый ящик и перейдите по ссылке, чтобы закончить процедуру проверки вашего нового адреса e-mail.
40
+ updated: Ваша учётная запись изменена.
41
+ sessions:
42
+ signed_in: Вход в систему выполнен.
43
+ signed_out: Выход из системы выполнен.
44
+ unlocks:
45
+ send_instructions: В течение нескольких минут вы получите письмо с инструкциями по разблокировке вашей учётной записи.
46
+ send_paranoid_instructions: Если ваша учётная запись существует, то в течение нескольких минут вы получите письмо с инструкциями по её разблокировке.
47
+ unlocked: Ваша учётная запись разблокирована. Теперь вы можете войти в систему.
48
+ failure:
49
+ user:
50
+ not_found_in_database: Пользователь не найден в базе
51
+ invalid: Неверный пароль
52
+ unauthenticated: Необходимо авторизоваться
53
+ unconfirmed: 'Необходимо подтвердить адрес электронной почты'
54
+ errors:
55
+ messages:
56
+ already_confirmed: уже подтверждена. Пожалуйста, попробуйте войти в систему
57
+ confirmation_period_expired:
58
+ expired: устарела. Пожалуйста, запросите новую
59
+ not_found: не найдена
60
+ not_locked: не заблокирована
61
+ not_saved:
62
+ few: ! '%{resource}: сохранение не удалось из-за %{count} ошибок'
63
+ many: ! '%{resource}: сохранение не удалось из-за %{count} ошибок'
64
+ one: ! '%{resource}: сохранение не удалось из-за %{count} ошибки'
65
+ other: ! '%{resource}: сохранение не удалось из-за %{count} ошибки'
@@ -0,0 +1,17 @@
1
+ ru:
2
+ views:
3
+ pagination:
4
+ first: "« Начало"
5
+ last: "Конец »"
6
+ previous: "‹ Предыдущая"
7
+ next: "Следующая ›"
8
+ truncate: "…"
9
+ helpers:
10
+ page_entries_info:
11
+ one_page:
12
+ display_entries:
13
+ zero: "%{entry_name} не найдены"
14
+ one: "Показано <b>1</b> %{entry_name}"
15
+ other: "Показаны <b>все %{count}</b> %{entry_name}"
16
+ more_pages:
17
+ display_entries: "Показаны %{entry_name} <b>%{first}&nbsp;-&nbsp;%{last}</b> из <b>%{total}</b>"
@@ -0,0 +1,78 @@
1
+ ru:
2
+ attributes:
3
+ user: Пользователь
4
+ name: Название
5
+ category: Раздел
6
+ file: Файл
7
+ short: Короткое описание
8
+ text: Текст
9
+ desc: Описание
10
+ image: Картинка
11
+ href: Ссылка
12
+ enabled: Включено
13
+ slug: URL код
14
+ text_slug: URL код
15
+ c_at: Создано
16
+ u_at: Обновлено
17
+ id: ID
18
+ content: Содержимое
19
+ time: Время
20
+ excerpt: Превью
21
+ sort: Сортировка
22
+ link: Ссылка
23
+ url: URL
24
+ address: Адрес
25
+ map_address: Адрес для карты
26
+ map_hint: Подсказка для карты
27
+ coordinates: Координаты из геокодирования
28
+ lat: Широта (вручную)
29
+ lon: Долгота (вручную)
30
+ description: SEO Description
31
+ email: Email
32
+ keywords: SEO Keywords
33
+ modifier_id: Кто правил
34
+ og_title: Og title
35
+ robots: Robots
36
+ title: Title
37
+ version: Version
38
+
39
+ mongoid:
40
+ models:
41
+ user: Пользователь
42
+ menu: Меню
43
+ page: Страница
44
+ contact_message: Сообщение
45
+ news: Новость
46
+
47
+ attributes:
48
+ user:
49
+ email: E-mail
50
+ old_password: Текущий пароль
51
+ password: Пароль
52
+ password_confirmation: Подтвердите пароль
53
+ remember_me: Запомнить
54
+ c_at: Создан
55
+ current_sign_in_ip: Текущий IP
56
+ last_sign_in_ip: Прошлый IP
57
+ first_name: Имя
58
+ last_name: Фамилия
59
+ phone: Телефон
60
+
61
+ page:
62
+ fullpath: URL
63
+ regexp: Подсветка меню
64
+ redirect: Редирект
65
+ image: Картинка
66
+ hidden: Скрытая
67
+ menus: Меню
68
+ contact_message:
69
+ name: Ваше имя
70
+ email: Ваш е-мейл
71
+ phone: Ваш телефон
72
+ content: Ваше сообщение
73
+
74
+ activemodel:
75
+ errors:
76
+ messages:
77
+ invalid_email_address: Неверный e-mail
78
+