Compare commits

...

133 Commits

Author SHA1 Message Date
Petko Bordjukov deebcbc95a New dev-friendly schedule endpoint 2024-10-03 23:25:01 +03:00
Tocho Tochev 5660c3ef53 Update talk confirmation template 2024-10-01 15:24:50 +03:00
Petko Bordjukov 71e8ab74da Limit access to archived conferences 2024-10-01 14:59:48 +03:00
Petko Bordjukov 89dd1890fd Add freshness headers to API 2024-10-01 01:36:19 +03:00
Petko Bordjukov 1018b93b11 Introduce thruster 2024-10-01 00:55:19 +03:00
Tocho Tochev 8cd4a4856d Remove the help text 2024-09-25 02:30:08 +03:00
Tocho Tochev 0cffe052c8 Ask volunteers to agree to our ToS 2024-09-25 02:30:08 +03:00
Tocho Tochev 6af2d09896 Fix speaker picture and add name 2024-09-23 13:24:42 +03:00
Tocho Tochev ef5935bd85 Fix race condition on emailing for new proposition 2024-09-22 23:02:25 +03:00
Tocho Tochev 10e68f3453 Use ruby 3.3.5 in all places 2024-09-22 22:44:30 +03:00
Petko Bordjukov 5bbe601110 Enable caching of halfnarp talks 2024-09-13 23:42:55 +03:00
Petko Bordjukov 89438b474b Limit data returned by halfnarp-friendly endpoint 2024-09-13 23:33:55 +03:00
Petko Bordjukov 7a64633ac0 Revert "Speed up json generation"
This reverts commit 6171e484cc.
2024-09-13 23:09:16 +03:00
Petko Bordjukov 6171e484cc Speed up json generation 2024-09-13 22:58:10 +03:00
Petko Bordjukov 2bacd84654 Assume SSL 2024-09-13 22:22:43 +03:00
Petko Bordjukov fc08089796 Introduce volunteer deletion 2024-09-08 18:27:47 +03:00
Petko Bordjukov 93611a3bd9 Configure Rubocop to work with StandardRB 2024-07-03 23:17:22 +03:00
Tocho Tochev 459be53b5c Notify with anonymised email on new volunteer (#48)
People enjoy the instant gratification of a random alert.

This brings back email notifications to organizers on volunteer sign-up so that relevant people can react as soon as possible.

The email now will contain anonymous data (team name and t-shirt size) and a link for curious people to view the profile.

I have considered adding another email address for such notifications, but it seems unnecessarily complicated.

Proof of work:

![image](/attachments/dabd0375-17ac-43c8-9698-678e768aa111)

![image](/attachments/fed16195-e668-47c7-9647-5fe069f1ed2d)

Reviewed-on: #48
Co-authored-by: Tocho Tochev <tocho@tochev.net>
Co-committed-by: Tocho Tochev <tocho@tochev.net>
2024-06-01 12:32:39 +03:00
Tocho Tochev bcac28d4ff Fix volunteer counts shown for a team (#46)
Before this change the count of volunteers in a team was not in sync with the filters applied.

![image](/attachments/0a3a0c5c-f3c2-4ef4-89d7-0bcf1fb9302d)

After the change:
![image](/attachments/b88719f6-fadf-4f07-b4d4-985533b0ea2f)

BTW I feel that we will end up refactoring the relation at some point in the future.

Reviewed-on: #46
Co-authored-by: Tocho Tochev <tocho@tochev.net>
Co-committed-by: Tocho Tochev <tocho@tochev.net>
2024-06-01 12:32:12 +03:00
Tocho Tochev 5ff505d246 Add indication for unverified volunteers (#45)
We want to have some indication for that some volunteers haven't confirmed their email.

Perhaps "unverified" is bad terminology, but until we have proper "verification" it will suffice.

(Ideally there would also be a filter, but my rails is way too rusty...)

Reviewed-on: #45
Co-authored-by: Tocho Tochev <tocho@tochev.net>
Co-committed-by: Tocho Tochev <tocho@tochev.net>
2024-06-01 12:31:38 +03:00
Tocho Tochev 87df897fe9 Remove stray docker-ignore (#43)
I don't think the `.docker-ignore` does anything when there is a `.dockerignore` so let's remove it.

I'm open to amending the PR.

Reviewed-on: #43
Co-authored-by: Tocho Tochev <tocho@tochev.net>
Co-committed-by: Tocho Tochev <tocho@tochev.net>
2024-06-01 12:30:57 +03:00
Tocho Tochev d44738bf58 Add localhost as name to initfest domains (#44)
Before this change if someone is using `localhost` for development instead of `127.0.0.1`, he would get a missing template error.

Reviewed-on: #44
Co-authored-by: Tocho Tochev <tocho@tochev.net>
Co-committed-by: Tocho Tochev <tocho@tochev.net>
2024-06-01 12:30:16 +03:00
Tocho Tochev 486a763277 Add docker-compose (#47)
This is a docker-compose based on production config, without mounting of the local folders, which I admit is a strange setup, but this was the simplest for me to get the local development going.

I'm perfectly ok with not merging this PR.

Reviewed-on: #47
Co-authored-by: Tocho Tochev <tocho@tochev.net>
Co-committed-by: Tocho Tochev <tocho@tochev.net>
2024-06-01 12:29:46 +03:00
Tocho Tochev cd8c3bbcc7 Add devise BG locale (#49)
I will raise the changes to the upstream project after review https://github.com/tochev/devise-i18n/pull/1/files

Co-authored-by: Albert Stefanov <aastefanov@outlook.com>
Reviewed-on: #49
Co-authored-by: Tocho Tochev <tocho@tochev.net>
Co-committed-by: Tocho Tochev <tocho@tochev.net>
2024-06-01 12:29:13 +03:00
Tocho Tochev 2180b0ea9f Update texts 2024-04-21 15:30:23 +03:00
Petko Bordjukov 1c42261f2a Tweak honeypot field for volunteers 2024-04-19 14:42:10 +03:00
Petko Bordjukov d123a8c69d ActiveStorage configuration 2024-04-19 00:54:54 +03:00
Petko Bordjukov ce6fac9764 Mark stored attachments as public 2024-04-19 00:41:21 +03:00
Petko Bordjukov 78c73d273e Make FROM mail addresses more friendly 2024-04-18 21:48:34 +03:00
Petko Bordjukov 1834beb13d Anti-spam measures for volunteering 2024-04-18 21:38:03 +03:00
Petko Bordjukov e461ec504f Add migration for existing volunteers 2024-04-18 19:03:35 +03:00
Petko Bordjukov 0e0d73cbbd Make volunteers choose a single main team 2024-04-18 18:57:26 +03:00
Petko Bordjukov f43c125e6d Fix asset name 2024-04-18 17:08:18 +03:00
Petko Bordjukov 977e231e14 Enable HTTP Early Hints 2024-04-14 00:48:04 +03:00
Petko Bordjukov 20c7c20633 Dockerise clarion 2024-04-13 23:16:43 +03:00
Petko Bordjukov 4c96ba1e9c Migrate from Refile and Carrierwave to ActiveStorage 2024-04-13 22:17:15 +03:00
Petko Bordjukov 281b69e66d Rails 7.1.3 Upgrade 2024-04-13 22:17:15 +03:00
Petko Bordjukov fdd75603f7 Rails 7.0.8 Upgrade 2024-04-13 22:17:15 +03:00
Petko Bordjukov 2ab307bede Rails 6.1.7.6 Upgrade 2024-04-13 22:17:15 +03:00
Petko Bordjukov 5628650e5b Rails 6.0.6.1 upgrade 2024-04-13 22:17:15 +03:00
Petko Bordjukov 28348696c5 Update todo 2024-04-08 14:14:42 +03:00
Petko Bordjukov b80eab40cc Slack's new api 2023-10-30 21:10:14 +02:00
Petko Bordjukov 14dacf02c1 Remove time limit in mailer 2023-10-14 23:11:03 +03:00
Petko Bordjukov 2aa5891e32
Text changes for 2023 (#41)
* Text changes for 2023

* Update app/views/event_mailer/rejection_notification.en.text.erb

Co-authored-by: mboshikyova-qb <87693649+mboshikyova-qb@users.noreply.github.com>

---------

Co-authored-by: mboshikyova-qb <87693649+mboshikyova-qb@users.noreply.github.com>
2023-10-10 22:56:37 +03:00
Petko Bordjukov 19ea5f5880
Merge pull request #39 from OpenFest/add-notes-to-export
Add notes to export
2023-10-07 19:32:52 +03:00
Tocho Tochev e71d9c683c
Add notes to export 2023-10-07 07:17:11 +00:00
Tocho Tochev 6326500ffe
Add event length to csv export 2023-09-27 19:09:22 +00:00
Tocho Tochev ddc11f4fcc Fix texts 2023-07-30 13:07:24 +03:00
Petko Bordjukov 2545a294f7 Enhance logging 2023-05-27 23:01:50 +03:00
Petko Bordjukov 72b7787003 Update volunteer mailer subject 2023-05-27 22:36:10 +03:00
Petko Bordjukov c04be87f53 fix feedback session logging 2022-10-19 01:42:37 +03:00
Petko Bordjukov 05617add98 Remove eagerness 2022-10-19 00:57:37 +03:00
Petko Bordjukov c818f30737 Bundle tweaks 2022-10-19 00:51:43 +03:00
Petko Bordjukov 0af5523a88 Feedback overview 2022-10-19 00:31:17 +03:00
Petko Bordjukov 76ce1bf861 Bulk update of vulnerable dependencies 2022-03-22 23:27:44 +02:00
Petko Bordjukov 74bd417da3 Fix dependency 2021-07-27 19:14:38 +03:00
Vasil Kolev e6ce449f99 mailer: fix typo in rejection mail 2021-07-25 22:33:18 +03:00
Vasil Kolev 32861f640d mailer: fix typo in rejection mail 2021-07-25 22:11:08 +03:00
Vasil Kolev 85b6331c21 mailer: update rejection mail to 2021 2021-07-25 21:22:59 +03:00
Vasil Kolev 98982b1823 mailer: 2021 specfic updates to confirmation mail 2021-07-25 21:14:58 +03:00
Petko Bordjukov c34955e106 Merge remote-tracking branch 'origin/pr/34' 2020-10-29 18:56:48 +02:00
Ivaylo Markov 6755ea4b1e Remove event link in iCalendar export 2020-10-29 18:44:31 +02:00
Petko Bordjukov e4f36af7c0
Merge pull request #33 from lz1irq/2020-speaker-emails
Update speaker rejection/confirmation emails to fit the online-only event
2020-10-22 20:24:55 +03:00
Ivaylo Markov 2c1b86b720 Update speaker rejection/confirmation emails to fit the online-only event 2020-10-22 20:06:09 +03:00
Petko Bordjukov 014c88c4e5
Merge pull request #32 from lz1irq/copyright_current_year
Always show current year for copyright in footer
2020-10-01 14:03:59 +03:00
Ivaylo Markov eacd083237 Always show current year for copyright in footer 2020-09-28 20:10:23 +03:00
Petko Bordjukov a95dcc866f Add time zone to ical 2019-11-01 04:54:42 +02:00
Petko Bordjukov 9dc216f30b ICAL export MVP 2019-10-28 13:20:12 +02:00
Petko Bordjukov 4e97ad0eaa Introduce a dependency on icalendar 2019-10-28 12:37:32 +02:00
Petko Bordjukov de45091c59 Fix issues with attaching QR code to email 2019-10-13 14:38:37 +03:00
Vasil Kolev b5c88b1f8e confimation_bg: grammar fix 2019-10-13 13:12:35 +03:00
Petko Bordjukov 49b85190f2 Add feedback link QR code to confirmation emails 2019-10-12 21:25:17 +03:00
Petko Bordjukov 3dfdbad313 Update the bundle 2019-10-04 14:47:55 +03:00
Petko Bordjukov 41718b9fca Do not show rejected propositions in gauge 2019-10-04 14:47:55 +03:00
Petko Bordjukov ec34b39699 Update the bundle 2019-07-19 11:40:33 +03:00
Petko Bordjukov ca91ba4255 Try installing chrome stable again 2019-06-17 14:48:27 +03:00
Petko Bordjukov cbd69fab5a Fix indentation in events#show view that caused a condition to be skipped 2019-06-17 14:23:56 +03:00
Petko Bordjukov f724e58800 Fix exception when trying to filter by volunteer team 2019-06-16 10:30:08 +03:00
Petko Bordjukov 8daac96d55 Fix volunteer images not showing
Had forgotten to add refile-mini_magick that is now required for all the
imagemagick-related functionality of refile.
2019-05-28 13:15:03 +03:00
Petko Bordjukov ab8b7102fb Add public/.well-known to liked dirs 2019-05-05 14:47:53 +03:00
Petko Bordjukov e67f719998 Convert end_date in conferences to timestamp with tz 2019-05-05 14:18:36 +03:00
Petko Bordjukov 7c57ff699d Add convert a missed timestamp column to timestamp with tz 2019-05-05 14:12:06 +03:00
Petko Bordjukov 23bd2ab334 Optimize management events view 2019-05-05 00:23:53 +03:00
Petko Bordjukov eeb92483f3 Try not installing chrome stable 2019-05-04 23:49:37 +03:00
Petko Bordjukov f704bdb82f Migrate every timestamp to timestamp with timezone 2019-05-04 23:45:47 +03:00
Petko Bordjukov 35aa246df4 Overhaul speaker info in event show 2019-05-01 01:48:44 +03:00
Petko Bordjukov ed85b96f4d Handle nil value 2019-04-30 01:07:48 +03:00
Petko Bordjukov 9930665156 Revamp the personal profile details view 2019-04-30 01:04:40 +03:00
Petko Bordjukov 7bb4ba478c Overhaul PersonalProfile details view 2019-04-29 21:34:03 +03:00
Petko Bordjukov 2d19046d35 Feedback in the event-related views 2019-04-29 14:37:45 +03:00
Petko Bordjukov b774cb9b11 Add params filtering in Management::EventsController#index 2019-04-29 00:31:36 +03:00
Petko Bordjukov 48c979549f Fix Capistrano Puma tasks 2019-04-29 00:14:57 +03:00
Petko Bordjukov e97dd3a09f Add capistrano-rvm 2019-04-29 00:01:33 +03:00
Petko Bordjukov 4a8ee144c1 Add a .ruby-version file 2019-04-28 23:58:11 +03:00
Petko Bordjukov 57db0a548c Enable PostgreSQL in Travis 2019-04-28 22:57:14 +03:00
Petko Bordjukov 2c36c8c354 Add example postgres db config 2019-04-28 22:54:38 +03:00
Petko Bordjukov 752c3123d6 Do not attempt to clean up a view
See https://github.com/DatabaseCleaner/database_cleaner/issues/504
2019-04-28 22:14:34 +03:00
Petko Bordjukov a8d7cde7a1 Explicity require the feature helpers 2019-04-28 22:06:19 +03:00
Petko Bordjukov e20861689f Mechanical standard.rb changes 2019-04-28 22:02:02 +03:00
Petko Bordjukov 895bf8a5f3 standard.rb 2019-04-28 22:02:02 +03:00
Petko Bordjukov 992ede0735 Add missing foreign key constraints and set correct on delete behaviour 2019-04-28 22:01:39 +03:00
Petko Bordjukov c40f65fade Flip all framework defaults and enable sql schema dumping 2019-04-28 12:48:45 +03:00
Petko Bordjukov 9c46c4cf5d Update Travis configuration 2019-04-28 12:01:06 +03:00
Petko Bordjukov 6f627d34d6 Inherit from ActiveRecord::Migration[4.2] 2019-04-28 11:56:16 +03:00
Petko Bordjukov c95a0c8d4b Fix broken specs 2019-04-28 11:52:05 +03:00
Petko Bordjukov e6b16ed1ae The event is not required for the existence of a slot 2019-04-28 11:52:05 +03:00
Petko Bordjukov c073cf56ca Add bootsnap 2019-04-28 11:52:05 +03:00
Petko Bordjukov b40c6ae877 rails app:update 2019-04-28 11:52:05 +03:00
Petko Bordjukov 52dc663119 Update the bundle to Rails 5.2 2019-04-28 11:52:05 +03:00
Petko Bordjukov 32b1b1c458 belongs_to :x, class_name: Class -> String 2019-04-28 11:52:05 +03:00
Petko Bordjukov e0abfa5253 Update globalize 2019-04-28 11:52:05 +03:00
Petko Bordjukov d284eb3e2b rake app:update 2019-04-28 11:52:05 +03:00
Petko Bordjukov 0d7571d637 Update to Rails 5.1 2019-04-28 02:18:18 +03:00
Petko Bordjukov 77e93809f6 uniq -> distinct 2019-04-28 02:17:21 +03:00
Petko Bordjukov 69f4c4dc26 before_filter -> before_action 2019-04-28 02:15:39 +03:00
Petko Bordjukov a7c9c83e02 redirect_to :back -> redirect_back 2019-04-28 02:12:54 +03:00
Petko Bordjukov eacc973cf2 Remove new framework defaults 2019-04-28 02:05:15 +03:00
Petko Bordjukov 88de288029 rake app:update 2019-04-28 02:02:09 +03:00
Petko Bordjukov 6ab333edf7 Update to Rails 5.0 2019-04-28 01:34:56 +03:00
Petko Bordjukov fffcd8db2c FactoryGirl -> FactoryBot 2019-04-28 01:25:13 +03:00
Petko Bordjukov e2217d4d6e We need an older version of the pg gem 2019-04-28 01:06:44 +03:00
Petko Bordjukov f4afa949d5 Update the bundle 2019-04-28 00:52:31 +03:00
Petko Bordjukov d727f0e4d4 Added more test scenarios 2019-04-27 21:57:07 +03:00
Petko Bordjukov d56c489f16 Add chrome to travis config 2019-01-08 20:49:40 +02:00
Petko Bordjukov 26d6c868b5 Specify Capybara javscript driver until we upgrade 2019-01-08 20:26:02 +02:00
Petko Bordjukov 8444015e90 Add tests for event review/edit and personal profile creation from admin 2019-01-08 20:09:47 +02:00
Petko Bordjukov 62b4fc4456 Set the correct host_name for the test conference 2019-01-08 20:09:47 +02:00
Petko Bordjukov db85a64686 Use translation in management/events controller 2019-01-08 20:09:47 +02:00
Petko Bordjukov 9848898695 Verify that the volunteer appears in the management interface 2019-01-08 20:09:47 +02:00
Petko Bordjukov 09440ba9c5 Test editing and deleting conferences 2019-01-08 20:09:47 +02:00
Petko Bordjukov 20787bcfb3 Turn off Guard notifications 2019-01-08 20:09:47 +02:00
Petko Bordjukov 34c2532141 Bump sqlite3 to fix MacOS compilation issue 2019-01-08 20:09:47 +02:00
Petko Bordjukov 8adfd721df Test creating a conference and activating its CFP 2019-01-08 20:09:47 +02:00
287 changed files with 6699 additions and 2059 deletions

43
.dockerignore Normal file
View File

@ -0,0 +1,43 @@
# See https://docs.docker.com/engine/reference/builder/#dockerignore-file for more about ignoring files.
# Ignore git directory.
/.git/
# Ignore bundler config.
/.bundle
# Ignore all environment files (except templates).
/.env*
!/.env*.erb
# Ignore all default key files.
/config/master.key
/config/credentials/*.key
# Ignore all logfiles and tempfiles.
/log/*
/tmp/*
!/log/.keep
!/tmp/.keep
# Ignore pidfiles, but keep the directory.
/tmp/pids/*
!/tmp/pids/.keep
# Ignore storage (uploaded files in development and any SQLite databases).
/storage/*
!/storage/.keep
/tmp/storage/*
!/tmp/storage/.keep
# Ignore assets.
/node_modules/
/app/assets/builds/*
!/app/assets/builds/.keep
/public/assets
# Archives
*.tar.gz
# SQL
*.sql

37
.gitignore vendored
View File

@ -7,18 +7,29 @@
# Ignore bundler config. # Ignore bundler config.
/.bundle /.bundle
# Ignore the default SQLite database. # Ignore all environment files (except templates).
/db/*.sqlite3 /.env*
/db/*.sqlite3-journal !/.env*.erb
# Ignore all logfiles and tempfiles. # Ignore all logfiles and tempfiles.
/log/*.log /log/*
/tmp /tmp/*
/config/database.yml !/log/.keep
/config/secrets.yml !/tmp/.keep
/db/schema.rb
/erd.pdf # Ignore pidfiles, but keep the directory.
/public/uploads/tmp/* /tmp/pids/*
/public/uploads/personal_profile/* !/tmp/pids/
.sass-cache/ !/tmp/pids/.keep
/coverage/
# Ignore storage (uploaded files in development and any SQLite databases).
/storage/*
!/storage/.keep
/tmp/storage/*
!/tmp/storage/
!/tmp/storage/.keep
/public/assets
# Ignore master key for decrypting credentials and more.
/config/master.key

7
.rubocop.yml Normal file
View File

@ -0,0 +1,7 @@
require: standard
inherit_gem:
standard: config/base.yml
AllCops:
DisabledByDefault: true

1
.ruby-version Normal file
View File

@ -0,0 +1 @@
3.3.5

View File

@ -1,7 +1,16 @@
language: ruby language: ruby
dist: xenial
cache: bundler cache: bundler
rvm: rvm:
- 2.4.5 - 3.3
script: script:
- RAILS_ENV=test bundle exec rake --trace bootstrap spec - RAILS_ENV=test bundle exec rake --trace bootstrap spec
sudo: false addons:
chrome: stable
apt:
packages:
- chromium-chromedriver
services:
- postgresql
before_script:
- ln -s /usr/lib/chromium-browser/chromedriver ~/bin/chromedriver

22
Capfile
View File

@ -1,8 +1,11 @@
# Load DSL and set up stages # Load DSL and set up stages
require 'capistrano/setup' require "capistrano/setup"
# Include default deployment tasks # Include default deployment tasks
require 'capistrano/deploy' require "capistrano/deploy"
require "capistrano/scm/git"
install_plugin Capistrano::SCM::Git
# Include tasks from other gems included in your Gemfile # Include tasks from other gems included in your Gemfile
# #
@ -15,16 +18,19 @@ require 'capistrano/deploy'
# https://github.com/capistrano/rails # https://github.com/capistrano/rails
# https://github.com/capistrano/passenger # https://github.com/capistrano/passenger
# #
# require 'capistrano/rvm' require 'capistrano/rvm'
# require 'capistrano/rbenv' # require 'capistrano/rbenv'
# require 'capistrano/chruby' # require 'capistrano/chruby'
require 'capistrano/bundler' require "capistrano/bundler"
require 'capistrano/rails/assets' require "capistrano/rails/assets"
require 'capistrano/rails/migrations' require "capistrano/rails/migrations"
require 'capistrano/puma' require "capistrano/puma"
install_plugin Capistrano::Puma # Default puma tasks
install_plugin Capistrano::Puma::Workers # if you want to control the workers (in cluster mode)
install_plugin Capistrano::Puma::Nginx # if you want to upload a nginx site template
# require 'capistrano/puma/nginx' # if you want to upload a nginx site template # require 'capistrano/puma/nginx' # if you want to upload a nginx site template
# require 'capistrano/passenger' # require 'capistrano/passenger'
# require 'capistrano/rvm' # require 'capistrano/rvm'
# Load custom tasks from `lib/capistrano/tasks` if you have any defined # Load custom tasks from `lib/capistrano/tasks` if you have any defined
Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r } Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r }

62
Dockerfile Normal file
View File

@ -0,0 +1,62 @@
# syntax = docker/dockerfile:1
# Make sure RUBY_VERSION matches the Ruby version in .ruby-version and Gemfile
ARG RUBY_VERSION=3.3.5
FROM registry.docker.com/library/ruby:$RUBY_VERSION-slim as base
# Rails app lives here
WORKDIR /rails
# Set production environment
ENV RAILS_ENV="production" \
BUNDLE_DEPLOYMENT="1" \
BUNDLE_PATH="/usr/local/bundle" \
BUNDLE_WITHOUT="development"
# Throw-away build stage to reduce size of final image
FROM base as build
# Install packages needed to build gems
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y build-essential git libvips pkg-config libpq-dev libsqlite3-dev nodejs yarn
# Install application gems
COPY Gemfile Gemfile.lock ./
RUN bundle install && \
rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git && \
bundle exec bootsnap precompile --gemfile
# Copy application code
COPY . .
# Precompile bootsnap code for faster boot times
RUN bundle exec bootsnap precompile app/ lib/
# Precompiling assets for production without requiring secret RAILS_MASTER_KEY
RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile
# Final stage for app image
FROM base
# Install packages needed for deployment
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y curl libsqlite3-0 nodejs libpq5 libvips && \
rm -rf /var/lib/apt/lists /var/cache/apt/archives
# Copy built artifacts: gems, application
COPY --from=build /usr/local/bundle /usr/local/bundle
COPY --from=build /rails /rails
# Run and own only the runtime files as a non-root user for security
RUN useradd rails --create-home --shell /bin/bash && \
chown -R rails:rails db log storage tmp
USER rails:rails
# Entrypoint prepares the database.
ENTRYPOINT ["/rails/bin/docker-entrypoint"]
# Start the server by default, this can be overwritten at runtime
EXPOSE 80
CMD ["./bin/thrust", "./bin/rails", "server", "--early-hints"]

119
Gemfile
View File

@ -1,95 +1,92 @@
source 'https://rubygems.org' source "https://rubygems.org"
gem 'rails', '~> 4.2.11' gem "rails", "~> 7.1.0"
gem "bootsnap"
gem "sprockets"
gem 'sqlite3' gem "sqlite3"
gem 'pg' gem "pg"
gem 'sass-rails' gem "sass-rails"
gem 'uglifier' gem "uglifier"
gem 'coffee-rails' gem "coffee-rails"
gem 'mini_racer', platforms: :ruby gem "jquery-rails"
gem 'jquery-rails'
gem 'slim-rails' gem "slim-rails"
gem 'rails-i18n' gem "rails-i18n"
gem 'devise' gem "devise"
gem 'devise-i18n' gem "devise-i18n"
gem 'simple_form' gem "simple_form"
# Phone validation # Phone validation
gem 'phony_rails' gem "phony"
gem "phony_rails"
# Picture uploads gem "puma", group: :production
gem 'carrierwave'
#gem 'rmagick'
gem "mini_magick"
gem "refile", require: ['refile/rails', 'refile/simple_form'] gem "globalize"
gem "refile-mini_magick"
gem 'puma', group: :production gem "yaml_db"
gem 'globalize' gem "bootstrap-sass"
gem "bootstrap-sass-extras"
gem "bootswatch-rails"
gem "autoprefixer-rails"
gem "font-awesome-sass", "~> 4.6.2"
gem 'yaml_db' gem "nested_form"
gem "jquery-datatables-rails"
# gem "morrisjs-rails"
gem "raphael-rails"
gem 'bootstrap-sass' gem "copy_carrierwave_file"
gem 'bootstrap-sass-extras'
gem 'bootswatch-rails'
gem 'autoprefixer-rails'
gem 'font-awesome-sass'
gem 'nested_form' gem "jbuilder"
gem 'jquery-datatables-rails'
gem 'morrisjs-rails'
gem 'raphael-rails'
gem 'copy_carrierwave_file' gem "search_object"
gem 'jbuilder' gem "faraday"
gem 'search_object' gem "rqrcode"
gem 'faraday' gem "draper"
gem 'rqrcode' gem "icalendar", require: ['icalendar', 'icalendar/tzinfo']
group :development do group :development do
gem 'spring' gem "spring"
gem 'spring-commands-rspec' gem "spring-commands-rspec"
gem 'guard-rspec' # Continuous testing with Guard gem "guard-rspec" # Continuous testing with Guard
gem 'rails-erd' gem "rails-erd"
gem 'pry-rails' gem "pry-rails"
# gem 'hirb' # gem 'hirb'
gem 'awesome_print' gem "awesome_print"
gem 'quiet_assets' gem "better_errors"
gem 'capistrano' gem "binding_of_caller"
gem 'capistrano-rails'
# gem 'capistrano-rvm'
gem 'capistrano3-puma'
gem 'better_errors'
gem 'binding_of_caller'
end end
group :development, :test do group :development, :test do
gem 'rspec-rails' gem "rspec-rails"
gem 'factory_girl_rails' gem "faker"
gem 'faker' gem "capybara"
gem 'capybara' gem "selenium-webdriver"
gem 'selenium-webdriver'
gem 'byebug' gem "byebug"
gem 'simplecov' gem "simplecov"
gem 'i18n-tasks' gem "i18n-tasks"
gem 'delorean' gem "delorean"
gem "standard"
end end
group :test do group :test do
gem 'database_cleaner' gem "database_cleaner"
gem "factory_bot_rails"
end end
gem "thruster", "~> 0.1.8"

View File

@ -1,158 +1,208 @@
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
actionmailer (4.2.11) actioncable (7.1.3)
actionpack (= 4.2.11) actionpack (= 7.1.3)
actionview (= 4.2.11) activesupport (= 7.1.3)
activejob (= 4.2.11) nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
zeitwerk (~> 2.6)
actionmailbox (7.1.3)
actionpack (= 7.1.3)
activejob (= 7.1.3)
activerecord (= 7.1.3)
activestorage (= 7.1.3)
activesupport (= 7.1.3)
mail (>= 2.7.1)
net-imap
net-pop
net-smtp
actionmailer (7.1.3)
actionpack (= 7.1.3)
actionview (= 7.1.3)
activejob (= 7.1.3)
activesupport (= 7.1.3)
mail (~> 2.5, >= 2.5.4) mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 1.0, >= 1.0.5) net-imap
actionpack (4.2.11) net-pop
actionview (= 4.2.11) net-smtp
activesupport (= 4.2.11) rails-dom-testing (~> 2.2)
rack (~> 1.6) actionpack (7.1.3)
rack-test (~> 0.6.2) actionview (= 7.1.3)
rails-dom-testing (~> 1.0, >= 1.0.5) activesupport (= 7.1.3)
rails-html-sanitizer (~> 1.0, >= 1.0.2) nokogiri (>= 1.8.5)
actionview (4.2.11) racc
activesupport (= 4.2.11) rack (>= 2.2.4)
rack-session (>= 1.0.1)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6)
actiontext (7.1.3)
actionpack (= 7.1.3)
activerecord (= 7.1.3)
activestorage (= 7.1.3)
activesupport (= 7.1.3)
globalid (>= 0.6.0)
nokogiri (>= 1.8.5)
actionview (7.1.3)
activesupport (= 7.1.3)
builder (~> 3.1) builder (~> 3.1)
erubis (~> 2.7.0) erubi (~> 1.11)
rails-dom-testing (~> 1.0, >= 1.0.5) rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.0, >= 1.0.3) rails-html-sanitizer (~> 1.6)
activejob (4.2.11) activejob (7.1.3)
activesupport (= 4.2.11) activesupport (= 7.1.3)
globalid (>= 0.3.0) globalid (>= 0.3.6)
activemodel (4.2.11) activemodel (7.1.3)
activesupport (= 4.2.11) activesupport (= 7.1.3)
activemodel-serializers-xml (1.0.2)
activemodel (> 5.x)
activesupport (> 5.x)
builder (~> 3.1) builder (~> 3.1)
activerecord (4.2.11) activerecord (7.1.3)
activemodel (= 4.2.11) activemodel (= 7.1.3)
activesupport (= 4.2.11) activesupport (= 7.1.3)
arel (~> 6.0) timeout (>= 0.4.0)
activesupport (4.2.11) activestorage (7.1.3)
i18n (~> 0.7) actionpack (= 7.1.3)
minitest (~> 5.1) activejob (= 7.1.3)
thread_safe (~> 0.3, >= 0.3.4) activerecord (= 7.1.3)
tzinfo (~> 1.1) activesupport (= 7.1.3)
addressable (2.5.2) marcel (~> 1.0)
public_suffix (>= 2.0.2, < 4.0) activesupport (7.1.3)
airbrussh (1.1.1) base64
sshkit (>= 1.6.1, != 1.7.0) bigdecimal
arel (6.0.4) concurrent-ruby (~> 1.0, >= 1.0.2)
ast (2.3.0) connection_pool (>= 2.2.5)
autoprefixer-rails (6.5.1) drb
execjs i18n (>= 1.6, < 2)
awesome_print (1.7.0) minitest (>= 5.1)
bcrypt (3.1.11) mutex_m
better_errors (2.1.1) tzinfo (~> 2.0)
coderay (>= 1.0.0) addressable (2.8.6)
erubis (>= 2.6.6) public_suffix (>= 2.0.2, < 6.0)
ast (2.4.2)
autoprefixer-rails (10.4.16.0)
execjs (~> 2)
awesome_print (1.9.2)
base64 (0.2.0)
bcrypt (3.1.20)
better_errors (2.10.1)
erubi (>= 1.0.0)
rack (>= 0.9.0) rack (>= 0.9.0)
binding_of_caller (0.7.2) rouge (>= 1.0.0)
better_html (2.0.2)
actionview (>= 6.0)
activesupport (>= 6.0)
ast (~> 2.0)
erubi (~> 1.4)
parser (>= 2.4)
smart_properties
bigdecimal (3.1.6)
binding_of_caller (1.0.0)
debug_inspector (>= 0.0.1) debug_inspector (>= 0.0.1)
bootstrap-sass (3.3.7) bootsnap (1.18.3)
msgpack (~> 1.2)
bootstrap-sass (3.4.1)
autoprefixer-rails (>= 5.2.1) autoprefixer-rails (>= 5.2.1)
sass (>= 3.3.4) sassc (>= 2.0.0)
bootstrap-sass-extras (0.0.7) bootstrap-sass-extras (0.1.0)
rails (>= 3.1.0) rails (>= 3.1.0)
bootswatch-rails (3.3.5) bootswatch-rails (3.3.5)
railties (>= 3.1) railties (>= 3.1)
builder (3.2.3) builder (3.2.4)
byebug (10.0.2) byebug (11.1.3)
capistrano (3.6.1) capybara (3.40.0)
airbrussh (>= 1.0.0)
capistrano-harrow
i18n
rake (>= 10.0.0)
sshkit (>= 1.9.0)
capistrano-bundler (1.2.0)
capistrano (~> 3.1)
sshkit (~> 1.2)
capistrano-harrow (0.5.3)
capistrano-rails (1.1.8)
capistrano (~> 3.1)
capistrano-bundler (~> 1.1)
capistrano3-puma (1.2.1)
capistrano (~> 3.0)
puma (>= 2.6)
capybara (3.10.1)
addressable addressable
matrix
mini_mime (>= 0.1.3) mini_mime (>= 0.1.3)
nokogiri (~> 1.8) nokogiri (~> 1.11)
rack (>= 1.6.0) rack (>= 1.6.0)
rack-test (>= 0.6.3) rack-test (>= 0.6.3)
regexp_parser (~> 1.2) regexp_parser (>= 1.5, < 3.0)
xpath (~> 3.2) xpath (~> 3.2)
carrierwave (0.11.2) carrierwave (3.0.5)
activemodel (>= 3.2.0) activemodel (>= 6.0.0)
activesupport (>= 3.2.0) activesupport (>= 6.0.0)
json (>= 1.7) addressable (~> 2.6)
mime-types (>= 1.16) image_processing (~> 1.1)
mimemagic (>= 0.3.0) marcel (~> 1.0.0)
childprocess (0.9.0) ssrf_filter (~> 1.0)
ffi (~> 1.0, >= 1.0.11)
choice (0.2.0) choice (0.2.0)
chronic (0.10.2) chronic (0.10.2)
chunky_png (1.3.8) chunky_png (1.4.0)
coderay (1.1.1) coderay (1.1.3)
coffee-rails (4.2.1) coffee-rails (5.0.0)
coffee-script (>= 2.2.0) coffee-script (>= 2.2.0)
railties (>= 4.0.0, < 5.2.x) railties (>= 5.2.0)
coffee-script (2.4.1) coffee-script (2.4.1)
coffee-script-source coffee-script-source
execjs execjs
coffee-script-source (1.10.0) coffee-script-source (1.12.2)
concurrent-ruby (1.1.4) concurrent-ruby (1.2.3)
connection_pool (2.4.1)
copy_carrierwave_file (1.3.0) copy_carrierwave_file (1.3.0)
carrierwave (>= 0.9) carrierwave (>= 0.9)
crass (1.0.4) crass (1.0.6)
database_cleaner (1.7.0) database_cleaner (2.0.2)
debug_inspector (0.0.2) database_cleaner-active_record (>= 2, < 3)
database_cleaner-active_record (2.1.0)
activerecord (>= 5.a)
database_cleaner-core (~> 2.0.0)
database_cleaner-core (2.0.1)
date (3.3.4)
debug_inspector (1.2.0)
delorean (2.1.0) delorean (2.1.0)
chronic chronic
devise (4.2.0) devise (4.9.3)
bcrypt (~> 3.0) bcrypt (~> 3.0)
orm_adapter (~> 0.1) orm_adapter (~> 0.1)
railties (>= 4.1.0, < 5.1) railties (>= 4.1.0)
responders responders
warden (~> 1.2.3) warden (~> 1.2.3)
devise-i18n (1.1.0) devise-i18n (1.12.0)
diff-lcs (1.3) devise (>= 4.9.0)
docile (1.1.5) diff-lcs (1.5.1)
domain_name (0.5.20160826) docile (1.4.0)
unf (>= 0.0.5, < 1.0.0) draper (4.0.2)
easy_translate (0.5.0) actionpack (>= 5.0)
json activemodel (>= 5.0)
thread activemodel-serializers-xml (>= 1.0)
thread_safe activesupport (>= 5.0)
erubis (2.7.0) request_store (>= 1.0)
execjs (2.7.0) ruby2_keywords
factory_girl (4.7.0) drb (2.2.0)
activesupport (>= 3.0.0) ruby2_keywords
factory_girl_rails (4.7.0) erubi (1.12.0)
factory_girl (~> 4.7.0) execjs (2.9.1)
railties (>= 3.0.0) factory_bot (6.4.6)
faker (1.6.6) activesupport (>= 5.0.0)
i18n (~> 0.5) factory_bot_rails (6.4.3)
faraday (0.9.2) factory_bot (~> 6.4)
multipart-post (>= 1.2, < 3) railties (>= 5.0.0)
ffi (1.10.0) faker (3.2.3)
i18n (>= 1.8.11, < 2)
faraday (2.9.0)
faraday-net_http (>= 2.0, < 3.2)
faraday-net_http (3.1.0)
net-http
ffi (1.16.3)
font-awesome-sass (4.6.2) font-awesome-sass (4.6.2)
sass (>= 3.2) sass (>= 3.2)
formatador (0.2.5) formatador (1.1.0)
globalid (0.4.1) globalid (1.2.1)
activesupport (>= 4.2.0) activesupport (>= 6.1)
globalize (5.0.1) globalize (6.3.0)
activemodel (>= 4.2.0, < 4.3) activemodel (>= 4.2, < 7.2)
activerecord (>= 4.2.0, < 4.3) activerecord (>= 4.2, < 7.2)
guard (2.14.0) request_store (~> 1.0)
guard (2.18.1)
formatador (>= 0.2.4) formatador (>= 0.2.4)
listen (>= 2.7, < 4.0) listen (>= 2.7, < 4.0)
lumberjack (~> 1.0) lumberjack (>= 1.0.12, < 2.0)
nenv (~> 0.1) nenv (~> 0.1)
notiffany (~> 0.0) notiffany (~> 0.0)
pry (>= 0.9.12) pry (>= 0.13.0)
shellany (~> 0.0) shellany (~> 0.0)
thor (>= 0.18.1) thor (>= 0.18.1)
guard-compat (1.2.1) guard-compat (1.2.1)
@ -160,241 +210,309 @@ GEM
guard (~> 2.1) guard (~> 2.1)
guard-compat (~> 1.1) guard-compat (~> 1.1)
rspec (>= 2.99.0, < 4.0) rspec (>= 2.99.0, < 4.0)
highline (1.7.8) highline (3.0.1)
http-cookie (1.0.3) i18n (1.14.1)
domain_name (~> 0.5)
i18n (0.9.5)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
i18n-tasks (0.9.5) i18n-tasks (1.0.13)
activesupport (>= 4.0.2) activesupport (>= 4.0.2)
ast (>= 2.1.0) ast (>= 2.1.0)
easy_translate (>= 0.5.0) better_html (>= 1.0, < 3.0)
erubis erubi
highline (>= 1.7.3) highline (>= 2.0.0)
i18n i18n
parser (>= 2.2.3.0) parser (>= 3.2.2.1)
term-ansicolor (>= 1.3.2) rails-i18n
rainbow (>= 2.2.2, < 4.0)
terminal-table (>= 1.5.1) terminal-table (>= 1.5.1)
jbuilder (2.6.0) icalendar (2.10.1)
activesupport (>= 3.0.0, < 5.1) ice_cube (~> 0.16)
multi_json (~> 1.2) ice_cube (0.16.4)
image_processing (1.12.2)
mini_magick (>= 4.9.5, < 5)
ruby-vips (>= 2.0.17, < 3)
io-console (0.7.2)
irb (1.11.2)
rdoc
reline (>= 0.4.2)
jbuilder (2.11.5)
actionview (>= 5.0.0)
activesupport (>= 5.0.0)
jquery-datatables-rails (3.4.0) jquery-datatables-rails (3.4.0)
actionpack (>= 3.1) actionpack (>= 3.1)
jquery-rails jquery-rails
railties (>= 3.1) railties (>= 3.1)
sass-rails sass-rails
jquery-rails (4.2.1) jquery-rails (4.6.0)
rails-dom-testing (>= 1, < 3) rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0) railties (>= 4.2.0)
thor (>= 0.14, < 2.0) thor (>= 0.14, < 2.0)
json (2.1.0) json (2.7.1)
libv8 (6.7.288.46.1) language_server-protocol (3.17.0.3)
listen (3.1.5) lint_roller (1.1.0)
rb-fsevent (~> 0.9, >= 0.9.4) listen (3.8.0)
rb-inotify (~> 0.9, >= 0.9.7) rb-fsevent (~> 0.10, >= 0.10.3)
ruby_dep (~> 1.2) rb-inotify (~> 0.9, >= 0.9.10)
loofah (2.2.3) loofah (2.22.0)
crass (~> 1.0.2) crass (~> 1.0.2)
nokogiri (>= 1.5.9) nokogiri (>= 1.12.0)
lumberjack (1.0.10) lumberjack (1.2.10)
mail (2.7.1) mail (2.8.1)
mini_mime (>= 0.1.1) mini_mime (>= 0.1.1)
method_source (0.8.2) net-imap
mime-types (2.99.3) net-pop
mimemagic (0.3.2) net-smtp
mini_magick (4.5.1) marcel (1.0.2)
mini_mime (1.0.1) matrix (0.4.2)
mini_portile2 (2.4.0) method_source (1.0.0)
mini_racer (0.2.4) mini_magick (4.12.0)
libv8 (>= 6.3) mini_mime (1.1.5)
minitest (5.11.3) mini_portile2 (2.8.5)
morrisjs-rails (0.5.1) minitest (5.22.2)
railties (> 3.1, < 5) msgpack (1.7.2)
multi_json (1.12.1) mutex_m (0.2.0)
multipart-post (2.0.0)
nenv (0.3.0) nenv (0.3.0)
nested_form (0.3.2) nested_form (0.3.2)
net-scp (1.2.1) net-http (0.4.1)
net-ssh (>= 2.6.5) uri
net-ssh (3.2.0) net-imap (0.4.10)
netrc (0.11.0) date
nokogiri (1.10.0) net-protocol
mini_portile2 (~> 2.4.0) net-pop (0.1.2)
notiffany (0.1.1) net-protocol
net-protocol (0.2.2)
timeout
net-smtp (0.4.0.1)
net-protocol
nio4r (2.7.0)
nokogiri (1.16.2)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
notiffany (0.1.3)
nenv (~> 0.1) nenv (~> 0.1)
shellany (~> 0.0) shellany (~> 0.0)
orm_adapter (0.5.0) orm_adapter (0.5.0)
parser (2.3.1.4) parallel (1.24.0)
ast (~> 2.2) parser (3.3.0.5)
pg (0.19.0) ast (~> 2.4.1)
phony (2.15.32) racc
phony_rails (0.14.4) pg (1.5.5)
phony (2.20.12)
phony_rails (0.15.0)
activesupport (>= 3.0) activesupport (>= 3.0)
phony (~> 2.15) phony (>= 2.18.12)
pry (0.10.4) pry (0.14.2)
coderay (~> 1.1.0) coderay (~> 1.1)
method_source (~> 0.8.1) method_source (~> 1.0)
slop (~> 3.4) pry-rails (0.3.9)
pry-rails (0.3.4) pry (>= 0.10.4)
pry (>= 0.9.10) psych (5.1.2)
public_suffix (3.0.3) stringio
puma (3.10.0) public_suffix (5.0.4)
quiet_assets (1.1.0) puma (6.4.2)
railties (>= 3.1, < 5.0) nio4r (~> 2.0)
rack (1.6.11) racc (1.7.3)
rack-protection (1.5.5) rack (3.0.9)
rack rack-session (2.0.0)
rack-test (0.6.3) rack (>= 3.0.0)
rack (>= 1.0) rack-test (2.1.0)
rails (4.2.11) rack (>= 1.3)
actionmailer (= 4.2.11) rackup (2.1.0)
actionpack (= 4.2.11) rack (>= 3)
actionview (= 4.2.11) webrick (~> 1.8)
activejob (= 4.2.11) rails (7.1.3)
activemodel (= 4.2.11) actioncable (= 7.1.3)
activerecord (= 4.2.11) actionmailbox (= 7.1.3)
activesupport (= 4.2.11) actionmailer (= 7.1.3)
bundler (>= 1.3.0, < 2.0) actionpack (= 7.1.3)
railties (= 4.2.11) actiontext (= 7.1.3)
sprockets-rails actionview (= 7.1.3)
rails-deprecated_sanitizer (1.0.3) activejob (= 7.1.3)
activesupport (>= 4.2.0.alpha) activemodel (= 7.1.3)
rails-dom-testing (1.0.9) activerecord (= 7.1.3)
activesupport (>= 4.2.0, < 5.0) activestorage (= 7.1.3)
nokogiri (~> 1.6) activesupport (= 7.1.3)
rails-deprecated_sanitizer (>= 1.0.1) bundler (>= 1.15.0)
rails-erd (1.5.0) railties (= 7.1.3)
activerecord (>= 3.2) rails-dom-testing (2.2.0)
activesupport (>= 3.2) activesupport (>= 5.0.0)
minitest
nokogiri (>= 1.6)
rails-erd (1.7.2)
activerecord (>= 4.2)
activesupport (>= 4.2)
choice (~> 0.2.0) choice (~> 0.2.0)
ruby-graphviz (~> 1.2) ruby-graphviz (~> 1.2)
rails-html-sanitizer (1.0.4) rails-html-sanitizer (1.6.0)
loofah (~> 2.2, >= 2.2.2) loofah (~> 2.21)
rails-i18n (4.0.9) nokogiri (~> 1.14)
i18n (~> 0.7) rails-i18n (7.0.8)
railties (~> 4.0) i18n (>= 0.7, < 2)
railties (4.2.11) railties (>= 6.0.0, < 8)
actionpack (= 4.2.11) railties (7.1.3)
activesupport (= 4.2.11) actionpack (= 7.1.3)
rake (>= 0.8.7) activesupport (= 7.1.3)
thor (>= 0.18.1, < 2.0) irb
rake (12.3.2) rackup (>= 1.0.0)
rake (>= 12.2)
thor (~> 1.0, >= 1.2.2)
zeitwerk (~> 2.6)
rainbow (3.1.1)
rake (13.1.0)
raphael-rails (2.1.2) raphael-rails (2.1.2)
rb-fsevent (0.9.7) rb-fsevent (0.11.2)
rb-inotify (0.9.7) rb-inotify (0.10.1)
ffi (>= 0.5.0) ffi (~> 1.0)
refile (0.6.2) rdoc (6.6.2)
mime-types psych (>= 4.0.0)
rest-client (~> 1.8) regexp_parser (2.9.0)
sinatra (~> 1.4.5) reline (0.4.2)
refile-mini_magick (0.2.0) io-console (~> 0.5)
mini_magick (~> 4.0) request_store (1.6.0)
refile (~> 0.5) rack (>= 1.4)
regexp_parser (1.2.0) responders (3.1.1)
responders (2.3.0) actionpack (>= 5.2)
railties (>= 4.2.0, < 5.1) railties (>= 5.2)
rest-client (1.8.0) rexml (3.2.6)
http-cookie (>= 1.0.2, < 2.0) rouge (4.2.0)
mime-types (>= 1.16, < 3.0) rqrcode (2.2.0)
netrc (~> 0.7)
rqrcode (0.10.1)
chunky_png (~> 1.0) chunky_png (~> 1.0)
rspec (3.5.0) rqrcode_core (~> 1.0)
rspec-core (~> 3.5.0) rqrcode_core (1.2.0)
rspec-expectations (~> 3.5.0) rspec (3.13.0)
rspec-mocks (~> 3.5.0) rspec-core (~> 3.13.0)
rspec-core (3.5.4) rspec-expectations (~> 3.13.0)
rspec-support (~> 3.5.0) rspec-mocks (~> 3.13.0)
rspec-expectations (3.5.0) rspec-core (3.13.0)
rspec-support (~> 3.13.0)
rspec-expectations (3.13.0)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.5.0) rspec-support (~> 3.13.0)
rspec-mocks (3.5.0) rspec-mocks (3.13.0)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.5.0) rspec-support (~> 3.13.0)
rspec-rails (3.5.2) rspec-rails (6.1.1)
actionpack (>= 3.0) actionpack (>= 6.1)
activesupport (>= 3.0) activesupport (>= 6.1)
railties (>= 3.0) railties (>= 6.1)
rspec-core (~> 3.5.0) rspec-core (~> 3.12)
rspec-expectations (~> 3.5.0) rspec-expectations (~> 3.12)
rspec-mocks (~> 3.5.0) rspec-mocks (~> 3.12)
rspec-support (~> 3.5.0) rspec-support (~> 3.12)
rspec-support (3.5.0) rspec-support (3.13.0)
ruby-graphviz (1.2.2) rubocop (1.60.2)
ruby_dep (1.5.0) json (~> 2.3)
rubyzip (1.2.2) language_server-protocol (>= 3.17.0)
sass (3.4.22) parallel (~> 1.10)
sass-rails (5.0.6) parser (>= 3.3.0.2)
railties (>= 4.0.0, < 6) rainbow (>= 2.2.2, < 4.0)
sass (~> 3.1) regexp_parser (>= 1.8, < 3.0)
sprockets (>= 2.8, < 4.0) rexml (>= 3.2.5, < 4.0)
sprockets-rails (>= 2.0, < 4.0) rubocop-ast (>= 1.30.0, < 2.0)
tilt (>= 1.1, < 3) ruby-progressbar (~> 1.7)
search_object (1.1.1) unicode-display_width (>= 2.4.0, < 3.0)
selenium-webdriver (3.141.0) rubocop-ast (1.30.0)
childprocess (~> 0.5) parser (>= 3.2.1.0)
rubyzip (~> 1.2, >= 1.2.2) rubocop-performance (1.20.2)
rubocop (>= 1.48.1, < 2.0)
rubocop-ast (>= 1.30.0, < 2.0)
ruby-graphviz (1.2.5)
rexml
ruby-progressbar (1.13.0)
ruby-vips (2.2.0)
ffi (~> 1.12)
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
sass (3.7.4)
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 (6.0.0)
sassc-rails (~> 2.1, >= 2.1.1)
sassc (2.4.0)
ffi (~> 1.9)
sassc-rails (2.1.2)
railties (>= 4.0.0)
sassc (>= 2.0)
sprockets (> 3.0)
sprockets-rails
tilt
search_object (1.2.5)
selenium-webdriver (4.18.1)
base64 (~> 0.2)
rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2, < 3.0)
websocket (~> 1.0)
shellany (0.0.1) shellany (0.0.1)
simple_form (3.3.1) simple_form (5.3.0)
actionpack (> 4, < 5.1) actionpack (>= 5.2)
activemodel (> 4, < 5.1) activemodel (>= 5.2)
simplecov (0.12.0) simplecov (0.22.0)
docile (~> 1.1.0) docile (~> 1.1)
json (>= 1.8, < 3) simplecov-html (~> 0.11)
simplecov-html (~> 0.10.0) simplecov_json_formatter (~> 0.1)
simplecov-html (0.10.0) simplecov-html (0.12.3)
sinatra (1.4.7) simplecov_json_formatter (0.1.4)
rack (~> 1.5) slim (5.2.1)
rack-protection (~> 1.4) temple (~> 0.10.0)
tilt (>= 1.3, < 3) tilt (>= 2.1.0)
slim (3.0.7) slim-rails (3.6.3)
temple (~> 0.7.6)
tilt (>= 1.3.3, < 2.1)
slim-rails (3.1.1)
actionpack (>= 3.1) actionpack (>= 3.1)
railties (>= 3.1) railties (>= 3.1)
slim (~> 3.0) slim (>= 3.0, < 6.0, != 5.0.0)
slop (3.6.0) smart_properties (1.17.0)
spring (2.0.0) spring (4.1.3)
activesupport (>= 4.2)
spring-commands-rspec (1.0.4) spring-commands-rspec (1.0.4)
spring (>= 0.9.1) spring (>= 0.9.1)
sprockets (3.7.2) sprockets (4.2.1)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
rack (> 1, < 3) rack (>= 2.2.4, < 4)
sprockets-rails (3.2.1) sprockets-rails (3.4.2)
actionpack (>= 4.0) actionpack (>= 5.2)
activesupport (>= 4.0) activesupport (>= 5.2)
sprockets (>= 3.0.0) sprockets (>= 3.0.0)
sqlite3 (1.3.12) sqlite3 (1.7.2)
sshkit (1.11.3) mini_portile2 (~> 2.8.0)
net-scp (>= 1.1.2) ssrf_filter (1.1.2)
net-ssh (>= 2.8.0) standard (1.34.0)
temple (0.7.7) language_server-protocol (~> 3.17.0.2)
term-ansicolor (1.4.0) lint_roller (~> 1.0)
tins (~> 1.0) rubocop (~> 1.60)
terminal-table (1.7.3) standard-custom (~> 1.0.0)
unicode-display_width (~> 1.1.1) standard-performance (~> 1.3)
thor (0.20.3) standard-custom (1.0.2)
thread (0.2.2) lint_roller (~> 1.0)
thread_safe (0.3.6) rubocop (~> 1.50)
tilt (2.0.5) standard-performance (1.3.1)
tins (1.12.0) lint_roller (~> 1.1)
tzinfo (1.2.5) rubocop-performance (~> 1.20.2)
thread_safe (~> 0.1) stringio (3.1.0)
uglifier (3.0.2) temple (0.10.3)
terminal-table (3.0.2)
unicode-display_width (>= 1.1.1, < 3)
thor (1.3.0)
thruster (0.1.8)
tilt (2.3.0)
timeout (0.4.1)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
uglifier (4.2.0)
execjs (>= 0.3.0, < 3) execjs (>= 0.3.0, < 3)
unf (0.1.4) unicode-display_width (2.5.0)
unf_ext uri (0.13.0)
unf_ext (0.0.7.2) warden (1.2.9)
unicode-display_width (1.1.1) rack (>= 2.0.9)
warden (1.2.6) webrick (1.8.1)
rack (>= 1.0) websocket (1.2.10)
websocket-driver (0.7.6)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
xpath (3.2.0) xpath (3.2.0)
nokogiri (~> 1.8) nokogiri (~> 1.8)
yaml_db (0.4.2) yaml_db (0.7.0)
rails (>= 3.0, < 5.1) rails (>= 3.0)
rake (>= 0.8.7) rake (>= 0.8.7)
zeitwerk (2.6.13)
PLATFORMS PLATFORMS
ruby ruby
@ -404,46 +522,40 @@ DEPENDENCIES
awesome_print awesome_print
better_errors better_errors
binding_of_caller binding_of_caller
bootsnap
bootstrap-sass bootstrap-sass
bootstrap-sass-extras bootstrap-sass-extras
bootswatch-rails bootswatch-rails
byebug byebug
capistrano
capistrano-rails
capistrano3-puma
capybara capybara
carrierwave
coffee-rails coffee-rails
copy_carrierwave_file copy_carrierwave_file
database_cleaner database_cleaner
delorean delorean
devise devise
devise-i18n devise-i18n
factory_girl_rails draper
factory_bot_rails
faker faker
faraday faraday
font-awesome-sass font-awesome-sass (~> 4.6.2)
globalize globalize
guard-rspec guard-rspec
i18n-tasks i18n-tasks
icalendar
jbuilder jbuilder
jquery-datatables-rails jquery-datatables-rails
jquery-rails jquery-rails
mini_magick
mini_racer
morrisjs-rails
nested_form nested_form
pg pg
phony
phony_rails phony_rails
pry-rails pry-rails
puma puma
quiet_assets rails (~> 7.1.0)
rails (~> 4.2.11)
rails-erd rails-erd
rails-i18n rails-i18n
raphael-rails raphael-rails
refile
refile-mini_magick
rqrcode rqrcode
rspec-rails rspec-rails
sass-rails sass-rails
@ -454,9 +566,12 @@ DEPENDENCIES
slim-rails slim-rails
spring spring
spring-commands-rspec spring-commands-rspec
sprockets
sqlite3 sqlite3
standard
thruster (~> 0.1.8)
uglifier uglifier
yaml_db yaml_db
BUNDLED WITH BUNDLED WITH
1.17.1 2.5.6

View File

@ -1,18 +1,20 @@
# More info at https://github.com/guard/guard#readme # More info at https://github.com/guard/guard#readme
guard :rspec, cmd: 'spring rspec' do guard :rspec, cmd: "spring rspec" do
watch(%r{^spec/.+_spec\.rb$}) watch(%r{^spec/.+_spec\.rb$})
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" } watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
watch('spec/spec_helper.rb') { 'spec' } watch("spec/spec_helper.rb") { "spec" }
watch('spec/rails_helper.rb') { 'spec' } watch("spec/rails_helper.rb") { "spec" }
watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
watch(%r{^app/(.*)(\.erb|\.haml|\.slim)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" } watch(%r{^app/(.*)(\.erb|\.haml|\.slim)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] } watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
watch(%r{^spec/support/(.+)\.rb$}) { 'spec' } watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
watch('config/routes.rb') { 'spec/routing' } watch("config/routes.rb") { "spec/routing" }
watch('app/controllers/application_controller.rb') { 'spec/controllers' } watch("app/controllers/application_controller.rb") { "spec/controllers" }
# Capybara features specs # Capybara features specs
watch(%r{^app/views/(.+)/.*\.(erb|haml|slim)$}) { |m| "spec/features/#{m[1]}_spec.rb" } watch(%r{^app/views/(.+)/.*\.(erb|haml|slim)$}) { |m| "spec/features/#{m[1]}_spec.rb" }
notification :off
end end

View File

@ -1,11 +1,31 @@
Clarion # Clarion
=======
A CfP automation system for OpenFest. A CfP automation system for OpenFest.
Installation ## Installation
------------
1. `git clone https://github.com/ignisf/clarion.git` ### For local development
2. Run `bundle install; bin/rake bootstrap`
3. You can now run the rails server with `bin/rails s` 1. `git clone https://git.openfest.org/Site/clarion/`
2. Run `rvm install "ruby-$(cat .ruby-version)"; rvm install "ruby-$(cat .ruby-version)"`
3. Start up postgresql
4. Run `bundle install; bin/rake bootstrap`
5. You can now run the rails server with `bin/rails s`
### For production
`docker build -t clarion:latest -f Dockerfile .`
Note that the docker image contains a default user (for credentials see `db/seeds.rb`).
### docker-compose
`docker-compose up` will bring everything up on `http://127.0.0.1:3000` with production configuration.
## Initial Usage
1. Go to `http://127.0.0.1:3000/management/`
2. Login (for initial creds see `db/seeds.rb`)
3. Change the credentials
4. Create a conference.
- NB: When creating a conference make sure to use the same `domain` as the one you are currently using, otherwise nothing will be shown.

View File

@ -1,6 +1,6 @@
# Add your own tasks in files placed in lib/tasks ending in .rake, # 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. # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
require File.expand_path('../config/application', __FILE__) require File.expand_path("../config/application", __FILE__)
Rails.application.load_tasks Rails.application.load_tasks

15
TODO
View File

@ -1,14 +1,14 @@
- User-facing: - User-facing:
- Event proposal: lecture, workshop, open space (CRUD) - # Event proposal: lecture, workshop, open space (CRUD)
- Volunteership - # Volunteership
- Sponsorship - Sponsorship
- Admin: - Admin:
- # Create a conference, halls, tracks - # Create a conference, halls, tracks
- # Starting a CFP - # Starting a CFP
- # conferences#show -> admin dashboard - # conferences#show -> admin dashboard
- volunteers#index -> approved volunteers - # volunteers#index -> approved volunteers
- sponsorships#index -> generate token, links to send around - # sponsorships#index -> generate token, links to send around
- scheduling -> calendar with events - scheduling -> calendar with events
- # proposals#index -> undecided events, grouped by user - # proposals#index -> undecided events, grouped by user
@ -17,12 +17,11 @@
- users: - users:
- # edit profile: image upload and stuff - # edit profile: image upload and stuff
- # show profile - # show profile
- Edit profiles instead - # Edit profiles instead
- Home-area user CRUD - # Home-area user CRUD
- # Controller before_action that checks for current_conference - # Controller before_action that checks for current_conference
- Devise view styling - # Devise view styling
Notes: Notes:
- .row > .col-lg-12 -> put that outside of the "yield"? - .row > .col-lg-12 -> put that outside of the "yield"?
- human_attribute_name -> create a short alias?

View File

@ -0,0 +1,6 @@
//= link_tree ../images
//= link_tree ../../../lib/initfest/assets/images
//= link_directory ../javascripts .js
//= link_directory ../stylesheets .css
//= link initfest/application.css
//= link initfest/application.js

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
height="665"
width="1185"
id="svg4"
version="1.1"
viewBox="0 0 1185 665"
role="img"
class="svg-inline--fa fa-user-secret fa-w-14"
data-icon="user-secret"
data-prefix="fas"
focusable="false"
aria-hidden="true">
<metadata
id="metadata10">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8" />
<path
style="fill:currentColor"
id="path2"
d="m 752.4,384.79271 23.9,-62.6 c 4,-10.5 -3.7,-21.7 -15,-21.7 h -58.5 c 11,-18.9 17.8,-40.6 17.8,-64 v -0.3 c 39.2,-7.8 64,-19.1 64,-31.7 0,-13.3 -27.3,-25.1 -70.1,-33 -9.2,-32.8 -27,-65.8 -40.6,-82.799998 -9.5,-11.9 -25.9,-15.6 -39.5,-8.8 l -27.6,13.8 c -9,4.5 -19.6,4.5 -28.6,0 l -27.6,-13.8 c -13.6,-6.8 -30,-3.1 -39.5,8.8 -13.5,16.999998 -31.4,49.999998 -40.6,82.799998 -42.7,7.9 -70,19.7 -70,33 0,12.6 24.8,23.9 64,31.7 v 0.3 c 0,23.4 6.8,45.1 17.8,64 h -57.5 c -11.5,0 -19.2,11.7 -14.7,22.3 l 25.8,60.2 c -40.1,23.3 -67.4,66.2 -67.4,115.9 v 44.8 c 0,24.7 20.1,44.8 44.8,44.8 h 358.4 c 24.7,0 44.8,-20.1 44.8,-44.8 v -44.8 c 0,-48.4 -25.8,-90.4 -64.1,-114.1 z m -207.9,171.7 -41.6,-192 49.6,32 24,40 z m 96,0 -32,-120 24,-40 49.6,-32 z m 41.7,-298.5 c -3.9,11.9 -7,24.6 -16.5,33.4 -10.1,9.3 -48,22.4 -64,-25 -2.8,-8.4 -15.4,-8.4 -18.3,0 -17,50.2 -56,32.4 -64,25 -9.5,-8.8 -12.7,-21.5 -16.5,-33.4 -0.8,-2.5 -6.3,-5.7 -6.3,-5.8 v -10.8 c 28.3,3.6 61,5.8 96,5.8 35,0 67.7,-2.1 96,-5.8 v 10.8 c -0.1,0.1 -5.6,3.2 -6.4,5.8 z" />
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -3,6 +3,5 @@
//= require jquery_nested_form //= require jquery_nested_form
//= require bootstrap-sprockets //= require bootstrap-sprockets
//= require raphael //= require raphael
//= require morris
//= require chroma-js/chroma //= require chroma-js/chroma
//= require_directory . //= require_directory .

View File

@ -22,6 +22,10 @@ th.action, td.action {
} }
} }
th.main {
width: 100%;
}
.conference-title { .conference-title {
display: inline-block; display: inline-block;
@include button-size($padding-base-vertical, $padding-base-horizontal, $font-size-base, $line-height-base, $btn-border-radius-base); @include button-size($padding-base-vertical, $padding-base-horizontal, $font-size-base, $line-height-base, $btn-border-radius-base);

View File

@ -1,24 +1,10 @@
.speaker-profile { .panel .panel-image img {
@extend .col-sm-offset-2; display: block;
@extend .col-sm-8; max-width: 100%;
max-height: 250px;
.center { height: auto;
text-align: center; margin-right: auto;
} margin-left: auto;
width: 100%;
.profile-image { object-fit: cover;
@extend .img-thumbnail;
max-width: 171px;
max-height: 180px;
}
.social {
@extend .btn-group;
margin-top: 10px;
a {
@extend .btn;
@extend .btn-default;
}
}
} }

View File

@ -6,7 +6,7 @@
@import "bootstrap"; @import "bootstrap";
@import "bootswatch/simplex/bootswatch"; @import "bootswatch/simplex/bootswatch";
@import "morris"; /* @import "morris"; */
@import "users"; @import "users";
@import "colors"; @import "colors";

View File

@ -0,0 +1,8 @@
class Api::ConferencesController < Api::ApplicationController
include ::PublicApiExposing
def index
@conferences = Conference.all
fresh_when @conferences
end
end

View File

@ -1,9 +1,10 @@
class Api::EventTypesController < Api::ApplicationController class Api::EventTypesController < Api::ApplicationController
include ::CurrentConferenceAssigning include ::CurrentConferenceAssigning
include ::PublicApiExposing include ::PublicApiExposing
before_filter :require_current_conference! before_action :require_current_conference!
def index def index
@event_types = current_conference.event_types.includes(:translations) @event_types = current_conference.event_types.includes(:translations)
fresh_when @event_types
end end
end end

View File

@ -1,14 +1,13 @@
class Api::EventsController < Api::ApplicationController class Api::EventsController < Api::ApplicationController
include ::CurrentConferenceAssigning include ::CurrentConferenceAssigning
include ::PublicApiExposing include ::PublicApiExposing
before_filter :require_current_conference! before_action :require_current_conference!
def index def index
@events = current_conference.events.approved.joins(:proposition).includes(:participations) @events = current_conference.events.approved.joins(:proposition).includes(:participations)
end end
def halfnarp_friendly def halfnarp_friendly
@events = current_conference.events.includes(:track, :event_type) @events = current_conference.events.joins(:proposition).includes(:track, :event_type).where.not(propositions: {status: :rejected})
render json: @events, include: [:track, :event_type]
end end
end end

View File

@ -1,9 +1,10 @@
class Api::HallsController < Api::ApplicationController class Api::HallsController < Api::ApplicationController
include ::CurrentConferenceAssigning include ::CurrentConferenceAssigning
include ::PublicApiExposing include ::PublicApiExposing
before_filter :require_current_conference! before_action :require_current_conference!
def index def index
@halls = current_conference.halls @halls = current_conference.halls
fresh_when @halls
end end
end end

View File

@ -0,0 +1,9 @@
class Api::SchedulesController < Api::ApplicationController
include ::CurrentConferenceAssigning
include ::PublicApiExposing
before_action :require_current_conference!
def show
@halls = Conference.last.halls.includes(:translations, slots: {approved_event: [:participants_with_personal_profiles, :proposition]})
end
end

View File

@ -1,9 +1,11 @@
class Api::SlotsController < Api::ApplicationController class Api::SlotsController < Api::ApplicationController
include ::CurrentConferenceAssigning include ::CurrentConferenceAssigning
include ::PublicApiExposing include ::PublicApiExposing
before_filter :require_current_conference! before_action :require_current_conference!
def index def index
@slots = current_conference.slots @slots = current_conference.slots
fresh_when @slots
end end
end end

View File

@ -1,9 +1,10 @@
class Api::SpeakersController < Api::ApplicationController class Api::SpeakersController < Api::ApplicationController
include ::CurrentConferenceAssigning include ::CurrentConferenceAssigning
include ::PublicApiExposing include ::PublicApiExposing
before_filter :require_current_conference! before_action :require_current_conference!
def index def index
@speakers = PersonalProfile.joins(user: {participations: {event: :proposition}}).where(events: {id: current_conference.approved_events.pluck(:id)}, conference: current_conference).distinct @speakers = PersonalProfile.joins(user: {participations: {event: :proposition}}).where(events: {id: current_conference.approved_events.pluck(:id)}, conference: current_conference).distinct
fresh_when @speakers
end end
end end

View File

@ -1,9 +1,10 @@
class Api::TracksController < Api::ApplicationController class Api::TracksController < Api::ApplicationController
include ::CurrentConferenceAssigning include ::CurrentConferenceAssigning
include ::PublicApiExposing include ::PublicApiExposing
before_filter :require_current_conference! before_action :require_current_conference!
def index def index
@tracks = current_conference.tracks.includes(:translations) @tracks = current_conference.tracks.includes(:translations)
fresh_when @tracks
end end
end end

View File

@ -4,15 +4,15 @@ class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception. # Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead. # For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception protect_from_forgery with: :exception
before_filter :configure_permitted_parameters, if: :devise_controller? before_action :configure_permitted_parameters, if: :devise_controller?
before_action :set_locale before_action :set_locale
before_action :set_view_paths before_action :set_view_paths
# TODO: make this get the domain from the database # TODO: make this get the domain from the database
#layout Proc.new { |controller| controller.request.host } # layout Proc.new { |controller| controller.request.host }
layout 'public/application' layout "public/application"
def self.default_url_options(options={}) def self.default_url_options(options = {})
if I18n.locale != I18n.default_locale if I18n.locale != I18n.default_locale
options.merge({locale: I18n.locale}) options.merge({locale: I18n.locale})
else else
@ -24,17 +24,17 @@ class ApplicationController < ActionController::Base
def set_locale def set_locale
I18n.locale = params[:locale] || I18n.default_locale I18n.locale = params[:locale] || I18n.default_locale
if user_signed_in? and current_user.language != I18n.locale if user_signed_in? && (current_user.language != I18n.locale)
current_user.update(language: I18n.locale) current_user.update(language: I18n.locale)
end end
end end
def set_view_paths def set_view_paths
# TODO: make this get the domain from the database # TODO: make this get the domain from the database
prepend_view_path 'lib/initfest/views' if request.host =~ /openfest/ prepend_view_path "lib/initfest/views" if request.host =~ /openfest/
prepend_view_path 'lib/initfest/views' if request.host =~ /example/ prepend_view_path "lib/initfest/views" if request.host =~ /example/
prepend_view_path 'lib/initfest/views' if request.host =~ /127\.0\.0/ prepend_view_path "lib/initfest/views" if request.host =~ /^127\.0\.0/
prepend_view_path "lib/initfest/views" if request.host =~ /^localhost$/
end end
protected protected

View File

@ -11,8 +11,8 @@ module CurrentConferenceAssigning
end end
def current_conference def current_conference
if not @current_conference unless @current_conference
if @conference and not @conference.new_record? if @conference && !@conference.new_record?
@current_conference = @conference @current_conference = @conference
elsif params[:conference_id].present? elsif params[:conference_id].present?
@current_conference = Conference.find(params[:conference_id]) @current_conference = Conference.find(params[:conference_id])
@ -23,8 +23,8 @@ module CurrentConferenceAssigning
end end
def require_current_conference! def require_current_conference!
if not current_conference? unless current_conference?
raise ActionController::RoutingError.new('Not Found') raise ActionController::RoutingError.new("Not Found")
end end
end end
end end

View File

@ -1,11 +1,11 @@
require 'active_support/concern' require "active_support/concern"
module PublicApiExposing module PublicApiExposing
extend ActiveSupport::Concern extend ActiveSupport::Concern
def set_access_control_headers def set_access_control_headers
if request.format.json? if request.format.json?
response.headers['Access-Control-Allow-Origin'] = '*' response.headers["Access-Control-Allow-Origin"] = "*"
end end
end end

View File

@ -6,9 +6,9 @@ class ConfirmationsController < Devise::ConfirmationsController
if resource.errors.empty? if resource.errors.empty?
set_flash_message(:notice, :confirmed) if is_flashing_format? set_flash_message(:notice, :confirmed) if is_flashing_format?
sign_in(resource_name, resource) sign_in(resource_name, resource)
respond_with_navigational(resource){ redirect_to after_confirmation_path_for(resource_name, resource) } respond_with_navigational(resource) { redirect_to after_confirmation_path_for(resource_name, resource) }
else else
respond_with_navigational(resource.errors, status: :unprocessable_entity){ render :new } respond_with_navigational(resource.errors, status: :unprocessable_entity) { render :new }
end end
end end
end end

View File

@ -2,12 +2,12 @@ module Management
class CallForParticipationsController < ManagementController class CallForParticipationsController < ManagementController
def create def create
current_conference.call_for_participation.open! current_conference.call_for_participation.open!
redirect_to :back redirect_back fallback_location: [:management, current_conference]
end end
def destroy def destroy
current_conference.call_for_participation.close! current_conference.call_for_participation.close!
redirect_to :back redirect_back fallback_location: [:management, current_conference]
end end
end end
end end

View File

@ -2,10 +2,10 @@ module Management
class ConferencesController < ManagementController class ConferencesController < ManagementController
def new def new
@conference = Conference.new @conference = Conference.new
@conference.event_types.build(name: 'Event type 1') @conference.event_types.build(name: "Event type 1")
@conference.tracks.build(name: 'Track 1') @conference.tracks.build(name: "Track 1")
@conference.halls.build(name: 'Hall 1') @conference.halls.build(name: "Hall 1")
@conference.volunteer_teams.build(name: 'Volunteer Team 1') @conference.volunteer_teams.build(name: "Volunteer Team 1")
end end
def create def create
@ -49,13 +49,13 @@ module Management
begin begin
if @conference.update_vote_data! if @conference.update_vote_data!
flash[:notice] = t('.vote_data_successfully_updated') flash[:notice] = t(".vote_data_successfully_updated")
else else
flash[:alert] = t('.error_during_vote_data_save') flash[:alert] = t(".error_during_vote_data_save")
end end
redirect_to :back redirect_back fallback_location: [:management, @conference]
rescue StandardError => e rescue => e
flash[:alert] = t('.error_during_connection_with_voting_endpoint', error: e.message) flash[:alert] = t(".error_during_connection_with_voting_endpoint", error: e.message)
render :vote_results render :vote_results
end end
end end
@ -75,9 +75,9 @@ module Management
:title, :email, :start_date, :end_date, :description, :host_name, :title, :email, :start_date, :end_date, :description, :host_name,
:planned_cfp_end_date, :vote_data_endpoint, :planned_cfp_end_date, :vote_data_endpoint,
event_types_attributes: [:id, :name, :description, :maximum_length, event_types_attributes: [:id, :name, :description, :maximum_length,
:minimum_length, :_destroy], :minimum_length, :_destroy,],
tracks_attributes: [:id, :name, :color, :css_class, :description, tracks_attributes: [:id, :name, :color, :css_class, :description,
:css_style, :foreground_color, :_destroy], :css_style, :foreground_color, :_destroy,],
halls_attributes: [:id, :name, :_destroy], halls_attributes: [:id, :name, :_destroy],
volunteer_teams_attributes: [:id, :name, :description, :color, :_destroy] volunteer_teams_attributes: [:id, :name, :description, :color, :_destroy]
) )

View File

@ -2,8 +2,15 @@ module Management
class EventsController < ManagementController class EventsController < ManagementController
def index def index
@conference = find_conference @conference = find_conference
@filters = params[:filters] || {} @filters = filter_params || {}
@events = EventSearch.new(scope: Event.where(conference: @conference).eager_load(:participants_with_personal_profiles, :proposition, :proposer, :track, :event_type).preload(:conference), filters: params[:filters]).results
@events = EventSearch
.new(scope: Event.where(conference: @conference)
.eager_load(:participants_with_personal_profiles,
:proposition, :proposer, {track: [:translations]},
{event_type: [:translations]}, :feedbacks)
.preload(:conference), filters: params[:filters]).results
# @events = @conference.events.order(:title).includes(:proposition, :proposer, :track, :event_type) # @events = @conference.events.order(:title).includes(:proposition, :proposer, :track, :event_type)
end end
@ -22,10 +29,10 @@ module Management
@event = @conference.events.find(params[:id]) @event = @conference.events.find(params[:id])
if @event.update(event_params) if @event.update(event_params)
flash[:notice] = 'Event was successfully updated.' flash[:notice] = t(".event_successfully_updated")
redirect_to [:management, @conference, @event] redirect_to [:management, @conference, @event]
else else
render action: 'edit' render action: "edit"
end end
end end
@ -46,27 +53,38 @@ module Management
private private
def find_conference def find_conference
Conference.find(params[:conference_id]) Conference.eager_load({tracks: :translations}, {event_types: :translations}).find(params[:conference_id])
end
def filter_params
params.fetch(:filters, {}).permit(
:event_type_id,
:track_id,
:language,
:status,
:confirmed,
:not_confirmed,
)
end end
def event_params def event_params
params.require(:event).permit( params.require(:event).permit(
:title, :title,
:subtitle, :subtitle,
:length, :length,
:language, :language,
:abstract, :abstract,
:description, :description,
:notes, :notes,
:track_id, :track_id,
:event_type_id, :event_type_id,
participations_attributes: [ participations_attributes: [
:id, :id,
:participant_id, :participant_id,
:approved, :approved,
:_destroy :_destroy,
] ]
) )
end end
end end
end end

View File

@ -0,0 +1,13 @@
module Management
class FeedbackController < ManagementController
def index
@conference = find_conference
end
private
def find_conference
Conference.find(params[:conference_id])
end
end
end

View File

@ -1,14 +1,18 @@
require 'csv' require "csv"
module Management module Management
class ManagementController < ::ApplicationController class ManagementController < ::ApplicationController
before_action :authenticate_user!, :authorize_user! before_action :authenticate_user!, :authorize_user!
layout 'management' layout "management"
private private
def authorize_user! def authorize_user!
head :forbidden unless current_user.admin? if params[:conference_id] && params[:conference_id].to_i < Conference.last.id
head :forbidden unless current_user.admin? && current_user.owner?
else
head :forbidden unless current_user.admin?
end
end end
end end
end end

View File

@ -10,7 +10,7 @@ module Management
@user = find_profile.user @user = find_profile.user
@user.toggle_admin! @user.toggle_admin!
redirect_to :back redirect_back fallback_location: {action: :show}
end end
def show def show
@ -18,7 +18,7 @@ module Management
@profile = find_profile @profile = find_profile
@user = @profile.user @user = @profile.user
if not @profile unless @profile
flash[:error] = "No profile, needs to be created" flash[:error] = "No profile, needs to be created"
redirect_to action: :edit redirect_to action: :edit
end end
@ -36,7 +36,7 @@ module Management
@profile = @user.build_personal_profile(@conference, profile_params) @profile = @user.build_personal_profile(@conference, profile_params)
if @profile.save if @profile.save
flash[:notice] = t('.successfully_created') flash[:notice] = t(".successfully_created")
redirect_to management_conference_personal_profile_path(@profile, conference_id: @conference.id) redirect_to management_conference_personal_profile_path(@profile, conference_id: @conference.id)
else else
render action: :new render action: :new
@ -53,10 +53,10 @@ module Management
@conference = find_conference @conference = find_conference
@profile = find_profile @profile = find_profile
if @profile.update_attributes(profile_params) if @profile.update(profile_params)
redirect_to [:management, @conference, @profile] redirect_to [:management, @conference, @profile]
else else
render action: 'edit' render action: "edit"
end end
end end

View File

@ -10,7 +10,7 @@ module Management
@proposition.update(proposition_params) @proposition.update(proposition_params)
redirect_to :back redirect_back fallback_location: [:management, current_conference, @proposition]
end end
private private

View File

@ -3,8 +3,8 @@ module Management
include CurrentConferenceAssigning include CurrentConferenceAssigning
def index def index
@filters = params[:filters] || {} @filters = filter_params || {}
@volunteers = VolunteerSearch.new(scope: Volunteer.where(conference: current_conference).eager_load(:volunteer_teams), filters: params[:filters]).results @volunteers = VolunteerSearch.new(scope: Volunteer.where(conference: current_conference).eager_load(:volunteer_team), filters: params[:filters]).results
end end
def show def show
@ -15,6 +15,12 @@ module Management
@volunteer = current_conference.volunteers.find(params[:id]) @volunteer = current_conference.volunteers.find(params[:id])
end end
def destroy
@volunteer = current_conference.volunteers.find(params[:id])
@volunteer.destroy!
redirect_to management_conference_volunteers_path(conference_id: current_conference.id)
end
def update def update
@volunteer = current_conference.volunteers.find(params[:id]) @volunteer = current_conference.volunteers.find(params[:id])
@ -26,12 +32,18 @@ module Management
end end
private private
def filter_params
params.fetch(:filters, {}).permit(:volunteer_team_id)
end
def volunteer_params def volunteer_params
params.require(:volunteer).permit(:name, :picture, :email, :phone, params.require(:volunteer).permit(:name, :picture, :email, :phone,
:tshirt_size, :tshirt_cut, :tshirt_size, :tshirt_cut,
:food_preferences, :previous_experience, :food_preferences, :previous_experience,
:notes, :language, :notes, :language, :terms_accepted,
volunteer_team_ids: []) :volunteer_team_id,
additional_volunteer_team_ids: [])
end end
end end
end end

View File

@ -1,7 +1,7 @@
module Public module Public
class ApplicationController < ::ApplicationController class ApplicationController < ::ApplicationController
include ::CurrentConferenceAssigning include ::CurrentConferenceAssigning
before_filter :require_current_conference! before_action :require_current_conference!
def current_conference def current_conference
@current_conference ||= Conference.order(created_at: :desc).find_by(host_name: request.host) @current_conference ||= Conference.order(created_at: :desc).find_by(host_name: request.host)

View File

@ -2,33 +2,32 @@ class Public::ConferenceFeedbacksController < Public::ApplicationController
def index def index
@conference = current_conference @conference = current_conference
@unrated_events = @conference.events @unrated_events = @conference.events
.joins(:proposition).approved .joins(:proposition).approved
.joins('LEFT JOIN feedbacks ON feedbacks.feedback_receiving_id = events.id AND feedbacks.feedback_receiving_type = \'Event\'') .joins("LEFT JOIN feedbacks ON feedbacks.feedback_receiving_id = events.id AND feedbacks.feedback_receiving_type = 'Event'")
.where('feedbacks.session_id != ? OR feedbacks.id IS NULL', session.id).distinct .where("feedbacks.session_id != ? OR feedbacks.id IS NULL", session.id.to_s).distinct
@rated_events = @conference.events @rated_events = @conference.events
.joins(:proposition).approved .joins(:proposition).approved
.joins(:feedbacks) .joins(:feedbacks)
.where(feedbacks: {session_id: session.id}).distinct .where(feedbacks: {session_id: session.id.to_s}).distinct
end end
def new def new
if current_conference.feedbacks.where(session_id: session.id).exists? if current_conference.feedbacks.where(session_id: session.id.to_s).exists?
@feedback = current_conference.feedbacks.where(session_id: session.id).order(updated_at: :asc).last @feedback = current_conference.feedbacks.where(session_id: session.id.to_s).order(updated_at: :asc).last
else else
@feedback = current_conference.feedbacks.build @feedback = current_conference.feedbacks.build
@feedback.author_email = Feedback.where(session_id: session.id).order(updated_at: :asc).last.try(:author_email) @feedback.author_email = Feedback.where(session_id: session.id.to_s).order(updated_at: :asc).last.try(:author_email)
end end
end end
def create def create
@feedback = current_conference.feedbacks.build(feedback_params) @feedback = current_conference.feedbacks.build(feedback_params)
@feedback.ip_address = request.remote_ip @feedback.ip_address = request.remote_ip
@feedback.session_id = session.id @feedback.session_id = session.id.to_s
if @feedback.save if @feedback.save
flash[:notice] = I18n.t('public.conference_feedbacks.new.success') flash[:notice] = I18n.t("public.conference_feedbacks.new.success")
redirect_to conference_feedbacks_path redirect_to conference_feedbacks_path
else else
render :new, status: :unprocessable_entity render :new, status: :unprocessable_entity

View File

@ -1,11 +1,10 @@
class Public::EventFeedbackQrcodesController < Public::ApplicationController class Public::EventFeedbackQrcodesController < Public::ApplicationController
def show def show
event = current_conference.events.joins(:proposition).approved.find(params[:event_id]) event = current_conference.events.joins(:proposition).approved.find(params[:event_id]).decorate
@qr = RQRCode::QRCode.new(new_event_feedback_url(event_id: event.id), level: :l)
respond_to do |format| respond_to do |format|
format.svg do format.svg do
render(inline: @qr.as_svg(shape_rendering: 'crispEdges', module_size: 11, fill: 'ffffff', offset: 10), render(inline: event.feedback_qr_code_as_svg,
filename: "feedback_qr_code_#{event.id}.svg") filename: "feedback_qr_code_#{event.id}.svg")
end end
end end

View File

@ -1,20 +1,20 @@
class Public::EventFeedbacksController < Public::ApplicationController class Public::EventFeedbacksController < Public::ApplicationController
def new def new
if event.feedbacks.where(session_id: session.id).exists? if event.feedbacks.where(session_id: session.id.to_s).exists?
@feedback = event.feedbacks.where(session_id: session.id).order(updated_at: :asc).last @feedback = event.feedbacks.where(session_id: session.id.to_s).order(updated_at: :asc).last
else else
@feedback = event.feedbacks.build @feedback = event.feedbacks.build
@feedback.author_email = Feedback.where(session_id: session.id).order(updated_at: :asc).last.try(:author_email) @feedback.author_email = Feedback.where(session_id: session.id.to_s).order(updated_at: :asc).last.try(:author_email)
end end
end end
def create def create
@feedback = event.feedbacks.build(feedback_params) @feedback = event.feedbacks.build(feedback_params)
@feedback.ip_address = request.remote_ip @feedback.ip_address = request.remote_ip
@feedback.session_id = session.id @feedback.session_id = session.id.to_s
if @feedback.save if @feedback.save
flash[:notice] = I18n.t('public.event_feedbacks.new.success') flash[:notice] = I18n.t("public.event_feedbacks.new.success")
redirect_to conference_feedbacks_path redirect_to conference_feedbacks_path
else else
render :new, status: :unprocessable_entity render :new, status: :unprocessable_entity

View File

@ -1,9 +1,9 @@
module Public module Public
class EventsController < Public::ApplicationController class EventsController < Public::ApplicationController
before_filter :authenticate_user! before_action :authenticate_user!
def index def index
@events = Event.joins(:conference, :proposition, :participations).where(conference: current_conference).where('propositions.proposer_id = ? OR participations.participant_id = ?', current_user.id, current_user.id) @events = Event.joins(:conference, :proposition, :participations).where(conference: current_conference).where("propositions.proposer_id = ? OR participations.participant_id = ?", current_user.id, current_user.id)
end end
def edit def edit
@ -22,7 +22,7 @@ module Public
@event.participations.build participant: current_user, approved: true @event.participations.build participant: current_user, approved: true
if @event.save if @event.save
flash[:notice] = I18n.t('views.events.event_successfully_created', event_type: @event.event_type.name.mb_chars.downcase) flash[:notice] = I18n.t("views.events.event_successfully_created", event_type: @event.event_type.name.mb_chars.downcase)
after_save_redirect after_save_redirect
else else
render action: :new render action: :new
@ -33,7 +33,7 @@ module Public
@event = Event.joins(:participations).find_by(id: params[:id], participations: {participant_id: current_user.id}) @event = Event.joins(:participations).find_by(id: params[:id], participations: {participant_id: current_user.id})
if @event.update(event_params) if @event.update(event_params)
flash[:notice] = I18n.t('views.events.event_successfully_updated', event_type: @event.event_type.name.mb_chars.downcase) flash[:notice] = I18n.t("views.events.event_successfully_updated", event_type: @event.event_type.name.mb_chars.downcase)
after_save_redirect after_save_redirect
else else
render action: :edit render action: :edit
@ -44,9 +44,9 @@ module Public
@event = current_user.events.approved.find(params[:id]) @event = current_user.events.approved.find(params[:id])
if @event.confirm! if @event.confirm!
flash[:notice] = I18n.t('views.events.successfully_confirmed', event_type: @event.event_type.name.mb_chars.downcase) flash[:notice] = I18n.t("views.events.successfully_confirmed", event_type: @event.event_type.name.mb_chars.downcase)
else else
flash[:alert] = I18n.t('views.events.error_on_confirmation', event_type: @event.event_type.name.mb_chars.downcase) flash[:alert] = I18n.t("views.events.error_on_confirmation", event_type: @event.event_type.name.mb_chars.downcase)
end end
after_save_redirect after_save_redirect

View File

@ -1,12 +1,12 @@
module Public module Public
class PersonalProfilesController < Public::ApplicationController class PersonalProfilesController < Public::ApplicationController
before_filter :authenticate_user! before_action :authenticate_user!
def create def create
@profile = current_user.build_personal_profile(current_conference, profile_params) @profile = current_user.build_personal_profile(current_conference, profile_params)
if @profile.save if @profile.save
flash[:notice] = t('views.personal_profiles.successfully_created') flash[:notice] = t("views.personal_profiles.successfully_created")
redirect_to root_path redirect_to root_path
else else
render action: :new render action: :new
@ -20,11 +20,11 @@ module Public
def update def update
@profile = current_user.personal_profile(current_conference) @profile = current_user.personal_profile(current_conference)
if @profile.update_attributes(profile_params) if @profile.update(profile_params)
flash[:notice] = t('views.personal_profiles.successfully_updated') flash[:notice] = t("views.personal_profiles.successfully_updated")
redirect_to root_path redirect_to root_path
else else
render action: 'edit' render action: "edit"
end end
end end

View File

@ -0,0 +1,20 @@
module Public
class VolunteerConfirmationsController < Public::ApplicationController
def create
@volunteer = Volunteer.find_by!(unique_id: params[:id])
if ActiveSupport::SecurityUtils.secure_compare(@volunteer.confirmation_token, params[:confirmation_token])
@volunteer.transaction do
@volunteer.touch(:confirmed_at)
@volunteer.update(confirmation_token: nil)
end
@volunteer.send_notification_to_volunteer
redirect_to edit_volunteer_path(@volunteer.unique_id), notice: I18n.t("views.volunteers.email_confirmed_successfully")
else
redirect_to root_path, alert: I18n.t("views.volunteers.email_confirmation_error")
end
end
end
end

View File

@ -1,5 +1,6 @@
module Public module Public
class VolunteersController < Public::ApplicationController class VolunteersController < Public::ApplicationController
before_action :check_honey_pot, only: [:create, :edit]
def new def new
@volunteer = current_conference.volunteers.build @volunteer = current_conference.volunteers.build
end end
@ -11,7 +12,7 @@ module Public
def create def create
@volunteer = current_conference.volunteers.build volunteer_params @volunteer = current_conference.volunteers.build volunteer_params
if @volunteer.save if @volunteer.save
flash[:notice] = I18n.t('views.volunteers.successful_application') flash[:notice] = I18n.t("views.volunteers.successful_application")
redirect_to edit_volunteer_path(@volunteer.unique_id) redirect_to edit_volunteer_path(@volunteer.unique_id)
else else
render :new, status: :unprocessable_entity render :new, status: :unprocessable_entity
@ -21,7 +22,7 @@ module Public
def update def update
@volunteer = current_conference.volunteers.find_by! unique_id: params[:id] @volunteer = current_conference.volunteers.find_by! unique_id: params[:id]
if @volunteer.update volunteer_params if @volunteer.update volunteer_params
flash[:notice] = I18n.t('views.volunteers.successful_application_edit') flash[:notice] = I18n.t("views.volunteers.successful_application_edit")
redirect_to edit_volunteer_path(@volunteer.unique_id) redirect_to edit_volunteer_path(@volunteer.unique_id)
else else
render :edit, status: :unprocessable_entity render :edit, status: :unprocessable_entity
@ -30,11 +31,15 @@ module Public
private private
def check_honey_pot
head :unauthorized unless params.dig(:volunteer_ht, :full_name).blank?
end
def volunteer_params def volunteer_params
params.require(:volunteer).permit( params.require(:volunteer).permit(
:name, :picture, :email, :phone, :tshirt_size, :tshirt_cut, :name, :picture, :email, :phone, :tshirt_size, :tshirt_cut,
:food_preferences, :previous_experience, :notes, :language, :food_preferences, :previous_experience, :notes, :language,
volunteer_team_ids: [] :terms_accepted, :volunteer_team_id,
) )
end end
end end

View File

@ -1,6 +1,6 @@
module Public module Public
class VolunteershipsController < Public::ApplicationController class VolunteershipsController < Public::ApplicationController
before_filter :authenticate_user!, only: [:create, :destroy] before_action :authenticate_user!, only: [:create, :destroy]
def index def index
@volunteer_teams = current_conference.volunteer_teams @volunteer_teams = current_conference.volunteer_teams
@ -12,9 +12,9 @@ module Public
@volunteership.build_proposition proposer: current_user, status: :undecided @volunteership.build_proposition proposer: current_user, status: :undecided
if @volunteership.save if @volunteership.save
flash[:notice] = I18n.t('views.volunteerships.you_successfully_applied_for', team: @volunteership.volunteer_team.name) flash[:notice] = I18n.t("views.volunteerships.you_successfully_applied_for", team: @volunteership.volunteer_team.name)
else else
flash[:error] = I18n.t('views.volunteerships.an error_occurred_while_applying') flash[:error] = I18n.t("views.volunteerships.an error_occurred_while_applying")
end end
after_save_redirect after_save_redirect
@ -24,7 +24,7 @@ module Public
@volunteership = current_user.volunteerships.find params[:id] @volunteership = current_user.volunteerships.find params[:id]
if @volunteership.destroy if @volunteership.destroy
flash[:notice] = I18n.t('views.volunteerships.you_successfully_retracted_your_application_for', team: @volunteership.volunteer_team.name) flash[:notice] = I18n.t("views.volunteerships.you_successfully_retracted_your_application_for", team: @volunteership.volunteer_team.name)
end end
redirect_to volunteerships_path redirect_to volunteerships_path

View File

@ -0,0 +1,8 @@
class ApplicationDecorator < Draper::Decorator
# Define methods for all decorated objects.
# Helpers are accessed through `helpers` (aka `h`). For example:
#
# def percent_amount
# h.number_to_percentage object.amount, precision: 2
# end
end

View File

@ -0,0 +1,20 @@
class EventDecorator < Draper::Decorator
delegate_all
# Define presentation-specific methods here. Helpers are accessed through
# `helpers` (aka `h`). You can override attributes, for example:
#
# def created_at
# helpers.content_tag :span, class: 'time' do
# object.created_at.strftime("%a %m/%d/%y")
# end
# end
def feedback_qr_code
RQRCode::QRCode.new(h.new_event_feedback_url(event_id: id), level: :l)
end
def feedback_qr_code_as_svg
feedback_qr_code.as_svg(shape_rendering: "crispEdges", module_size: 11, fill: "ffffff", offset: 10)
end
end

View File

@ -10,7 +10,7 @@ module ApplicationHelper
"undecided" => "default", "undecided" => "default",
"approved" => "info", "approved" => "info",
"rejected" => "danger", "rejected" => "danger",
"backup" => "warning" "backup" => "warning",
}.with_indifferent_access[status] }.with_indifferent_access[status]
end end
@ -19,7 +19,7 @@ module ApplicationHelper
"undecided" => "question", "undecided" => "question",
"approved" => "thumbs-up", "approved" => "thumbs-up",
"rejected" => "thumbs-down", "rejected" => "thumbs-down",
"backup" => "refresh" "backup" => "refresh",
}.with_indifferent_access[status] }.with_indifferent_access[status]
end end
@ -29,38 +29,60 @@ module ApplicationHelper
def action_buttons(conference, record, actions = [:index, :show, :edit, :destroy]) def action_buttons(conference, record, actions = [:index, :show, :edit, :destroy])
klass = record.class klass = record.class
output = '' output = ""
if actions.include? :index if actions.include? :index
output += link_to(icon(:list), [:management, conference, klass], { output += link_to(icon(:list), [:management, conference, klass], {
title: t('actions.index.button', models: klass.model_name.human(count: 2)), title: t("actions.index.button", models: klass.model_name.human(count: 2)),
class: 'btn btn-info' class: "btn btn-info",
}) })
end end
if actions.include? :show if actions.include? :show
output += link_to(icon(:eye), [:management, conference, record], { output += link_to(icon(:eye), [:management, conference, record], {
title: t('actions.view.button', model: klass.model_name.human), title: t("actions.view.button", model: klass.model_name.human),
class: 'btn btn-info' class: "btn btn-info",
}) })
end end
if actions.include? :edit if actions.include? :edit
output += link_to(icon(:edit), [:edit, :management, conference, record], { output += link_to(icon(:edit), [:edit, :management, conference, record], {
title: t('actions.edit.button', model: klass.model_name.human), title: t("actions.edit.button", model: klass.model_name.human),
class: 'btn btn-warning' class: "btn btn-warning",
}) })
end end
if actions.include? :destroy if actions.include? :destroy
output += link_to(icon(:trash), [:management, conference, record], { output += link_to(icon(:trash), [:management, conference, record], {
method: :delete, method: :delete,
data: {confirm: t('actions.are_you_sure')}, data: {confirm: t("actions.are_you_sure")},
title: t('actions.destroy.button', model: klass.model_name.human), title: t("actions.destroy.button", model: klass.model_name.human),
class: 'btn btn-danger' class: "btn btn-danger",
}) })
end end
output.html_safe output.html_safe
end end
def rating_label_color(rating)
return nil if rating.nil?
case rating.round
when (0...3) then 'primary'
when 3 then 'danger'
when 4 then 'warning'
when 5 then 'info'
when 6 then 'success'
end
end
def human_rating(rating)
return nil if rating.nil?
case rating.round
when (0...3) then t('ratings.poor')
when 3 then t('ratings.average')
when 4 then t('ratings.good')
when 5 then t('ratings.very_good')
when 6 then t('ratings.excellent')
end
end
end end

View File

@ -5,26 +5,26 @@ module ConferencesHelper
start_date = conference.created_at.to_date start_date = conference.created_at.to_date
end_date = Time.zone.now.to_date < conference.start_date.to_date ? Time.zone.now.to_date : conference.start_date.to_date end_date = Time.zone.now.to_date < conference.start_date.to_date ? Time.zone.now.to_date : conference.start_date.to_date
chart_data = (start_date..end_date).map do |date| chart_data = (start_date..end_date).map { |date|
{ {
created_at: date, created_at: date,
new_submissions: submissions_by_day[date].try(:first).try(:number) || 0, new_submissions: submissions_by_day[date].try(:first).try(:number) || 0,
new_confirmations: confirmed_by_day[date].try(:first).try(:number) || 0 new_confirmations: confirmed_by_day[date].try(:first).try(:number) || 0,
} }
end }
chart_data.each_with_index do |entry, index| chart_data.each_with_index do |entry, index|
entry[:all_submissions] = if index == 0 entry[:all_submissions] = if index == 0
entry[:new_submissions] entry[:new_submissions]
else else
chart_data[index - 1][:all_submissions] + entry[:new_submissions] chart_data[index - 1][:all_submissions] + entry[:new_submissions]
end end
entry[:all_confirmations] = if index == 0 entry[:all_confirmations] = if index == 0
entry[:new_confirmations] entry[:new_confirmations]
else else
chart_data[index - 1][:all_confirmations] + entry[:new_confirmations] chart_data[index - 1][:all_confirmations] + entry[:new_confirmations]
end end
end end
chart_data chart_data
end end

View File

@ -1,16 +1,16 @@
module EventsHelper module EventsHelper
def links_to_event_participants_for(event) def links_to_event_participants_for(event)
event.participants_with_personal_profiles.map do |participant| event.participants_with_personal_profiles.map { |participant|
if participant.has_personal_profile? if participant.has_personal_profile?
link_to icon(:user, participant.name), link_to icon(:user, participant.name),
management_conference_personal_profile_path(participant.personal_profile_id, conference_id: event.conference.id) management_conference_personal_profile_path(participant.personal_profile_id, conference_id: event.conference.id)
else else
link_to icon('user-plus', participant.personal_email), link_to icon("user-plus", participant.personal_email),
new_management_conference_personal_profile_path(conference_id: event.conference.id, new_management_conference_personal_profile_path(conference_id: event.conference.id,
user_id: participant.id), user_id: participant.id),
title: t('management.events.event.create_profile'), class: 'bg-danger' title: t("management.events.event.create_profile"), class: "bg-danger"
end end
end.join(', ').html_safe }.join(", ").html_safe
end end
def participant_names_with_emails(event) def participant_names_with_emails(event)
@ -25,13 +25,11 @@ module EventsHelper
end end
def participant_names(event) def participant_names(event)
event.participants.map do |participant| event.participants.map { |participant|
if participant.personal_profile(event.conference).present? if participant.personal_profile(event.conference).present?
profile = participant.personal_profile(event.conference) profile = participant.personal_profile(event.conference)
"#{profile.name}" profile.name.to_s
else
nil
end end
end.compact }.compact
end end
end end

View File

@ -1,15 +1,21 @@
# coding: utf-8
class EventMailer < ActionMailer::Base class EventMailer < ActionMailer::Base
helper ApplicationHelper
def confirmation_request(event) def confirmation_request(event)
@event = event @event = event.decorate
I18n.locale = @event.proposer.language I18n.locale = @event.proposer.language
attachments['feedback-link-qr-code.svg'] = {
mime_type: 'image/svg+xml',
content: @event.feedback_qr_code_as_svg
}
mail to: @event.proposer.email, mail to: @event.proposer.email,
from: 'program@openfest.org', from: "program@openfest.org",
subject: I18n.t('event_mailer.acceptance_notification.subject', subject: I18n.t("event_mailer.acceptance_notification.subject",
conference: @event.conference.title, conference: @event.conference.title,
submission_type: @event.event_type.name.mb_chars.downcase.to_s, submission_type: @event.event_type.name.mb_chars.downcase.to_s,
title: @event.title) title: @event.title)
end end
def rejection_notification(event) def rejection_notification(event)
@ -17,11 +23,10 @@ class EventMailer < ActionMailer::Base
I18n.locale = @event.proposer.language I18n.locale = @event.proposer.language
mail to: @event.proposer.email, mail to: @event.proposer.email,
from: 'program@openfest.org', from: "program@openfest.org",
subject: I18n.t('event_mailer.rejection_notification.subject', subject: I18n.t("event_mailer.rejection_notification.subject",
conference: @event.conference.title, conference: @event.conference.title,
submission_type: @event.event_type.name.mb_chars.downcase.to_s, submission_type: @event.event_type.name.mb_chars.downcase.to_s,
title: @event.title) title: @event.title)
end end
end end

View File

@ -1,4 +1,3 @@
# coding: utf-8
class PropositionMailer < ActionMailer::Base class PropositionMailer < ActionMailer::Base
def new_proposition_notification(proposition) def new_proposition_notification(proposition)
@proposition = proposition @proposition = proposition

View File

@ -1,10 +1,11 @@
# coding: utf-8
class VolunteerMailer < ActionMailer::Base class VolunteerMailer < ActionMailer::Base
def team_notification(new_volunteer) def team_notification(new_volunteer)
@volunteer = new_volunteer @volunteer = new_volunteer
mail(to: @volunteer.conference.email, mail(
subject: "Нов доброволец #{@volunteer.name} <#{@volunteer.email}> за екип(и) #{@volunteer.volunteer_teams.map(&:name).join(', ')}") to: @volunteer.conference.email,
subject: "Нов доброволец за #{@volunteer.conference.title} - #{@volunteer.volunteer_team.name}"
)
end end
def volunteer_notification(new_volunteer) def volunteer_notification(new_volunteer)
@ -12,8 +13,19 @@ class VolunteerMailer < ActionMailer::Base
I18n.locale = @volunteer.language I18n.locale = @volunteer.language
mail(to: @volunteer.email, mail(to: @volunteer.email,
reply_to: @volunteer.conference.email, reply_to: @volunteer.conference.email,
from: 'no-reply@openfest.org', from: "OpenFest <cfp@openfest.org>",
subject: I18n.t('volunteer_mailer.success_notification.subject', subject: I18n.t("volunteer_mailer.success_notification.subject",
conference_name: @volunteer.conference.title))
end
def volunteer_email_confirmation(new_volunteer)
@volunteer = new_volunteer
I18n.locale = @volunteer.language
mail(to: @volunteer.email,
reply_to: @volunteer.conference.email,
from: "OpenFest <cfp@openfest.org>",
subject: I18n.t("volunteer_mailer.email_confirmation.subject",
conference_name: @volunteer.conference.title)) conference_name: @volunteer.conference.title))
end end
end end

View File

@ -2,7 +2,7 @@ class CallForParticipation < ActiveRecord::Base
belongs_to :conference belongs_to :conference
def open! def open!
self.opens_at = Time.now unless self.opens_at.present? self.opens_at = Time.now unless opens_at.present?
self.closes_at = nil self.closes_at = nil
save save
end end
@ -13,14 +13,14 @@ class CallForParticipation < ActiveRecord::Base
end end
def open? def open?
self.opens_at.present? and self.opens_at < Time.now opens_at.present? && (opens_at < Time.now)
end end
def closed? def closed?
self.closes_at.present? and self.closes_at < Time.now closes_at.present? && (closes_at < Time.now)
end end
def in_progress? def in_progress?
open? and not closed? open? && !closed?
end end
end end

View File

@ -0,0 +1,11 @@
module FeedbackReceiving
extend ActiveSupport::Concern
def average_rating
feedbacks.average(:rating)
end
def rated?
feedbacks.size > 0
end
end

View File

@ -14,30 +14,35 @@ class Conference < ActiveRecord::Base
has_many :halls has_many :halls
has_many :event_types has_many :event_types
has_many :events has_many :events
has_many :approved_events, -> { joins(:proposition).approved }, class_name: 'Event' has_many :approved_events, -> { joins(:proposition).approved }, class_name: "Event"
has_many :conflict_counts, through: :events has_many :conflict_counts, through: :events
has_many :volunteer_teams has_many :volunteer_teams
has_many :volunteers has_many :volunteers
has_one :call_for_participation, dependent: :destroy has_one :call_for_participation
has_many :participants, -> { uniq }, class_name: 'User', through: :events has_many :participants, -> { distinct }, class_name: "User", through: :events
has_many :participant_profiles, class_name: 'PersonalProfile' has_many :participant_profiles, class_name: "PersonalProfile"
has_many :slots, through: :halls has_many :slots, through: :halls
has_many :feedbacks, as: :feedback_receiving, dependent: :destroy has_many :editions, primary_key: :host_name, foreign_key: :host_name, class_name: "Conference"
has_many :editions, primary_key: :host_name, foreign_key: :host_name, class_name: 'Conference'
has_many :events_of_all_editions, through: :editions, source: :events has_many :events_of_all_editions, through: :editions, source: :events
include FeedbackReceiving
has_many :feedbacks, as: :feedback_receiving
has_many :feedbacks_with_comment, -> { where.not(comment: [nil, ""]) }, as: :feedback_receiving, class_name: 'Feedback'
has_many :event_feedbacks, through: :events, source: :feedbacks
has_many :event_feedbacks_with_comment, through: :events, source: :feedbacks_with_comment
accepts_nested_attributes_for :tracks, :halls, :event_types, :volunteer_teams, accepts_nested_attributes_for :tracks, :halls, :event_types, :volunteer_teams,
reject_if: :all_blank, allow_destroy: true reject_if: :all_blank, allow_destroy: true
after_create :create_call_for_participation after_create :create_call_for_participation
def submissions_grouped_by_day def submissions_grouped_by_day
submissions = events.group('date(events.created_at)').select('date(events.created_at) as created_at, count(events.id) as number') submissions = events.group("date(events.created_at)").select("date(events.created_at) as created_at, count(events.id) as number")
submissions.group_by { |s| s.created_at.to_date } submissions.group_by { |s| s.created_at.to_date }
end end
def submissions_grouped_by_confirmation_day def submissions_grouped_by_confirmation_day
submissions = events.joins(:proposition).approved.confirmed.group('date(propositions.confirmed_at)').select('date(propositions.confirmed_at) as confirmed_at, count(events.id) as number') submissions = events.joins(:proposition).approved.confirmed.group("date(propositions.confirmed_at)").select("date(propositions.confirmed_at) as confirmed_at, count(events.id) as number")
submissions.group_by { |s| s.confirmed_at.to_date } submissions.group_by { |s| s.confirmed_at.to_date }
end end
@ -46,7 +51,7 @@ class Conference < ActiveRecord::Base
end end
def has_vote_results? def has_vote_results?
vote_data_updated_at.present? and number_of_ballots_cast > 0 vote_data_updated_at.present? && (number_of_ballots_cast > 0)
end end
def has_voting_endpoint? def has_voting_endpoint?
@ -54,8 +59,8 @@ class Conference < ActiveRecord::Base
end end
def update_conflict_data! def update_conflict_data!
update_vote_data! or raise ActiveRecord::Rollback update_vote_data! || raise(ActiveRecord::Rollback)
events.all? { |event| event.update_conflict_data(false) } or raise ActiveRecord::Rollback events.all? { |event| event.update_conflict_data(false) } || raise(ActiveRecord::Rollback)
end end
def most_conflicts def most_conflicts
@ -74,17 +79,16 @@ class Conference < ActiveRecord::Base
conflict_counts.unscoped.where(left: approved_events, right: approved_events).order(number_of_conflicts: :asc).first.try(:number_of_conflicts) || 0 conflict_counts.unscoped.where(left: approved_events, right: approved_events).order(number_of_conflicts: :asc).first.try(:number_of_conflicts) || 0
end end
private private
def planned_cfp_end_date_is_before_start_date def planned_cfp_end_date_is_before_start_date
if planned_cfp_end_date.present? and start_date.present? and planned_cfp_end_date > start_date if planned_cfp_end_date.present? && start_date.present? && (planned_cfp_end_date > start_date)
errors.add(:planned_cfp_end_date, :cannot_be_after_start_date) errors.add(:planned_cfp_end_date, :cannot_be_after_start_date)
end end
end end
def start_date_is_before_end_date def start_date_is_before_end_date
if start_date.present? and end_date.present? and start_date > end_date if start_date.present? && end_date.present? && (start_date > end_date)
errors.add(:end_date, :cannot_be_before_start_date) errors.add(:end_date, :cannot_be_before_start_date)
end end
end end
@ -95,38 +99,38 @@ class Conference < ActiveRecord::Base
attr_accessor :conference attr_accessor :conference
def number_of_ballots def number_of_ballots
@number_of_ballots ||= remote_summary_data['number_of_ballots'] @number_of_ballots ||= remote_summary_data["number_of_ballots"]
end end
def ranking def ranking
@ranking ||= remote_summary_data['ranking'].map { |ranking_entry| EventRanking.new ranking_entry } @ranking ||= remote_summary_data["ranking"].map { |ranking_entry| EventRanking.new ranking_entry }
end end
def conflicts def conflicts
@conflicts ||= remote_summary_data['conflicts'].map { |conflicts_entry| ConflictsForEvent.new conflicts_entry } @conflicts ||= remote_summary_data["conflicts"].map { |conflicts_entry| ConflictsForEvent.new conflicts_entry }
end end
def save def save
conference.transaction do conference.transaction do
conference.number_of_ballots_cast = number_of_ballots conference.number_of_ballots_cast = number_of_ballots
conflicts.all?(&:save) or raise ActiveRecord::Rollback conflicts.all?(&:save) || raise(ActiveRecord::Rollback)
ranking.all?(&:save) or raise ActiveRecord::Rollback ranking.all?(&:save) || raise(ActiveRecord::Rollback)
conference.touch :vote_data_updated_at conference.touch :vote_data_updated_at
conference.save or raise ActiveRecord::Rollback conference.save || raise(ActiveRecord::Rollback)
end end
end end
private private
def connection def connection
@connection ||= Faraday.new(url: conference.vote_data_endpoint + '/summary.json', @connection ||= Faraday.new(url: conference.vote_data_endpoint + "/summary.json",
headers: {'Content-Type' => 'application/json'}) headers: {"Content-Type" => "application/json"})
end end
def remote_summary_data def remote_summary_data
@remote_summary_data ||= JSON.parse(connection.get do |request| @remote_summary_data ||= JSON.parse(connection.get { |request|
request.body = {summary: {talk_ids: conference.events.pluck(:id)}}.to_json request.body = {summary: {talk_ids: conference.events.pluck(:id)}}.to_json
end.body) }.body)
end end
class ConflictsForEvent class ConflictsForEvent
@ -136,14 +140,13 @@ class Conference < ActiveRecord::Base
def save def save
@event = Event.find(talk_id) @event = Event.find(talk_id)
@event.conflict_counts.destroy_all or raise ActiveRecord::Rollback @event.conflict_counts.destroy_all || raise(ActiveRecord::Rollback)
conflicts.all? do |right_event_id, number_of_conflicts| conflicts.all? do |right_event_id, number_of_conflicts|
ConflictCount.create left_id: talk_id, right_id: right_event_id, number_of_conflicts: number_of_conflicts ConflictCount.create left_id: talk_id, right_id: right_event_id, number_of_conflicts: number_of_conflicts
end or raise ActiveRecord::Rollback end || raise(ActiveRecord::Rollback)
end end
end end
class EventRanking class EventRanking
include ActiveModel::Model include ActiveModel::Model

View File

@ -1,5 +1,5 @@
class ConflictCount < ActiveRecord::Base class ConflictCount < ActiveRecord::Base
belongs_to :left, class_name: 'Event' belongs_to :left, class_name: "Event"
belongs_to :right, class_name: 'Event' belongs_to :right, class_name: "Event"
has_one :conference, through: :left has_one :conference, through: :left
end end

View File

@ -6,20 +6,24 @@ class Event < ActiveRecord::Base
has_one :slot has_one :slot
has_many :participations, dependent: :destroy has_many :participations, dependent: :destroy
has_many :pending_participations, ->() { pending }, class_name: 'Participation' has_many :pending_participations, -> { pending }, class_name: "Participation"
has_many :approved_participations, ->() { approved }, class_name: 'Participation' has_many :approved_participations, -> { approved }, class_name: "Participation"
has_many :participants, through: :approved_participations has_many :participants, through: :approved_participations
has_many :participants_with_personal_profiles, through: :approved_participations, source: :participant_with_personal_profile has_many :participants_with_personal_profiles, through: :approved_participations, source: :participant_with_personal_profile
has_many :conflict_counts, -> { order(number_of_conflicts: :desc) }, foreign_key: :left_id has_many :conflict_counts, -> { order(number_of_conflicts: :desc) }, foreign_key: :left_id
has_many :feedbacks, as: :feedback_receiving, dependent: :destroy
has_many :feedbacks, as: :feedback_receiving
has_many :feedbacks_with_comment, -> { where.not(comment: [nil, ""]) }, as: :feedback_receiving, class_name: 'Feedback'
include FeedbackReceiving
belongs_to :event_type belongs_to :event_type
scope :ranked, -> { where.not(ranked: nil).where.not(votes: nil) } scope :ranked, -> { where.not(ranked: nil).where.not(votes: nil) }
scope :approved, -> { where(propositions: {status: Proposition.statuses[:approved]})} scope :approved, -> { where(propositions: {status: Proposition.statuses[:approved]})}
scope :approved_joined, -> { joins(:proposition).merge(Proposition.approved) }
validates :conference, presence: true validates :conference, presence: true
validates :title, presence: true, length: { maximum: 65 } validates :title, presence: true, length: {maximum: 65}
validates :abstract, presence: true validates :abstract, presence: true
validates :description, presence: true validates :description, presence: true
validates :agreement, acceptance: true validates :agreement, acceptance: true
@ -61,16 +65,16 @@ class Event < ActiveRecord::Base
language: language, language: language,
abstract: abstract, abstract: abstract,
description: description, description: description,
notes: notes notes: notes,
} }
end end
def ranked? def ranked?
conference.has_vote_results? and rank.present? and number_of_votes.present? conference.has_vote_results? && rank.present? && number_of_votes.present?
end end
def per_cent_of_votes def per_cent_of_votes
if conference.has_vote_results? and conference.number_of_ballots_cast > 0 if conference.has_vote_results? && (conference.number_of_ballots_cast > 0)
Rational(number_of_votes * 100, conference.number_of_ballots_cast) Rational(number_of_votes * 100, conference.number_of_ballots_cast)
else else
Float::NAN Float::NAN
@ -80,20 +84,20 @@ class Event < ActiveRecord::Base
private private
def event_type_belongs_to_the_selected_conference def event_type_belongs_to_the_selected_conference
unless conference.present? and conference.event_types.include?(event_type) unless conference.present? && conference.event_types.include?(event_type)
errors.add :event_type, :must_be_a_valid_event_type errors.add :event_type, :must_be_a_valid_event_type
end end
end end
def track_belongs_to_the_selected_conference def track_belongs_to_the_selected_conference
unless conference.present? and conference.tracks.include?(track) unless conference.present? && conference.tracks.include?(track)
errors.add :track, :must_be_a_valid_track errors.add :track, :must_be_a_valid_track
end end
end end
def length_is_within_the_permitted_interval def length_is_within_the_permitted_interval
if event_type.present? if event_type.present?
unless length >= event_type.minimum_length and length <= event_type.maximum_length unless (length >= event_type.minimum_length) && (length <= event_type.maximum_length)
errors.add :length, :must_be_between, minimum: event_type.minimum_length, maximum: event_type.maximum_length errors.add :length, :must_be_between, minimum: event_type.minimum_length, maximum: event_type.maximum_length
end end
end end

View File

@ -9,6 +9,6 @@ class EventSearch
option(:confirmed) { |scope, value| scope.joins(:proposition).approved.where.not(propositions: {confirmed_at: nil}) } option(:confirmed) { |scope, value| scope.joins(:proposition).approved.where.not(propositions: {confirmed_at: nil}) }
option(:not_confirmed) { |scope, value| scope.joins(:proposition).approved.where(propositions: {confirmed_at: nil}) } option(:not_confirmed) { |scope, value| scope.joins(:proposition).approved.where(propositions: {confirmed_at: nil}) }
sort_by 'title' sort_by "title"
config[:defaults]['sort'] = "#{config[:sort_attributes].first} asc" config[:defaults]["sort"] = "#{config[:sort_attributes].first} asc"
end end

View File

@ -1,7 +1,7 @@
class Feedback < ActiveRecord::Base class Feedback < ActiveRecord::Base
belongs_to :feedback_receiving, polymorphic: true belongs_to :feedback_receiving, polymorphic: true
validates :rating, presence: true, inclusion: {in: [2, 3, 4, 5 ,6]} validates :rating, presence: true, inclusion: {in: [2, 3, 4, 5, 6]}
before_create :destroy_older_feedbacks_by_the_session before_create :destroy_older_feedbacks_by_the_session

View File

@ -3,7 +3,7 @@ class Participant < ActiveRecord::Base
self.primary_key = :participant_id self.primary_key = :participant_id
def twitter=(handle) def twitter=(handle)
write_attribute :twitter, handle.gsub(/\A@/,'') if handle write_attribute :twitter, handle.gsub(/\A@/, "") if handle
end end
def name def name

View File

@ -1,8 +1,8 @@
class Participation < ActiveRecord::Base class Participation < ActiveRecord::Base
belongs_to :participant, class_name: User belongs_to :participant, class_name: "User"
has_one :participant_with_personal_profile, class_name: Participant has_one :participant_with_personal_profile, class_name: "Participant"
belongs_to :event belongs_to :event
validates :participant_id, presence: true validates :participant_id, presence: true
scope :approved, ->() { where approved: true } scope :approved, -> { where approved: true }
scope :pending, ->() { where.not approved: true } scope :pending, -> { where.not approved: true }
end end

View File

@ -11,14 +11,14 @@ class PersonalProfile < ActiveRecord::Base
validates :twitter, format: {with: /\A[a-z0-9_]{1,15}\z/i}, allow_blank: true validates :twitter, format: {with: /\A[a-z0-9_]{1,15}\z/i}, allow_blank: true
validates :github, format: {with: /\A[a-z0-9][a-z0-9\-]*\z/i}, allow_blank: true validates :github, format: {with: /\A[a-z0-9][a-z0-9\-]*\z/i}, allow_blank: true
phony_normalize :mobile_phone, default_country_code: 'BG', add_plus: false phony_normalize :mobile_phone, default_country_code: "BG", add_plus: false
mount_uploader :picture, PictureUploader has_one_attached :picture
accepts_nested_attributes_for :user accepts_nested_attributes_for :user
def twitter=(handle) def twitter=(handle)
write_attribute :twitter, handle.gsub(/\A@/,'') if handle write_attribute :twitter, handle.gsub(/\A@/, "") if handle
end end
def name def name

View File

@ -1,10 +1,10 @@
class Proposition < ActiveRecord::Base class Proposition < ActiveRecord::Base
belongs_to :proposer, class_name: 'User' belongs_to :proposer, class_name: "User"
belongs_to :proposable, polymorphic: true, dependent: :destroy belongs_to :proposable, polymorphic: true, dependent: :destroy
enum status: [:undecided, :approved, :rejected, :backup] enum status: [:undecided, :approved, :rejected, :backup]
delegate :proposable_title, :proposable_type, :proposable_description, to: :proposable delegate :proposable_title, :proposable_type, :proposable_description, to: :proposable
after_create :send_creation_notification after_commit :send_creation_notification, on: [:create]
before_destroy :send_withdrawal_notification before_destroy :send_withdrawal_notification
def confirm! def confirm!

View File

@ -1,4 +1,5 @@
class Slot < ActiveRecord::Base class Slot < ActiveRecord::Base
belongs_to :hall belongs_to :hall
belongs_to :event belongs_to :event, required: false
belongs_to :approved_event, -> { joins(:proposition).approved_joined }, class_name: 'Event', foreign_key: 'event_id'
end end

View File

@ -9,7 +9,7 @@ class Track < ActiveRecord::Base
translates :name, :description translates :name, :description
def color=(hex_triplet) def color=(hex_triplet)
write_attribute :color, hex_triplet.gsub(/\A#/,'') if hex_triplet write_attribute :color, hex_triplet.gsub(/\A#/, "") if hex_triplet
end end
def color def color

View File

@ -2,17 +2,29 @@ class User < ActiveRecord::Base
# Include default devise modules. Others available are: # Include default devise modules. Others available are:
# :lockable, :timeoutable and :omniauthable # :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable, :confirmable, devise :database_authenticatable, :registerable, :confirmable,
:recoverable, :rememberable, :trackable, :validatable :recoverable, :rememberable, :trackable, :validatable
has_many :personal_profiles, dependent: :destroy has_many :personal_profiles, dependent: :destroy
has_many :lectures has_many :lectures
has_many :workshops has_many :workshops
has_many :propositions, foreign_key: :proposer_id has_many :propositions, foreign_key: :proposer_id
has_many :events, through: :propositions, source: :proposable, source_type: 'Event' has_many :events, through: :propositions, source: :proposable, source_type: "Event"
has_many :feedbacks, through: :events
has_many :feedbacks_with_comment, -> { where.not(comment: [nil, '']) }, through: :events
has_many :participations, foreign_key: :participant_id has_many :participations, foreign_key: :participant_id
has_many :events_participated_in, through: :participations, source: :event has_many :events_participated_in, through: :participations, source: :event
has_many :volunteerships, foreign_key: :volunteer_id has_many :volunteerships, foreign_key: :volunteer_id
include FeedbackReceiving
def average_rating
return nil unless rated?
ratings_per_event = feedbacks.group(:feedback_receiving_type,
:feedback_receiving_id).average(:rating).values
BigDecimal(ratings_per_event.reduce(&:+)) / BigDecimal(ratings_per_event.size)
end
def find_or_build_personal_profile(conference, params = {}) def find_or_build_personal_profile(conference, params = {})
current_profile = personal_profile(conference) current_profile = personal_profile(conference)
if current_profile.present? if current_profile.present?
@ -26,7 +38,7 @@ class User < ActiveRecord::Base
def build_personal_profile(conference, params = {}) def build_personal_profile(conference, params = {})
if personal_profiles.last.present? if personal_profiles.last.present?
new_personal_profile = personal_profiles.last.try(:dup) new_personal_profile = personal_profiles.last.try(:dup)
CopyCarrierwaveFile::CopyFileService.new(personal_profiles.last, new_personal_profile, :picture).set_file new_personal_profile.picture.attach(personal_profiles.last.picture.blob)
else else
new_personal_profile = personal_profiles.build new_personal_profile = personal_profiles.build
end end

View File

@ -3,44 +3,63 @@ class Volunteer < ActiveRecord::Base
TSHIRT_CUTS = [:unisex, :female] TSHIRT_CUTS = [:unisex, :female]
FOOD_PREFERENCES = [:none, :vegetarian, :vegan] FOOD_PREFERENCES = [:none, :vegetarian, :vegan]
attachment :picture, type: :image has_one_attached :picture
validates :name, :language, :tshirt_size, :tshirt_cut, :food_preferences, presence: true validates :name, :language, :tshirt_size, :tshirt_cut, :food_preferences, presence: true
validates :tshirt_size, inclusion: {in: TSHIRT_SIZES.map(&:to_s)} validates :tshirt_size, inclusion: {in: TSHIRT_SIZES.map(&:to_s)}
validates :tshirt_cut, inclusion: {in: TSHIRT_CUTS.map(&:to_s)} validates :tshirt_cut, inclusion: {in: TSHIRT_CUTS.map(&:to_s)}
validates :food_preferences, inclusion: {in: FOOD_PREFERENCES.map(&:to_s)} validates :food_preferences, inclusion: {in: FOOD_PREFERENCES.map(&:to_s)}
validates :email, format: {with: /\A[^@]+@[^@]+\z/}, presence: true validates :email, format: {with: /\A[^@]+@[^@]+\z/}, presence: true, uniqueness: {scope: :conference_id}
validates :phone, presence: true, format: {with: /\A[+\- \(\)0-9]+\z/} validates :phone, presence: true, format: {with: /\A[+\- \(\)0-9]+\z/}
validates :volunteer_teams, presence: true validates :volunteer_team, presence: true
validates :terms_accepted, acceptance: true
validate :volunteer_teams_belong_to_conference validate :volunteer_teams_belong_to_conference
phony_normalize :phone, default_country_code: 'BG' phony_normalize :phone, default_country_code: "BG"
belongs_to :conference belongs_to :conference
has_and_belongs_to_many :volunteer_teams belongs_to :volunteer_team
has_and_belongs_to_many :additional_volunteer_teams, class_name: "VolunteerTeam"
before_create :ensure_main_volunteer_team_is_part_of_additional_volunteer_teams
before_create :assign_unique_id before_create :assign_unique_id
after_create :send_notification_to_organizers before_create :assign_confirmation_token
after_create :send_notification_to_volunteer after_commit :send_email_confirmation_to_volunteer, on: [:create]
after_commit :send_email_to_organisers, on: [:create] # technically the volunteer's email is not confirmed yet
private
def assign_unique_id
self.unique_id = Digest::SHA256.hexdigest(SecureRandom.uuid)
end
def send_notification_to_organizers
VolunteerMailer.team_notification(self).deliver_later
end
def send_notification_to_volunteer def send_notification_to_volunteer
VolunteerMailer.volunteer_notification(self).deliver_later VolunteerMailer.volunteer_notification(self).deliver_later
end end
private
def ensure_main_volunteer_team_is_part_of_additional_volunteer_teams
self.additional_volunteer_teams |= [volunteer_team] if volunteer_team
end
def assign_unique_id
self.unique_id = Digest::SHA256.hexdigest(SecureRandom.uuid)
end
def assign_confirmation_token
self.confirmation_token = Digest::SHA256.hexdigest(SecureRandom.uuid)
end
def send_email_confirmation_to_volunteer
VolunteerMailer.volunteer_email_confirmation(self).deliver_later
end
def send_email_to_organisers
VolunteerMailer.team_notification(self).deliver_later
end
def volunteer_teams_belong_to_conference def volunteer_teams_belong_to_conference
conference_volunteer_teams = conference.volunteer_teams conference_volunteer_teams = conference.volunteer_teams
unless volunteer_teams.all? { |team| conference_volunteer_teams.include? team } unless additional_volunteer_teams.all? { |team| conference_volunteer_teams.include? team }
errors.add :volunteer_teams, :invalid_volunteer_team errors.add :additional_volunteer_teams, :invalid_volunteer_team
end
unless conference_volunteer_teams.include?(volunteer_team)
errors.add :volunteer_team, :invalid_volunteer_team
end end
end end
end end

View File

@ -1,8 +1,8 @@
class VolunteerSearch class VolunteerSearch
include SearchObject.module(:sorting) include SearchObject.module(:sorting)
option(:volunteer_team_id) { |scope, value| scope.joins(:volunteer_teams).where volunteer_teams: {id: value} } option(:volunteer_team_id) { |scope, value| scope.joins(:volunteer_team).where volunteer_team: {id: value} }
sort_by 'name' sort_by "name"
config[:defaults]['sort'] = "#{config[:sort_attributes].first} asc" config[:defaults]["sort"] = "#{config[:sort_attributes].first} asc"
end end

View File

@ -1,6 +1,7 @@
class VolunteerTeam < ActiveRecord::Base class VolunteerTeam < ActiveRecord::Base
belongs_to :conference belongs_to :conference
has_and_belongs_to_many :volunteers has_many :volunteers, inverse_of: :volunteer_team
has_and_belongs_to_many :supporters, class_name: "Volunteer", inverse_of: :additional_volunteer_teams
validates :name, presence: true validates :name, presence: true
validates :color, presence: true, format: {with: /\A#?[a-f0-9]{6}\z/i} validates :color, presence: true, format: {with: /\A#?[a-f0-9]{6}\z/i}
@ -13,7 +14,7 @@ class VolunteerTeam < ActiveRecord::Base
end end
def color=(hex_triplet) def color=(hex_triplet)
write_attribute :color, hex_triplet.gsub(/\A#/,'') if hex_triplet write_attribute :color, hex_triplet.gsub(/\A#/, "") if hex_triplet
end end
def color def color

View File

@ -2,7 +2,7 @@ class Volunteership < ActiveRecord::Base
include Proposable include Proposable
belongs_to :volunteer_team belongs_to :volunteer_team
belongs_to :volunteer, class_name: 'User' belongs_to :volunteer, class_name: "User"
has_one :conference, through: :volunteer_team has_one :conference, through: :volunteer_team
def proposable_title def proposable_title

View File

@ -1,55 +0,0 @@
class PictureUploader < CarrierWave::Uploader::Base
# Include RMagick or MiniMagick support:
#include CarrierWave::RMagick
include CarrierWave::MiniMagick
# Choose what kind of storage to use for this uploader:
storage :file
# storage :fog
# Override the directory where uploaded files will be stored.
# This is a sensible default for uploaders that are meant to be mounted:
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
# Provide a default URL as a default if there hasn't been a file uploaded:
def default_url
ActionController::Base.helpers.asset_path("fallback/profile_picture/" + [version_name, "default.png"].compact.join('_'))
end
# Process files as they are uploaded:
# process :scale => [200, 300]
#
# def scale(width, height)
# # do something
# end
# Create different versions of your uploaded files:
version :medium do
process :resize_to_fit => [171, 180]
end
version :thumb do
process :resize_to_fit => [50, 50]
end
version :schedule do
process :resize_to_fill => [100, 100]
end
# Add a white list of extensions which are allowed to be uploaded.
# For images you might use something like this:
def extension_white_list
%w(jpg jpeg png)
end
# Override the filename of the uploaded files:
# Avoid using model.id or version_name here, see uploader/store.rb for details.
# def filename
# "something.jpg" if original_filename
# end
end

View File

@ -0,0 +1 @@
json.array! @conferences, :id, :title, :start_date, :end_date, :created_at, :updated_at

View File

@ -0,0 +1,13 @@
json.array! @events, cached: ->(event) { [event, event.track, event.event_type] } do |event|
json.id event.id
json.title event.title
json.abstract event.abstract
json.track_id event.track_id
json.track do
json.name event.track.name
end
json.event_type do
json.name event.event_type.name
end
end

View File

@ -0,0 +1,24 @@
<%
cal = Icalendar::Calendar.new
cal.add_timezone(Time.zone.tzinfo.ical_timezone(Time.now))
@events.each do |event|
next unless event&.slot&.starts_at
cal.event do |ical_event|
ical_event.dtstart = Icalendar::Values::DateTime.new(event.slot.starts_at, 'tzid' => event.slot.starts_at.time_zone.tzinfo.identifier)
ical_event.dtend = Icalendar::Values::DateTime.new(event.slot.ends_at, 'tzid' => event.slot.ends_at.time_zone.tzinfo.identifier)
ical_event.summary = event.title
ical_event.description = event.description
ical_event.created = Icalendar::Values::DateTime.new(event.created_at, 'tzid' => event.created_at.time_zone.tzinfo.identifier)
ical_event.last_modified = Icalendar::Values::DateTime.new(event.updated_at, 'tzid' => event.updated_at.time_zone.tzinfo.identifier)
ical_event.location = event.slot.hall.name
ical_event.alarm do |alarm|
alarm.summary = event.title
alarm.trigger = "-PT15M"
end
end
end
cal.publish
-%>
<%= raw cal.to_ical %>

View File

@ -0,0 +1,22 @@
@halls.each do |hall|
json.set! hall.name do
json.days do
hall.slots.to_a.sort_by(&:starts_at).group_by { |slot| slot.starts_at.to_date }.each do |day, slots|
json.set! day do
json.array! slots do |slot|
next unless slot.approved_event
json.starts_at slot.starts_at
json.starts_at_human l(slot.starts_at, format: '%a, %H:%M')
json.title slot.approved_event.title
json.speakers do
json.array! slot.approved_event.participants_with_personal_profiles do |participant|
json.name participant.name
json.email participant.public_email
end
end
end
end
end
end
end
end

View File

@ -1,6 +1,6 @@
@speakers.each do |speaker| @speakers.each do |speaker|
json.set! speaker.user_id do json.set! speaker.user_id do
json.extract! speaker, :twitter, :github, :biography, :public_email, :organisation, :last_name, :first_name json.extract! speaker, :twitter, :github, :biography, :public_email, :organisation, :last_name, :first_name, :name
json.picture speaker.picture.serializable_hash json.picture rails_blob_url(speaker.picture.variant(resize_to_fill: [100, 100]))
end end
end end

View File

@ -1,22 +1,15 @@
Здравейте, Здравейте,
Радваме се да Ви съобщим, че предложението Ви за участие в <%= @event.conference.title %> с <%= @event.event_type.name.mb_chars.downcase %> на тема „<%= @event.title %>“ беше одобрено. <% if @event.slot.present? -%> Имаме удоволствието да ви информираме, че Вашето предложение за участие в „<%= @event.conference.title %>“ с <%= @event.event_type.name.mb_chars.downcase %> на тема „<%= @event.title %>“ беше одобрено. <% if @event.slot.present? -%>
Ще се проведе на <%= I18n.l @event.slot.starts_at, format: :long %> часа. Ще се проведе на <%= I18n.l @event.slot.starts_at, format: :long %> часа.
<% else %> <% else %>
Все още не са избрани час и дата за провеждането му. Определянето на датата и часа на провеждане все още предстои.
<% end %> <% end %>
Моля, потвърдете участието си възможно най-скоро като кликнете на следния линк: Моля, потвърдете Вашето участие възможно най-скоро като кликнете на следния линк:
<%= confirm_event_url @event, host: @event.conference.host_name, protocol: 'https' %> <%= confirm_event_url @event, host: @event.conference.host_name, protocol: 'https' %>
Моля пишете на ofvideo@openfest.org, ако имате специфични изисквания, например: С приложения QR код към този имейл ще можете да достъпите формуляра за обратна връзка на предложеното от Вас събитие. Моля, включете го в презентацията си.
* да ви осигурим лаптоп за презентация;
* звук от презентацията ви;
* имате нужда да изпозлвате жична или безжична връзка до Internet за презентацията си;
* ползвате macbook.
Отговорете на този email, ако възникнат някакви въпроси.
Поздрави, Поздрави,
Екипът на <%= @event.conference.title %> Екипът на <%= @event.conference.title %>

View File

@ -1,23 +1,17 @@
Hello, Hello,
We are happy to notify you that your request for participation in <%= @event.conference.title %> with the <%= @event.event_type.name.downcase %> titled "<%= @event.title %>" was approved. <% if @event.slot.present? -%> We are thrilled to inform you that your request for participation in <%= @event.conference.title %> with the <%= @event.event_type.name.downcase %> titled "<%= @event.title %>" has been approved. <% if @event.slot.present? -%>
It has been scheduled for <%= I18n.l @event.slot.starts_at, format: :long %>. It has been scheduled for <%= I18n.l @event.slot.starts_at, format: :long %>.
<% else %> <% else %>
It has not been scheduled yet. It has not been scheduled yet.
<% end %> <% end %>
Please confirm your participation as soon as you can by following the following link: To confirm your participation please follow the link below as soon as you can:
<%= confirm_event_url @event, host: @event.conference.host_name, protocol: 'https' %> <%= confirm_event_url @event, host: @event.conference.host_name, protocol: 'https' %>
We kindly request that you ensure the inclusion of the QR code attached to this email in your presentation. It links directly to the feedback form for your presentation, making it easier for attendees to provide valuable insights.
Please email ofvideo@openfest.org if you hav any specific requirements, for example: Should you have any questions or require further information, please do not hesitate to contact us by replying to this email.
* You need us to provide you with a laptop for your presentation; Best regards,
* You need to play sound for your presentation;
* You need either wired or wireless Internet connectivity;
* You're using a macbook.
Please respond to this email in case you have any questions.
Regards,
The <%= @event.conference.title %> Team The <%= @event.conference.title %> Team

View File

@ -1,9 +1,10 @@
Здравейте, Здравейте,
За съжаление, поради големия брой предложения за лекции, които получихме тази година, както и ограниченото време и брой зали, с които разполагаме, вашето предложение за <%= @event.event_type.name.mb_chars.downcase %> на тема „<%= @event.title %>“ не влезе в основната програма. За съжаление, поради голямото количество предложения за лекции, които получихме тази година, и ограниченото ни време, предложението ви за <%= @event.event_type.name.mb_chars.downcase %> на тема „<%= @event.title %>“ няма да може да бъде включено в програмата.
Тъй като темата звучи интересно, бихме ви предложили да се включите с lightning talk (петминутна презентация с или без слайдове) в дните на конференциятa. Обаче, ако желаете, може да представите лекцията си в рамките на 5 минути като част от програмата на Lightning talks (кратки 5 минутни лекции). Записването за тези слотове ще бъде възможно на място.
Записването тези кратки презентации ще се случва на регистрацията на <%= @event.conference.title %> след откриването на събитието в събота. Благодарим ви за интереса и участието ви в нашето събитие. До скоро!
До скоро! Екипът на <%= @event.conference.title %> Поздрави,
Екипът на <%= @event.conference.title %>

View File

@ -1,10 +1,10 @@
Hi, Hello,
Regretfully, because of the high amount of talk submissions that we've received this year and the limited amounts of time and rooms that we have, your submission for a <%= @event.event_type.name.mb_chars.downcase %> titled "<%= @event.title %>" was not included in the program. We regret to inform you that, due to an overwhelming number of talk submissions and the time constraints, we were unable to include your submission for a <%= @event.event_type.name.mb_chars.downcase %> titled "<%= @event.title %>" in the main schedule.
As the topic might still be interesting to our audience, it would be possible to join as a lightning talk (a five-minute talk with or without slides) during the conference. However, we have an alternative option available for you. If you're interested, you can present a condensed, 5-minute version of your talk during our Lightning Talks slot at the event venue. You can sign up for Lightning Talks in person during the event.
The sign-up for these talks happens at the registration of <%= @event.conference.title %> after the opening on Saturday. We appreciate your interest in contributing to <%= @event.conference.title %> and hope to see you there!
See you soon, Best regards,
<%= @event.conference.title %> team <%= @event.conference.title %> Team

View File

@ -12,10 +12,10 @@ html
- else - else
| Clarion | Clarion
= stylesheet_link_tag "management/application" = stylesheet_link_tag "management/application", nopush: false
= csrf_meta_tags = csrf_meta_tags
body body
main main
= render 'layouts/management/flash' = render 'layouts/management/flash'
== yield == yield
= javascript_include_tag "management/application" = javascript_include_tag "management/application", nopush: false

View File

@ -15,11 +15,11 @@ html
- else - else
| Clarion | Clarion
= stylesheet_link_tag "management/application" = stylesheet_link_tag "management/application", nopush: false
= csrf_meta_tags = csrf_meta_tags
body body
= render 'layouts/management/navigation' = render 'layouts/management/navigation'
main main
= render 'layouts/management/flash' = render 'layouts/management/flash'
== yield == yield
= javascript_include_tag "management/application" = javascript_include_tag "management/application", nopush: false

View File

@ -32,7 +32,9 @@ nav.navbar.navbar-static-top.navbar-inverse role="navigation"
/ li class="#{'active' if controller_name == 'propositions'}" / li class="#{'active' if controller_name == 'propositions'}"
/ = link_to [:management, current_conference, :propositions] do / = link_to [:management, current_conference, :propositions] do
/ => icon 'question', Proposition.model_name.human(count: 2).mb_chars.capitalize, class: 'fa-fw' / => icon 'question', Proposition.model_name.human(count: 2).mb_chars.capitalize, class: 'fa-fw'
li class="#{'active' if controller_name == 'feedback'}"
= link_to management_conference_feedback_index_path(current_conference) do
=> icon 'star-half-o', t('activerecord.models.feedback', count: 2).mb_chars.capitalize, class: 'fa-fw'
ul.nav.navbar-nav.navbar-right ul.nav.navbar-nav.navbar-right
li.dropdown li.dropdown
= link_to '#', class: 'dropdown-toggle', data: {toggle: 'dropdown'} do = link_to '#', class: 'dropdown-toggle', data: {toggle: 'dropdown'} do

View File

@ -20,9 +20,12 @@
.label.label-info .label.label-info
= event.rank = event.rank
=< number_to_percentage(event.per_cent_of_votes, strip_insignificant_zeros: true, precision: 2) =< number_to_percentage(event.per_cent_of_votes, strip_insignificant_zeros: true, precision: 2)
- if event.rated?
dt = t(".average_rating")
dd
=> human_rating(event.average_rating)
.badge class="badge-#{rating_label_color(event.average_rating)}"
= number_with_precision event.average_rating, precision: 2, strip_insignificant_zeros: true
td.visible-md.visible-lg.visible-xl td.visible-md.visible-lg.visible-xl
= links_to_event_participants_for(event) = links_to_event_participants_for(event)
td.action td.action

View File

@ -1,89 +1,123 @@
- personal_profile = speaker.personal_profile(@conference) || speaker.personal_profiles.last - personal_profile = speaker.personal_profile(@conference) || speaker.personal_profiles.last
.panel.panel-default .row
.panel-body .col-sm-4
.media .panel.panel-default
.media-left.hidden-sm.hidden-xs - if personal_profile.present?
- if personal_profile.present? .panel-image
= image_tag personal_profile.picture.medium.url, class: "profile-image" = image_tag personal_profile.picture
- else .panel-body
= icon :user, class: 'fa-5x' .media
.media-body .media-body
.text-center.visible-sm.visible-xs h4.media-heading
- if personal_profile.present? = personal_profile.name
= image_tag personal_profile.picture.medium.url, class: "profile-image img-thumbnail" hr
- else = simple_format(truncate(personal_profile.biography, omission: '... ', length: 300) { link_to(t('.continue'), [:management, @conference, personal_profile])})
= icon :user, class: 'fa-5x' ul.list-group
- if personal_profile.present?
h4.media-heading
= personal_profile.name
- unless personal_profile.conference == @conference
small<
| (
= t('.profile_from', conference: personal_profile.conference.title)
| )
hr
h4 = PersonalProfile.human_attribute_name(:biography)
= simple_format personal_profile.biography
h4 = t '.contacts'
- if personal_profile.organisation.present? - if personal_profile.organisation.present?
p #{icon :briefcase} @#{personal_profile.organisation} li.list-group-item
h5.list-group-item-heading = PersonalProfile.human_attribute_name :organisation
p.list-group-item-text = personal_profile.organisation
- if personal_profile.twitter.present? - if personal_profile.twitter.present?
p #{icon :twitter} @#{personal_profile.twitter} li.list-group-item
- if personal_profile.public_email.present? h5.list-group-item-heading = PersonalProfile.human_attribute_name :twitter
p #{icon :envelope} #{personal_profile.public_email} (#{PersonalProfile.human_attribute_name(:public_email).mb_chars.downcase}) p.list-group-item-text
p #{icon :envelope} #{speaker.email} (#{PersonalProfile.human_attribute_name(:email).mb_chars.downcase}) = link_to "@#{personal_profile.twitter}", "https://twitter.com/#{personal_profile.twitter}", target: '_blank'
p #{glyph :phone} #{Phony.format(personal_profile.mobile_phone, format: :international)} - if personal_profile.github.present?
- else li.list-group-item
h4.media-heading h5.list-group-item-heading = PersonalProfile.human_attribute_name :github
= speaker.email p.list-group-item-text
hr = link_to personal_profile.github, "https://github.com/#{personal_profile.github}", target: '_blank'
p .panel-footer
= t('.no_profile') .text-right
=< link_to icon('user-plus', t('.create_profile')), new_management_conference_personal_profile_path(conference_id: @conference.id, user_id: speaker.id), class: ['btn', 'btn-primary', 'btn-xs'] .btn-group.btn-group-sm
- if personal_profile.conference == @conference
= action_buttons @conference, personal_profile, [:show, :edit]
- else
= link_to [:management, @conference, :personal_profiles, {personal_profile: {user_id: speaker.id}}], class: ['btn', 'btn-primary'], title: t('actions.clone.title', model: PersonalProfile.model_name.human), method: :post do
=> icon('clone')
= link_to [:new, :management, @conference, :personal_profile, {user_id: speaker.id}], class: ['btn', 'btn-primary'], title: t('actions.create.title', model: PersonalProfile.model_name.human) do
=> icon('user-plus')
- else
.panel-image
= image_tag 'user-secret-solid.svg'
.panel-body
.media
.media-body
h4.media-heading
speaker.email
hr
p = t '.the_participant_has_not_created_a_profile'
ul.list-group
li.list-group-item
h5.list-group-item-heading = t '.private_email'
p.list-group-item-text = speaker.email
.col-sm-8
.row
.col-md-6
.panel.panel-info
.panel-heading
.row
.col-xs-3
= icon 'files-o', '', class: 'fa-5x'
.col-xs-9.text-right
.huge
= speaker.events_participated_in.size
div
= Event.model_name.human(count: speaker.events_participated_in.size)
.col-md-6
.panel class="panel-#{rating_label_color(speaker.average_rating || 5)}" title=human_rating(speaker.average_rating)
.panel-heading
.row
.col-xs-3
= icon 'star', '', class: 'fa-5x'
.col-xs-9.text-right
.huge
= number_with_precision(speaker.average_rating, precision: 2, strip_insignificant_zeros: true) || ''
div
= User.human_attribute_name(:average_rating).downcase
.row
.col-xs-12
h4 = t '.other_event_propositions'
- if speaker.events_participated_in.where.not(id: @event.id).any? - if speaker.events_participated_in.where.not(id: @event.id).any?
h4 = t '.previous_event_propositions' .panel.panel-default
table.table.table-striped.table-hover.record-table table.table.table-striped.table-hover.record-table
thead thead
tr
th
= Event.human_attribute_name :title
th.text-center
= Event.human_attribute_name :rank
th
= Event.human_attribute_name :conference
th
= Event.human_attribute_name :status
th
tbody
- speaker.events_participated_in.where.not(id: @event.id).order(created_at: :desc).each do |event|
tr tr
td = event.title th.main
td.text-center = Event.human_attribute_name :title
- if event.ranked? th.text-center
= Event.human_attribute_name :rating
th.text-center.hidden-md.hidden-sm.hidden-xs
= Event.human_attribute_name :rank
th.hidden-md.hidden-sm.hidden-xs
= Event.human_attribute_name :conference
th.hidden-md.hidden-sm.hidden-xs
= Event.human_attribute_name :status
th.hidden-md.hidden-sm.hidden-xs
tbody
- speaker.events_participated_in.where.not(id: @event.id).order(created_at: :desc).each do |event|
tr
td = event.title
td.text-center
- if event.rated?
.large .large
.label.label-info = event.rank .label class="label-#{rating_label_color(event.average_rating)}"
td = event.conference.title = number_with_precision event.average_rating, precision: 2, strip_insignificant_zeros: true
td td.text-center.hidden-md.hidden-sm.hidden-xs
span class="label label-lg label-#{proposition_status_class(event.status)}" - if event.ranked?
= icon(proposition_status_glyph(event.status), t("activerecord.attributes.proposition.statuses.#{event.status}")) .large
.label.label-info = event.rank
td.hidden-md.hidden-sm.hidden-xs = event.conference.title
td.hidden-md.hidden-sm.hidden-xs
span class="label label-lg label-#{proposition_status_class(event.status)}"
= icon(proposition_status_glyph(event.status), t("activerecord.attributes.proposition.statuses.#{event.status}"))
td.actions td.actions
.btn-group.btn-group-sm .btn-group.btn-group-sm
= action_buttons event.conference, event, [:show] = action_buttons event.conference, event, [:show]
- else
p = t '.no_other_event_propositions'
- if personal_profile.present?
.panel-footer
.text-right
.btn-group.btn-group-sm
- if personal_profile.conference == @conference
= action_buttons @conference, personal_profile, [:show, :edit]
- else
= link_to [:management, @conference, :personal_profiles, {personal_profile: {user_id: speaker.id}}], class: ['btn', 'btn-primary'], title: t('actions.clone.title', model: PersonalProfile.model_name.human), method: :post do
=> icon('clone')
= link_to [:new, :management, @conference, :personal_profile, {user_id: speaker.id}], class: ['btn', 'btn-primary'], title: t('actions.create.title', model: PersonalProfile.model_name.human) do
=> icon('user-plus')

View File

@ -1,5 +1,5 @@
<%- csv_headers = %w{id title subtitle type track language paticipants status rank number_of_votes} -%> <%- csv_headers = %w{id title subtitle type track language paticipants length status rank number_of_votes proposer_notes} -%>
<%= CSV.generate_line(csv_headers).html_safe -%> <%= CSV.generate_line(csv_headers).html_safe -%>
<%- @events.each do |event| -%> <%- @events.each do |event| -%>
<%= CSV.generate_line([event.id, event.title, event.subtitle, event.event_type.name, event.track.name, event.language, participant_names_with_emails(event).join(', '), event.status, event.rank, event.number_of_votes]).html_safe -%> <%= CSV.generate_line([event.id, event.title, event.subtitle, event.event_type.name, event.track.name, event.language, participant_names_with_emails(event).join(', '), event.length, event.status, event.rank, event.number_of_votes, event.notes]).html_safe -%>
<%- end -%> <%- end -%>

View File

@ -7,8 +7,8 @@
= Event.model_name.human(count: 2).mb_chars.titleize = Event.model_name.human(count: 2).mb_chars.titleize
small< small<
| ( | (
= t '.total', current: @events.count, total: current_conference.events.count = t '.total', current: @events.size, total: @conference.events.size
=< Event.model_name.human(count: current_conference.events.count) =< Event.model_name.human(count: @conference.events.size)
| ) | )
.row.visible-sm .row.visible-sm
.col-xs-12 .col-xs-12
@ -21,73 +21,73 @@
.panel-body .panel-body
ul.nav.nav-pills.nav-stacked ul.nav.nav-pills.nav-stacked
= content_tag :li, role: "presentation", class: @filters[:event_type_id].blank? ? 'active' : nil = content_tag :li, role: "presentation", class: @filters[:event_type_id].blank? ? 'active' : nil
= link_to management_conference_events_path(current_conference, filters: @filters.except(:event_type_id)) = link_to management_conference_events_path(@conference, filters: @filters.except(:event_type_id))
= t '.all' = t '.all'
span.badge.pull-right = current_conference.events.count span.badge.pull-right = @conference.events.size
- current_conference.event_types.each do |event_type| - @conference.event_types.each do |event_type|
= content_tag :li, role: "presentation", class: @filters[:event_type_id].to_i == event_type.id ? 'active' : nil = content_tag :li, role: "presentation", class: @filters[:event_type_id].to_i == event_type.id ? 'active' : nil
= link_to management_conference_events_path(current_conference, filters: @filters.merge({event_type_id: event_type.id})) = link_to management_conference_events_path(@conference, filters: @filters.merge({event_type_id: event_type.id}))
= event_type.name = event_type.name
span.badge.pull-right = current_conference.events.where(event_type: event_type).count span.badge.pull-right = @conference.events.where(event_type: event_type).size
.panel.panel-default .panel.panel-default
.panel-heading .panel-heading
= Event.human_attribute_name(:track) = Event.human_attribute_name(:track)
.panel-body .panel-body
ul.nav.nav-pills.nav-stacked ul.nav.nav-pills.nav-stacked
= content_tag :li, role: "presentation", class: @filters[:track_id].blank? ? 'active' : nil = content_tag :li, role: "presentation", class: @filters[:track_id].blank? ? 'active' : nil
= link_to management_conference_events_path(current_conference, filters: @filters.except(:track_id)) = link_to management_conference_events_path(@conference, filters: @filters.except(:track_id))
= t '.all' = t '.all'
span.badge.pull-right = current_conference.events.count span.badge.pull-right = @conference.events.size
- current_conference.tracks.each do |track| - @conference.tracks.each do |track|
= content_tag :li, role: "presentation", class: @filters[:track_id] == track.id.to_s ? 'active' : nil = content_tag :li, role: "presentation", class: @filters[:track_id] == track.id.to_s ? 'active' : nil
= link_to management_conference_events_path(current_conference, filters: @filters.merge({track_id: track.id})) = link_to management_conference_events_path(@conference, filters: @filters.merge({track_id: track.id}))
= track.name = track.name
span.badge.pull-right = current_conference.events.where(track: track).count span.badge.pull-right = @conference.events.where(track: track).size
.panel.panel-default .panel.panel-default
.panel-heading .panel-heading
= Event.human_attribute_name(:language) = Event.human_attribute_name(:language)
.panel-body .panel-body
ul.nav.nav-pills.nav-stacked ul.nav.nav-pills.nav-stacked
= content_tag :li, role: "presentation", class: @filters[:language].blank? ? 'active' : nil = content_tag :li, role: "presentation", class: @filters[:language].blank? ? 'active' : nil
= link_to management_conference_events_path(current_conference, filters: @filters.except(:language)) = link_to management_conference_events_path(@conference, filters: @filters.except(:language))
= t '.all' = t '.all'
span.badge.pull-right = current_conference.events.count span.badge.pull-right = @conference.events.size
- I18n.available_locales.map(&:to_s).each do |language| - I18n.available_locales.map(&:to_s).each do |language|
= content_tag :li, role: "presentation", class: @filters[:language] == language ? 'active' : nil = content_tag :li, role: "presentation", class: @filters[:language] == language ? 'active' : nil
= link_to management_conference_events_path(current_conference, filters: @filters.merge({language: language})) = link_to management_conference_events_path(@conference, filters: @filters.merge({language: language}))
= t("locales.#{language}") = t("locales.#{language}")
span.badge.pull-right = current_conference.events.where(language: language).count span.badge.pull-right = @conference.events.where(language: language).size
.panel.panel-default .panel.panel-default
.panel-heading .panel-heading
= Proposition.human_attribute_name(:status) = Proposition.human_attribute_name(:status)
.panel-body .panel-body
ul.nav.nav-pills.nav-stacked ul.nav.nav-pills.nav-stacked
= content_tag :li, role: "presentation", class: @filters[:status].blank? ? 'active' : nil = content_tag :li, role: "presentation", class: @filters[:status].blank? ? 'active' : nil
= link_to management_conference_events_path(current_conference, filters: @filters.except(:status)) = link_to management_conference_events_path(@conference, filters: @filters.except(:status))
= t '.all' = t '.all'
span.badge.pull-right = current_conference.events.count span.badge.pull-right = @conference.events.size
- Proposition.statuses.each do |status_name, status_id| - Proposition.statuses.each do |status_name, status_id|
= content_tag :li, role: "presentation", class: @filters[:status] == status_id.to_s ? 'active' : nil = content_tag :li, role: "presentation", class: @filters[:status] == status_id.to_s ? 'active' : nil
= link_to management_conference_events_path(current_conference, filters: @filters.merge({status: status_id})) = link_to management_conference_events_path(@conference, filters: @filters.merge({status: status_id}))
= t "activerecord.attributes.proposition.statuses.#{status_name}" = t "activerecord.attributes.proposition.statuses.#{status_name}"
span.badge.pull-right = current_conference.events.joins(:proposition).where(propositions: {status: status_id}).count span.badge.pull-right = @conference.events.joins(:proposition).where(propositions: {status: status_id}).size
.panel.panel-default .panel.panel-default
.panel-heading .panel-heading
= Proposition.human_attribute_name(:confirmed) = Proposition.human_attribute_name(:confirmed)
.panel-body .panel-body
ul.nav.nav-pills.nav-stacked ul.nav.nav-pills.nav-stacked
= content_tag :li, role: "presentation", class: @filters[:confirmed].blank? && @filters[:not_confirmed].blank? ? 'active' : nil = content_tag :li, role: "presentation", class: @filters[:confirmed].blank? && @filters[:not_confirmed].blank? ? 'active' : nil
= link_to management_conference_events_path(current_conference, filters: @filters.except(:confirmed, :not_confirmed)) = link_to management_conference_events_path(@conference, filters: @filters.except(:confirmed, :not_confirmed))
= t '.all' = t '.all'
span.badge.pull-right = current_conference.events.count span.badge.pull-right = @conference.events.size
= content_tag :li, role: "presentation", class: @filters[:confirmed].present? ? 'active' : nil = content_tag :li, role: "presentation", class: @filters[:confirmed].present? ? 'active' : nil
= link_to management_conference_events_path(current_conference, filters: @filters.except(:not_confirmed).merge({confirmed: true})) = link_to management_conference_events_path(@conference, filters: @filters.except(:not_confirmed).merge({confirmed: true}))
= t "activerecord.attributes.proposition.confirmation.confirmed" = t "activerecord.attributes.proposition.confirmation.confirmed"
span.badge.pull-right = current_conference.events.joins(:proposition).approved.where.not(propositions: {confirmed_at: nil}).count span.badge.pull-right = @conference.events.joins(:proposition).approved.where.not(propositions: {confirmed_at: nil}).size
= content_tag :li, role: "presentation", class: @filters[:not_confirmed].present? ? 'active' : nil = content_tag :li, role: "presentation", class: @filters[:not_confirmed].present? ? 'active' : nil
= link_to management_conference_events_path(current_conference, filters: @filters.except(:confirmed).merge({not_confirmed: true})) = link_to management_conference_events_path(@conference, filters: @filters.except(:confirmed).merge({not_confirmed: true}))
= t "activerecord.attributes.proposition.confirmation.not_confirmed" = t "activerecord.attributes.proposition.confirmation.not_confirmed"
span.badge.pull-right = current_conference.events.joins(:proposition).approved.where(propositions: {confirmed_at: nil}).count span.badge.pull-right = @conference.events.joins(:proposition).approved.where(propositions: {confirmed_at: nil}).size
.col-md-9 .col-md-9
.panel.panel-default .panel.panel-default
@ -102,5 +102,5 @@
tbody tbody
= render(partial: 'event', collection: @events) || render(partial: 'no_records') = render(partial: 'event', collection: @events) || render(partial: 'no_records')
.panel-footer.text-right .panel-footer.text-right
= link_to management_conference_events_path(current_conference, filters: @filters, format: 'csv'), class: 'btn btn-info' = link_to management_conference_events_path(@conference, filters: @filters, format: 'csv'), class: 'btn btn-info'
= icon :download, t('.export') = icon :download, t('.export')

View File

@ -65,52 +65,88 @@
h3 = Event.human_attribute_name :participants h3 = Event.human_attribute_name :participants
= render partial: 'speaker', collection: @event.participants = render partial: 'speaker', collection: @event.participants
- if @conference.has_vote_results? or @conference.has_voting_endpoint? - if @conference.start_date.past? || @event.rated?
.row .row
.col-xs-12 .col-xs-12
h2 h3 = Event.human_attribute_name :feedbacks
= t '.conflicts' - if @event.rated?
small< = t '.between_approved_events' .row
.panel.panel-default .col-md-10
table.table.table-striped.table-hover.record-table .panel.panel-default
- if @conference.has_vote_results? and @conference.approved_events.count > 2 .panel-heading = t('.comments')
thead - if @event.feedbacks_with_comment.size > 0
tr table.table.table-striped
th.text-right tbody
= t '.percent' = render partial: '/management/shared/feedback', collection: @event.feedbacks_with_comment
th - else
= Event.model_name.human.mb_chars.capitalize .panel-body
th = t ('.no_comments_received')
tbody .col-md-2
- if @conference.has_vote_results? .panel.panel-info
- if @conference.approved_events.count > 2 .panel-heading
- @event.conflict_counts.where(right_id: @conference.approved_events.pluck(:id)).includes(:right).each do |conflict_count| = t '.average_grade'
- conflict_percent = Rational(conflict_count.number_of_conflicts, @conference.number_of_ballots_cast) .panel-body.text-right
tr .huge
td.text-right = number_with_precision(@event.average_rating, precision: 2, strip_insignificant_zeros: true) || ''
.large = t('.total_feedback_grades', total_grades: @event.feedbacks.count, count: @event.feedbacks.count)
span.label.label-success data-conflicts="#{conflict_count.number_of_conflicts}" data-most-conflicts="#{@conference.most_conflicts_between_approved_events}" data-least-conflicts="#{@conference.least_conflicts_between_approved_events}" - else
= number_to_percentage(conflict_percent * 100, strip_insignificant_zeros: true, precision: 2) p = t '.no_feedback_received'
td
h4 = conflict_count.right.title
h5 = conflict_count.right.subtitle
= links_to_event_participants_for(conflict_count.right)
td.actions
= action_buttons @conference, conflict_count.right, [:show] - if @conference.has_vote_results? or @conference.has_voting_endpoint?
.row
.col-xs-12
h2
- if @conference.start_date.future?
= t '.conflicts'
- else
= t '.top_conflicts'
small< = t '.between_approved_events'
.panel.panel-default
table.table.table-striped.table-hover.record-table
- if @conference.has_vote_results? and @conference.approved_events.count > 2
thead
tr
th.text-right
= t '.percent'
th.main
= Event.model_name.human.mb_chars.capitalize
th
tbody
- if @conference.has_vote_results?
- if @conference.approved_events.count > 2
- if @conference.start_date.future?
- conflict_counts = @event.conflict_counts.where(right_id: @conference.approved_events.pluck(:id)).includes(:right)
- else
- conflict_counts = @event.conflict_counts.where(right_id: @conference.approved_events.pluck(:id)).includes(:right).limit(5)
- conflict_counts.each do |conflict_count|
- conflict_percent = Rational(conflict_count.number_of_conflicts, @conference.number_of_ballots_cast)
tr
td.text-right
.large
span.label.label-success data-conflicts="#{conflict_count.number_of_conflicts}" data-most-conflicts="#{@conference.most_conflicts_between_approved_events}" data-least-conflicts="#{@conference.least_conflicts_between_approved_events}"
= number_to_percentage(conflict_percent * 100, strip_insignificant_zeros: true, precision: 2)
td
h4 = conflict_count.right.title
h5 = conflict_count.right.subtitle
= links_to_event_participants_for(conflict_count.right)
td.actions
= action_buttons @conference, conflict_count.right, [:show]
- else
tr
td colspan="20"
= t '.no_approved_events'
- else - else
tr tr
td colspan="20" td colspan="20"
= t '.no_approved_events' = t 'management.conferences.vote_results.vote_data_never_updated'
- else = link_to update_vote_data_management_conference_path(@conference), method: :patch, class: ['btn', 'btn-primary'] do
tr = icon :refresh, t('management.conferences.vote_results.fetch_vote_results')
td colspan="20" - if @conference.has_vote_results?
= t 'management.conferences.vote_results.vote_data_never_updated' .panel-footer.text-right
.btn-group
= link_to conflicts_management_conference_event_path(@event, conference_id: @conference.id), class: ['btn', 'btn-info'] do
= icon :percent, t('.conflicts')
= link_to update_vote_data_management_conference_path(@conference), method: :patch, class: ['btn', 'btn-primary'] do
= icon :refresh, t('management.conferences.vote_results.fetch_vote_results') = icon :refresh, t('management.conferences.vote_results.fetch_vote_results')
- if @conference.has_vote_results?
.panel-footer.text-right
.btn-group
= link_to conflicts_management_conference_event_path(@event, conference_id: @conference.id), class: ['btn', 'btn-info'] do
= icon :percent, t('.conflicts')
= link_to update_vote_data_management_conference_path(@conference), method: :patch, class: ['btn', 'btn-primary'] do
= icon :refresh, t('management.conferences.vote_results.fetch_vote_results')

View File

@ -0,0 +1,8 @@
<%- csv_headers = %w{id feedback_receiving_type feedback_receiving_id name/title author_email rating comment ip session_id created_at} -%>
<%= CSV.generate_line(csv_headers).html_safe -%>
<%- @conference.feedbacks.each do |feedback| -%>
<%= CSV.generate_line([feedback.id, feedback.feedback_receiving_type, feedback.feedback_receiving_id, feedback.feedback_receiving&.title || feefback.feedback_receiving&.name, feedback.author_email, feedback.rating, feedback.comment, feedback.ip_address, feedback.session_id, feedback.created_at]).html_safe -%>
<%- end -%>
<%- @conference.event_feedbacks.each do |feedback| -%>
<%= CSV.generate_line([feedback.id, feedback.feedback_receiving_type, feedback.feedback_receiving_id, feedback.feedback_receiving&.title || feefback.feedback_receiving&.name, feedback.author_email, feedback.rating, feedback.comment, feedback.ip_address, feedback.session_id, feedback.created_at]).html_safe -%>
<%- end -%>

View File

@ -0,0 +1,62 @@
- content_for :title
= t '.feedback'
.row
.col-lg-12
h1.page-header
= t '.feedback'
= link_to management_conference_feedback_index_path(@conference, format: 'csv'), class: 'btn btn-info pull-right'
= icon :download, t('.export')
- if @conference.start_date.past? || @conference.rated?
.row
.col-xs-12
h3
=< t '.overall_organisation'
- if @conference.rated?
.row
.col-md-10
.panel.panel-default
.panel-heading = t('.comments')
- if @conference.feedbacks_with_comment.order(created_at: :asc).size > 0
table.table.table-striped
tbody
= render partial: '/management/shared/feedback', collection: @conference.feedbacks_with_comment.order(created_at: :asc), locals: {hide_title: true}
- else
.panel-body
= t ('.no_comments_received')
.col-md-2
.panel.panel-info
.panel-heading
= t '.average_grade'
.panel-body.text-right
.huge
= number_with_precision(@conference.average_rating, precision: 2, strip_insignificant_zeros: true) || ''
= t('.total_feedback_grades', total_grades: @conference.feedbacks.count, count: @conference.feedbacks.count)
- else
p = t '.no_feedback_received'
- if @conference.start_date.past? || @conference.event_feedbacks_with_comment.order(created_at: :asc).size > 0
.row
.col-xs-12
h3
=< t '.events'
- if @conference.event_feedbacks.size > 0
.row
.col-md-10
.panel.panel-default
.panel-heading = t('.comments')
table.table.table-striped
tbody
= render partial: '/management/shared/feedback', collection: @conference.event_feedbacks_with_comment.order(created_at: :asc)
.col-md-2
.panel.panel-info
.panel-heading
= t '.average_grade'
.panel-body.text-right
.huge
= number_with_precision(@conference.event_feedbacks.average(:rating), precision: 2, strip_insignificant_zeros: true) || ''
= t('.total_feedback_grades', total_grades: @conference.event_feedbacks.count, count: @conference.event_feedbacks.count)
- else
p = t '.no_comments_received'

View File

@ -10,9 +10,9 @@
.panel-body .panel-body
.row .row
.col-lg-12 .col-lg-12
- if f.object.picture.present? - if f.object.picture.attached?
.col-sm-offset-3.col-sm-9 .col-sm-offset-3.col-sm-9
= image_tag f.object.picture.medium.url, class: 'img-thumbnail' = image_tag f.object.picture.variant(resize_to_limit: [150, 150]), class: 'img-thumbnail'
= f.input :picture, wrapper: :horizontal_file_input = f.input :picture, wrapper: :horizontal_file_input

View File

@ -27,10 +27,9 @@
.media .media
.media-left .media-left
- if profile.present? - if profile.present?
= image_tag(profile.picture.thumb.url) = image_tag(profile.picture.variant(resize_to_fill: [50, 50]))
- else - else
= image_tag(PictureUploader.new.thumb.url) = image_tag('avatar-placeholder.png')
.media-body .media-body
h4.media-heading h4.media-heading
- if profile.try(:name).present? - if profile.try(:name).present?

View File

@ -5,65 +5,117 @@
.col-lg-12 .col-lg-12
h1.page-header h1.page-header
= PersonalProfile.model_name.human.mb_chars.capitalize = PersonalProfile.model_name.human.mb_chars.capitalize
.row
.panel.panel-default .col-sm-5.col-md-4
.panel-body .panel.panel-default
.media .panel-image
.media-left.hidden-sm.hidden-xs = image_tag @profile.picture
= image_tag @profile.picture.medium.url, class: "profile-image" .panel-body
.media-body .media
.text-center.visible-sm.visible-xs .media-body
= image_tag @profile.picture.medium.url, class: "profile-image img-thumbnail" h4.media-heading
h4.media-heading = @profile.name
= @profile.name hr
hr = simple_format @profile.biography
h4 = PersonalProfile.human_attribute_name(:biography) ul.list-group
= simple_format @profile.biography - if @profile.mobile_phone.present?
h4 = t '.contacts' li.list-group-item
h5.list-group-item-heading = PersonalProfile.human_attribute_name :mobile_phone
p.list-group-item-text = Phony.format(@profile.mobile_phone, format: :international)
- if @profile.organisation.present? - if @profile.organisation.present?
p = icon :briefcase, @profile.organisation li.list-group-item
h5.list-group-item-heading = PersonalProfile.human_attribute_name :organisation
p.list-group-item-text = @profile.organisation
- if @profile.twitter.present? - if @profile.twitter.present?
p = icon :twitter, "@#{@profile.twitter}" li.list-group-item
h5.list-group-item-heading = PersonalProfile.human_attribute_name :twitter
p.list-group-item-text
= link_to "@#{@profile.twitter}", "https://twitter.com/#{@profile.twitter}", target: '_blank'
- if @profile.github.present? - if @profile.github.present?
p = icon :github, @profile.github li.list-group-item
h5.list-group-item-heading = PersonalProfile.human_attribute_name :github
p.list-group-item-text
= link_to @profile.github, "https://github.com/#{@profile.github}", target: '_blank'
- if @profile.public_email.present? - if @profile.public_email.present?
p = icon :envelope, "#{@profile.public_email} (#{PersonalProfile.human_attribute_name(:public_email).mb_chars.downcase})" li.list-group-item
p = icon :envelope, "#{@user.email} (#{User.human_attribute_name(:email).mb_chars.downcase})" h5.list-group-item-heading = PersonalProfile.human_attribute_name :public_email
p.list-group-item-text = @profile.public_email
li.list-group-item
h5.list-group-item-heading = t '.private_email'
p.list-group-item-text = @user.email
.panel-footer
.text-right
.btn-group.btn-group-sm
= action_buttons @conference, @profile, [:edit, :destroy]
.col-sm-7.col-md-8
.row
.col-md-6
.panel.panel-info
.panel-heading
.row
.col-xs-3
= icon 'files-o', '', class: 'fa-5x'
.col-xs-9.text-right
.huge
= @user.events_participated_in.size
div
= Event.model_name.human(count: @user.events_participated_in.size)
.col-md-6
.panel class="panel-#{rating_label_color(@user.average_rating || 5)}" title=human_rating(@user.average_rating)
.panel-heading
.row
.col-xs-3
= icon 'star', '', class: 'fa-5x'
.col-xs-9.text-right
.huge
= number_with_precision(@user.average_rating, precision: 2, strip_insignificant_zeros: true) || ''
div
= User.human_attribute_name(:average_rating).downcase
- if @user.events_participated_in.any? h2 = t '.talk_history'
h4 = t '.event_propositions' .panel.panel-default
- if @user.events_participated_in.any?
table.table.table-striped.table-hover.record-table table.table.table-striped.table-hover.record-table
thead thead
tr tr
th th.main
= Event.human_attribute_name :title = Event.human_attribute_name :title
th.text-center th.text-center
= Event.human_attribute_name :rating
th.text-center.hidden-md.hidden-sm.hidden-xs
= Event.human_attribute_name :rank = Event.human_attribute_name :rank
th th.hidden-md.hidden-sm.hidden-xs
= Event.human_attribute_name :conference = Event.human_attribute_name :conference
th th.hidden-md.hidden-sm.hidden-xs
= Event.human_attribute_name :status = Event.human_attribute_name :status
th th
tbody tbody
- @user.events_participated_in.order(created_at: :desc).each do |event| - @user.events_participated_in.order(created_at: :desc).each do |event|
tr tr
td = event.title td = event.title
td.text-center td.text-center
- if event.rated?
.large
.label class="label-#{rating_label_color(event.average_rating)}" title=human_rating(event.average_rating)
= number_with_precision event.average_rating, precision: 2, strip_insignificant_zeros: true
td.text-center.hidden-md.hidden-sm.hidden-xs
- if event.ranked? - if event.ranked?
.large .large
.label.label-info = event.rank .label.label-info = event.rank
td = event.conference.title td.hidden-md.hidden-sm.hidden-xs
td = event.conference.title
td.hidden-md.hidden-sm.hidden-xs
span class="label label-lg label-#{proposition_status_class(event.status)}" span class="label label-lg label-#{proposition_status_class(event.status)}"
= icon(proposition_status_glyph(event.status), t("activerecord.attributes.proposition.statuses.#{event.status}")) = icon(proposition_status_glyph(event.status), t("activerecord.attributes.proposition.statuses.#{event.status}"))
td.actions td.actions
.btn-group.btn-group-sm .btn-group.btn-group-sm
= action_buttons event.conference, event, [:show] = action_buttons event.conference, event, [:show]
h2 = t '.comments_from_the_audience'
- if @user.feedbacks_with_comment.size > 0
.panel-footer .panel.panel-default
.text-right table.table.table-striped
.btn-group.btn-group-sm tbody
= action_buttons @conference, @profile, [:edit, :destroy] = render partial: '/management/shared/feedback', collection: @user.feedbacks_with_comment.order(created_at: :desc)
- else
p = t '.no_comments_received'

View File

@ -0,0 +1,16 @@
tr
td.text-center
.huge
.label class="label-#{rating_label_color(feedback.rating)}" title=human_rating(feedback.rating)
= feedback.rating
td
blockquote
= simple_format feedback.comment
footer
- if feedback.author_email.present?
= feedback.author_email
- else
= t(".anonymous")
- if !local_assigns[:hide_title]
span<> = t '.about'
= link_to feedback.feedback_receiving.title, [:management, current_conference, feedback.feedback_receiving]

View File

@ -5,13 +5,14 @@
.col-lg-12 .col-lg-12
- if f.object.picture.present? - if f.object.picture.present?
.col-sm-offset-3.col-sm-9 .col-sm-offset-3.col-sm-9
= attachment_image_tag(@volunteer, :picture, :fill, 150, 150) if @volunteer.picture.present? = @volunteer.picture.variant(resize_to_limit: [150, 150]) if @volunteer.picture.attached?
= f.input :picture, as: :attachment, wrapper: :horizontal_file_input, direct: true = f.input :picture, as: :file, wrapper: :horizontal_file_input, direct: true
= f.input :name, autofocus: true = f.input :name, autofocus: true
= f.input :email = f.input :email
= f.association :volunteer_teams, as: :check_boxes, wrapper: :horizontal_radio_and_checkboxes, collection: current_conference.volunteer_teams = f.association :volunteer_team, as: :radio_buttons, wrapper: :horizontal_radio_and_checkboxes, collection: current_conference.volunteer_teams
= f.association :additional_volunteer_teams, as: :check_boxes, wrapper: :horizontal_radio_and_checkboxes, collection: current_conference.volunteer_teams
= f.input :phone, input_html: {value: @volunteer.phone.try(:phony_formatted, format: :international)} = f.input :phone, input_html: {value: @volunteer.phone.try(:phony_formatted, format: :international)}
= f.input :language, as: :radio_buttons, wrapper: :horizontal_radio_and_checkboxes, collection: locale_collection, include_blank: false, checked: (@volunteer.language.presence || I18n.locale) = f.input :language, as: :radio_buttons, wrapper: :horizontal_radio_and_checkboxes, collection: locale_collection, include_blank: false, checked: (@volunteer.language.presence || I18n.locale)
= f.input :tshirt_size, collection: Volunteer::TSHIRT_SIZES, as: :radio_buttons, wrapper: :horizontal_radio_and_checkboxes, checked: (@volunteer.tshirt_size.presence || :m) = f.input :tshirt_size, collection: Volunteer::TSHIRT_SIZES, as: :radio_buttons, wrapper: :horizontal_radio_and_checkboxes, checked: (@volunteer.tshirt_size.presence || :m)
@ -19,5 +20,6 @@
= f.input :food_preferences, collection: Volunteer::FOOD_PREFERENCES, wrapper: :horizontal_radio_and_checkboxes, as: :radio_buttons, checked: (@volunteer.food_preferences.presence || :none) = f.input :food_preferences, collection: Volunteer::FOOD_PREFERENCES, wrapper: :horizontal_radio_and_checkboxes, as: :radio_buttons, checked: (@volunteer.food_preferences.presence || :none)
= f.input :previous_experience = f.input :previous_experience
= f.input :notes = f.input :notes
= f.input :terms_accepted
.panel-footer.text-right .panel-footer.text-right
= f.submit class: 'btn btn-primary' = f.submit class: 'btn btn-primary'

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