Skip to content
10 changes: 10 additions & 0 deletions app/assets/javascripts/rails_admin/custom/parse_markdown.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
$(document).on('rails_admin.dom_ready', function() {
if($('.markdown-input').length && $('.markdown-output').length) {
const input = $('.markdown-input')[0]
const output = $('.markdown-output')[0]
output.innerHTML = marked.parse(input.value)
input.addEventListener('input', function(a,b) {
output.innerHTML = marked.parse(input.value)
})
}
});

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions app/assets/stylesheets/pages.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Place all the styles related to the Pages controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
13 changes: 13 additions & 0 deletions app/controllers/pages_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

class PagesController < ApplicationController
before_action :load_game

def index
@pages = @game.pages
end

def show
@page = @game.pages.find_by path: params[:id].downcase
end
end
4 changes: 4 additions & 0 deletions app/helpers/pages_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# frozen_string_literal: true

module PagesHelper
end
1 change: 1 addition & 0 deletions app/models/game.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class Game < ApplicationRecord
has_many :standard_solved_challenges, through: :divisions
has_many :pentest_solved_challenges, through: :divisions
has_many :solved_challenges, through: :divisions
has_many :pages
end

enum board_layout: { jeopardy: 0, teams_x_challenges: 1, multiple_categories: 2, title_and_description: 3 }
Expand Down
19 changes: 19 additions & 0 deletions app/models/page.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# frozen_string_literal: true

class Page < ApplicationRecord
belongs_to :game, optional: false

validates :path,
presence: true,
uniqueness: { scope: :game_id, case_sensitive: false },
format: { with: /\A[a-zA-Z\-]*\z/, message: 'path can only consist of letters and dashes' }
before_save :downcase_path

def preview
body
end

def downcase_path
path.downcase!
end
end
8 changes: 8 additions & 0 deletions app/views/pages/index.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
- content_for :header do
= t('pages.index.pages_header')
- if @pages.count == 0
%h4{ class: "zero-items-text" }= t('pages.no_pages')
- else
- @pages.each do |page|
%div.page
= link_to page.title, game_page_path(page.path)
12 changes: 12 additions & 0 deletions app/views/pages/show.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
- content_for :header do
%div{ :class => "title" }
- if @page.title.blank?
= t('pages.show.no_title')
- else
= @page.title

%div{ :class => "body", :style => "margin-bottom:40px;" }
- if @page.body.blank?
= t('pages.show.no_body')
- else
= sanitize(Kramdown::Document.new(@page.body, :input => 'markdown').to_html)
1 change: 1 addition & 0 deletions app/views/rails_admin/main/_markdown_input.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
%textarea{class: 'form-control markdown-input', rows: '10', cols: '64', name: 'page[body]', id: 'page_body'}=form.object.body
2 changes: 2 additions & 0 deletions app/views/rails_admin/main/_markdown_output.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
%div{class: 'markdown-output', style: 'width: fit-content; border: 1px solid #ccc; border-radius: 4px; padding: 6px 12px', readonly: true}=form.object.body
%p{class: 'markdown-disclaimer'}Disclaimer: Preview may appear differently on final page. Use two returns for line breaks.
5 changes: 5 additions & 0 deletions app/views/rails_admin/main/_page_url.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.input-group
.input-group-addon
%span{class: 'input-group-text'}=request.host + (request.local? ? ':3000' : '') + '/game/pages/'
%input.form-control{id: "page_path", name: 'page[path]', type: "text", value: form.object.path}

29 changes: 29 additions & 0 deletions config/initializers/rails_admin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,35 @@
end
end

config.model 'Page' do
edit do
field :title
field :body do
partial "markdown_input"
end
field :preview do
partial "markdown_output"
end
field :path do
partial "page_url"
end
field :game
end
list do
field :title
field :path do
label 'Link'
pretty_value do
v = bindings[:view]
page = bindings[:object]
url = page.path
v.link_to(url, '/game/pages/' + url)
end
end
field :game
end
end

# If you want to track changes on your models:
# config.audit_with :history, 'User'

Expand Down
7 changes: 7 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,13 @@ en:
messages:
messages_header: Messages
no_messages: No Messages
pages:
index:
pages_header: Custom Pages
no_pages: No pages
show:
no_title: Page
no_body: This page is empty
home:
index:
description: Description
Expand Down
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
get :privacy_notice
get :summary
get :terms_of_service
resources :pages, only: %i[index show]
resources :messages, only: [:index]
resources :achievements, only: [:index]
resources :divisions, only: [:index]
Expand Down
10 changes: 10 additions & 0 deletions db/migrate/20220114214507_create_pages.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class CreatePages < ActiveRecord::Migration[6.1]
def change
create_table :pages do |t|
t.string :title
t.text :body

t.timestamps
end
end
end
5 changes: 5 additions & 0 deletions db/migrate/20220118150442_add_game_to_pages.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddGameToPages < ActiveRecord::Migration[6.1]
def change
add_reference :pages, :game, null: false, foreign_key: true
end
end
5 changes: 5 additions & 0 deletions db/migrate/20220118214259_add_path_to_pages.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddPathToPages < ActiveRecord::Migration[6.1]
def change
add_column :pages, :path, :string
end
end
5 changes: 5 additions & 0 deletions db/migrate/20220118214936_change_column_null.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class ChangeColumnNull < ActiveRecord::Migration[6.1]
def change
change_column_null :pages, :path, false
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddUniqueIndexToGameIdAndPath < ActiveRecord::Migration[6.1]
def change
add_index :pages, [:game_id, :path], unique: true
end
end
16 changes: 14 additions & 2 deletions db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 2021_10_11_144501) do
ActiveRecord::Schema.define(version: 2022_01_18_221736) do

# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
Expand Down Expand Up @@ -195,6 +195,17 @@
t.index ["reset_password_token"], name: "index_models_on_reset_password_token", unique: true
end

create_table "pages", force: :cascade do |t|
t.string "title"
t.text "body"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.bigint "game_id", null: false
t.string "path", null: false
t.index ["game_id", "path"], name: "index_pages_on_game_id_and_path", unique: true
t.index ["game_id"], name: "index_pages_on_game_id"
end

create_table "rails_admin_histories", id: :serial, force: :cascade do |t|
t.text "message"
t.string "username"
Expand Down Expand Up @@ -236,8 +247,8 @@
t.integer "division_id"
t.boolean "eligible", default: false
t.integer "slots_available", default: 0
t.boolean "looking_for_members", default: true, null: false
t.string "team_location", default: ""
t.boolean "looking_for_members", default: true, null: false
t.index "lower((team_name)::text)", name: "index_teams_on_team_name_unique", unique: true
t.index ["division_id"], name: "index_teams_on_division_id"
t.index ["team_captain_id"], name: "index_teams_on_team_captain_id"
Expand Down Expand Up @@ -331,6 +342,7 @@
add_foreign_key "file_submissions", "challenges"
add_foreign_key "file_submissions", "users"
add_foreign_key "flags", "teams"
add_foreign_key "pages", "games"
add_foreign_key "submitted_flags", "flags"
add_foreign_key "teams", "divisions"
end
13 changes: 13 additions & 0 deletions test/controllers/pages_controller_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
require "test_helper"

class PagesControllerTest < ActionController::TestCase

def setup
create(:active_game)
end

test "should get index" do
get :index
assert_response :success
end
end
9 changes: 9 additions & 0 deletions test/factories/pages.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FactoryBot.define do
factory :page do
title { "test custom page" }
body { "this is the body" }
path { "page" }

game
end
end
42 changes: 42 additions & 0 deletions test/integration/pages_display_model_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
require 'test_helper'

class PagesDisplayModesTest < ActionDispatch::IntegrationTest
include TeamsHelper
include Devise::Test::IntegrationHelpers

def setup
@game = create(:active_game)
end

test 'page list displays correctly when there are pages' do
page = create(:page, title: 'Example Title', body: 'Example Body', path: 'page')

get "/game/pages"
assert_select 'div[class=page]' do
assert_select 'a', /Example Title/
assert_select 'a', :href => /game\/pages\/page/
end
end

test 'no pages show when there are no pages' do
get "/game/pages"
assert_select 'h4[class=zero-items-text]', /No Pages/
end

test 'page displays contents correctly' do
page = create(:page, title: 'Example Title', body: 'Example Body', path: 'page')

get "/game/pages/page"
assert_select 'div[class=title]', /Example Title/
assert_select 'div[class=body]', /Example Body/
end

test 'template page displays when page has no contents' do
page = create(:page, title: nil, body: nil, path: 'page')

get "/game/pages/page"
assert_select 'div[class=title]', /Page/
assert_select 'div[class=body]', /This page is empty/
end

end
13 changes: 13 additions & 0 deletions test/models/page_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
require "test_helper"

class PageTest < ActiveSupport::TestCase
def setup
create(:active_game)
end

test 'page preview reflects body' do
page = create(:page, title: 'Example Title', body: 'Example Body', path: 'page')

assert_equal page.preview, 'Example Body'
end
end