diff --git a/app/controllers/contests_controller.rb b/app/controllers/contests_controller.rb index 6d7928b..a3d9ad1 100644 --- a/app/controllers/contests_controller.rb +++ b/app/controllers/contests_controller.rb @@ -69,6 +69,6 @@ class ContestsController < ApplicationController end def contest_params - params.expect(contest: [ :name, :team, :allow_registration ]) + params.expect(contest: [ :name, :team, :allow_registration, :slug ]) end end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 13595bc..95b8859 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -1,13 +1,12 @@ class SessionsController < ApplicationController allow_unauthenticated_access only: %i[ new create ] rate_limit to: 10, within: 3.minutes, only: :create, with: -> { redirect_to new_session_url, alert: "Try again later." } + before_action :skip_authorization def new - skip_authorization end def create - skip_authorization if user = User.authenticate_by(params.permit(:email_address, :password)) start_new_session_for user redirect_to after_authentication_url diff --git a/app/models/contest.rb b/app/models/contest.rb index 044217d..3bda000 100644 --- a/app/models/contest.rb +++ b/app/models/contest.rb @@ -5,6 +5,7 @@ # id :integer not null, primary key # allow_registration :boolean default(FALSE) # name :string +# slug :string # team :boolean default(FALSE) # created_at :datetime not null # updated_at :datetime not null @@ -24,4 +25,7 @@ class Contest < ApplicationRecord has_many :completions, dependent: :destroy has_many :contestants, dependent: :destroy has_many :puzzles, dependent: :destroy + + validates :name, presence: true + validates :slug, presence: true, format: { with: /\A(\w|-)*\z/, message: 'Only alphanumeric characters, "-" and "_" allowed.' } end diff --git a/app/views/contests/_form.html.slim b/app/views/contests/_form.html.slim index fddfd6b..6e008b1 100644 --- a/app/views/contests/_form.html.slim +++ b/app/views/contests/_form.html.slim @@ -4,6 +4,12 @@ .form-floating = form.text_field :name, autocomplete: "off", class: "form-control" = form.label :name, class: "required" + .row.mb-3 + .col + .form-floating + = form.text_field :slug, autocomplete: "off", class: "form-control" + = form.label :slug, class: "required" + .form-text This will be used for building the public scoreboard URL: https://puzzle-scoreboard.org/public/<slug>. .row.mb-3 .col .form-check.form-switch diff --git a/app/views/contests/show.html.slim b/app/views/contests/show.html.slim index 39c11f6..cda8dc5 100644 --- a/app/views/contests/show.html.slim +++ b/app/views/contests/show.html.slim @@ -1,4 +1,4 @@ -.row.mb-2 +.row.mb-4 .col css: .badges { margin-top: -18px; position: absolute; } @@ -6,10 +6,16 @@ - @badges.each do |badge| span.badge.text-bg-info.me-2 = badge + +.row.mb-4 + .col .float-end a.btn.btn-primary href=edit_contest_path(@contest) | Edit contest - + p + |> Public scoreboard: + = link_to root_url + "public/#{@contest.slug}" + .row.mb-4 .col-6 .row diff --git a/config/initializers/form_errors.rb b/config/initializers/form_errors.rb index 54cd7b4..a95e2c6 100644 --- a/config/initializers/form_errors.rb +++ b/config/initializers/form_errors.rb @@ -2,9 +2,9 @@ ActionView::Base.field_error_proc = proc do |html_tag, instance| if html_tag.include? "" + appended_html = "
#{instance.error_message.map(&:capitalize).uniq.join(", ")}
" else - appended_html = "
#{instance.error_message.humanize}
" + appended_html = "
#{instance.error_message.capitalize}
" end html_tag + appended_html.html_safe else diff --git a/db/migrate/20250322164205_add_slug_to_contest.rb b/db/migrate/20250322164205_add_slug_to_contest.rb new file mode 100644 index 0000000..da9deb4 --- /dev/null +++ b/db/migrate/20250322164205_add_slug_to_contest.rb @@ -0,0 +1,5 @@ +class AddSlugToContest < ActiveRecord::Migration[8.0] + def change + add_column :contests, :slug, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 590f8bd..00ec1c3 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.0].define(version: 2025_03_22_071308) do +ActiveRecord::Schema[8.0].define(version: 2025_03_22_164205) do create_table "active_storage_attachments", force: :cascade do |t| t.string "name", null: false t.string "record_type", null: false @@ -67,6 +67,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_03_22_071308) do t.datetime "updated_at", null: false t.boolean "team", default: false t.boolean "allow_registration", default: false + t.string "slug" t.index ["user_id"], name: "index_contests_on_user_id" end diff --git a/test/fixtures/contests.yml b/test/fixtures/contests.yml index d1367ad..df508c3 100644 --- a/test/fixtures/contests.yml +++ b/test/fixtures/contests.yml @@ -7,6 +7,7 @@ # id :integer not null, primary key # allow_registration :boolean default(FALSE) # name :string +# slug :string # team :boolean default(FALSE) # created_at :datetime not null # updated_at :datetime not null diff --git a/test/models/contest_test.rb b/test/models/contest_test.rb index 21a2705..b92497f 100644 --- a/test/models/contest_test.rb +++ b/test/models/contest_test.rb @@ -5,6 +5,7 @@ # id :integer not null, primary key # allow_registration :boolean default(FALSE) # name :string +# slug :string # team :boolean default(FALSE) # created_at :datetime not null # updated_at :datetime not null