diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index a4542c19..e4754e1e 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -300,7 +300,7 @@ Lint/UnusedBlockArgument: # Offense count: 136 Metrics/AbcSize: - Max: 86 + Max: 98 # Offense count: 258 # Configuration parameters: CountComments, ExcludedMethods. @@ -320,7 +320,7 @@ Metrics/LineLength: # Offense count: 133 # Configuration parameters: CountComments. Metrics/MethodLength: - Max: 56 + Max: 63 # Offense count: 4 # Configuration parameters: CountComments. diff --git a/Dockerfile b/Dockerfile index 4cc5c04c..6cf50964 100644 --- a/Dockerfile +++ b/Dockerfile @@ -38,7 +38,10 @@ RUN chown -R osem.root /osem/ && \ # data directory is used to cache the secret key in a file ENV DATA_DIR /data RUN install -d -m 0770 -o osem -g root $DATA_DIR -VOLUME ["$DATA_DIR"] +# data persistence for uploaded files (logos, other pictures, etc) +RUN mkdir 0775 -p /osem/tmp/cache /osem/tmp/uploads/ && \ + chown -R osem.osem /osem/tmp/ +VOLUME ["$DATA_DIR", "/osem/tmp/uploads/", "/osem/public/system/"] USER osem EXPOSE 9292 diff --git a/Gemfile b/Gemfile index e624a1f7..9be883a3 100644 --- a/Gemfile +++ b/Gemfile @@ -209,6 +209,9 @@ gem 'sprockets-rails' # for multiple speakers select on proposal/event forms gem 'selectize-rails' +# For collecting performance data +gem 'skylight' + # Nokogiri < 1.8.1 is subject to: # CVE-2017-0663, CVE-2017-7375, CVE-2017-7376, CVE-2017-9047, CVE-2017-9048, # CVE-2017-9049, CVE-2017-9050 diff --git a/Gemfile.lock b/Gemfile.lock index 0f64b0e8..9e07eb20 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -297,7 +297,7 @@ GEM nenv (0.3.0) netrc (0.11.0) nio4r (2.1.0) - nokogiri (1.8.1) + nokogiri (1.8.2) mini_portile2 (~> 2.3.0) notiffany (0.1.1) nenv (~> 0.1) @@ -505,6 +505,8 @@ GEM simplecov-html (~> 0.10.0) simplecov-html (0.10.0) sixarm_ruby_unaccent (1.1.1) + skylight (1.5.1) + activesupport (>= 3.0.0) slop (3.6.0) sort_alphabetical (1.0.2) unicode_utils (>= 1.2.2) @@ -668,6 +670,7 @@ DEPENDENCIES sass-rails (>= 4.0.2) selectize-rails shoulda-matchers + skylight spring-commands-rspec sprockets-rails sqlite3 diff --git a/INSTALL.md b/INSTALL.md index ea47fd93..06249929 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -106,6 +106,8 @@ There are a couple of environment variables you can set to configure OSEM. Check | STRIPE_PUBLISHABLE_KEY | *string* | Publishable Key for Stripe Gateway | STRIPE_SECRET_KEY | *string* | Secret Key for Stripe Gateway | OSEM_REDIS_URL | *string* | Redis server URL e.g. redis://localhost:6379/1 +| SKYLIGHT_AUTHENTICATION | *string* | (Optional) Authentication token for Skylight +| SKYLIGHT_PUBLIC_DASHBOARD_URL | *string* | (Optional) URL to your public Skylight dashboard ### Online Ticket Payments We use [Stripe](https://stripe.com) for accepting your ticket payments securely over the web. @@ -130,3 +132,8 @@ Open a separate terminal and go into the directory where the rails app is presen ``` bundle exec rake jobs:work ``` + +## Performance +If you are experiencing performance issues (or just curious), you may be able to [apply a free Skylight account](https://www.skylight.io/oss). +Once you have your account setup, simply set `SKYLIGHT_AUTHENTICATION` and `SKYLIGHT_PUBLIC_DASHBOARD_URL` in your `.env` file. +If you are reporting a performance issue or submiting a performance patch, it would be helpful (but not required) to link to the relevant Skylight data. diff --git a/app/assets/fonts/mastodon.eot b/app/assets/fonts/mastodon.eot new file mode 100755 index 00000000..59cd79ab Binary files /dev/null and b/app/assets/fonts/mastodon.eot differ diff --git a/app/assets/fonts/mastodon.otf b/app/assets/fonts/mastodon.otf new file mode 100755 index 00000000..2d415249 Binary files /dev/null and b/app/assets/fonts/mastodon.otf differ diff --git a/app/assets/fonts/mastodon.svg b/app/assets/fonts/mastodon.svg new file mode 100755 index 00000000..8ea5bf6e --- /dev/null +++ b/app/assets/fonts/mastodon.svg @@ -0,0 +1,14 @@ + + + +Copyright (C) 2017 by original authors @ fontello.com + + + + + + + + + + \ No newline at end of file diff --git a/app/assets/fonts/mastodon.ttf b/app/assets/fonts/mastodon.ttf new file mode 100755 index 00000000..60c8ad30 Binary files /dev/null and b/app/assets/fonts/mastodon.ttf differ diff --git a/app/assets/fonts/mastodon.woff b/app/assets/fonts/mastodon.woff new file mode 100755 index 00000000..73365373 Binary files /dev/null and b/app/assets/fonts/mastodon.woff differ diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index c95bbb87..0767d15d 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -18,4 +18,5 @@ *= require osem-navbar *= require selectize *= require selectize.bootstrap3 + *= require mastodon */ diff --git a/app/assets/stylesheets/breakpoints.css.scss b/app/assets/stylesheets/breakpoints.scss similarity index 100% rename from app/assets/stylesheets/breakpoints.css.scss rename to app/assets/stylesheets/breakpoints.scss diff --git a/app/assets/stylesheets/mastodon.scss b/app/assets/stylesheets/mastodon.scss new file mode 100755 index 00000000..ddb481d1 --- /dev/null +++ b/app/assets/stylesheets/mastodon.scss @@ -0,0 +1,45 @@ +@font-face { + font-family: 'mastodon'; + src: font-url('mastodon.eot'); + src: local('☺'), + font-url('mastodon.otf') format('opentype'), + font-url('mastodon.woff') format('woff'), + font-url('mastodon.ttf') format('truetype'), + font-url('mastodon.svg') format('svg'); + font-weight: normal; + font-style: normal; +} + + [class^="icon-"]:before, [class*=" icon-"]:before { + font-family: "mastodon"; + font-style: normal; + font-weight: normal; + speak: none; + + display: inline-block; + text-decoration: inherit; + width: 1em; + margin-right: .2em; + text-align: center; + /* opacity: .8; */ + + /* For safety - reset parent styles, that can break glyph codes*/ + font-variant: normal; + text-transform: none; + + /* fix buttons height, for twitter bootstrap */ + line-height: 1em; + + /* you can be more comfortable with increased icons size */ + /* font-size: 120%; */ + + /* Font smoothing. That was taken from TWBS */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + + /* Uncomment for 3D effect */ + /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ +} + +.icon-mastodon:before { content: '\e800'; } /* '' */ +.icon-mastodon_alt:before { content: '\e801'; } /* '' */ diff --git a/app/assets/stylesheets/osem-dashboard.css.scss b/app/assets/stylesheets/osem-dashboard.scss similarity index 96% rename from app/assets/stylesheets/osem-dashboard.css.scss rename to app/assets/stylesheets/osem-dashboard.scss index 5dc69879..b2d0d125 100644 --- a/app/assets/stylesheets/osem-dashboard.css.scss +++ b/app/assets/stylesheets/osem-dashboard.scss @@ -1,4 +1,4 @@ -@import "breakpoints.css.scss"; +@import "breakpoints.scss"; .dashbox span.fa { line-height: 1.1em; diff --git a/app/assets/stylesheets/osem-fonts.css.scss b/app/assets/stylesheets/osem-fonts.scss similarity index 100% rename from app/assets/stylesheets/osem-fonts.css.scss rename to app/assets/stylesheets/osem-fonts.scss diff --git a/app/assets/stylesheets/osem-navbar.css.scss b/app/assets/stylesheets/osem-navbar.scss similarity index 96% rename from app/assets/stylesheets/osem-navbar.css.scss rename to app/assets/stylesheets/osem-navbar.scss index bfaac9ac..011ebc89 100644 --- a/app/assets/stylesheets/osem-navbar.css.scss +++ b/app/assets/stylesheets/osem-navbar.scss @@ -1,4 +1,4 @@ -@import "breakpoints.css.scss"; +@import "breakpoints.scss"; .nav-osem { border: none; @include breakpoint(xs) { diff --git a/app/assets/stylesheets/osem-payments.css.scss b/app/assets/stylesheets/osem-payments.scss similarity index 100% rename from app/assets/stylesheets/osem-payments.css.scss rename to app/assets/stylesheets/osem-payments.scss diff --git a/app/assets/stylesheets/osem-rating.css.scss b/app/assets/stylesheets/osem-rating.scss similarity index 100% rename from app/assets/stylesheets/osem-rating.css.scss rename to app/assets/stylesheets/osem-rating.scss diff --git a/app/assets/stylesheets/osem-schedule.css.scss b/app/assets/stylesheets/osem-schedule.scss similarity index 100% rename from app/assets/stylesheets/osem-schedule.css.scss rename to app/assets/stylesheets/osem-schedule.scss diff --git a/app/assets/stylesheets/osem-splash.css.scss b/app/assets/stylesheets/osem-splash.scss similarity index 99% rename from app/assets/stylesheets/osem-splash.css.scss rename to app/assets/stylesheets/osem-splash.scss index 387dfc00..32cbb822 100644 --- a/app/assets/stylesheets/osem-splash.css.scss +++ b/app/assets/stylesheets/osem-splash.scss @@ -1,4 +1,4 @@ -@import "breakpoints.css.scss"; +@import "breakpoints.scss"; #splash { // Counter the general padding for #content diff --git a/app/assets/stylesheets/osem.css.scss b/app/assets/stylesheets/osem.scss similarity index 94% rename from app/assets/stylesheets/osem.css.scss rename to app/assets/stylesheets/osem.scss index bbcabc44..7d0d0519 100644 --- a/app/assets/stylesheets/osem.css.scss +++ b/app/assets/stylesheets/osem.scss @@ -115,3 +115,9 @@ p.comment-body { float: right; } } + +/* omniauth btn grp */ +#openid-btn-grp{ + display: flex; + justify-content: center; +} \ No newline at end of file diff --git a/app/assets/stylesheets/strap-on.css.scss b/app/assets/stylesheets/strap-on.scss similarity index 100% rename from app/assets/stylesheets/strap-on.css.scss rename to app/assets/stylesheets/strap-on.scss diff --git a/app/controllers/admin/conferences_controller.rb b/app/controllers/admin/conferences_controller.rb index 921a23d1..6342f09f 100644 --- a/app/controllers/admin/conferences_controller.rb +++ b/app/controllers/admin/conferences_controller.rb @@ -20,6 +20,10 @@ module Admin @total_submissions = Event.count @new_submissions = Event.where('created_at > ?', current_user.last_sign_in_at).count + @total_withdrawn = Event.where(state: :withdrawn).count + @new_withdrawn = Event + .where('state = ? and created_at > ?', 'withdrawn', current_user.last_sign_in_at).count + @active_conferences = Conference.get_active_conferences_for_dashboard # pending or the last two @deactive_conferences = Conference .get_conferences_without_active_for_dashboard(@active_conferences) # conferences without active @@ -116,13 +120,19 @@ module Admin @total_reg = @conference.registrations.count @new_reg = @conference.registrations.where('created_at > ?', current_user.last_sign_in_at).count - @total_submissions = @program.events.count - @new_submissions = @program.events + @all_events = @program.events + + @total_submissions = @all_events.count + @new_submissions = @all_events .where('created_at > ?', current_user.last_sign_in_at).count @program_length = @conference.current_program_hours @new_program_length = @conference.new_program_hours(current_user.last_sign_in_at) + @total_withdrawn = @all_events.where(state: :withdrawn).count + @new_withdrawn = @all_events + .where('state = "withdrawn" and created_at > ?', current_user.last_sign_in_at).count + # Step by step list @conference_progress = @conference.get_status @@ -162,13 +172,17 @@ module Admin # Doughnut charts @event_type_distribution = @conference.event_type_distribution @event_type_distribution_confirmed = @conference.event_type_distribution(:confirmed) + @event_type_distribution_withdrawn = @conference.event_type_distribution(:withdrawn) @difficulty_levels_distribution = @conference.difficulty_levels_distribution @difficulty_levels_distribution_confirmed = @conference .difficulty_levels_distribution(:confirmed) + @difficulty_levels_distribution_withdrawn = @conference + .difficulty_levels_distribution(:withdrawn) @tracks_distribution = @conference.tracks_distribution @tracks_distribution_confirmed = @conference.tracks_distribution(:confirmed) + @tracks_distribution_withdrawn = @conference.tracks_distribution(:withdrawn) # Recent actions information @recent_events = @conference.program.events.limit(5).order(created_at: :desc) diff --git a/app/controllers/admin/contacts_controller.rb b/app/controllers/admin/contacts_controller.rb index 72ac0d42..1003e098 100644 --- a/app/controllers/admin/contacts_controller.rb +++ b/app/controllers/admin/contacts_controller.rb @@ -22,7 +22,7 @@ module Admin # Only allow a trusted parameter "white list" through. def contact_params - params.require(:contact).permit(:social_tag, :email, :facebook, :googleplus, :twitter, :instagram, :public, :sponsor_email) + params.require(:contact).permit(:social_tag, :email, :facebook, :googleplus, :twitter, :instagram, :mastodon, :public, :sponsor_email) end end end diff --git a/app/controllers/conferences_controller.rb b/app/controllers/conferences_controller.rb index 4b01ca29..1c9b37b3 100644 --- a/app/controllers/conferences_controller.rb +++ b/app/controllers/conferences_controller.rb @@ -20,6 +20,11 @@ class ConferencesController < ApplicationController authorize! :show, @conference # TODO: reduce the 10 queries performed here splashpage = @conference.splashpage + + unless splashpage.present? + redirect_to admin_conference_splashpage_path(@conference.short_title) && return + end + if splashpage.include_cfp cfps = @conference.program.cfps @call_for_events = cfps.find { |call| call.cfp_type == 'events' } @@ -50,6 +55,7 @@ class ConferencesController < ApplicationController @sponsorship_levels = @conference.sponsorship_levels.eager_load( :sponsors ).order('sponsorship_levels.position ASC', 'sponsors.name') + @sponsors = @conference.sponsors end end diff --git a/app/controllers/physical_tickets_controller.rb b/app/controllers/physical_tickets_controller.rb index 2f6f18ec..c3cc40cb 100644 --- a/app/controllers/physical_tickets_controller.rb +++ b/app/controllers/physical_tickets_controller.rb @@ -10,7 +10,7 @@ class PhysicalTicketsController < ApplicationController end def show - @file_name = "ticket_for_#{@conference.short_title}" + @file_name = "ticket_for_#{@conference.short_title}.pdf" @user = @physical_ticket.user @ticket_layout = @conference.ticket_layout.to_sym @qrcode_image = RQRCode::QRCode.new(@physical_ticket.token).as_png(size: 180, border_modules: 0) diff --git a/app/models/ability.rb b/app/models/ability.rb index b833b6f4..d6b3ef21 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -66,7 +66,13 @@ class Ability can [:new, :create], Registration do |registration| conference = registration.conference - conference.registration_open? && !conference.registration_limit_exceeded? || conference.program.speakers.confirmed.include?(user) + if conference.user_registered? user + false + elsif conference.program.speakers.confirmed.include?(user) && conference.registration_period + true + else + conference.registration_open? && !conference.registration_limit_exceeded? + end end can :index, Organization diff --git a/app/models/conference.rb b/app/models/conference.rb index 7f821cdc..81e9ec32 100644 --- a/app/models/conference.rb +++ b/app/models/conference.rb @@ -595,11 +595,11 @@ class Conference < ApplicationRecord # * +ActiveRecord+ def self.get_active_conferences_for_dashboard result = Conference.where('start_date > ?', Time.now) - .select('id, short_title, color, start_date') + .select('id, short_title, color, start_date, organization_id') if result.empty? result = Conference - .select('id, short_title, color, start_date').limit(2) + .select('id, short_title, color, start_date, organization_id').limit(2) .order(start_date: :desc) end result @@ -611,7 +611,7 @@ class Conference < ApplicationRecord # ====Returns # * +ActiveRecord+ def self.get_conferences_without_active_for_dashboard(active_conferences) - result = Conference.select('id, short_title, color, start_date').order(start_date: :desc) + result = Conference.select('id, short_title, color, start_date, organization_id').order(start_date: :desc) result - active_conferences end @@ -719,7 +719,7 @@ class Conference < ApplicationRecord # * +True+ -> If the registration limit has been reached or exceeded # * +False+ -> If the registration limit hasn't been exceeded def registration_limit_exceeded? - registration_limit > 0 && registrations.count + program.speakers.confirmed.count - program.speakers.confirmed.registered(program.conference).count >= registration_limit + registration_limit > 0 && registrations.count + program.speakers.confirmed.unregistered(program.conference).count >= registration_limit end # Returns an hexadecimal color given a collection. The returned color changed @@ -1029,7 +1029,7 @@ class Conference < ApplicationRecord # * +hash+ -> object_type => {color, value} def calculate_event_distribution(group_by_id, association_symbol, state = nil) grouped = if state - program.events.select(group_by_id).where('state = ?', 'confirmed').group(group_by_id) + program.events.select(group_by_id).where('state = ?', state).group(group_by_id) else program.events.select(group_by_id).group(group_by_id) end diff --git a/app/models/contact.rb b/app/models/contact.rb index 799c8748..dd7196dd 100644 --- a/app/models/contact.rb +++ b/app/models/contact.rb @@ -5,11 +5,11 @@ class Contact < ApplicationRecord validates :conference, presence: true # Conferences only have one contact - validates :facebook, :twitter, :googleplus, :instagram, + validates :facebook, :twitter, :googleplus, :instagram, :mastodon, format: URI::regexp(%w(http https)), allow_blank: true def has_social_media? - return true if facebook.present? || twitter.present? || googleplus.present? || instagram.present? || email.present? + return true if facebook.present? || twitter.present? || googleplus.present? || instagram.present? || mastodon.present? || email.present? false end end diff --git a/app/models/program.rb b/app/models/program.rb index dab2f2b6..14554786 100644 --- a/app/models/program.rb +++ b/app/models/program.rb @@ -51,6 +51,10 @@ class Program < ApplicationRecord def registered(conference) joins(:registrations).where('registrations.conference_id = ?', conference.id) end + + def unregistered(conference) + self - registered(conference) + end end accepts_nested_attributes_for :event_types, allow_destroy: true diff --git a/app/models/registration.rb b/app/models/registration.rb index 1198324e..45d2d55b 100644 --- a/app/models/registration.rb +++ b/app/models/registration.rb @@ -1,4 +1,5 @@ class Registration < ApplicationRecord + require 'csv' belongs_to :user belongs_to :conference diff --git a/app/models/ticket.rb b/app/models/ticket.rb index b49cf1b9..80e7f3e0 100644 --- a/app/models/ticket.rb +++ b/app/models/ticket.rb @@ -62,7 +62,11 @@ class Ticket < ApplicationRecord def tickets_turnover_total(id) tickets = TicketPurchase.where(ticket_id: id).paid - tickets.inject(0){ |sum, ticket| sum + (ticket.amount_paid * ticket.quantity) } + if tickets.blank? + Money.new(0, Ticket.find(id).price_currency) + else + tickets.inject(Money.new(0, tickets.first.price_currency)){ |sum, ticket| sum + (ticket.amount_paid * ticket.quantity) } + end end def tickets_sold diff --git a/app/views/admin/booths/show.html.haml b/app/views/admin/booths/show.html.haml index 6ed823e5..02d44609 100644 --- a/app/views/admin/booths/show.html.haml +++ b/app/views/admin/booths/show.html.haml @@ -1,7 +1,8 @@ .row .col-md-12 %h3 - = image_tag(@booth.picture.thumb.url, size: '20%', alt: '') + - if @booth.logo_link + = image_tag( @booth.picture.thumb.url, size: '20%', alt: '') = @booth.title .btn-group.pull-right = link_to 'Edit', edit_admin_conference_booth_path(@conference.short_title, @booth), class: 'btn btn-mini btn-primary' diff --git a/app/views/admin/campaigns/_form.html.haml b/app/views/admin/campaigns/_form.html.haml index 6b826300..71aa075e 100644 --- a/app/views/admin/campaigns/_form.html.haml +++ b/app/views/admin/campaigns/_form.html.haml @@ -11,7 +11,7 @@ .col-md-8 = semantic_form_for(@campaign, url: (@campaign.new_record? ? admin_conference_campaigns_path : admin_conference_campaign_path(@conference.short_title, @campaign))) do |f| = f.inputs do - = f.input :name + = f.input :name, input_html: { autofocus: true } = f.inputs name: 'UTM Parameters' do = f.input :utm_campaign, label: 'Campaign', hint: 'Groups all of the content from one campaign. E.g. 20percentpromocode' = f.input :utm_source, label: 'Source', hint: 'Which website is sending you traffic. E.g. Facebook, google+, blog' diff --git a/app/views/admin/cfps/index.html.haml b/app/views/admin/cfps/index.html.haml index 09c208cf..1b80f411 100644 --- a/app/views/admin/cfps/index.html.haml +++ b/app/views/admin/cfps/index.html.haml @@ -7,7 +7,7 @@ - if @program.cfps .row .col-md-12 - %table.table.table-hover.datatable#tickets + %table.table.table-hover#tickets %thead %th Type %th Start Date diff --git a/app/views/admin/commercials/index.html.haml b/app/views/admin/commercials/index.html.haml index 32779e2c..095ddad2 100644 --- a/app/views/admin/commercials/index.html.haml +++ b/app/views/admin/commercials/index.html.haml @@ -14,7 +14,7 @@ .row .col-md-6 = semantic_form_for(@commercial, url: admin_conference_commercials_path(conference_id: @conference.short_title)) do |f| - = f.input :url, label: 'URL', as: :string, input_html: { required: 'required' }, + = f.input :url, label: 'URL', as: :string, input_html: { required: 'required', autofocus: true }, hint: 'Just paste the url of your video/photo provider. YouTube, Vimeo, SpeakerDeck, SlideShare, Instagram, Flickr.' = f.action :submit, as: :button, button_html: { class: 'btn btn-primary pull-right', disabled: true } %hr diff --git a/app/views/admin/conferences/_doughnut_chart.html.haml b/app/views/admin/conferences/_doughnut_chart.html.haml index f4a8f0ba..6ad75cef 100644 --- a/app/views/admin/conferences/_doughnut_chart.html.haml +++ b/app/views/admin/conferences/_doughnut_chart.html.haml @@ -1,6 +1,6 @@ .text-center %h4 #{title} - %canvas.doughnut_chart{ id: "dough_#{title}", 'data-chart' => data.to_json } + %canvas.doughnut_chart{ id: "dough_#{title.parameterize.underscore}", 'data-chart' => data.to_json } - if data - data.each do |key, value| %span{ 'style' => "border-bottom: 3px solid #{value['color']}" } #{key}: #{value['value']} @@ -8,7 +8,7 @@ :javascript $(document).ready( function(){ - var d = $("#dough_#{title}"); + var d = $("#dough_#{title.parameterize.underscore}"); var dt = d.get(0).getContext('2d'); $(window).resize( respondCanvas ); diff --git a/app/views/admin/conferences/index.html.haml b/app/views/admin/conferences/index.html.haml index 02208aeb..2fde1575 100644 --- a/app/views/admin/conferences/index.html.haml +++ b/app/views/admin/conferences/index.html.haml @@ -1,5 +1,5 @@ .row - .col-sm-4.col-xs-4 + .col-sm-3.col-xs-3 .dashbox.text-center %span.fa.fa-user.fa-lg %span.fa.fa-lg @@ -11,7 +11,7 @@ %span.label.label-success{ title: "+ #{@new_user} since you last logged in!" } + = @new_user - .col-sm-4.col-xs-4 + .col-sm-3.col-xs-3 .dashbox.text-center %span.fa.fa-check-square.fa-lg %span.fa.fa-lg @@ -23,7 +23,7 @@ %span.label.label-success{ title: "+#{@new_reg} since you last logged in!" } + = @new_reg - .col-sm-4.col-xs-4 + .col-sm-3.col-xs-3 .dashbox.text-center %span.fa.fa-file-text.fa-lg %span.fa.fa-lg @@ -35,6 +35,18 @@ %span.label.label-success{ title: "+#{@new_submissions} since you last logged in!" } + = @new_submissions + .col-sm-3.col-xs-3 + .dashbox.text-center + %span.fa.fa-archive.fa-lg + %span.fa.fa-lg + = @total_withdrawn + %p + %small + #{'Withdrawn'.pluralize(@total_withdrawn)} + - if @new_withdrawn + %span.label.label-success{ title: "+#{@new_withdrawn} since you last logged in!" } + + + = @new_withdrawn .row#registrations .col-md-8 diff --git a/app/views/admin/conferences/show.html.haml b/app/views/admin/conferences/show.html.haml index cddae4c2..ca59d8d9 100644 --- a/app/views/admin/conferences/show.html.haml +++ b/app/views/admin/conferences/show.html.haml @@ -3,7 +3,7 @@ Dashboard for #{@conference.title} %hr .row - .col-sm-4.col-xs-4 + .col-sm-3.col-xs-3 .dashbox.text-center %span.fa.fa-user.fa-lg %span.fa.fa-lg @@ -16,7 +16,7 @@ + = @new_reg - .col-sm-4.col-xs-4 + .col-sm-3.col-xs-3 .dashbox.text-center %span.fa.fa-check-square.fa-lg %span.fa.fa-lg @@ -29,7 +29,7 @@ + = @new_submissions - .col-sm-4.col-xs-4 + .col-sm-3.col-xs-3 .dashbox.text-center %span.fa.fa-file-text.fa-lg %span.fa.fa-lg @@ -42,6 +42,19 @@ + = @new_program_length + .col-sm-3.col-xs-3 + .dashbox.text-center + %span.fa.fa-archive.fa-lg + %span.fa.fa-lg + = @total_withdrawn + %p + %small + #{'Withdrawn'.pluralize(@total_withdrawn)} + - if @new_withdrawn + %span.label.label-success{ title: "+#{@new_withdrawn} since you last logged in!" } + + + = @new_withdrawn + .row .col-md-12 .row @@ -91,6 +104,10 @@ %a{ href: '#distribution_confirmed', 'data-toggle' => 'tab' } %span.fa.fa-comment Confirmed + %li + %a{ href: '#distribution_withdrawn', 'data-toggle' => 'tab' } + %span.fa.fa-archive + Withdrawn .tab-content .tab-pane.active#distribution_all .row @@ -108,6 +125,14 @@ = render partial: 'doughnut_chart', locals: {title: 'Difficulty levels', data: @difficulty_levels_distribution_confirmed} .col-md-4 = render partial: 'doughnut_chart', locals: {title: 'Tracks', data: @tracks_distribution_confirmed} + .tab-pane#distribution_withdrawn + .row + .col-md-4 + = render partial: 'doughnut_chart', locals: {title: 'Event types', data: @event_type_distribution_withdrawn} + .col-md-4 + = render partial: 'doughnut_chart', locals: {title: 'Difficulty levels', data: @difficulty_levels_distribution_withdrawn} + .col-md-4 + = render partial: 'doughnut_chart', locals: {title: 'Tracks', data: @tracks_distribution_withdrawn} .row .col-md-8 diff --git a/app/views/admin/contacts/edit.html.haml b/app/views/admin/contacts/edit.html.haml index 872977b0..1c48e48a 100644 --- a/app/views/admin/contacts/edit.html.haml +++ b/app/views/admin/contacts/edit.html.haml @@ -8,7 +8,7 @@ .col-md-8 = semantic_form_for(@contact, url: admin_conference_contact_path(@conference.short_title), html: {multipart: true}) do |f| = f.inputs name: 'Mail' do - = f.input :email, hint: 'Contact email address for your conference. Will be used as reply-to address in emails sent out by the system.' + = f.input :email, input_html: { autofocus: true }, hint: 'Contact email address for your conference. Will be used as reply-to address in emails sent out by the system.' = f.input :sponsor_email, hint: 'This will appear in the sponsor segment of the splash for the sponsors to contact to the organizers' = f.inputs name: 'Social Media' do = f.input :social_tag, hint: "The hashtag you'll use on Twitter and Google+. Don't include the '#' sign!'" @@ -16,4 +16,5 @@ = f.input :googleplus, label: 'Google+ Url', hint: 'This will appear in the social media section as the link to the Google+ Page of your Conference' = f.input :twitter, hint: 'This will appear in the social media section as the link to the Twitter Page of your Conference' = f.input :instagram, hint: 'This will appear in the social media section as the link to the Instagram Page of your Conference' + = f.input :mastodon, hint: 'This will appear in the social media section as the link to the Mastodon Page of your Conference' = f.action :submit, as: :button, button_html: { class: 'btn btn-primary' } diff --git a/app/views/admin/difficulty_levels/_form.html.haml b/app/views/admin/difficulty_levels/_form.html.haml index fa47a88b..f28ab7e3 100644 --- a/app/views/admin/difficulty_levels/_form.html.haml +++ b/app/views/admin/difficulty_levels/_form.html.haml @@ -9,7 +9,7 @@ .row .col-md-8 = semantic_form_for(@difficulty_level, url: (@difficulty_level.new_record? ? admin_conference_program_difficulty_levels_path : admin_conference_program_difficulty_level_path(@conference.short_title, @difficulty_level))) do |f| - = f.input :title, required: true + = f.input :title, input_html: { autofocus: true }, required: true = f.input :description, input_html: {rows: 3, class: 'span6'} = f.input :color, input_html: {size: 6, type: 'color'} %p.text-right diff --git a/app/views/admin/event_types/_form.html.haml b/app/views/admin/event_types/_form.html.haml index 51a0ad99..29fac398 100644 --- a/app/views/admin/event_types/_form.html.haml +++ b/app/views/admin/event_types/_form.html.haml @@ -9,7 +9,7 @@ .row .col-md-12 = semantic_form_for(@event_type, url: (@event_type.new_record? ? admin_conference_program_event_types_path : admin_conference_program_event_type_path(@conference.short_title, @event_type))) do |f| - = f.input :title + = f.input :title, input_html: { autofocus: true } = f.input :length, input_html: {size: 3, type: 'number', step: @event_type.program.schedule_interval, min: @event_type.program.schedule_interval} = f.input :description = f.input :minimum_abstract_length, input_html: {size: 3} diff --git a/app/views/admin/organizations/index.html.haml b/app/views/admin/organizations/index.html.haml index baf9bc80..7226d2a6 100644 --- a/app/views/admin/organizations/index.html.haml +++ b/app/views/admin/organizations/index.html.haml @@ -2,8 +2,9 @@ .col-md-12 .page-header %h1 Organizations - .btn-group.pull-right - = link_to 'Create Organization', new_admin_organization_path, class: 'btn btn-success pull-right' + - if can? :manage, :all + .btn-group.pull-right + = link_to 'Create Organization', new_admin_organization_path, class: 'btn btn-success pull-right' %p.text-muted Manage organizations in OSEM .row diff --git a/app/views/admin/programs/_form.html.haml b/app/views/admin/programs/_form.html.haml index 035093f0..9b42e2b0 100644 --- a/app/views/admin/programs/_form.html.haml +++ b/app/views/admin/programs/_form.html.haml @@ -9,7 +9,7 @@ = f.input :schedule_fluid, label: 'Allow submitters to change their event after it is scheduled' = f.input :rating, hint: 'Enter the number of different rating levels you want to have for voting on proposals. Enter 0 if you do not want to vote on proposals.' = f.input :languages, hint: "Enter the languages allowed for events as values of #{link_to('ISO 639-1', 'http://www.loc.gov/standards/iso639-2/php/code_list.php', target: "_blank")} language codes separated with commas. The first language would be the default language. Leave it blank if you do not want to specify languages.".html_safe - = f.input :schedule_interval, hint: "It is the minimal time interval of your schedule. The value should be 5, 6, 10, 12, 15, 20, 30 or 60. Warning! Some events could be unscheduled when changing this value." + = f.input :schedule_interval, input_html: { autofocus: true }, hint: "It is the minimal time interval of your schedule. The value should be 5, 6, 10, 12, 15, 20, 30 or 60. Warning! Some events could be unscheduled when changing this value." = f.input :blind_voting, hint: 'Enable this feature if you do not want to show voting results and voters prior to user submitting a vote. For the feature to work you need to set the voting dates below as well' = f.input :voting_start_date, as: :string, input_html: { id: 'datetimepicker-voting_start_date', readonly: true, value: (f.object.voting_start_date.to_formatted_s(:db_without_seconds) unless f.object.voting_start_date.nil?) } = f.input :voting_end_date, as: :string, input_html: { id: 'datetimepicker-voting_start_date', readonly: true, value: (f.object.voting_end_date.to_formatted_s(:db_without_seconds) unless f.object.voting_end_date.nil?) } diff --git a/app/views/admin/reports/_all_events.html.haml b/app/views/admin/reports/_all_events.html.haml index 98f405ea..cca8968a 100644 --- a/app/views/admin/reports/_all_events.html.haml +++ b/app/views/admin/reports/_all_events.html.haml @@ -5,7 +5,7 @@ All Events = "(#{@events.length})" %p.text-muted - All submissions and the information that they are mssing + All submissions and the information that they are missing .col-md-12 %table.table.table-striped.table-bordered.table-hover.datatable diff --git a/app/views/admin/resources/_form.html.haml b/app/views/admin/resources/_form.html.haml index ef999104..ce5f25b0 100644 --- a/app/views/admin/resources/_form.html.haml +++ b/app/views/admin/resources/_form.html.haml @@ -8,7 +8,7 @@ .row .col-md-8 = semantic_form_for(@resource, :url => (@resource.new_record? ? admin_conference_resources_path : admin_conference_resource_path(@conference.short_title, @resource))) do |f| - = f.input :name + = f.input :name, input_html: { autofocus: true } = f.input :description, input_html: { rows: 5, data: { provide: 'markdown-editable' } } = f.input :used = f.input :quantity diff --git a/app/views/admin/roles/_form.html.haml b/app/views/admin/roles/_form.html.haml index 2204ef89..860f43a9 100644 --- a/app/views/admin/roles/_form.html.haml +++ b/app/views/admin/roles/_form.html.haml @@ -10,6 +10,6 @@ = semantic_form_for @role, url: @url do |f| .row .col-md-5 - = f.input :description + = f.input :description, input_html: { autofocus: true } = f.action :submit, as: :button, button_html: { class: 'btn btn-primary' } diff --git a/app/views/admin/roles/show.html.haml b/app/views/admin/roles/show.html.haml index b264bc5f..b8796121 100644 --- a/app/views/admin/roles/show.html.haml +++ b/app/views/admin/roles/show.html.haml @@ -24,7 +24,7 @@ = u.label 'Add user by email: ' .input-group - = u.input :email, label: false, placeholder: "User's email" + = u.input :email, input_html: { autofocus: true }, label: false, placeholder: "User's email" .input-group-btn = u.submit 'Add', id: 'user-add', class: 'btn btn-primary' diff --git a/app/views/admin/rooms/_form.html.haml b/app/views/admin/rooms/_form.html.haml index eec272cd..90de99e0 100644 --- a/app/views/admin/rooms/_form.html.haml +++ b/app/views/admin/rooms/_form.html.haml @@ -9,7 +9,7 @@ .row .col-md-8 = semantic_form_for(@room, url: (@room.new_record? ? admin_conference_venue_rooms_path : admin_conference_venue_room_path(@conference.short_title, @room))) do |f| - = f.input :name, input_html: { autofocus: true} + = f.input :name, input_html: { autofocus: true } = f.input :size, label: 'Capacity', input_html: {size: 5} %p.text-right = f.action :submit, as: :button, button_html: { class: 'btn btn-primary' } diff --git a/app/views/admin/sponsors/_form.html.haml b/app/views/admin/sponsors/_form.html.haml index ba24f9f3..c465fc48 100644 --- a/app/views/admin/sponsors/_form.html.haml +++ b/app/views/admin/sponsors/_form.html.haml @@ -9,7 +9,7 @@ .row .col-md-8 = semantic_form_for(@sponsor, url: (@sponsor.new_record? ? admin_conference_sponsors_path : admin_conference_sponsor_path(@conference.short_title, @sponsor))) do |f| - = f.input :name + = f.input :name, input_html: { autofocus: true } = f.input :description = image_tag f.object.picture.thumb.url if f.object.picture? = f.input :picture diff --git a/app/views/admin/sponsors/index.html.haml b/app/views/admin/sponsors/index.html.haml index 1c8f1077..d2fc512c 100644 --- a/app/views/admin/sponsors/index.html.haml +++ b/app/views/admin/sponsors/index.html.haml @@ -19,7 +19,8 @@ - @conference.sponsors.each do |sponsor| %tr %td - = image_tag(sponsor.picture.thumb.url, width: '20%') + - if sponsor.picture? + = image_tag(sponsor.picture.thumb.url, width: '20%') %td = sponsor.name %td diff --git a/app/views/admin/sponsorship_levels/_form.html.haml b/app/views/admin/sponsorship_levels/_form.html.haml index 60848928..ae819a71 100644 --- a/app/views/admin/sponsorship_levels/_form.html.haml +++ b/app/views/admin/sponsorship_levels/_form.html.haml @@ -9,6 +9,6 @@ .row .col-md-8 = semantic_form_for(@sponsorship_level, url: (@sponsorship_level.new_record? ? admin_conference_sponsorship_levels_path : admin_conference_sponsorship_level_path(@conference.short_title, @sponsorship_level))) do |f| - = f.input :title + = f.input :title, input_html: { autofocus: true } %p.text-right = f.action :submit, as: :button, button_html: { class: 'btn btn-primary' } diff --git a/app/views/admin/targets/_form.html.haml b/app/views/admin/targets/_form.html.haml index 1e81989b..df22922e 100644 --- a/app/views/admin/targets/_form.html.haml +++ b/app/views/admin/targets/_form.html.haml @@ -9,7 +9,7 @@ .col-md-8 = semantic_form_for(@target, url: (@target.new_record? ? admin_conference_targets_path : admin_conference_target_path(@conference.short_title, @target))) do |f| = f.input :due_date, as: :string, input_html: { class: 'target-due-date-datepicker'}, label: 'Until when do you want to have ' - = f.input :target_count, label: 'this amount of ' + = f.input :target_count, input_html: { autofocus: true }, label: 'this amount of ' = f.input :unit, as: :select, label: 'Unit', class: 'form-control', collection: Target.units.values, include_blank: false %p.text-right = f.action :submit, as: :button, button_html: { class: 'btn btn-primary' } diff --git a/app/views/admin/tracks/_form.html.haml b/app/views/admin/tracks/_form.html.haml index a621e1a4..36ff5734 100644 --- a/app/views/admin/tracks/_form.html.haml +++ b/app/views/admin/tracks/_form.html.haml @@ -9,7 +9,7 @@ .row .col-md-12 = semantic_form_for(@track, url: (@track.new_record? ? admin_conference_program_tracks_path(@conference.short_title) : admin_conference_program_track_path(@conference.short_title, @track))) do |f| - = f.input :name + = f.input :name, input_html: { autofocus: true } = f.input :short_name, hint: "A short and unique handle for the track, using only letters, numbers, underscores, and dashes. This will be used to identify the track in URLs etc. Example: 'my_awesome_track'", input_html: { required: 'required', pattern: '[a-zA-Z0-9_-]+', title: 'Only letters, numbers, underscores, and dashes.' } = f.input :color, input_html: {size: 6, type: 'color'}, required: true = f.input :start_date, as: :string, input_html: { id: 'registration-period-start-datepicker', start_date: @conference.start_date, end_date: @conference.end_date, readonly: 'readonly', required: @track.self_organized_and_accepted_or_confirmed? } diff --git a/app/views/admin/venues/show.html.haml b/app/views/admin/venues/show.html.haml index b50c10da..36906e05 100644 --- a/app/views/admin/venues/show.html.haml +++ b/app/views/admin/venues/show.html.haml @@ -8,7 +8,7 @@ .row .col-md-6 - if @conference.venue.location? - = render '/conferences/venue_map' + = render '/conferences/venue_map', venue: @conference.venue -else - if @venue.commercial.nil? .row diff --git a/app/views/booths/_form.html.haml b/app/views/booths/_form.html.haml index 55a51432..4f5b6018 100644 --- a/app/views/booths/_form.html.haml +++ b/app/views/booths/_form.html.haml @@ -2,7 +2,7 @@ .row .col-md-8 = semantic_form_for(@booth, url: @url, html: { multipart: true }) do |f| - = f.input :title, as: :string, autofocus: true, required: true + = f.input :title, as: :string, input_html: { autofocus: true }, required: true = f.input :description, input_html: { rows: 5, data: { provide: 'markdown-editable' } }, required: true, hint: 'This field becomes public upon request acceptance' = f.input :reasoning, input_html: { rows: 5, data: { provide: 'markdown-editable' } }, required: true, diff --git a/app/views/booths/show.html.haml b/app/views/booths/show.html.haml index 6b12229d..f1790504 100644 --- a/app/views/booths/show.html.haml +++ b/app/views/booths/show.html.haml @@ -6,7 +6,7 @@ = image_tag(@booth.picture.thumb.url, size: '20%', alt: '') = @booth.title .btn-group.pull-right - = link_to 'Edit', edit_admin_conference_booth_path(@conference.short_title, @booth), class: 'btn btn-mini btn-primary' + = link_to 'Edit', edit_conference_booth_path(@conference.short_title, @booth), class: 'btn btn-mini btn-primary' .row .col-md-12 diff --git a/app/views/conferences/_call_for_papers.haml b/app/views/conferences/_call_for_papers.haml index 3bf0f295..b47d7118 100644 --- a/app/views/conferences/_call_for_papers.haml +++ b/app/views/conferences/_call_for_papers.haml @@ -23,5 +23,5 @@ left! %p.cta-button = link_to "Submit your proposal now", - conference_program_proposals_path(conference_id), + new_conference_program_proposal_path(conference_id), class: 'btn btn-success btn-lg text-center' diff --git a/app/views/conferences/_call_for_tracks.haml b/app/views/conferences/_call_for_tracks.haml index 7eb80fa6..6d78bd05 100644 --- a/app/views/conferences/_call_for_tracks.haml +++ b/app/views/conferences/_call_for_tracks.haml @@ -15,5 +15,5 @@ left! %p.cta-button = link_to("Submit your request for track", - conference_program_tracks_path(conference_id), + new_conference_program_track_path(conference_id), class: 'btn btn-success btn-lg text-center') diff --git a/app/views/conferences/_modal_description.haml b/app/views/conferences/_modal_description.haml index eb6f2b69..7fe4173b 100644 --- a/app/views/conferences/_modal_description.haml +++ b/app/views/conferences/_modal_description.haml @@ -15,7 +15,8 @@ - if object.is_a? Sponsor - img_classes << ['img-sponsor', "img-sponsor-#{object.sponsorship_level.position}"] - = image_tag get_logo(object), class: img_classes + - if object.picture? + = image_tag get_logo(object), class: img_classes %p.description = object.description .modal-footer diff --git a/app/views/conferences/_social_media.haml b/app/views/conferences/_social_media.haml index 32cf1b16..28a87dbc 100644 --- a/app/views/conferences/_social_media.haml +++ b/app/views/conferences/_social_media.haml @@ -15,6 +15,9 @@ - unless contact.googleplus.blank? = link_to "#{ contact.googleplus }" do %i.fa.fa-google-plus-square.fa-4x + - unless contact.mastodon.blank? + = link_to "#{ contact.mastodon }" do + %i.fa.icon-mastodon.fa-4x - unless contact.email.blank? = mail_to "#{ contact.email }" do %i.fa.fa-envelope-o.fa-4x diff --git a/app/views/conferences/_sponsors.haml b/app/views/conferences/_sponsors.haml index 8f4d55c6..5d7bbaa9 100644 --- a/app/views/conferences/_sponsors.haml +++ b/app/views/conferences/_sponsors.haml @@ -2,7 +2,7 @@ %li %a.smoothscroll{ href: '#sponsors' } Sponsors -- cache [conference, sponsorship_levels, '#splash#sponsors'] do +- cache [conference, sponsors, sponsorship_levels, '#splash#sponsors'] do %section#sponsors .container - sponsorship_levels.each do |sponsorship_level| @@ -31,6 +31,6 @@ = link_to(sponsorship_mailto(conference)) do Contact us! -- sponsorship_levels.collect(&:sponsors).flatten.each do |sponsor| +- sponsors.each do |sponsor| - content_for :modals do = render 'modal_description', object: sponsor diff --git a/app/views/conferences/show.html.haml b/app/views/conferences/show.html.haml index d43c99cf..246ccc35 100644 --- a/app/views/conferences/show.html.haml +++ b/app/views/conferences/show.html.haml @@ -29,7 +29,7 @@ - if @conference.registration_open? = render 'registration', conference: @conference, registration_period: @conference.registration_period, - tickets: @tickets, conference_id: @conference_id.short_title + tickets: @tickets, conference_id: @conference.short_title - if @conference.splashpage.include_tickets && @conference.tickets.any? = render 'tickets', conference: @conference, tickets: @tickets @@ -43,7 +43,8 @@ -# sponsorship - if @conference.splashpage.include_sponsors = render 'sponsors', conference: @conference, - sponsorship_levels: @sponsorship_levels + sponsorship_levels: @sponsorship_levels, + sponsors: @sponsors -# footer - if @conference.splashpage.include_social_media diff --git a/app/views/devise/shared/_openid_links.html.haml b/app/views/devise/shared/_openid_links.html.haml index 8bcc96da..09c7a7ea 100644 --- a/app/views/devise/shared/_openid_links.html.haml +++ b/app/views/devise/shared/_openid_links.html.haml @@ -1,5 +1,5 @@ .text-center - .btn-group.btn-group-lg + .btn-group.btn-group-lg#openid-btn-grp - omniauth_configured.each do |provider| = link_to "user_#{provider}_omniauth_authorize".to_sym, class: "btn btn-success btn-lg", id: "omniauth-#{provider}", diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index aa155bcb..128f491a 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -37,11 +37,14 @@ %p.muted.text-center %small This tool is - =link_to "free software,", "http://www.gnu.org/philosophy/free-sw.html" + #{link_to "free software", "http://www.gnu.org/philosophy/free-sw.html"}, released under the - =link_to "MIT license.", "http://opensource.org/licenses/MIT" + #{link_to "MIT license", "http://opensource.org/licenses/MIT"}. You can run, copy, distribute, study, change and improve it. The source code and the developers are on - =link_to "github.", "https://github.com/openSUSE/osem" + #{link_to "GitHub", "https://github.com/openSUSE/osem"}. + - if ENV["SKYLIGHT_PUBLIC_DASHBOARD_URL"].present? + Performance data is available on + #{link_to "Skylight", ENV["SKYLIGHT_PUBLIC_DASHBOARD_URL"]}. = yield :script_body = piwik_tracking_tag diff --git a/app/views/proposals/_proposal_form.html.haml b/app/views/proposals/_proposal_form.html.haml index 4b66240c..1b642d94 100644 --- a/app/views/proposals/_proposal_form.html.haml +++ b/app/views/proposals/_proposal_form.html.haml @@ -1,6 +1,6 @@ = semantic_form_for(@event, url: @url) do |f| = f.inputs name: 'Proposal Information' do - = f.input :title, as: :string, required: true + = f.input :title, as: :string, input_html: { autofocus: true }, required: true = f.input :subtitle, as: :string diff --git a/config.ru b/config.ru index 988dac89..a0123600 100644 --- a/config.ru +++ b/config.ru @@ -1,4 +1,5 @@ # This file is used by Rack-based servers to start the application. require ::File.expand_path('../config/environment', __FILE__) +use Rack::Deflater run Osem::Application diff --git a/config/application.rb b/config/application.rb index 4e3844ad..7e5e3a5d 100644 --- a/config/application.rb +++ b/config/application.rb @@ -60,6 +60,9 @@ module Osem # Version of your assets, change this if you want to expire all your assets config.assets.version = '1.0' + # Set cache headers + config.public_file_server.headers = { 'Cache-Control' => 'public, max-age=31536000' } + config.active_job.queue_adapter = :delayed_job end end diff --git a/config/environments/production.rb b/config/environments/production.rb index 11a8c06b..2a9bf1f8 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -25,6 +25,10 @@ Osem::Application.configure do # Generate digests for assets URLs config.assets.digest = true + config.assets.css_compressor = :sass + config.assets.js_compressor = :uglifier + config.assets.gzip = true + # Defaults to nil and saved in location specified by config.assets.prefix # config.assets.manifest = YOUR_PATH diff --git a/db/migrate/20171201163628_add_mastodon_to_contact.rb b/db/migrate/20171201163628_add_mastodon_to_contact.rb new file mode 100644 index 00000000..9b022019 --- /dev/null +++ b/db/migrate/20171201163628_add_mastodon_to_contact.rb @@ -0,0 +1,5 @@ +class AddMastodonToContact < ActiveRecord::Migration + def change + add_column :contacts, :mastodon, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 43602f9b..48815a8a 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20171130172334) do +ActiveRecord::Schema.define(version: 20171201163628) do create_table "ahoy_events", force: :cascade do |t| t.integer "visit_id" @@ -145,6 +145,7 @@ ActiveRecord::Schema.define(version: 20171130172334) do t.datetime "created_at" t.datetime "updated_at" t.string "sponsor_email" + t.string "mastodon" end create_table "delayed_jobs", force: :cascade do |t| @@ -495,7 +496,7 @@ ActiveRecord::Schema.define(version: 20171130172334) do t.integer "user_id" t.integer "payment_id" t.integer "week" - t.float "amount_paid" + t.float "amount_paid", default: 0.0 end create_table "ticket_scannings", force: :cascade do |t| diff --git a/docker-compose.env.example b/docker-compose.env.example index 905ff612..7bda181b 100644 --- a/docker-compose.env.example +++ b/docker-compose.env.example @@ -34,7 +34,7 @@ SECRET_KEY_BASE=changemechangemechangeme" OSEM_EMAIL_ADDRESS="osem@mailhog" OSEM_SMTP_AUTHENTICATION="login" OSEM_SMTP_ADDRESS="mailhog" -OSEM_SMTP_PORT="1025" +OSEM_SMTP_PORT=1025 OSEM_SMTP_USERNAME="mailhog" OSEM_SMTP_PASSWORD="mailhog" diff --git a/dotenv.example b/dotenv.example index e0e3f519..6d8dd025 100644 --- a/dotenv.example +++ b/dotenv.example @@ -46,6 +46,10 @@ OSEM_SUSE_SECRET='' STRIPE_PUBLISHABLE_KEY='' STRIPE_SECRET_KEY='' +# (OPTIONAL) Skylight keys. See https://www.skylight.io/oss. +# SKYLIGHT_AUTHENTICATION='' +# SKYLIGHT_PUBLIC_DASHBOARD_URL='https://oss.skylight.io/app/applications/xxxxxxxxxxxx' + # Disable linting of factories in the test suite. # Speeds up turn around times of tests OSEM_FACTORY_LINT="false" diff --git a/spec/controllers/admin/conferences_controller_spec.rb b/spec/controllers/admin/conferences_controller_spec.rb index ff9d676b..984ef137 100644 --- a/spec/controllers/admin/conferences_controller_spec.rb +++ b/spec/controllers/admin/conferences_controller_spec.rb @@ -95,6 +95,75 @@ describe Admin::ConferencesController do get :show, id: conference.short_title expect(response).to render_template :show end + + it 'assigns conference withdrawn events distribution to event_type_distribution_withdrawn' do + conference + create(:event, program: conference.program) + workshop = create(:event_type, title: 'Workshop', color: '#000000', program: conference.program) + lecture = create(:event_type, title: 'Lecture', color: '#ffffff', program: conference.program) + get :show, id: conference.short_title + expect(assigns(:event_type_distribution_withdrawn)).to be_empty + create(:event, program: conference.program, state: 'withdrawn', event_type: lecture) + create(:event, program: conference.program, state: 'withdrawn', event_type: workshop) + get :show, id: conference.short_title + expect(assigns(:event_type_distribution_withdrawn)).not_to be_empty + result = {} + result['Workshop'] = { + 'value' => 1, + 'color' => '#000000' + } + result['Lecture'] = { + 'value' => 1, + 'color' => '#FFFFFF' + } + expect(assigns(:event_type_distribution_withdrawn)).to eq(result) + end + + it 'assigns conference withdrawn difficulty level distribution to difficulty_levels_distribution_withdrawn' do + conference + create(:event, program: conference.program) + get :show, id: conference.short_title + expect(assigns(:difficulty_levels_distribution_withdrawn)).to be_empty + easy = create(:difficulty_level, title: 'Easy', color: '#000000') + hard = create(:difficulty_level, title: 'Hard', color: '#ffffff') + create(:event, program: conference.program, state: 'withdrawn', difficulty_level: easy) + create(:event, program: conference.program, state: 'withdrawn', difficulty_level: hard) + get :show, id: conference.short_title + expect(assigns(:difficulty_levels_distribution_withdrawn)).not_to be_empty + result = {} + result['Easy'] = { + 'value' => 1, + 'color' => '#000000' + } + result['Hard'] = { + 'value' => 1, + 'color' => '#FFFFFF' + } + expect(assigns(:difficulty_levels_distribution_withdrawn)).to eq(result) + end + + it 'assigns conference withdrawn track distribution to tracks_distribution_withdrawn' do + conference + create(:event, program: conference.program) + get :show, id: conference.short_title + expect(assigns(:tracks_distribution_withdrawn)).to be_empty + track_one = create(:track, name: 'Track One', color: '#000000', program: conference.program) + track_two = create(:track, name: 'Track Two', color: '#FFFFFF', program: conference.program) + create(:event, program: conference.program, state: 'withdrawn', track: track_one) + create(:event, program: conference.program, state: 'withdrawn', track: track_two) + get :show, id: conference.short_title + expect(assigns(:tracks_distribution_withdrawn)).not_to be_empty + result = {} + result['Track One'] = { + 'value' => 1, + 'color' => '#000000' + } + result['Track Two'] = { + 'value' => 1, + 'color' => '#FFFFFF' + } + expect(assigns(:tracks_distribution_withdrawn)).to eq(result) + end end describe 'GET #index' do diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index 214e522a..4d1c2eab 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -30,7 +30,45 @@ describe ApplicationController, type: :controller do expect(controller.after_sign_in_path_for(user)).to eq conferences_path end end - + end + + end + +end + +describe ApplicationController, type: :request do + let(:conference) { create(:conference) } + + describe 'Skylight link' do + + around do |example| + original_value = ENV['SKYLIGHT_PUBLIC_DASHBOARD_URL'] + example.run + ENV['SKYLIGHT_PUBLIC_DASHBOARD_URL'] = original_value + end + + context 'when SKYLIGHT_PUBLIC_DASHBOARD_URL is set' do + before do + ENV['SKYLIGHT_PUBLIC_DASHBOARD_URL'] = 'https://oss.skylight.io/app/applications/my-osem' + end + + it 'should include a link to view performance data' do + get '/' + expect(response.body).to match(/Performance data/i) + expect(response.body).to include('https://oss.skylight.io/app/applications/my-osem') + end + end + + context 'when SKYLIGHT_PUBLIC_DASHBOARD_URL is not set' do + before do + ENV['SKYLIGHT_PUBLIC_DASHBOARD_URL'] = nil + end + + it 'should not include a link to view performance data' do + get '/' + expect(response.body).to_not match(/performance data/i) + expect(response.body).to_not match(/skylight/i) + end end end diff --git a/spec/models/ability_spec.rb b/spec/models/ability_spec.rb index 7936ddbb..26522913 100644 --- a/spec/models/ability_spec.rb +++ b/spec/models/ability_spec.rb @@ -89,11 +89,131 @@ describe 'User' do let(:other_self_organized_track) { create(:track, :self_organized) } it{ should be_able_to(:manage, user) } - it{ should be_able_to(:manage, registration_public) } it{ should be_able_to(:manage, registration_not_public) } - it{ should_not be_able_to(:new, Registration.new(conference_id: conference_with_closed_registration.id))} - it{ should_not be_able_to(:create, Registration.new(conference_id: conference_with_closed_registration.id))} + + # Test for user can register or not + context 'when user is not a speaker with event confirmed' do + let(:conference) { create(:conference) } + + context 'when the registration is closed' do + before :each do + create(:registration_period, conference: conference, start_date: Date.current - 6.days, end_date: Date.current - 6.days) + end + + it{ should_not be_able_to(:new, Registration.new(conference: conference)) } + it{ should_not be_able_to(:create, Registration.new(conference: conference)) } + end + + context 'when the registration period is not set' do + it{ should_not be_able_to(:new, Registration.new(conference: conference)) } + it{ should_not be_able_to(:create, Registration.new(conference: conference)) } + end + + context 'when user has already registered' do + before :each do + create(:registration, conference: conference, user: user) + end + + it{ should_not be_able_to(:new, Registration.new(conference: conference)) } + it{ should_not be_able_to(:create, Registration.new(conference: conference)) } + end + + context 'when registrations are open' do + before :each do + create(:registration_period, conference: conference) + end + + it{ should be_able_to(:new, Registration.new(conference: conference)) } + it{ should be_able_to(:create, Registration.new(conference: conference)) } + + context 'when user has not registered with no registration_limit_exceeded' do + before :each do + conference.registration_limit = 1 + end + + it{ should be_able_to(:new, Registration.new(conference: conference)) } + it{ should be_able_to(:create, Registration.new(conference: conference)) } + end + + context 'when user has not registered with registration_limit_exceeded' do + before :each do + conference.registration_limit = 1 + create(:registration, conference: conference, user: user2) + end + + it{ should_not be_able_to(:new, Registration.new(conference: conference)) } + it{ should_not be_able_to(:create, Registration.new(conference: conference)) } + end + end + end + + context 'when user is a speaker with event confirmed' do + let(:conference_with_speaker_confirmed) { create(:conference) } + let(:event_with_speaker_confirmed) { create(:event, state: 'confirmed', program: conference_with_speaker_confirmed.program) } + + before :each do + event_with_speaker_confirmed.speakers << user + end + + context 'when registration period is not set' do + it{ should_not be_able_to(:new, Registration.new(conference: conference_with_speaker_confirmed)) } + it{ should_not be_able_to(:create, Registration.new(conference: conference_with_speaker_confirmed)) } + end + + context 'when speaker has already registered' do + before :each do + create(:registration, conference: conference_with_speaker_confirmed, user: user) + end + + it{ should_not be_able_to(:new, Registration.new(conference: conference_with_speaker_confirmed)) } + it{ should_not be_able_to(:create, Registration.new(conference: conference_with_speaker_confirmed)) } + end + + context 'when registration period is set' do + before :each do + create(:registration_period, conference: conference_with_speaker_confirmed) + end + + context 'when registrations are open' do + context 'when speaker has not registered' do + it{ should be_able_to(:new, Registration.new(conference: conference_with_speaker_confirmed)) } + it{ should be_able_to(:create, Registration.new(conference: conference_with_speaker_confirmed)) } + end + + context 'when registration_limit_exceeded' do + before :each do + conference_with_speaker_confirmed.registration_limit = 1 + create(:registration, conference: conference_with_speaker_confirmed, user: user2) + end + + it{ should be_able_to(:new, Registration.new(conference: conference_with_speaker_confirmed)) } + it{ should be_able_to(:create, Registration.new(conference: conference_with_speaker_confirmed)) } + end + end + + context 'when registrations are closed' do + before :each do + create(:registration_period, conference: conference_with_speaker_confirmed, start_date: Date.current - 6.days, end_date: Date.current - 6.days) + end + + context 'when speaker has not registered' do + it{ should be_able_to(:new, Registration.new(conference: conference_with_speaker_confirmed)) } + it{ should be_able_to(:create, Registration.new(conference: conference_with_speaker_confirmed)) } + end + + context 'with registration_limit_exceeded' do + before :each do + conference_with_speaker_confirmed.registration_limit = 1 + create(:registration, conference: conference_with_speaker_confirmed, user: user2) + end + + it{ should be_able_to(:new, Registration.new(conference: conference_with_speaker_confirmed)) } + it{ should be_able_to(:create, Registration.new(conference: conference_with_speaker_confirmed)) } + end + end + end + end it{ should be_able_to(:index, Ticket) } it{ should be_able_to(:manage, TicketPurchase.new(user_id: user.id)) }