Make open source

This commit is contained in:
John Lees-Miller
2018-10-27 16:46:26 +01:00
commit 6e82ba528b
163 changed files with 3912 additions and 0 deletions

22
.gitignore vendored Normal file
View File

@ -0,0 +1,22 @@
# See https://help.github.com/articles/ignoring-files for more about ignoring files.
#
# If you find yourself ignoring temporary files generated by your text editor
# or operating system, you probably want to add a global ignore instead:
# git config --global core.excludesfile '~/.gitignore_global'
# Ignore bundler config.
/.bundle
# Ignore all logfiles and tempfiles.
/log/*
/tmp/*
!/log/.keep
!/tmp/.keep
/node_modules
/yarn-error.log
.byebug_history
.DS_Store
/development.env

22
.rubocop.yml Normal file
View File

@ -0,0 +1,22 @@
AllCops:
Exclude:
- 'bin/*'
- 'db/schema.rb'
Style/ClassAndModuleChildren:
Exclude:
- 'test/**/*'
Style/Documentation:
Exclude:
- 'app/controllers/**/*'
- 'app/helpers/**/*'
- 'app/mailers/**/*'
- 'app/**/application_*'
- 'config/*'
- 'db/**/*'
- 'test/**/*'
Metrics/LineLength:
Max: 80
IgnoredPatterns: ['\A\s*#']

16
Dockerfile Normal file
View File

@ -0,0 +1,16 @@
FROM ruby:2.5.0
RUN apt-get update -qq &&\
apt-get install -y build-essential libpq-dev nodejs &&\
useradd --user-group --create-home --shell /bin/false app
ENV HOME=/home/app
USER app
COPY --chown=app:app Gemfile Gemfile.lock $HOME/wedding/
WORKDIR $HOME/wedding
RUN bundle
CMD ["bundle", "exec", "rails", "server", "-b", "0.0.0.0"]
COPY --chown=app:app . $HOME/wedding/

50
Gemfile Normal file
View File

@ -0,0 +1,50 @@
source 'https://rubygems.org'
git_source(:github) do |repo_name|
repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?('/')
"https://github.com/#{repo_name}.git"
end
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 5.1.5'
# Use postgresql as the database for Active Record
gem 'pg', '>= 0.18', '< 2.0'
# Use Puma as the app server
gem 'puma', '~> 3.7'
# Use SCSS for stylesheets
gem 'sass-rails', '~> 5.0'
# Use Uglifier as compressor for JavaScript assets
gem 'uglifier', '>= 1.3.0'
# See https://github.com/rails/execjs#readme for more supported runtimes
# gem 'therubyracer', platforms: :ruby
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.5'
gem 'activeadmin'
gem 'devise'
gem 'bootstrap'
gem 'jquery-rails'
gem 'rack-canonical-host'
gem 'recaptcha', require: 'recaptcha/rails'
gem 'auto_strip_attributes'
gem 'scenic'
group :development, :test do
gem 'rubocop'
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
gem 'byebug', platforms: %i[mri mingw x64_mingw]
# Adds support for Capybara system testing and selenium driver
gem 'capybara', '~> 2.13'
gem 'selenium-webdriver'
end
group :development do
# Access an IRB console on exception pages or by using <%= console %> anywhere in the code.
gem 'listen', '>= 3.0.5', '< 3.2'
gem 'web-console', '>= 3.3.0'
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
gem 'spring'
gem 'spring-watcher-listen', '~> 2.0.0'
end

294
Gemfile.lock Normal file
View File

@ -0,0 +1,294 @@
GEM
remote: https://rubygems.org/
specs:
actioncable (5.1.5)
actionpack (= 5.1.5)
nio4r (~> 2.0)
websocket-driver (~> 0.6.1)
actionmailer (5.1.5)
actionpack (= 5.1.5)
actionview (= 5.1.5)
activejob (= 5.1.5)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0)
actionpack (5.1.5)
actionview (= 5.1.5)
activesupport (= 5.1.5)
rack (~> 2.0)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionview (5.1.5)
activesupport (= 5.1.5)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.3)
activeadmin (1.2.1)
arbre (>= 1.1.1)
coffee-rails
formtastic (~> 3.1)
formtastic_i18n
inherited_resources (~> 1.7)
jquery-rails (>= 4.2.0)
kaminari (>= 0.15, < 2.0)
railties (>= 4.2, < 5.2)
ransack (~> 1.3)
sass (~> 3.1)
sprockets (< 4.1)
activejob (5.1.5)
activesupport (= 5.1.5)
globalid (>= 0.3.6)
activemodel (5.1.5)
activesupport (= 5.1.5)
activerecord (5.1.5)
activemodel (= 5.1.5)
activesupport (= 5.1.5)
arel (~> 8.0)
activesupport (5.1.5)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (~> 0.7)
minitest (~> 5.1)
tzinfo (~> 1.1)
addressable (2.5.2)
public_suffix (>= 2.0.2, < 4.0)
arbre (1.1.1)
activesupport (>= 3.0.0)
arel (8.0.0)
ast (2.4.0)
auto_strip_attributes (2.4.0)
activerecord (>= 4.0)
autoprefixer-rails (8.2.0)
execjs
bcrypt (3.1.11)
bindex (0.5.0)
bootstrap (4.0.0)
autoprefixer-rails (>= 6.0.3)
popper_js (>= 1.12.9, < 2)
sass (>= 3.5.2)
builder (3.2.3)
byebug (10.0.1)
capybara (2.18.0)
addressable
mini_mime (>= 0.1.3)
nokogiri (>= 1.3.3)
rack (>= 1.0.0)
rack-test (>= 0.5.4)
xpath (>= 2.0, < 4.0)
childprocess (0.9.0)
ffi (~> 1.0, >= 1.0.11)
coffee-rails (4.2.2)
coffee-script (>= 2.2.0)
railties (>= 4.0.0)
coffee-script (2.4.1)
coffee-script-source
execjs
coffee-script-source (1.12.2)
concurrent-ruby (1.0.5)
crass (1.0.3)
devise (4.4.3)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 4.1.0, < 6.0)
responders
warden (~> 1.2.3)
erubi (1.7.1)
execjs (2.7.0)
ffi (1.9.23)
formtastic (3.1.5)
actionpack (>= 3.2.13)
formtastic_i18n (0.6.0)
globalid (0.4.1)
activesupport (>= 4.2.0)
has_scope (0.7.1)
actionpack (>= 4.1, < 5.2)
activesupport (>= 4.1, < 5.2)
i18n (0.9.5)
concurrent-ruby (~> 1.0)
inherited_resources (1.8.0)
actionpack (>= 4.2, <= 5.2)
has_scope (~> 0.6)
railties (>= 4.2, <= 5.2)
responders
jbuilder (2.7.0)
activesupport (>= 4.2.0)
multi_json (>= 1.2)
jquery-rails (4.3.1)
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
json (2.1.0)
kaminari (1.1.1)
activesupport (>= 4.1.0)
kaminari-actionview (= 1.1.1)
kaminari-activerecord (= 1.1.1)
kaminari-core (= 1.1.1)
kaminari-actionview (1.1.1)
actionview
kaminari-core (= 1.1.1)
kaminari-activerecord (1.1.1)
activerecord
kaminari-core (= 1.1.1)
kaminari-core (1.1.1)
listen (3.1.5)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
ruby_dep (~> 1.2)
loofah (2.2.2)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
mail (2.7.0)
mini_mime (>= 0.1.1)
method_source (0.9.0)
mini_mime (1.0.0)
mini_portile2 (2.3.0)
minitest (5.11.3)
multi_json (1.13.1)
nio4r (2.3.0)
nokogiri (1.8.2)
mini_portile2 (~> 2.3.0)
orm_adapter (0.5.0)
parallel (1.12.1)
parser (2.5.0.5)
ast (~> 2.4.0)
pg (1.0.0)
polyamorous (1.3.3)
activerecord (>= 3.0)
popper_js (1.12.9)
powerpack (0.1.1)
public_suffix (3.0.2)
puma (3.11.3)
rack (2.0.4)
rack-canonical-host (0.2.3)
addressable (> 0, < 3)
rack (>= 1.0.0, < 3)
rack-test (0.8.3)
rack (>= 1.0, < 3)
rails (5.1.5)
actioncable (= 5.1.5)
actionmailer (= 5.1.5)
actionpack (= 5.1.5)
actionview (= 5.1.5)
activejob (= 5.1.5)
activemodel (= 5.1.5)
activerecord (= 5.1.5)
activesupport (= 5.1.5)
bundler (>= 1.3.0)
railties (= 5.1.5)
sprockets-rails (>= 2.0.0)
rails-dom-testing (2.0.3)
activesupport (>= 4.2.0)
nokogiri (>= 1.6)
rails-html-sanitizer (1.0.4)
loofah (~> 2.2, >= 2.2.2)
railties (5.1.5)
actionpack (= 5.1.5)
activesupport (= 5.1.5)
method_source
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
rainbow (3.0.0)
rake (12.3.1)
ransack (1.8.8)
actionpack (>= 3.0)
activerecord (>= 3.0)
activesupport (>= 3.0)
i18n
polyamorous (~> 1.3.2)
rb-fsevent (0.10.3)
rb-inotify (0.9.10)
ffi (>= 0.5.0, < 2)
recaptcha (4.7.0)
json
responders (2.4.0)
actionpack (>= 4.2.0, < 5.3)
railties (>= 4.2.0, < 5.3)
rubocop (0.54.0)
parallel (~> 1.10)
parser (>= 2.5)
powerpack (~> 0.1)
rainbow (>= 2.2.2, < 4.0)
ruby-progressbar (~> 1.7)
unicode-display_width (~> 1.0, >= 1.0.1)
ruby-progressbar (1.9.0)
ruby_dep (1.5.0)
rubyzip (1.2.1)
sass (3.5.6)
sass-listen (~> 4.0.0)
sass-listen (4.0.0)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
sass-rails (5.0.7)
railties (>= 4.0.0, < 6)
sass (~> 3.1)
sprockets (>= 2.8, < 4.0)
sprockets-rails (>= 2.0, < 4.0)
tilt (>= 1.1, < 3)
scenic (1.4.1)
activerecord (>= 4.0.0)
railties (>= 4.0.0)
selenium-webdriver (3.11.0)
childprocess (~> 0.5)
rubyzip (~> 1.2)
spring (2.0.2)
activesupport (>= 4.2)
spring-watcher-listen (2.0.1)
listen (>= 2.7, < 4.0)
spring (>= 1.2, < 3.0)
sprockets (3.7.1)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-rails (3.2.1)
actionpack (>= 4.0)
activesupport (>= 4.0)
sprockets (>= 3.0.0)
thor (0.20.0)
thread_safe (0.3.6)
tilt (2.0.8)
tzinfo (1.2.5)
thread_safe (~> 0.1)
uglifier (4.1.8)
execjs (>= 0.3.0, < 3)
unicode-display_width (1.3.0)
warden (1.2.7)
rack (>= 1.0)
web-console (3.5.1)
actionview (>= 5.0)
activemodel (>= 5.0)
bindex (>= 0.4.0)
railties (>= 5.0)
websocket-driver (0.6.5)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.3)
xpath (3.0.0)
nokogiri (~> 1.8)
PLATFORMS
ruby
DEPENDENCIES
activeadmin
auto_strip_attributes
bootstrap
byebug
capybara (~> 2.13)
devise
jbuilder (~> 2.5)
jquery-rails
listen (>= 3.0.5, < 3.2)
pg (>= 0.18, < 2.0)
puma (~> 3.7)
rack-canonical-host
rails (~> 5.1.5)
recaptcha
rubocop
sass-rails (~> 5.0)
scenic
selenium-webdriver
spring
spring-watcher-listen (~> 2.0.0)
uglifier (>= 1.3.0)
web-console (>= 3.3.0)
BUNDLED WITH
1.16.1

1
Procfile Normal file
View File

@ -0,0 +1 @@
web: bundle exec rails server -p $PORT

68
README.md Normal file
View File

@ -0,0 +1,68 @@
# Wedding with Rails
This is a simple rails app for managing the guest list for a wedding (or similar event). It's intended to be easy to customize.
## Features
- Guests can RSVP, including dietary requirements and plus ones
- Sends guests a confirmation email they can use to manage their RSVP
- Responsive design
- [Active Admin](https://activeadmin.info/) interface to manage the attendee list
- Optional invisible [reCAPTCHA](https://www.google.com/recaptcha) integration to avoid spam
- Sets headers to avoid being indexed by search engines
## Development
### Initial Setup
The simplest way to get started is to use [Docker](https://www.docker.com/).
```
cp development.env.template development.env
docker-compose build
docker-compose up -d db
docker-compose run --rm web bash -lc 'bin/rails db:create db:migrate'
docker-compose run --rm web bash -lc 'bin/rails db:seed'
```
Watch the output of `db:seed` for the default admin credentials:
```
Admin user email: admin@example.com
Admin user password: (random string)
```
### Running the App
```
docker-compose up -d
docker-compose logs -f
```
By default, the app will run on http://localhost:3000
To access the database:
```
docker-compose exec db psql wedding_development postgres
```
To get a shell:
```
docker-compose exec web bash
```
Once you have a shell in the container, you can run rails commands as normal.
```
bin/rails console
bin/rails test
```
## Default photo credits
- https://www.pexels.com/photo/selective-focus-photography-of-two-penguins-689784/
- https://www.pexels.com/photo/cold-glacier-iceberg-melting-48178/
- https://www.pexels.com/photo/flock-of-penguins-1036155/
- https://www.pexels.com/photo/pink-petaled-flower-boquette-122734/

6
Rakefile Normal file
View File

@ -0,0 +1,6 @@
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
require_relative 'config/application'
Rails.application.load_tasks

27
app/admin/admin_users.rb Normal file
View File

@ -0,0 +1,27 @@
ActiveAdmin.register AdminUser do
permit_params :email, :password, :password_confirmation
index do
selectable_column
id_column
column :email
column :current_sign_in_at
column :sign_in_count
column :created_at
actions
end
filter :email
filter :current_sign_in_at
filter :sign_in_count
filter :created_at
form do |f|
f.inputs do
f.input :email
f.input :password
f.input :password_confirmation
end
f.actions
end
end

25
app/admin/attendees.rb Normal file
View File

@ -0,0 +1,25 @@
ActiveAdmin.register Attendee do
scope :child?
scope :diet?
controller do
def apply_sorting(chain)
params[:order] ? chain : chain.reorder(last_name: :asc, first_name: :asc)
end
end
index do
column :first_name
column :last_name
column :email
column :diet
column :notes
column :child
column :updated_at
end
config.batch_actions = false
config.filters = false
config.per_page = 500
config.clear_action_items!
end

32
app/admin/dashboard.rb Normal file
View File

@ -0,0 +1,32 @@
ActiveAdmin.register_page 'Dashboard' do
menu priority: 1, label: proc { I18n.t('active_admin.dashboard') }
content title: proc { I18n.t('active_admin.dashboard') } do
div class: 'blank_slate_container', id: 'dashboard_default_message' do
span class: 'blank_slate' do
span I18n.t('active_admin.dashboard_welcome.welcome')
small I18n.t('active_admin.dashboard_welcome.call_to_action')
end
end
# Here is an example of a simple dashboard with columns and panels.
#
# columns do
# column do
# panel "Recent Posts" do
# ul do
# Post.recent(5).map do |post|
# li link_to(post.title, admin_post_path(post))
# end
# end
# end
# end
# column do
# panel "Info" do
# para "Welcome to ActiveAdmin."
# end
# end
# end
end
end

21
app/admin/guests.rb Normal file
View File

@ -0,0 +1,21 @@
ActiveAdmin.register Guest do
permit_params :email, :first_name, :last_name, :attending, :diet, :songs,
:notes
scope :confirmed
scope :attending
scope :not_attending
form do |_f|
inputs 'Guest' do
input :email, as: :string
input :first_name
input :last_name
input :attending
input :diet, as: :text
input :songs, as: :text
input :notes, as: :text
end
actions
end
end

13
app/admin/plus_ones.rb Normal file
View File

@ -0,0 +1,13 @@
ActiveAdmin.register PlusOne do
permit_params :first_name, :last_name, :diet, :child
form do |_f|
inputs 'Plus One' do
input :first_name
input :last_name
input :diet, as: :text
input :child
end
actions
end
end

View File

@ -0,0 +1,3 @@
//= link_tree ../images
//= link_directory ../javascripts .js
//= link_directory ../stylesheets .css

0
app/assets/images/.keep Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

BIN
app/assets/images/jumbo.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

BIN
app/assets/images/plan.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

BIN
app/assets/images/venue.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 KiB

View File

@ -0,0 +1 @@
//= require active_admin/base

View File

@ -0,0 +1,19 @@
// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's
// vendor/assets/javascripts directory can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file. JavaScript code in this file should be added after the last require_* statement.
//
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require rails-ujs
//= require jquery
//= require popper
//= require bootstrap-sprockets
//= require_tree .
//= stub active_admin

View File

@ -0,0 +1,13 @@
// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.
$(function () {
var radioMatcher = '#guest-edit-form input[name="guest[attending]"]'
var radioButtons = $(radioMatcher)
function toggleAttendingOnly () {
var attending = $(radioMatcher + ':checked').val()
$('.guest-attending-only textarea').attr('disabled', attending !== 'true')
}
radioButtons.on('change', toggleAttendingOnly)
toggleAttendingOnly()
})

View File

@ -0,0 +1,2 @@
// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.

View File

@ -0,0 +1,2 @@
// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.

View File

@ -0,0 +1,65 @@
/*
* This is a manifest file that'll be compiled into application.css, which will include all the files
* listed below.
*
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's
* vendor/assets/stylesheets directory can be referenced here using a relative path.
*
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
* files in this directory. Styles in this file should be added after the last require_* statement.
* It is generally better to create a new file per style scope.
*
*/
@import url('https://fonts.googleapis.com/css?family=Montserrat:400,700|Arvo:400');
$font-family-sans-serif: Montserrat, Helvetica, Arial, sans-serif;
$font-family-serif: Arvo, Georgia, "Times New Roman", Times, serif;
$font-family-base: $font-family-serif;
$headings-font-family: $font-family-sans-serif;
$body-color: rgba(28, 13, 10, 0.7);
$headings-color: rgba(28, 13, 10, 0.7);
$theme-colors: (
"primary": #ff745c
);
$enable-rounded: false;
@import "bootstrap";
@import "guests";
@import "welcome";
h1 {
font-size: 60px;
font-weight: 700;
letter-spacing: 0.0833333em;
text-transform: uppercase;
}
h2 {
color: theme-color("primary");
font-size: 28px;
font-weight: 700;
line-height: 1.5em;
letter-spacing: 0.0833333em;
text-transform: uppercase;
svg {
fill: currentColor;
margin: 0 auto;
height: 100px;
}
}
.wedding-cta-btn {
letter-spacing: 2px;
padding: 0.75rem 1.5rem;
}
.wedding-required {
color: theme-color("primary");
}

View File

@ -0,0 +1,27 @@
// Place all the styles related to the Guests controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
#guests-leader {
background:
linear-gradient(rgba(28, 13, 10, 0.4), rgba(28, 13, 10, 0.6)),
image-url('flowers.jpg') center center no-repeat;
background-size: cover;
h1 {
color: rgba(255, 255, 255, 0.95);
}
}
.guests-rsvp-nav {
padding-bottom: $spacer;
@media (max-width: 768px) {
li.nav-item { width:100%; }
}
}
.guests-buttons {
padding-top: $spacer;
padding-bottom: 10 * $spacer;
}

View File

@ -0,0 +1,3 @@
// Place all the styles related to the PlusOnes controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/

View File

@ -0,0 +1,104 @@
// Place all the styles related to the Welcome controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
#welcome-index-leader {
background: linear-gradient(rgba(28, 13, 10, 0.4), rgba(28, 13, 10, 0.6)),
image-url("jumbo.jpg") center center no-repeat;
background-size: cover;
height: 820px;
* {
color: rgba(255, 255, 255, 0.95);
}
h1 {
margin-top: 200px;
}
.leader-date {
font-size: 17px;
letter-spacing: 4px;
padding: 12pt;
text-transform: uppercase;
}
}
#welcome-index-when-where {
h1 {
padding: 3 * $spacer 0;
}
}
#welcome-index-venue-leader {
background: linear-gradient(rgba(28, 13, 10, 0.4), rgba(28, 13, 10, 0.6)),
image-url("venue.jpg") center center no-repeat;
background-size: cover;
margin-top: 5 * $spacer;
* {
color: rgba(255, 255, 255, 0.95);
}
h1 {
padding: 3 * $spacer 0;
}
}
#welcome-index-venue {
h2 {
padding-top: 3 * $spacer;
padding-bottom: $spacer;
text-align: center;
}
p {
text-align: justify;
}
}
#welcome-index-wedding-plan-leader {
background: linear-gradient(rgba(28, 13, 10, 0.4), rgba(28, 13, 10, 0.6)),
image-url("plan.jpg") center center no-repeat;
background-size: cover;
margin-top: 5 * $spacer;
* {
color: rgba(255, 255, 255, 0.95);
}
h1 {
padding: 3 * $spacer 0;
}
}
#welcome-index-wedding-plan {
h2 {
padding-top: 3 * $spacer;
padding-bottom: $spacer;
text-align: center;
}
p {
text-align: justify;
}
}
#welcome-index-footer {
background-color: #faebe3;
margin-top: 5 * $spacer;
#welcome-index-footer-cta {
padding-top: 8 * $spacer;
padding-bottom: 3 * $spacer;
}
#welcome-index-footer-credits {
padding-top: 5 * $spacer;
padding-bottom: 3 * $spacer;
small {
color: rgba(28, 13, 10, 0.4);
}
}
}

View File

@ -0,0 +1,9 @@
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
if ENV['BASIC_AUTH_NAME'] && ENV['BASIC_AUTH_PASSWORD']
http_basic_authenticate_with \
name: ENV['BASIC_AUTH_NAME'],
password: ENV['BASIC_AUTH_PASSWORD']
end
end

View File

View File

@ -0,0 +1,75 @@
class GuestsController < ApplicationController
def new
respond_to :html
@guest = Guest.new
end
def create
respond_to :html
@guest = Guest.new(guest_params)
unless ENV['RECAPTCHA_SECRET_KEY'].blank? || verify_recaptcha(model: @guest)
render :new
return
end
if @guest.save
redirect_to guest_path(@guest)
else
existing_guest = Guest.find_by(email: guest_params[:email])
if existing_guest
@guest = existing_guest
GuestMailer.welcome_back_email(@guest).deliver_now
render :new_exists
else
render :new
end
end
end
def show
respond_to :html
@guest = Guest.find_by_id_token(params[:id])
end
def update
@guest = Guest.find_by_id_token(params[:id])
if @guest.update(guest_params)
if @guest.attending?
redirect_to guest_plus_ones_path(@guest)
else
redirect_to confirm_guest_path(@guest)
end
else
render :show
end
end
def confirm
respond_to :html
@guest = Guest.find_by_id_token(params[:id])
end
def complete
respond_to :html
@guest = Guest.find_by_id_token(params[:id])
Guest.transaction do
@guest.update!(guest_params)
@guest.touch :confirmed_at
end
GuestMailer.confirmation_email(@guest).deliver_now
end
private
def guest_params
params.require(:guest).permit(
:email, :first_name, :last_name, :attending, :diet, :songs, :notes
)
end
end

View File

@ -0,0 +1,53 @@
class PlusOnesController < ApplicationController
before_action do
@guest = Guest.find_by_id_token(params[:guest_id])
end
def new
respond_to :html
@plus_one = @guest.plus_ones.new
end
def create
respond_to :html
@plus_one = @guest.plus_ones.new(plus_one_params)
if @plus_one.save
redirect_to guest_plus_ones_path(@guest)
else
render :new
end
end
def index
respond_to :html
@plus_ones = @guest.plus_ones.order(:first_name, :last_name)
end
def edit
@plus_one = @guest.plus_ones.find(params[:id])
respond_to :html
end
def update
@plus_one = @guest.plus_ones.find(params[:id])
if @plus_one.update(plus_one_params)
redirect_to guest_plus_ones_path(@guest)
else
render :edit
end
end
def destroy
respond_to :html
@plus_one = @guest.plus_ones.find_by(id: params[:id])
@plus_one.destroy if @plus_one
redirect_to guest_plus_ones_path(@guest)
end
private
def plus_one_params
params.require(:plus_one).permit(:first_name, :last_name, :diet, :child)
end
end

View File

@ -0,0 +1,6 @@
class WelcomeController < ApplicationController
def index
@google_maps_url = 'https://goo.gl/maps/nBAxNAsmPSS2'
@cool_earth_url = 'https://www.coolearth.org/'
end
end

View File

@ -0,0 +1,24 @@
module ApplicationHelper
def flash_class(key)
case key
when :notice then 'alert alert-info'
when :success then 'alert alert-success'
when :error then 'alert alert-error'
when :alert then 'alert alert-error'
end
end
def errors_for(object)
return unless object.errors.any?
content_tag(:div, class: 'mb-3 card border-danger') do
concat(content_tag(:div, class: 'card-header bg-danger text-white') do
concat "Oops, #{pluralize(object.errors.count, 'problem')}:"
end)
concat(content_tag(:ul, class: 'mb-0 list-group list-group-flush') do
object.errors.full_messages.each do |msg|
concat content_tag(:li, msg, class: 'list-group-item')
end
end)
end
end
end

View File

@ -0,0 +1,2 @@
module GuestsHelper
end

View File

@ -0,0 +1,2 @@
module PlusOnesHelper
end

View File

@ -0,0 +1,2 @@
module WelcomeHelper
end

View File

@ -0,0 +1,2 @@
class ApplicationJob < ActiveJob::Base
end

View File

@ -0,0 +1,4 @@
class ApplicationMailer < ActionMailer::Base
default from: 'from@example.com'
layout 'mailer'
end

View File

@ -0,0 +1,19 @@
class GuestMailer < ApplicationMailer
default from: ENV['FROM_EMAIL'], reply_to: ENV['CONTACT_EMAIL']
def confirmation_email(guest)
@guest = guest
mail(
to: guest.name_with_email,
subject: "#{I18n.t(:wedding_name)}: RSVP Confirmation"
)
end
def welcome_back_email(guest)
@guest = guest
mail(
to: guest.name_with_email,
subject: "#{I18n.t(:wedding_name)}: Your RSVP"
)
end
end

9
app/models/admin_user.rb Normal file
View File

@ -0,0 +1,9 @@
#
# ActiveAdmin console user.
#
class AdminUser < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable,
:recoverable, :rememberable, :trackable, :validatable
end

View File

@ -0,0 +1,3 @@
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
end

7
app/models/attendee.rb Normal file
View File

@ -0,0 +1,7 @@
#
# A guest or plus one.
#
class Attendee < ApplicationRecord
scope :diet?, -> { where.not(diet: nil) }
scope :child?, -> { where(child: true) }
end

View File

View File

@ -0,0 +1,27 @@
#
# Make a model findable only when a secure token is provided.
#
module FindableWithToken
extend ActiveSupport::Concern
ID_TOKEN_RX = /\A(\d+)-(\w+)\z/
included do
has_secure_token
def to_param
id ? "#{id}-#{token}" : nil
end
def self.find_by_id_token(id_token)
raise ActiveRecord::RecordNotFound unless id_token =~ ID_TOKEN_RX
id = Regexp.last_match(1)
token = Regexp.last_match(2)
record = find(id)
raise ActiveRecord::RecordNotFound unless
ActiveSupport::SecurityUtils.secure_compare(record.token, token)
record
end
end
end

41
app/models/guest.rb Normal file
View File

@ -0,0 +1,41 @@
#
# A primary guest.
#
class Guest < ApplicationRecord
include FindableWithToken
auto_strip_attributes :email, :first_name, :last_name, :diet, :songs, :notes
validates :email, presence: true, uniqueness: true
validates :email, format: Devise.email_regexp, allow_blank: true
validates :first_name, presence: true, if: :persisted?
validates :first_name, length: { maximum: 1024 }
validates :last_name, presence: true, if: :persisted?
validates :last_name, length: { maximum: 1024 }
def name
"#{first_name} #{last_name}"
end
def name_with_email
"#{name} <#{email}>"
end
# Don't allow long or odd names in emails; may be spam.
def email_safe_salutation
return 'Hello,' if
first_name.blank? || first_name !~ /\A[\p{Word}\s'-]{1,30}\z/i
"Dear #{first_name},"
end
validates :diet, length: { maximum: 8192 }
validates :songs, length: { maximum: 8192 }
validates :notes, length: { maximum: 8192 }
has_many :plus_ones, dependent: :destroy
scope :confirmed, -> { where.not(confirmed_at: nil) }
scope :attending, -> { confirmed.where(attending: true) }
scope :not_attending, -> { confirmed.where(attending: false) }
end

19
app/models/plus_one.rb Normal file
View File

@ -0,0 +1,19 @@
#
# An extra guest.
#
class PlusOne < ApplicationRecord
belongs_to :guest
auto_strip_attributes :first_name, :last_name, :diet
validates :first_name, presence: true
validates :first_name, length: { maximum: 1024 }
validates :last_name, presence: true
validates :last_name, length: { maximum: 1024 }
def name
"#{first_name} #{last_name}"
end
validates :diet, length: { maximum: 8192 }
end

View File

@ -0,0 +1,3 @@
<strong>Alert</strong><br>
Qikiqtaaluk Region, Nunavut<br>
Canada<br>

View File

@ -0,0 +1,3 @@
<strong>Halley Research Station</strong><br>
Brunt Ice Shelf<br>
Antartica

View File

@ -0,0 +1,25 @@
<p><%= @guest.email_safe_salutation %></p>
<% if @guest.attending %>
<p>We look forward to seeing you!</p>
<p>The address is:</p>
<address><%= render partial: 'application/wedding_address' %></address>
<h3>Your RSVP</h3>
<p>If you would like to view or update your RSVP, you can use the following link to get back to it:</p>
<p><%= link_to guest_url(@guest), guest_url(@guest) %></p>
<h3>Sending a Card?</h3>
<p>If you need it, our mailing address is:</p>
<address><%= render partial: 'application/mailing_address' %></address>
<h3>Questions and answers</h3>
<p>Please check <%= link_to 'our wedding website', root_url %> for more information, or you can reply to this email.</p>
<% else %>
<p>We're sorry to hear you can't make it. If you change your mind, you can update your RSVP with the link below:</p>
<p><%= link_to guest_url(@guest), guest_url(@guest) %></p>
<p>We hope to see you another time!</p>
<% end %>
<p>&nbsp;</p>
<p><%= t :wedding_couple_names %></p>

View File

@ -0,0 +1,11 @@
<p><%= @guest.email_safe_salutation %></p>
<p>You recently entered your email address to RSVP on <%= ENV['CANONICAL_HOST'] %>.</p>
<p>If you would like to view or update your RSVP, you can use the following link to get back to it:</p>
<p><%= link_to guest_url(@guest), guest_url(@guest) %></p>
<p>If you have any questions, you can ask us by reply.</p>
<p>&nbsp;</p>
<p><%= t :wedding_couple_names %></p>

View File

@ -0,0 +1,9 @@
<div class="form-group row">
<div class="col-md-3">
<%= form.label :diet, 'Dietary Preferences', class: 'col-form-label' %>
</div>
<div class="col-md-9">
<%= form.text_area :diet, id: "#{id_stem}_diet", class: 'form-control' %>
<small class="form-text text-muted">For example, vegan, vegetarian, gluten free, and any allergies or intolerances.</small>
</div>
</div>

View File

@ -0,0 +1,5 @@
<div id="guests-leader" class="jumbotron text-center">
<div class="container">
<h1>RSVP</h1>
</div>
</div>

View File

@ -0,0 +1,19 @@
<div class="form-group row">
<div class="col-md-3">
<%= form.label :first_name, 'First Name', class: 'col-form-label' %>
<span class="wedding-required">*</span>
</div>
<div class="col-md-9">
<%= form.text_field :first_name, id: "#{id_stem}_first_name", class: 'form-control', autofocus: true, required: true %>
</div>
</div>
<div class="form-group row">
<div class="col-md-3">
<%= form.label :last_name, 'Last Name', class: 'col-form-label' %>
<span class="wedding-required">*</span>
</div>
<div class="col-md-9">
<%= form.text_field :last_name, id: "#{id_stem}_last_name", class: 'form-control', required: true %>
</div>
</div>

View File

@ -0,0 +1,21 @@
<nav class="guests-rsvp-nav">
<ul class="nav nav-pills nav-fill">
<li class="nav-item">
<%= link_to '1. About You', guest_path(@guest), class: ['nav-link', active == 1 ? 'active' : ''] %>
</li>
<li class="nav-item">
<% if active >= 2 %>
<%= link_to '2. Plus Ones', guest_plus_ones_path(@guest), class: ['nav-link', active == 2 ? 'active' : ''] %>
<% else %>
<a class="nav-link <%= active == 2 ? 'active' : '' %>">2. Plus Ones</a>
<% end %>
</li>
<li class="nav-item">
<% if active >= 3 %>
<%= link_to '3. Confirm', confirm_guest_path(@guest), class: ['nav-link', active == 3 ? 'active' : ''] %>
<% else %>
<a class="nav-link <%= active == 3 ? 'active' : '' %>">3. Confirm</a>
<% end %>
</li>
</ul>
</nav>

View File

@ -0,0 +1,12 @@
<div class="container">
<div class="row">
<div class="offset-lg-2 col-lg-8">
<%= render partial: 'leader' %>
<h2>Thanks!</h2>
<p>We've sent an email confirmation to <tt><%= @guest.email %></tt>.</p>
<p>If you don't receive the email within a few minutes, please check your spam folder, and if it's not there <%= mail_to ENV['CONTACT_EMAIL'], 'contact us' %>.</p>
<p>The email contains a link that you can use to update your RSVP later if you need to.</p>
<p><%= link_to 'Back to the Home Page', root_path %></p>
</div>
</div>
</div>

View File

@ -0,0 +1,98 @@
<div class="container">
<div class="row">
<div class="offset-lg-2 col-lg-8">
<%= render partial: 'leader' %>
<%= render partial: 'rsvp_nav', locals: { active: 3 } %>
<h2>Confirm</h2>
<%= form_with(model: @guest, url: complete_guest_path(@guest), local: true) do |form| %>
<div class="row">
<div class="col-md-12">
<%= errors_for(@guest) %>
</div>
</div>
<% if @guest.attending? %>
<h3>Your Details</h3>
<div class="card mb-3">
<h5 class="card-header"><%= @guest.name %></h5>
<div class="card-body">
<div class="row">
<div class="col-md-4">
Email
</div>
<div class="col-md-8">
<p><tt><%= @guest.email %></tt></p>
</div>
</div>
<div class="row">
<div class="col-md-4">
Dietary Preferences
</div>
<div class="col-md-8">
<% if @guest.diet.present? %>
<pre><%= @guest.diet %></pre>
<% else %>
<p>(None)</p>
<% end %>
</div>
</div>
<div class="row">
<div class="col-md-4">
Song Suggestions
</div>
<div class="col-md-8">
<% if @guest.songs.present? %>
<pre><%= @guest.songs %></pre>
<% else %>
<p>(None)</p>
<% end %>
</div>
</div>
<div class="row">
<div class="col-md-12 text-right">
<%= link_to 'Edit', guest_path(@guest), class: 'card-link', data: { disable_with: 'Loading...' } %>
</div>
</div>
</div>
</div>
<h3>Plus Ones</h3>
<% if @guest.plus_ones.any? %>
<%= render @guest.plus_ones %>
<% else %>
<p>(None &mdash; just you.)</p>
<% end %>
<% else %>
<p>Sorry to hear you won't be joining us!</p>
<h3>Your Details</h3>
<div class="card mb-3">
<h5 class="card-header"><%= @guest.name %></h5>
<div class="card-body">
<div class="row">
<div class="col-md-4">
Email
</div>
<div class="col-md-8">
<p><tt><%= @guest.email %></tt></p>
</div>
</div>
</div>
</div>
<% end %>
<h3>Other Comments?</h3>
<%= form.text_area :notes, class: 'form-control' %>
<small class="form-text text-muted">Anything else you'd like to let us know?</small>
<div class="row guests-buttons">
<div class="offset-md-3 col-md-6 text-center">
<%= form.submit 'Complete RSVP', class: 'btn btn-primary', data: { disable_with: 'Sending...' } %>
</div>
</div>
<% end %>
</div>
</div>
</div>

View File

@ -0,0 +1,30 @@
<div class="container">
<div class="row">
<div class="offset-lg-2 col-lg-8">
<%= render partial: 'leader' %>
<p>Please enter your email address to begin. We'll send you an email with important information once you have RSVP'd.</p>
<%= form_with scope: :guest, url: guests_path, local: true do |form| %>
<div class="row">
<div class="col-md-12">
<%= errors_for(@guest) %>
</div>
</div>
<div class="form-group row">
<div class="col-md-3">
<%= form.label :email, class: 'col-form-label' %>
</div>
<div class="col-md-9">
<%= form.email_field :email, id: 'guest_email', class: 'form-control', autofocus: true, required: true %>
</div>
</div>
<div class="row guests-buttons">
<div class="offset-md-3 col-md-6 text-center">
<%= invisible_recaptcha_tags text: 'Continue', class: 'btn btn-primary', data: { disable_with: 'Sending...' } %>
</div>
</div>
<% end %>
</div>
</div>
</div>

View File

@ -0,0 +1,11 @@
<div class="container">
<div class="row">
<div class="offset-lg-2 col-lg-8">
<%= render partial: 'leader' %>
<h2>Welcome Back</h2>
<p>It looks like you've already registered your RSVP, so we've sent you an email with a link that you can use to update it.</p>
<p>Please check your <tt><%= @guest.email %></tt> email including spam folders!</p>
<p>If you don't receive the email, please <%= mail_to ENV['CONTACT_EMAIL'], 'contact us' %>.</p>
</div>
</div>
</div>

View File

@ -0,0 +1,67 @@
<div class="container">
<div class="row">
<div class="offset-lg-2 col-lg-8">
<%= render partial: 'leader' %>
<%= render partial: 'rsvp_nav', locals: { active: 1 } %>
<h2>About You</h2>
<%= form_with(model: @guest, method: :patch, local: true, id: 'guest-edit-form') do |form| %>
<div class="row">
<div class="col-md-12">
<%= errors_for(@guest) %>
</div>
</div>
<div class="form-group row">
<div class="col-md-3">
<%= form.label :email, class: 'col-form-label' %>
<span class="wedding-required">*</span>
</div>
<div class="col-md-9">
<%= form.email_field :email, id: 'guest_email', class: 'form-control', required: true %>
</div>
</div>
<%= render partial: 'name', locals: { form: form, id_stem: 'guest' } %>
<div class="form-group row">
<div class="offset-md-3 col-md-9">
<p><strong>Will you be joining us?</strong></p>
<div class="form-check form-check-inline">
<%= form.radio_button :attending, 'true', checked: true, id: 'guest_attending_true', class: 'form-check-input' %>
<%= form.label :attending, 'Yes', value: 'true', class: 'form-check-label' %>
</div>
<div class="form-check form-check-inline">
<%= form.radio_button :attending, 'false', id: 'guest_attending_false', class: 'form-check-input' %>
<%= form.label :attending, 'No', value: 'false', class: 'form-check-label' %>
</div>
</div>
</div>
<div class="guest-attending-only">
<%= render partial: 'dietary_preferences', locals: { form: form, id_stem: 'guest' } %>
<div class="form-group row">
<div class="col-md-3">
<%= form.label :songs, 'Song Suggestions', class: 'col-form-label' %>
</div>
<div class="col-md-9">
<%= form.text_area :songs, id: 'guest_songs', class: 'form-control' %>
<small class="form-text text-muted">We're crowd sourcing our playlist! Choose some songs you'd like to hear.</small>
</div>
</div>
</div>
<div class="row guests-buttons">
<div class="col-md-3">
<span class="wedding-required">* Required</span>
</div>
<div class="col-md-6 text-center">
<%= form.submit 'Continue', class: 'btn btn-primary', data: { disable_with: 'Sending...' } %>
</div>
</div>
<% end %>
</div>
</div>
</div>

View File

@ -0,0 +1,21 @@
<!DOCTYPE html>
<html>
<head>
<title><%= t :wedding_name %></title>
<%= csrf_meta_tags %>
<%= stylesheet_link_tag 'application', media: 'all' %>
<%= javascript_include_tag 'application' %>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex, nofollow">
</head>
<body>
<% flash.each do |key, value| %>
<%= content_tag(:div, class: flash_class(key)) do -%>
<%= value %>
<% end -%>
<% end %>
<%= yield %>
</body>
</html>

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<%= yield %>
</body>
</html>

View File

@ -0,0 +1 @@
<%= yield %>

View File

@ -0,0 +1,32 @@
<%= form_with(model: [@guest, @plus_one], local: true) do |form| %>
<div class="row">
<div class="col-md-12">
<%= errors_for(@plus_one) %>
</div>
</div>
<%= render partial: 'guests/name', locals: { form: form, id_stem: 'plus_one' } %>
<%= render partial: 'guests/dietary_preferences', locals: { form: form, id_stem: 'plus_one' } %>
<div class="form-group row">
<div class="offset-md-3 col-md-9">
<div class="form-check form-check-inline">
<%= form.check_box :child, id: 'plus_one_child', class: 'form-check-input' %>
<%= form.label :child, 'Child (12 or under)', class: 'form-check-label' %>
</div>
</div>
</div>
<div class="row guests-buttons">
<div class="col-md-3">
<span class="wedding-required">* Required</span>
</div>
<div class="col-md-6 text-center">
<%= form.submit submit_action, class: 'btn btn-primary', data: { disable_with: 'Sending...' } %>
</div>
<div class="col-md-3 text-right">
<%= link_to 'Cancel', guest_plus_ones_path(@guest), class: 'btn btn-secondary' %>
</div>
</div>
<% end %>

View File

@ -0,0 +1,31 @@
<div class="card mb-3">
<h5 class="card-header"><%= plus_one.name %></h5>
<div class="card-body">
<div class="row">
<div class="col-md-4">
Dietary Preferences
</div>
<div class="col-md-8">
<% if plus_one.diet.present? %>
<pre><%= plus_one.diet %></pre>
<% else %>
<p>(None)</p>
<% end %>
</div>
</div>
<div class="row">
<div class="col-md-4">
Child
</div>
<div class="col-md-8">
<p><%= plus_one.child ? 'Yes' : 'No' %></p>
</div>
</div>
<div class="row">
<div class="col-md-12 text-right">
<%= link_to 'Edit', edit_guest_plus_one_path(plus_one.guest, plus_one), class: 'card-link', data: { disable_with: 'Loading...' } %>
<%= link_to 'Remove', guest_plus_one_path(plus_one.guest, plus_one), method: :delete, class: 'card-link', data: { disable_with: 'Removing...', confirm: 'Are you sure?' } %>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,11 @@
<div class="container">
<div class="row">
<div class="offset-lg-2 col-lg-8">
<%= render partial: 'guests/leader' %>
<%= render partial: 'guests/rsvp_nav', locals: { active: 2 } %>
<h2>Edit Plus One</h2>
<%= render partial: 'form', locals: { submit_action: 'Save Plus One' } %>
</div>
</div>
</div>

View File

@ -0,0 +1,26 @@
<div class="container">
<div class="row">
<div class="offset-lg-2 col-lg-8">
<%= render partial: 'guests/leader' %>
<%= render partial: 'guests/rsvp_nav', locals: { active: 2 } %>
<h2>Plus Ones</h2>
<p>Please let us know below if you're bringing someone else to the wedding. Children are also very welcome.
<p>&nbsp;</p>
<% if @plus_ones.any? %>
<%= render @plus_ones %>
<% else %>
<p>No plus ones added yet. Flying solo.</p>
<% end %>
<p>
<%= link_to 'Add Plus One', new_guest_plus_one_path(@guest), class: 'btn btn-primary', data: { disable_with: 'Loading...' } %>
</p>
<p>&nbsp;</p>
<p class="guests-buttons">
<%= link_to 'Continue', confirm_guest_path(@guest), class: 'btn btn-primary' %>
</p>
</div>
</div>
</div>

View File

@ -0,0 +1,11 @@
<div class="container">
<div class="row">
<div class="offset-lg-2 col-lg-8">
<%= render partial: 'guests/leader' %>
<%= render partial: 'guests/rsvp_nav', locals: { active: 2 } %>
<h2>Add Plus One</h2>
<%= render partial: 'form', locals: { submit_action: 'Add Plus One' } %>
</div>
</div>
</div>

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 8 8">
<path d="M0 0v2h7v-2h-7zm0 3v4.91c0 .05.04.09.09.09h6.81c.05 0 .09-.04.09-.09v-4.91h-7zm1 1h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm-4 2h1v1h-1v-1zm2 0h1v1h-1v-1z" />
</svg>

After

Width:  |  Height:  |  Size: 263 B

View File

@ -0,0 +1,14 @@
<footer id="welcome-index-footer" class="page-footer">
<div class="container-fluid">
<div class="row">
<div id="welcome-index-footer-cta" class="col-md-12 text-center">
<%= link_to 'RSVP TODAY!', new_guest_path, class: 'btn btn-primary wedding-cta-btn' %>
</div>
</div>
<div class="row">
<div class="col-md-12 text-center" id="welcome-index-footer-credits">
<small><% t :photo_credits %></small>
</div>
</div>
</div>
</footer>

View File

@ -0,0 +1,11 @@
<section id="welcome-index-leader" class="jumbotron text-center">
<div class="container">
<h1 class="jumbotron-heading"><%= t :wedding_couple_names %></h1>
<p class="leader-date">
<%= t :wedding_date %>, <%= t :wedding_location %>
</p>
<p>
<%= link_to 'RSVP TODAY!', new_guest_path, class: 'btn btn-primary wedding-cta-btn' %>
</p>
</div>
</section>

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 8 8">
<path d="M3 0c-1.66 0-3 1.34-3 3 0 2 3 5 3 5s3-3 3-5c0-1.66-1.34-3-3-3zm0 1c1.11 0 2 .9 2 2 0 1.11-.89 2-2 2-1.1 0-2-.89-2-2 0-1.1.9-2 2-2z" transform="translate(1)" />
</svg>

After

Width:  |  Height:  |  Size: 264 B

View File

@ -0,0 +1,40 @@
<section id="welcome-index-wedding-plan-leader" class="jumbotron text-center">
<div class="container">
<h1>The Plan</h1>
</div>
</section>
<section id="welcome-index-wedding-plan">
<div class="container">
<div class="row">
<div class="offset-md-3 col-md-6">
<h2>Order of the Day</h2>
<p><strong>12:30pm</strong> &mdash; Guests arrive</p>
<p><strong>1pm</strong> &mdash; Wedding service</p>
<p><strong>1:30pm</strong> &mdash; Cocktails, photos, confetti and canapes</p>
<p><strong>3pm-6pm</strong> &mdash; Food and drink</p>
<p><strong>7pm</strong> &mdash; Cutting of the cake! Drinking, dancing!</p>
<p><strong>12am</strong> &mdash; Bar closes</p>
<p>There will be a tab for the evening, followed by a cash bar.</p>
<p><strong>Dress code</strong> is warm.</p>
<h2>Gifts &amp; Cards</h2>
<p>We'd be pleased if you made a donation to charity rather than bringing a gift. Our goal is to raise £1,000 for <%= link_to 'Cool Earth', @cool_earth_url %>, the non-profit that works alongside rainforest communities to halt deforestation and climate change.</p>
<p class="text-center"><%= link_to 'Donate to Cool Earth', @cool_earth_url, class: 'btn btn-primary wedding-cta-btn' %></p>
<p>If you'd like to send us a card, we'll send you our mailing address by email once you RSVP.</p>
<h2>Menu</h2>
<p><strong>Starter</strong> &mdash; Home Cured Salmon Gravadlax with Horseradish Cream &amp; Beetroot Tart <sup>*</sup> &dagger;</p>
<p><strong>Main</strong> &mdash; Fricassee of Chicken with Lyonnaise Potato &ddagger;</p>
<p><strong>Dessert</strong> &mdash; Choux Swan with Chocolate & Brandy Sauce <sup>&sect;</sup></p>
<p><strong>Children's Menu</strong>: Garlic bread, chicken goujons and chips, ice cream.</p>
<ul class="list-unstyled">
<li><small><sup>*</sup> Gluten free &amp; vegan starter: Tomato soup</small></li>
<li><small>&dagger; Gluten free &amp; dairy free starter: Gravadlax served without tart</small></li>
<li><small>&ddagger; Main is gluten free. Vegan main: Aubergine &amp; Black Bean Chilli with Cumin Rice</small></li>
<li><small><sup>&sect;</sup> Vegan, gluten free &amp; dairy free dessert: Fruit platter</small></li>
</ul>
<p>The venue is making every effort to cater for vegan, vegetarian, gluten free, allergies and other dietary preferences, as indicated in your RSVPs.</p>
<h2>RSVPs</h2>
<p>Please RSVP as soon as possible, so we know who is coming and can make sure we keep a seat and a piece of cake for you if you can come! If it turns out you cant make it, please let us know as soon as you can.</p>
</div>
</div>
</div>
</section>

View File

@ -0,0 +1,19 @@
<section id="welcome-index-venue-leader" class="jumbotron text-center">
<div class="container">
<h1>The Venue</h1>
</div>
</section>
<section id="welcome-index-venue">
<div class="container">
<div class="row">
<div class="offset-md-3 col-md-6">
<h2>How do I get there?</h2>
<p>Ships sail regularly from Ushuaia, Argentina. Allow two days.</p>
<p>There is ample snow mobile parking. Watch out for penguins.</p>
<h2>Coming from out of town?</h2>
<p>The station has a number of rooms that you can get at the <strong>discounted wedding rate</strong>.</p>
<p>Check in is from 3pm, so you may not be able to get ready in your room if you book it just for the night of the wedding.</p>
</div>
</div>
</div>
</section>

View File

@ -0,0 +1,33 @@
<section id="welcome-index-when-where">
<div class="container">
<div class="row">
<div class="col-md-12 text-center">
<h1>When &amp; Where</h1>
</div>
</div>
<div class="row">
<div class="col-md-6 text-center">
<h2><%= link_to 'wedding.ics' do -%>
<%= render partial: 'calendar_svg' %>
<%- end %></h2>
<p>
<strong>12:30pm &ndash; late</strong><br>
<%= t :wedding_date %><br>
&nbsp;<br>
</p>
<p><%= link_to 'Add to Calendar', 'wedding.ics' %></p>
</div>
<div class="col-md-6 text-center">
<h2><%= link_to @google_maps_url do -%>
<%= render partial: 'map_marker_svg' %>
<%- end %></h2>
<p>
<%= render partial: 'wedding_address' %>
</p>
<p>
<%= link_to 'Google Maps', @google_maps_url %>
</p>
</div>
</div>
</div>
</section>

View File

@ -0,0 +1,7 @@
<main role="main">
<%= render partial: 'jumbo' %>
<%= render partial: 'when_where' %>
<%= render partial: 'venue' %>
<%= render partial: 'plan' %>
</main>
<%= render partial: 'footer' %>

3
bin/bundle Executable file
View File

@ -0,0 +1,3 @@
#!/usr/bin/env ruby
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
load Gem.bin_path('bundler', 'bundle')

9
bin/rails Executable file
View File

@ -0,0 +1,9 @@
#!/usr/bin/env ruby
begin
load File.expand_path('../spring', __FILE__)
rescue LoadError => e
raise unless e.message.include?('spring')
end
APP_PATH = File.expand_path('../config/application', __dir__)
require_relative '../config/boot'
require 'rails/commands'

9
bin/rake Executable file
View File

@ -0,0 +1,9 @@
#!/usr/bin/env ruby
begin
load File.expand_path('../spring', __FILE__)
rescue LoadError => e
raise unless e.message.include?('spring')
end
require_relative '../config/boot'
require 'rake'
Rake.application.run

38
bin/setup Executable file
View File

@ -0,0 +1,38 @@
#!/usr/bin/env ruby
require 'pathname'
require 'fileutils'
include FileUtils
# path to your application root.
APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
def system!(*args)
system(*args) || abort("\n== Command #{args} failed ==")
end
chdir APP_ROOT do
# This script is a starting point to setup your application.
# Add necessary setup steps to this file.
puts '== Installing dependencies =='
system! 'gem install bundler --conservative'
system('bundle check') || system!('bundle install')
# Install JavaScript dependencies if using Yarn
# system('bin/yarn')
# puts "\n== Copying sample files =="
# unless File.exist?('config/database.yml')
# cp 'config/database.yml.sample', 'config/database.yml'
# end
puts "\n== Preparing database =="
system! 'bin/rails db:setup'
puts "\n== Removing old logs and tempfiles =="
system! 'bin/rails log:clear tmp:clear'
puts "\n== Restarting application server =="
system! 'bin/rails restart'
end

17
bin/spring Executable file
View File

@ -0,0 +1,17 @@
#!/usr/bin/env ruby
# This file loads spring without using Bundler, in order to be fast.
# It gets overwritten when you run the `spring binstub` command.
unless defined?(Spring)
require 'rubygems'
require 'bundler'
lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read)
spring = lockfile.specs.detect { |spec| spec.name == "spring" }
if spring
Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path
gem 'spring', spring.version
require 'spring/binstub'
end
end

29
bin/update Executable file
View File

@ -0,0 +1,29 @@
#!/usr/bin/env ruby
require 'pathname'
require 'fileutils'
include FileUtils
# path to your application root.
APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
def system!(*args)
system(*args) || abort("\n== Command #{args} failed ==")
end
chdir APP_ROOT do
# This script is a way to update your development environment automatically.
# Add necessary update steps to this file.
puts '== Installing dependencies =='
system! 'gem install bundler --conservative'
system('bundle check') || system!('bundle install')
puts "\n== Updating database =="
system! 'bin/rails db:migrate'
puts "\n== Removing old logs and tempfiles =="
system! 'bin/rails log:clear tmp:clear'
puts "\n== Restarting application server =="
system! 'bin/rails restart'
end

11
bin/yarn Executable file
View File

@ -0,0 +1,11 @@
#!/usr/bin/env ruby
VENDOR_PATH = File.expand_path('..', __dir__)
Dir.chdir(VENDOR_PATH) do
begin
exec "yarnpkg #{ARGV.join(" ")}"
rescue Errno::ENOENT
$stderr.puts "Yarn executable was not detected in the system."
$stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install"
exit 1
end
end

8
config.ru Normal file
View File

@ -0,0 +1,8 @@
# This file is used by Rack-based servers to start the application.
require_relative 'config/environment'
require_relative 'lib/rack/x_robots_tag'
use Rack::XRobotsTag
use Rack::CanonicalHost, ENV['CANONICAL_HOST'] if ENV['CANONICAL_HOST']
run Rails.application

28
config/application.rb Normal file
View File

@ -0,0 +1,28 @@
require_relative 'boot'
require 'rails'
# Pick the frameworks you want:
require 'active_model/railtie'
require 'active_job/railtie'
require 'active_record/railtie'
require 'action_controller/railtie'
require 'action_mailer/railtie'
require 'action_view/railtie'
# require "action_cable/engine"
require 'sprockets/railtie'
require 'rails/test_unit/railtie'
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
module Wedding
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 5.1
# Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers
# -- all .rb files in that directory are automatically loaded.
end
end

3
config/boot.rb Normal file
View File

@ -0,0 +1,3 @@
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
require 'bundler/setup' # Set up gems listed in the Gemfile.

87
config/database.yml Normal file
View File

@ -0,0 +1,87 @@
# PostgreSQL. Versions 9.1 and up are supported.
#
# Install the pg driver:
# gem install pg
# On OS X with Homebrew:
# gem install pg -- --with-pg-config=/usr/local/bin/pg_config
# On OS X with MacPorts:
# gem install pg -- --with-pg-config=/opt/local/lib/postgresql84/bin/pg_config
# On Windows:
# gem install pg
# Choose the win32 build.
# Install PostgreSQL and put its /bin directory on your path.
#
# Configure Using Gemfile
# gem 'pg'
#
default: &default
adapter: postgresql
encoding: unicode
# For details on connection pooling, see Rails configuration guide
# http://guides.rubyonrails.org/configuring.html#database-pooling
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
development:
<<: *default
database: wedding_development
# The specified database role being used to connect to postgres.
# To create additional roles in postgres see `$ createuser --help`.
# When left blank, postgres will use the default role. This is
# the same name as the operating system user that initialized the database.
username: postgres
# The password associated with the postgres role (username).
#password:
# Connect on a TCP socket. Omitted by default since the client uses a
# domain socket that doesn't need configuration. Windows does not have
# domain sockets, so uncomment these lines.
host: db
# The TCP port the server listens on. Defaults to 5432.
# If your server runs on a different port number, change accordingly.
#port: 5432
# Schema search path. The server defaults to $user,public
#schema_search_path: myapp,sharedapp,public
# Minimum log levels, in increasing order:
# debug5, debug4, debug3, debug2, debug1,
# log, notice, warning, error, fatal, and panic
# Defaults to warning.
#min_messages: notice
# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
<<: *default
username: postgres
host: db
database: wedding_test
# As with config/secrets.yml, you never want to store sensitive information,
# like your database password, in your source code. If your source code is
# ever seen by anyone, they now have access to your database.
#
# Instead, provide the password as a unix environment variable when you boot
# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
# for a full rundown on how to provide these environment variables in a
# production deployment.
#
# On Heroku and other platform providers, you may have a full connection URL
# available as an environment variable. For example:
#
# DATABASE_URL="postgres://myuser:mypass@localhost/somedatabase"
#
# You can use this database configuration with:
#
# production:
# url: <%= ENV['DATABASE_URL'] %>
#
production:
<<: *default
database: wedding_production
username: wedding
password: <%= ENV['WEDDING_DATABASE_PASSWORD'] %>

5
config/environment.rb Normal file
View File

@ -0,0 +1,5 @@
# Load the Rails application.
require_relative 'application'
# Initialize the Rails application.
Rails.application.initialize!

View File

@ -0,0 +1,58 @@
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
# In the development environment your application's code is reloaded on
# every request. This slows down response time but is perfect for development
# since you don't have to restart the web server when you make code changes.
config.cache_classes = false
# Do not eager load code on boot.
config.eager_load = false
# Show full error reports.
config.consider_all_requests_local = true
# Enable/disable caching. By default caching is disabled.
if Rails.root.join('tmp/caching-dev.txt').exist?
config.action_controller.perform_caching = true
config.cache_store = :memory_store
config.public_file_server.headers = {
'Cache-Control' => "public, max-age=#{2.days.seconds.to_i}"
}
else
config.action_controller.perform_caching = false
config.cache_store = :null_store
end
# Don't care if the mailer can't send.
config.action_mailer.raise_delivery_errors = false
config.action_mailer.perform_caching = false
# Print deprecation notices to the Rails logger.
config.active_support.deprecation = :log
# Raise an error on page load if there are pending migrations.
config.active_record.migration_error = :page_load
# Debug mode disables concatenation and preprocessing of assets.
# This option may cause significant delays in view rendering with a large
# number of complex assets.
config.assets.debug = true
# Suppress logger output for asset requests.
config.assets.quiet = true
# Raises error for missing translations
# config.action_view.raise_on_missing_translations = true
# Use an evented file watcher to asynchronously detect changes in source code,
# routes, locales, etc. This feature depends on the listen gem.
config.file_watcher = ActiveSupport::EventedFileUpdateChecker
config.action_mailer.default_url_options = {
host: ENV['CANONICAL_HOST'], port: 3000
}
end

View File

@ -0,0 +1,101 @@
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
# Code is not reloaded between requests.
config.cache_classes = true
# Eager load code on boot. This eager loads most of Rails and
# your application in memory, allowing both threaded web servers
# and those relying on copy on write to perform better.
# Rake tasks automatically ignore this option for performance.
config.eager_load = true
# Full error reports are disabled and caching is turned on.
config.consider_all_requests_local = false
config.action_controller.perform_caching = true
# Attempt to read encrypted secrets from `config/secrets.yml.enc`.
# Requires an encryption key in `ENV["RAILS_MASTER_KEY"]` or
# `config/secrets.yml.key`.
config.read_encrypted_secrets = true
# Disable serving static files from the `/public` folder by default since
# Apache or NGINX already handles this.
config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
# Compress JavaScripts and CSS.
config.assets.js_compressor = :uglifier
# config.assets.css_compressor = :sass
# Do not fallback to assets pipeline if a precompiled asset is missed.
config.assets.compile = false
# `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb
# Enable serving of images, stylesheets, and JavaScripts from an asset server.
# config.action_controller.asset_host = 'http://assets.example.com'
# Specifies the header that your server uses for sending files.
# config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
# config.force_ssl = true
# Use the lowest log level to ensure availability of diagnostic information
# when problems arise.
config.log_level = :debug
# Prepend all log lines with the following tags.
config.log_tags = [:request_id]
# Use a different cache store in production.
# config.cache_store = :mem_cache_store
# Use a real queuing backend for Active Job (and separate queues per environment)
# config.active_job.queue_adapter = :resque
# config.active_job.queue_name_prefix = "wedding_#{Rails.env}"
config.action_mailer.perform_caching = false
# Ignore bad email addresses and do not raise email delivery errors.
# Set this to true and configure the email server for immediate delivery to raise delivery errors.
# config.action_mailer.raise_delivery_errors = false
config.action_mailer.default_url_options = {
host: ENV['CANONICAL_HOST'],
protocol: ENV['CANONICAL_HOST_PROTOCOL'] || 'https'
}
config.action_mailer.smtp_settings = {
address: ENV['SMTP_ADDRESS'],
port: (ENV['SMTP_PORT'] || 587).to_i,
domain: ENV['SMTP_DOMAIN'],
user_name: ENV['SMTP_USER_NAME'],
password: ENV['SMTP_PASSWORD'],
authentication: ENV['SMTP_AUTHENTICATION'] || 'plain',
enable_starttls_auto: ENV['SMTP_ENABLE_STARTTLS_AUTO'] || 'true'
}
# Enable locale fallbacks for I18n (makes lookups for any locale fall back to
# the I18n.default_locale when a translation cannot be found).
config.i18n.fallbacks = true
# Send deprecation notices to registered listeners.
config.active_support.deprecation = :notify
# Use default logging formatter so that PID and timestamp are not suppressed.
config.log_formatter = ::Logger::Formatter.new
# Use a different logger for distributed setups.
# require 'syslog/logger'
# config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
if ENV['RAILS_LOG_TO_STDOUT'].present?
logger = ActiveSupport::Logger.new(STDOUT)
logger.formatter = config.log_formatter
config.logger = ActiveSupport::TaggedLogging.new(logger)
end
# Do not dump schema after migrations.
config.active_record.dump_schema_after_migration = false
end

View File

@ -0,0 +1,44 @@
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
# The test environment is used exclusively to run your application's
# test suite. You never need to work with it otherwise. Remember that
# your test database is "scratch space" for the test suite and is wiped
# and recreated between test runs. Don't rely on the data there!
config.cache_classes = true
# Do not eager load code on boot. This avoids loading your whole application
# just for the purpose of running a single test. If you are using a tool that
# preloads Rails for running tests, you may have to set it to true.
config.eager_load = false
# Configure public file server for tests with Cache-Control for performance.
config.public_file_server.enabled = true
config.public_file_server.headers = {
'Cache-Control' => "public, max-age=#{1.hour.seconds.to_i}"
}
# Show full error reports and disable caching.
config.consider_all_requests_local = true
config.action_controller.perform_caching = false
# Raise exceptions instead of rendering exception templates.
config.action_dispatch.show_exceptions = false
# Disable request forgery protection in test environment.
config.action_controller.allow_forgery_protection = false
config.action_mailer.perform_caching = false
# Tell Action Mailer not to deliver emails to the real world.
# The :test delivery method accumulates sent emails in the
# ActionMailer::Base.deliveries array.
config.action_mailer.delivery_method = :test
config.action_mailer.default_url_options = { host: 'example.com' }
# Print deprecation notices to the stderr.
config.active_support.deprecation = :stderr
# Raises error for missing translations
# config.action_view.raise_on_missing_translations = true
end

View File

@ -0,0 +1,293 @@
ActiveAdmin.setup do |config|
# == Site Title
#
# Set the title that is displayed on the main layout
# for each of the active admin pages.
#
config.site_title = 'Wedding'
# Set the link url for the title. For example, to take
# users to your main site. Defaults to no link.
#
# config.site_title_link = "/"
# Set an optional image to be displayed for the header
# instead of a string (overrides :site_title)
#
# Note: Aim for an image that's 21px high so it fits in the header.
#
# config.site_title_image = "logo.png"
# == Default Namespace
#
# Set the default namespace each administration resource
# will be added to.
#
# eg:
# config.default_namespace = :hello_world
#
# This will create resources in the HelloWorld module and
# will namespace routes to /hello_world/*
#
# To set no namespace by default, use:
# config.default_namespace = false
#
# Default:
# config.default_namespace = :admin
#
# You can customize the settings for each namespace by using
# a namespace block. For example, to change the site title
# within a namespace:
#
# config.namespace :admin do |admin|
# admin.site_title = "Custom Admin Title"
# end
#
# This will ONLY change the title for the admin section. Other
# namespaces will continue to use the main "site_title" configuration.
# == User Authentication
#
# Active Admin will automatically call an authentication
# method in a before filter of all controller actions to
# ensure that there is a currently logged in admin user.
#
# This setting changes the method which Active Admin calls
# within the application controller.
config.authentication_method = :authenticate_admin_user!
# == User Authorization
#
# Active Admin will automatically call an authorization
# method in a before filter of all controller actions to
# ensure that there is a user with proper rights. You can use
# CanCanAdapter or make your own. Please refer to documentation.
# config.authorization_adapter = ActiveAdmin::CanCanAdapter
# In case you prefer Pundit over other solutions you can here pass
# the name of default policy class. This policy will be used in every
# case when Pundit is unable to find suitable policy.
# config.pundit_default_policy = "MyDefaultPunditPolicy"
# You can customize your CanCan Ability class name here.
# config.cancan_ability_class = "Ability"
# You can specify a method to be called on unauthorized access.
# This is necessary in order to prevent a redirect loop which happens
# because, by default, user gets redirected to Dashboard. If user
# doesn't have access to Dashboard, he'll end up in a redirect loop.
# Method provided here should be defined in application_controller.rb.
# config.on_unauthorized_access = :access_denied
# == Current User
#
# Active Admin will associate actions with the current
# user performing them.
#
# This setting changes the method which Active Admin calls
# (within the application controller) to return the currently logged in user.
config.current_user_method = :current_admin_user
# == Logging Out
#
# Active Admin displays a logout link on each screen. These
# settings configure the location and method used for the link.
#
# This setting changes the path where the link points to. If it's
# a string, the strings is used as the path. If it's a Symbol, we
# will call the method to return the path.
#
# Default:
config.logout_link_path = :destroy_admin_user_session_path
# This setting changes the http method used when rendering the
# link. For example :get, :delete, :put, etc..
#
# Default:
# config.logout_link_method = :get
# == Root
#
# Set the action to call for the root path. You can set different
# roots for each namespace.
#
# Default:
# config.root_to = 'dashboard#index'
# == Admin Comments
#
# This allows your users to comment on any resource registered with Active Admin.
#
# You can completely disable comments:
# config.comments = false
#
# You can change the name under which comments are registered:
# config.comments_registration_name = 'AdminComment'
#
# You can change the order for the comments and you can change the column
# to be used for ordering:
# config.comments_order = 'created_at ASC'
#
# You can disable the menu item for the comments index page:
# config.comments_menu = false
#
# You can customize the comment menu:
# config.comments_menu = { parent: 'Admin', priority: 1 }
# == Batch Actions
#
# Enable and disable Batch Actions
#
config.batch_actions = true
# == Controller Filters
#
# You can add before, after and around filters to all of your
# Active Admin resources and pages from here.
#
# config.before_action :do_something_awesome
# == Localize Date/Time Format
#
# Set the localize format to display dates and times.
# To understand how to localize your app with I18n, read more at
# https://github.com/svenfuchs/i18n/blob/master/lib%2Fi18n%2Fbackend%2Fbase.rb#L52
#
config.localize_format = :long
# == Setting a Favicon
#
# config.favicon = 'favicon.ico'
# == Meta Tags
#
# Add additional meta tags to the head element of active admin pages.
#
# Add tags to all pages logged in users see:
# config.meta_tags = { author: 'My Company' }
# By default, sign up/sign in/recover password pages are excluded
# from showing up in search engine results by adding a robots meta
# tag. You can reset the hash of meta tags included in logged out
# pages:
# config.meta_tags_for_logged_out_pages = {}
# == Removing Breadcrumbs
#
# Breadcrumbs are enabled by default. You can customize them for individual
# resources or you can disable them globally from here.
#
# config.breadcrumb = false
# == Create Another Checkbox
#
# Create another checkbox is disabled by default. You can customize it for individual
# resources or you can enable them globally from here.
#
# config.create_another = true
# == Register Stylesheets & Javascripts
#
# We recommend using the built in Active Admin layout and loading
# up your own stylesheets / javascripts to customize the look
# and feel.
#
# To load a stylesheet:
# config.register_stylesheet 'my_stylesheet.css'
#
# You can provide an options hash for more control, which is passed along to stylesheet_link_tag():
# config.register_stylesheet 'my_print_stylesheet.css', media: :print
#
# To load a javascript file:
# config.register_javascript 'my_javascript.js'
# == CSV options
#
# Set the CSV builder separator
# config.csv_options = { col_sep: ';' }
#
# Force the use of quotes
# config.csv_options = { force_quotes: true }
# == Menu System
#
# You can add a navigation menu to be used in your application, or configure a provided menu
#
# To change the default utility navigation to show a link to your website & a logout btn
#
# config.namespace :admin do |admin|
# admin.build_menu :utility_navigation do |menu|
# menu.add label: "My Great Website", url: "http://www.mygreatwebsite.com", html_options: { target: :blank }
# admin.add_logout_button_to_menu menu
# end
# end
#
# If you wanted to add a static menu item to the default menu provided:
#
# config.namespace :admin do |admin|
# admin.build_menu :default do |menu|
# menu.add label: "My Great Website", url: "http://www.mygreatwebsite.com", html_options: { target: :blank }
# end
# end
# == Download Links
#
# You can disable download links on resource listing pages,
# or customize the formats shown per namespace/globally
#
# To disable/customize for the :admin namespace:
#
# config.namespace :admin do |admin|
#
# # Disable the links entirely
# admin.download_links = false
#
# # Only show XML & PDF options
# admin.download_links = [:xml, :pdf]
#
# # Enable/disable the links based on block
# # (for example, with cancan)
# admin.download_links = proc { can?(:view_download_links) }
#
# end
# == Pagination
#
# Pagination is enabled by default for all resources.
# You can control the default per page count for all resources here.
#
# config.default_per_page = 30
#
# You can control the max per page count too.
#
# config.max_per_page = 10_000
# == Filters
#
# By default the index screen includes a "Filters" sidebar on the right
# hand side with a filter for each attribute of the registered model.
# You can enable or disable them for all resources here.
#
# config.filters = true
#
# By default the filters include associations in a select, which means
# that every record will be loaded for each association.
# You can enabled or disable the inclusion
# of those filters by default here.
#
# config.include_default_association_filters = true
# == Footer
#
# By default, the footer shows the current Active Admin version. You can
# override the content of the footer here.
#
# config.footer = 'my custom footer text'
# == Sorting
#
# By default ActiveAdmin::OrderClause is used for sorting logic
# You can inherit it with own class and inject it for all resources
#
# config.order_clause = MyOrderClause
end

View File

@ -0,0 +1,8 @@
# Be sure to restart your server when you modify this file.
# ActiveSupport::Reloader.to_prepare do
# ApplicationController.renderer.defaults.merge!(
# http_host: 'example.org',
# https: false
# )
# end

View File

@ -0,0 +1,16 @@
# Be sure to restart your server when you modify this file.
# Version of your assets, change this if you want to expire all your assets.
Rails.application.config.assets.version = '1.0'
# Add additional assets to the asset load path.
# Rails.application.config.assets.paths << Emoji.images_path
# Add Yarn node_modules folder to the asset load path.
Rails.application.config.assets.paths << Rails.root.join('node_modules')
# Precompile additional assets.
# application.js, application.css, and all non-JS/CSS in the app/assets
# folder are already added.
Rails.application.config.assets.precompile += %w[
active_admin.js active_admin.css
]

View File

@ -0,0 +1,7 @@
# Be sure to restart your server when you modify this file.
# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
# Rails.backtrace_cleaner.remove_silencers!

View File

@ -0,0 +1,5 @@
# Be sure to restart your server when you modify this file.
# Specify a serializer for the signed and encrypted cookie jars.
# Valid options are :json, :marshal, and :hybrid.
Rails.application.config.action_dispatch.cookies_serializer = :json

View File

@ -0,0 +1,284 @@
# frozen_string_literal: true
# Use this hook to configure devise mailer, warden hooks and so forth.
# Many of these configuration options can be set straight in your model.
Devise.setup do |config|
# The secret key used by Devise. Devise uses this key to generate
# random tokens. Changing this key will render invalid all existing
# confirmation, reset password and unlock tokens in the database.
# Devise will use the `secret_key_base` as its `secret_key`
# by default. You can change it below and use your own secret key.
# config.secret_key = '8503191e4f81690b54e3e9724a67d7a850b028fc9113845240ec6957e80527bfbf4d67d9ce850d67cc0f34293d1775d1b91844c4a4fb1fea4e2bd06412c19754'
# ==> Controller configuration
# Configure the parent class to the devise controllers.
# config.parent_controller = 'DeviseController'
# ==> Mailer Configuration
# Configure the e-mail address which will be shown in Devise::Mailer,
# note that it will be overwritten if you use your own mailer class
# with default "from" parameter.
config.mailer_sender =
'please-change-me-at-config-initializers-devise@example.com'
# Configure the class responsible to send e-mails.
# config.mailer = 'Devise::Mailer'
# Configure the parent class responsible to send e-mails.
# config.parent_mailer = 'ActionMailer::Base'
# ==> ORM configuration
# Load and configure the ORM. Supports :active_record (default) and
# :mongoid (bson_ext recommended) by default. Other ORMs may be
# available as additional gems.
require 'devise/orm/active_record'
# ==> Configuration for any authentication mechanism
# Configure which keys are used when authenticating a user. The default is
# just :email. You can configure it to use [:username, :subdomain], so for
# authenticating a user, both parameters are required. Remember that those
# parameters are used only when authenticating and not when retrieving from
# session. If you need permissions, you should implement that in a before filter.
# You can also supply a hash where the value is a boolean determining whether
# or not authentication should be aborted when the value is not present.
# config.authentication_keys = [:email]
# Configure parameters from the request object used for authentication. Each entry
# given should be a request method and it will automatically be passed to the
# find_for_authentication method and considered in your model lookup. For instance,
# if you set :request_keys to [:subdomain], :subdomain will be used on authentication.
# The same considerations mentioned for authentication_keys also apply to request_keys.
# config.request_keys = []
# Configure which authentication keys should be case-insensitive.
# These keys will be downcased upon creating or modifying a user and when used
# to authenticate or find a user. Default is :email.
config.case_insensitive_keys = [:email]
# Configure which authentication keys should have whitespace stripped.
# These keys will have whitespace before and after removed upon creating or
# modifying a user and when used to authenticate or find a user. Default is :email.
config.strip_whitespace_keys = [:email]
# Tell if authentication through request.params is enabled. True by default.
# It can be set to an array that will enable params authentication only for the
# given strategies, for example, `config.params_authenticatable = [:database]` will
# enable it only for database (email + password) authentication.
# config.params_authenticatable = true
# Tell if authentication through HTTP Auth is enabled. False by default.
# It can be set to an array that will enable http authentication only for the
# given strategies, for example, `config.http_authenticatable = [:database]` will
# enable it only for database authentication. The supported strategies are:
# :database = Support basic authentication with authentication key + password
# config.http_authenticatable = false
# If 401 status code should be returned for AJAX requests. True by default.
# config.http_authenticatable_on_xhr = true
# The realm used in Http Basic Authentication. 'Application' by default.
# config.http_authentication_realm = 'Application'
# It will change confirmation, password recovery and other workflows
# to behave the same regardless if the e-mail provided was right or wrong.
# Does not affect registerable.
# config.paranoid = true
# By default Devise will store the user in session. You can skip storage for
# particular strategies by setting this option.
# Notice that if you are skipping storage for all authentication paths, you
# may want to disable generating routes to Devise's sessions controller by
# passing skip: :sessions to `devise_for` in your config/routes.rb
config.skip_session_storage = [:http_auth]
# By default, Devise cleans up the CSRF token on authentication to
# avoid CSRF token fixation attacks. This means that, when using AJAX
# requests for sign in and sign up, you need to get a new CSRF token
# from the server. You can disable this option at your own risk.
# config.clean_up_csrf_token_on_authentication = true
# When false, Devise will not attempt to reload routes on eager load.
# This can reduce the time taken to boot the app but if your application
# requires the Devise mappings to be loaded during boot time the application
# won't boot properly.
# config.reload_routes = true
# ==> Configuration for :database_authenticatable
# For bcrypt, this is the cost for hashing the password and defaults to 11. If
# using other algorithms, it sets how many times you want the password to be hashed.
#
# Limiting the stretches to just one in testing will increase the performance of
# your test suite dramatically. However, it is STRONGLY RECOMMENDED to not use
# a value less than 10 in other environments. Note that, for bcrypt (the default
# algorithm), the cost increases exponentially with the number of stretches (e.g.
# a value of 20 is already extremely slow: approx. 60 seconds for 1 calculation).
config.stretches = Rails.env.test? ? 1 : 11
# Set up a pepper to generate the hashed password.
# config.pepper = 'c6a10cf554e4adbf20edc08e8c51250a17fefd23a0e288bf5792a94fb402ebb588acbf830f7d67387c81821ab8673320f730ca58dca6cf8622f1b13303e3f8ff'
# Send a notification to the original email when the user's email is changed.
# config.send_email_changed_notification = false
# Send a notification email when the user's password is changed.
# config.send_password_change_notification = false
# ==> Configuration for :confirmable
# A period that the user is allowed to access the website even without
# confirming their account. For instance, if set to 2.days, the user will be
# able to access the website for two days without confirming their account,
# access will be blocked just in the third day. Default is 0.days, meaning
# the user cannot access the website without confirming their account.
# config.allow_unconfirmed_access_for = 2.days
# A period that the user is allowed to confirm their account before their
# token becomes invalid. For example, if set to 3.days, the user can confirm
# their account within 3 days after the mail was sent, but on the fourth day
# their account can't be confirmed with the token any more.
# Default is nil, meaning there is no restriction on how long a user can take
# before confirming their account.
# config.confirm_within = 3.days
# If true, requires any email changes to be confirmed (exactly the same way as
# initial account confirmation) to be applied. Requires additional unconfirmed_email
# db field (see migrations). Until confirmed, new email is stored in
# unconfirmed_email column, and copied to email column on successful confirmation.
config.reconfirmable = true
# Defines which key will be used when confirming an account
# config.confirmation_keys = [:email]
# ==> Configuration for :rememberable
# The time the user will be remembered without asking for credentials again.
# config.remember_for = 2.weeks
# Invalidates all the remember me tokens when the user signs out.
config.expire_all_remember_me_on_sign_out = true
# If true, extends the user's remember period when remembered via cookie.
# config.extend_remember_period = false
# Options to be passed to the created cookie. For instance, you can set
# secure: true in order to force SSL only cookies.
# config.rememberable_options = {}
# ==> Configuration for :validatable
# Range for password length.
config.password_length = 6..128
# Email regex used to validate email formats. It simply asserts that
# one (and only one) @ exists in the given string. This is mainly
# to give user feedback and not to assert the e-mail validity.
config.email_regexp = /\A[^@\s]+@[^@\s]+\z/
# ==> Configuration for :timeoutable
# The time you want to timeout the user session without activity. After this
# time the user will be asked for credentials again. Default is 30 minutes.
# config.timeout_in = 30.minutes
# ==> Configuration for :lockable
# Defines which strategy will be used to lock an account.
# :failed_attempts = Locks an account after a number of failed attempts to sign in.
# :none = No lock strategy. You should handle locking by yourself.
# config.lock_strategy = :failed_attempts
# Defines which key will be used when locking and unlocking an account
# config.unlock_keys = [:email]
# Defines which strategy will be used to unlock an account.
# :email = Sends an unlock link to the user email
# :time = Re-enables login after a certain amount of time (see :unlock_in below)
# :both = Enables both strategies
# :none = No unlock strategy. You should handle unlocking by yourself.
# config.unlock_strategy = :both
# Number of authentication tries before locking an account if lock_strategy
# is failed attempts.
# config.maximum_attempts = 20
# Time interval to unlock the account if :time is enabled as unlock_strategy.
# config.unlock_in = 1.hour
# Warn on the last attempt before the account is locked.
# config.last_attempt_warning = true
# ==> Configuration for :recoverable
#
# Defines which key will be used when recovering the password for an account
# config.reset_password_keys = [:email]
# Time interval you can reset your password with a reset password key.
# Don't put a too small interval or your users won't have the time to
# change their passwords.
config.reset_password_within = 6.hours
# When set to false, does not sign a user in automatically after their password is
# reset. Defaults to true, so a user is signed in automatically after a reset.
# config.sign_in_after_reset_password = true
# ==> Configuration for :encryptable
# Allow you to use another hashing or encryption algorithm besides bcrypt (default).
# You can use :sha1, :sha512 or algorithms from others authentication tools as
# :clearance_sha1, :authlogic_sha512 (then you should set stretches above to 20
# for default behavior) and :restful_authentication_sha1 (then you should set
# stretches to 10, and copy REST_AUTH_SITE_KEY to pepper).
#
# Require the `devise-encryptable` gem when using anything other than bcrypt
# config.encryptor = :sha512
# ==> Scopes configuration
# Turn scoped views on. Before rendering "sessions/new", it will first check for
# "users/sessions/new". It's turned off by default because it's slower if you
# are using only default views.
# config.scoped_views = false
# Configure the default scope given to Warden. By default it's the first
# devise role declared in your routes (usually :user).
# config.default_scope = :user
# Set this configuration to false if you want /users/sign_out to sign out
# only the current scope. By default, Devise signs out all scopes.
# config.sign_out_all_scopes = true
# ==> Navigation configuration
# Lists the formats that should be treated as navigational. Formats like
# :html, should redirect to the sign in page when the user does not have
# access, but formats like :xml or :json, should return 401.
#
# If you have any extra navigational formats, like :iphone or :mobile, you
# should add them to the navigational formats lists.
#
# The "*/*" below is required to match Internet Explorer requests.
# config.navigational_formats = ['*/*', :html]
# The default HTTP method used to sign out a resource. Default is :delete.
config.sign_out_via = :delete
# ==> OmniAuth
# Add a new OmniAuth provider. Check the wiki for more information on setting
# up on your models and hooks.
# config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo'
# ==> Warden configuration
# If you want to use other strategies, that are not supported by Devise, or
# change the failure app, you can configure them inside the config.warden block.
#
# config.warden do |manager|
# manager.intercept_401 = false
# manager.default_strategies(scope: :user).unshift :some_external_strategy
# end
# ==> Mountable engine configurations
# When using Devise inside an engine, let's call it `MyEngine`, and this engine
# is mountable, there are some extra configurations to be taken into account.
# The following options are available, assuming the engine is mounted as:
#
# mount MyEngine, at: '/my_engine'
#
# The router that invoked `devise_for`, in the example above, would be:
# config.router_name = :my_engine
#
# When using OmniAuth, Devise cannot automatically set OmniAuth path,
# so you need to do it manually. For the users scope, it would be:
# config.omniauth_path_prefix = '/my_engine/users/auth'
end

View File

@ -0,0 +1,4 @@
# Be sure to restart your server when you modify this file.
# Configure sensitive parameters which will be filtered from the log file.
Rails.application.config.filter_parameters += [:password]

View File

@ -0,0 +1,16 @@
# Be sure to restart your server when you modify this file.
# Add new inflection rules using the following format. Inflections
# are locale specific, and you may define rules for as many different
# locales as you wish. All of these examples are active by default:
# ActiveSupport::Inflector.inflections(:en) do |inflect|
# inflect.plural /^(ox)$/i, '\1en'
# inflect.singular /^(ox)en/i, '\1'
# inflect.irregular 'person', 'people'
# inflect.uncountable %w( fish sheep )
# end
# These inflection rules are supported but not enabled by default:
# ActiveSupport::Inflector.inflections(:en) do |inflect|
# inflect.acronym 'RESTful'
# end

View File

@ -0,0 +1,4 @@
# Be sure to restart your server when you modify this file.
# Add new mime types for use in respond_to blocks:
# Mime::Type.register "text/richtext", :rtf

Some files were not shown because too many files have changed in this diff Show More