rails_cms 0.0.1

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 (55) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +20 -0
  3. data/README.md +28 -0
  4. data/Rakefile +32 -0
  5. data/app/controllers/cms/admin/audios_controller.rb +24 -0
  6. data/app/controllers/cms/admin/base_controller.rb +5 -0
  7. data/app/controllers/cms/admin/videos_controller.rb +30 -0
  8. data/app/controllers/cms/api/audios_controller.rb +16 -0
  9. data/app/controllers/cms/api/base_controller.rb +5 -0
  10. data/app/controllers/cms/api/video_tags_controller.rb +40 -0
  11. data/app/controllers/cms/api/video_taxons_controller.rb +18 -0
  12. data/app/controllers/cms/api/videos_controller.rb +119 -0
  13. data/app/controllers/cms/base_controller.rb +5 -0
  14. data/app/controllers/cms/my/base_controller.rb +5 -0
  15. data/app/jobs/cms/video_wm_job.rb +9 -0
  16. data/app/models/audio.rb +5 -0
  17. data/app/models/cms/ext/user.rb +24 -0
  18. data/app/models/cms/model/audio.rb +25 -0
  19. data/app/models/cms/model/progression.rb +18 -0
  20. data/app/models/cms/model/video.rb +128 -0
  21. data/app/models/cms/post_tag.rb +3 -0
  22. data/app/models/cms/video_tag.rb +3 -0
  23. data/app/models/cms/video_taxon.rb +3 -0
  24. data/app/models/progression.rb +5 -0
  25. data/app/models/video.rb +9 -0
  26. data/app/views/cms/admin/audios/_filter.html.erb +10 -0
  27. data/app/views/cms/admin/audios/_form.html.erb +6 -0
  28. data/app/views/cms/admin/audios/edit.html.erb +9 -0
  29. data/app/views/cms/admin/audios/index.html.erb +47 -0
  30. data/app/views/cms/admin/audios/new.html.erb +9 -0
  31. data/app/views/cms/admin/audios/show.html.erb +22 -0
  32. data/app/views/cms/admin/base/_nav.html.erb +16 -0
  33. data/app/views/cms/admin/videos/_form.html.erb +8 -0
  34. data/app/views/cms/admin/videos/_search_form.html.erb +6 -0
  35. data/app/views/cms/admin/videos/edit.html.erb +9 -0
  36. data/app/views/cms/admin/videos/index.html.erb +62 -0
  37. data/app/views/cms/admin/videos/new.html.erb +9 -0
  38. data/app/views/cms/admin/videos/show.html.erb +22 -0
  39. data/app/views/cms/api/audios/_audio.json.jbuilder +4 -0
  40. data/app/views/cms/api/audios/index.json.jbuilder +2 -0
  41. data/app/views/cms/api/videos/_detail.json.jbuilder +8 -0
  42. data/app/views/cms/api/videos/_video.json.jbuilder +30 -0
  43. data/app/views/cms/api/videos/index.json.jbuilder +36 -0
  44. data/app/views/cms/api/videos/list.json.jbuilder +9 -0
  45. data/app/views/cms/api/videos/show.json.jbuilder +1 -0
  46. data/config/locales/en.enum.yml +5 -0
  47. data/config/locales/en.notify.yml +7 -0
  48. data/config/locales/zh.enum.yml +9 -0
  49. data/config/locales/zh.notify.yml +7 -0
  50. data/config/routes.rb +23 -0
  51. data/db/migrate/20181203082644_rails_media_init.rb +34 -0
  52. data/lib/rails_cms/config.rb +11 -0
  53. data/lib/rails_cms/engine.rb +10 -0
  54. data/lib/rails_cms.rb +2 -0
  55. metadata +110 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 152a4ad0bd6718e7a70770a5a3d5857ee5f0b4f53acd4d186454c39503a7ebfe
4
+ data.tar.gz: 646a8a77932b4733ec48bfb142475c490923350e0e5d946a7028c73c5d95f611
5
+ SHA512:
6
+ metadata.gz: d9335916cfdf3e2123ec96c576592b83871efd4d2c4274ab8af896175ede6e2c28da7a6409cb92a91d371613b07851e2b73fb948ef174df7b2b071a8315ce65f
7
+ data.tar.gz: 054cd5a4e85dd268b02639e35e85a9a58054771bb2cea8d4a8af8392cd374feca0bc5b8dcefb453681b9124b3e2c25cada32ef1760b8c6b2d14ca64807b6fb7e
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2019-Present Mingyuan Qin <mingyuan0715@foxmail.com>
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,28 @@
1
+ # RailsCms
2
+ Short description and motivation.
3
+
4
+ ## Usage
5
+ How to use my plugin.
6
+
7
+ ## Installation
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'rails_cms'
12
+ ```
13
+
14
+ And then execute:
15
+ ```bash
16
+ $ bundle
17
+ ```
18
+
19
+ Or install it yourself as:
20
+ ```bash
21
+ $ gem install rails_cms
22
+ ```
23
+
24
+ ## Contributing
25
+ Contribution directions go here.
26
+
27
+ ## License
28
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,32 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'RailsCms'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
18
+ load 'rails/tasks/engine.rake'
19
+
20
+ load 'rails/tasks/statistics.rake'
21
+
22
+ require 'bundler/gem_tasks'
23
+
24
+ require 'rake/testtask'
25
+
26
+ Rake::TestTask.new(:test) do |t|
27
+ t.libs << 'test'
28
+ t.pattern = 'test/**/*_test.rb'
29
+ t.verbose = false
30
+ end
31
+
32
+ task default: :test
@@ -0,0 +1,24 @@
1
+ module Cms
2
+ class Admin::AudiosController < Admin::BaseController
3
+ before_action :set_audio, only: [:show, :edit, :update, :destroy]
4
+
5
+ def index
6
+ @audios = Audio.page(params[:page])
7
+ end
8
+
9
+ private
10
+ def set_audio
11
+ @audio = Audio.find(params[:id])
12
+ end
13
+
14
+ def audio_params
15
+ params.fetch(:audio, {}).permit(
16
+ :title,
17
+ :state,
18
+ :author_id,
19
+ :media
20
+ )
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,5 @@
1
+ module Cms
2
+ class Admin::BaseController < AdminController
3
+
4
+ end
5
+ end
@@ -0,0 +1,30 @@
1
+ module Cms
2
+ class Admin::VideosController < Admin::BaseController
3
+ before_action :set_video, only: [:show, :edit, :update, :destroy]
4
+
5
+ def index
6
+ q_params = {}.with_indifferent_access
7
+ q_params.merge! params.permit(:author_id)
8
+ @videos = Video.default_where(q_params).order(id: :desc).page(params[:page])
9
+ end
10
+
11
+ private
12
+ def set_video
13
+ @video = Video.find(params[:id])
14
+ end
15
+
16
+ def video_params
17
+ q = params.fetch(:video, {}).permit(
18
+ :title,
19
+ :state,
20
+ :media,
21
+ :cover,
22
+ :author_id,
23
+ :video_taxon_id,
24
+ )
25
+ q.merge!(author_id: current_user.id) if q[:author_id].blank?
26
+ q
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,16 @@
1
+ class Media::Api::AudiosController < Media::Api::BaseController
2
+ before_action :set_audio, only: [:show, :update, :destroy]
3
+
4
+ def index
5
+ @audios = Audio.order(id: :desc).page(params[:page])
6
+ end
7
+
8
+ private
9
+ def set_audio
10
+ @audio = Audio.find(params[:id])
11
+ end
12
+
13
+ def audio_params
14
+ params.fetch(:audio, {})
15
+ end
16
+ end
@@ -0,0 +1,5 @@
1
+ class Media::Api::BaseController < RailsMedia.config.api_class.constantize
2
+
3
+
4
+
5
+ end
@@ -0,0 +1,40 @@
1
+ class Media::Api::VideoTagsController < Media::Api::BaseController
2
+ before_action :set_video_tag, only: [:show, :update]
3
+
4
+ def index
5
+ @video_tags = VideoTag.page(params[:page]).per(params[:per])
6
+
7
+ render json: { video_tags: @video_tags.as_json(only: [:id, :name]) }
8
+ end
9
+
10
+ def show
11
+ render json: @video_tag
12
+ end
13
+
14
+ def create
15
+ @video_tag = VideoTag.new(video_tag_params)
16
+
17
+ if @video_tag.save
18
+ render json: @video_tag, status: :created
19
+ else
20
+ render json: @video_tag.errors, status: :unprocessable_entity
21
+ end
22
+ end
23
+
24
+ def update
25
+ if @video_tag.update(video_tag_params)
26
+ render json: @video_tag
27
+ else
28
+ render json: @video_tag.errors, status: :unprocessable_entity
29
+ end
30
+ end
31
+
32
+ private
33
+ def set_video_tag
34
+ @video_tag = VideoTag.find(params[:id])
35
+ end
36
+
37
+ def video_tag_params
38
+ params.fetch(:video_tag, {}).permit(:name)
39
+ end
40
+ end
@@ -0,0 +1,18 @@
1
+ class Media::Api::VideoTaxonsController < Media::Api::BaseController
2
+ before_action :set_video_taxon, only: [:show]
3
+
4
+ def index
5
+ @video_taxons = VideoTaxon.page(params[:page])
6
+
7
+ render json: { video_taxons: @video_taxons.as_json(only: [:id, :name, :position]) }
8
+ end
9
+
10
+ def show
11
+ render json: @video_taxon
12
+ end
13
+
14
+ private
15
+ def set_video_taxon
16
+ @video_taxon = VideoTaxon.find(params[:id])
17
+ end
18
+ end
@@ -0,0 +1,119 @@
1
+ class Media::Api::VideosController < Media::Api::BaseController
2
+ before_action :set_video, only: [:show, :viewed, :update, :destroy]
3
+ before_action :require_login, only: [:viewed, :starred, :create, :update, :destroy]
4
+
5
+ def index
6
+ q_params = params.permit(:video_taxon_id, :author_id, 'title-like', 'created_at-desc', 'view_count-desc', :id)
7
+ unless q_params[:author_id].to_i == current_user&.id
8
+ q_params.merge! state: 'verified'
9
+ end
10
+
11
+ @videos = Video.with_attached_media.with_attached_cover.includes(:progressions, :comments, :author, :video_taxon).default_where(q_params).order(id: :desc).page(params[:page]).per(params[:per])
12
+
13
+ if params[:subscribe].present?
14
+ @videos = @videos.where("author_id in (?)", current_user.follow_users.pluck(:id))
15
+ end
16
+
17
+ if current_user && params[:starred]
18
+ @star_ids = current_user.stars.where(starred_type: 'Video').pluck(:starred_id)
19
+ @videos = @videos.where(id: @star_ids)
20
+ elsif current_user
21
+ @star_ids = current_user.stars.where(starred_type: 'Video', starred_id: @videos.pluck(:id)).pluck(:starred_id)
22
+ end
23
+ end
24
+
25
+ def list
26
+ ids = []
27
+ if params[:id]
28
+ set_video
29
+ ids << params[:id]
30
+ end
31
+
32
+ q_params = params.permit(:video_taxon_id, :author_id, 'title-like', 'created_at-desc', 'view_count-desc', :per)
33
+ unless q_params[:author_id].to_i == current_user&.id
34
+ q_params.merge! state: 'verified'
35
+ end
36
+
37
+ if @video
38
+ if params[:scope].blank? || params[:scope] == 'pre'
39
+ @pre_videos = @video.pre_videos(q_params)
40
+ ids += @pre_videos.pluck(:id)
41
+ end
42
+ if params[:scope].blank? || params[:scope] == 'next'
43
+ @next_videos = @video.next_videos(q_params)
44
+ ids += @next_videos.pluck(:id)
45
+ end
46
+ else
47
+ @next_videos = Video.default_where(q_params).order(id: :desc).page(params[:page]).per(params[:per])
48
+ ids += @next_videos.pluck(:id)
49
+ end
50
+ if current_user && params[:starred]
51
+ @star_ids = current_user.stars.where(starred_type: 'Video').pluck(:starred_id)
52
+ @next_videos = @next_videos.where(id: @star_ids) if @next_videos
53
+ @pre_videos = @pre_videos.where(id: @star_ids) if @pre_videos
54
+ elsif current_user
55
+ @star_ids = current_user.stars.where(starred_type: 'Video', starred_id: ids).pluck(:starred_id)
56
+ end
57
+ end
58
+
59
+ def starred
60
+ @star_ids = current_user.stars.where(starred_type: 'Video').pluck(:starred_id)
61
+ @videos = Video.where(id: @star_ids).page(params[:page])
62
+ render :index
63
+ end
64
+
65
+ def show
66
+ end
67
+
68
+ def videos
69
+ @videos = user.videos
70
+ render json: { videos: @videos }
71
+ end
72
+
73
+ def viewed
74
+ p = @video.progressions.find_or_initialize_by(user_id: current_user.id)
75
+ p.state = 'done'
76
+ p.save
77
+
78
+ render 'show'
79
+ end
80
+
81
+ def create
82
+ @video = Video.new(video_params)
83
+
84
+ if @video.save
85
+ if current_user
86
+ @star_ids = current_user.stars.where(starred_type: 'Video', starred_id: @video.id).pluck(:starred_id)
87
+ end
88
+ render 'show', status: :created
89
+ else
90
+ render json: @video.errors, status: :unprocessable_entity
91
+ end
92
+ end
93
+
94
+ def update
95
+ if @video.update(video_params)
96
+ render 'show'
97
+ else
98
+ render json: @video.errors, status: :unprocessable_entity
99
+ end
100
+ end
101
+
102
+ private
103
+ def set_video
104
+ @video = Video.find(params[:id])
105
+ if current_user
106
+ @star_ids = current_user.stars.where(starred_type: 'Video', starred_id: @video.id).pluck(:starred_id)
107
+ end
108
+ end
109
+
110
+ def video_params
111
+ params.fetch(:video, {}).permit(
112
+ :title,
113
+ :media,
114
+ :cover,
115
+ :video_taxon_id,
116
+ tag_ids: []
117
+ ).merge(author_id: current_user.id)
118
+ end
119
+ end
@@ -0,0 +1,5 @@
1
+ module Cms
2
+ class BaseController < ApplicationController
3
+
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Cms
2
+ class My::BaseController < MyController
3
+
4
+ end
5
+ end
@@ -0,0 +1,9 @@
1
+ module Cms
2
+ class VideoWmJob < ApplicationJob
3
+
4
+ def perform(video)
5
+ video.water_mark
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ module Cms
2
+ class Audio < ApplicationRecord
3
+ include Model::Audio
4
+ end
5
+ end
@@ -0,0 +1,24 @@
1
+ module RailsMedia::User
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ has_many :videos, foreign_key: :author_id
6
+ end
7
+
8
+ def videos_count
9
+ if defined?(super) && super
10
+ super
11
+ else
12
+ videos.count
13
+ end
14
+ end
15
+
16
+ def video_liked_count
17
+ videos.sum(:liked_count)
18
+ end
19
+
20
+ def video_comments_count
21
+ videos.sum(:comments_count)
22
+ end
23
+
24
+ end
@@ -0,0 +1,25 @@
1
+ module Cms
2
+ module Model::Audio
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ belongs_to :author, class_name: 'Auth::User', optional: true
7
+
8
+ has_one_attached :media
9
+ has_one_attached :cover
10
+ end
11
+
12
+ def media_url
13
+ media.service_url if media.attachment.present?
14
+ end
15
+
16
+ def cover_url
17
+ cover.service_url if cover.attachment.present?
18
+ end
19
+
20
+ def duration
21
+ media_blob.duration
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,18 @@
1
+ module Cms
2
+ module Model::Progression
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ belongs_to :user, class_name: 'Auth::User'
7
+
8
+ belongs_to :progressive, polymorphic: true, counter_cache: :view_count
9
+
10
+ enum state: {
11
+ init: 'init',
12
+ doing: 'doing',
13
+ done: 'done'
14
+ }
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+ module Cms
3
+ module RailsMedia::Video
4
+ WM_PREFIX = '0wm'
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ attribute :share_count, :integer, default: 0
9
+ attribute :view_count, :integer, default: 0
10
+ attribute :liked_count, :integer, default: 0
11
+ attribute :comments_count, :integer, default: 0
12
+ attribute :state, :string, default: 'draft'
13
+
14
+ belongs_to :author, class_name: 'User', optional: true
15
+ belongs_to :video_taxon, optional: true
16
+
17
+ has_many :taggeds, as: :tagging, dependent: :delete_all
18
+ has_many :tags, through: :taggeds
19
+ has_many :attitudes, as: :attitudinal, dependent: :delete_all
20
+ has_many :progressions, as: :progressive, dependent: :delete_all
21
+
22
+ has_one_attached :media
23
+ has_one_attached :cover
24
+
25
+ after_create_commit :doing_water_mark
26
+ after_create_commit :doing_video_tag
27
+
28
+ enum state: {
29
+ draft: 'draft',
30
+ verified: 'verified',
31
+ rejected: 'rejected'
32
+ }
33
+ acts_as_notify :default, only: [:title], methods: [:state_i18n, :cover_url]
34
+ end
35
+
36
+ def do_trigger(params = {})
37
+ self.trigger_to state: params[:state]
38
+
39
+ self.class.transaction do
40
+ self.save!
41
+ to_notification(
42
+ receiver: self.author,
43
+ linked: self,
44
+ verbose: true
45
+ ) if self.author
46
+ end
47
+ end
48
+
49
+ def viewed?(user_id)
50
+ progressions.done.exists?(user_id: user_id)
51
+ end
52
+
53
+ def media_url
54
+ media.blob.service_url if media.attachment.present?
55
+ end
56
+
57
+ def media_wm_url
58
+ if water_mark_job
59
+ QiniuHelper.download_url([WM_PREFIX, self.media_blob&.key].join('_'))
60
+ else
61
+ media_url
62
+ end
63
+ end
64
+
65
+ def cover_url
66
+ cover.blob.service_url if cover.attachment.present?
67
+ end
68
+
69
+ def pre_videos(params = {})
70
+ per = params.delete(:per) || 10
71
+ q = { 'id-gt': self.id }.merge params
72
+
73
+ self.class.default_where(q).order(id: :asc).limit(per)
74
+ end
75
+
76
+ def next_videos(params = {})
77
+ per = params.delete(:per) || 10
78
+ q = { 'id-lt': self.id }.merge params
79
+
80
+ self.class.default_where(q).order(id: :desc).limit(per)
81
+ end
82
+
83
+ def share_url(current_user)
84
+ url_helpers = Rails.application.routes.url_helpers
85
+
86
+ if current_user
87
+ url_helpers.video_url(self.id, user_id: current_user.id)
88
+ else
89
+ url_helpers.video_url(self.id)
90
+ end
91
+ end
92
+
93
+ def water_mark
94
+ if water_mark_job.blank?
95
+ r = QiniuHelper.av_watermark(self.media.key, RailsMedia.config.water_mark_url, gravity: 'North', prefix: WM_PREFIX)
96
+ self.update(water_mark_job: r['persistentId'])
97
+ r
98
+ else
99
+ r = QiniuHelper.prefop(water_mark_job)
100
+ self.update(water_mark_job: r['desc'])
101
+ r
102
+ end
103
+ end
104
+
105
+ def doing_water_mark
106
+ if RailsMedia.config.water_mark
107
+ VideoWmJob.perform_later(self)
108
+ end
109
+ end
110
+
111
+ def cache_entity_logs
112
+ self.update share_count: logs_count('share_video')
113
+ end
114
+
115
+ def doing_video_tag
116
+ reg = /#[^#]+[#|\s]/
117
+ r = self.title.scan(reg)
118
+ tag_str = r.map do |tag|
119
+ tag.gsub(/(^#|#$)/, '')
120
+ end
121
+ tag_ids = tag_str.map do |tag|
122
+ VideoTag.find_or_create_by(name: tag).id
123
+ end
124
+ self.tag_ids = tag_ids
125
+ end
126
+
127
+ end
128
+ end
@@ -0,0 +1,3 @@
1
+ class PostTag < Tag
2
+
3
+ end
@@ -0,0 +1,3 @@
1
+ class VideoTag < Tag
2
+
3
+ end
@@ -0,0 +1,3 @@
1
+ class VideoTaxon < Taxon
2
+
3
+ end
@@ -0,0 +1,5 @@
1
+ module Cms
2
+ class Progression < ApplicationRecord
3
+ include Model::Progression
4
+ end
5
+ end
@@ -0,0 +1,9 @@
1
+ module Cms
2
+ class Video < ApplicationRecord
3
+ include Model::Video
4
+ #include CheckMachine
5
+ include Growth::Ext::Entity
6
+ include Interact::Model::Like
7
+ include Interact::Model::Commentable
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ <div class="ui attached segment">
2
+
3
+ <%= search_form_with do |f| %>
4
+ <div class="fields">
5
+ <%= f.submit 'Search' %>
6
+ </div>
7
+ <% end %>
8
+
9
+ </div>
10
+
@@ -0,0 +1,6 @@
1
+ <%= form_with model: [:admin, @audio], local: true do |f| %>
2
+ <%= render 'shared/error_messages', target: @audio %>
3
+ <%= f.text_field :title %>
4
+ <%= f.file_field :media %>
5
+ <%= f.submit %>
6
+ <% end %>
@@ -0,0 +1,9 @@
1
+ <div class="ui segment breadcrumb">
2
+ <%= link_to 'Back', admin_audios_path, class: 'section' %>
3
+ <div class="divider">/</div>
4
+ <div class="active section">Edit</div>
5
+ </div>
6
+
7
+ <div class="ui segment">
8
+ <%= render 'form' %>
9
+ </div>
@@ -0,0 +1,47 @@
1
+ <div class="ui top attached borderless menu">
2
+ <div class="header item">Admin Audios</div>
3
+ <div class="right menu">
4
+ <div class="item">
5
+ <%= link_to 'New Admin Audio', new_admin_audio_path, class: 'ui teal button' %>
6
+ </div>
7
+ </div>
8
+ </div>
9
+
10
+ <%= render 'filter' %>
11
+
12
+ <table class="ui bottom attached table">
13
+ <thead>
14
+ <tr>
15
+ <th><%= Audio.human_attribute_name(:id) %></th>
16
+ <th><%= Audio.human_attribute_name(:title) %></th>
17
+ <th><%= Audio.human_attribute_name(:state) %></th>
18
+ <th><%= Audio.human_attribute_name(:author) %></th>
19
+ <th>预览</th>
20
+ <th>Actions</th>
21
+ </tr>
22
+ </thead>
23
+ <tbody>
24
+ <% @audios.each do |audio| %>
25
+ <tr>
26
+ <td><%= audio.id %></td>
27
+ <td><%= audio.title %></td>
28
+ <td><%= audio.state %></td>
29
+ <td><%= audio.author.name if audio.author %></td>
30
+ <td><%= link_to '预览', rails_ext_video_path(audio.media.id), target: '_blank' if audio.media.attached? %></td>
31
+ <td class="ui labels">
32
+ <%= link_to admin_audio_path(audio), data: { tooltip: t('.show') }, class: 'ui blue mini icon button' do %>
33
+ <i class="location arrow icon"></i>
34
+ <% end %>
35
+ <%= link_to edit_admin_audio_path(audio), data: { tooltip: t('.edit') }, class: 'ui pink mini icon button' do %>
36
+ <i class="pencil alternate icon"></i>
37
+ <% end %>
38
+ <%= link_to admin_audio_path(audio), method: :delete, data: { tooltip: t('.destroy'), confirm: 'Are you sure?' }, class: 'ui red mini icon button' do %>
39
+ <i class="times icon"></i>
40
+ <% end %>
41
+ </td>
42
+ </tr>
43
+ <% end %>
44
+ </tbody>
45
+ </table>
46
+
47
+ <%= paginate @audios %>
@@ -0,0 +1,9 @@
1
+ <div class="ui segment breadcrumb">
2
+ <%= link_to 'Back', admin_audios_path, class: 'section' %>
3
+ <div class="divider">/</div>
4
+ <div class="active section">Add</div>
5
+ </div>
6
+
7
+ <div class="ui segment">
8
+ <%= render 'form' %>
9
+ </div>
@@ -0,0 +1,22 @@
1
+ <div class="ui segment breadcrumb">
2
+ <%= link_to 'Back', admin_audios_path, class: 'section' %>
3
+ <div class="divider">/</div>
4
+ <div class="active section">Show</div>
5
+ </div>
6
+
7
+ <table class="ui very basic large table">
8
+ <tbody>
9
+ <tr>
10
+ <td class="right aligned"><%= Audio.human_attribute_name(:title) %></td>
11
+ <td><%= @audio.title %></td>
12
+ </tr>
13
+ <tr>
14
+ <td class="right aligned"><%= Audio.human_attribute_name(:state) %></td>
15
+ <td><%= @audio.state %></td>
16
+ </tr>
17
+ <tr>
18
+ <td class="right aligned"><%= Audio.human_attribute_name(:author) %></td>
19
+ <td><%= @audio.author %></td>
20
+ </tr>
21
+ </tbody>
22
+ </table>
@@ -0,0 +1,16 @@
1
+ <div class="ui vertical fluid icon menu">
2
+ <div class="header center link item" id="close_side">
3
+ <i class="content icon"></i>
4
+ </div>
5
+ <div class="item ui dropdown">
6
+ <a class="<%= active_helper(controllers: ['audios', 'videos'], active_class: 'title active', item_class: 'title') %>">
7
+ <i class="file alternate icon"></i>
8
+ <span>资讯</span>
9
+ </a>
10
+ <div class="<%= active_helper(controllers: ['audios', 'videos'], active_class: 'menu content active', item_class: 'menu content') %>">
11
+ <%= link_to '音频管理', admin_audios_path, class: active_helper(controllers: 'audios') %>
12
+ <%= link_to '视频管理', admin_videos_path, class: active_helper(controllers: 'videos') %>
13
+ </div>
14
+ </div>
15
+ </div>
16
+
@@ -0,0 +1,8 @@
1
+ <%= form_with model: [:admin, @video], local: true do |f| %>
2
+ <%= render 'shared/error_messages', target: @video %>
3
+ <%= f.text_field :title %>
4
+ <%= f.collection_select :video_taxon_id, VideoTaxon.all, :id, :name %>
5
+ <%= f.file_field :media %>
6
+ <%= f.file_field :cover %>
7
+ <%= f.submit %>
8
+ <% end %>
@@ -0,0 +1,6 @@
1
+ <%= search_form_with do |f| %>
2
+ <div class="fields">
3
+ <%= f.text_field 'user.name' %>
4
+ <%= f.submit 'Search' %>
5
+ </div>
6
+ <% end %>
@@ -0,0 +1,9 @@
1
+ <div class="ui segment breadcrumb">
2
+ <%= link_to 'Back', admin_videos_path, class: 'section' %>
3
+ <div class="divider">/</div>
4
+ <div class="active section">Edit</div>
5
+ </div>
6
+
7
+ <div class="ui segment">
8
+ <%= render 'form' %>
9
+ </div>
@@ -0,0 +1,62 @@
1
+ <div class="ui top attached borderless menu">
2
+ <div class="header item">Admin Videos</div>
3
+ <div class="right menu">
4
+ <div class="item">
5
+ <%= link_to 'New Admin Video', new_admin_video_path, class: 'ui teal button' %>
6
+ </div>
7
+ </div>
8
+ </div>
9
+
10
+ <div class="ui attached segment">
11
+ <%= render 'search_form' %>
12
+ </div>
13
+
14
+ <table class="ui bottom attached table">
15
+ <thead>
16
+ <tr>
17
+ <th><%= Video.human_attribute_name(:id) %></th>
18
+ <th><%= Video.human_attribute_name(:title) %></th>
19
+ <th><%= Video.human_attribute_name(:cover) %></th>
20
+ <th><%= Video.human_attribute_name(:media) %></th>
21
+ <th><%= Video.human_attribute_name(:state) %></th>
22
+ <th><%= Video.human_attribute_name(:author_id) %></th>
23
+ <th><%= Video.human_attribute_name(:created_at) %></th>
24
+ <th>Actions</th>
25
+ </tr>
26
+ </thead>
27
+ <tbody>
28
+ <% @videos.each do |video| %>
29
+ <tr>
30
+ <td><%= video.id %></td>
31
+ <td><%= video.title %></td>
32
+ <td><%= image_tag video.cover, class: 'ui small image' if video.cover.attached? %></td>
33
+ <td><%= link_to '预览', rails_ext_video_path(video.media&.id), target: '_blank' %></td>
34
+ <td class="ui labels">
35
+ <label class="ui label"><%= video.state_i18n %></label>
36
+ <div class="ui mini buttons">
37
+ <%= link_to t('.approve'), new_check_path('Video', video.id, state: Video.states[:verified]), remote: true, class: 'ui positive button' %>
38
+ <div class="or"></div>
39
+ <%= link_to t('.reject'), new_check_path('Video', video.id, state: Video.states[:rejected]), remote: true, class: 'ui negative button' %>
40
+ </div>
41
+ </td>
42
+ <td>
43
+ <%= link_to video.author.name, admin_user_path(video.author_id) if video.author %>
44
+ </td>
45
+ <td><time><%= video.created_at.to_s(:rfc822) %></time></td>
46
+ <td class="ui labels">
47
+ <%= link_to admin_video_path(video), data: { tooltip: t('.show') }, class: 'ui blue mini icon button' do %>
48
+ <i class="location arrow icon"></i>
49
+ <% end %>
50
+ <%= link_to edit_admin_video_path(video), data: { tooltip: t('.edit') }, class: 'ui pink mini icon button' do %>
51
+ <i class="pencil alternate icon"></i>
52
+ <% end %>
53
+ <%= link_to admin_video_path(video), method: :delete, data: { tooltip: t('.destroy'), confirm: 'Are you sure?' }, class: 'ui red mini icon button' do %>
54
+ <i class="times icon"></i>
55
+ <% end %>
56
+ </td>
57
+ </tr>
58
+ <% end %>
59
+ </tbody>
60
+ </table>
61
+
62
+ <%= paginate @videos %>
@@ -0,0 +1,9 @@
1
+ <div class="ui segment breadcrumb">
2
+ <%= link_to 'Back', admin_videos_path, class: 'section' %>
3
+ <div class="divider">/</div>
4
+ <div class="active section">Add</div>
5
+ </div>
6
+
7
+ <div class="ui segment">
8
+ <%= render 'form' %>
9
+ </div>
@@ -0,0 +1,22 @@
1
+ <div class="ui segment breadcrumb">
2
+ <%= link_to 'Back', admin_videos_path, class: 'section' %>
3
+ <div class="divider">/</div>
4
+ <div class="active section">Show</div>
5
+ </div>
6
+
7
+ <table class="ui very basic large table">
8
+ <tbody>
9
+ <tr>
10
+ <td class="right aligned"><%= Video.human_attribute_name(:title) %></td>
11
+ <td><%= @video.title %></td>
12
+ </tr>
13
+ <tr>
14
+ <td class="right aligned"><%= Video.human_attribute_name(:state) %></td>
15
+ <td><%= @video.state %></td>
16
+ </tr>
17
+ <tr>
18
+ <td class="right aligned"><%= Video.human_attribute_name(:author_id) %></td>
19
+ <td><%= @video.author_id %></td>
20
+ </tr>
21
+ </tbody>
22
+ </table>
@@ -0,0 +1,4 @@
1
+ json.extract! audio, :id, :title, :cover_url, :media_url, :duration
2
+ if audio.author
3
+ json.author audio.author, :id, :name, :avatar_url
4
+ end
@@ -0,0 +1,2 @@
1
+ json.audios @audios, partial: 'audio', as: :audio
2
+ json.partial! 'api/shared/pagination', items: @audios
@@ -0,0 +1,8 @@
1
+ json.partial! 'video', video: video
2
+ json.tags video.tags do |tag|
3
+ json.extract! tag, :id, :name
4
+ end
5
+ json.comments video.comments do |comment|
6
+ json.extract! comment, :id, :title, :content, :state, :score, :liked_count, :created_at
7
+ json.commenter comment.user, :id, :name, :avatar_url
8
+ end
@@ -0,0 +1,30 @@
1
+ json.extract! video,
2
+ :id,
3
+ :title,
4
+ :cover_url,
5
+ :view_count,
6
+ :liked_count,
7
+ :share_count,
8
+ :created_at,
9
+ :rewardable
10
+ json.share_url video.share_url(current_user)
11
+ json.comments_count video.comments.count
12
+ if video.author
13
+ json.author video.author, :id, :name, :avatar_url
14
+ end
15
+ json.media do
16
+ json.extract! video.media.metadata, *(video.media.metadata.keys & ['height', 'width'])
17
+ json.duration video.media.blob.duration
18
+ json.duration_str video.media.blob.duration_str
19
+ json.url video.media.service_url
20
+ json.wm_url video.media_wm_url
21
+ end
22
+ if video.video_taxon
23
+ json.video_taxon video.video_taxon, :id, :name
24
+ end
25
+ if current_user
26
+ json.starred @star_ids.include?(video.id)
27
+ json.viewed video.viewed?(current_user.id)
28
+ json.liked video.liked?(current_user.id)
29
+ json.rewardable_codes video.rewardable_codes(current_user.id)
30
+ end
@@ -0,0 +1,36 @@
1
+ json.partial! 'api/shared/pagination', items: @videos
2
+
3
+ json.videos do
4
+ json.array! @videos do |video|
5
+ json.id video.id
6
+ json.title video.title
7
+ json.cover_url video.cover_url
8
+ json.view_count video.view_count
9
+ json.liked_count video.liked_count
10
+ json.share_count video.share_count
11
+ json.created_at video.created_at
12
+ json.rewardable video.rewardable
13
+ json.share_url video.share_url(current_user)
14
+ json.comments_count video.comments.count
15
+ if video.author
16
+ json.author video.author, :id, :name, :avatar_url
17
+ end
18
+ json.media do
19
+ json.extract! video.media.metadata, *(video.media.metadata.keys & ['height', 'width'])
20
+ json.duration video.media.blob.duration
21
+ json.duration_str video.media.blob.duration_str
22
+ json.url video.media.service_url
23
+ json.wm_url video.media_wm_url
24
+ end
25
+ if video.video_taxon
26
+ json.video_taxon video.video_taxon, :id, :name
27
+ end
28
+ if current_user
29
+ json.starred @star_ids.include?(video.id)
30
+ json.viewed video.viewed?(current_user.id)
31
+ json.liked video.liked?(current_user.id)
32
+ json.rewardable_codes video.rewardable_codes(current_user.id)
33
+ end
34
+ end
35
+ end
36
+
@@ -0,0 +1,9 @@
1
+ if defined?(@video)
2
+ json.video @video, partial: 'detail', as: :video
3
+ end
4
+ if defined?(@pre_videos)
5
+ json.pre @pre_videos, partial: 'detail', as: :video
6
+ end
7
+ if defined?(@next_videos)
8
+ json.next @next_videos, partial: 'detail', as: :video
9
+ end
@@ -0,0 +1 @@
1
+ json.video @video, partial: 'detail', as: :video
@@ -0,0 +1,5 @@
1
+ en:
2
+ activerecord:
3
+ attributes:
4
+ taxon/type:
5
+ VideoTaxon: Video Taxon
@@ -0,0 +1,7 @@
1
+ en:
2
+ activerecord:
3
+ notify:
4
+ video:
5
+ default:
6
+ title: '%{title}'
7
+ body: Your video has been %{state_i18n}
@@ -0,0 +1,9 @@
1
+ zh:
2
+ activerecord:
3
+ attributes:
4
+ taxon/type:
5
+ VideoTaxon: 视频分类
6
+ video/state:
7
+ draft: 待审核
8
+ verified: 已通过
9
+ rejected: 未通过
@@ -0,0 +1,7 @@
1
+ zh:
2
+ activerecord:
3
+ notify:
4
+ video:
5
+ default:
6
+ title: 您好!您提交的视频审核%{state_i18n}
7
+ body: '%{title}'
data/config/routes.rb ADDED
@@ -0,0 +1,23 @@
1
+ Rails.application.routes.draw do
2
+
3
+ namespace :cms, defaults: { business: 'cms' } do
4
+ resources :videos, on: [:show]
5
+
6
+ scope :api, module: 'media/api', as: :api do
7
+ resources :videos do
8
+ get :list, on: :collection
9
+ get :starred, on: :collection
10
+ patch :viewed, on: :member
11
+ end
12
+ resources :video_taxons
13
+ resources :video_tags
14
+ resources :audios, only: [:index]
15
+ end
16
+
17
+ namespace :admin, defaults: { namespace: 'admin' } do
18
+ resources :videos
19
+ resources :audios
20
+ end
21
+ end
22
+
23
+ end
@@ -0,0 +1,34 @@
1
+ class RailsMediaInit < ActiveRecord::Migration[5.2]
2
+ def change
3
+
4
+ create_table :videos do |t|
5
+ t.references :author
6
+ t.references :video_taxon
7
+ t.string :title
8
+ t.string :state
9
+ t.integer :liked_count
10
+ t.integer :view_count
11
+ t.integer :comments_count
12
+ t.string :water_mark_job
13
+ t.timestamps
14
+ end
15
+
16
+ create_table :audios do |t|
17
+ t.references :author
18
+ t.string :title
19
+ t.string :state
20
+ t.timestamps
21
+ end
22
+
23
+ create_table :progressions do |t|
24
+ t.references :user
25
+ t.references :progressive, polymorphic: true
26
+ t.float :rate
27
+ t.float :time
28
+ t.float :duration
29
+ t.string :state
30
+ t.timestamps
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,11 @@
1
+ require 'active_support/configurable'
2
+
3
+ module RailsCms
4
+ include ActiveSupport::Configurable
5
+
6
+ configure do |config|
7
+ config.water_mark = false
8
+ config.water_mark_url = 'http://cloud.1314-edu.com/watermark.gif'
9
+ end
10
+
11
+ end
@@ -0,0 +1,10 @@
1
+ module RailsCms
2
+ class Engine < ::Rails::Engine
3
+
4
+ config.autoload_paths += Dir[
5
+ "#{config.root}/app/models/tag",
6
+ "#{config.root}/app/models/taxon"
7
+ ]
8
+
9
+ end
10
+ end
data/lib/rails_cms.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'rails_cms/engine'
2
+ require 'rails_cms/config'
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rails_cms
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - qinmingyuan
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-10-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails_extend
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: Description of RailsCms.
28
+ email:
29
+ - mingyuan0715@foxmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - LICENSE
35
+ - README.md
36
+ - Rakefile
37
+ - app/controllers/cms/admin/audios_controller.rb
38
+ - app/controllers/cms/admin/base_controller.rb
39
+ - app/controllers/cms/admin/videos_controller.rb
40
+ - app/controllers/cms/api/audios_controller.rb
41
+ - app/controllers/cms/api/base_controller.rb
42
+ - app/controllers/cms/api/video_tags_controller.rb
43
+ - app/controllers/cms/api/video_taxons_controller.rb
44
+ - app/controllers/cms/api/videos_controller.rb
45
+ - app/controllers/cms/base_controller.rb
46
+ - app/controllers/cms/my/base_controller.rb
47
+ - app/jobs/cms/video_wm_job.rb
48
+ - app/models/audio.rb
49
+ - app/models/cms/ext/user.rb
50
+ - app/models/cms/model/audio.rb
51
+ - app/models/cms/model/progression.rb
52
+ - app/models/cms/model/video.rb
53
+ - app/models/cms/post_tag.rb
54
+ - app/models/cms/video_tag.rb
55
+ - app/models/cms/video_taxon.rb
56
+ - app/models/progression.rb
57
+ - app/models/video.rb
58
+ - app/views/cms/admin/audios/_filter.html.erb
59
+ - app/views/cms/admin/audios/_form.html.erb
60
+ - app/views/cms/admin/audios/edit.html.erb
61
+ - app/views/cms/admin/audios/index.html.erb
62
+ - app/views/cms/admin/audios/new.html.erb
63
+ - app/views/cms/admin/audios/show.html.erb
64
+ - app/views/cms/admin/base/_nav.html.erb
65
+ - app/views/cms/admin/videos/_form.html.erb
66
+ - app/views/cms/admin/videos/_search_form.html.erb
67
+ - app/views/cms/admin/videos/edit.html.erb
68
+ - app/views/cms/admin/videos/index.html.erb
69
+ - app/views/cms/admin/videos/new.html.erb
70
+ - app/views/cms/admin/videos/show.html.erb
71
+ - app/views/cms/api/audios/_audio.json.jbuilder
72
+ - app/views/cms/api/audios/index.json.jbuilder
73
+ - app/views/cms/api/videos/_detail.json.jbuilder
74
+ - app/views/cms/api/videos/_video.json.jbuilder
75
+ - app/views/cms/api/videos/index.json.jbuilder
76
+ - app/views/cms/api/videos/list.json.jbuilder
77
+ - app/views/cms/api/videos/show.json.jbuilder
78
+ - config/locales/en.enum.yml
79
+ - config/locales/en.notify.yml
80
+ - config/locales/zh.enum.yml
81
+ - config/locales/zh.notify.yml
82
+ - config/routes.rb
83
+ - db/migrate/20181203082644_rails_media_init.rb
84
+ - lib/rails_cms.rb
85
+ - lib/rails_cms/config.rb
86
+ - lib/rails_cms/engine.rb
87
+ homepage: https://github.com/work-design/rails_cms
88
+ licenses:
89
+ - MIT
90
+ metadata: {}
91
+ post_install_message:
92
+ rdoc_options: []
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ requirements: []
106
+ rubygems_version: 3.2.22
107
+ signing_key:
108
+ specification_version: 4
109
+ summary: Summary of RailsCms.
110
+ test_files: []