diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..e235b236 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +Dockerfile +docker-compose.* diff --git a/.gitignore b/.gitignore index 4d5fd427..75a1bdc6 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,5 @@ pickle-email-*.html .env.development .env.test .env.local +docker-compose.env +docker-compose.yml diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..dab432ca --- /dev/null +++ b/Dockerfile @@ -0,0 +1,45 @@ +FROM ruby:2.3 + +MAINTAINER TheAssassin + +# required for compiling assets +RUN apt-get update && \ + apt-get install -y nodejs nodejs-legacy mariadb-client imagemagick + +# used to run the container without root permissions +RUN adduser --home /osem/ --system --group --disabled-login --disabled-password osem + +# required to detect when the database is up and running in init.sh +RUN cd /usr/bin && \ + wget https://github.com/jwilder/dockerize/releases/download/v0.3.0/dockerize-linux-amd64-v0.3.0.tar.gz -O dockerize.tar.gz && \ + echo "36e8319cdf9d2b07340f456ec61cfa0f495ec6c130b02ad9c116fd55a5c43fa1 dockerize.tar.gz" | sha256sum -c && \ + tar -xf dockerize.tar.gz && \ + rm dockerize.tar.gz + +# explicitly add Gemfile and install dependencies using bundler to make use of +# Docker's caching +WORKDIR /osem/ +COPY Gemfile /osem/ +COPY Gemfile.lock /osem/ +RUN bundle install --without test development + +# add OSEM files and prepare them for use inside a Docker container +COPY . /osem/ +RUN chown osem.osem /osem/ -R && \ + mv /osem/config/database.yml.docker /osem/config/database.yml + +# data directory is used to cache the secret key in a file +ENV DATA_DIR /data +RUN install -d -m 0700 -o osem $DATA_DIR +VOLUME ["$DATA_DIR"] + +USER osem +EXPOSE 9292 + +COPY docker/init.sh /init.sh + +# a user could override this if they wanted to serve the static files directly +# from a webserver +ENV RAILS_SERVE_STATIC_FILES 1 + +CMD ["bash", "/init.sh"] diff --git a/Gemfile b/Gemfile index e6245e78..1af89f34 100644 --- a/Gemfile +++ b/Gemfile @@ -22,9 +22,6 @@ gem 'responders', '~> 2.0' gem 'mysql2' # gem 'pg' -# for observing records -gem 'rails-observers' - # for tracking data changes gem 'paper_trail' diff --git a/Gemfile.lock b/Gemfile.lock index 89d52471..9ea214c2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -399,8 +399,6 @@ GEM rails-i18n (4.0.8) i18n (~> 0.7) railties (~> 4.0) - rails-observers (0.1.2) - activemodel (~> 4.0) rails_12factor (0.0.3) rails_serve_static_assets rails_stdout_logging @@ -632,7 +630,6 @@ DEPENDENCIES rails-assets-trianglify! rails-assets-waypoints! rails-i18n (~> 4.0.0) - rails-observers rails_12factor rdoc-generator-fivefish redcarpet diff --git a/INSTALL.md b/INSTALL.md index 9cbfafcc..29dc5cec 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -11,7 +11,9 @@ OSEM is an [semantic versioned](http://semver.org/) app. That means given a vers ## Download You can find the latest OSEM releases on our [release page](https://github.com/openSUSE/osem/releases/latest) ([older release here](https://github.com/openSUSE/osem/releases)) + ## Deploy + OSEM is a *Ruby on Rails* application. We recommend to run OSEM in production with [mod_passenger](https://www.phusionpassenger.com/download/#open_source) and the [apache web-server](https://www.apache.org/). There are tons of guides on how to deploy rails apps on various base operating systems. [Check Google](https://encrypted.google.com/search?hl=en&q=ruby%20on%20rails%20apache%20passenger) ;-) @@ -24,6 +26,55 @@ If you have an heroku account you can also Deploy +### Deploy with Docker + +You can deploy OSEM using [Docker](https://docker.com/) and [Docker-Compose](https://docs.docker.com/compose/overview/). + +*This is just a short guide and does not explain how to use Docker and/or Docker-Compose. You need some experience with these tools to be able to deploy OSEM with Docker properly.* + +First of all, copy `docker-compose.yml.example` to `docker-compose.yml` and `docker-compose.env.example` to `docker-compose.env`. You should immediately change +`docker-compose.env`'s permissions to `0600` to make sure all the passphrases in it are kept secret. +(Tip: the easiest and most secure way to do it is to do it with a single command, for example `install -m 0600 docker-compose.env.example docker-compose.env`). + +There are two configurations to deploy OSEM with Docker: *evaluation mode* and *production mode*. + +#### Evaluation mode + +If you want to evaluate OSEM to see if it fits your needs, the default configuration in `docker-compose.env` will work perfectly fine for you. +For convenience reasons, `docker-compose.yml` already contains a [MailHog](https://github.com/mailhog/MailHog) service configuration. MailHog +is going to catch every email sent by OSEM and displays them on a special web service. Thus, it eliminitates the need to set up an SMTP server just to try out OSEM. +Just point your browser to http://localhost:8025 to get access to registration confirmation links etc. + +Run `docker-compose up --build` to start the services. On first run, it will take a few minutes to initialize the database. Thus, wait a few minutes before you open up +http://localhost:9292 in your browser. + +#### Production mode + +To deploy OSEM for production, you have to make a few changes to `docker-compose.yml`. First, remove (or comment) the `mailhog` service, as it is only useful for evaluation and +cannot be used for production. +You can change the forwarded port from port `9292` to any other value if this port is already in use or you just want to use another one. + +Next, you have to modify `docker-compose.env`. This file works as a Docker-like replacement for the regular Rails `.env` files described below. +You can configure any of the configuration values shown in the **Configure** section below in it. The most essential variables are already configured to standard +values in `docker-compose.env` which you most likely want to change. + +First, you need to modify the email related settings. You need a working SMTP server for OSEM to send out registration confirmation mails etc. + +For security reasons, the following variables have to be changed, too: + + - `MYSQL_PASSWORD` + - `MYSQL_ROOT_PASSWORD` + - `SECRET_KEY_BASE` + +These variables need to be set to the correct values at first, as they are used to initialize everything. Modification of these variables after installation and initialization is more +complicated and out of this document's scope. + +As with any other Docker-Compose configuration, run `docker-compose up --build` (or `docker-compose up --build -d` to run in background) to start the services. During the first +start, the database has to be initialized which can take several minutes. The web service is by default exposed on localhost only as it is intended to be served by a reverse proxy (for SSL +termination, caching etc.). +You should not directly expose the web server port unless you have a good reason to do so. + + ## Configure There are a couple of environment variables you can set to configure OSEM. Check out the *dotenv.example* file. diff --git a/app/models/comment.rb b/app/models/comment.rb index 947b2f54..4bb90719 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -8,7 +8,7 @@ class Comment < ActiveRecord::Base # want user to vote on the quality of comments. #acts_as_votable - belongs_to :commentable, polymorphic: true + belongs_to :commentable, counter_cache: true, polymorphic: true # NOTE: Comments belong to a user belongs_to :user diff --git a/app/models/concerns/revision_count.rb b/app/models/concerns/revision_count.rb new file mode 100644 index 00000000..71789f34 --- /dev/null +++ b/app/models/concerns/revision_count.rb @@ -0,0 +1,11 @@ +module RevisionCount + extend ActiveSupport::Concern + + included do + after_update :increment_revision + end + + def increment_revision + conference.update_column(:revision, conference.revision + 1) + end +end diff --git a/app/models/conference.rb b/app/models/conference.rb index cf94b334..68cdc186 100644 --- a/app/models/conference.rb +++ b/app/models/conference.rb @@ -1,4 +1,5 @@ class Conference < ActiveRecord::Base + include RevisionCount require 'uri' serialize :events_per_week, Hash # Needed to call 'Conference.with_role' in /models/ability.rb @@ -6,6 +7,8 @@ class Conference < ActiveRecord::Base resourcify :roles, dependent: :delete_all default_scope { order('start_date DESC') } + scope :upcoming, (-> { where('end_date >= ?', Date.current) }) + scope :past, (-> { where('end_date < ?', Date.current) }) belongs_to :organization @@ -734,6 +737,15 @@ class Conference < ActiveRecord::Base (start_hour..(end_hour - 1)).cover?(current_hour) ? current_hour - start_hour : 0 end + ## + # Return the current conference object to be used in RevisionCount + # + # ====Returns + # * +ActiveRecord+ + def conference + self + end + private # Returns a different html colour for every i and consecutive colors are diff --git a/app/models/event.rb b/app/models/event.rb index f8f9c2d8..8436aac9 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -1,5 +1,6 @@ class Event < ActiveRecord::Base include ActiveRecord::Transitions + include RevisionCount has_paper_trail on: [:create, :update], ignore: [:updated_at, :guid, :week], meta: { conference_id: :conference_id } acts_as_commentable @@ -251,6 +252,10 @@ class Event < ActiveRecord::Base event_schedules.find_by(schedule_id: program.selected_schedule_id).try(:start_time) end + def conference + program.conference + end + private ## diff --git a/app/models/physical_ticket.rb b/app/models/physical_ticket.rb index a0874a6e..6142c875 100644 --- a/app/models/physical_ticket.rb +++ b/app/models/physical_ticket.rb @@ -3,4 +3,5 @@ class PhysicalTicket < ActiveRecord::Base has_one :ticket, through: :ticket_purchase has_one :conference, through: :ticket_purchase has_one :user, through: :ticket_purchase + has_many :ticket_scannings end diff --git a/app/models/revision_observer.rb b/app/models/revision_observer.rb deleted file mode 100644 index 4d2784f4..00000000 --- a/app/models/revision_observer.rb +++ /dev/null @@ -1,26 +0,0 @@ -# -# suseconferenceclient relies on a 'revision' attribute for caching and -# doing some calculations. -# -# It should be incremented after any change in the conference or in any -# associated models -# -# This observer updates the revision column in a non-intrusive way, -# preventing validations, callbacks or exceptions to be triggered -# -# Relying on paper_trail could also be an option, but a 'revision' column -# in table 'conferences' looks like a more simple and straightforward solution -# -class RevisionObserver < ActiveRecord::Observer - observe :conference, :event, :room, :track - - def after_save(model) - begin - conference = model.kind_of?(Conference) ? model : model.conference - conference.reload.increment(:revision) - conference.update_column(:revision, conference.revision) - rescue - nil - end - end -end diff --git a/app/models/room.rb b/app/models/room.rb index 1477a2c6..f8150f78 100644 --- a/app/models/room.rb +++ b/app/models/room.rb @@ -1,4 +1,5 @@ class Room < ActiveRecord::Base + include RevisionCount belongs_to :venue has_many :event_schedules, dependent: :destroy @@ -10,6 +11,10 @@ class Room < ActiveRecord::Base validates :size, numericality: { only_integer: true, greater_than: 0 }, allow_nil: true + def conference + venue.conference + end + private def generate_guid diff --git a/app/models/ticket_scanning.rb b/app/models/ticket_scanning.rb new file mode 100644 index 00000000..6ce9d00c --- /dev/null +++ b/app/models/ticket_scanning.rb @@ -0,0 +1,3 @@ +class TicketScanning < ActiveRecord::Base + belongs_to :physical_ticket +end diff --git a/app/models/track.rb b/app/models/track.rb index d3502bb0..f65506d8 100644 --- a/app/models/track.rb +++ b/app/models/track.rb @@ -1,4 +1,5 @@ class Track < ActiveRecord::Base + include RevisionCount belongs_to :program has_many :events, dependent: :nullify @@ -16,6 +17,10 @@ class Track < ActiveRecord::Base before_validation :capitalize_color + def conference + program.conference + end + private def generate_guid diff --git a/app/views/admin/events/index.html.haml b/app/views/admin/events/index.html.haml index 0e204c87..50d7acd5 100644 --- a/app/views/admin/events/index.html.haml +++ b/app/views/admin/events/index.html.haml @@ -184,4 +184,4 @@ %ul.dropdown-menu{ role: 'menu' } = render 'change_state_dropdown', event: event %td.text-center - = link_to "#{event.comment_threads.count}", admin_conference_program_event_path(@conference.short_title, event), anchor: 'comments-div' + = link_to "#{event.comments_count}", admin_conference_program_event_path(@conference.short_title, event), anchor: 'comments-div' diff --git a/app/views/admin/organizations/index.html.haml b/app/views/admin/organizations/index.html.haml index 52e2877a..aa682c57 100644 --- a/app/views/admin/organizations/index.html.haml +++ b/app/views/admin/organizations/index.html.haml @@ -20,9 +20,9 @@ %td = organization.name %td - = organization.conferences.count + = organization.conferences.upcoming.count %td - = organization.conferences.count + = organization.conferences.past.count %td .btn-group = link_to 'Edit', edit_admin_organization_path(organization), diff --git a/app/views/layouts/_admin_sidebar.html.haml b/app/views/layouts/_admin_sidebar.html.haml index 2c3eaf52..1c7bac9a 100644 --- a/app/views/layouts/_admin_sidebar.html.haml +++ b/app/views/layouts/_admin_sidebar.html.haml @@ -16,7 +16,7 @@ %span.fa.fa-cog Manage = conference.short_title - - if (current_user.is_admin) || (current_user.has_role? :organizer, :any) + - if can? :new, Conference.new %li = link_to(new_admin_conference_path) do %span.fa.fa-plus diff --git a/app/views/layouts/_admin_sidebar_index.html.haml b/app/views/layouts/_admin_sidebar_index.html.haml index 3844339b..01356f42 100644 --- a/app/views/layouts/_admin_sidebar_index.html.haml +++ b/app/views/layouts/_admin_sidebar_index.html.haml @@ -16,7 +16,7 @@ %span.fa.fa-cog Manage = conference.short_title - - if can? :create, Conference + - if can? :new, Conference.new %li = link_to(new_admin_conference_path) do %span.fa.fa-plus diff --git a/app/views/layouts/_user_menu.html.haml b/app/views/layouts/_user_menu.html.haml index 01432b3c..38a96b08 100644 --- a/app/views/layouts/_user_menu.html.haml +++ b/app/views/layouts/_user_menu.html.haml @@ -28,10 +28,10 @@ = link_to(admin_conferences_path()) do %span.fa.fa-home Administration - - if can? :create, Conference + - if can? :new, Conference.new =link_to(new_admin_conference_path) do %span.fa.fa-plus - Create Conference + New Conference -if @conference and @conference.id and can? :show, @conference %li = link_to(admin_conference_path(@conference.short_title)) do diff --git a/config/application.rb b/config/application.rb index 9123e5bb..07ba40f3 100644 --- a/config/application.rb +++ b/config/application.rb @@ -25,7 +25,6 @@ module Osem # Activate observers that should always be running. # config.active_record.observers = :cacher, :garbage_collector, :forum_observer - config.active_record.observers = :revision_observer # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. diff --git a/config/database.yml.docker b/config/database.yml.docker new file mode 100644 index 00000000..60ef8947 --- /dev/null +++ b/config/database.yml.docker @@ -0,0 +1,7 @@ +production: + adapter: mysql2 + host: <%= ENV['DATABASE_HOST'] %> + port: <%= ENV['DATABASE_PORT'] %> + username: <%= ENV['MYSQL_USER'] %> + password: <%= ENV['MYSQL_PASSWORD'] %> + database: <%= ENV['MYSQL_DATABASE'] %> diff --git a/db/migrate/20161229080315_add_comments_count_to_events.rb b/db/migrate/20161229080315_add_comments_count_to_events.rb new file mode 100644 index 00000000..e1d6ebec --- /dev/null +++ b/db/migrate/20161229080315_add_comments_count_to_events.rb @@ -0,0 +1,10 @@ +class AddCommentsCountToEvents < ActiveRecord::Migration + def change + add_column :events, :comments_count, :integer, default: 0, null: false + + Event.find_each do |event| + comments_count = event.comment_threads.count + event.update_attribute(:comments_count, comments_count) unless comments_count.zero? + end + end +end diff --git a/db/migrate/20170108053041_add_default_to_revision_in_conference.rb b/db/migrate/20170108053041_add_default_to_revision_in_conference.rb new file mode 100644 index 00000000..1c3571de --- /dev/null +++ b/db/migrate/20170108053041_add_default_to_revision_in_conference.rb @@ -0,0 +1,5 @@ +class AddDefaultToRevisionInConference < ActiveRecord::Migration + def change + change_column :conferences, :revision, :integer, default: 0, null: false + end +end diff --git a/db/migrate/20170711102511_create_ticket_scannings.rb b/db/migrate/20170711102511_create_ticket_scannings.rb new file mode 100644 index 00000000..d1879f68 --- /dev/null +++ b/db/migrate/20170711102511_create_ticket_scannings.rb @@ -0,0 +1,9 @@ +class CreateTicketScannings < ActiveRecord::Migration + def change + create_table :ticket_scannings do |t| + t.integer :physical_ticket_id, null: false + + t.timestamps null: false + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 4a9cc724..b9f8e3f4 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,22 +11,19 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170629162450) do - - # These are extensions that must be enabled in order to support this database - enable_extension "plpgsql" +ActiveRecord::Schema.define(version: 20170711102511) do create_table "ahoy_events", force: :cascade do |t| - t.integer "visit_id" + t.uuid "visit_id", limit: 16 t.integer "user_id" t.string "name" t.text "properties" t.datetime "time" end - add_index "ahoy_events", ["time"], name: "index_ahoy_events_on_time", using: :btree - add_index "ahoy_events", ["user_id"], name: "index_ahoy_events_on_user_id", using: :btree - add_index "ahoy_events", ["visit_id"], name: "index_ahoy_events_on_visit_id", using: :btree + add_index "ahoy_events", ["time"], name: "index_ahoy_events_on_time" + add_index "ahoy_events", ["user_id"], name: "index_ahoy_events_on_user_id" + add_index "ahoy_events", ["visit_id"], name: "index_ahoy_events_on_visit_id" create_table "answers", force: :cascade do |t| t.string "title" @@ -69,9 +66,9 @@ ActiveRecord::Schema.define(version: 20170629162450) do t.integer "rgt" end - add_index "comments", ["commentable_id"], name: "index_comments_on_commentable_id", using: :btree - add_index "comments", ["commentable_type"], name: "index_comments_on_commentable_type", using: :btree - add_index "comments", ["user_id"], name: "index_comments_on_user_id", using: :btree + add_index "comments", ["commentable_id"], name: "index_comments_on_commentable_id" + add_index "comments", ["commentable_type"], name: "index_comments_on_commentable_type" + add_index "comments", ["user_id"], name: "index_comments_on_user_id" create_table "commercials", force: :cascade do |t| t.string "commercial_id" @@ -93,7 +90,7 @@ ActiveRecord::Schema.define(version: 20170629162450) do t.datetime "created_at" t.datetime "updated_at" t.string "logo_file_name" - t.integer "revision" + t.integer "revision", default: 0, null: false t.boolean "use_vpositions", default: false t.boolean "use_vdays", default: false t.boolean "use_volunteers" @@ -142,7 +139,7 @@ ActiveRecord::Schema.define(version: 20170629162450) do t.datetime "updated_at" end - add_index "delayed_jobs", ["priority", "run_at"], name: "delayed_jobs_priority", using: :btree + add_index "delayed_jobs", ["priority", "run_at"], name: "delayed_jobs_priority" create_table "difficulty_levels", force: :cascade do |t| t.string "title" @@ -195,10 +192,10 @@ ActiveRecord::Schema.define(version: 20170629162450) do t.datetime "updated_at", null: false end - add_index "event_schedules", ["event_id", "schedule_id"], name: "index_event_schedules_on_event_id_and_schedule_id", unique: true, using: :btree - add_index "event_schedules", ["event_id"], name: "index_event_schedules_on_event_id", using: :btree - add_index "event_schedules", ["room_id"], name: "index_event_schedules_on_room_id", using: :btree - add_index "event_schedules", ["schedule_id"], name: "index_event_schedules_on_schedule_id", using: :btree + add_index "event_schedules", ["event_id", "schedule_id"], name: "index_event_schedules_on_event_id_and_schedule_id", unique: true + add_index "event_schedules", ["event_id"], name: "index_event_schedules_on_event_id" + add_index "event_schedules", ["room_id"], name: "index_event_schedules_on_room_id" + add_index "event_schedules", ["schedule_id"], name: "index_event_schedules_on_schedule_id" create_table "event_types", force: :cascade do |t| t.string "title", null: false @@ -242,6 +239,7 @@ ActiveRecord::Schema.define(version: 20170629162450) do t.boolean "is_highlight", default: false t.integer "program_id" t.integer "max_attendees" + t.integer "comments_count", default: 0, null: false end create_table "events_registrations", force: :cascade do |t| @@ -312,7 +310,7 @@ ActiveRecord::Schema.define(version: 20170629162450) do t.integer "schedule_interval", default: 15, null: false end - add_index "programs", ["selected_schedule_id"], name: "index_programs_on_selected_schedule_id", using: :btree + add_index "programs", ["selected_schedule_id"], name: "index_programs_on_selected_schedule_id" create_table "qanswers", force: :cascade do |t| t.integer "question_id" @@ -384,8 +382,8 @@ ActiveRecord::Schema.define(version: 20170629162450) do t.string "resource_type" end - add_index "roles", ["name", "resource_type", "resource_id"], name: "index_roles_on_name_and_resource_type_and_resource_id", using: :btree - add_index "roles", ["name"], name: "index_roles_on_name", using: :btree + add_index "roles", ["name", "resource_type", "resource_id"], name: "index_roles_on_name_and_resource_type_and_resource_id" + add_index "roles", ["name"], name: "index_roles_on_name" create_table "rooms", force: :cascade do |t| t.string "guid", null: false @@ -400,7 +398,7 @@ ActiveRecord::Schema.define(version: 20170629162450) do t.datetime "updated_at", null: false end - add_index "schedules", ["program_id"], name: "index_schedules_on_program_id", using: :btree + add_index "schedules", ["program_id"], name: "index_schedules_on_program_id" create_table "splashpages", force: :cascade do |t| t.integer "conference_id" @@ -470,6 +468,12 @@ ActiveRecord::Schema.define(version: 20170629162450) do t.integer "week" end + create_table "ticket_scannings", force: :cascade do |t| + t.integer "physical_ticket_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + create_table "tickets", force: :cascade do |t| t.integer "conference_id" t.string "title", null: false @@ -524,17 +528,17 @@ ActiveRecord::Schema.define(version: 20170629162450) do t.boolean "is_disabled", default: false end - add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree - add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree - add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree - add_index "users", ["username"], name: "index_users_on_username", unique: true, using: :btree + add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true + add_index "users", ["email"], name: "index_users_on_email", unique: true + add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true + add_index "users", ["username"], name: "index_users_on_username", unique: true create_table "users_roles", force: :cascade do |t| t.integer "role_id" t.integer "user_id" end - add_index "users_roles", ["user_id", "role_id"], name: "index_users_roles_on_user_id_and_role_id", using: :btree + add_index "users_roles", ["user_id", "role_id"], name: "index_users_roles_on_user_id_and_role_id" create_table "vchoices", force: :cascade do |t| t.integer "vday_id" @@ -578,10 +582,10 @@ ActiveRecord::Schema.define(version: 20170629162450) do t.integer "conference_id" end - add_index "versions", ["item_type", "item_id"], name: "index_versions_on_item_type_and_item_id", using: :btree + add_index "versions", ["item_type", "item_id"], name: "index_versions_on_item_type_and_item_id" create_table "visits", force: :cascade do |t| - t.uuid "visitor_id" + t.uuid "visitor_id", limit: 16 t.string "ip" t.text "user_agent" t.text "referrer" @@ -603,7 +607,7 @@ ActiveRecord::Schema.define(version: 20170629162450) do t.datetime "started_at" end - add_index "visits", ["user_id"], name: "index_visits_on_user_id", using: :btree + add_index "visits", ["user_id"], name: "index_visits_on_user_id" create_table "votes", force: :cascade do |t| t.integer "event_id" diff --git a/docker-compose.env.example b/docker-compose.env.example new file mode 100644 index 00000000..2aedbe73 --- /dev/null +++ b/docker-compose.env.example @@ -0,0 +1,39 @@ +## database related variables ## + +# variables prefixed with MYSQL_ are used by both database and web containers +# variables prefixed with DATABASE_ are used exlusively by the web container + +MYSQL_DATABASE=osem +MYSQL_USER=osem +MYSQL_PASSWORD=changemeimmediately +MYSQL_ROOT_PASSWORD=changemeevenmoreimmediately + +# the following settings should not be modified unless the database service +# is renamed in docker-compose.yml or you plan to use an external database +DATABASE_HOST=database +DATABASE_PORT=3306 + + +## OSEM options ## +# you can configure any option described in this document here instead of +# having to create a .env file: +# https://github.com/openSUSE/osem/blob/master/dotenv.example + +OSEM_NAME=Dockerized OSEM +OSEM_HOSTNAME=http://localhost:9292 +OSEM_ERRBIT_HOST=localhost +SECRET_KEY_BASE=changemechangemechangeme + +# these settings work for the MailHog server that is enabled by default in +# docker-compose.yml +# if you do not plan to use MailHog (you most likely don't want to), you need +# to change these settings to use an external working mailserver, otherwise +# your users are going to see the HTTP status 500 page +# you should comment out or remove the mailhog service from docker-compose.yml, +# too +OSEM_EMAIL_ADDRESS=osem@mailhog +OSEM_SMTP_AUTHENTICATION=login +OSEM_SMTP_ADDRESS=mailhog +OSEM_SMTP_PORT=1025 +OSEM_SMTP_USERNAME=mailhog +OSEM_SMTP_PASSWORD=mailhog diff --git a/docker-compose.yml.example b/docker-compose.yml.example new file mode 100644 index 00000000..0959523c --- /dev/null +++ b/docker-compose.yml.example @@ -0,0 +1,29 @@ +version: "2" + +services: + database: + image: mariadb:10.1 + env_file: docker-compose.env + volumes: + - database:/var/lib/mysql + + mailhog: + image: mailhog/mailhog:latest + ports: + - "127.0.0.1:8025:8025" + + web: + build: . + env_file: docker-compose.env + depends_on: + - database + - mailhog + ports: + - "127.0.0.1:9292:9292" + volumes: + - "web:/data" + +# these named volumes are used to persist data +volumes: + database: + web: diff --git a/docker/init.sh b/docker/init.sh new file mode 100644 index 00000000..3a2031f3 --- /dev/null +++ b/docker/init.sh @@ -0,0 +1,51 @@ +#! /bin/bash + +set -e + +# data directory is required for caching the secret key in a file +if [ "$DATA_DIR" == "" ]; then + echo -n "Error: DATA_DIR environment variable not set!" + echo "Are you sure you are running this script in a Docker container?" + exit 1 +fi + +SECRET_KEY_FILE="$DATA_DIR/secret_key" + +if [ ! -f "$SECRET_KEY_FILE" ]; then + echo ">>> Creating a new secret key file..." + install -m 0600 /dev/null "$SECRET_KEY_FILE" + SECRET_KEY=$(bundle exec rake secret) + echo "$key" > "$SECRET_KEY_FILE" + chmod -w "$SECRET_KEY_FILE" +else + SECRET_KEY=$(cat "$SECRET_KEY_FILE") +fi + +export SECRET_KEY +export RAILS_ENV=production + +install -m 0600 /dev/null .my.cnf +cat > .my.cnf <>> Waiting for database to get ready to connect..." +dockerize -wait tcp://$DATABASE_HOST:$DATABASE_PORT -timeout 60s true + +if [ $(echo "show tables;" | mysql --host $DATABASE_HOST --port $DATABASE_PORT $MYSQL_DATABASE | wc -l) -le 1 ]; then + echo ">>> Initializing database..." + bundle exec rake db:schema:load +fi + +echo ">>> Upgrading database..." +bundle exec rake db:migrate + +rm .my.cnf + +echo ">>> Precompiling assets..." +bundle exec rake assets:precompile + +echo ">>> Starting application server..." +exec bundle exec rails server -e production -b 0.0.0.0 -p 9292 diff --git a/spec/features/cfp_ability_spec.rb b/spec/features/cfp_ability_spec.rb index 235dedea..7af4e119 100644 --- a/spec/features/cfp_ability_spec.rb +++ b/spec/features/cfp_ability_spec.rb @@ -46,6 +46,7 @@ feature 'Has correct abilities' do expect(page).to_not have_link('Goals', href: "/admin/conferences/#{conference.short_title}/targets") expect(page).to have_link('Roles', href: "/admin/conferences/#{conference.short_title}/roles") expect(page).to have_link('Resources', href: "/admin/conferences/#{conference.short_title}/resources") + expect(page).to_not have_link('New Conference', href: '/admin/conferences/new') visit admin_conference_venue_rooms_path(conference.short_title) expect(current_path).to eq(admin_conference_venue_rooms_path(conference.short_title)) diff --git a/spec/features/info_desk_ability_spec.rb b/spec/features/info_desk_ability_spec.rb index 7d0039fe..20aa586b 100644 --- a/spec/features/info_desk_ability_spec.rb +++ b/spec/features/info_desk_ability_spec.rb @@ -46,6 +46,7 @@ feature 'Has correct abilities' do expect(page).to have_link('Registrations', href: "/admin/conferences/#{conference.short_title}/registrations") expect(page).to have_link('Questions', href: "/admin/conferences/#{conference.short_title}/questions") expect(page).to_not have_link('E-Mails', href: "/admin/conferences/#{conference.short_title}/emails") + expect(page).to_not have_link('New Conference', href: '/admin/conferences/new') visit admin_organizations_path expect(current_path).to eq(admin_organizations_path) diff --git a/spec/features/organization_admin_ability_spec.rb b/spec/features/organization_admin_ability_spec.rb index 3a5ffcd3..aefb89f4 100644 --- a/spec/features/organization_admin_ability_spec.rb +++ b/spec/features/organization_admin_ability_spec.rb @@ -55,6 +55,7 @@ feature 'Has correct abilities' do expect(page).to have_link('E-Mails', href: "/admin/conferences/#{conference.short_title}/emails") expect(page).to have_link('Roles', href: "/admin/conferences/#{conference.short_title}/roles") expect(page).to have_link('Resources', href: "/admin/conferences/#{conference.short_title}/resources") + expect(page).to have_link('New Conference', href: '/admin/conferences/new') visit edit_admin_conference_path(conference.short_title) expect(current_path).to eq(edit_admin_conference_path(conference.short_title)) diff --git a/spec/features/organizer_ability_spec.rb b/spec/features/organizer_ability_spec.rb index 7617bc0a..34d87b0a 100644 --- a/spec/features/organizer_ability_spec.rb +++ b/spec/features/organizer_ability_spec.rb @@ -58,6 +58,7 @@ feature 'Has correct abilities' do expect(page).to have_link('E-Mails', href: "/admin/conferences/#{conference.short_title}/emails") expect(page).to have_link('Roles', href: "/admin/conferences/#{conference.short_title}/roles") expect(page).to have_link('Resources', href: "/admin/conferences/#{conference.short_title}/resources") + expect(page).to_not have_link('New Conference', href: '/admin/conferences/new') visit admin_conference_path(other_conference.short_title) expect(page).to have_link('Add venue', href: "/admin/conferences/#{other_conference.short_title}/venue/new") diff --git a/spec/models/conference_spec.rb b/spec/models/conference_spec.rb index eaef9293..91c06140 100755 --- a/spec/models/conference_spec.rb +++ b/spec/models/conference_spec.rb @@ -1660,4 +1660,48 @@ describe Conference do expect{ conference.save }.to change{ EventSchedule.count }.from(2).to(1) end end + + describe '#revision' do + let(:track) { create(:track, program: subject.program) } + let(:event) { create(:event, program: subject.program, track: track) } + let(:venue) { create(:venue, conference: subject) } + let(:room) { create(:room, venue: venue) } + + it 'for change in conference' do + subject.title = 'changed' + expect{ subject.save }.to change { subject.revision }.by(1) + end + + it 'for change in event' do + event.title = 'changed' + expect{ event.save }.to change { subject.revision }.by(1) + end + + it 'for change in track' do + track.name = 'changed' + expect{ track.save }.to change { subject.revision }.by(1) + end + + it 'for change in room' do + room.name = 'changed' + expect{ room.save }.to change { subject.revision }.by(1) + end + end + + describe '.upcoming' do + let!(:upcoming_conference) { create(:conference) } + let!(:past_conference) { create(:conference, start_date: Date.current - 1.days, end_date: Date.current - 1.days) } + subject { Conference.upcoming } + + it { is_expected.to eq [upcoming_conference] } + end + + describe '.past' do + let!(:upcoming_conference) { create(:conference) } + let!(:past_conference1) { create(:conference, start_date: Date.current - 1.days, end_date: Date.current - 1.days) } + let!(:past_conference2) { create(:conference, start_date: Date.current - 2.days, end_date: Date.current - 1.days) } + subject { Conference.past } + + it { is_expected.to eq [past_conference1, past_conference2] } + end end diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index e1c2297c..b723d151 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -98,6 +98,29 @@ describe Event do end end + describe '#comments_count' do + context 'has a valid counter cache' do + before do + create(:comment, commentable: event) + end + + it 'successfully increments comments_count' do + expected = expect do + create(:comment, commentable: event) + end + expected.to change { event.comments_count }.by(1) + end + + it 'successfully decrements comments_count' do + expected = expect do + event.comment_threads.last.destroy + event.reload + end + expected.to change { event.comments_count }.by(-1) + end + end + end + describe 'scope ' do context 'confirmed' do it 'returns only confirmed events' do