ad_space 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +22 -0
- data/Gemfile.lock +240 -0
- data/MIT-LICENSE +20 -0
- data/README.md +92 -0
- data/Rakefile +8 -0
- data/ad_space.gemspec +25 -0
- data/app/assets/builds/application.js +7397 -0
- data/app/assets/builds/application.js.map +7 -0
- data/app/assets/config/ad_space_manifest.js +2 -0
- data/app/assets/javascript/ad_space/application.js +0 -0
- data/app/assets/stylesheets/ad_space/application.css +15 -0
- data/app/controllers/ad_space/advertisements_controller.rb +121 -0
- data/app/controllers/ad_space/api/v1/advertisements_controller.rb +36 -0
- data/app/controllers/ad_space/application_controller.rb +30 -0
- data/app/controllers/ad_space/dashboard_controller.rb +6 -0
- data/app/controllers/ad_space/slots_controller.rb +70 -0
- data/app/helpers/ad_space/advertisements_helper.rb +4 -0
- data/app/helpers/ad_space/application_helper.rb +15 -0
- data/app/helpers/ad_space/dashboard_helper.rb +4 -0
- data/app/helpers/ad_space/slots_helper.rb +4 -0
- data/app/javascript/application.js +3 -0
- data/app/javascript/controllers/application.js +9 -0
- data/app/javascript/controllers/hello_controller.js +153 -0
- data/app/javascript/controllers/image_upload_controller.js +46 -0
- data/app/javascript/controllers/index.js +11 -0
- data/app/jobs/ad_space/application_job.rb +4 -0
- data/app/mailers/ad_space/application_mailer.rb +6 -0
- data/app/models/ad_space/advertisement.rb +67 -0
- data/app/models/ad_space/application_record.rb +5 -0
- data/app/models/ad_space/breadcrumb.rb +14 -0
- data/app/models/ad_space/slot.rb +27 -0
- data/app/serializers/v1/advertisement_serializer.rb +30 -0
- data/app/validators/url_or_custom_scheme_validator.rb +20 -0
- data/app/views/ad_space/advertisements/_advertisement.html.erb +102 -0
- data/app/views/ad_space/advertisements/_form.html.erb +143 -0
- data/app/views/ad_space/advertisements/edit.html.erb +10 -0
- data/app/views/ad_space/advertisements/index.html.erb +15 -0
- data/app/views/ad_space/advertisements/new.html.erb +9 -0
- data/app/views/ad_space/advertisements/show.html.erb +13 -0
- data/app/views/ad_space/dashboard/index.html.erb +7 -0
- data/app/views/ad_space/slots/_form.html.erb +44 -0
- data/app/views/ad_space/slots/_slot.html.erb +30 -0
- data/app/views/ad_space/slots/edit.html.erb +10 -0
- data/app/views/ad_space/slots/index.html.erb +14 -0
- data/app/views/ad_space/slots/new.html.erb +9 -0
- data/app/views/ad_space/slots/show.html.erb +10 -0
- data/app/views/layouts/ad_space/application.html.erb +43 -0
- data/config/routes.rb +22 -0
- data/db/migrate/20231026152912_create_ad_space_slots.rb +11 -0
- data/db/migrate/20231026160758_create_ad_space_advertisements.rb +25 -0
- data/db/migrate/20231027070536_add_scheme_url_to_ad_space_advertisements.rb +10 -0
- data/db/migrate/20231027083935_add_target_version_code_to_ad_space_advertisements.rb +6 -0
- data/db/migrate/20231027115707_add_is_splash_to_ad_space_slots.rb +5 -0
- data/lib/ad_space/app.rb +16 -0
- data/lib/ad_space/configuration.rb +32 -0
- data/lib/ad_space/engine.rb +12 -0
- data/lib/ad_space/version.rb +3 -0
- data/lib/ad_space.rb +17 -0
- data/lib/tasks/ad_space_tasks.rake +4 -0
- metadata +161 -0
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module V1
|
4
|
+
class AdvertisementSerializer < ActiveModel::Serializer
|
5
|
+
attributes :uuid, :started_at, :ended_at, :slot, :ad_type, :visibility
|
6
|
+
|
7
|
+
attribute :scheme_url, if: -> { object.ad_type == :custom }
|
8
|
+
attribute :image_url, if: -> { object.ad_type == :custom }
|
9
|
+
attribute :dsp_type, if: -> { object.ad_type == :dsp }
|
10
|
+
|
11
|
+
attribute :splash_ui_type, if: -> { object.splash? }
|
12
|
+
attribute :splash_countdown, if: -> { object.splash? }
|
13
|
+
|
14
|
+
def slot
|
15
|
+
object.slot.uuid
|
16
|
+
end
|
17
|
+
|
18
|
+
def started_at
|
19
|
+
object.started_at.to_i
|
20
|
+
end
|
21
|
+
|
22
|
+
def ended_at
|
23
|
+
object.started_at.to_i
|
24
|
+
end
|
25
|
+
|
26
|
+
def image_url
|
27
|
+
object.cover_url(instance_options[:size])
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class UrlOrCustomSchemeValidator < ActiveModel::EachValidator
|
2
|
+
def validate_each(record, attribute, value)
|
3
|
+
unless valid_url?(value) || valid_custom_scheme?(value)
|
4
|
+
record.errors.add(attribute, "must be a valid URL or have the format 'xxx://xxxxx'")
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def valid_url?(value)
|
11
|
+
uri = URI.parse(value)
|
12
|
+
uri.is_a?(URI::HTTP) || uri.is_a?(URI::HTTPS)
|
13
|
+
rescue URI::InvalidURIError
|
14
|
+
false
|
15
|
+
end
|
16
|
+
|
17
|
+
def valid_custom_scheme?(value)
|
18
|
+
value =~ /\A\w+:\/\/\w+\z/
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
<%= turbo_frame_tag advertisement do %>
|
2
|
+
<div id="<%= dom_id advertisement %>">
|
3
|
+
<p>
|
4
|
+
<strong>Name:</strong>
|
5
|
+
<%= advertisement.name %>
|
6
|
+
</p>
|
7
|
+
|
8
|
+
<p>
|
9
|
+
<strong>Uuid:</strong>
|
10
|
+
<%= advertisement.uuid %>
|
11
|
+
</p>
|
12
|
+
|
13
|
+
<p>
|
14
|
+
<strong>Slot:</strong>
|
15
|
+
<%= advertisement.slot_id %>
|
16
|
+
</p>
|
17
|
+
|
18
|
+
<p>
|
19
|
+
<strong>Published at:</strong>
|
20
|
+
<%= advertisement.published_at %>
|
21
|
+
</p>
|
22
|
+
|
23
|
+
<p>
|
24
|
+
<strong>Archived at:</strong>
|
25
|
+
<%= advertisement.archived_at %>
|
26
|
+
</p>
|
27
|
+
|
28
|
+
<p>
|
29
|
+
<strong>Started at:</strong>
|
30
|
+
<%= advertisement.started_at %>
|
31
|
+
</p>
|
32
|
+
|
33
|
+
<p>
|
34
|
+
<strong>Ended at:</strong>
|
35
|
+
<%= advertisement.ended_at %>
|
36
|
+
</p>
|
37
|
+
|
38
|
+
<p>
|
39
|
+
<strong>Platform:</strong>
|
40
|
+
<%= advertisement.platform %>
|
41
|
+
</p>
|
42
|
+
|
43
|
+
<p>
|
44
|
+
<strong>Archived at:</strong>
|
45
|
+
<%= advertisement.archived_at %>
|
46
|
+
</p>
|
47
|
+
|
48
|
+
<p>
|
49
|
+
<strong>Weight:</strong>
|
50
|
+
<%= advertisement.weight %>
|
51
|
+
</p>
|
52
|
+
|
53
|
+
<p>
|
54
|
+
<strong>Remark:</strong>
|
55
|
+
<%= advertisement.remark %>
|
56
|
+
</p>
|
57
|
+
|
58
|
+
<p>
|
59
|
+
<strong>Pv count:</strong>
|
60
|
+
<%= advertisement.pv_count %>
|
61
|
+
</p>
|
62
|
+
|
63
|
+
<p>
|
64
|
+
<strong>Uv count:</strong>
|
65
|
+
<%= advertisement.uv_count %>
|
66
|
+
</p>
|
67
|
+
|
68
|
+
<p>
|
69
|
+
<strong>Click count:</strong>
|
70
|
+
<%= advertisement.click_count %>
|
71
|
+
</p>
|
72
|
+
|
73
|
+
<% if advertisement.cover_sm.attached? %>
|
74
|
+
<p>
|
75
|
+
<strong>Small Cover Image:</strong>
|
76
|
+
<%= image_tag advertisement.cover_sm.url, style: "max-width: 200px; height: auto;" %>
|
77
|
+
</p>
|
78
|
+
<% end %>
|
79
|
+
|
80
|
+
<% if advertisement.cover_base.attached? %>
|
81
|
+
<p>
|
82
|
+
<strong>Base Cover Image:</strong>
|
83
|
+
<%= image_tag advertisement.cover_base.url, style: "max-width: 200px; height: auto;" %>
|
84
|
+
</p>
|
85
|
+
<% end %>
|
86
|
+
|
87
|
+
<% if advertisement.cover_md.attached? %>
|
88
|
+
<p>
|
89
|
+
<strong>Medium Cover Image:</strong>
|
90
|
+
<%= image_tag advertisement.cover_md.url, style: "max-width: 200px; height: auto;" %>
|
91
|
+
</p>
|
92
|
+
<% end %>
|
93
|
+
|
94
|
+
<% if advertisement.cover_lg.attached? %>
|
95
|
+
<p>
|
96
|
+
<strong>Large Cover Image:</strong>
|
97
|
+
<%= image_tag advertisement.cover_lg.url, style: "max-width: 200px; height: auto;" %>
|
98
|
+
</p>
|
99
|
+
<% end %>
|
100
|
+
</div>
|
101
|
+
|
102
|
+
<% end %>
|
@@ -0,0 +1,143 @@
|
|
1
|
+
<%= form_with(model: advertisement, html: { "data-controller" => "hello", "data-hello-is-dsp-ad-value" => advertisement.ad_type.blank? ? true : advertisement.ad_type == "dsp", "data-hello-is-splash-value" => advertisement.slot.blank? ? false : advertisement.slot.splash? }) do |form| %>
|
2
|
+
<% if advertisement.errors.any? %>
|
3
|
+
<div style="color: red">
|
4
|
+
<h2><%= pluralize(advertisement.errors.count, "error") %> prohibited this advertisement from being saved:</h2>
|
5
|
+
|
6
|
+
<ul>
|
7
|
+
<% advertisement.errors.each do |error| %>
|
8
|
+
<li><%= error.full_message %></li>
|
9
|
+
<% end %>
|
10
|
+
</ul>
|
11
|
+
</div>
|
12
|
+
<% end %>
|
13
|
+
|
14
|
+
<div>
|
15
|
+
<%= form.label :platform, style: "display: block" %>
|
16
|
+
<%= form.select :platform, AdSpace::Advertisement.platforms.keys.map { |platform| [platform.capitalize, platform] } %>
|
17
|
+
</div>
|
18
|
+
|
19
|
+
<div>
|
20
|
+
<%= form.label :name, style: "display: block" %>
|
21
|
+
<%= form.text_field :name %>
|
22
|
+
</div>
|
23
|
+
|
24
|
+
<div>
|
25
|
+
<%= form.label :slot_id, "Slot", style: "display: block" %>
|
26
|
+
<%= form.select :slot_id, options_for_select(
|
27
|
+
AdSpace::Slot.all.map { |s| [s.name, s.id, data: { is_splash: s.splash? }] },
|
28
|
+
advertisement.slot_id,
|
29
|
+
), {}, data: { hello_target: "slot", action: "hello#handleSlotChange" } %>
|
30
|
+
</div>
|
31
|
+
|
32
|
+
<div>
|
33
|
+
<%= form.label :visibility, style: "display: block" %>
|
34
|
+
<%= form.select :visibility, AdSpace::Advertisement.visibilities.keys.map { |key| [key.humanize, key] } %>
|
35
|
+
</div>
|
36
|
+
|
37
|
+
<div>
|
38
|
+
<%= form.label :target_version_codes, style: "display: block" %>
|
39
|
+
<%= form.text_field :target_version_codes, placeholder: "Enter version codes separated by commas", value: form.object.target_version_codes.blank? ? "" : form.object.target_version_codes.join(",") %>
|
40
|
+
</div>
|
41
|
+
|
42
|
+
<div>
|
43
|
+
<%= form.label :started_at, style: "display: block" %>
|
44
|
+
<%= form.datetime_field :started_at %>
|
45
|
+
</div>
|
46
|
+
|
47
|
+
<div>
|
48
|
+
<%= form.label :ended_at, style: "display: block" %>
|
49
|
+
<%= form.datetime_field :ended_at %>
|
50
|
+
</div>
|
51
|
+
|
52
|
+
<div>
|
53
|
+
<%= form.label :ad_type, style: "display: block" %>
|
54
|
+
<%= form.select :ad_type,
|
55
|
+
AdSpace::Advertisement.ad_types.keys.map { |key| [key.humanize, key] },
|
56
|
+
{},
|
57
|
+
data: { hello_target: "adType", action: "hello#handleAdTypeChnage" }
|
58
|
+
%>
|
59
|
+
</div>
|
60
|
+
|
61
|
+
<div>
|
62
|
+
<%= form.label :dsp_type, style: "display: block" %>
|
63
|
+
<%= form.select :dsp_type, AdSpace::Advertisement.dsp_types.keys.map { |key| [key.humanize, key] } %>
|
64
|
+
</div>
|
65
|
+
|
66
|
+
<div data-hello-target="schemeUrl">
|
67
|
+
<%= form.label :scheme_url, style: "display: block" %>
|
68
|
+
<%= form.text_field :scheme_url %>
|
69
|
+
</div>
|
70
|
+
|
71
|
+
<%# 默认兜底图 %>
|
72
|
+
<div data-hello-target="baseCover" data-controller="image-upload", data-image-upload-url-value=<%= remove_cover_advertisement_path(advertisement, size: "base")%>>
|
73
|
+
<%= form.label :cover_base, "Base Cover Image", style: "display: block" %>
|
74
|
+
<%= form.file_field :cover_base, data: {image_upload_target: "input"} %>
|
75
|
+
<% if advertisement.cover_base.attached? %>
|
76
|
+
<%= image_tag advertisement.cover_base.url, style: "max-width: 200px; height: auto;", data: {image_upload_target: "preview"} %>
|
77
|
+
<button type="button" data-image-upload-target="removeButton">移除图片</button>
|
78
|
+
<% else %>
|
79
|
+
<img data-image-upload-target="preview" style="max-width: 200px; height: auto;">
|
80
|
+
<button type="button" data-image-upload-target="removeButton" style="display: none;">移除图片</button>
|
81
|
+
<% end %>
|
82
|
+
</div>
|
83
|
+
|
84
|
+
<div data-hello-target="splashCountdown">
|
85
|
+
<%= form.label :splash_countdown, style: "display: block" %>
|
86
|
+
<%= form.number_field :splash_countdown, min: 0, step: 1, value: form.object.splash_countdown %>
|
87
|
+
</div>
|
88
|
+
|
89
|
+
<div data-hello-target="splashUiType">
|
90
|
+
<%= form.label :splash_ui_type, style: "display: block" %>
|
91
|
+
<%= form.select :splash_ui_type, AdSpace::Advertisement.splash_ui_types.keys.map { |key| [key.humanize, key] } %>
|
92
|
+
</div>
|
93
|
+
|
94
|
+
<div data-hello-target="smCover" data-controller="image-upload", data-image-upload-url-value=<%= remove_cover_advertisement_path(advertisement, size: "sm")%>>
|
95
|
+
<%= form.label :cover_sm, "Small Cover Image", style: "display: block" %>
|
96
|
+
<%= form.file_field :cover_sm, data: {image_upload_target: "input"} %>
|
97
|
+
<% if advertisement.cover_sm.attached? %>
|
98
|
+
<%= image_tag advertisement.cover_sm.url, style: "max-width: 200px; height: auto;", data: {image_upload_target: "preview"} %>
|
99
|
+
<button type="button" data-image-upload-target="removeButton">移除图片</button>
|
100
|
+
<% else %>
|
101
|
+
<img data-image-upload-target="preview" style="max-width: 200px; height: auto;">
|
102
|
+
<button type="button" data-image-upload-target="removeButton" style="display: none;">移除图片</button>
|
103
|
+
<% end %>
|
104
|
+
</div>
|
105
|
+
|
106
|
+
<div data-hello-target="mdCover" data-controller="image-upload", data-image-upload-url-value=<%= remove_cover_advertisement_path(advertisement, size: "md")%>>
|
107
|
+
<%= form.label :cover_md, "Medium Cover Image", style: "display: block" %>
|
108
|
+
<%= form.file_field :cover_md, data: {image_upload_target: "input"} %>
|
109
|
+
<% if advertisement.cover_md.attached? %>
|
110
|
+
<%= image_tag advertisement.cover_md.url, style: "max-width: 200px; height: auto;", data: {image_upload_target: "preview"} %>
|
111
|
+
<button type="button" data-image-upload-target="removeButton">移除图片</button>
|
112
|
+
<% else %>
|
113
|
+
<img data-image-upload-target="preview" style="max-width: 200px; height: auto;">
|
114
|
+
<button type="button" data-image-upload-target="removeButton" style="display: none;">移除图片</button>
|
115
|
+
<% end %>
|
116
|
+
</div>
|
117
|
+
|
118
|
+
<div data-hello-target="lgCover" data-controller="image-upload", data-image-upload-url-value=<%= remove_cover_advertisement_path(advertisement, size: "lg")%>>
|
119
|
+
<%= form.label :cover_lg, "Large Cover Image", style: "display: block" %>
|
120
|
+
<%= form.file_field :cover_lg, data: {image_upload_target: "input"} %>
|
121
|
+
<% if advertisement.cover_lg.attached? %>
|
122
|
+
<%= image_tag advertisement.cover_lg.url, style: "max-width: 200px; height: auto;", data: {image_upload_target: "preview"} %>
|
123
|
+
<button type="button" data-image-upload-target="removeButton">移除图片</button>
|
124
|
+
<% else %>
|
125
|
+
<img data-image-upload-target="preview" style="max-width: 200px; height: auto;">
|
126
|
+
<button type="button" data-image-upload-target="removeButton" style="display: none;">移除图片</button>
|
127
|
+
<% end %>
|
128
|
+
</div>
|
129
|
+
|
130
|
+
<div>
|
131
|
+
<%= form.label :weight, style: "display: block" %>
|
132
|
+
<%= form.number_field :weight %>
|
133
|
+
</div>
|
134
|
+
|
135
|
+
<div>
|
136
|
+
<%= form.label :remark, style: "display: block" %>
|
137
|
+
<%= form.text_area :remark %>
|
138
|
+
</div>
|
139
|
+
|
140
|
+
<div>
|
141
|
+
<%= form.submit %>
|
142
|
+
</div>
|
143
|
+
<% end %>
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<p style="color: green"><%= notice %></p>
|
2
|
+
|
3
|
+
<h1>Advertisements</h1>
|
4
|
+
|
5
|
+
<div id="advertisements">
|
6
|
+
<% @advertisements.each do |advertisement| %>
|
7
|
+
<%= render advertisement %>
|
8
|
+
<p>
|
9
|
+
<%= link_to "Show this advertisement", advertisement %>
|
10
|
+
</p>
|
11
|
+
|
12
|
+
<% end %>
|
13
|
+
</div>
|
14
|
+
|
15
|
+
<%= link_to "New advertisement", new_advertisement_path %>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<p style="color: green"><%= notice %></p>
|
2
|
+
|
3
|
+
<%= render @advertisement %>
|
4
|
+
|
5
|
+
<div>
|
6
|
+
<%= link_to "Edit this advertisement", edit_advertisement_path(@advertisement) %> |
|
7
|
+
<%= link_to "Back to advertisements", advertisements_path %>
|
8
|
+
<%= button_to "#{@advertisement.published? ? 'Unpublish' : 'Publish'} this advertisement", publish_advertisement_path(@advertisement) %>
|
9
|
+
<%= button_to "#{@advertisement.archived? ? 'Unarchive' : 'Archive'} this advertisement", archive_advertisement_path(@advertisement) %>
|
10
|
+
|
11
|
+
<br/>
|
12
|
+
<%= button_to "Destroy this advertisement", @advertisement, method: :delete %>
|
13
|
+
</div>
|
@@ -0,0 +1,7 @@
|
|
1
|
+
<h1>📺 AdSpace</h1>
|
2
|
+
<ul>
|
3
|
+
<li><%= link_to "广告", advertisements_path %></li>
|
4
|
+
<li><%= link_to "位置", slots_path %></li>
|
5
|
+
<li><%= link_to "Github", "https://github.com/42up/ad_space" %></li>
|
6
|
+
<li><%= link_to "Doc", "https://github.com/42up/ad_space/blob/main/README.md" %></li>
|
7
|
+
</ul>
|
@@ -0,0 +1,44 @@
|
|
1
|
+
<%= form_with(model: slot) do |form| %>
|
2
|
+
<% if slot.errors.any? %>
|
3
|
+
<div style="color: red">
|
4
|
+
<h2><%= pluralize(slot.errors.count, "error") %> prohibited this slot from being saved:</h2>
|
5
|
+
|
6
|
+
<ul>
|
7
|
+
<% slot.errors.each do |error| %>
|
8
|
+
<li><%= error.full_message %></li>
|
9
|
+
<% end %>
|
10
|
+
</ul>
|
11
|
+
</div>
|
12
|
+
<% end %>
|
13
|
+
|
14
|
+
<div>
|
15
|
+
<%= form.label :name, style: "display: block" %>
|
16
|
+
<%= form.text_field :name %>
|
17
|
+
</div>
|
18
|
+
|
19
|
+
<div>
|
20
|
+
<%= form.label :remark, style: "display: block" %>
|
21
|
+
<%= form.text_area :remark %>
|
22
|
+
</div>
|
23
|
+
|
24
|
+
<div data-controller="image-upload", data-image-upload-url-value=<%= remove_demo_slot_path(slot)%>>
|
25
|
+
<%= form.label :demo, "Demo", style: "display: block" %>
|
26
|
+
<%= form.file_field :demo, data: {image_upload_target: "input"} %>
|
27
|
+
<% if slot.demo.attached? %>
|
28
|
+
<%= image_tag slot.demo.url, style: "max-width: 200px; height: auto;", data: {image_upload_target: "preview"} %>
|
29
|
+
<button type="button" data-image-upload-target="removeButton">移除图片</button>
|
30
|
+
<% else %>
|
31
|
+
<img data-image-upload-target="preview" style="max-width: 200px; height: auto;">
|
32
|
+
<button type="button" data-image-upload-target="removeButton" style="display: none;">移除图片</button>
|
33
|
+
<% end %>
|
34
|
+
</div>
|
35
|
+
|
36
|
+
<div>
|
37
|
+
<%= form.label :is_splash, "Is Splash" %>
|
38
|
+
<%= form.check_box :is_splash, { checked: form.object.is_splash } %>
|
39
|
+
</div>
|
40
|
+
|
41
|
+
<div>
|
42
|
+
<%= form.submit %>
|
43
|
+
</div>
|
44
|
+
<% end %>
|
@@ -0,0 +1,30 @@
|
|
1
|
+
<div id="<%= dom_id slot %>">
|
2
|
+
<p>
|
3
|
+
<strong>Name:</strong>
|
4
|
+
<%= slot.name %>
|
5
|
+
</p>
|
6
|
+
|
7
|
+
<p>
|
8
|
+
<strong>Uuid:</strong>
|
9
|
+
<%= slot.uuid %>
|
10
|
+
</p>
|
11
|
+
|
12
|
+
<p>
|
13
|
+
<strong>Remark:</strong>
|
14
|
+
<%= slot.remark %>
|
15
|
+
</p>
|
16
|
+
|
17
|
+
<p>
|
18
|
+
<strong>Is Splash:</strong>
|
19
|
+
<%= slot.is_splash %>
|
20
|
+
</p>
|
21
|
+
|
22
|
+
<% if slot.demo.attached? %>
|
23
|
+
<p>
|
24
|
+
<strong>Demo:</strong>
|
25
|
+
<%= image_tag slot.demo.url, style: "max-width: 200px; height: auto;" %>
|
26
|
+
</p>
|
27
|
+
<% end %>
|
28
|
+
|
29
|
+
|
30
|
+
</div>
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<p style="color: green"><%= notice %></p>
|
2
|
+
|
3
|
+
<h1>Slots</h1>
|
4
|
+
|
5
|
+
<div id="slots">
|
6
|
+
<% @slots.each do |slot| %>
|
7
|
+
<%= render slot %>
|
8
|
+
<p>
|
9
|
+
<%= link_to "Show this slot", slot %>
|
10
|
+
</p>
|
11
|
+
<% end %>
|
12
|
+
</div>
|
13
|
+
|
14
|
+
<%= link_to "New slot", new_slot_path %>
|
@@ -0,0 +1,43 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title><%= breadcrumbs.map(&:name).reverse.append("📺 AdSpace").join(" | ") %></title>
|
5
|
+
<%= csrf_meta_tags %>
|
6
|
+
<%= csp_meta_tag %>
|
7
|
+
|
8
|
+
<% if AdSpace::PACKED %>
|
9
|
+
<%= javascript_include_tag "/ad_space-assets/application", "data-turbo-track": "reload", defer: true %>
|
10
|
+
<%= stylesheet_link_tag "/ad_space-assets/application", "data-turbo-track": "reload", defer: true %>
|
11
|
+
<% else %>
|
12
|
+
<%= javascript_include_tag "application", "data-turbo-track": "reload", defer: true %>
|
13
|
+
<%= stylesheet_link_tag "ad_space/application", "data-turbo-track": "reload", defer: true %>
|
14
|
+
<% end %>
|
15
|
+
</head>
|
16
|
+
<body>
|
17
|
+
|
18
|
+
<div>
|
19
|
+
当前用户:
|
20
|
+
<b style="color: blue;"><%= user_identifier(_current_user) %></b>
|
21
|
+
</div>
|
22
|
+
|
23
|
+
<nav>
|
24
|
+
<ol style="display: flex; gap: 0.25rem; align-items: center; font-size: 0.875rem; list-style-type: none;">
|
25
|
+
<% breadcrumbs.each do |crumb| %>
|
26
|
+
<li>
|
27
|
+
<% if crumb.link? %>
|
28
|
+
<%= link_to crumb.name, crumb.path, style: "color: #6b7280;" %>
|
29
|
+
<% else %>
|
30
|
+
<span style="color: #111827;"><%= crumb.name %></span>
|
31
|
+
<% end %>
|
32
|
+
<% unless crumb == breadcrumbs.last %>
|
33
|
+
<span style="color: #6b7280;"> / </span>
|
34
|
+
<% end %>
|
35
|
+
</li>
|
36
|
+
<% end %>
|
37
|
+
</ol>
|
38
|
+
</nav>
|
39
|
+
|
40
|
+
<%= yield %>
|
41
|
+
|
42
|
+
</body>
|
43
|
+
</html>
|
data/config/routes.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
AdSpace::Engine.routes.draw do
|
2
|
+
resources :advertisements do
|
3
|
+
member do
|
4
|
+
post :publish
|
5
|
+
post :archive
|
6
|
+
delete 'remove_cover/:size', to: 'advertisements#remove_cover', as: :remove_cover
|
7
|
+
end
|
8
|
+
end
|
9
|
+
resources :slots do
|
10
|
+
member do
|
11
|
+
delete 'remove_demo', to: 'slots#remove_demo', as: :remove_demo
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
namespace :api do
|
16
|
+
namespace :v1 do
|
17
|
+
get :ads, to: "advertisements#index"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
root "dashboard#index"
|
22
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class CreateAdSpaceAdvertisements < ActiveRecord::Migration[7.1]
|
2
|
+
def change
|
3
|
+
create_table :ad_space_advertisements do |t|
|
4
|
+
t.string :name
|
5
|
+
t.string :uuid
|
6
|
+
t.belongs_to :slot, foreign_key: { to_table: :ad_space_slots }, null: false
|
7
|
+
t.datetime :published_at
|
8
|
+
t.datetime :started_at
|
9
|
+
t.datetime :ended_at
|
10
|
+
t.integer :platform
|
11
|
+
t.datetime :archived_at
|
12
|
+
t.integer :weight, default: 0
|
13
|
+
t.text :remark
|
14
|
+
t.integer :pv_count, default: 0
|
15
|
+
t.integer :uv_count, default: 0
|
16
|
+
t.integer :click_count, default: 0
|
17
|
+
|
18
|
+
t.timestamps
|
19
|
+
end
|
20
|
+
|
21
|
+
add_index :ad_space_advertisements, :platform
|
22
|
+
add_index :ad_space_advertisements, :published_at
|
23
|
+
add_index :ad_space_advertisements, [:started_at, :ended_at]
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
class AddSchemeUrlToAdSpaceAdvertisements < ActiveRecord::Migration[7.1]
|
2
|
+
def change
|
3
|
+
add_column :ad_space_advertisements, :ad_type, :integer
|
4
|
+
add_column :ad_space_advertisements, :visibility, :integer
|
5
|
+
add_column :ad_space_advertisements, :scheme_url, :string
|
6
|
+
add_column :ad_space_advertisements, :splash_ui_type, :integer
|
7
|
+
add_column :ad_space_advertisements, :splash_countdown, :integer, default: 5
|
8
|
+
add_column :ad_space_advertisements, :dsp_type, :integer
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
class AddTargetVersionCodeToAdSpaceAdvertisements < ActiveRecord::Migration[7.1]
|
2
|
+
def change
|
3
|
+
add_column :ad_space_advertisements, :target_version_codes, :bigint, array: true, default: [0]
|
4
|
+
add_index :ad_space_advertisements, :target_version_codes, using: :gin
|
5
|
+
end
|
6
|
+
end
|
data/lib/ad_space/app.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
module AdSpace
|
2
|
+
class App
|
3
|
+
class << self
|
4
|
+
def init(request:)
|
5
|
+
# Set the current host for ActiveStorage
|
6
|
+
begin
|
7
|
+
if defined?(ActiveStorage::Current)
|
8
|
+
ActiveStorage::Current.url_options = {protocol: request.protocol, host: request.host, port: request.port}
|
9
|
+
end
|
10
|
+
rescue => exception
|
11
|
+
Rails.logger.debug "[AdSpace] Failed to set ActiveStorage::Current.url_options, #{exception.inspect}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module AdSpace
|
2
|
+
class Configuration
|
3
|
+
attr_writer :app_name
|
4
|
+
attr_writer :root_path
|
5
|
+
attr_accessor :current_user
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@root_path = "/ad_space"
|
9
|
+
@app_name = ::Rails.application.class.to_s.split("::").first.underscore.humanize(keep_id_suffix: true)
|
10
|
+
end
|
11
|
+
|
12
|
+
def current_user_method(&block)
|
13
|
+
@current_user = block if block.present?
|
14
|
+
end
|
15
|
+
|
16
|
+
def current_user_method=(method)
|
17
|
+
@current_user = method if method.present?
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.configuration
|
22
|
+
@configuration ||= Configuration.new
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.configuration=(config)
|
26
|
+
@configuration = config
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.configure
|
30
|
+
yield configuration
|
31
|
+
end
|
32
|
+
end
|