From e8ca73a80165aa9ededefcfb062c83e03376e984 Mon Sep 17 00:00:00 2001 From: Godwin Date: Wed, 19 Jul 2017 20:29:25 -0700 Subject: [PATCH] Workshop auth fixes and better error reporting --- Gemfile | 4 +- Rakefile | 6 + app/assets/javascripts/main.js | 6 +- app/assets/stylesheets/_application.scss | 28 +-- app/assets/stylesheets/_settings.scss | 15 +- app/assets/stylesheets/user-mailer.scss | 66 ++++- app/controllers/application_controller.rb | 226 ++++++++---------- app/controllers/conferences_controller.rb | 5 + app/controllers/workshops_controller.rb | 74 +++--- app/helpers/admin_helper.rb | 30 +-- app/helpers/form_helper.rb | 46 +--- app/mailers/user_mailer.rb | 36 ++- app/views/user_mailer/error_report.html.haml | 90 ++++--- app/views/workshops/facilitate.html.haml | 3 +- .../facilitate_request_sent.html.haml | 5 +- app/views/workshops/show.html.haml | 4 +- config/locales/en.yml | 7 +- config/locales/fr.yml | 1 + db/schema.rb | 32 ++- features/workshops.feature | 21 +- 20 files changed, 372 insertions(+), 333 deletions(-) diff --git a/Gemfile b/Gemfile index 0fec91c..43a7aac 100644 --- a/Gemfile +++ b/Gemfile @@ -30,8 +30,8 @@ gem 'launchy' # bundle config local.lingua_franca ../lingua_franca # bundle config local.marmara ../marmara gem 'bikecollectives_core', git: 'https://github.com/bikebike/bikecollectives_core.git', branch: 'master' -gem 'bumbleberry', git: 'https://github.com/bumbleberry/bumbleberry.git', branch: '2017' -gem 'lingua_franca', git: 'https://github.com/lingua-franca/lingua_franca.git', branch: '2017' +gem 'bumbleberry', git: 'https://github.com/bumbleberry/bumbleberry.git', branch: 'master' +gem 'lingua_franca', git: 'https://github.com/lingua-franca/lingua_franca.git', branch: 'master' gem 'marmara', git: 'https://github.com/lingua-franca/marmara.git', branch: 'master' # Bike!Bike! specific stuff diff --git a/Rakefile b/Rakefile index b85cfb9..0c21bcb 100644 --- a/Rakefile +++ b/Rakefile @@ -135,3 +135,9 @@ namespace :cucumber do raise exception unless exception.nil? end end + +namespace :clean do + task requests: :environment do + Request.delete_all(['created_at < ?', DateTime.now - 1.day]) + end +end diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js index db7bb13..9501c0a 100644 --- a/app/assets/javascripts/main.js +++ b/app/assets/javascripts/main.js @@ -1,5 +1,5 @@ (function() { - window.onerror = function(message, url, lineNumber) { + window.onerror = function(message, url, lineNumber, column, errorObj) { //save error and send to server for example. var request = new XMLHttpRequest(); request.open('POST', '/js_error', true); @@ -7,7 +7,9 @@ request.send( 'message=' + encodeURI(message) + '&url=' + encodeURI(url) + - '&lineNumber=' + encodeURI(lineNumber) + + '&line=' + encodeURI(lineNumber) + + '&col=' + encodeURI(column) + + (errorObj && errorObj.stack ? '&stack=' + encodeURI(errorObj.stack) : '') + '&location=' + encodeURI(window.location.href) ); return false; diff --git a/app/assets/stylesheets/_application.scss b/app/assets/stylesheets/_application.scss index 28b5756..eceec87 100644 --- a/app/assets/stylesheets/_application.scss +++ b/app/assets/stylesheets/_application.scss @@ -803,26 +803,22 @@ ul.menu { .message { display: inline-block; - @include font-family(secondary); font-size: 1.25em; padding: 1em 2em; - border: 0.25rem solid $blue; - background-color: rgba($blue, 0.25); + background-color: $text-on-blue; + @include message; } &.error .message { - border-color: $red; - background-color: rgba($red, 0.25); + background-color: $text-on-red; } &.complete .message { - border-color: $green; - background-color: rgba($green, 0.25); + background-color: $text-on-green; } &.warning .message { - border-color: $yellow; - background-color: rgba($yellow, 0.25); + background-color: $text-on-yellow; } } @@ -1732,14 +1728,12 @@ ul.warnings { .info-message { position: relative; min-height: 4em; - border: 0.2em solid rgba(0, 0, 0, 0.1); - background-color: rgba($colour-3, 0.333); + background-color: $text-on-yellow; padding: 1em 1em 1em 4em; - @include font-family(secondary); text-align: left; margin: 1em; width: auto; - @include default-box-shadow(top, 2); + @include message; @include before { content: '!'; @@ -1754,7 +1748,7 @@ ul.warnings { text-align: center; line-height: 1.5em; font-size: 1.5em; - background-color: $colour-3; + background-color: $yellow; @include _(border-radius, 50%); @include default-box-shadow(top, 2); } @@ -1769,15 +1763,15 @@ ul.warnings { } .success-info { - background-color: rgba($colour-5, 0.333); + background-color: $text-on-green; @include before { - background-color: $colour-5; + background-color: $green; } } .error-info { - background-color: rgba($red, 0.333); + background-color: $text-on-red; @include before { background-color: $red; diff --git a/app/assets/stylesheets/_settings.scss b/app/assets/stylesheets/_settings.scss index 470eac1..537f557 100644 --- a/app/assets/stylesheets/_settings.scss +++ b/app/assets/stylesheets/_settings.scss @@ -28,6 +28,13 @@ $yellow: $colour-3; $orange: $colour-4; $green: $colour-5; +$text-on-blue: rgba($blue, 0.333); +$text-on-pink: rgba($pink, 0.333); +$text-on-yellow: rgba($yellow, 0.333); +$text-on-orange: rgba($orange, 0.333); +$text-on-green: rgba($green, 0.333); +$text-on-red: rgba($red, 0.333); + $link-colour: darken($colour-1, 13%); $selected-colour: rgba($blue, 0.5); @@ -105,7 +112,7 @@ $selected-colour: rgba($blue, 0.5); } @mixin text-stroke { - @include _(text-stroke, 1px rgba(0, 0, 0, 0.25)); + @include _(text-stroke, 1px rgba($black, 0.25)); } @mixin button { @@ -174,3 +181,9 @@ $selected-colour: rgba($blue, 0.5); @mixin not-link-like { text-decoration: none; } + +@mixin message { + @include font-family(secondary); + border: 0.2em solid rgba($black, 0.1); + @include default-box-shadow(top, 2); +} diff --git a/app/assets/stylesheets/user-mailer.scss b/app/assets/stylesheets/user-mailer.scss index b312e63..d5678f9 100644 --- a/app/assets/stylesheets/user-mailer.scss +++ b/app/assets/stylesheets/user-mailer.scss @@ -1,5 +1,7 @@ @import "settings"; +$max-table-width: 512px; + body { width: 100% !important; -webkit-text-size-adjust: 100%; @@ -76,6 +78,10 @@ p { p, blockquote { margin: 1em; line-height: 1.3333em; + + &.signature { + margin: 0 0 1em; + } } blockquote { @@ -127,7 +133,7 @@ h1, h2, h3, h4, h5, h6 { table { border-collapse: collapse; - max-width: 512px; + max-width: $max-table-width; min-width: 280px; mso-table-lspace: 0pt; mso-table-rspace: 0pt; @@ -179,16 +185,34 @@ table#ecxbb_full_width { } code { - color: #C33; - font-size: 0.9em; + color: firebrick; + white-space: pre; + margin: 0 0 2em; + display: block; + max-width: $max-table-width; + overflow: auto; + box-sizing: border-box; + + &.signature { + margin: 0 0 1em; + font-size: 1.5em; + color: navy; + text-align: left; + white-space: normal; + overflow: hidden; + } + + &.backtrace { + padding: 1em; + border: 1px solid #CCC; + background-color: lemonchiffon; + } } pre { font-size: 1.5em; padding: 1em; - background-color: #333; - color: antiquewhite; - word-break: break-word; + word-break: break; } .diff, .ecxdiff { @@ -240,8 +264,8 @@ h3 { padding: 10px 15px; margin-left: 20px; border-bottom: 3px solid darken($colour-5, 10%); - -webkit-box-shadow: 0 0.5em 1.5em -0.75em #000; - box-shadow: 0 0.5em 1.5em -0.75em #000; + -webkit-box-shadow: 0 0.5em 1.5em -0.75em $black; + box-shadow: 0 0.5em 1.5em -0.75em $black; cursor: pointer !important; } @@ -266,3 +290,29 @@ h3 { } } } + +.request-details { + width: 100%; + max-width: 800px; + overflow: auto; + + th, td { + border: 1px solid #CCC; + font-size: 1em; + padding: 0.5em; + vertical-align: top; + font-family: monospace; + } + + table tr { + background-color: aliceblue; + } + + .spacer { + background-color: transparent; + + td { + border: 0; + } + } +} diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 10b78b0..5dbd0cb 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,35 +1,53 @@ class ApplicationController < BaseController protect_from_forgery with: :exception, :except => [:do_confirm, :js_error, :admin_update] - before_filter :capture_page_info + before_filter :application_setup + after_filter :capture_page_info helper_method :protect, :policies - # @@test_host - # @@test_location - def default_url_options { host: "#{request.protocol}#{request.host_with_port}", trailing_slash: true } end def capture_page_info - # capture request info in case an error occurs - # if request.method == "GET" && (params[:controller] != 'application' || params[:action] != 'contact') - # session[:last_request] - # request_info = { - # 'params' => params, - # 'request' => { - # 'remote_ip' => request.remote_ip, - # 'uuid' => request.uuid, - # 'original_url' => request.original_url, - # 'env' => Hash.new - # } - # } - # request.env.each do |key, value| - # request_info['request']['env'][key.to_s] = value.to_s - # end - # # session['request_info'] = request_info - # end + capture_response unless @user_type == :potential_bot || @user_type == :bot + end + + def capture_response(response_code = nil) + Request.create( + request_id: request.uuid, + session_id: session.id, + application: :bikebike, + response: (response_code || response.code || 0).to_i, + data: { + user: logged_in? ? current_user.id : nil, + params: @original_params || params, + remote_ip: request.remote_ip, + request_method: request.method, + url: request.original_url, + user_agent: request.user_agent, + language: request.env['HTTP_ACCEPT_LANGUAGE'], + cookies: request.env['HTTP_COOKIE'], + requested_with: request.env['HTTP_X_REQUESTED_WITH'] + }) + + @error_reports.each { |report| report_on(report) } if @error_reports + end + + def report_on(report) + return if Rails.env.development? || Rails.env.test? + send_mail(:error_report, report.signature) + end + + def application_setup + if request.user_agent =~ /Googlebot/ + @user_type = :bot + elsif request.url =~ /^.*\.php(\?.*)?$/ + @user_type = :potential_bot + else + @user_type = :normal + end # get the current conferences and set them globally status_hierarchy = { @@ -82,51 +100,6 @@ class ApplicationController < BaseController @is_policy_page = true end - def js_error - # send and email if this is production - report = "A JavaScript error has occurred on #{params[:location]}" - if params[:location] == params[:url] - report += " on line #{params[:lineNumber]}" - else - report += " in #{params[:url]}:#{params[:lineNumber]}" - end - - begin - # log the error - logger.info "A JavaScript error has occurred on #{params[:location]}:#{params[:lineNumber]}: #{params[:message]}" - - if Rails.env.preview? || Rails.env.production? - # don't worry about bots - unless request.user_agent =~ /Googlebot/ - request_info = { - 'remote_ip' => request.remote_ip, - 'uuid' => request.uuid, - 'original_url' => request.original_url, - 'env' => Hash.new - } - request.env.each do |key, value| - request_info['env'][key.to_s] = value.to_s - end - - send_mail(:error_report, - "A JavaScript error has occurred", - report, - params[:message], - nil, - request_info, - params, - current_user, - Time.now.strftime("%d/%m/%Y %H:%M") - ) - end - end - rescue Exception => exception2 - logger.info exception2.to_s - logger.info exception2.backtrace.join("\n") - end - render json: {} - end - def confirmation_sent(user) template = 'login_confirmation_sent' @page_title ||= 'page_titles.403.Please_Check_Email' @@ -150,6 +123,7 @@ class ApplicationController < BaseController def locale_not_available!(locale = nil) set_default_locale + @original_params = params.clone params[:_original_action] = params[:action] params[:action] = 'error-locale-not-available' @page_title = 'page_titles.404.Locale_Not_Available' @@ -167,6 +141,28 @@ class ApplicationController < BaseController render 'application/locale_not_available', status: 404 end + def on_error(report, exception = nil) + @error_reports ||= [] + @error_reports << report + logger.info report.backtrace + + raise exception if exception.present? && Rails.env.development? + end + + def js_error + stack = params[:stack] || "#{params[:message]}\n\tat #{params[:url] || params[:location]}:#{params[:line]}:#{params[:col]}" + requests = Request.where(session_id: session.id).order("created_at DESC") + on_error( + Report.create( + request_id: requests.first.request_id, + signature: params[:message], + severity: :error, + source: :javascript, + backtrace: stack)) + + render json: {} + end + unless Rails.env.test? rescue_from StandardError do |exception| handle_exception exception @@ -177,37 +173,31 @@ class ApplicationController < BaseController end def handle_exception(exception) - # log the error - logger.info exception.to_s - logger.info exception.backtrace.join("\n") - - # send and email if this is production - if Rails.env.preview? || Rails.env.production? - suppress(Exception) do - request_info = { - 'remote_ip' => request.remote_ip, - 'uuid' => request.uuid, - 'original_url' => request.original_url, - 'env' => Hash.new - } - request.env.each do |key, value| - request_info['env'][key.to_s] = value.to_s - end - send_mail(:error_report, - "An error has occurred in #{Rails.env}", - nil, - exception.to_s, - exception.backtrace.join("\n"), - request_info, - params, - current_user, - Time.now.strftime("%d/%m/%Y %H:%M") - ) - end - end + # remove memory location from anonymous classes so tat we have a common signature + classMatcher = /#<(.*?):0x[0-9a-f]+>/ + message = exception.message + message.gsub!(classMatcher, '\1') while message =~ classMatcher + stack = ([message] + exception.backtrace).join("\n ") + + on_error( + Report.create( + request_id: request.uuid, + signature: message, + severity: :error, + source: :application, + backtrace: stack), exception) + end - # raise the error if we are in development so that we can debug it - raise exception if Rails.env.development? + def i18n_exception(str, exception, locale, key) + message = "#{exception.class.name}: #{exception.to_s}" + stack = "#{message}\n #{caller.join("\n ")}" + on_error( + Report.create( + request_id: request.uuid, + signature: message, + severity: :error, + source: :i18n, + backtrace: stack)) end def protect(&block) @@ -291,6 +281,7 @@ class ApplicationController < BaseController end def error_404(args = {}) + @original_params = params.clone params[:_original_action] = params[:action] params[:action] = 'error-404' @page_title = 'page_titles.404.Page_Not_Found' @@ -308,6 +299,7 @@ class ApplicationController < BaseController @template = template @page_title ||= 'page_titles.403.Access_Denied' @main_title ||= @page_title + @original_params = params.clone params[:_original_action] = params[:action] params[:action] = 'error-403' @@ -317,11 +309,13 @@ class ApplicationController < BaseController def error_500(exception = nil) @page_title = 'page_titles.500.An_Error_Occurred' @main_title = 'error.500.title' + @original_params = params.clone params[:_original_action] = params[:action] params[:action] = 'error-500' @exception = exception super(exception) + capture_response(500) end def on_translation_change(object, data, locale, translator_id) @@ -353,39 +347,6 @@ class ApplicationController < BaseController end end - def i18n_exception(str, exception, locale, key) - # log it - logger.info "Missing translation found for: #{key}" - - # send an email if this is production - if Rails.env.preview? || Rails.env.production? - begin - request_info = { - 'remote_ip' => request.remote_ip, - 'uuid' => request.uuid, - 'original_url' => request.original_url, - 'env' => Hash.new - } - request.env.each do |key, value| - request_info['env'][key.to_s] = value.to_s - end - send_mail(:error_report, - "A missing translation found in #{Rails.env}", - "

A translation for #{key} in #{locale.to_s} was found. The text that was rendered to the user was:

#{str || 'nil'}
", - exception.to_s, - nil, - request_info, - params, - current_user.id, - Time.now.strftime("%d/%m/%Y %H:%M") - ) - rescue Exception => exception2 - logger.info exception2.to_s - logger.info exception2.backtrace.join("\n") - end - end - end - def set_success_message(message, is_ajax = false) if is_ajax @success_message = message @@ -613,10 +574,15 @@ class ApplicationController < BaseController end def set_conference_registration! - @registration = set_conference_registration + set_conference_registration raise ActiveRecord::PremissionDenied unless @registration.present? end + def ensure_registration_is_complete! + set_conference_registration! + raise ActiveRecord::PremissionDenied unless @registration.registered? + end + def set_or_create_conference_registration set_conference_registration return @registration if @registration.present? diff --git a/app/controllers/conferences_controller.rb b/app/controllers/conferences_controller.rb index ff88956..fc04a8d 100644 --- a/app/controllers/conferences_controller.rb +++ b/app/controllers/conferences_controller.rb @@ -82,6 +82,11 @@ class ConferencesController < ApplicationController # get the current step @step = current_registration_step(@this_conference, current_user) + if @update_status.nil? && flash[:status_message].present? + @update_status = flash[:status_message][:status] + @update_message = flash[:status_message][:message] + end + if @step == :payment_form && (params[:token].present? || @test_token.present?) result = paypal_payment_confirm(@this_conference, current_user, params) data_to_instance_variables(result) diff --git a/app/controllers/workshops_controller.rb b/app/controllers/workshops_controller.rb index fd60519..c96628c 100644 --- a/app/controllers/workshops_controller.rb +++ b/app/controllers/workshops_controller.rb @@ -26,7 +26,8 @@ class WorkshopsController < ApplicationController def create_workshop set_conference - set_conference_registration! + ensure_registration_is_complete! + @workshop = Workshop.new @languages = [I18n.locale.to_sym] @needs = [] @@ -47,7 +48,7 @@ class WorkshopsController < ApplicationController def edit_workshop set_conference - set_conference_registration! + ensure_registration_is_complete! @workshop = Workshop.find_by_id_and_conference_id(params[:workshop_id], @this_conference.id) return do_404 unless @workshop.present? @@ -82,7 +83,7 @@ class WorkshopsController < ApplicationController def delete_workshop set_conference - set_conference_registration! + ensure_registration_is_complete! @workshop = Workshop.find_by_id_and_conference_id(params[:workshop_id], @this_conference.id) return do_404 unless @workshop.present? @@ -95,9 +96,9 @@ class WorkshopsController < ApplicationController @workshop.destroy end - return redirect_to register_step_path(@this_conference.slug, 'workshops') + return redirect_to workshops_path(@this_conference.slug) end - return redirect_to view_workshop_url(@this_conference.slug, @workshop.id) + return redirect_to view_workshop_path(@this_conference.slug, @workshop.id) end @register_template = :workshops @@ -106,13 +107,13 @@ class WorkshopsController < ApplicationController def save_workshop set_conference - set_conference_registration! + ensure_registration_is_complete! if params[:button].to_sym != :save if params[:workshop_id].present? - return redirect_to view_workshop_url(@this_conference.slug, params[:workshop_id]) + return redirect_to view_workshop_path(@this_conference.slug, params[:workshop_id]) end - return redirect_to register_step_path(@this_conference.slug, 'workshops') + return redirect_to workshops_path(@this_conference.slug) end if params[:workshop_id].present? @@ -120,8 +121,8 @@ class WorkshopsController < ApplicationController return do_404 unless workshop.present? can_edit = workshop.can_edit?(current_user) else - workshop = Workshop.new(:conference_id => @this_conference.id) - workshop.workshop_facilitators = [WorkshopFacilitator.new(:user_id => current_user.id, :role => :creator)] + workshop = Workshop.new(conference_id: @this_conference.id) + workshop.workshop_facilitators = [WorkshopFacilitator.new(user_id: current_user.id, role: :creator)] can_edit = true end @@ -157,27 +158,27 @@ class WorkshopsController < ApplicationController workshop.save # Rouge nil facilitators have been know to be created, just destroy them here now - WorkshopFacilitator.where(:user_id => nil).destroy_all + WorkshopFacilitator.where(user_id: nil).destroy_all else return do_403 end - redirect_to view_workshop_url(@this_conference.slug, workshop.id) + redirect_to view_workshop_path(@this_conference.slug, workshop.id) end def toggle_workshop_interest set_conference - set_conference_registration! + ensure_registration_is_complete! workshop = Workshop.find_by_id_and_conference_id(params[:workshop_id], @this_conference.id) return do_404 unless workshop # save the current state interested = workshop.interested? current_user # remove all associated fields - WorkshopInterest.delete_all(:workshop_id => workshop.id, :user_id => current_user.id) + WorkshopInterest.delete_all(workshop_id: workshop.id, user_id: current_user.id) # creat the new interest row if we weren't interested before - WorkshopInterest.create(:workshop_id => workshop.id, :user_id => current_user.id) unless interested + WorkshopInterest.create(workshop_id: workshop.id, user_id: current_user.id) unless interested if request.xhr? render json: [ @@ -192,13 +193,13 @@ class WorkshopsController < ApplicationController ] else # go back to the workshop - redirect_to view_workshop_url(@this_conference.slug, workshop.id) + redirect_to view_workshop_path(@this_conference.slug, workshop.id) end end def facilitate_workshop set_conference - set_conference_registration! + ensure_registration_is_complete! @workshop = Workshop.find_by_id_and_conference_id(params[:workshop_id], @this_conference.id) return do_404 unless @workshop return do_403 if @workshop.facilitator?(current_user) || !current_user @@ -209,7 +210,7 @@ class WorkshopsController < ApplicationController def facilitate_request set_conference - set_conference_registration! + ensure_registration_is_complete! workshop = Workshop.find_by_id_and_conference_id(params[:workshop_id], @this_conference.id) return do_404 unless workshop return do_403 if workshop.facilitator?(current_user) || !current_user @@ -219,12 +220,12 @@ class WorkshopsController < ApplicationController send_mail(:workshop_facilitator_request, workshop.id, current_user.id, params[:message]) - redirect_to sent_facilitate_workshop_url(@this_conference.slug, workshop.id) + redirect_to sent_facilitate_workshop_path(@this_conference.slug, workshop.id) end def sent_facilitate_request set_conference - set_conference_registration! + ensure_registration_is_complete! @workshop = Workshop.find_by_id_and_conference_id(params[:workshop_id], @this_conference.id) return do_404 unless @workshop return do_403 unless @workshop.requested_collaborator?(current_user) @@ -236,7 +237,7 @@ class WorkshopsController < ApplicationController def approve_facilitate_request return do_403 unless logged_in? set_conference - set_conference_registration! + ensure_registration_is_complete! workshop = Workshop.find_by_id_and_conference_id(params[:workshop_id], @this_conference.id) return do_404 unless workshop.present? @@ -253,24 +254,24 @@ class WorkshopsController < ApplicationController LinguaFranca.with_locale(user.locale) do send_mail(:workshop_facilitator_request_approved, workshop.id, user.id) end - return redirect_to view_workshop_url(@this_conference.slug, workshop.id) + return redirect_to view_workshop_path(@this_conference.slug, workshop.id) end when :deny if workshop.active_facilitator?(current_user) && workshop.requested_collaborator?(User.find(user_id)) WorkshopFacilitator.delete_all( - :workshop_id => workshop.id, - :user_id => user_id) + workshop_id: workshop.id, + user_id: user_id) LinguaFranca.with_locale user.locale do send_mail(:workshop_facilitator_request_denied, workshop.id, user.id) end - return redirect_to view_workshop_url(@this_conference.slug, workshop.id) + return redirect_to view_workshop_path(@this_conference.slug, workshop.id) end when :remove if workshop.can_remove?(current_user, user) WorkshopFacilitator.delete_all( - :workshop_id => workshop.id, - :user_id => user_id) - return redirect_to view_workshop_url(@this_conference.slug, workshop.id) + workshop_id: workshop.id, + user_id: user_id) + return redirect_to view_workshop_path(@this_conference.slug, workshop.id) end when :switch_ownership if workshop.creator?(current_user) @@ -282,7 +283,7 @@ class WorkshopsController < ApplicationController workshop.id, user_id) f.role = :creator f.save - return redirect_to view_workshop_url(@this_conference.slug, workshop.id) + return redirect_to view_workshop_path(@this_conference.slug, workshop.id) end end @@ -291,7 +292,7 @@ class WorkshopsController < ApplicationController def add_workshop_facilitator set_conference - set_conference_registration! + ensure_registration_is_complete! user = User.find_user(params[:email]) @@ -313,12 +314,12 @@ class WorkshopsController < ApplicationController end end - return redirect_to view_workshop_url(@this_conference.slug, params[:workshop_id]) + return redirect_to view_workshop_path(@this_conference.slug, params[:workshop_id]) end def add_comment set_conference - set_conference_registration! + ensure_registration_is_complete! workshop = Workshop.find_by_id_and_conference_id(params[:workshop_id], @this_conference.id) return do_404 unless workshop && current_user @@ -335,7 +336,7 @@ class WorkshopsController < ApplicationController elsif params[:button] = 'add_comment' new_comment = workshop.add_comment(current_user, params[:comment]) - workshop.active_facilitators.each do | u | + workshop.active_facilitators.each do |u| unless u.id == current_user.id LinguaFranca.with_locale u.locale do send_mail(:workshop_comment, workshop.id, new_comment.id, u.id) @@ -346,20 +347,21 @@ class WorkshopsController < ApplicationController return do_404 end - return redirect_to view_workshop_url(@this_conference.slug, workshop.id, anchor: "comment-#{new_comment.id}") + return redirect_to view_workshop_path(@this_conference.slug, workshop.id, anchor: "comment-#{new_comment.id}") end rescue_from ActiveRecord::PremissionDenied do |exception| if !@this_conference.can_register? do_404 elsif logged_in? - redirect_to 'conferences/register' + flash[:status_message] = { message: :registration_required, status: :warning } + redirect_to register_path(@this_conference.slug) else @register_template = :confirm_email @page_title = "articles.conference_registration.headings.#{@this_conference.registration_status == :open ? '': 'Pre_'}Registration_Details" @main_title = "articles.conference_registration.headings.#{@this_conference.registration_status == :open ? '': 'Pre_'}Register" @main_title_vars = { vars: { title: @this_conference.title } } - render 'conferences/register' + render register_path(@this_conference.slug) end end diff --git a/app/helpers/admin_helper.rb b/app/helpers/admin_helper.rb index 848354c..d8bfb5b 100644 --- a/app/helpers/admin_helper.rb +++ b/app/helpers/admin_helper.rb @@ -59,8 +59,8 @@ module AdminHelper def get_administration_group(administration_step) admin_step = administration_step.to_sym admin_step = administration_sub_steps[admin_step] if administration_sub_steps[admin_step].present? - administration_steps.each do | group, steps | - steps.each do | step | + administration_steps.each do |group, steps| + steps.each do |step| return group if step == admin_step end end @@ -75,35 +75,35 @@ module AdminHelper case to.to_sym when :registered - ConferenceRegistration.where(conference_id: conference.id).each do | r | - users << r.user if ((r.steps_completed || []).include? 'questions') && r.user.present? && r.is_attending != 'n' + ConferenceRegistration.where(conference_id: conference.id).each do |r| + users << r.user if r.registered? && r.user.present? && r.attending? end when :pre_registered - ConferenceRegistration.where(conference_id: conference.id).each do | r | - users << r.user if registration_status(r) == :preregistered && r.is_attending != 'n' + ConferenceRegistration.where(conference_id: conference.id).each do |r| + users << r.user if r.attending? end when :workshop_facilitators user_hash = {} - Workshop.where(conference_id: conference.id).each do | w | - w.active_facilitators.each do | u | + Workshop.where(conference_id: conference.id).each do |w| + w.active_facilitators.each do |u| user_hash[u.id] ||= u if u.present? end end users = user_hash.values when :unregistered - ConferenceRegistration.where(conference_id: conference.id).each do | r | - users << r.user if registration_status(r) == :unregistered && r.is_attending != 'n' + ConferenceRegistration.where(conference_id: conference.id).each do |r| + users << r.user if !r.registered? && r.attending? end when :housing_providers - ConferenceRegistration.where(conference_id: conference.id, can_provide_housing: true).each do | r | + ConferenceRegistration.where(conference_id: conference.id, can_provide_housing: true).each do |r| users << r.user if r.user.present? end when :guests - ConferenceRegistration.where(conference_id: conference.id, housing: 'house').each do | r | - users << r.user if r.user.present? && r.is_attending != 'n' + ConferenceRegistration.where(conference_id: conference.id, housing: 'house').each do |r| + users << r.user if r.user.present? && r.attending? end when :all - User.all.each do | u | + User.all.each do |u| users << u if u.present? && (u.is_subscribed.nil? || u.is_subscribed) end end @@ -151,7 +151,7 @@ module AdminHelper end end - (((((@schedule[@day] || {})[:times] || {})[@time] || {})[:item] || {})[:workshops] || {}).each do | l, w | + (((((@schedule[@day] || {})[:times] || {})[@time] || {})[:item] || {})[:workshops] || {}).each do |l, w| if w[:workshop].id != workshop.id f_a = w[:workshop].active_facilitators.map { | f | f.id } f_b = workshop.active_facilitators.map { | f | f.id } diff --git a/app/helpers/form_helper.rb b/app/helpers/form_helper.rb index a488b9e..a9ccbc2 100644 --- a/app/helpers/form_helper.rb +++ b/app/helpers/form_helper.rb @@ -704,58 +704,14 @@ module FormHelper return options end - def registration_step_menu - steps = current_registration_steps(@registration) - return '' unless steps.present? && steps.length > 1 - - pre_registration_steps = '' - post_registration_steps = '' - post_registration = false - - steps.each do |step| - text = _"articles.conference_registration.headings.#{step[:name].to_s}" - - if step[:name] == :workshops - post_registration = true - end - - h = content_tag :li, class: [step[:enabled] ? :enabled : nil, @register_template == step[:name] ? :current : nil, post_registration ? :post : :pre].compact do - if step[:enabled] - content_tag :div, (link_to text, register_step_path(@this_conference.slug, step[:name])).html_safe, class: :step - else - content_tag :div, text, class: :step - end - end - - if post_registration - post_registration_steps += h.html_safe - else - pre_registration_steps += h.html_safe - end - end - - html = ( - row class: 'flow-steps' do - columns do - (content_tag :ul, id: 'registration-steps' do - pre_registration_steps.html_safe + - post_registration_steps.html_safe - end).html_safe - end - end - ) - - return html.html_safe - end - def broadcast_options(conference = nil) conference ||= @this_conference || @conference options = [ :registered, :pre_registered, - :workshop_facilitators, :unregistered, + :workshop_facilitators, :housing_providers, :guests, :all diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb index e4ce88d..8f67c6d 100644 --- a/app/mailers/user_mailer.rb +++ b/app/mailers/user_mailer.rb @@ -123,15 +123,29 @@ class UserMailer < ActionMailer::Base mail to: @user.named_email, subject: clean_subject(subject) end - def error_report(subject, message, report, exception, request, params, user, time = nil) - @message = message - @report = report - @exception = exception - @request = request - @params = params - @time = time - @user = User.find(user) if user.present? - mail to: 'goodgodwin@hotmail.com', subject: clean_subject(subject) + def error_report(report_signature) + @reports = Report.where(signature: report_signature).order('created_at DESC') + @report = @reports.first + + return unless @report.present? + + @title = case @report.source.to_sym + when :javascript + "JavaScript fatal report" + when :i18n + "Missing translation report" + else + "Fatal report" + end + subject = "#{@title}: #{report_signature}" + + @request = Request.find_by_request_id(@report.request_id) + + return unless @request.present? + + @user = User.find(@request.data['user'].to_i) if @request.data['user'].present? + + mail to: administrators, subject: clean_subject(subject) end def contact(from, subject, message, email_list) @@ -172,4 +186,8 @@ class UserMailer < ActionMailer::Base @subject = subject return subject end + + def administrators + User.where(role: :administrator).map(&:named_email).join(',') + end end diff --git a/app/views/user_mailer/error_report.html.haml b/app/views/user_mailer/error_report.html.haml index f43ff07..7227268 100644 --- a/app/views/user_mailer/error_report.html.haml +++ b/app/views/user_mailer/error_report.html.haml @@ -1,53 +1,43 @@ -%p=@message.html_safe if @message.present? -%pre=@report +%h1=@title -- if @exception.present? - %h1 Backtrace - %pre=@exception +%h2 Error details +%code.signature{class: "#{@report.source}-signature"} + = "#{@report.signature}" +%code.backtrace{class: "#{@report.source}-backtrace"}=@report.backtrace -%h1 Details -%table.error-report - %tr - %th Key - %th Value - %tr - %td User ID - %td=@user.present? ? @user.id : '' - %tr - %td User Name - %td=@user.present? ? @user.name : '' - %tr - %td User Email - %td=@user.present? ? @user.email : '' - %tr - %td IP Address - %td=@request['remote_ip'] - %tr - %td UUID - %td=@request['uuid'] - %tr - %td URL - %td=@request['original_url'] - %tr - %td Time - %td=@time - -%h1 Params -%table.error-report - %tr - %th Key - %th Value - - @params.each do | key, value | - %tr - %td=key - %td=value - -%h1 Request Environment -%table.error-report - %tr - %th Key - %th Value - - @request['env'].each do | key, value | +%h2 Request details +.request-details + %table %tr - %td=key - %td=value + %th Response code + %td=@request.response + - @request.data.each do |key, value| + - if key.to_s == 'user' + - if @user + %tr + %th User ID + %td=@user.id + %tr + %th User Email + %td=@user.email + %tr + %th User Name + %td=@user.firstname + - else + %tr + %th User ID + %td NULL + %tr.spacer + %td{ colspan: 2 } + - else + %tr + %th=key.to_s.titlecase + %td + - if value.is_a?(Hash) + %table + - value.each do |k, v| + %tr + %th=k + %td=v + - else + = value.to_s diff --git a/app/views/workshops/facilitate.html.haml b/app/views/workshops/facilitate.html.haml index d127d4e..843bc57 100644 --- a/app/views/workshops/facilitate.html.haml +++ b/app/views/workshops/facilitate.html.haml @@ -1,11 +1,10 @@ = render 'conferences/page_header', page_key: 'Facilitate_Workshop' %article = row do - = form_tag facilitate_workshop_request_path(@this_conference.slug, @workshop.id), class: 'composition' do + = form_tag facilitate_workshop_request_path(@this_conference.slug, @workshop.id), class: :composition do = (hidden_field_tag :workshop_id, @workshop.id) if @workshop = columns(medium: 12) do %h2=_'articles.workshops.headings.facilitate', "Request to Facilitate ‘#{@workshop.title}’", vars: { workshop_title: @workshop.title } - = registration_step_menu =_('articles.workshops.paragraphs.facilitate_request','Please tell the current workshop facilitators who you are, why you want to help facilitate the workshop, and how you think you will help make the workshop better. All of the current facilitators will be emailed and they may ask more questions before approving or denying your request. Please note that this will reveal your email address to the facilitators.') .text-area-field.input-field = label_tag :message diff --git a/app/views/workshops/facilitate_request_sent.html.haml b/app/views/workshops/facilitate_request_sent.html.haml index 2527ccd..9d2e38f 100644 --- a/app/views/workshops/facilitate_request_sent.html.haml +++ b/app/views/workshops/facilitate_request_sent.html.haml @@ -3,10 +3,9 @@ %article = row do %h2=_'page_titles.conferences.Facilitate_Workshop' - = registration_step_menu = columns(medium: 12) do =_('articles.workshops.paragraphs.facilitate_request_sent','Your request has been sent. You will receive an email once your request is approved or denied or if the current facilitators have any questions.') = columns(medium: 12) do .actions - = link_to (_'actions.workshops.View'), view_workshop_path(@this_conference.slug, @workshop.id), :class => 'button' - = link_to (_'actions.workshops.View_All'), register_step_path(@this_conference.slug, :wokshops), :class => 'button' + = link_to (_'actions.workshops.View'), view_workshop_path(@this_conference.slug, @workshop.id), class: :button + = link_to (_'actions.workshops.View_All'), register_step_path(@this_conference.slug, :wokshops), class: :button diff --git a/app/views/workshops/show.html.haml b/app/views/workshops/show.html.haml index a41ce41..e0a4dad 100644 --- a/app/views/workshops/show.html.haml +++ b/app/views/workshops/show.html.haml @@ -11,5 +11,5 @@ = row do = columns(medium: 12) do .actions.center - = (link_to (_'actions.workshops.Edit'), edit_workshop_path(@this_conference.slug, @workshop.id), :class => [:button, :modify]) if @workshop.can_edit?(current_user) - = (link_to (_'actions.workshops.Delete'), delete_workshop_path(@this_conference.slug, @workshop.id), :class => 'button delete') if @workshop.can_delete?(current_user) + = (link_to (_'actions.workshops.Edit'), edit_workshop_path(@this_conference.slug, @workshop.id), class: [:button, :modify]) if @workshop.can_edit?(current_user) + = (link_to (_'actions.workshops.Delete'), delete_workshop_path(@this_conference.slug, @workshop.id), class: 'button delete') if @workshop.can_delete?(current_user) diff --git a/config/locales/en.yml b/config/locales/en.yml index 4c01d5b..48b0588 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1687,6 +1687,7 @@ en: payment_pending: Thank you! Your payment is currently pending. companion_unregistered: Your companion has not yet registered. Please ensure that they do to guarantee you are housed together. + registration_required: Please complete your registration step_names: group_ride: Group ride? hosting_other: Other @@ -2441,13 +2442,13 @@ en: add: Add new options: send_to: - registered: Everyone who has registered - pre_registered: Everyone who has pre-registered or registered + registered: Everyone who has completed their registration + pre_registered: Everyone who has begun or completed the registration process unregistered: Everyone who has not completed their registration workshop_facilitators: All workshop facilitators housing_providers: Housing providers guests: Everyone who has requested housing - all: Everyone + all: Everyone who has ever registered for a conference conferences: types: annual: Annual Bike!Bike! diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 03eaa71..c408787 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -1516,6 +1516,7 @@ fr: companion_unregistered: Votre partenaire ne s’est pas encore inscrit ou inscrite. Veuillez vous assurer qu’il ou elle le fasse pour que vous soyez hébergés ensemble. + registration_required: Veuillez compléter votre inscription policy: headings: The_Agreement: L’ Accord diff --git a/db/schema.rb b/db/schema.rb index 311ffa5..1f6fe54 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170613022506) do +ActiveRecord::Schema.define(version: 20170719024801) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -361,6 +361,26 @@ ActiveRecord::Schema.define(version: 20170613022506) do t.datetime "updated_at" end + create_table "reports", force: :cascade do |t| + t.string "request_id" + t.string "signature" + t.string "severity" + t.string "source" + t.string "backtrace" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + create_table "requests", force: :cascade do |t| + t.string "request_id" + t.string "session_id" + t.json "data" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "application" + t.integer "response" + end + create_table "sessions", force: :cascade do |t| t.string "session_id", null: false t.text "data" @@ -380,11 +400,11 @@ ActiveRecord::Schema.define(version: 20170613022506) do end create_table "translation_records", force: :cascade do |t| - t.string "locale" - t.integer "translator_id" - t.string "key" - t.text "value" - t.datetime "created_at" + t.string "locale" + t.integer "translator_id" + t.string "key" + t.text "value" + t.date "created_at" end create_table "translations", force: :cascade do |t| diff --git a/features/workshops.feature b/features/workshops.feature index 3057d7f..cca8a9c 100644 --- a/features/workshops.feature +++ b/features/workshops.feature @@ -3,12 +3,11 @@ Feature: Workshops Given there is an upcoming conference And registration is open And I am logged in - And registered for the conference + And registered And on the registration page Then I should see 'New Workshop' And should see 'View Workshops' - Then I should see 'New Workshop' But I should not see any workshops When I click on 'New Workshop' @@ -66,6 +65,24 @@ Feature: Workshops Then I should see 'New Workshop' But I should not see any workshops + Scenario: Un-Registered users cannot create workshops + Given there is an upcoming conference + And the conference has workshop info copy + And registration is open + And I am logged in + And on the landing page + + Then I should see 'View Workshops' + + When I click on 'View Workshops' + Then I should be on the workshops page + And I should see 'New Workshop' + But I should not see any workshops + + When I click on 'New Workshop' + Then I should see 'Please complete your registration' + But I should not see 'Create a Workshop' + Scenario: Users can comment on and translate their own workshops Given that there is an upcoming conference And registration is open