spina-admin-conferences-blog 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (136) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +42 -0
  4. data/Rakefile +34 -0
  5. data/app/assets/config/spina_admin_conferences_blog_manifest.js +0 -0
  6. data/app/controllers/spina/admin/conferences/blog/categories_controller.rb +80 -0
  7. data/app/controllers/spina/admin/conferences/blog/posts_controller.rb +103 -0
  8. data/app/decorators/spina/admin/conferences/blog/post_decorator.rb +26 -0
  9. data/app/helpers/spina/admin/conferences/blog/posts_helper.rb +18 -0
  10. data/app/models/spina/admin/conferences/blog/application_record.rb +12 -0
  11. data/app/models/spina/admin/conferences/blog/category.rb +23 -0
  12. data/app/models/spina/admin/conferences/blog/post.rb +52 -0
  13. data/app/models/spina/admin/conferences/blog.rb +10 -0
  14. data/app/views/layouts/spina/admin/conferences/blog/blog.html.haml +29 -0
  15. data/app/views/layouts/spina/admin/conferences/blog/categories.html.haml +10 -0
  16. data/app/views/spina/admin/conferences/blog/categories/_category.html.haml +8 -0
  17. data/app/views/spina/admin/conferences/blog/categories/_form.html.haml +42 -0
  18. data/app/views/spina/admin/conferences/blog/categories/edit.html.haml +3 -0
  19. data/app/views/spina/admin/conferences/blog/categories/index.html.haml +23 -0
  20. data/app/views/spina/admin/conferences/blog/categories/new.html.haml +2 -0
  21. data/app/views/spina/admin/conferences/blog/posts/_form.html.haml +43 -0
  22. data/app/views/spina/admin/conferences/blog/posts/_form_post_configuration.html.haml +50 -0
  23. data/app/views/spina/admin/conferences/blog/posts/_form_post_content.html.haml +46 -0
  24. data/app/views/spina/admin/conferences/blog/posts/_form_post_seo.html.haml +14 -0
  25. data/app/views/spina/admin/conferences/blog/posts/_post.html.haml +19 -0
  26. data/app/views/spina/admin/conferences/blog/posts/edit.html.haml +3 -0
  27. data/app/views/spina/admin/conferences/blog/posts/index.html.haml +23 -0
  28. data/app/views/spina/admin/conferences/blog/posts/new.html.haml +2 -0
  29. data/app/views/spina/admin/hooks/conferences/blog/_primary_navigation.html.haml +10 -0
  30. data/config/initializers/friendly_id.rb +94 -0
  31. data/config/locales/en.yml +41 -0
  32. data/config/locales/nl.yml +42 -0
  33. data/config/locales/pt-BR.yml +41 -0
  34. data/config/routes.rb +32 -0
  35. data/db/migrate/1_create_spina_blog_posts.rb +18 -0
  36. data/db/migrate/2_create_spina_blog_friendly_id_slugs.rb +19 -0
  37. data/db/migrate/3_create_spina_blog_categories.rb +12 -0
  38. data/db/migrate/4_add_category_id_to_spina_blog_posts.rb +7 -0
  39. data/db/migrate/5_add_featured_to_spina_blog_posts.rb +9 -0
  40. data/db/migrate/6_add_seo_fields.rb +11 -0
  41. data/lib/generators/spina_admin_conferences_blog/install_generator.rb +24 -0
  42. data/lib/spina/admin/conferences/blog/configuration.rb +17 -0
  43. data/lib/spina/admin/conferences/blog/engine.rb +24 -0
  44. data/lib/spina/admin/conferences/blog/version.rb +8 -0
  45. data/lib/spina/admin/conferences/blog.rb +18 -0
  46. data/lib/spina/admin/conferences.rb +6 -0
  47. data/lib/spina/admin.rb +5 -0
  48. data/lib/tasks/spina/admin/conferences/blog_tasks.rake +14 -0
  49. data/spec/controllers/spina/admin/conferences/blog/categories_controller_spec.rb +99 -0
  50. data/spec/controllers/spina/admin/conferences/blog/posts_controller_spec.rb +152 -0
  51. data/spec/dummy/Rakefile +8 -0
  52. data/spec/dummy/app/assets/config/manifest.js +4 -0
  53. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  54. data/spec/dummy/app/assets/javascripts/cable.js +13 -0
  55. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  56. data/spec/dummy/app/assets/stylesheets/default/application.css.sass +0 -0
  57. data/spec/dummy/app/channels/application_cable/channel.rb +6 -0
  58. data/spec/dummy/app/channels/application_cable/connection.rb +6 -0
  59. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  60. data/spec/dummy/app/helpers/application_helper.rb +4 -0
  61. data/spec/dummy/app/jobs/application_job.rb +4 -0
  62. data/spec/dummy/app/mailers/application_mailer.rb +6 -0
  63. data/spec/dummy/app/models/application_record.rb +6 -0
  64. data/spec/dummy/app/views/default/pages/homepage.html.haml +2 -0
  65. data/spec/dummy/app/views/default/pages/show.html.haml +3 -0
  66. data/spec/dummy/app/views/default/shared/_navigation.html.haml +2 -0
  67. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  68. data/spec/dummy/app/views/layouts/default/application.html.haml +11 -0
  69. data/spec/dummy/app/views/layouts/mailer.html.erb +13 -0
  70. data/spec/dummy/app/views/layouts/mailer.text.erb +1 -0
  71. data/spec/dummy/bin/bundle +5 -0
  72. data/spec/dummy/bin/rails +6 -0
  73. data/spec/dummy/bin/rake +6 -0
  74. data/spec/dummy/bin/setup +36 -0
  75. data/spec/dummy/bin/update +31 -0
  76. data/spec/dummy/config/application.rb +16 -0
  77. data/spec/dummy/config/boot.rb +7 -0
  78. data/spec/dummy/config/cable.yml +9 -0
  79. data/spec/dummy/config/database.yml +17 -0
  80. data/spec/dummy/config/environment.rb +7 -0
  81. data/spec/dummy/config/environments/development.rb +56 -0
  82. data/spec/dummy/config/environments/production.rb +88 -0
  83. data/spec/dummy/config/environments/test.rb +44 -0
  84. data/spec/dummy/config/initializers/application_controller_renderer.rb +8 -0
  85. data/spec/dummy/config/initializers/assets.rb +13 -0
  86. data/spec/dummy/config/initializers/backtrace_silencers.rb +9 -0
  87. data/spec/dummy/config/initializers/cookies_serializer.rb +7 -0
  88. data/spec/dummy/config/initializers/filter_parameter_logging.rb +6 -0
  89. data/spec/dummy/config/initializers/inflections.rb +18 -0
  90. data/spec/dummy/config/initializers/mime_types.rb +6 -0
  91. data/spec/dummy/config/initializers/mobility.rb +16 -0
  92. data/spec/dummy/config/initializers/new_framework_defaults.rb +25 -0
  93. data/spec/dummy/config/initializers/session_store.rb +5 -0
  94. data/spec/dummy/config/initializers/spina.rb +22 -0
  95. data/spec/dummy/config/initializers/themes/default.rb +32 -0
  96. data/spec/dummy/config/initializers/wrap_parameters.rb +16 -0
  97. data/spec/dummy/config/locales/en.yml +23 -0
  98. data/spec/dummy/config/puma.rb +49 -0
  99. data/spec/dummy/config/routes.rb +6 -0
  100. data/spec/dummy/config/secrets.yml +22 -0
  101. data/spec/dummy/config/spring.rb +8 -0
  102. data/spec/dummy/config.ru +7 -0
  103. data/spec/dummy/db/migrate/20190513103729_create_spina_tables.spina.rb +132 -0
  104. data/spec/dummy/db/migrate/20190513103730_create_spina_translation_tables.spina.rb +46 -0
  105. data/spec/dummy/db/migrate/20190513103731_create_spina_navigations.spina.rb +24 -0
  106. data/spec/dummy/db/migrate/20190513103732_add_password_reset_token_to_spina_users.spina.rb +9 -0
  107. data/spec/dummy/db/migrate/20190513103733_remove_translated_columns.spina.rb +14 -0
  108. data/spec/dummy/db/migrate/20190513103734_create_spina_options.spina.rb +11 -0
  109. data/spec/dummy/db/migrate/20190513103735_create_spina_settings.spina.rb +14 -0
  110. data/spec/dummy/db/migrate/20190513103736_create_spina_media_folders.spina.rb +11 -0
  111. data/spec/dummy/db/migrate/20190513103737_create_spina_images.spina.rb +13 -0
  112. data/spec/dummy/db/migrate/20190513103738_create_spina_image_collections.spina.rb +17 -0
  113. data/spec/dummy/db/migrate/20190513103739_create_spina_resources.spina.rb +18 -0
  114. data/spec/dummy/db/schema.rb +288 -0
  115. data/spec/dummy/db/seeds.rb +4 -0
  116. data/spec/dummy/public/404.html +67 -0
  117. data/spec/dummy/public/422.html +67 -0
  118. data/spec/dummy/public/500.html +66 -0
  119. data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
  120. data/spec/dummy/public/apple-touch-icon.png +0 -0
  121. data/spec/dummy/public/favicon.ico +0 -0
  122. data/spec/factories/spina/admin/conferences/blog/categories.rb +11 -0
  123. data/spec/factories/spina/admin/conferences/blog/posts.rb +16 -0
  124. data/spec/models/spina/admin/conferences/blog/category_spec.rb +21 -0
  125. data/spec/models/spina/admin/conferences/blog/post_spec.rb +39 -0
  126. data/spec/rails_helper.rb +68 -0
  127. data/spec/spec_helper.rb +97 -0
  128. data/spec/support/capybara.rb +25 -0
  129. data/spec/support/controller_helpers.rb +25 -0
  130. data/spec/support/controller_routes.rb +13 -0
  131. data/spec/support/factory_bot.rb +5 -0
  132. data/spec/support/rails_controller_testing.rb +13 -0
  133. data/spec/support/system_tests.rb +11 -0
  134. data/spec/system/spina/admin/conferences/blog/posts_spec.rb +34 -0
  135. data/spec/system/support/image.png +0 -0
  136. metadata +508 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f48bd9e0dcef8d18d11ad490f317221c5a35314ac5edbadd344eb8c7f626c82c
4
+ data.tar.gz: c9246101dc71030fbbfba1d4c7af71aa5d7d1070a699d10df445e430fdb0722c
5
+ SHA512:
6
+ metadata.gz: c38f80add14ad94be8a44041cc4cec47ea9799d25df54280ed2f53453d36ccf2a0cf025a1c1f22c390e00ed67e1caa0c05ed4f20402494286a4f88e3e32174de
7
+ data.tar.gz: c0069a54fcd156f8c50ad164b4be8013af03355c75cf803273037002d982fb2edf78feda6aed4948091e572bc2734ce057dd88e797c97cd37dba0936a0b356cd
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2017 Tom Simnett
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,42 @@
1
+ [![Build Status](https://travis-ci.org/SpinaCMS/spina-blog.svg?branch=master)](https://travis-ci.org/initforthe/spina-blog) [![Code Climate](https://codeclimate.com/github/initforthe/spina-blog/badges/gpa.svg)](https://codeclimate.com/github/initforthe/spina-blog) [![Test Coverage](https://codeclimate.com/github/initforthe/spina-blog/badges/coverage.svg)](https://codeclimate.com/github/initforthe/spina-blog/coverage)
2
+
3
+ # Spina::Blog
4
+ Simple blog engine for [Spina CMS](https://www.spinacms.com/). It supports posts and categories.
5
+
6
+ ## Usage
7
+ How to use my plugin.
8
+
9
+ ## Installation
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```ruby
13
+ gem 'spina-blog'
14
+ ```
15
+
16
+ And then execute:
17
+ ```bash
18
+ $ bundle
19
+ ```
20
+
21
+ And install:
22
+ ```bash
23
+ $ rails g spina_blog:install
24
+ ```
25
+
26
+ Or install it yourself as:
27
+ ```bash
28
+ $ gem install spina-blog
29
+ ```
30
+
31
+ ## Contributing
32
+ The version of Spina to develop this engine against is defined in the gemspec. To override the version of refinery to develop against, edit the project Gemfile to point to a local path containing a clone of Spina CMS.
33
+
34
+ ### Testing
35
+
36
+ With rake spec
37
+ ```bash
38
+ $ bundle exec rake spec
39
+ ```
40
+
41
+ ## License
42
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require 'bundler/setup'
5
+ rescue LoadError
6
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
7
+ end
8
+
9
+ require 'rdoc/task'
10
+
11
+ RDoc::Task.new(:rdoc) do |rdoc|
12
+ rdoc.rdoc_dir = 'rdoc'
13
+ rdoc.title = 'Spina::Admin::Conferences::Blog'
14
+ rdoc.options << '--line-numbers'
15
+ rdoc.rdoc_files.include('README.md')
16
+ rdoc.rdoc_files.include('lib/**/*.rb')
17
+ end
18
+
19
+ require 'bundler/gem_tasks'
20
+
21
+ APP_RAKEFILE = File.expand_path('spec/dummy/Rakefile', __dir__)
22
+ load 'rails/tasks/engine.rake'
23
+
24
+ Bundler::GemHelper.install_tasks
25
+
26
+ Dir[File.join(File.dirname(__FILE__), 'tasks/**/*.rake')].each { |f| load f }
27
+
28
+ require 'rspec/core'
29
+ require 'rspec/core/rake_task'
30
+
31
+ desc 'Run all specs in spec directory (excluding plugin specs)'
32
+ RSpec::Core::RakeTask.new(spec: 'app:db:test:prepare')
33
+
34
+ task default: :spec
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spina
4
+ module Admin
5
+ module Conferences::Blog
6
+ # Spina::Admin::Conferences::Blog::CategoriesController
7
+ class CategoriesController < AdminController
8
+ before_action :category, except: %i[new create index]
9
+ before_action :set_breadcrumb
10
+ before_action :set_locale
11
+
12
+ decorates_assigned :category
13
+
14
+ layout 'spina/admin/conferences/blog/categories'
15
+
16
+ def index
17
+ @categories = Spina::Admin::Conferences::Blog::Category.order(:name)
18
+ end
19
+
20
+ def new
21
+ @category = Spina::Admin::Conferences::Blog::Category.new
22
+ add_breadcrumb I18n.t('spina.blog.categories.new')
23
+ render layout: 'spina/admin/admin'
24
+ end
25
+
26
+ def create
27
+ @category = Spina::Admin::Conferences::Blog::Category.new category_params
28
+ if @category.save
29
+ redirect_to spina.edit_admin_conferences_blog_category_url(@category.id),
30
+ notice: t('spina.blog.categories.saved')
31
+ else
32
+ add_breadcrumb I18n.t('spina.blog.categories.new')
33
+ render :new, layout: 'spina/admin/admin'
34
+ end
35
+ end
36
+
37
+ def edit
38
+ add_breadcrumb @category.name
39
+ render layout: 'spina/admin/admin'
40
+ end
41
+
42
+ def update
43
+ if @category.update(category_params)
44
+ add_breadcrumb @category.name
45
+ redirect_to spina.edit_admin_conferences_blog_category_url(
46
+ @category.id, params: { locale: @locale }
47
+ ), notice: t('spina.blog.categories.saved')
48
+ else
49
+ render :edit, layout: 'spina/admin/admin'
50
+ end
51
+ end
52
+
53
+ def destroy
54
+ @category.destroy
55
+ redirect_to spina.admin_conferences_blog_categories_path
56
+ end
57
+
58
+ private
59
+
60
+ def set_breadcrumb
61
+ add_breadcrumb I18n.t('spina.blog.categories.name'),
62
+ spina.admin_conferences_blog_categories_path
63
+ end
64
+
65
+ def category
66
+ @category = Spina::Admin::Conferences::Blog::Category.find params[:id]
67
+ end
68
+
69
+ def set_locale
70
+ @locale = params[:locale] || I18n.default_locale
71
+ I18n.locale = @locale
72
+ end
73
+
74
+ def category_params
75
+ params.require(:category).permit(:name)
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spina
4
+ module Admin
5
+ module Conferences::Blog
6
+ # Spina::Admin::Conferences::Blog::PostsController
7
+ class PostsController < AdminController
8
+ before_action :post, except: %i[index live draft future new create]
9
+ before_action :set_breadcrumb
10
+ before_action :set_tabs, only: %i[new create edit update]
11
+ before_action :set_locale
12
+
13
+ decorates_assigned :post
14
+
15
+ layout 'spina/admin/conferences/blog/blog'
16
+
17
+ def index
18
+ @posts = Spina::Admin::Conferences::Blog::Post.order(created_at: :desc)
19
+ end
20
+
21
+ def live
22
+ @posts = Spina::Admin::Conferences::Blog::Post.live.order(created_at: :desc)
23
+ render :index
24
+ end
25
+
26
+ def draft
27
+ @posts = Spina::Admin::Conferences::Blog::Post.draft.order(created_at: :desc)
28
+ render :index
29
+ end
30
+
31
+ def future
32
+ @posts = Spina::Admin::Conferences::Blog::Post.future.order(created_at: :desc)
33
+ render :index
34
+ end
35
+
36
+ def new
37
+ @post = Spina::Admin::Conferences::Blog::Post.new
38
+ add_breadcrumb I18n.t('spina.blog.posts.new')
39
+ render layout: 'spina/admin/admin'
40
+ end
41
+
42
+ def create
43
+ @post = Spina::Admin::Conferences::Blog::Post.new post_params
44
+ if @post.save
45
+ redirect_to spina.edit_admin_conferences_blog_post_url(@post.id),
46
+ notice: t('spina.blog.posts.saved')
47
+ else
48
+ add_breadcrumb I18n.t('spina.blog.posts.new')
49
+ render :new, layout: 'spina/admin/admin'
50
+ end
51
+ end
52
+
53
+ def edit
54
+ add_breadcrumb @post.title
55
+ render layout: 'spina/admin/admin'
56
+ end
57
+
58
+ def update
59
+ if @post.update(post_params)
60
+ add_breadcrumb @post.title
61
+ redirect_to spina.edit_admin_conferences_blog_post_url(
62
+ @post.id, params: { locale: @locale }
63
+ ), notice: t('spina.blog.posts.saved')
64
+ else
65
+ render :edit, layout: 'spina/admin/admin'
66
+ end
67
+ end
68
+
69
+ def destroy
70
+ @post.destroy
71
+ redirect_to spina.admin_conferences_blog_posts_path
72
+ end
73
+
74
+ private
75
+
76
+ def post
77
+ @post = Spina::Admin::Conferences::Blog::Post.find(params[:id])
78
+ end
79
+
80
+ def set_breadcrumb
81
+ add_breadcrumb I18n.t('spina.blog.posts.title'),
82
+ spina.admin_conferences_blog_posts_path
83
+ end
84
+
85
+ def set_tabs
86
+ @tabs = %w[post_content post_configuration post_seo]
87
+ end
88
+
89
+ def set_locale
90
+ @locale = params[:locale] || I18n.default_locale
91
+ I18n.locale = @locale
92
+ end
93
+
94
+ def post_params
95
+ params.require(:post).permit(
96
+ :title, :slug, :excerpt, :content, :image_id, :draft, :published_at,
97
+ :user_id, :category_id, :featured, :seo_title, :description
98
+ )
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spina
4
+ module Admin::Conferences::Blog
5
+ # Spina::Blog::PostDecorator
6
+ class PostDecorator < Draper::Decorator
7
+ delegate_all
8
+
9
+ def published_date
10
+ l model.published_at.to_date, format: :long
11
+ end
12
+
13
+ def word_count
14
+ h.strip_tags(content).split.size
15
+ end
16
+
17
+ def image_count
18
+ Nokogiri::HTML(content).css('img').size
19
+ end
20
+
21
+ def read_time
22
+ ((word_count / (275 / 60.to_f) + (image_count * 12)) / 60).ceil
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spina
4
+ module Admin::Conferences::Blog
5
+ # Spina::Blog::PostsHelper
6
+ module PostsHelper
7
+ def formatted_date(year, month)
8
+ if month
9
+ date = Date.new year.to_i, month.to_i
10
+ date.strftime('%B %Y')
11
+ else
12
+ date = Date.new year.to_i
13
+ date.strftime('%Y')
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spina
4
+ module Admin::Conferences::Blog
5
+ # Spina::ApplicationRecord
6
+ class ApplicationRecord < ActiveRecord::Base
7
+ extend Mobility
8
+
9
+ self.abstract_class = true
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spina
4
+ module Admin::Conferences::Blog
5
+ # Spina::Blog::Category
6
+ class Category < ApplicationRecord
7
+ extend FriendlyId
8
+
9
+ friendly_id :name, use: :slugged
10
+
11
+ has_many :posts, class_name: 'Spina::Blog::Post', inverse_of: :category
12
+ validates :name, presence: true, uniqueness: { case_sensitive: false }
13
+
14
+ def to_s
15
+ name
16
+ end
17
+
18
+ def self.table_name
19
+ 'spina_blog_categories'
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spina
4
+ module Admin::Conferences::Blog
5
+ # Spina::Blog::Post
6
+ class Post < ApplicationRecord
7
+ extend FriendlyId
8
+
9
+ friendly_id :title, use: :slugged
10
+
11
+ belongs_to :image, optional: true, class_name: 'Spina::Image'
12
+
13
+ belongs_to :user
14
+ belongs_to :category, inverse_of: :posts
15
+
16
+ validates :title, :content, presence: true
17
+
18
+ before_save :set_published_at
19
+
20
+ # Create a 301 redirect if the slug changed
21
+ after_update :rewrite_rule, if: -> { saved_change_to_slug? }
22
+
23
+ scope :available, -> { where('published_at <= ?', Time.zone.now) }
24
+ scope :future, -> { where('published_at >= ?', Time.zone.now) }
25
+ scope :draft, -> { where(draft: true) }
26
+ scope :live, -> { where(draft: false) }
27
+ scope :featured, -> { where(featured: true) }
28
+ scope :unfeatured, -> { where(featured: false) }
29
+
30
+ def self.table_name
31
+ 'spina_blog_posts'
32
+ end
33
+
34
+ private
35
+
36
+ def set_published_at
37
+ self.published_at = Time.now if !draft? && published_at.blank?
38
+ end
39
+
40
+ def should_generate_new_friendly_id?
41
+ slug.blank? || draft_changed? || super
42
+ end
43
+
44
+ def rewrite_rule
45
+ ::Spina::RewriteRule.create(
46
+ old_path: "/blog/posts/#{slug_before_last_save}",
47
+ new_path: "/blog/posts/#{slug}"
48
+ )
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spina
4
+ # Spina::Blog
5
+ module Admin::Conferences::Blog
6
+ def self.table_name_prefix
7
+ 'spina_blog_'
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,29 @@
1
+ - content_for(:permanent_notice) do
2
+ - unless current_account.robots_allowed?
3
+ .permanent-notice.permanent-notice-info
4
+ = icon('search')
5
+ %p
6
+ =t 'spina.pages.invisible_to_search_engines'
7
+ %small=t 'spina.pages.invisible_to_search_engines_description'
8
+
9
+ - content_for :application do
10
+ %header#header
11
+ #header_actions
12
+ = yield(:header_actions) if content_for?(:header_actions)
13
+
14
+ .breadcrumbs= render_breadcrumbs separator: '<div class="divider"></div>'
15
+
16
+ %nav#secondary.tabs
17
+ %ul
18
+ %li{class: ('active' if controller_name == 'posts' and action_name == 'index')}
19
+ = link_to t('spina.blog.posts.all_posts'), spina.admin_conferences_blog_posts_path
20
+ %li{class: ('active' if controller_name == 'posts' and action_name == 'live')}
21
+ = link_to t('spina.blog.posts.live_posts'), spina.live_admin_conferences_blog_posts_path
22
+ %li{class: ('active' if controller_name == 'posts' and action_name == 'draft')}
23
+ = link_to t('spina.blog.posts.draft_posts'), spina.draft_admin_conferences_blog_posts_path
24
+ %li{class: ('active' if controller_name == 'posts' and action_name == 'future')}
25
+ = link_to t('spina.blog.posts.scheduled_posts'), spina.future_admin_conferences_blog_posts_path
26
+
27
+ = yield
28
+
29
+ = render template: "layouts/spina/admin/admin"
@@ -0,0 +1,10 @@
1
+ - content_for :application do
2
+ %header#header
3
+ #header_actions
4
+ = yield(:header_actions) if content_for?(:header_actions)
5
+
6
+ .breadcrumbs= render_breadcrumbs separator: '<div class="divider"></div>'
7
+
8
+ = yield
9
+
10
+ = render template: "layouts/spina/admin/admin"
@@ -0,0 +1,8 @@
1
+ %tr{data: { id: category.id }}
2
+ %td.nowrap
3
+ %h3= link_to category.name, spina.edit_admin_conferences_blog_category_path(category.id)
4
+
5
+ %td.nowrap.text-right
6
+ = link_to spina.edit_admin_conferences_blog_category_path(category.id), class: 'button button-link' do
7
+ = icon 'pencil-outline'
8
+ = t('spina.blog.categorys.edit')
@@ -0,0 +1,42 @@
1
+ - if @category.errors.any?
2
+ - content_for :notifications do
3
+ .notification.notification-danger.animated.fadeInRight
4
+ = icon('exclamation')
5
+ .notification-message
6
+ =t 'spina.notifications.alert'
7
+ %small= @category.errors.full_messages.join('<br />').html_safe
8
+ = link_to '#', data: {close_notification: true} do
9
+ = icon('cross')
10
+
11
+ %header#header
12
+ .breadcrumbs= render_breadcrumbs separator: '<div class="divider"></div>'
13
+ - unless @category.new_record?
14
+ - if Spina.config.locales.size > 1
15
+ %div{style: 'display: inline-block; margin-left: 12px; top: -4px', data: {dropdown: true}}
16
+ = link_to '#', class: 'button button-link button-round button-small', data: {trigger: 'dropdown', target: '#locales'} do
17
+ = icon('comment')
18
+ = @locale.upcase
19
+
20
+ %ul#locales
21
+ - Spina.config.locales.each do |locale|
22
+ %li
23
+ = link_to t("languages.#{locale}"), "?locale=#{locale}", style: ('font-weight: 600' if @locale.to_s == locale.to_s)
24
+
25
+ #header_actions
26
+ %button.button.button-primary{type: 'submit', style: 'margin-right: 0', data: {disable_with: t('spina.blog.categories.saving')}}
27
+ = icon('check')
28
+ =t 'spina.blog.categories.save'
29
+
30
+ = hidden_field_tag :locale, @locale
31
+
32
+ - Mobility.with_locale(@locale) do
33
+ .active.well
34
+ .horizontal-form
35
+ .horizontal-form-group
36
+ .horizontal-form-label
37
+ = Spina::Admin::Conferences::Blog::Category.human_attribute_name :name
38
+ .horizontal-form-content
39
+ = f.text_field :name, placeholder: Spina::Admin::Conferences::Blog::Category.human_attribute_name(:name_placeholder)
40
+
41
+ - unless @category.new_record?
42
+ .pull-right= link_to t('spina.permanently_delete'), spina.admin_conferences_blog_category_path(@category.id), method: :delete, data: {confirm: t('spina.blog.category.delete_confirmation', subject: @category.name)}, class: 'button button-link button-danger'
@@ -0,0 +1,3 @@
1
+ = form_for [spina, :admin, :conferences, :blog, @category], url: spina.admin_conferences_blog_category_path(@category.id),
2
+ html: {id: 'category_form', autocomplete: "off"} do |f|
3
+ = render 'form', f: f
@@ -0,0 +1,23 @@
1
+ - content_for :header_actions do
2
+ %span
3
+ = link_to spina.new_admin_conferences_blog_category_path, class: 'button button-primary', style: 'margin-right: 0' do
4
+ = icon 'plus'
5
+ = t 'spina.blog.categories.new'
6
+
7
+
8
+
9
+ .table-container
10
+ %table.table
11
+ %thead
12
+ %tr
13
+ %th Category name
14
+ %th
15
+
16
+ %tbody
17
+ - if @categories.any?
18
+ = render partial: 'category', collection: @categories, as: :category
19
+
20
+ - else
21
+ %tr
22
+ %td.align-center{colspan: 2}
23
+ %em There are no categories yet. Create your first one!
@@ -0,0 +1,2 @@
1
+ = form_for [spina, :admin, :conferences, :blog, @category], html: {id: 'category_form', autocomplete: "off"} do |f|
2
+ = render 'form', f: f
@@ -0,0 +1,43 @@
1
+ - if @post.errors.any?
2
+ - content_for :notifications do
3
+ .notification.notification-danger.animated.fadeInRight
4
+ = icon('exclamation')
5
+ .notification-message
6
+ =t 'spina.notifications.alert'
7
+ %small= @post.errors.full_messages.join('<br />').html_safe
8
+ = link_to '#', data: {close_notification: true} do
9
+ = icon('cross')
10
+
11
+ %header#header
12
+ .breadcrumbs= render_breadcrumbs separator: '<div class="divider"></div>'
13
+ - unless @post.new_record?
14
+ - if Spina.config.locales.size > 1
15
+ %div{style: 'display: inline-block; margin-left: 12px; top: -4px', data: {dropdown: true}}
16
+ = link_to '#', class: 'button button-link button-round button-small', data: {trigger: 'dropdown', target: '#locales'} do
17
+ = icon('comment')
18
+ = @locale.upcase
19
+
20
+ %ul#locales
21
+ - Spina.config.locales.each do |locale|
22
+ %li
23
+ = link_to t("languages.#{locale}"), "?locale=#{locale}", style: ('font-weight: 600' if @locale.to_s == locale.to_s)
24
+
25
+ #header_actions
26
+ %button.button.button-primary{type: 'submit', style: 'margin-right: 0', data: {disable_with: t('spina.blog.posts.saving')}}
27
+ = icon('check')
28
+ =t 'spina.blog.posts.save'
29
+
30
+ %nav#secondary.tabs
31
+ %ul
32
+ - @tabs.each_with_index do |tab, i|
33
+ %li{class: (i==0 ? 'active' : '')}
34
+ = link_to t("spina.blog.posts.#{tab}"), "##{tab}"
35
+
36
+ = hidden_field_tag :locale, @locale
37
+
38
+ - Mobility.with_locale(@locale) do
39
+ - @tabs.each do |tab|
40
+ = render "spina/admin/conferences/blog/posts/form_#{tab}", f: f
41
+
42
+ - unless @post.new_record?
43
+ .pull-right= link_to t('spina.permanently_delete'), spina.admin_conferences_blog_post_path(@post.id), method: :delete, data: {confirm: t('spina.post.delete_confirmation', subject: @post.title)}, class: 'button button-link button-danger'
@@ -0,0 +1,50 @@
1
+ #post_configuration.tab-content
2
+ .horizontal-form
3
+ %div{style: "margin: 40px 0"}
4
+
5
+ .horizontal-form-group
6
+ .horizontal-form-label
7
+ = Spina::Admin::Conferences::Blog::Post.human_attribute_name :category
8
+ .horizontal-form-content
9
+ .select-dropdown
10
+ = f.select :category_id, Spina::Admin::Conferences::Blog::Category.all.collect{|u| [u.name, u.id]},
11
+ include_blank: true, prompt: true
12
+
13
+ .horizontal-form-group
14
+ .horizontal-form-label
15
+ = Spina::Admin::Conferences::Blog::Post.human_attribute_name :draft
16
+ %small= Spina::Admin::Conferences::Blog::Post.human_attribute_name :draft_description
17
+ .horizontal-form-content
18
+ = f.check_box :draft, data: {switch: true}
19
+
20
+ .horizontal-form-group
21
+ .horizontal-form-label
22
+ = Spina::Admin::Conferences::Blog::Post.human_attribute_name :featured
23
+ %small= Spina::Admin::Conferences::Blog::Post.human_attribute_name :featured_description
24
+ .horizontal-form-content
25
+ = f.check_box :featured, data: {switch: true}
26
+
27
+ .horizontal-form-group
28
+ .horizontal-form-label
29
+ = Spina::Admin::Conferences::Blog::Post.human_attribute_name :slug
30
+ %small= Spina::Admin::Conferences::Blog::Post.human_attribute_name :slug_description
31
+ .horizontal-form-content
32
+ = f.text_field :slug, placeholder: Spina::Admin::Conferences::Blog::Post.human_attribute_name(:slug_placeholder)
33
+ - if @post.slug
34
+ %small
35
+ Current permalink:
36
+ \ /blog/posts/#{@post.slug}
37
+
38
+ .horizontal-form-group
39
+ .horizontal-form-label
40
+ = Spina::Admin::Conferences::Blog::Post.human_attribute_name :published_at
41
+ .horizontal-form-content
42
+ = f.datetime_field :published_at
43
+
44
+ .horizontal-form-group
45
+ .horizontal-form-label
46
+ = Spina::Admin::Conferences::Blog::Post.human_attribute_name :spina_user
47
+ .horizontal-form-content
48
+ .select-dropdown
49
+ = f.select :user_id, Spina::User.all.collect{|u| [u.name, u.id]},
50
+ include_blank: true, prompt: true