character 0.1.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -1
  3. data/.rspec +1 -0
  4. data/README.md +185 -14
  5. data/Rakefile +8 -1
  6. data/app/assets/images/character/logo.jpg +0 -0
  7. data/app/assets/javascripts/character.coffee +134 -0
  8. data/app/assets/javascripts/character/dashboard/_visitors.coffee +27 -0
  9. data/app/assets/javascripts/character/dashboard/layout.coffee +156 -0
  10. data/app/assets/javascripts/character/dashboard/module.coffee +51 -0
  11. data/app/assets/javascripts/character/generic/details.coffee +233 -0
  12. data/app/assets/javascripts/character/generic/helpers/compact_object.coffee +7 -0
  13. data/app/assets/javascripts/character/generic/helpers/data_inputs.coffee +21 -0
  14. data/app/assets/javascripts/character/generic/helpers/date_select.coffee +45 -0
  15. data/app/assets/javascripts/character/generic/helpers/editor.coffee +11 -0
  16. data/app/assets/javascripts/character/generic/helpers/redactor.coffee +38 -0
  17. data/app/assets/javascripts/character/generic/helpers/reorder.coffee +36 -0
  18. data/app/assets/javascripts/character/generic/layout.coffee +40 -0
  19. data/app/assets/javascripts/character/generic/list.coffee +214 -0
  20. data/app/assets/javascripts/character/generic/model.coffee +135 -0
  21. data/app/assets/javascripts/character/generic/module.coffee +157 -0
  22. data/app/assets/javascripts/character/images/module.coffee +148 -0
  23. data/app/assets/javascripts/character/pages/module.coffee +43 -0
  24. data/app/assets/javascripts/character/posts/module.coffee +113 -0
  25. data/app/assets/javascripts/character/settings/_admins.coffee +61 -0
  26. data/app/assets/javascripts/character/settings/_authors.coffee +56 -0
  27. data/app/assets/javascripts/character/settings/_categories.coffee +61 -0
  28. data/app/assets/javascripts/character/settings/_layout.coffee +7 -0
  29. data/app/assets/javascripts/character/settings/_redirects.coffee +56 -0
  30. data/app/assets/javascripts/character/settings/_website.coffee +7 -0
  31. data/app/assets/javascripts/character/settings/details.coffee +16 -0
  32. data/app/assets/javascripts/character/settings/layout.coffee +46 -0
  33. data/app/assets/javascripts/character/settings/module.coffee +78 -0
  34. data/app/assets/stylesheets/character.scss +37 -0
  35. data/app/assets/stylesheets/character/_admins.scss +30 -0
  36. data/app/assets/stylesheets/character/_authors.scss +30 -0
  37. data/app/assets/stylesheets/character/_categories.scss +32 -0
  38. data/app/assets/stylesheets/character/_dashboard.scss +143 -0
  39. data/app/assets/stylesheets/character/_posts.scss +93 -0
  40. data/app/assets/stylesheets/character/_redirects.scss +35 -0
  41. data/app/assets/stylesheets/character/base.scss +967 -0
  42. data/app/assets/stylesheets/character/typography.scss +29 -0
  43. data/app/controllers/character/api_controller.rb +170 -0
  44. data/app/controllers/character/application_controller.rb +37 -0
  45. data/app/controllers/character/settings_controller.rb +72 -0
  46. data/app/controllers/concerns/character/auth_concern.rb +41 -0
  47. data/app/controllers/concerns/character/instance_concern.rb +31 -0
  48. data/app/controllers/concerns/character/json_object_concern.rb +32 -0
  49. data/app/controllers/concerns/character/model_class_concern.rb +28 -0
  50. data/app/controllers/concerns/character/params_concern.rb +33 -0
  51. data/app/controllers/concerns/character/templates_concern.rb +32 -0
  52. data/app/controllers/concerns/not_found.rb +18 -0
  53. data/app/controllers/concerns/website_settings.rb +18 -0
  54. data/app/controllers/pages_controller.rb +8 -0
  55. data/app/controllers/posts_controller.rb +43 -0
  56. data/app/helpers/character_helper.rb +8 -0
  57. data/app/helpers/page_helper.rb +67 -0
  58. data/app/inputs/foundation_string_input.rb +44 -0
  59. data/app/inputs/foundation_switch_input.rb +35 -0
  60. data/app/models/character/image.rb +12 -0
  61. data/app/models/character/page.rb +21 -0
  62. data/app/models/character/post.rb +32 -12
  63. data/app/models/character/post_author.rb +22 -0
  64. data/app/models/character/post_category.rb +21 -0
  65. data/app/models/character/redirect.rb +15 -0
  66. data/app/models/character/settings/variable.rb +23 -0
  67. data/app/models/character/sitemap/sitemap_generator_helper.rb +15 -0
  68. data/app/models/character/user.rb +29 -0
  69. data/app/models/concerns/created_ago.rb +12 -0
  70. data/app/models/concerns/hideable.rb +27 -0
  71. data/app/models/concerns/orderable.rb +8 -0
  72. data/app/models/concerns/report.rb +11 -0
  73. data/app/models/concerns/report_daily.rb +32 -0
  74. data/app/models/concerns/report_monthly.rb +18 -0
  75. data/app/models/concerns/report_weekly.rb +19 -0
  76. data/app/models/concerns/updated_ago.rb +12 -0
  77. data/app/models/reports/analytics_daily.rb +26 -0
  78. data/app/models/reports/analytics_monthly.rb +16 -0
  79. data/app/models/reports/analytics_weekly.rb +16 -0
  80. data/app/services/google_analytics.rb +43 -0
  81. data/app/uploaders/character/image_uploader.rb +22 -0
  82. data/app/uploaders/character/settings/file_uploader.rb +5 -0
  83. data/app/views/character/character.html.erb +67 -0
  84. data/app/views/character/generic/form.html.erb +8 -0
  85. data/app/views/character/pages/form.html.erb +28 -0
  86. data/app/views/character/posts/form.html.erb +38 -0
  87. data/app/views/character/settings/admins.html.erb +29 -0
  88. data/app/views/character/settings/post_authors.html.erb +28 -0
  89. data/app/views/character/settings/post_categories.html.erb +31 -0
  90. data/app/views/character/settings/redirects.html.erb +30 -0
  91. data/app/views/character/settings/settings_group.html.erb +67 -0
  92. data/app/views/errors/not_found.html.erb +157 -0
  93. data/app/views/pages/_default.html.erb +3 -0
  94. data/app/views/pages/_redactor.html.erb +3 -0
  95. data/app/views/pages/show.html.erb +5 -0
  96. data/app/views/posts/_post.html.erb +17 -0
  97. data/app/views/posts/author.html.erb +18 -0
  98. data/app/views/posts/category.html.erb +18 -0
  99. data/app/views/posts/index.html.erb +18 -0
  100. data/app/views/posts/rss.builder +19 -0
  101. data/app/views/posts/show.html.erb +14 -0
  102. data/app/views/shared/_google_analytics.html.erb +13 -0
  103. data/character.gemspec +48 -5
  104. data/doc/README_old.md +161 -0
  105. data/doc/generic_app.md +19 -0
  106. data/doc/img/demo-1.jpg +0 -0
  107. data/doc/img/demo-2.jpg +0 -0
  108. data/doc/img/demo-3.jpg +0 -0
  109. data/doc/img/demo-4.jpg +0 -0
  110. data/doc/img/demo-5.jpg +0 -0
  111. data/doc/instances.md +39 -0
  112. data/doc/settings.md +1 -0
  113. data/lib/character.rb +29 -1
  114. data/lib/character/engine.rb +33 -1
  115. data/lib/character/generators/bootstrap_generator.rb +51 -0
  116. data/lib/character/instance.rb +59 -0
  117. data/lib/character/routing.rb +42 -5
  118. data/lib/character/settings.rb +101 -0
  119. data/lib/character/templates/admin.coffee +15 -0
  120. data/lib/character/templates/admin.scss +3 -0
  121. data/lib/character/templates/application.html.erb +44 -0
  122. data/lib/character/templates/application.scss +12 -0
  123. data/lib/character/templates/assets.rb +1 -0
  124. data/lib/character/templates/initializer.rb +5 -0
  125. data/lib/character/templates/settings.scss +11 -0
  126. data/lib/character/templates/settings.yml +67 -0
  127. data/lib/character/templates/typography.scss +13 -0
  128. data/lib/character/version.rb +2 -2
  129. data/lib/mongoid/carrierwave_serialization_patch.rb +9 -0
  130. data/lib/tasks/analytics.rake +52 -0
  131. data/test/config/application.rb +65 -0
  132. data/test/config/mongoid.yml +12 -0
  133. data/test/config/secrets.yml +22 -0
  134. data/test/controllers/character/api_controller_test.rb +94 -0
  135. data/test/factories/product_factory.rb +5 -0
  136. data/test/lib/character/engine_test.rb +33 -0
  137. data/test/lib/character/routing_test.rb +31 -0
  138. data/test/test_helper.rb +48 -0
  139. data/vendor/assets/javascripts/backbone.js +944 -794
  140. data/vendor/assets/javascripts/jquery.fileupload.js +1426 -0
  141. data/vendor/assets/javascripts/jquery.form.js +1278 -0
  142. data/vendor/assets/javascripts/jquery.iframe-transport.js +214 -0
  143. data/vendor/assets/javascripts/raphael.js +8117 -0
  144. data/vendor/assets/javascripts/raphael.morris.js +1885 -0
  145. data/vendor/assets/javascripts/underscore.inflection.js +177 -0
  146. data/vendor/assets/javascripts/underscore.string.js +1 -1
  147. data/vendor/assets/stylesheets/csspinner.css +361 -0
  148. data/vendor/assets/stylesheets/normalize.css +423 -0
  149. metadata +499 -49
  150. data/app/controllers/character/posts_controller.rb +0 -27
  151. data/lib/generators/character/install_generator.rb +0 -42
  152. data/lib/generators/character/templates/README +0 -1
  153. data/lib/generators/character/templates/admin/character.rb +0 -3
  154. data/vendor/assets/fonts/general_foundicons.eot +0 -0
  155. data/vendor/assets/fonts/general_foundicons.svg +0 -15
  156. data/vendor/assets/fonts/general_foundicons.ttf +0 -0
  157. data/vendor/assets/fonts/general_foundicons.woff +0 -0
  158. data/vendor/assets/javascripts/character/index.js.coffee +0 -53
  159. data/vendor/assets/javascripts/character/models/post.js.coffee +0 -39
  160. data/vendor/assets/javascripts/character/views/app.js.coffee +0 -81
  161. data/vendor/assets/javascripts/character/views/editor.js.coffee +0 -231
  162. data/vendor/assets/javascripts/character/views/editor_settings.js.coffee +0 -44
  163. data/vendor/assets/javascripts/character/views/index.js.coffee +0 -116
  164. data/vendor/assets/javascripts/character/views/preview.js.coffee +0 -49
  165. data/vendor/assets/javascripts/jquery.smartresize.js +0 -30
  166. data/vendor/assets/javascripts/lodash.js +0 -4258
  167. data/vendor/assets/javascripts/showdown.js +0 -62
  168. data/vendor/assets/stylesheets/character/_base.css.scss +0 -84
  169. data/vendor/assets/stylesheets/character/_icons.css.scss.erb +0 -96
  170. data/vendor/assets/stylesheets/character/_view_editor.css.scss +0 -115
  171. data/vendor/assets/stylesheets/character/_view_index.css.scss +0 -73
  172. data/vendor/assets/stylesheets/character/_view_preview.css.scss +0 -49
  173. data/vendor/assets/stylesheets/character/index.css.scss +0 -32
@@ -0,0 +1,33 @@
1
+ module Character::ParamsConcern
2
+ extend ActiveSupport::Concern
3
+
4
+ private
5
+
6
+ def attr_name_or_map(attr_name, val)
7
+ # RECURSION is used to map all hashes in params to update nested documents
8
+ if val.is_a?(Hash)
9
+ map = {} ; map[attr_name] = []
10
+
11
+ if val.first[0] == val.first[0].to_i.to_s # check if key is an integer which means we have an array of nested objects
12
+ val.first[1].each { |arr_value_key, arr_value_value| map[attr_name] << attr_name_or_map(arr_value_key, arr_value_value) }
13
+ else
14
+ val.each { |hsh_key, hsh_value| map[attr_name] << attr_name_or_map(hsh_key, hsh_value) }
15
+ end
16
+
17
+ return map
18
+ elsif val.is_a?(Array)
19
+ map = {} ; map[attr_name] = []
20
+ return map
21
+ else
22
+ return attr_name
23
+ end
24
+ end
25
+
26
+ def permit_params(namespace='api')
27
+ permit_fields = []
28
+ params[namespace].each do |key, value|
29
+ permit_fields << attr_name_or_map(key, value)
30
+ end
31
+ params.require(namespace).permit(permit_fields)
32
+ end
33
+ end
@@ -0,0 +1,32 @@
1
+ module Character::TemplatesConcern
2
+ extend ActiveSupport::Concern
3
+
4
+ def form_template
5
+ @form_template ||= begin
6
+ template_folder = model_class.name.underscore.to_s.pluralize
7
+ template_folder.gsub!('character/', '')
8
+
9
+ generic_template_folder = "character/#{ template_folder }"
10
+ instance_template_folder = "#{ character_instance.name }/#{ template_folder }"
11
+
12
+ if template_exists?("form", instance_template_folder, false)
13
+ "#{ instance_template_folder }/form"
14
+ elsif template_exists?("form", generic_template_folder, false)
15
+ "#{ generic_template_folder }/form"
16
+ else
17
+ @generic_form_fields = model_class.fields.keys - %w( _id _type created_at _position _keywords updated_at deleted_at )
18
+ "character/generic/form"
19
+ end
20
+ end
21
+ end
22
+
23
+ def form_action_url(object)
24
+ form_attributes_namespace()
25
+
26
+ if object.persisted?
27
+ "/#{ character_instance.name }/#{ model_slug }/#{ object.id }"
28
+ else
29
+ "/#{ character_instance.name }/#{ model_slug }"
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,18 @@
1
+ module NotFound
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ rescue_from Mongoid::Errors::DocumentNotFound do |exception|
6
+ redirect_or_not_found()
7
+ end
8
+
9
+ def redirect_or_not_found
10
+ redirect = Character::Redirect.where(path: request.path).first
11
+ if redirect
12
+ redirect_to redirect.destination, status: redirect.type
13
+ else
14
+ raise ActionController::RoutingError.new('Not Found')
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ module WebsiteSettings
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ before_filter :set_website_settings
6
+
7
+ def set_website_settings
8
+ @domain = ::Settings.value("Website::Domain")
9
+ @title = ::Settings.value("Website::Title")
10
+ @description = ::Settings.value("Website::Description")
11
+ @ga_id = ::Settings.value("Website::Google Analytics ID")
12
+ @ga_domain = @domain.gsub('www.', '')
13
+
14
+ @header_html = ::Settings.value("Layout::Header")
15
+ @footer_html = ::Settings.value("Layout::Footer")
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,8 @@
1
+ class PagesController < ApplicationController
2
+ include WebsiteSettings
3
+ include NotFound
4
+
5
+ def show
6
+ @object = Character::Page.find_by(path: '/' + (params[:path] || ''))
7
+ end
8
+ end
@@ -0,0 +1,43 @@
1
+ class PostsController < ApplicationController
2
+ include WebsiteSettings
3
+ include NotFound
4
+
5
+ def index
6
+ page = params[:page] || 1
7
+ @search = params[:q] || ''
8
+ @kaminari_params = @search.empty? ? {} : { q: @search }
9
+
10
+ @posts = Character::Post.published
11
+ @posts = @posts.search(@search) if not @search.empty?
12
+ @posts = @posts.page(page).per(10)
13
+ end
14
+
15
+ def author
16
+ page = params[:page] || 1
17
+
18
+ @author = Character::PostAuthor.find(params[:slug])
19
+ @posts = @author.posts.published
20
+ @posts = @posts.page(page).per(10)
21
+ end
22
+
23
+ def category
24
+ page = params[:page] || 1
25
+
26
+ @category = Character::PostCategory.find(params[:slug])
27
+ @posts = @category.posts.published
28
+ @posts = @posts.page(page).per(10)
29
+ end
30
+
31
+ def show
32
+ @post = Character::Post.find(params[:slug])
33
+ end
34
+
35
+ def rss
36
+ page = params[:page] || 1
37
+ @posts = Character::Post.published.page(page).per(10)
38
+
39
+ respond_to do |format|
40
+ format.all { render :layout => false, content_type: 'text/xml; charset=utf-8' }
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,8 @@
1
+ module CharacterHelper
2
+ def page_templates()
3
+ app_pages_templates = Dir.glob('app/views/pages/_*.html.erb').collect do |t|
4
+ t.split('/_').last.gsub('.html.erb', '')
5
+ end
6
+ app_pages_templates + %w( redactor default )
7
+ end
8
+ end
@@ -0,0 +1,67 @@
1
+ module PageHelper
2
+ def inline_editable(template_content_key, placeholder='', default_content='')
3
+ if @object
4
+ if placeholder.empty?
5
+ placeholder = template_content_key + '...'
6
+ end
7
+
8
+ page = @object
9
+ class_underscore = @object.class.name.underscore.gsub('/', '_')
10
+
11
+ content = page.template_content[template_content_key]
12
+ content ||= default_content
13
+
14
+ if @form_action_url
15
+ """<span class='character-editor'
16
+ data-input-name='#{class_underscore}[template_content][#{ template_content_key }]'
17
+ data-options=\"placeholder:'#{ placeholder }';disableReturn:true;disableToolbar:true;\"
18
+ >#{ content }</span>""".html_safe
19
+ else
20
+ content.html_safe
21
+ end
22
+ end
23
+ end
24
+
25
+ def editable(template_content_key, placeholder='', default_content='')
26
+ if @object
27
+ if placeholder.empty?
28
+ placeholder = template_content_key + '...'
29
+ end
30
+
31
+ page = @object
32
+ class_underscore = @object.class.name.underscore.gsub('/', '_')
33
+
34
+ content = page.template_content[template_content_key]
35
+ content ||= default_content
36
+
37
+ if @form_action_url
38
+ """<div class='character-editor'
39
+ data-input-name='#{class_underscore}[template_content][#{ template_content_key }]'
40
+ data-options=\"placeholder:'#{ placeholder }';targetBlanks:true;\"
41
+ >#{ content }</div>""".html_safe
42
+ else
43
+ content.html_safe
44
+ end
45
+ end
46
+ end
47
+
48
+ def redactor(template_content_key, default_content='')
49
+ if @object
50
+ page = @object
51
+ class_underscore = @object.class.name.underscore.gsub('/', '_')
52
+
53
+ content = page.template_content[template_content_key]
54
+ content ||= default_content
55
+
56
+ if @form_action_url
57
+ """<textarea id='redactor' class='character-redactor' placeholder='Type text here...'>#{ content }</textarea>""".html_safe
58
+ else
59
+ content.html_safe
60
+ end
61
+ end
62
+ end
63
+
64
+ def settings(group, name)
65
+ ::Settings.group(group)[name].value
66
+ end
67
+ end
@@ -0,0 +1,44 @@
1
+ class FoundationStringInput < SimpleForm::Inputs::StringInput
2
+ def input
3
+ unless string?
4
+ input_html_classes.unshift("string")
5
+ input_html_options[:type] ||= :text if html5?
6
+ end
7
+
8
+ html = @builder.text_field(attribute_name, input_html_options)
9
+
10
+ options = input_options
11
+
12
+ prefix_columns = 0
13
+ postfix_columns = 0
14
+
15
+ if options[:prefix]
16
+ prefix_label = options[:prefix][:label]
17
+ prefix_columns = options[:prefix][:columns] || 0
18
+ end
19
+
20
+ if options[:postfix]
21
+ postfix_label = options[:postfix][:label]
22
+ postfix_columns = options[:postfix][:columns] || 0
23
+ end
24
+
25
+ input_columns = 12 - prefix_columns - postfix_columns
26
+
27
+ prefix_html = ""
28
+ postfix_html = ""
29
+
30
+ if prefix_label
31
+ prefix_html = "<div class='small-#{prefix_columns} columns'>
32
+ <span class='prefix'>#{prefix_label}</span>
33
+ </div>"
34
+ end
35
+
36
+ if postfix_label
37
+ postfix_html = "<div class='small-#{postfix_columns} columns'>
38
+ <span class='postfix'>#{postfix_label}</span>
39
+ </div>"
40
+ end
41
+
42
+ "<div class='row collapse'>#{prefix_html}<div class='small-#{input_columns} columns'>#{ html }</div>#{postfix_html}</div>".html_safe
43
+ end
44
+ end
@@ -0,0 +1,35 @@
1
+ class FoundationSwitchInput < SimpleForm::Inputs::CollectionRadioButtonsInput
2
+ def input
3
+ options = input_options
4
+
5
+ # do not wrap items in tags
6
+ options[:item_wrapper_tag] = false
7
+
8
+ # setting custom labels if provided
9
+ if options[:labels]
10
+ collection = [false, true].zip(options[:labels])
11
+ else
12
+ collection = [[false, 'No'], [true, 'Yes']]
13
+ end
14
+
15
+ # internal helpers to worki with collection
16
+ label_method = :last
17
+ value_method = :first
18
+
19
+ html = @builder.send("collection_radio_buttons",
20
+ attribute_name, collection, value_method, label_method,
21
+ options, input_html_options, &collection_block_for_nested_boolean_style
22
+ )
23
+
24
+ # wrap html into foundation layout
25
+
26
+ "<div class='switch round'>#{ html }<span></span></div>"
27
+ end
28
+
29
+ protected
30
+
31
+ # disable label input
32
+ def label_input
33
+ input
34
+ end
35
+ end
@@ -0,0 +1,12 @@
1
+ class Character::Image
2
+ include Mongoid::Document
3
+ include Mongoid::Timestamps
4
+
5
+ mount_uploader :image, Character::ImageUploader
6
+
7
+ # scopes
8
+ default_scope -> { order_by(created_at: :desc) }
9
+
10
+ # indexes
11
+ index({ created_at: 1, date: -1 })
12
+ end
@@ -0,0 +1,21 @@
1
+ class Character::Page
2
+ include Mongoid::Document
3
+ include Mongoid::Timestamps
4
+ include UpdatedAgo
5
+ include Orderable
6
+ include Hideable
7
+
8
+ # attributes
9
+ field :title
10
+ field :path
11
+ field :template_name, default: 'redactor'
12
+ field :template_content, type: Hash, default: {}
13
+
14
+ # indexes
15
+ index({ path: 1 })
16
+
17
+ # helpers
18
+ def template_path
19
+ "pages/#{ template_name }"
20
+ end
21
+ end
@@ -1,20 +1,40 @@
1
+ # encoding: UTF-8
1
2
  class Character::Post
2
3
  include Mongoid::Document
3
4
  include Mongoid::Timestamps
5
+ include Mongoid::Slug
6
+ include Mongoid::Search
7
+ include UpdatedAgo
8
+ include Hideable
4
9
 
5
- field :title
6
- field :published, type: Boolean, default: false
7
- field :date, type: Date
8
- field :slug
9
- field :md
10
- field :html
11
- field :views, type: Integer, default: 0
10
+ # attributes
11
+ field :title, default: ''
12
+ field :subtitle, default: ''
13
+ field :body_html, default: ''
12
14
 
13
- #featured_image
14
- #category
15
+ field :featured_image, type: Hash, default: { 'url' => '' }
16
+ field :published_at, type: DateTime, default: -> { DateTime.now }
15
17
 
16
- default_scope order_by date: :desc
18
+ # relations
19
+ belongs_to :category, class_name: "Character::PostCategory"
20
+ belongs_to :author, class_name: "Character::PostAuthor"
17
21
 
18
- scope :drafts, where(published: false)
19
- scope :published, where(published: true)
22
+ # slugs and search
23
+ slug :title, history: true
24
+ search_in :title, :body_html, :category => :title
25
+
26
+ # scopes
27
+ default_scope -> { order_by(published_at: :desc) }
28
+ scope :published, -> { where(hidden: false).lte(published_at: DateTime.now) }
29
+ scope :scheduled, -> { where(hidden: false).gt(published_at: DateTime.now) }
30
+ scope :drafts, -> { where(hidden: true) }
31
+
32
+ # indexes
33
+ index({ slug: 1 })
34
+ index({ hidden: 1, published_at: -1 })
35
+
36
+ # helpers
37
+ def featured_image_url
38
+ featured_image ? featured_image['url'] : ''
39
+ end
20
40
  end
@@ -0,0 +1,22 @@
1
+ # encoding: UTF-8
2
+ class Character::PostAuthor
3
+ include Mongoid::Document
4
+ include Mongoid::Timestamps
5
+ include Mongoid::Slug
6
+ include Mongoid::Attributes::Dynamic # required to remove users using _delete field
7
+
8
+ # attributes
9
+ field :name
10
+ field :title
11
+ field :email
12
+ field :url
13
+
14
+ # relations
15
+ has_many :posts, class_name: 'Character::Post'
16
+
17
+ # slugs
18
+ slug :name, history: true
19
+
20
+ # indexes
21
+ index({ slug: 1 })
22
+ end
@@ -0,0 +1,21 @@
1
+ # encoding: UTF-8
2
+ class Character::PostCategory
3
+ include Mongoid::Document
4
+ include Mongoid::Timestamps
5
+ include Mongoid::Slug
6
+ include Mongoid::Attributes::Dynamic # required to remove users using _delete field
7
+ include Orderable
8
+
9
+ # attributes
10
+ field :title
11
+
12
+ # relations
13
+ has_many :posts, class_name: 'Character::Post'
14
+
15
+ # slugs
16
+ slug :title, history: true
17
+
18
+ # indexes
19
+ index({ slug: 1 })
20
+ index({ _position: -1 })
21
+ end