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.
/.bundle
# Ignore the default SQLite database.
/db/*.sqlite3
/db/*.sqlite3-journal
# Ignore all environment files (except templates).
/.env*
!/.env*.erb
# Ignore all logfiles and tempfiles.
/log/*.log
/tmp
/config/database.yml
/config/secrets.yml
/db/schema.rb
/erd.pdf
/public/uploads/tmp/*
/public/uploads/personal_profile/*
.sass-cache/
/coverage/
/log/*
/tmp/*
!/log/.keep
!/tmp/.keep
# Ignore pidfiles, but keep the directory.
/tmp/pids/*
!/tmp/pids/
!/tmp/pids/.keep
# 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
dist: xenial
cache: bundler
rvm:
- 2.4.5
- 3.3
script:
- 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
require 'capistrano/setup'
require "capistrano/setup"
# 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
#
@ -15,16 +18,19 @@ require 'capistrano/deploy'
# https://github.com/capistrano/rails
# https://github.com/capistrano/passenger
#
# require 'capistrano/rvm'
require 'capistrano/rvm'
# require 'capistrano/rbenv'
# require 'capistrano/chruby'
require 'capistrano/bundler'
require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'
require 'capistrano/puma'
require "capistrano/bundler"
require "capistrano/rails/assets"
require "capistrano/rails/migrations"
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/passenger'
# require 'capistrano/rvm'
# 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 'pg'
gem "sqlite3"
gem "pg"
gem 'sass-rails'
gem "sass-rails"
gem 'uglifier'
gem 'coffee-rails'
gem 'mini_racer', platforms: :ruby
gem 'jquery-rails'
gem "uglifier"
gem "coffee-rails"
gem "jquery-rails"
gem 'slim-rails'
gem "slim-rails"
gem 'rails-i18n'
gem "rails-i18n"
gem 'devise'
gem 'devise-i18n'
gem "devise"
gem "devise-i18n"
gem 'simple_form'
gem "simple_form"
# Phone validation
gem 'phony_rails'
gem "phony"
gem "phony_rails"
# Picture uploads
gem 'carrierwave'
#gem 'rmagick'
gem "mini_magick"
gem "puma", group: :production
gem "refile", require: ['refile/rails', 'refile/simple_form']
gem "refile-mini_magick"
gem "globalize"
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 'bootstrap-sass-extras'
gem 'bootswatch-rails'
gem 'autoprefixer-rails'
gem 'font-awesome-sass'
gem "copy_carrierwave_file"
gem 'nested_form'
gem 'jquery-datatables-rails'
gem 'morrisjs-rails'
gem 'raphael-rails'
gem "jbuilder"
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
gem 'spring'
gem 'spring-commands-rspec'
gem 'guard-rspec' # Continuous testing with Guard
gem 'rails-erd'
gem 'pry-rails'
gem "spring"
gem "spring-commands-rspec"
gem "guard-rspec" # Continuous testing with Guard
gem "rails-erd"
gem "pry-rails"
# gem 'hirb'
gem 'awesome_print'
gem 'quiet_assets'
gem 'capistrano'
gem 'capistrano-rails'
# gem 'capistrano-rvm'
gem 'capistrano3-puma'
gem 'better_errors'
gem 'binding_of_caller'
gem "awesome_print"
gem "better_errors"
gem "binding_of_caller"
end
group :development, :test do
gem 'rspec-rails'
gem 'factory_girl_rails'
gem 'faker'
gem 'capybara'
gem 'selenium-webdriver'
gem "rspec-rails"
gem "faker"
gem "capybara"
gem "selenium-webdriver"
gem 'byebug'
gem 'simplecov'
gem 'i18n-tasks'
gem "byebug"
gem "simplecov"
gem "i18n-tasks"
gem 'delorean'
gem "delorean"
gem "standard"
end
group :test do
gem 'database_cleaner'
gem "database_cleaner"
gem "factory_bot_rails"
end
gem "thruster", "~> 0.1.8"

View File

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

View File

@ -1,18 +1,20 @@
# 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{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
watch('spec/spec_helper.rb') { 'spec' }
watch('spec/rails_helper.rb') { 'spec' }
watch("spec/spec_helper.rb") { "spec" }
watch("spec/rails_helper.rb") { "spec" }
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/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('config/routes.rb') { 'spec/routing' }
watch('app/controllers/application_controller.rb') { 'spec/controllers' }
watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
watch("config/routes.rb") { "spec/routing" }
watch("app/controllers/application_controller.rb") { "spec/controllers" }
# Capybara features specs
watch(%r{^app/views/(.+)/.*\.(erb|haml|slim)$}) { |m| "spec/features/#{m[1]}_spec.rb" }
notification :off
end

View File

@ -1,11 +1,31 @@
Clarion
=======
# Clarion
A CfP automation system for OpenFest.
Installation
------------
## Installation
1. `git clone https://github.com/ignisf/clarion.git`
2. Run `bundle install; bin/rake bootstrap`
3. You can now run the rails server with `bin/rails s`
### For local development
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,
# 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

15
TODO
View File

@ -1,14 +1,14 @@
- User-facing:
- Event proposal: lecture, workshop, open space (CRUD)
- Volunteership
- # Event proposal: lecture, workshop, open space (CRUD)
- # Volunteership
- Sponsorship
- Admin:
- # Create a conference, halls, tracks
- # Starting a CFP
- # conferences#show -> admin dashboard
- volunteers#index -> approved volunteers
- sponsorships#index -> generate token, links to send around
- # volunteers#index -> approved volunteers
- # sponsorships#index -> generate token, links to send around
- scheduling -> calendar with events
- # proposals#index -> undecided events, grouped by user
@ -17,12 +17,11 @@
- users:
- # edit profile: image upload and stuff
- # show profile
- Edit profiles instead
- # Edit profiles instead
- Home-area user CRUD
- # Home-area user CRUD
- # Controller before_action that checks for current_conference
- Devise view styling
- # Devise view styling
Notes:
- .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 bootstrap-sprockets
//= require raphael
//= require morris
//= require chroma-js/chroma
//= require_directory .

View File

@ -22,6 +22,10 @@ th.action, td.action {
}
}
th.main {
width: 100%;
}
.conference-title {
display: inline-block;
@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 {
@extend .col-sm-offset-2;
@extend .col-sm-8;
.center {
text-align: center;
}
.profile-image {
@extend .img-thumbnail;
max-width: 171px;
max-height: 180px;
}
.social {
@extend .btn-group;
margin-top: 10px;
a {
@extend .btn;
@extend .btn-default;
}
}
.panel .panel-image img {
display: block;
max-width: 100%;
max-height: 250px;
height: auto;
margin-right: auto;
margin-left: auto;
width: 100%;
object-fit: cover;
}

View File

@ -6,7 +6,7 @@
@import "bootstrap";
@import "bootswatch/simplex/bootswatch";
@import "morris";
/* @import "morris"; */
@import "users";
@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
include ::CurrentConferenceAssigning
include ::PublicApiExposing
before_filter :require_current_conference!
before_action :require_current_conference!
def index
@event_types = current_conference.event_types.includes(:translations)
fresh_when @event_types
end
end

View File

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

View File

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

View File

@ -1,9 +1,10 @@
class Api::SpeakersController < Api::ApplicationController
include ::CurrentConferenceAssigning
include ::PublicApiExposing
before_filter :require_current_conference!
before_action :require_current_conference!
def index
@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

View File

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

View File

@ -4,15 +4,15 @@ class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
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_view_paths
# TODO: make this get the domain from the database
#layout Proc.new { |controller| controller.request.host }
layout 'public/application'
# layout Proc.new { |controller| controller.request.host }
layout "public/application"
def self.default_url_options(options={})
def self.default_url_options(options = {})
if I18n.locale != I18n.default_locale
options.merge({locale: I18n.locale})
else
@ -24,17 +24,17 @@ class ApplicationController < ActionController::Base
def set_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)
end
end
def set_view_paths
# 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 =~ /example/
prepend_view_path 'lib/initfest/views' if request.host =~ /127\.0\.0/
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 =~ /^127\.0\.0/
prepend_view_path "lib/initfest/views" if request.host =~ /^localhost$/
end
protected

View File

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

View File

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

View File

@ -6,9 +6,9 @@ class ConfirmationsController < Devise::ConfirmationsController
if resource.errors.empty?
set_flash_message(:notice, :confirmed) if is_flashing_format?
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
respond_with_navigational(resource.errors, status: :unprocessable_entity){ render :new }
respond_with_navigational(resource.errors, status: :unprocessable_entity) { render :new }
end
end
end

View File

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

View File

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

View File

@ -2,8 +2,15 @@ module Management
class EventsController < ManagementController
def index
@conference = find_conference
@filters = params[:filters] || {}
@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
@filters = filter_params || {}
@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)
end
@ -22,10 +29,10 @@ module Management
@event = @conference.events.find(params[:id])
if @event.update(event_params)
flash[:notice] = 'Event was successfully updated.'
flash[:notice] = t(".event_successfully_updated")
redirect_to [:management, @conference, @event]
else
render action: 'edit'
render action: "edit"
end
end
@ -46,27 +53,38 @@ module Management
private
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
def event_params
params.require(:event).permit(
:title,
:subtitle,
:length,
:language,
:abstract,
:description,
:notes,
:track_id,
:event_type_id,
participations_attributes: [
:id,
:participant_id,
:approved,
:_destroy
]
)
:title,
:subtitle,
:length,
:language,
:abstract,
:description,
:notes,
:track_id,
:event_type_id,
participations_attributes: [
:id,
:participant_id,
:approved,
:_destroy,
]
)
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
class ManagementController < ::ApplicationController
before_action :authenticate_user!, :authorize_user!
layout 'management'
layout "management"
private
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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
module Public
class ApplicationController < ::ApplicationController
include ::CurrentConferenceAssigning
before_filter :require_current_conference!
before_action :require_current_conference!
def current_conference
@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
@conference = current_conference
@unrated_events = @conference.events
.joins(:proposition).approved
.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
.joins(:proposition).approved
.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.to_s).distinct
@rated_events = @conference.events
.joins(:proposition).approved
.joins(:feedbacks)
.where(feedbacks: {session_id: session.id}).distinct
.joins(:proposition).approved
.joins(:feedbacks)
.where(feedbacks: {session_id: session.id.to_s}).distinct
end
def new
if current_conference.feedbacks.where(session_id: session.id).exists?
@feedback = current_conference.feedbacks.where(session_id: session.id).order(updated_at: :asc).last
if current_conference.feedbacks.where(session_id: session.id.to_s).exists?
@feedback = current_conference.feedbacks.where(session_id: session.id.to_s).order(updated_at: :asc).last
else
@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
def create
@feedback = current_conference.feedbacks.build(feedback_params)
@feedback.ip_address = request.remote_ip
@feedback.session_id = session.id
@feedback.session_id = session.id.to_s
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
else
render :new, status: :unprocessable_entity

View File

@ -1,11 +1,10 @@
class Public::EventFeedbackQrcodesController < Public::ApplicationController
def show
event = current_conference.events.joins(:proposition).approved.find(params[:event_id])
@qr = RQRCode::QRCode.new(new_event_feedback_url(event_id: event.id), level: :l)
event = current_conference.events.joins(:proposition).approved.find(params[:event_id]).decorate
respond_to do |format|
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")
end
end

View File

@ -1,20 +1,20 @@
class Public::EventFeedbacksController < Public::ApplicationController
def new
if event.feedbacks.where(session_id: session.id).exists?
@feedback = event.feedbacks.where(session_id: session.id).order(updated_at: :asc).last
if event.feedbacks.where(session_id: session.id.to_s).exists?
@feedback = event.feedbacks.where(session_id: session.id.to_s).order(updated_at: :asc).last
else
@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
def create
@feedback = event.feedbacks.build(feedback_params)
@feedback.ip_address = request.remote_ip
@feedback.session_id = session.id
@feedback.session_id = session.id.to_s
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
else
render :new, status: :unprocessable_entity

View File

@ -1,9 +1,9 @@
module Public
class EventsController < Public::ApplicationController
before_filter :authenticate_user!
before_action :authenticate_user!
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
def edit
@ -22,7 +22,7 @@ module Public
@event.participations.build participant: current_user, approved: true
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
else
render action: :new
@ -33,7 +33,7 @@ module Public
@event = Event.joins(:participations).find_by(id: params[:id], participations: {participant_id: current_user.id})
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
else
render action: :edit
@ -44,9 +44,9 @@ module Public
@event = current_user.events.approved.find(params[:id])
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
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
after_save_redirect

View File

@ -1,12 +1,12 @@
module Public
class PersonalProfilesController < Public::ApplicationController
before_filter :authenticate_user!
before_action :authenticate_user!
def create
@profile = current_user.build_personal_profile(current_conference, profile_params)
if @profile.save
flash[:notice] = t('views.personal_profiles.successfully_created')
flash[:notice] = t("views.personal_profiles.successfully_created")
redirect_to root_path
else
render action: :new
@ -20,11 +20,11 @@ module Public
def update
@profile = current_user.personal_profile(current_conference)
if @profile.update_attributes(profile_params)
flash[:notice] = t('views.personal_profiles.successfully_updated')
if @profile.update(profile_params)
flash[:notice] = t("views.personal_profiles.successfully_updated")
redirect_to root_path
else
render action: 'edit'
render action: "edit"
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
class VolunteersController < Public::ApplicationController
before_action :check_honey_pot, only: [:create, :edit]
def new
@volunteer = current_conference.volunteers.build
end
@ -11,7 +12,7 @@ module Public
def create
@volunteer = current_conference.volunteers.build volunteer_params
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)
else
render :new, status: :unprocessable_entity
@ -21,7 +22,7 @@ module Public
def update
@volunteer = current_conference.volunteers.find_by! unique_id: params[:id]
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)
else
render :edit, status: :unprocessable_entity
@ -30,11 +31,15 @@ module Public
private
def check_honey_pot
head :unauthorized unless params.dig(:volunteer_ht, :full_name).blank?
end
def volunteer_params
params.require(:volunteer).permit(
:name, :picture, :email, :phone, :tshirt_size, :tshirt_cut,
:food_preferences, :previous_experience, :notes, :language,
volunteer_team_ids: []
:terms_accepted, :volunteer_team_id,
)
end
end

View File

@ -1,6 +1,6 @@
module Public
class VolunteershipsController < Public::ApplicationController
before_filter :authenticate_user!, only: [:create, :destroy]
before_action :authenticate_user!, only: [:create, :destroy]
def index
@volunteer_teams = current_conference.volunteer_teams
@ -12,9 +12,9 @@ module Public
@volunteership.build_proposition proposer: current_user, status: :undecided
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
flash[:error] = I18n.t('views.volunteerships.an error_occurred_while_applying')
flash[:error] = I18n.t("views.volunteerships.an error_occurred_while_applying")
end
after_save_redirect
@ -24,7 +24,7 @@ module Public
@volunteership = current_user.volunteerships.find params[:id]
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
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",
"approved" => "info",
"rejected" => "danger",
"backup" => "warning"
"backup" => "warning",
}.with_indifferent_access[status]
end
@ -19,7 +19,7 @@ module ApplicationHelper
"undecided" => "question",
"approved" => "thumbs-up",
"rejected" => "thumbs-down",
"backup" => "refresh"
"backup" => "refresh",
}.with_indifferent_access[status]
end
@ -29,38 +29,60 @@ module ApplicationHelper
def action_buttons(conference, record, actions = [:index, :show, :edit, :destroy])
klass = record.class
output = ''
output = ""
if actions.include? :index
output += link_to(icon(:list), [:management, conference, klass], {
title: t('actions.index.button', models: klass.model_name.human(count: 2)),
class: 'btn btn-info'
title: t("actions.index.button", models: klass.model_name.human(count: 2)),
class: "btn btn-info",
})
end
if actions.include? :show
output += link_to(icon(:eye), [:management, conference, record], {
title: t('actions.view.button', model: klass.model_name.human),
class: 'btn btn-info'
title: t("actions.view.button", model: klass.model_name.human),
class: "btn btn-info",
})
end
if actions.include? :edit
output += link_to(icon(:edit), [:edit, :management, conference, record], {
title: t('actions.edit.button', model: klass.model_name.human),
class: 'btn btn-warning'
})
output += link_to(icon(:edit), [:edit, :management, conference, record], {
title: t("actions.edit.button", model: klass.model_name.human),
class: "btn btn-warning",
})
end
if actions.include? :destroy
output += link_to(icon(:trash), [:management, conference, record], {
method: :delete,
data: {confirm: t('actions.are_you_sure')},
title: t('actions.destroy.button', model: klass.model_name.human),
class: 'btn btn-danger'
})
output += link_to(icon(:trash), [:management, conference, record], {
method: :delete,
data: {confirm: t("actions.are_you_sure")},
title: t("actions.destroy.button", model: klass.model_name.human),
class: "btn btn-danger",
})
end
output.html_safe
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

View File

@ -5,26 +5,26 @@ module ConferencesHelper
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
chart_data = (start_date..end_date).map do |date|
chart_data = (start_date..end_date).map { |date|
{
created_at: date,
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|
entry[:all_submissions] = if index == 0
entry[:new_submissions]
else
chart_data[index - 1][:all_submissions] + entry[:new_submissions]
end
entry[:new_submissions]
else
chart_data[index - 1][:all_submissions] + entry[:new_submissions]
end
entry[:all_confirmations] = if index == 0
entry[:new_confirmations]
else
chart_data[index - 1][:all_confirmations] + entry[:new_confirmations]
end
entry[:new_confirmations]
else
chart_data[index - 1][:all_confirmations] + entry[:new_confirmations]
end
end
chart_data
end

View File

@ -1,16 +1,16 @@
module EventsHelper
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?
link_to icon(:user, participant.name),
management_conference_personal_profile_path(participant.personal_profile_id, conference_id: event.conference.id)
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,
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.join(', ').html_safe
}.join(", ").html_safe
end
def participant_names_with_emails(event)
@ -25,13 +25,11 @@ module EventsHelper
end
def participant_names(event)
event.participants.map do |participant|
event.participants.map { |participant|
if participant.personal_profile(event.conference).present?
profile = participant.personal_profile(event.conference)
"#{profile.name}"
else
nil
profile.name.to_s
end
end.compact
}.compact
end
end

View File

@ -1,15 +1,21 @@
# coding: utf-8
class EventMailer < ActionMailer::Base
helper ApplicationHelper
def confirmation_request(event)
@event = event
@event = event.decorate
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,
from: 'program@openfest.org',
subject: I18n.t('event_mailer.acceptance_notification.subject',
conference: @event.conference.title,
submission_type: @event.event_type.name.mb_chars.downcase.to_s,
title: @event.title)
from: "program@openfest.org",
subject: I18n.t("event_mailer.acceptance_notification.subject",
conference: @event.conference.title,
submission_type: @event.event_type.name.mb_chars.downcase.to_s,
title: @event.title)
end
def rejection_notification(event)
@ -17,11 +23,10 @@ class EventMailer < ActionMailer::Base
I18n.locale = @event.proposer.language
mail to: @event.proposer.email,
from: 'program@openfest.org',
subject: I18n.t('event_mailer.rejection_notification.subject',
conference: @event.conference.title,
submission_type: @event.event_type.name.mb_chars.downcase.to_s,
title: @event.title)
from: "program@openfest.org",
subject: I18n.t("event_mailer.rejection_notification.subject",
conference: @event.conference.title,
submission_type: @event.event_type.name.mb_chars.downcase.to_s,
title: @event.title)
end
end

View File

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

View File

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

View File

@ -2,7 +2,7 @@ class CallForParticipation < ActiveRecord::Base
belongs_to :conference
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
save
end
@ -13,14 +13,14 @@ class CallForParticipation < ActiveRecord::Base
end
def open?
self.opens_at.present? and self.opens_at < Time.now
opens_at.present? && (opens_at < Time.now)
end
def closed?
self.closes_at.present? and self.closes_at < Time.now
closes_at.present? && (closes_at < Time.now)
end
def in_progress?
open? and not closed?
open? && !closed?
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 :event_types
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 :volunteer_teams
has_many :volunteers
has_one :call_for_participation, dependent: :destroy
has_many :participants, -> { uniq }, class_name: 'User', through: :events
has_many :participant_profiles, class_name: 'PersonalProfile'
has_one :call_for_participation
has_many :participants, -> { distinct }, class_name: "User", through: :events
has_many :participant_profiles, class_name: "PersonalProfile"
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
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,
reject_if: :all_blank, allow_destroy: true
reject_if: :all_blank, allow_destroy: true
after_create :create_call_for_participation
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 }
end
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 }
end
@ -46,7 +51,7 @@ class Conference < ActiveRecord::Base
end
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
def has_voting_endpoint?
@ -54,8 +59,8 @@ class Conference < ActiveRecord::Base
end
def update_conflict_data!
update_vote_data! or raise ActiveRecord::Rollback
events.all? { |event| event.update_conflict_data(false) } or raise ActiveRecord::Rollback
update_vote_data! || raise(ActiveRecord::Rollback)
events.all? { |event| event.update_conflict_data(false) } || raise(ActiveRecord::Rollback)
end
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
end
private
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)
end
end
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)
end
end
@ -95,38 +99,38 @@ class Conference < ActiveRecord::Base
attr_accessor :conference
def number_of_ballots
@number_of_ballots ||= remote_summary_data['number_of_ballots']
@number_of_ballots ||= remote_summary_data["number_of_ballots"]
end
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
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
def save
conference.transaction do
conference.number_of_ballots_cast = number_of_ballots
conflicts.all?(&:save) or raise ActiveRecord::Rollback
ranking.all?(&:save) or raise ActiveRecord::Rollback
conflicts.all?(&:save) || raise(ActiveRecord::Rollback)
ranking.all?(&:save) || raise(ActiveRecord::Rollback)
conference.touch :vote_data_updated_at
conference.save or raise ActiveRecord::Rollback
conference.save || raise(ActiveRecord::Rollback)
end
end
private
def connection
@connection ||= Faraday.new(url: conference.vote_data_endpoint + '/summary.json',
headers: {'Content-Type' => 'application/json'})
@connection ||= Faraday.new(url: conference.vote_data_endpoint + "/summary.json",
headers: {"Content-Type" => "application/json"})
end
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
end.body)
}.body)
end
class ConflictsForEvent
@ -136,14 +140,13 @@ class Conference < ActiveRecord::Base
def save
@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|
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
class EventRanking
include ActiveModel::Model

View File

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

View File

@ -6,20 +6,24 @@ class Event < ActiveRecord::Base
has_one :slot
has_many :participations, dependent: :destroy
has_many :pending_participations, ->() { pending }, class_name: 'Participation'
has_many :approved_participations, ->() { approved }, class_name: 'Participation'
has_many :pending_participations, -> { pending }, class_name: "Participation"
has_many :approved_participations, -> { approved }, class_name: "Participation"
has_many :participants, through: :approved_participations
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 :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
scope :ranked, -> { where.not(ranked: nil).where.not(votes: nil) }
scope :approved, -> { where(propositions: {status: Proposition.statuses[:approved]})}
scope :approved_joined, -> { joins(:proposition).merge(Proposition.approved) }
validates :conference, presence: true
validates :title, presence: true, length: { maximum: 65 }
validates :title, presence: true, length: {maximum: 65}
validates :abstract, presence: true
validates :description, presence: true
validates :agreement, acceptance: true
@ -61,16 +65,16 @@ class Event < ActiveRecord::Base
language: language,
abstract: abstract,
description: description,
notes: notes
notes: notes,
}
end
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
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)
else
Float::NAN
@ -80,20 +84,20 @@ class Event < ActiveRecord::Base
private
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
end
end
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
end
end
def length_is_within_the_permitted_interval
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
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(:not_confirmed) { |scope, value| scope.joins(:proposition).approved.where(propositions: {confirmed_at: nil}) }
sort_by 'title'
config[:defaults]['sort'] = "#{config[:sort_attributes].first} asc"
sort_by "title"
config[:defaults]["sort"] = "#{config[:sort_attributes].first} asc"
end

View File

@ -1,7 +1,7 @@
class Feedback < ActiveRecord::Base
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

View File

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

View File

@ -1,8 +1,8 @@
class Participation < ActiveRecord::Base
belongs_to :participant, class_name: User
has_one :participant_with_personal_profile, class_name: Participant
belongs_to :participant, class_name: "User"
has_one :participant_with_personal_profile, class_name: "Participant"
belongs_to :event
validates :participant_id, presence: true
scope :approved, ->() { where approved: true }
scope :pending, ->() { where.not approved: true }
scope :approved, -> { where approved: true }
scope :pending, -> { where.not approved: true }
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 :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
def twitter=(handle)
write_attribute :twitter, handle.gsub(/\A@/,'') if handle
write_attribute :twitter, handle.gsub(/\A@/, "") if handle
end
def name

View File

@ -1,10 +1,10 @@
class Proposition < ActiveRecord::Base
belongs_to :proposer, class_name: 'User'
belongs_to :proposer, class_name: "User"
belongs_to :proposable, polymorphic: true, dependent: :destroy
enum status: [:undecided, :approved, :rejected, :backup]
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
def confirm!

View File

@ -1,4 +1,5 @@
class Slot < ActiveRecord::Base
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

View File

@ -9,7 +9,7 @@ class Track < ActiveRecord::Base
translates :name, :description
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
def color

View File

@ -2,17 +2,29 @@ class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable, :confirmable,
:recoverable, :rememberable, :trackable, :validatable
:recoverable, :rememberable, :trackable, :validatable
has_many :personal_profiles, dependent: :destroy
has_many :lectures
has_many :workshops
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 :events_participated_in, through: :participations, source: :event
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 = {})
current_profile = personal_profile(conference)
if current_profile.present?
@ -26,7 +38,7 @@ class User < ActiveRecord::Base
def build_personal_profile(conference, params = {})
if personal_profiles.last.present?
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
new_personal_profile = personal_profiles.build
end

View File

@ -3,44 +3,63 @@ class Volunteer < ActiveRecord::Base
TSHIRT_CUTS = [:unisex, :female]
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 :tshirt_size, inclusion: {in: TSHIRT_SIZES.map(&:to_s)}
validates :tshirt_cut, inclusion: {in: TSHIRT_CUTS.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 :volunteer_teams, presence: true
validates :volunteer_team, presence: true
validates :terms_accepted, acceptance: true
validate :volunteer_teams_belong_to_conference
phony_normalize :phone, default_country_code: 'BG'
phony_normalize :phone, default_country_code: "BG"
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
after_create :send_notification_to_organizers
after_create :send_notification_to_volunteer
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
before_create :assign_confirmation_token
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
def send_notification_to_volunteer
VolunteerMailer.volunteer_notification(self).deliver_later
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
conference_volunteer_teams = conference.volunteer_teams
unless volunteer_teams.all? { |team| conference_volunteer_teams.include? team }
errors.add :volunteer_teams, :invalid_volunteer_team
unless additional_volunteer_teams.all? { |team| conference_volunteer_teams.include? 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

View File

@ -1,8 +1,8 @@
class VolunteerSearch
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'
config[:defaults]['sort'] = "#{config[:sort_attributes].first} asc"
sort_by "name"
config[:defaults]["sort"] = "#{config[:sort_attributes].first} asc"
end

View File

@ -1,6 +1,7 @@
class VolunteerTeam < ActiveRecord::Base
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 :color, presence: true, format: {with: /\A#?[a-f0-9]{6}\z/i}
@ -13,7 +14,7 @@ class VolunteerTeam < ActiveRecord::Base
end
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
def color

View File

@ -2,7 +2,7 @@ class Volunteership < ActiveRecord::Base
include Proposable
belongs_to :volunteer_team
belongs_to :volunteer, class_name: 'User'
belongs_to :volunteer, class_name: "User"
has_one :conference, through: :volunteer_team
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|
json.set! speaker.user_id do
json.extract! speaker, :twitter, :github, :biography, :public_email, :organisation, :last_name, :first_name
json.picture speaker.picture.serializable_hash
json.extract! speaker, :twitter, :github, :biography, :public_email, :organisation, :last_name, :first_name, :name
json.picture rails_blob_url(speaker.picture.variant(resize_to_fill: [100, 100]))
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 %> часа.
<% else %>
Все още не са избрани час и дата за провеждането му.
Определянето на датата и часа на провеждане все още предстои.
<% end %>
Моля, потвърдете участието си възможно най-скоро като кликнете на следния линк:
Моля, потвърдете Вашето участие възможно най-скоро като кликнете на следния линк:
<%= confirm_event_url @event, host: @event.conference.host_name, protocol: 'https' %>
Моля пишете на ofvideo@openfest.org, ако имате специфични изисквания, например:
* да ви осигурим лаптоп за презентация;
* звук от презентацията ви;
* имате нужда да изпозлвате жична или безжична връзка до Internet за презентацията си;
* ползвате macbook.
Отговорете на този email, ако възникнат някакви въпроси.
С приложения QR код към този имейл ще можете да достъпите формуляра за обратна връзка на предложеното от Вас събитие. Моля, включете го в презентацията си.
Поздрави,
Екипът на <%= @event.conference.title %>

View File

@ -1,23 +1,17 @@
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 %>.
<% else %>
It has not been scheduled yet.
<% 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' %>
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;
* 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,
Best regards,
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,
<%= @event.conference.title %> team
Best regards,
<%= @event.conference.title %> Team

View File

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

View File

@ -15,11 +15,11 @@ html
- else
| Clarion
= stylesheet_link_tag "management/application"
= stylesheet_link_tag "management/application", nopush: false
= csrf_meta_tags
body
= render 'layouts/management/navigation'
main
= render 'layouts/management/flash'
== 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'}"
/ = link_to [:management, current_conference, :propositions] do
/ => 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
li.dropdown
= link_to '#', class: 'dropdown-toggle', data: {toggle: 'dropdown'} do

View File

@ -20,9 +20,12 @@
.label.label-info
= event.rank
=< 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
= links_to_event_participants_for(event)
td.action

View File

@ -1,89 +1,123 @@
- personal_profile = speaker.personal_profile(@conference) || speaker.personal_profiles.last
.panel.panel-default
.panel-body
.media
.media-left.hidden-sm.hidden-xs
- if personal_profile.present?
= image_tag personal_profile.picture.medium.url, class: "profile-image"
- else
= icon :user, class: 'fa-5x'
.media-body
.text-center.visible-sm.visible-xs
- if personal_profile.present?
= image_tag personal_profile.picture.medium.url, class: "profile-image img-thumbnail"
- else
= icon :user, class: 'fa-5x'
- 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'
.row
.col-sm-4
.panel.panel-default
- if personal_profile.present?
.panel-image
= image_tag personal_profile.picture
.panel-body
.media
.media-body
h4.media-heading
= personal_profile.name
hr
= simple_format(truncate(personal_profile.biography, omission: '... ', length: 300) { link_to(t('.continue'), [:management, @conference, personal_profile])})
ul.list-group
- 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?
p #{icon :twitter} @#{personal_profile.twitter}
- if personal_profile.public_email.present?
p #{icon :envelope} #{personal_profile.public_email} (#{PersonalProfile.human_attribute_name(:public_email).mb_chars.downcase})
p #{icon :envelope} #{speaker.email} (#{PersonalProfile.human_attribute_name(:email).mb_chars.downcase})
p #{glyph :phone} #{Phony.format(personal_profile.mobile_phone, format: :international)}
- else
h4.media-heading
= speaker.email
hr
p
= t('.no_profile')
=< 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']
li.list-group-item
h5.list-group-item-heading = PersonalProfile.human_attribute_name :twitter
p.list-group-item-text
= link_to "@#{personal_profile.twitter}", "https://twitter.com/#{personal_profile.twitter}", target: '_blank'
- if personal_profile.github.present?
li.list-group-item
h5.list-group-item-heading = PersonalProfile.human_attribute_name :github
p.list-group-item-text
= link_to personal_profile.github, "https://github.com/#{personal_profile.github}", target: '_blank'
.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')
- 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?
h4 = t '.previous_event_propositions'
table.table.table-striped.table-hover.record-table
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|
.panel.panel-default
table.table.table-striped.table-hover.record-table
thead
tr
td = event.title
td.text-center
- if event.ranked?
th.main
= Event.human_attribute_name :title
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
.label.label-info = event.rank
td = event.conference.title
td
span class="label label-lg label-#{proposition_status_class(event.status)}"
= icon(proposition_status_glyph(event.status), t("activerecord.attributes.proposition.statuses.#{event.status}"))
.label class="label-#{rating_label_color(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?
.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
.btn-group.btn-group-sm
= action_buttons event.conference, event, [:show]
- 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')
td.actions
.btn-group.btn-group-sm
= action_buttons event.conference, event, [:show]
- else
p = t '.no_other_event_propositions'

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 -%>
<%- @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 -%>

View File

@ -7,8 +7,8 @@
= Event.model_name.human(count: 2).mb_chars.titleize
small<
| (
= t '.total', current: @events.count, total: current_conference.events.count
=< Event.model_name.human(count: current_conference.events.count)
= t '.total', current: @events.size, total: @conference.events.size
=< Event.model_name.human(count: @conference.events.size)
| )
.row.visible-sm
.col-xs-12
@ -21,73 +21,73 @@
.panel-body
ul.nav.nav-pills.nav-stacked
= 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'
span.badge.pull-right = current_conference.events.count
- current_conference.event_types.each do |event_type|
span.badge.pull-right = @conference.events.size
- @conference.event_types.each do |event_type|
= 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
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-heading
= Event.human_attribute_name(:track)
.panel-body
ul.nav.nav-pills.nav-stacked
= 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'
span.badge.pull-right = current_conference.events.count
- current_conference.tracks.each do |track|
span.badge.pull-right = @conference.events.size
- @conference.tracks.each do |track|
= 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
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-heading
= Event.human_attribute_name(:language)
.panel-body
ul.nav.nav-pills.nav-stacked
= 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'
span.badge.pull-right = current_conference.events.count
span.badge.pull-right = @conference.events.size
- I18n.available_locales.map(&:to_s).each do |language|
= 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}")
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-heading
= Proposition.human_attribute_name(:status)
.panel-body
ul.nav.nav-pills.nav-stacked
= 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'
span.badge.pull-right = current_conference.events.count
span.badge.pull-right = @conference.events.size
- Proposition.statuses.each do |status_name, status_id|
= 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}"
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-heading
= Proposition.human_attribute_name(:confirmed)
.panel-body
ul.nav.nav-pills.nav-stacked
= 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'
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
= 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"
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
= 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"
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
.panel.panel-default
@ -102,5 +102,5 @@
tbody
= render(partial: 'event', collection: @events) || render(partial: 'no_records')
.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')

View File

@ -65,52 +65,88 @@
h3 = Event.human_attribute_name :participants
= render partial: 'speaker', collection: @event.participants
- if @conference.has_vote_results? or @conference.has_voting_endpoint?
.row
.col-xs-12
h2
= t '.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
= Event.model_name.human.mb_chars.capitalize
th
tbody
- if @conference.has_vote_results?
- if @conference.approved_events.count > 2
- @event.conflict_counts.where(right_id: @conference.approved_events.pluck(:id)).includes(:right).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)
- if @conference.start_date.past? || @event.rated?
.row
.col-xs-12
h3 = Event.human_attribute_name :feedbacks
- if @event.rated?
.row
.col-md-10
.panel.panel-default
.panel-heading = t('.comments')
- if @event.feedbacks_with_comment.size > 0
table.table.table-striped
tbody
= render partial: '/management/shared/feedback', collection: @event.feedbacks_with_comment
- 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(@event.average_rating, precision: 2, strip_insignificant_zeros: true) || ''
= t('.total_feedback_grades', total_grades: @event.feedbacks.count, count: @event.feedbacks.count)
- else
p = t '.no_feedback_received'
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
tr
td colspan="20"
= t '.no_approved_events'
- else
tr
td colspan="20"
= t 'management.conferences.vote_results.vote_data_never_updated'
= t 'management.conferences.vote_results.vote_data_never_updated'
= 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')
- 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')
- 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
.row
.col-lg-12
- if f.object.picture.present?
- if f.object.picture.attached?
.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

View File

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

View File

@ -5,65 +5,117 @@
.col-lg-12
h1.page-header
= PersonalProfile.model_name.human.mb_chars.capitalize
.panel.panel-default
.panel-body
.media
.media-left.hidden-sm.hidden-xs
= image_tag @profile.picture.medium.url, class: "profile-image"
.media-body
.text-center.visible-sm.visible-xs
= image_tag @profile.picture.medium.url, class: "profile-image img-thumbnail"
h4.media-heading
= @profile.name
hr
h4 = PersonalProfile.human_attribute_name(:biography)
= simple_format @profile.biography
h4 = t '.contacts'
.row
.col-sm-5.col-md-4
.panel.panel-default
.panel-image
= image_tag @profile.picture
.panel-body
.media
.media-body
h4.media-heading
= @profile.name
hr
= simple_format @profile.biography
ul.list-group
- if @profile.mobile_phone.present?
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?
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?
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?
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?
p = icon :envelope, "#{@profile.public_email} (#{PersonalProfile.human_attribute_name(:public_email).mb_chars.downcase})"
p = icon :envelope, "#{@user.email} (#{User.human_attribute_name(:email).mb_chars.downcase})"
li.list-group-item
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?
h4 = t '.event_propositions'
h2 = t '.talk_history'
.panel.panel-default
- if @user.events_participated_in.any?
table.table.table-striped.table-hover.record-table
thead
tr
th
th.main
= Event.human_attribute_name :title
th.text-center
= Event.human_attribute_name :rating
th.text-center.hidden-md.hidden-sm.hidden-xs
= Event.human_attribute_name :rank
th
th.hidden-md.hidden-sm.hidden-xs
= Event.human_attribute_name :conference
th
th.hidden-md.hidden-sm.hidden-xs
= Event.human_attribute_name :status
th
tbody
- @user.events_participated_in.order(created_at: :desc).each do |event|
tr
td = event.title
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?
.large
.label.label-info = event.rank
td = event.conference.title
td
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
.btn-group.btn-group-sm
= action_buttons event.conference, event, [:show]
.panel-footer
.text-right
.btn-group.btn-group-sm
= action_buttons @conference, @profile, [:edit, :destroy]
h2 = t '.comments_from_the_audience'
- if @user.feedbacks_with_comment.size > 0
.panel.panel-default
table.table.table-striped
tbody
= 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
- if f.object.picture.present?
.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 :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 :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)
@ -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 :previous_experience
= f.input :notes
= f.input :terms_accepted
.panel-footer.text-right
= f.submit class: 'btn btn-primary'

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