From 2087a8d680224e67bba853e186ea390d5be549e7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Apr 2026 01:00:27 +0000 Subject: [PATCH 1/6] Initial plan From 8044ff1e8e9db56f0d8f6f31a348762e18981115 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Apr 2026 01:17:28 +0000 Subject: [PATCH 2/6] feat: add archive banner based on semester date range Agent-Logs-Url: https://github.com/berkeley-cdss/berkeley-class-site/sessions/b541e009-643e-4e51-a543-e291bb28f017 Co-authored-by: cycomachead <1505907+cycomachead@users.noreply.github.com> --- _config.yml | 3 ++ _includes/header_custom.html | 46 ++++++++++++++++++++++++++++ _plugins/config_validator.rb | 31 +++++++++++++++++-- _sass/custom/custom.scss | 31 +++++++++++++++++-- spec/jekyll/config_validator_spec.rb | 43 ++++++++++++++++++++++++++ 5 files changed, 149 insertions(+), 5 deletions(-) create mode 100644 _includes/header_custom.html create mode 100644 spec/jekyll/config_validator_spec.rb diff --git a/_config.yml b/_config.yml index 772a75c..3579ae0 100644 --- a/_config.yml +++ b/_config.yml @@ -36,6 +36,8 @@ gradescope_course_id: 123456 # you can find this in the Gradescope URL after /co bcourses_course_id: 123456 # Same as above, but for bCourses. Leave blank if not in use... ed_course_id: 123456 # Again, same as above. sememster: spYY | suYY | faYY # set for the current seemester +semester_start_date: '2025-01-21' # Required for the archive banner logic (YYYY-MM-DD) +semester_end_date: '2025-05-09' # Required for the archive banner logic (YYYY-MM-DD) # TODO(setup): Set this to the file path of your course logo image, or leave it blank to display your course name. # To edit the size and positioning of the logo/course name, see _sass/custom/course_overrides.scss logo: /assets/images/berkeley_walking_bear.png @@ -45,6 +47,7 @@ course_department: dsus # This should be the page of all class archives # Typically just / for DS courses (with a visible index page), or /archives if you're hosting your own, or a link to the inst.eecs page # If you have no archive page, comment this line out or leave blank. +# We recommend listing archived offerings as "Archive" or "Not Updated" in course listings. class_archive_path: / # TODO(setup): Remove this line if your favicon is named favicon.ico and is in the root directory, diff --git a/_includes/header_custom.html b/_includes/header_custom.html new file mode 100644 index 0000000..e74ba98 --- /dev/null +++ b/_includes/header_custom.html @@ -0,0 +1,46 @@ +
+ + diff --git a/_plugins/config_validator.rb b/_plugins/config_validator.rb index d49aa59..4bc0ebd 100644 --- a/_plugins/config_validator.rb +++ b/_plugins/config_validator.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require 'date' + # A UC Berkeley-specific validator for Jekyll sites # This class validates the following options: # 1) Ensures required attributes are present in the config file. @@ -44,7 +46,9 @@ class ConfigValidator url: :validate_clean_url, baseurl: :validate_semester_format, course_department: :inclusion_validator, - color_scheme: :inclusion_validator + color_scheme: :inclusion_validator, + semester_start_date: :validate_iso8601_date, + semester_end_date: :validate_iso8601_date }.freeze attr_accessor :config, :errors @@ -61,13 +65,15 @@ def validate send(validator, key, config[key.to_s]) if @config.key?(key.to_s) end + validate_semester_date_range + raise ConfigValidationError, errors if errors.length.positive? puts 'Passed Berkeley YAML Config Validations' end def validate_keys! - required_keys = %i[baseurl course_department] + required_keys = %i[baseurl course_department semester_start_date semester_end_date] required_keys.each do |key| errors << "#{key} is missing from site config" unless @config.key?(key.to_s) end @@ -95,6 +101,27 @@ def inclusion_validator(key, value) allowed = self.class.const_get("VALID_#{key.upcase}") errors << "`#{key}` must be one of #{allowed} (not '#{value}')" unless allowed.include?(value) end + + def validate_iso8601_date(key, value) + return if parse_iso8601_date(value) + + errors << "`#{key}` must be a valid ISO-8601 date string (YYYY-MM-DD), not '#{value}'" + end + + def validate_semester_date_range + start_date = parse_iso8601_date(config['semester_start_date']) + end_date = parse_iso8601_date(config['semester_end_date']) + return unless start_date && end_date + return unless start_date > end_date + + errors << '`semester_start_date` must be on or before `semester_end_date`' + end + + def parse_iso8601_date(value) + Date.iso8601(value.to_s) + rescue Date::Error + nil + end end end diff --git a/_sass/custom/custom.scss b/_sass/custom/custom.scss index 50df695..73cfb3a 100644 --- a/_sass/custom/custom.scss +++ b/_sass/custom/custom.scss @@ -137,9 +137,34 @@ p.note::before { @include btn-color($white, #6929c4); } - .btn-officehours{ - @include btn-color($white, #009d9a); - } +.btn-officehours{ + @include btn-color($white, #009d9a); +} + +.archive-banner { + display: none; + position: fixed; + top: 0; + right: 0; + left: 0; + z-index: 999; + padding: 0.75rem 1rem; + background-color: #d02670; + color: #fff; + font-weight: 700; + text-align: center; +} + +.archive-banner-link, +.archive-banner-link:visited { + color: #fff; + font-weight: 700; + text-decoration: underline; +} + +body.archive-banner-visible { + padding-top: 3rem; +} @if $color-scheme == dark { @include dark-mode-overrides; diff --git a/spec/jekyll/config_validator_spec.rb b/spec/jekyll/config_validator_spec.rb new file mode 100644 index 0000000..0ca43f4 --- /dev/null +++ b/spec/jekyll/config_validator_spec.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_relative '../../_plugins/config_validator' + +RSpec.describe Jekyll::ConfigValidator do + let(:base_config) do + { + 'baseurl' => '/sp26', + 'course_department' => 'dsus', + 'semester_start_date' => '2026-01-21', + 'semester_end_date' => '2026-05-09' + } + end + + it 'validates with required semester dates present' do + validator = described_class.new(base_config) + + expect { validator.validate }.not_to raise_error + end + + it 'requires semester_start_date and semester_end_date' do + config = base_config.except('semester_start_date', 'semester_end_date') + validator = described_class.new(config) + + expect { validator.validate } + .to raise_error(ConfigValidationError, /semester_start_date is missing.*semester_end_date is missing/m) + end + + it 'validates ISO-8601 semester dates' do + validator = described_class.new(base_config.merge('semester_start_date' => 'spring-2026')) + + expect { validator.validate } + .to raise_error(ConfigValidationError, /`semester_start_date` must be a valid ISO-8601 date string/) + end + + it 'requires the start date to be on or before the end date' do + validator = described_class.new(base_config.merge('semester_start_date' => '2026-06-01')) + + expect { validator.validate } + .to raise_error(ConfigValidationError, /`semester_start_date` must be on or before `semester_end_date`/) + end +end From 5e4b6261bd44111163c418d752989e05c000afe1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Apr 2026 01:18:30 +0000 Subject: [PATCH 3/6] chore: fix semester typo in config comments Agent-Logs-Url: https://github.com/berkeley-cdss/berkeley-class-site/sessions/b541e009-643e-4e51-a543-e291bb28f017 Co-authored-by: cycomachead <1505907+cycomachead@users.noreply.github.com> --- _config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_config.yml b/_config.yml index 3579ae0..cb486cb 100644 --- a/_config.yml +++ b/_config.yml @@ -35,7 +35,7 @@ course_email: COURSE_EMAIL@berkeley.edu gradescope_course_id: 123456 # you can find this in the Gradescope URL after /courses bcourses_course_id: 123456 # Same as above, but for bCourses. Leave blank if not in use... ed_course_id: 123456 # Again, same as above. -sememster: spYY | suYY | faYY # set for the current seemester +semester: spYY | suYY | faYY # set for the current semester semester_start_date: '2025-01-21' # Required for the archive banner logic (YYYY-MM-DD) semester_end_date: '2025-05-09' # Required for the archive banner logic (YYYY-MM-DD) # TODO(setup): Set this to the file path of your course logo image, or leave it blank to display your course name. From 178b83995d4d5dacdca028b91e15657c104f265e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Apr 2026 06:27:48 +0000 Subject: [PATCH 4/6] fix: validate archive path and use berkeley rose color variable Agent-Logs-Url: https://github.com/berkeley-cdss/berkeley-class-site/sessions/3cf00aba-dd2e-446c-ba39-1db759bf2bf3 Co-authored-by: cycomachead <1505907+cycomachead@users.noreply.github.com> --- _config.yml | 5 ++--- _plugins/config_validator.rb | 19 +++++++++++++++++-- _sass/berkeley/variables.scss | 1 + _sass/custom/custom.scss | 2 +- spec/jekyll/config_validator_spec.rb | 22 +++++++++++++++++++++- 5 files changed, 42 insertions(+), 7 deletions(-) diff --git a/_config.yml b/_config.yml index cb486cb..ca4ac35 100644 --- a/_config.yml +++ b/_config.yml @@ -44,9 +44,8 @@ logo: /assets/images/berkeley_walking_bear.png # This should be one of eecs, dsus, stat # (Future) This will control some footer text, and later custom styling. course_department: dsus -# This should be the page of all class archives -# Typically just / for DS courses (with a visible index page), or /archives if you're hosting your own, or a link to the inst.eecs page -# If you have no archive page, comment this line out or leave blank. +# This path/URL is used by the archive banner shown when the site date is outside the semester window. +# Keep this key present. Default `/` is fine for class-listing homepages, and `/archive` is also a good option. # We recommend listing archived offerings as "Archive" or "Not Updated" in course listings. class_archive_path: / diff --git a/_plugins/config_validator.rb b/_plugins/config_validator.rb index 4bc0ebd..8d845f3 100644 --- a/_plugins/config_validator.rb +++ b/_plugins/config_validator.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'date' +require 'uri' # A UC Berkeley-specific validator for Jekyll sites # This class validates the following options: @@ -48,7 +49,8 @@ class ConfigValidator course_department: :inclusion_validator, color_scheme: :inclusion_validator, semester_start_date: :validate_iso8601_date, - semester_end_date: :validate_iso8601_date + semester_end_date: :validate_iso8601_date, + class_archive_path: :validate_archive_path }.freeze attr_accessor :config, :errors @@ -73,7 +75,7 @@ def validate end def validate_keys! - required_keys = %i[baseurl course_department semester_start_date semester_end_date] + required_keys = %i[baseurl course_department semester_start_date semester_end_date class_archive_path] required_keys.each do |key| errors << "#{key} is missing from site config" unless @config.key?(key.to_s) end @@ -117,6 +119,19 @@ def validate_semester_date_range errors << '`semester_start_date` must be on or before `semester_end_date`' end + def validate_archive_path(_key, value) + path = value.to_s.strip + return errors << '`class_archive_path` cannot be blank' if path.empty? + return if path.match?(%r{^/}) + + uri = URI.parse(path) + return if uri.is_a?(URI::HTTP) && uri.host + + errors << "`class_archive_path` must be an absolute path starting with `/` or a full URL, not '#{value}'" + rescue URI::InvalidURIError + errors << "`class_archive_path` must be an absolute path starting with `/` or a full URL, not '#{value}'" + end + def parse_iso8601_date(value) Date.iso8601(value.to_s) rescue Date::Error diff --git a/_sass/berkeley/variables.scss b/_sass/berkeley/variables.scss index 366f259..828c41b 100644 --- a/_sass/berkeley/variables.scss +++ b/_sass/berkeley/variables.scss @@ -12,6 +12,7 @@ $serif-font-family: 'Source Serif 4', 'Georiga', 'Baskerville', serif; $berkeley-blue-old: #003262; $berkeley-blue: #002676; // rgb(0, 38, 118); +$berkeley-rose-medium: #d02670; // Colors $link-color: $berkeley-blue; diff --git a/_sass/custom/custom.scss b/_sass/custom/custom.scss index 73cfb3a..a9d693e 100644 --- a/_sass/custom/custom.scss +++ b/_sass/custom/custom.scss @@ -149,7 +149,7 @@ p.note::before { left: 0; z-index: 999; padding: 0.75rem 1rem; - background-color: #d02670; + background-color: $berkeley-rose-medium; color: #fff; font-weight: 700; text-align: center; diff --git a/spec/jekyll/config_validator_spec.rb b/spec/jekyll/config_validator_spec.rb index 0ca43f4..b617bf3 100644 --- a/spec/jekyll/config_validator_spec.rb +++ b/spec/jekyll/config_validator_spec.rb @@ -9,7 +9,8 @@ 'baseurl' => '/sp26', 'course_department' => 'dsus', 'semester_start_date' => '2026-01-21', - 'semester_end_date' => '2026-05-09' + 'semester_end_date' => '2026-05-09', + 'class_archive_path' => '/' } end @@ -27,6 +28,25 @@ .to raise_error(ConfigValidationError, /semester_start_date is missing.*semester_end_date is missing/m) end + it 'requires class_archive_path' do + validator = described_class.new(base_config.except('class_archive_path')) + + expect { validator.validate } + .to raise_error(ConfigValidationError, /class_archive_path is missing from site config/) + end + + it 'allows class_archive_path as a full URL' do + validator = described_class.new(base_config.merge('class_archive_path' => 'https://c88c.org/archive')) + + expect { validator.validate }.not_to raise_error + end + + it 'rejects class_archive_path values that are not paths or full URLs' do + validator = described_class.new(base_config.merge('class_archive_path' => 'archive')) + expected = %r{`class_archive_path` must be an absolute path starting with `/` or a full URL} + expect { validator.validate }.to raise_error(ConfigValidationError, expected) + end + it 'validates ISO-8601 semester dates' do validator = described_class.new(base_config.merge('semester_start_date' => 'spring-2026')) From 05d2b88d6cbec8ed3e4308d25b13309592b56924 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 28 Apr 2026 02:11:57 +0000 Subject: [PATCH 5/6] refactor: use liquid for archive link in banner Agent-Logs-Url: https://github.com/berkeley-cdss/berkeley-class-site/sessions/ee13ee41-b0a1-4a91-b47b-76ed942f77f1 Co-authored-by: cycomachead <1505907+cycomachead@users.noreply.github.com> --- _includes/header_custom.html | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/_includes/header_custom.html b/_includes/header_custom.html index e74ba98..d42f67d 100644 --- a/_includes/header_custom.html +++ b/_includes/header_custom.html @@ -1,15 +1,13 @@