rocket_cms 0.1.13

Sign up to get free protection for your applications and to get access to all the features.
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,54 @@
1
+ module RocketCMS
2
+ def self.configuration
3
+ @configuration ||= Configuration.new
4
+ end
5
+
6
+ def self.configure
7
+ yield configuration
8
+ end
9
+
10
+ class Configuration
11
+ attr_accessor :news_image_styles
12
+ attr_accessor :news_per_page
13
+ attr_accessor :news_excerpt
14
+
15
+ attr_accessor :error_layout
16
+ attr_accessor :menu_max_depth
17
+
18
+ attr_accessor :search_enabled
19
+ attr_accessor :search_per_page
20
+ attr_accessor :search_models
21
+
22
+ attr_accessor :contacts_captcha
23
+ attr_accessor :contacts_fields
24
+ attr_accessor :contacts_message_required
25
+
26
+ def initialize
27
+ @news_image_styles = {
28
+ main: '400x200>',
29
+ thumb: '200x100>',
30
+ }
31
+ @news_per_page = 10
32
+ @news_excerpt = 12
33
+
34
+ @error_layout = 'application'
35
+ @menu_max_depth = 2
36
+
37
+ @search_enabled = false
38
+ @search_per_page = 10
39
+ @search_models = []
40
+
41
+ @contacts_captcha = false
42
+ @contacts_fields = {}
43
+ @contacts_message_required = true
44
+ end
45
+
46
+ def search_enabled=(val)
47
+ @search_enabled = val
48
+ if @search_enabled
49
+ @search_models << 'Page'
50
+ @search_models << 'News'
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,24 @@
1
+ module RocketCMS::Controller
2
+ extend ActiveSupport::Concern
3
+ included do
4
+ include RsErrors
5
+ include RsPages
6
+ include RsMenu
7
+ protect_from_forgery with: :exception
8
+ helper_method :page_title
9
+ end
10
+
11
+ protected
12
+
13
+ def page_title
14
+ if @seo_page.nil?
15
+ Settings.default_title
16
+ else
17
+ @seo_page.page_title
18
+ end
19
+ end
20
+
21
+ def ckeditor_authenticate
22
+ redirect_to '/' unless user_signed_in? && current_user.has_role?('admin')
23
+ end
24
+ end
@@ -0,0 +1,32 @@
1
+ module RocketCMS::ElasticSearch
2
+ extend ActiveSupport::Concern
3
+ included do
4
+ include Mongoid::Elasticsearch
5
+ elasticsearch!({
6
+ index_options: {
7
+ settings: {
8
+ index: {
9
+ analysis: {
10
+ analyzer: {
11
+ my_analyzer: {
12
+ type: "snowball",
13
+ language: "Russian"
14
+ }
15
+ }
16
+ }
17
+ }
18
+ }
19
+ },
20
+ index_mapings: {
21
+ name: {type: 'string', boost: 10, analyzer: 'my_analyzer'},
22
+ content: {type: 'string', boost: 1, analyzer: 'my_analyzer'},
23
+ }
24
+ })
25
+ def es_index?
26
+ enabled
27
+ end
28
+ def as_indexed_json
29
+ {name: name, content: SmartExcerpt.strip_tags(content)}
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,4 @@
1
+ module RocketCMS
2
+ class Engine < ::Rails::Engine
3
+ end
4
+ end
@@ -0,0 +1,16 @@
1
+ module RocketCMS
2
+ module Model
3
+ extend ActiveSupport::Concern
4
+ included do
5
+ include Mongoid::Document
6
+ include Mongoid::Timestamps::Short
7
+ include ActiveModel::ForbiddenAttributesProtection
8
+ include Trackable
9
+ include BooleanField
10
+ include SortField
11
+ include Mongoid::Paperclip
12
+ include SmartExcerpt
13
+ include SimpleCaptcha::ModelHelpers
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,37 @@
1
+ module RocketCMS
2
+ module Models
3
+ module ContactMessage
4
+ extend ActiveSupport::Concern
5
+ include RocketCMS::Model
6
+
7
+ included do
8
+ apply_simple_captcha
9
+
10
+ field :name, type: String
11
+ field :email, type: String
12
+ field :phone, type: String
13
+ field :content, type: String
14
+
15
+ validates_email_format_of :email, unless: 'email.blank?'
16
+
17
+ if RocketCMS.configuration.contacts_message_required
18
+ validates_presence_of :content
19
+ end
20
+
21
+ validate do
22
+ if email.blank? && phone.blank?
23
+ errors.add(:email, I18n.t('rs.no_contact_info'))
24
+ end
25
+ end
26
+
27
+ RocketCMS.configuration.contacts_fields.each_pair do |fn, ft|
28
+ field fn, type: ft
29
+ end
30
+
31
+ after_create do
32
+ ContactMailer.new_message_email(self).deliver
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,16 @@
1
+ module RocketCMS
2
+ module Models
3
+ module Menu
4
+ extend ActiveSupport::Concern
5
+ include RocketCMS::Model
6
+ include Enableable
7
+ include ManualSlug
8
+
9
+ included do
10
+ field :name, type: String
11
+ manual_slug :name
12
+ has_and_belongs_to_many :pages, inverse_of: :menus
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,61 @@
1
+ module RocketCMS
2
+ module Models
3
+ module News
4
+ extend ActiveSupport::Concern
5
+ include RocketCMS::Model
6
+ include Seoable
7
+ include Enableable
8
+ if RocketCMS.configuration.search_enabled
9
+ include RocketCMS::ElasticSearch
10
+ end
11
+ unless RocketCMS.configuration.news_image_styles.nil?
12
+ include Mongoid::Paperclip
13
+ end
14
+ include ManualSlug
15
+
16
+ included do
17
+ field :time, type: Time
18
+ index({enabled: 1, time: 1})
19
+ scope :after_now, -> { where(:time.lt => Time.now) }
20
+
21
+ field :excerpt, type: String
22
+ field :content, type: String
23
+
24
+ unless RocketCMS.configuration.news_image_styles.nil?
25
+ has_mongoid_attached_file :image, styles: RocketCMS.configuration.news_image_styles
26
+ validates_attachment_content_type :image, :content_type => ['image/gif', 'image/jpeg', 'image/jpg', 'image/png'], if: :image?
27
+ end
28
+
29
+ validates_presence_of :name, :content
30
+
31
+ before_validation do
32
+ self.time = Time.now if self.time.blank?
33
+ end
34
+
35
+ manual_slug :report_slug
36
+
37
+ scope :by_date, -> { desc(:time) }
38
+ scope :recent, ->(count = 5) { enabled.after_now.by_date.limit(count) }
39
+ unless RocketCMS.configuration.news_per_page.nil?
40
+ paginates_per RocketCMS.configuration.news_per_page
41
+ end
42
+
43
+ smart_excerpt :excerpt, :content, RocketCMS.configuration.news_excerpt
44
+
45
+ RocketCMS.apply_patches self
46
+ end
47
+
48
+ def report_slug
49
+ time.strftime('%Y-%m-%d') + '-' + name[0..20]
50
+ end
51
+
52
+ def html5_date
53
+ time.strftime('%Y-%m-%d')
54
+ end
55
+
56
+ def format_date
57
+ time.strftime('%d.%m.%Y')
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,86 @@
1
+ module RocketCMS
2
+ module Models
3
+ module Page
4
+ extend ActiveSupport::Concern
5
+
6
+ include RocketCMS::Model
7
+ include Enableable
8
+ include Seoable
9
+ include ManualSlug
10
+ if RocketCMS.configuration.search_enabled
11
+ include RocketCMS::ElasticSearch
12
+ end
13
+
14
+ included do
15
+ field :regexp, type: String
16
+ field :redirect, type: String
17
+ field :content, type: String
18
+ field :fullpath, type: String
19
+
20
+ scope :sorted, -> { order_by([:lft, :asc]) }
21
+ scope :menu, ->(menu_id) { enabled.sorted.where(menu_ids: menu_id) }
22
+
23
+ has_and_belongs_to_many :menus, inverse_of: :pages
24
+
25
+ acts_as_nested_set
26
+
27
+ manual_slug :name
28
+
29
+ before_validation do
30
+ self.fullpath = "/pages/#{slug}" if self.fullpath.blank?
31
+ end
32
+ validates_uniqueness_of :fullpath
33
+
34
+ validates_presence_of :name
35
+ end
36
+
37
+ def get_fullpath
38
+ redirect.blank? ? fullpath : redirect
39
+ end
40
+
41
+ def has_content?
42
+ @content_used.nil? && !content.blank?
43
+ end
44
+
45
+ def page_content
46
+ if @content_used.nil?
47
+ @content_used = true
48
+ if content.nil?
49
+ ''
50
+ else
51
+ content.gsub(/\{\{(.*?)\}\}/) do
52
+ Settings.get($1).val
53
+ end
54
+ end
55
+ else
56
+ ''
57
+ end
58
+ end
59
+
60
+ def is_current?(url)
61
+ if fullpath == '/'
62
+ url == '/'
63
+ else
64
+ url.match(clean_regexp)
65
+ end
66
+ end
67
+
68
+ def clean_regexp
69
+ if regexp.blank?
70
+ /^#{Regexp.escape(fullpath)}$/
71
+ else
72
+ begin
73
+ /#{regexp}/
74
+ rescue
75
+ # not a valid regexp - treat as literal search string
76
+ /#{Regexp.escape(regexp)}/
77
+ end
78
+ end
79
+ end
80
+
81
+ def nav_options
82
+ {highlights_on: clean_regexp}
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,58 @@
1
+ module RocketCMS
2
+ class PatchDSL
3
+ def self.call(&blk)
4
+ instance = new
5
+ instance.instance_eval(&blk)
6
+ instance.instance_variables.inject({}) { |result_hash, instance_variable|
7
+ result_hash[instance_variable[1..-1].to_sym] = instance.instance_variable_get(instance_variable)
8
+ result_hash
9
+ }
10
+ end
11
+
12
+ def initialize
13
+ @model = Proc.new {}
14
+ @admin = Proc.new {}
15
+ @list = Proc.new {}
16
+ @show = Proc.new {}
17
+ @edit = Proc.new {}
18
+ @export = Proc.new {}
19
+ end
20
+
21
+ def model(&blk); @model = blk; end
22
+ def admin(&blk); @admin = blk; end
23
+ def list(&blk); @list = blk; end
24
+ def show(&blk); @show = blk; end
25
+ def edit(&blk); @edit = blk; end
26
+ def export(&blk); @export = blk; end
27
+ end
28
+
29
+ @@patches = {}
30
+
31
+ def self.only_patches(s, sections)
32
+ s.instance_eval do
33
+ sections.each do |section|
34
+ send section do
35
+ RocketCMS.apply_patches self
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ def self.apply_patches(s)
42
+ if s.class.name.index('RailsAdmin::Config::Sections::').nil?
43
+ if s.class.name == 'RailsAdmin::Config::Model'
44
+ s.instance_eval(&@@patches[s.abstract_model.model_name][:admin]) unless @@patches[s.abstract_model.model_name].nil?
45
+ else
46
+ s.instance_eval(&@@patches[s.name][:model]) unless @@patches[s.name].nil?
47
+ end
48
+ else
49
+ model = s.abstract_model.model_name
50
+ action = s.class.name.split('::')[-1].downcase.to_sym
51
+ s.instance_eval(&@@patches[model][action]) unless @@patches[model].nil?
52
+ end
53
+ end
54
+
55
+ def self.patch(model, &blk)
56
+ @@patches[model] = PatchDSL.call(&blk)
57
+ end
58
+ end
@@ -0,0 +1,137 @@
1
+ module RailsAdmin
2
+ module Config
3
+ module Fields
4
+ module Types
5
+ class Menu < RailsAdmin::Config::Fields::Base
6
+ # Register field type for the type loader
7
+ RailsAdmin::Config::Fields::Types::register(self)
8
+ include RailsAdmin::Engine.routes.url_helpers
9
+
10
+ register_instance_option :pretty_value do
11
+ obj = bindings[:object]
12
+ ret = []
13
+ menus = Rails.cache.fetch 'menus', expires_in: 2.minutes do
14
+ ::Menu.all.map { |m| {id: m.id.to_s, name: m.name } }
15
+ end
16
+ menus.each do |m|
17
+ on = obj.menu_ids.include?(BSON::ObjectId.from_string(m[:id]))
18
+ ret << bindings[:view].link_to(
19
+ m[:name],
20
+ bindings[:view].toggle_menu_path(model_name: @abstract_model, id: obj.id, menu: m[:id], on: !on),
21
+ #method: :post,
22
+ title: m[:name],
23
+ class: "btn btn-mini #{on ? "btn-success" : "btn-danger"}",
24
+ style: 'margin-bottom: 5px;',
25
+ onclick: 'var $t = $(this); $.ajax({type: "POST", url: $t.attr("href"), data: {ajax:true}, success: function(r) { $t.attr("href", r.href); $t.attr("class", r.class); }, error: function(e) { alert(e.responseText); }}); return false;'
26
+ )
27
+ end
28
+ ('<div style="white-space: normal;">' + ret.join(' ') + '</div>').html_safe
29
+ end
30
+
31
+ register_instance_option :formatted_value do
32
+ pretty_value
33
+ end
34
+
35
+ register_instance_option :export_value do
36
+ nil
37
+ end
38
+
39
+ register_instance_option :partial do
40
+ :form_raw
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ module RailsAdmin
49
+ module Config
50
+ module Actions
51
+ class ToggleMenu < Base
52
+ RailsAdmin::Config::Actions.register(self)
53
+
54
+ # Is the action acting on the root level (Example: /admin/contact)
55
+ register_instance_option :root? do
56
+ false
57
+ end
58
+
59
+ register_instance_option :collection? do
60
+ false
61
+ end
62
+
63
+ # Is the action on an object scope (Example: /admin/team/1/edit)
64
+ register_instance_option :member? do
65
+ true
66
+ end
67
+
68
+ register_instance_option :controller do
69
+ proc do
70
+ ajax_link = Proc.new do |am, obj, menu, on|
71
+ render json: {
72
+ href: toggle_menu_path(model_name: am, id: obj.id, menu: menu.id, on: !on),
73
+ class: "btn btn-mini #{on ? "btn-success" : "btn-danger"}",
74
+ }
75
+ end
76
+ if params['id'].present?
77
+ begin
78
+ @object = @abstract_model.model.find(params['id'])
79
+ @menu = ::Menu.find(params[:menu])
80
+ if params[:on] == 'true'
81
+ @object.menus << @menu
82
+ else
83
+ @object.menus.delete(@menu)
84
+ end
85
+
86
+ if @object.save
87
+ if params['ajax'].present?
88
+ if params[:on] == 'true'
89
+ ajax_link.call(@abstract_model, @object, @menu, true)
90
+ else
91
+ ajax_link.call(@abstract_model, @object, @menu, false)
92
+ end
93
+ else
94
+ if params[:on] == 'true'
95
+ flash[:success] = I18n.t('rs.m.enabled', menu: @menu.name)
96
+ else
97
+ flash[:success] = I18n.t('rs.m.disabled', menu: @menu.name)
98
+ end
99
+ end
100
+ else
101
+ if params['ajax'].present?
102
+ render text: @object.errors.full_messages.join(', '), layout: false, status: 422
103
+ else
104
+ flash[:error] = @object.errors.full_messages.join(', ')
105
+ end
106
+ end
107
+ rescue Exception => e
108
+ if params['ajax'].present?
109
+ render text: I18n.t('rs.m.error', err: e.to_s), status: 422
110
+ else
111
+ flash[:error] = I18n.t('rs.m.error', err: e.to_s)
112
+ end
113
+ end
114
+ else
115
+ if params['ajax'].present?
116
+ render text: I18n.t('rs.m.no_id'), status: 422
117
+ else
118
+ flash[:error] = I18n.t('rs.m.no_id')
119
+ end
120
+
121
+ end
122
+
123
+ redirect_to :back unless params['ajax'].present?
124
+ end
125
+ end
126
+
127
+ register_instance_option :link_icon do
128
+ 'icon-move'
129
+ end
130
+
131
+ register_instance_option :http_methods do
132
+ [:post]
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end