diff --git a/Gemfile b/Gemfile index 7dbdc9ea2..8ba09b3c5 100644 --- a/Gemfile +++ b/Gemfile @@ -3,7 +3,6 @@ source 'https://rubygems.org' gem 'rails', '4.0.2' #gem 'turbolinks' -gem 'protected_attributes' # support legacy 'attr_accessible' gem 'rails-i18n' gem 'pg' diff --git a/Gemfile.lock b/Gemfile.lock index 430a06ade..443bcfb9e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -282,8 +282,6 @@ GEM polyglot (0.3.3) private_pub (1.0.3) faye - protected_attributes (1.0.5) - activemodel (>= 4.0.1, < 5.0) pry (0.9.12.4) coderay (~> 1.0) method_source (~> 0.8) @@ -470,7 +468,6 @@ DEPENDENCIES pg_search! poltergeist private_pub - protected_attributes pry-rails rack-cache rails (= 4.0.2) diff --git a/app/controllers/api/messages_controller.rb b/app/controllers/api/messages_controller.rb index 206a1a852..136ef571d 100644 --- a/app/controllers/api/messages_controller.rb +++ b/app/controllers/api/messages_controller.rb @@ -13,7 +13,7 @@ def create good = good || @talk.venue.users.include?(user) return render text: 'Computer says no', status: 740 unless good - message = @talk.messages.build(params[:message]) + message = @talk.messages.build(message_params) message.user = user message.save! @@ -43,4 +43,8 @@ def verified_request? super || form_authenticity_token == request.headers['X-XSRF-TOKEN'] end + def message_params + params.require(:message).permit(:content) + end + end diff --git a/app/controllers/api/social_shares_controller.rb b/app/controllers/api/social_shares_controller.rb index 0999d5029..92ab0a5f1 100644 --- a/app/controllers/api/social_shares_controller.rb +++ b/app/controllers/api/social_shares_controller.rb @@ -3,7 +3,7 @@ class Api::SocialSharesController < ApplicationController before_filter :authenticate_user! def create - @social_share = SocialShare.new(params[:social_share]) + @social_share = SocialShare.new(social_share_params) @social_share.request_ip = request.remote_addr @social_share.user_agent = request.user_agent @social_share.user_id = current_user.id @@ -18,9 +18,11 @@ def create end private + def social_share_params - params.require(:social_share).permit(:request_ip, :user_agent, - :shareable_id, :shareable_type, - :social_network) + params.require(:social_share).permit( :shareable_id, + :shareable_type, + :social_network ) end + end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 340d4df0f..94894e64f 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -76,10 +76,10 @@ def store_location before_filter :update_sanitized_params, if: :devise_controller? + # strong parameters for devise def update_sanitized_params devise_parameter_sanitizer.for(:sign_up) do |u| - u.permit(:firstname, :lastname, :accept_terms_of_use, - :email, :password, :password_confirmation) + u.permit(UsersController::PERMITTED_ATTRS) end end diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb index 68eca3ee0..c37627e80 100644 --- a/app/controllers/comments_controller.rb +++ b/app/controllers/comments_controller.rb @@ -7,7 +7,7 @@ def create authorize! :create, Comment venue = Venue.find(params[:venue_id]) - comment = venue.comments.build(params[:comment]) + comment = venue.comments.build(comment_params) comment.user = current_user if comment.save @@ -29,4 +29,8 @@ def send_email(comment, users) end end + def comment_params + params.require(:comment).permit(:content) + end + end diff --git a/app/controllers/embed_talks_controller.rb b/app/controllers/embed_talks_controller.rb index 478b519f2..a870dc842 100644 --- a/app/controllers/embed_talks_controller.rb +++ b/app/controllers/embed_talks_controller.rb @@ -1,4 +1,5 @@ class EmbedTalksController < ApplicationController + after_action :allow_iframe, only: :show def show @@ -14,4 +15,5 @@ def show def allow_iframe response.headers.except! 'X-Frame-Options' end + end diff --git a/app/controllers/errors_controller.rb b/app/controllers/errors_controller.rb index 275e1819e..8b6c63b74 100644 --- a/app/controllers/errors_controller.rb +++ b/app/controllers/errors_controller.rb @@ -1,4 +1,5 @@ class ErrorsController < ApplicationController + skip_before_filter :check_browser # Handling exceptions dynamically using middleware. @@ -16,4 +17,5 @@ def show # Dedicated landing page for outdated browsers def upgrade_browser end + end diff --git a/app/controllers/reminders_controller.rb b/app/controllers/reminders_controller.rb new file mode 100644 index 000000000..794a9a85f --- /dev/null +++ b/app/controllers/reminders_controller.rb @@ -0,0 +1,36 @@ +# TODO use cancan for authorization +class RemindersController < ApplicationController + + before_action :authenticate_user! + + # GET /reminders + def index + @reminders = current_user.reminders + end + + # POST /reminders + def create + @reminder = Reminder.new + @reminder.user = current_user + + rememberable ||= Talk.find(params[:talk_id]) + rememberable ||= Venue.find(params[:venue_id]) + raise "Cannot find Rememberable with #{params.inspect}" if rememberable.nil? + @reminder.rememberable = rememberable + + if @reminder.save + redirect_to rememberable, notice: I18n.t('reminders.create.success') + else + redirect_to rememberable, error: I18n.t('reminders.create.failure') + end + end + + # DELETE /reminders/1 + def destroy + @reminder = Reminder.find(params[:id]) + @reminder.destroy + redirect_to current_user, anchor: 'reminders', + notice: I18n.t('reminders.destroy.success') + end + +end diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index 1b3e50619..0f3fa3bb2 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -2,15 +2,15 @@ class SearchController < ApplicationController PER_PAGE = 10 + before_action :set_query + # POST /search def create - redirect_to "/search/1/" + u(params[:query]) + redirect_to "/search/1/" + u(@query) end # GET /search/1/:query def show - @query = params[:query] - @results = PgSearch.multisearch(@query). paginate(page: params[:page], per_page: PER_PAGE) @@ -33,4 +33,9 @@ def u(str) ERB::Util.url_encode(str) end + def set_query + params.permit(:query) + @query = params[:query] + end + end diff --git a/app/controllers/talks_controller.rb b/app/controllers/talks_controller.rb index 8b298f4b8..28a5fc849 100644 --- a/app/controllers/talks_controller.rb +++ b/app/controllers/talks_controller.rb @@ -115,7 +115,8 @@ def talk_params params.require(:talk).permit(:title, :teaser, :starts_at_date, :starts_at_time, :duration, :description, :collect, :image, - :tag_list, :guest_list, :language) + :tag_list, :guest_list, :language, + :format) end end diff --git a/app/controllers/users/omniauth_callbacks_controller.rb b/app/controllers/users/omniauth_callbacks_controller.rb index 15c2ad5e7..81fd369bc 100644 --- a/app/controllers/users/omniauth_callbacks_controller.rb +++ b/app/controllers/users/omniauth_callbacks_controller.rb @@ -1,10 +1,10 @@ -require 'pp' - class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController + include Devise::Controllers::Rememberable def facebook - logger.debug("OmniauthCallbacks#facebook - omniauth.auth: \n #{request.env['omniauth.auth']}\n") + logger.debug("OmniauthCallbacks#facebook - omniauth.auth: \n" + + " #{request.env['omniauth.auth']}\n") @user = User.find_for_facebook_oauth(request.env["omniauth.auth"], current_user) unless @user.valid? @@ -12,7 +12,7 @@ def facebook flash[:error] = @user.errors.full_message(:email, "is in use") redirect_to new_user_registration_url and return end - + if @user.persisted? flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Facebook" remember_me(@user) diff --git a/app/controllers/users/registrations_controller.rb b/app/controllers/users/registrations_controller.rb index 12c3e22f9..ce42cf4a6 100644 --- a/app/controllers/users/registrations_controller.rb +++ b/app/controllers/users/registrations_controller.rb @@ -1,6 +1,7 @@ class Users::RegistrationsController < Devise::RegistrationsController + def new - resource = build_resource(params[:user]) + resource = build_resource(user_params) respond_with resource end @@ -8,4 +9,12 @@ def create @guest_user = session[:guest_user_id] = nil super end + + private + + def user_params + return {} unless params[:user] # for redirect on subscribe + params.require(:user).permit(:firstname, :lastname, :email) + end + end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index bba13b47e..46917e667 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -1,5 +1,14 @@ class UsersController < ApplicationController + PERMITTED_ATTRS = [ :firstname, + :lastname, + :accept_terms_of_use, + :email, + :avatar, + :header, + :password, + :password_confirmation ] + before_filter :authenticate_user!, :only => [:edit,:update,:destroy] # layout "application", :only => [:welcome] @@ -47,7 +56,7 @@ def edit # POST /users # POST /users.json def create - @user = User.new(params[:user]) + @user = User.new(user_params) respond_to do |format| if @user.save @@ -69,7 +78,7 @@ def update authorize! :update, @user respond_to do |format| - if @user.update_attributes(params[:user]) + if @user.update_attributes(user_params) format.html do redirect_to @user, flash: { notice: I18n.t("flash.actions.update.notice") } end @@ -100,4 +109,10 @@ def destroy end end + private + + def user_params + params.require(:user).permit(PERMITTED_ATTRS) + end + end diff --git a/app/controllers/venues_controller.rb b/app/controllers/venues_controller.rb index ab68c6c15..b5cce7b4a 100644 --- a/app/controllers/venues_controller.rb +++ b/app/controllers/venues_controller.rb @@ -23,10 +23,10 @@ def show format.html do @upcoming_talks = @venue.talks.where(state: [:prelive, :live]).ordered @archived_talks = @venue.talks.archived.ordered - + @participation = @venue.participations.find_by(user_id: current_user.id) - + @show_join = @participation.nil? && current_user != @venue.user end @@ -51,9 +51,6 @@ def new # GET /venues/1/edit def edit - if params[:renew] - @renew = true - end authorize! :edit, @venue respond_to do |format| format.html {} @@ -64,7 +61,7 @@ def edit # POST /venues # POST /venues.json def create - @venue = Venue.new(params[:venue]) + @venue = Venue.new(venue_params) @venue.user = current_user authorize! :create, @venue @@ -90,7 +87,7 @@ def update authorize! :update, @venue respond_to do |format| - if @venue.update_attributes(params[:venue]) + if @venue.update_attributes(venue_params) format.html { redirect_to @venue, notice: 'Venue was successfully updated.' } format.json { head :no_content } else @@ -113,7 +110,7 @@ def destroy end end - def tags # TODO: check if needed + def tags scope = ActsAsTaggableOn::Tag.where(["name ILIKE ?", "%#{params[:q]}%"]) tags = scope.paginate(:page => params[:page], :per_page => params[:limit] || 10) render json: { tags: tags, total: scope.count } @@ -131,4 +128,10 @@ def set_venue @venue = Venue.find(params[:id]) end + # Only allow a trusted parameter "white list" through. + def venue_params + params.require(:venue).permit(:title, :teaser, :description, + :image, :tag_list) + end + end diff --git a/app/models/appearance.rb b/app/models/appearance.rb index f4026f40f..d0a99578e 100644 --- a/app/models/appearance.rb +++ b/app/models/appearance.rb @@ -6,8 +6,6 @@ # * user_id [integer] - belongs to :user class Appearance < ActiveRecord::Base - attr_accessible :user_id - validates :user, :talk, presence: true belongs_to :user diff --git a/app/models/comment.rb b/app/models/comment.rb index 3a2ba0080..396ccd629 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -8,8 +8,6 @@ # * user_id [integer, not null] - belongs to :user class Comment < ActiveRecord::Base - attr_accessible :content - belongs_to :commentable, polymorphic: true belongs_to :user diff --git a/app/models/message.rb b/app/models/message.rb index 330f0d5c6..2693e3fb9 100644 --- a/app/models/message.rb +++ b/app/models/message.rb @@ -7,8 +7,6 @@ # * user_id [integer] - belongs to :user class Message < ActiveRecord::Base - attr_accessible :content - belongs_to :user belongs_to :talk diff --git a/app/models/reminder.rb b/app/models/reminder.rb new file mode 100644 index 000000000..99268ae56 --- /dev/null +++ b/app/models/reminder.rb @@ -0,0 +1,6 @@ +class Reminder < ActiveRecord::Base + belongs_to :user + belongs_to :rememberable, polymorphic: true + + validates :user, presence: true +end diff --git a/app/models/setting.rb b/app/models/setting.rb index b934b6445..1e5547644 100644 --- a/app/models/setting.rb +++ b/app/models/setting.rb @@ -22,8 +22,6 @@ class Setting < ActiveRecord::Base validates :key, :value, presence: true - attr_accessible :key, :value - class << self def get(key) # try to find an entry in the db diff --git a/app/models/social_share.rb b/app/models/social_share.rb index 13e99cfee..9ef6c3836 100644 --- a/app/models/social_share.rb +++ b/app/models/social_share.rb @@ -9,10 +9,9 @@ # * user_agent [string] - TODO: document me # * user_id [integer] - TODO: document me class SocialShare < ActiveRecord::Base - attr_accessible :request_ip, :user_agent, :user_id, :shareable_id, - :shareable_type, :social_network belongs_to :shareable, polymorphic: true validates :shareable_type, inclusion: %w(talk venue) + end diff --git a/app/models/talk.rb b/app/models/talk.rb index ed1a83750..5ff763c17 100644 --- a/app/models/talk.rb +++ b/app/models/talk.rb @@ -85,11 +85,6 @@ class Talk < ActiveRecord::Base acts_as_taggable - attr_accessible :title, :teaser, :duration, :uri, - :description, :collect, :image, :tag_list, - :guest_list, :starts_at_date, :starts_at_time, - :language - belongs_to :venue, :inverse_of => :talks has_many :appearances, dependent: :destroy has_many :guests, through: :appearances, source: :user diff --git a/app/models/user.rb b/app/models/user.rb index c71a87190..edd947ee2 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -29,19 +29,14 @@ class User < ActiveRecord::Base extend FriendlyId friendly_id :name, use: [:slugged, :finders] - attr_accessible :password, :password_confirmation, :remember_me - attr_accessible :email, :firstname, :lastname - attr_accessible :provider, :uid, :last_request_at - attr_accessible :accept_terms_of_use, :guest, :header, :avatar, :about - attr_accessible :timezone, :website - has_many :comments, dependent: :destroy has_many :messages, dependent: :destroy has_many :venues # as owner has_many :participations, dependent: :destroy has_many :participating_venues, through: :participations, source: :venue - + has_many :reminders, dependent: :destroy + dragonfly_accessor :header do default Rails.root.join('app/assets/images/defaults/user-header.jpg') end @@ -134,5 +129,10 @@ def for_select def insider? !!(email =~ /@voicerepublic.com$/) end + + def remembers?(model) + reminders.exists?( rememberable_id: model.id, + rememberable_type: model.class.name ) + end end diff --git a/app/models/venue.rb b/app/models/venue.rb index e9c5a8019..94f764156 100644 --- a/app/models/venue.rb +++ b/app/models/venue.rb @@ -22,9 +22,6 @@ class Venue < ActiveRecord::Base acts_as_taggable - attr_accessible :title, :teaser, :description, :tag_list, - :talks_attributes, :image, :uri - # TODO: rename to host belongs_to :user diff --git a/app/views/shared/_podcast.rss.builder b/app/views/shared/_podcast.rss.builder index 7231ac940..7743fc855 100644 --- a/app/views/shared/_podcast.rss.builder +++ b/app/views/shared/_podcast.rss.builder @@ -45,7 +45,7 @@ xml.rss namespaces.merge(version: '2.0') do langs = @podcast.talks.map(&:language).compact langs = %w(en) if langs.empty? main_lang = langs.inject(Hash.new { |h, k| h[k] = 0 }) { |h, l| h[l]+=1; h }.to_a.sort_by { |e| e.last }.last.first - xml.language main_lang + xml.language main_lang xml.image do xml.url @podcast.image_url xml.title do @@ -80,7 +80,7 @@ xml.rss namespaces.merge(version: '2.0') do talks.each do |talk| # skip talks where media is missing for whatever reason next unless talk.podcast_file - + xml.item do xml.title h talk.title @@ -89,6 +89,7 @@ xml.rss namespaces.merge(version: '2.0') do xml.itunes :summary, talk.description_as_plaintext xml.itunes :subtitle, talk.teaser + # TODO: Maybe we want to show the speakers here? xml.itunes :author, talk.venue.user.name xml.itunes :duration, talk.podcast_file[:duration] xml.itunes :explicit, 'no' diff --git a/app/views/shared/_reminder.html.haml b/app/views/shared/_reminder.html.haml new file mode 100644 index 000000000..2041b56c9 --- /dev/null +++ b/app/views/shared/_reminder.html.haml @@ -0,0 +1,8 @@ +%li + - target = reminder.rememberable + - case target + - when Talk + = render partial: 'shared/talk_medium_box', locals: { talk: target } + - else + - raise "Hey, you need to implement display of #{target.class.name}" + = link_to t('.delete_reminder'), reminder, method: 'delete' diff --git a/app/views/shared/_social_meta_tags.html.haml b/app/views/shared/_social_meta_tags.html.haml index 66e736e16..58743276b 100644 --- a/app/views/shared/_social_meta_tags.html.haml +++ b/app/views/shared/_social_meta_tags.html.haml @@ -2,7 +2,7 @@ - title = @talk.title - image = "https://#{request.host}#{@talk.flyer.path}" - keywords = @talk.try(:tag_list) -- author = @talk.venue.user.name +- author = @talk.speakers || @talk.venue.user.name - _user_url = user_url(@talk.user) - _venue_url = venue_url(@talk.venue) -# Google diff --git a/app/views/shared/_talk_medium_box.html.haml b/app/views/shared/_talk_medium_box.html.haml index a3aa350b2..ccf873351 100644 --- a/app/views/shared/_talk_medium_box.html.haml +++ b/app/views/shared/_talk_medium_box.html.haml @@ -1,4 +1,4 @@ -- talk = talk_medium_box +- talk ||= talk_medium_box %li.talk-medium-box.box-grey .left-box diff --git a/app/views/talks/_fields.html.haml b/app/views/talks/_fields.html.haml index a4e54f9f0..a90b0be2f 100644 --- a/app/views/talks/_fields.html.haml +++ b/app/views/talks/_fields.html.haml @@ -17,7 +17,8 @@ = f.input :language, as: :grouped_select, collection: Talk::LANGUAGES, input_html: { class: 'languageSelect' }, group_method: :last, label_method: :last, value_method: :first - + = f.input :format, collection: t('.formats') + %br %br -#testdatepicker diff --git a/app/views/talks/show.html.haml b/app/views/talks/show.html.haml index 54bbc4570..6e0052ea5 100644 --- a/app/views/talks/show.html.haml +++ b/app/views/talks/show.html.haml @@ -159,26 +159,36 @@ .talk-talkers-box.row .large-12.columns.text-center - .avatar.avatar-host - .avatar-box - = render partial: "shared/avatar_image", locals: {user: @talk.venue.user, img_size: 62} - .avatar-name.text-center - = @talk.venue.user.name - .avatar(ng-repeat="guest in guests()") - .avatar-box - = render partial: "shared/avatar_image", locals: {user: 'guest', img_size: 62} - .avatar-name.text-center - {{ guest.name }} - .host-actions(ng-show='showHostActions()') - .guest-demote-link(ng-click="demote(guest.id)") - = t('.demote') - + - unless @talk.speakers.blank? + %p.speakers= @talk.speakers + - else + .avatar.avatar-host + .avatar-box + = render partial: "shared/avatar_image", locals: {user: @talk.venue.user, img_size: 62} + .avatar-name.text-center + = @talk.venue.user.name + .avatar(ng-repeat="guest in guests()") + .avatar-box + = render partial: "shared/avatar_image", locals: {user: 'guest', img_size: 62} + .avatar-name.text-center + {{ guest.name }} + .host-actions(ng-show='showHostActions()') + .guest-demote-link(ng-click="demote(guest.id)") + = t('.demote') + .talk-visitors-action-box.row.collapse-large-only(ng-show="userIsAListener()") .large-12.columns .participate-button-box.text-center - url = [ @talk.venue, :participations ] = link_to t('.participate'), url, method: 'post', class: 'button-vr' + - unless current_user.remembers?(@talk) + .row + .large-12.columns + .remember-button-box.text-center + - url = [ @talk, :reminders ] + = link_to t('.remember'), url, method: 'post', class: 'button-vr' + .talk-host-and-participants-action-box.row .large-12.columns.text-center .talkers-action-box diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 3306286bb..3f6d525b6 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -34,6 +34,10 @@ %a(href="#venues") %span.icon-guide = t('.venues') + %dd.text-center + %a(href="#reminders") + %span.icon-reminders + = t('.reminders') -# -#%dd.text-center -# %a(href="#schedule") @@ -50,6 +54,13 @@ #venues.venues-content.content.active = render partial: 'shared/venue_medium_box', collection: @user.venues + #reminders.reminders-content.content + - if @user.reminders.empty? + = t('.no-reminders') + - else + %ul + = render partial: 'shared/reminder', collection: @user.reminders + #schedule.content TODO #activity.content TODO diff --git a/config/locales/en.yml b/config/locales/en.yml index 79e07d17f..c77f6d19b 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -805,7 +805,7 @@ en: download_flyer: Download embed_player: Embed Player download_talk: Download Talk - initializing: Initializing + initializing: Initializing talk_is_processing: Talk is processing please_reload_this_page: Please reload this page discussion: Chat @@ -836,6 +836,15 @@ en: title: Title teaser: Teaser tag_list: Tag list + formats: + - Conference + - Reading + - Lecture + - Meeting + - Discussion + - Podcast + - Concert + - Other index: talks_on_voice_republic: TALKS ON VOICE REPUBLIC now_live: NOW LIVE @@ -930,3 +939,14 @@ en:
  • Download Internet Explorer (If you have an older version of Windows, you need to upgrade to get a newer version of Internet explorer. It's easier to use Chrome or Firefox.)
  • + simple_form: + hints: + talk: + language: Please select the main language of your talk. + format: Please provide the format purely for informational purposes. + reminders: + create: + success: Reminder was successfully created. + failure: Reminder could not be created. + destroy: + success: Reminder was successfully destroyed. diff --git a/config/routes.rb b/config/routes.rb index 65e407d80..b6fb2b638 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -40,8 +40,11 @@ get :recent get :upcoming end + resources :reminders, only: [:create] end + resources :reminders, only: [:destroy] + devise_scope :user do delete "/users/sign_out" => "devise/sessions#destroy" end diff --git a/db/migrate/20140703142905_create_reminders.rb b/db/migrate/20140703142905_create_reminders.rb new file mode 100644 index 000000000..2b5d8b330 --- /dev/null +++ b/db/migrate/20140703142905_create_reminders.rb @@ -0,0 +1,10 @@ +class CreateReminders < ActiveRecord::Migration + def change + create_table :reminders do |t| + t.references :user, index: true + t.references :rememberable, polymorphic: true, index: true + + t.timestamps + end + end +end diff --git a/db/migrate/20140721092301_add_format_and_speakers_to_talks.rb b/db/migrate/20140721092301_add_format_and_speakers_to_talks.rb new file mode 100644 index 000000000..891073eb2 --- /dev/null +++ b/db/migrate/20140721092301_add_format_and_speakers_to_talks.rb @@ -0,0 +1,6 @@ +class AddFormatAndSpeakersToTalks < ActiveRecord::Migration + def change + add_column :talks, :format, :string + add_column :talks, :speakers, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 49522b40c..16f877f8d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,10 +11,12 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20140702134835) do +ActiveRecord::Schema.define(version: 20140721092301) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" + enable_extension "pg_trgm" + enable_extension "unaccent" create_table "active_admin_comments", force: true do |t| t.string "namespace" @@ -62,8 +64,8 @@ create_table "comments", force: true do |t| t.text "content" t.integer "user_id", null: false - t.datetime "created_at" - t.datetime "updated_at" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.integer "commentable_id" t.string "commentable_type" end @@ -102,8 +104,8 @@ create_table "participations", force: true do |t| t.integer "venue_id" t.integer "user_id" - t.datetime "created_at" - t.datetime "updated_at" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end add_index "participations", ["user_id"], name: "index_participations_on_user_id", using: :btree @@ -119,6 +121,17 @@ add_index "pg_search_documents", ["content"], name: "index_pg_search_documents_on_content", using: :btree + create_table "reminders", force: true do |t| + t.integer "user_id" + t.integer "rememberable_id" + t.string "rememberable_type" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "reminders", ["rememberable_id", "rememberable_type"], name: "index_reminders_on_rememberable_id_and_rememberable_type", using: :btree + add_index "reminders", ["user_id"], name: "index_reminders_on_user_id", using: :btree + create_table "settings", force: true do |t| t.string "key" t.string "value" @@ -186,6 +199,8 @@ t.string "grade" t.string "language", default: "en" t.string "slug" + t.string "format" + t.string "speakers" end add_index "talks", ["grade"], name: "index_talks_on_grade", using: :btree @@ -195,8 +210,8 @@ create_table "users", force: true do |t| t.string "firstname" t.string "lastname" - t.datetime "created_at" - t.datetime "updated_at" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.string "email", default: "", null: false t.string "encrypted_password", default: "", null: false t.string "reset_password_token" @@ -226,8 +241,8 @@ create_table "venues", force: true do |t| t.text "description" t.string "title" - t.datetime "created_at" - t.datetime "updated_at" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.string "teaser" t.integer "user_id" t.text "options", default: "--- {}\n" diff --git a/doc/strong_parameters.org b/doc/strong_parameters.org new file mode 100644 index 000000000..0cb073630 --- /dev/null +++ b/doc/strong_parameters.org @@ -0,0 +1,20 @@ +| Controller | Model | Permitted Params | +|----------------------+-------------+-----------------------------------------------------------------------------------------------------------| +| Api::Messages | Message | content | +| Api::SocialShares | SocialShare | shareable_id, shareable_type, social_network | +| Api::Talks | - | - | +| Api::Users | - | - | +|----------------------+-------------+-----------------------------------------------------------------------------------------------------------| +| Comments | Comment | content | +| EmbedTalks | - | - | +| Errors | - | - | +| LandingPage | - | - | +| Participations | - | - | +| Search | n/a | query | +| Talks | Talk | title, teaser, description, starts_at_time, starts_at_date, duration, record, image, tag_list, guest_list | +| Users | User | firstname, lastname, accept_terms_of_use, email, avatar, header, password, password_confirmation | +| Venues | Venue | title, teaser, description, image, tag_list | +|----------------------+-------------+-----------------------------------------------------------------------------------------------------------| +| Users::Registrations | User | :firstname, :lastname, :email | +| Users::Sessions | - | - | + diff --git a/lib/faye_ttl.rb b/lib/faye_ttl.rb new file mode 100644 index 000000000..2c876028a --- /dev/null +++ b/lib/faye_ttl.rb @@ -0,0 +1,61 @@ +# FayeTtl will cache incoming messages on selected channels +# for a given time (ttl) and annotate subscription responses +# of those channels with cached data. +# +# Options +# * channels - array of channels +# * ttl - number of seconds, defaults to 14400 (4 hours) +# +class FayeTtl < Struct.new(:opts) + + attr_accessor :history + + def added(e) + self.history = Hash.new { |h, k| h[k] = [] } + end + + def incoming(message, callback) + channel = message['channel'] + if channel.start_with?(*channels) + message['data']['timestamp'] = Time.now.to_i + history[channel] << message['data'] + end + + callback.call(message) + end + + def outgoing(message, callback) + channel = message['channel'] + if channel =~ /\/meta\/subscribe/ + subscription = message['subscription'] + if subscription.start_with?(*channels) + messages = history[subscription] + unless messages.empty? + # TODO move this into the incoming message handler + messages = messages.delete_if do |m| + (Time.now.to_i - m['timestamp']) > ttl + end + message['ext'] = { 'cached' => messages } + self.history[subscription] = messages + end + end + end + + callback.call(message) + end + + private + + def ttl + @ttl ||= opts[:ttl] || 14400 + end + + def channels + return @channels unless @channels.nil? + raise "no channels given" if opts[:channels].nil? + @channels = opts[:channels].tap do |r| + r.is_a?(Array) ? r : [ r ] + end + end + +end diff --git a/private_pub.ru b/private_pub.ru index e90717f20..6bc982adb 100644 --- a/private_pub.ru +++ b/private_pub.ru @@ -3,6 +3,7 @@ require "bundler/setup" require "yaml" require "faye" require "private_pub" +require File.expand_path('../lib/faye_ttl', __FILE__) Faye::WebSocket.load_adapter('thin') @@ -12,6 +13,12 @@ PrivatePub.load_config(file, env) faye = PrivatePub.faye_app +faye.add_extension FayeTtl.new(channels: %w( /dj + /event/talk + /monitoring + /notification + /stat )) + if env == 'development' faye.bind(:publish) do |client_id, channel, data| puts "publish #{client_id} #{channel} #{data.inspect}" diff --git a/spec/controllers/api/social_shares_controller_spec.rb b/spec/controllers/api/social_shares_controller_spec.rb index 454ad65f1..a15471b3b 100644 --- a/spec/controllers/api/social_shares_controller_spec.rb +++ b/spec/controllers/api/social_shares_controller_spec.rb @@ -5,7 +5,7 @@ describe 'anonymous' do it 'does not return with unauthorized' do @talk = FactoryGirl.create(:talk) - xhr :post, :create, id: @talk.id + xhr :post, :create, id: @talk.id, social_share: { asdf: 'asdf' } response.status.should_not be(401) response.status.should be(200) end @@ -31,7 +31,8 @@ it 'tracks what was shared' do expect { xhr :post, :create, social_share: { shareable_type: 'talk', - shareable_id: @talk.id, social_network: 'facebook' } + shareable_id: @talk.id, + social_network: 'facebook' } }.to change(SocialShare, :count).by(1) share = SocialShare.last @@ -42,7 +43,8 @@ end it 'returns json to verify success' do - xhr :post, :create, social_share: { shareable_type: 'talk', shareable_id: @talk.id } + xhr :post, :create, social_share: { shareable_type: 'talk', + shareable_id: @talk.id } res = JSON.parse(response.body) res['message'].should == I18n.t("social_share/has_been_tracked") end diff --git a/spec/controllers/reminders_controller_spec.rb b/spec/controllers/reminders_controller_spec.rb new file mode 100644 index 000000000..f12bb755f --- /dev/null +++ b/spec/controllers/reminders_controller_spec.rb @@ -0,0 +1,44 @@ +require 'spec_helper' + +describe RemindersController do + + describe "POST create" do + describe "with valid params" do + + let(:talk) { FactoryGirl.create(:talk) } + + it "creates a new Reminder" do + expect { + post :create, talk_id: talk.id + }.to change(Reminder, :count).by(1) + end + + it "assigns a newly created reminder as @reminder" do + post :create, talk_id: talk.id + assigns(:reminder).should be_a(Reminder) + assigns(:reminder).should be_persisted + end + + it "redirects to the created reminder" do + post :create, talk_id: talk.id + response.should redirect_to(talk) + end + end + end + + describe "DELETE destroy" do + it "destroys the requested reminder" do + reminder = FactoryGirl.create(:reminder) + expect { + delete :destroy, {:id => reminder.to_param} + }.to change(Reminder, :count).by(-1) + end + + it "redirects to the users reminders list" do + reminder = FactoryGirl.create(:reminder) + delete :destroy, {:id => reminder.to_param} + response.should redirect_to(controller.current_user) + end + end + +end diff --git a/spec/controllers/talks_controller_spec.rb b/spec/controllers/talks_controller_spec.rb index e08ab047d..b1af664ae 100644 --- a/spec/controllers/talks_controller_spec.rb +++ b/spec/controllers/talks_controller_spec.rb @@ -87,6 +87,12 @@ post :create, { venue_id: @venue.id, talk: valid_attributes } Talk.all[2].tag_list.should_not be_empty end + + it 'talk has attached format after creation' do + Talk.count.should be(2) # @talk & @talk_2 + post :create, { venue_id: @venue.id, talk: valid_attributes.merge(format: "foo_conf") } + Talk.all[2].format.should == "foo_conf" + end end describe "download message history" do diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index f0d5af0e6..22871fa67 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -7,26 +7,14 @@ request.env['warden'].stub :authenticate! => @user controller.stub :current_user => @user end - # This should return the minimal set of attributes required to create a valid - # User. As you add validations to User, be sure to - # update the return value of this method accordingly. - def valid_attributes - FactoryGirl.attributes_for(:user) - end - - # This should return the minimal set of values that should be in the session - # in order to pass any filters (e.g. authentication) defined in - # UsersController. Be sure to keep this updated too. - def valid_session - {} - end describe "GET show" do let(:user) { FactoryGirl.create(:user) } it "assigns the requested user as @user" do - get :show, {:id => user.to_param}, valid_session + get :show, {:id => user.to_param} assigns(:user).should eq(user) end + it "returns http success with format rss" do get :show, id: user.to_param, format: 'rss' response.should be_success @@ -36,22 +24,18 @@ def valid_session describe "PUT update" do describe "with valid params" do it "updates the requested user" do - # Assuming there are no other users in the database, this - # specifies that the User created on the previous line - # receives the :update_attributes message with whatever params are - # submitted in the request. - User.any_instance.should_receive(:update_attributes).with({'these' => 'params'}) - put :update, {:id => @user.to_param, :user => {'these' => 'params'}}, valid_session + put :update, id: @user.to_param, user: { firstname: 'Hugo' } + expect(@user.reload.firstname).to eq('Hugo') end it "assigns the requested user as @user" do - put :update, {:id => @user.to_param, :user => valid_attributes}, valid_session + put :update, id: @user.to_param, user: { firstname: 'Hugo' } assigns(:user).should eq(@user) end it "redirects to the user" do - put :update, {:id => @user.to_param, :user => @user.attributes[:email] }, valid_session # - response.should redirect_to(user_url(:id => @user)) + put :update, id: @user.to_param, user: { firstname: 'Hugo' } + response.should redirect_to(user_url(id: @user)) end end @@ -59,14 +43,14 @@ def valid_session it "assigns the user as @user" do # Trigger the behavior that occurs when invalid params are submitted User.any_instance.stub(:save).and_return(false) - put :update, {:id => @user.to_param, :user => {}}, valid_session + put :update, id: @user.to_param, user: { firstname: 'Hugo' } assigns(:user).should eq(@user) end it "re-renders the 'edit' template" do # Trigger the behavior that occurs when invalid params are submitted User.any_instance.stub(:save).and_return(false) - put :update, {:id => @user.to_param, :user => {}}, valid_session + put :update, id: @user.to_param, user: { firstname: 'Hugo' } response.should render_template("edit") end end diff --git a/spec/controllers/venues_controller_spec.rb b/spec/controllers/venues_controller_spec.rb index 4fff760a4..6ac081540 100644 --- a/spec/controllers/venues_controller_spec.rb +++ b/spec/controllers/venues_controller_spec.rb @@ -31,6 +31,10 @@ def valid_attributes FactoryGirl.attributes_for(:venue) end + def invalid_attributes + { asdf: 'asdf' } + end + def valid_session {} end @@ -102,12 +106,12 @@ def valid_session describe "with invalid params" do it "assigns a newly created but unsaved venue as @venue" do - post :create, {:venue => { }}, valid_session + post :create, {venue: invalid_attributes}, valid_session assigns(:venue).should be_a_new(Venue) end it "re-renders the 'new' template" do - post :create, {:venue => { }}, valid_session + post :create, {venue: invalid_attributes}, valid_session response.should render_template("new") end @@ -123,14 +127,9 @@ def valid_session describe "PUT update" do describe "with valid params" do it "updates the requested venue" do - venue = FactoryGirl.create(:venue, :user => @user) - _time = Time.now - # Assuming there are no other venues in the database, this - # specifies that the Venue created on the previous line - # receives the :update_attributes message with whatever params are - # submitted in the request. - Venue.any_instance.should_receive(:update_attributes).with({ "start_time" => _time.to_s }) - put :update, {:id => venue.to_param, :venue => { "start_time" => _time.to_s }}, valid_session + venue = FactoryGirl.create(:venue, user: @user) + put :update, {id: venue.to_param, venue: { title: 'blablub' }}, valid_session + expect(venue.reload.title).to eq('blablub') end it "assigns the requested venue as @venue" do @@ -151,7 +150,7 @@ def valid_session venue = FactoryGirl.create(:venue, :user => @user) # Trigger the behavior that occurs when invalid params are submitted Venue.any_instance.stub(:save).and_return(false) - put :update, {:id => venue.to_param, :venue => { }}, valid_session + put :update, {:id => venue.to_param, venue: invalid_attributes}, valid_session assigns(:venue).should eq(venue) end @@ -159,7 +158,7 @@ def valid_session venue = FactoryGirl.create(:venue, :user => @user) # Trigger the behavior that occurs when invalid params are submitted Venue.any_instance.stub(:save).and_return(false) - put :update, {:id => venue.to_param, :venue => { }}, valid_session + put :update, {:id => venue.to_param, venue: invalid_attributes}, valid_session response.should render_template("edit") end end @@ -168,7 +167,7 @@ def valid_session it "raises permission denied" do venue = FactoryGirl.create(:venue) expect { - put :update, {:id => venue.to_param, :venue => { }}, valid_session + put :update, {:id => venue.to_param, venue: invalid_attributes}, valid_session }.to raise_error # CanCan::AccessDenied end end diff --git a/spec/factories.rb b/spec/factories.rb index 606e1f068..a2e80a1e7 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -65,4 +65,8 @@ value "MyString" end + factory :reminder do + user + rememberable nil + end end diff --git a/spec/models/reminder_spec.rb b/spec/models/reminder_spec.rb new file mode 100644 index 000000000..9ed91dec2 --- /dev/null +++ b/spec/models/reminder_spec.rb @@ -0,0 +1,9 @@ +require 'spec_helper' + +describe Reminder do + + it 'is invalid without a user' do + expect(FactoryGirl.build(:reminder, user: nil)).to_not be_valid + end + +end