govuk_web_banners 0.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ddede52ca24adcae2aa28d4dc5f449c53be00ab21806415acaaa6b49a740f27a
4
- data.tar.gz: e87d8c81b5efd160291082c460a7136454be0fc5fc2f981ea065a1dfba10d48d
3
+ metadata.gz: bb07dddf7ac05db797217587e7f2c33f08bf95e237744e9b8a9ef5fc5b0ed6b6
4
+ data.tar.gz: 8ad155d095c70dbbc54bd1998ead9cffb2a26c8be03dfeb7911cc58b49e40ac4
5
5
  SHA512:
6
- metadata.gz: d2c9d65a8a83b580c26dd1867a06f8cc00e0dae232994c853e43ddcc32818f8dec30346e9486b234d4dc6cb6a97b070f4072b127053b3212f7e1e3a45842f8a9
7
- data.tar.gz: d87961bc141844b15a3666f0b33a29c023c0c6b9515378497e5a77069a1e70ec873bbb970bc8c165f9fe86e101effc6d4ead173c52b22f1ecb5fce19028283de
6
+ metadata.gz: 4d1c6549546b14a2db797bd2b2ac4fbfc5335b96b7e2f9be8f1b63da7fb9496771bbea9aa3e45a2f72f885f1180015b27ccba97b7e86910690b31c3bb0e22ba8
7
+ data.tar.gz: 85c422e6d1658f144fd275fecf15f26bdd8408421ed9cb7096d55a850df2eeaeb972fdb2ea5235eeb346b3c1c4376d2bd506c90e86d46be7de98047ac0def595
data/README.md CHANGED
@@ -3,7 +3,6 @@ Proof of Concept for centralising handling of Recruitment, Global, and Emergency
3
3
  banners (currently spread across apps)
4
4
 
5
5
  ## Usage
6
- Currently supports the emergency banner and recruitment banners.
7
6
 
8
7
  ## Adding the gem to your application
9
8
  Add this line to your application's Gemfile:
@@ -30,7 +29,13 @@ Add the JS dependencies to your existing asset dependencies file:
30
29
 
31
30
  ## Adding emergency banners
32
31
 
33
- Emergency banners are passed to the [Layout for Public](https://components.publishing.service.gov.uk/component-guide/layout_for_public) component, which is currently applied to each frontend app by the slimmer/static wrapping code - so you will only need to handle emergency banners in your app when Slimmer is removed from it. Once Slimmer is removed and you are calling the layout_for_public component directly in your app, add the emergency banner partial to the component's `emergency_banner:` key:
32
+ Emergency banners are passed to the [Layout for
33
+ Public](https://components.publishing.service.gov.uk/component-guide/layout_for_public)
34
+ component, which is currently applied to each frontend app by the slimmer/static
35
+ wrapping code - so you will only need to handle emergency banners in your app
36
+ when Slimmer is removed from it. Once Slimmer is removed and you are calling the
37
+ layout_for_public component directly in your app, add the emergency banner
38
+ partial to the component's `emergency_banner:` key:
34
39
 
35
40
  ```
36
41
  <%= render "govuk_publishing_components/components/layout_for_public", {
@@ -40,7 +45,8 @@ Emergency banners are passed to the [Layout for Public](https://components.publi
40
45
  ...etc
41
46
  ```
42
47
 
43
- if you want the homepage variant of the banner, you can add `homepage: true` to the render call:
48
+ if you want the homepage variant of the banner, you can add `homepage: true` to
49
+ the render call:
44
50
 
45
51
  ```
46
52
  <%= render "govuk_publishing_components/components/layout_for_public", {
@@ -50,9 +56,16 @@ if you want the homepage variant of the banner, you can add `homepage: true` to
50
56
  ...etc
51
57
  ```
52
58
 
53
- Your app will also need access to the whitehall shared redis cluster (which is used to signal the emergency banner is up), via the `EMERGENCY_BANNER_REDIS_URL` environment variable (here is an example of [setting this in govuk-helm-charts](https://github.com/alphagov/govuk-helm-charts/blob/7818eaa22fc194d21548f316bcc5a46c2023dcb6/charts/app-config/values-staging.yaml#L3337-L3338)). You'll need to allow this in all three environments.
59
+ Your app will also need access to the whitehall shared redis cluster (which is
60
+ used to signal the emergency banner is up), via the `EMERGENCY_BANNER_REDIS_URL`
61
+ environment variable (here is an example of [setting this in
62
+ govuk-helm-charts](https://github.com/alphagov/govuk-helm-charts/blob/7818eaa22fc194d21548f316bcc5a46c2023dcb6/charts/app-config/values-staging.yaml#L3337-L3338)).
63
+ You'll need to allow this in all three environments.
54
64
 
55
- Finally, you'll need to configure a connection to the redis cluster, available at `Rails.application.config.emergency_banner_redis_client`. The suggested way of doing this is creating an initializer at `/config/initializers/govuk_web_banners.rb` with the content:
65
+ Finally, you'll need to configure a connection to the redis cluster, available
66
+ at `Rails.application.config.emergency_banner_redis_client`. The suggested way
67
+ of doing this is creating an initializer at
68
+ `/config/initializers/govuk_web_banners.rb` with the content:
56
69
 
57
70
  ```
58
71
  Rails.application.config.emergency_banner_redis_client = Redis.new(
@@ -61,11 +74,100 @@ Rails.application.config.emergency_banner_redis_client = Redis.new(
61
74
  )
62
75
  ```
63
76
 
77
+ ## Adding global banners
78
+
79
+ Global banners are passed to the [Layout for
80
+ Public](https://components.publishing.service.gov.uk/component-guide/layout_for_public)
81
+ component, which is currently applied to each frontend app by the slimmer/static
82
+ wrapping code - so you will only need to handle global banners in your app when
83
+ Slimmer is removed from it. Once Slimmer is removed and you are calling the
84
+ layout_for_public component directly in your app, add the global banner partial
85
+ to the component's `global_banner:` key:
86
+
87
+ ```
88
+ <%= render "govuk_publishing_components/components/layout_for_public", {
89
+ draft_watermark: draft_environment,
90
+ global_banner: render("govuk_web_banners/global_banner"), # <-- Add this line
91
+ full_width: false,
92
+ ...etc
93
+ ```
94
+
95
+ ## Updating banner information in the gem
96
+
97
+ Data for the global banners can be found at
98
+ `config/govuk_web_banners/global_banners.yml`. To add a banner to the config,
99
+ add an entry under the banners: array. Note that this array must always be
100
+ valid, so if there are no banners in the file, it must contain at least
101
+ `global_banners: []`
102
+
103
+ ### Example banner entry
104
+
105
+ ```
106
+ global_banners:
107
+ - name: Banner 1
108
+ title: "Register to Vote"
109
+ title_href: /register-to-vote
110
+ text: "You must register to vote before the election"
111
+ permanent: false
112
+ exclude_paths:
113
+ - /find-your-local-electoral-office
114
+ start_date: 2024/10/21
115
+ end_date: 2024/11/18
116
+ ```
117
+ Each banner must include a `title`, `title_href`, `text`, and a valid `start_date`.
118
+ `title_href` can be either a valid URL or a path on gov.uk.
119
+
120
+ > [!NOTE]
121
+ >
122
+ > `start_date` is **mandatory** here (unlike in recruitment banners) because it's
123
+ > needed to create a banner_version to pass to the underlying component. This lets
124
+ > the component reset the cookie that records how many times a banner has been seen
125
+ > (by default banners are shown only three times, see the `permanent` option below.)
126
+
127
+ Optional keys are:
128
+ - `name` (an identifying name for this banner, not rendered
129
+ anywhere)
130
+ - `permanent` (defaults to false. If false, banner is hidden if the user
131
+ has consented to cookies and has seen this banner more than 3 times)
132
+ - `exclude_paths` an array of paths on which the banner should not be shown.
133
+ Note that the banner is never shown on the path it points to, this
134
+ list is to include any additional pages.
135
+ - `end_date` (the banner stops being active at the *start* of the day
136
+ specified as `end_date`). Start and end dates must be in the YYYY/MM/DD
137
+ format parsable as a YAML -> Date.
138
+
139
+ ### Validations on the global banners config file
140
+
141
+ The config file will be checked during CI, so an invalid file can't be released
142
+ as a gem and we are nudged to make sure it's kept tidy. These checks include:
143
+
144
+ * the global_banners array must be a valid YAML array
145
+ * all banners have a `title`, `title_href`, and `info_text`.
146
+ * all banners must have a valid `start_date`.
147
+
148
+ It will also display warnings (but not fail CI)
149
+
150
+ * if there are banners that have expired - you are encouraged to remove
151
+ obsolete config, but it will not prevent you merging changes.
152
+ * if `title_href` of any banner points to a page that are not currently live on
153
+ GOV.UK - this may be intentional (if the banner points to a page that isn't
154
+ yet published), or it may indicate a typo in the path.
155
+ * if any `exclude_paths` value points to a page that is not currently live on
156
+ GOV.UK - this may be intentional, or it may indicate a typo in the path.
157
+ * if two global banners will be active on the same day (this is only a warning
158
+ because ultimately we may need this, but the current iteration of the
159
+ component does not support it)
160
+
161
+ Note that some of this validation code is in the
162
+ `/lib/govuk_web_banners/validators/global_banner.rb` file, which should be
163
+ tested to ensure the checking is valid, but will not be bundled into the
164
+ released gem.
165
+
64
166
  ## Adding recruitment banners
65
167
 
66
168
  Add a call to the partial in the layout or view that you want banners to appear
67
- in (typically recruitment banners should be in the layout, below the breadcrumbs
68
- and just above the `main` element):
169
+ in (typically recruitment banners should be in the layout, below the
170
+ breadcrumbs and just above the `main` element):
69
171
 
70
172
  ```
71
173
  <%= render "govuk_web_banners/recruitment_banner" %>
@@ -102,8 +204,8 @@ banners:
102
204
  page_paths:
103
205
  - /
104
206
  - /foreign-travel-advice
105
- start_date: 21/10/2024
106
- end_date: 18/11/2024
207
+ start_date: 2024/10/21
208
+ end_date: 2024/11/18
107
209
  ```
108
210
 
109
211
  The required keys are `suggestion_text`, `suggestion_link_text`, and
@@ -113,10 +215,10 @@ paths on which the banner should be shown).
113
215
  Optional keys are `name` (an identifying name for this banner, not rendered
114
216
  anywhere), and `start_date` / `end_date` (the banner becomes active at the start
115
217
  of the day specified as `start_date`, and stops at the *start* of the day
116
- specified as `end_date`). Start and end dates must be in the DD/MM/YYYY format
218
+ specified as `end_date`). Start and end dates must be in the YYYY/MM/DD format
117
219
  parsable as a YAML -> Date.
118
220
 
119
- ### Keeping the config file valid and tidy
221
+ ### Validations on the recruitment banners config file
120
222
 
121
223
  The config file will be checked during CI, so an invalid file can't be released
122
224
  as a gem and we are forced to make sure it's kept tidy. These checks include:
@@ -137,8 +239,9 @@ It will also display warnings (but not fail CI)
137
239
  may indicate a typo in the path.
138
240
 
139
241
  Note that some of this validation code is in the
140
- lib/govuk_web_banners/validators path, which should be tested to ensure the
141
- checking is valid, but will not be bundled into the released gem.
242
+ `lib/govuk_web_banners/validator/recruitment_banner.rb` file, which should be
243
+ tested to ensure the checking is valid, but will not be bundled into the
244
+ released gem.
142
245
 
143
246
  ## License
144
247
  The gem is available as open source under the terms of the [MIT
data/Rakefile CHANGED
@@ -12,15 +12,13 @@ require "bundler/gem_tasks"
12
12
  RuboCop::RakeTask.new
13
13
  RSpec::Core::RakeTask.new
14
14
 
15
+ require "govuk_web_banners/validators/global_banner"
15
16
  require "govuk_web_banners/validators/recruitment_banner"
16
17
  require "rainbow"
17
18
 
18
- desc "show errors in the live config"
19
- task :check_config do
20
- validator = GovukWebBanners::Validators::RecruitmentBanner.new(GovukWebBanners::RecruitmentBanner.all_banners)
21
-
19
+ def output_validator_info(validator, name)
22
20
  if !validator.valid?
23
- puts Rainbow("\nLive config contains errors!").red
21
+ puts Rainbow("\nLive #{name} config contains errors!").red
24
22
  validator.errors.each_key do |key|
25
23
  puts(key)
26
24
  validator.errors[key].each { |error| puts(" - #{error}") }
@@ -28,19 +26,35 @@ task :check_config do
28
26
  puts
29
27
  exit(1)
30
28
  elsif validator.warnings?
31
- puts Rainbow("\nLive config is valid, but with warnings").yellow
29
+ puts Rainbow("\nLive #{name} config is valid, but with warnings").yellow
32
30
  validator.warnings.each_key do |key|
33
31
  puts(key)
34
32
  validator.warnings[key].each { |warnings| puts(" - #{warnings}") }
35
33
  end
36
34
  puts
37
35
  else
38
- puts Rainbow("\nLive config is valid!\n").green
36
+ puts Rainbow("\nLive #{name} config is valid!\n").green
39
37
  end
38
+ end
39
+
40
+ desc "show errors in the live global banner config"
41
+ task :check_global_config do
42
+ validator = GovukWebBanners::Validators::GlobalBanner.new(GovukWebBanners::GlobalBanner.all_banners)
43
+ output_validator_info(validator, "global banner")
44
+ rescue StandardError => e
45
+ puts(e)
46
+ puts Rainbow("Live global banner config could not be read (if there are no banners, check global_banner key is marked as an empty array - global_banners: [])").red
47
+ exit(1)
48
+ end
49
+
50
+ desc "show errors in the live recruitment banner config"
51
+ task :check_recruitment_config do
52
+ validator = GovukWebBanners::Validators::RecruitmentBanner.new(GovukWebBanners::RecruitmentBanner.all_banners)
53
+ output_validator_info(validator, "recruitment banner")
40
54
  rescue StandardError => e
41
55
  puts(e)
42
- puts("Live config could not be read (if there are no banners, check banner key is marked as an empty array - banners: [])")
56
+ puts Rainbow("Live recruitment banner config could not be read (if there are no banners, check banner key is marked as an empty array - banners: [])").red
43
57
  exit(1)
44
58
  end
45
59
 
46
- task default: %i[check_config rubocop spec]
60
+ task default: %i[check_global_config check_recruitment_config rubocop spec]
@@ -1 +1,2 @@
1
1
  //= require govuk_publishing_components/components/intervention
2
+ //= require govuk_publishing_components/components/global-banner
@@ -0,0 +1,10 @@
1
+ <% global_banners = GovukWebBanners::GlobalBanner.for_path(request.path) %>
2
+ <% if global_banners.any? %>
3
+ <%= render "govuk_publishing_components/components/global_banner", {
4
+ title: global_banners.first.title,
5
+ title_path: global_banners.first.title_href,
6
+ text: global_banners.first.text,
7
+ permanent: global_banners.first.permanent,
8
+ banner_version: global_banners.first.version,
9
+ } %>
10
+ <% end %>
@@ -0,0 +1,2 @@
1
+ # Check README.md for how to format this file
2
+ global_banners: []
@@ -1,38 +1,6 @@
1
- # Example usage of adding a banner to the banners list
2
-
3
- # banners:
4
- # - name: Banner 1
5
- # suggestion_text: "Help improve GOV.UK"
6
- # suggestion_link_text: "Sign up to take part in user research (opens in a new tab)"
7
- # survey_url: https://google.com
8
- # page_paths:
9
- # - /
10
- # - /foreign-travel-advice
11
- # start_date: 21/10/2024
12
- # end_date: 18/11/2024
13
-
14
- # start_date and end_date are optional, everything else is mandatory.
15
- #
16
- # Note that this file must contain a valid banners array, so if there are no banners
17
- # currently included, the file should at least contain banners: []
1
+ # Check README.md for how to format this file
18
2
  banners:
19
- - name: HMRC banner 03/01/2025
20
- suggestion_text: "Help improve GOV.UK"
21
- suggestion_link_text: "Sign up to take part in user research (opens in a new tab)"
22
- survey_url: https://survey.take-part-in-research.service.gov.uk/jfe/form/SV_74GjifgnGv6GsMC?Source=BannerList_HMRC_CCG_Compliance
23
- page_paths:
24
- # government-frontend
25
- - /government/collections/tax-compliance-detailed-information
26
- - /government/collections/hm-revenue-and-customs-compliance-checks-factsheets
27
- - /difficulties-paying-hmrc
28
- - /tax-help
29
- - /get-help-hmrc-extra-support
30
- - /guidance/voluntary-and-community-sector-organisations-who-can-give-you-extra-support
31
- - /tax-appeals
32
- - /guidance/tax-disputes-alternative-dispute-resolution-adr
33
- start_date: 03/01/2025
34
- end_date: 31/01/2025
35
- - name: UKVI banner 30/12/2025
3
+ - name: UKVI banner 2025/12/30
36
4
  suggestion_text: "Help improve GOV.UK"
37
5
  suggestion_link_text: "Take part in user research (opens in a new tab)"
38
6
  survey_url: https://surveys.publishing.service.gov.uk/s/XYVRGN/
@@ -42,5 +10,37 @@ banners:
42
10
  # collections
43
11
  - /browse/visas-immigration
44
12
  - /government/organisations/uk-visas-and-immigration
45
- start_date: 30/12/2024
46
- end_date: 24/02/2025
13
+ start_date: 2024/12/30
14
+ end_date: 2025/02/24
15
+ - name: HMRC banner 2025/02/13
16
+ suggestion_text: "Help improve GOV.UK"
17
+ suggestion_link_text: "Sign up to take part in user research (opens in a new tab)"
18
+ survey_url: https://survey.take-part-in-research.service.gov.uk/jfe/form/SV_74GjifgnGv6GsMC?Source=BannerList_HMRC_Maternity_Pay_TAD
19
+ page_paths:
20
+ # government-frontend
21
+ - /shared-parental-leave-and-pay
22
+ - /maternity-pay-leave
23
+ - /paternity-pay-leave
24
+ - /working-when-pregnant-your-rights
25
+ - /employee-rights-when-on-leave
26
+ # smart-answers
27
+ - /maternity-paternity-pay-leave
28
+ # frontend
29
+ - /plan-shared-parental-leave-pay
30
+ start_date: 2025/02/13
31
+ end_date: 2025/03/13
32
+ - name: HMRC banner (employers) 2025/02/13
33
+ suggestion_text: "Help improve GOV.UK"
34
+ suggestion_link_text: "Sign up to take part in user research (opens in a new tab)"
35
+ survey_url: https://survey.take-part-in-research.service.gov.uk/jfe/form/SV_74GjifgnGv6GsMC?Source=BannerList_HMRC_Maternity_Pay_Employers_TAD
36
+ page_paths:
37
+ # government-frontend
38
+ - /shared-parental-leave-and-pay-employer-guide
39
+ - /employers-maternity-pay-leave
40
+ - /employers-paternity-pay-leave
41
+ - /employers-parental-bereavement-pay-leave
42
+ - /employers-adoption-pay-leave
43
+ # smart-answers
44
+ - /maternity-paternity-calculator
45
+ start_date: 2025/02/13
46
+ end_date: 2025/03/13
@@ -0,0 +1,46 @@
1
+ module GovukWebBanners
2
+ class GlobalBanner
3
+ BANNER_CONFIG_FILE = "../../config/govuk_web_banners/global_banners.yml".freeze
4
+
5
+ def self.for_path(path)
6
+ active_banners.reject { |b| b.exclude_paths.include?(path) }
7
+ end
8
+
9
+ def self.active_banners
10
+ all_banners.select(&:active?)
11
+ end
12
+
13
+ def self.all_banners
14
+ global_banners_file_path = Rails.root.join(__dir__, BANNER_CONFIG_FILE)
15
+ global_banners_data = YAML.load_file(global_banners_file_path)
16
+ global_banners_data["global_banners"].map { |attributes| GlobalBanner.new(attributes:) }
17
+ end
18
+
19
+ attr_reader :name, :title, :title_href, :text, :start_date, :end_date, :show_arrows, :permanent, :exclude_paths
20
+
21
+ def initialize(attributes:)
22
+ @name = attributes["name"]
23
+
24
+ @title = attributes["title"]
25
+ @title_href = attributes["title_href"]
26
+ @text = attributes["text"]
27
+
28
+ @start_date = attributes["start_date"] ? ActiveSupport::TimeZone[GovukWebBanners::TIME_ZONE].parse(attributes["start_date"]) : nil
29
+ @end_date = attributes["end_date"] ? ActiveSupport::TimeZone[GovukWebBanners::TIME_ZONE].parse(attributes["end_date"]) : Time.now + 10.years
30
+ @show_arrows = attributes["show_arrows"] == "true"
31
+ @permanent = attributes["permanent"] == "true"
32
+ @exclude_paths = attributes["exclude_paths"] || []
33
+ @exclude_paths << title_href if title_href&.start_with?("/")
34
+ end
35
+
36
+ # NB: .between? is inclusive. To make it exclude the end date, we set the end range as
37
+ # 1 second earlier.
38
+ def active?
39
+ Time.zone.now.between?(start_date, end_date - 1.second)
40
+ end
41
+
42
+ def version
43
+ start_date.getutc.to_i
44
+ end
45
+ end
46
+ end
@@ -26,8 +26,8 @@ module GovukWebBanners
26
26
  @suggestion_link_text = attributes["suggestion_link_text"]
27
27
  @survey_url = attributes["survey_url"]
28
28
  @page_paths = attributes["page_paths"]
29
- @start_date = attributes["start_date"] ? Time.parse(attributes["start_date"]) : Time.at(0)
30
- @end_date = attributes["end_date"] ? Time.parse(attributes["end_date"]) : Time.now + 10.years
29
+ @start_date = attributes["start_date"] ? ActiveSupport::TimeZone[GovukWebBanners::TIME_ZONE].parse(attributes["start_date"]) : Time.at(0)
30
+ @end_date = attributes["end_date"] ? ActiveSupport::TimeZone[GovukWebBanners::TIME_ZONE].parse(attributes["end_date"]) : Time.now + 10.years
31
31
  end
32
32
 
33
33
  # NB: .between? is inclusive. To make it exclude the end date, we set the end range as
@@ -1,3 +1,3 @@
1
1
  module GovukWebBanners
2
- VERSION = "0.4.0".freeze
2
+ VERSION = "1.0.0".freeze
3
3
  end
@@ -1,9 +1,11 @@
1
1
  require "govuk_publishing_components"
2
2
 
3
+ require "govuk_web_banners/global_banner"
3
4
  require "govuk_web_banners/emergency_banner"
4
5
  require "govuk_web_banners/engine"
5
6
  require "govuk_web_banners/recruitment_banner"
6
7
  require "govuk_web_banners/version"
7
8
 
8
9
  module GovukWebBanners
10
+ TIME_ZONE = "London".freeze
9
11
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: govuk_web_banners
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GOV.UK Dev
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-01-20 00:00:00.000000000 Z
10
+ date: 2025-02-04 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: govuk_app_config
@@ -176,11 +176,14 @@ files:
176
176
  - app/assets/config/govuk_web_banners_manifest.js
177
177
  - app/assets/javascripts/govuk_web_banners/dependencies.js
178
178
  - app/views/govuk_web_banners/_emergency_banner.html.erb
179
+ - app/views/govuk_web_banners/_global_banner.html.erb
179
180
  - app/views/govuk_web_banners/_recruitment_banner.html.erb
181
+ - config/govuk_web_banners/global_banners.yml
180
182
  - config/govuk_web_banners/recruitment_banners.yml
181
183
  - lib/govuk_web_banners.rb
182
184
  - lib/govuk_web_banners/emergency_banner.rb
183
185
  - lib/govuk_web_banners/engine.rb
186
+ - lib/govuk_web_banners/global_banner.rb
184
187
  - lib/govuk_web_banners/recruitment_banner.rb
185
188
  - lib/govuk_web_banners/version.rb
186
189
  homepage: https://github.com/alphagov/govuk_web_banners