Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Merge branch 'master' into question-creation-time

Conflicts:
	app/models/question.rb
  • Loading branch information...
commit 759418f2f06f77b5db3207f4d7bc6f1a8ffd5480 2 parents 6a3b679 + 39228f2
Murilo Pereira mpereira authored

Showing 33 changed files with 233 additions and 115 deletions. Show diff stats Hide diff stats

  1. +37 0 app/controllers/application_controller.rb
  2. +11 1 app/controllers/auth_callback_controller.rb
  3. +6 5 app/controllers/comments_controller.rb
  4. +2 0  app/controllers/questions_controller.rb
  5. +0 1  app/controllers/search_results_controller.rb
  6. +2 0  app/controllers/topics_controller.rb
  7. +3 1 app/controllers/url_invitations_controller.rb
  8. +15 83 app/controllers/users_controller.rb
  9. +11 0 app/mailers/notifier.rb
  10. +2 0  app/models/news_item.rb
  11. +10 9 app/models/question.rb
  12. +2 0  app/models/search_result.rb
  13. +3 0  app/models/url_invitation.rb
  14. +30 3 app/models/user.rb
  15. +11 0 app/models/user_question_info.rb
  16. +4 0 app/stylesheets/themes/plain/_base.sass
  17. +1 0  app/stylesheets/themes/plain/_questions.sass
  18. +9 0 app/views/notifier/converted_invitation.html.haml
  19. +8 0 app/views/notifier/converted_invitation.text.erb
  20. +2 1  app/views/search_results/_search_result.html.haml
  21. +1 1  app/views/shared/_comments.html.haml
  22. +2 1  app/views/welcome/_news_item.html.haml
  23. +8 7 app/views/welcome/_sidebar.html.haml
  24. +2 0  config/locales/mailers/en.yml
  25. +2 0  config/locales/mailers/pt-BR.yml
  26. +7 0 config/locales/notifier/en.yml
  27. +6 0 config/locales/notifier/pt-BR.yml
  28. +4 0 experiments/email_converted_invitation.rb
  29. +3 0  experiments/metrics/converted_invitation.rb
  30. +1 0  lib/support/response_fetcher.rb
  31. +16 0 lib/tasks/news_items.rake
  32. +10 0 lib/tasks/questions.rake
  33. +2 2 public/javascripts/modules/show_question.js
37 app/controllers/application_controller.rb
@@ -43,8 +43,13 @@ def _vanity_identity
43 43 #
44 44 # Options:
45 45 # exclude_guests: don't track non-logged visitors _at all_.
  46 + # inviter: track the user's inviter.
46 47 def identifier(options = {})
47 48 if (identity = _vanity_identity) && !options[:preserve_identity]
  49 + if options[:inviter] && (url = UrlInvitation.first(:invitee_ids => identity.id))
  50 + return url.inviter.id
  51 + end
  52 +
48 53 identity.id
49 54 elsif options[:exclude_guests]
50 55 Umamao::UntrackedUser.instance.id
@@ -53,6 +58,38 @@ def identifier(options = {})
53 58 end
54 59 end
55 60
  61 + # This method tracks the information for signup. It is used in users#create
  62 + # and auth_callback#signup_with_provider
  63 + def handle_signup_track(user, url_invitation, group_invitation)
  64 + tracking_properties = {}
  65 +
  66 + if invitation = Invitation.find_by_invitation_token(user.invitation_token)
  67 + tracking_properties[:invited_by] = invitation.sender.email
  68 + end
  69 +
  70 + if group_invitation
  71 + tracking_properties[:invited_by] = group_invitation
  72 + end
  73 +
  74 + if url_invitation = UrlInvitation.find_by_ref(url_invitation)
  75 + tracking_properties[:invited_by] = url_invitation.inviter.id
  76 +
  77 + # Do not track for all UrlInvitations because we are tracking
  78 + # if the user has invited more people
  79 + if url_invitation.active?
  80 + track_bingo(:converted_invitation)
  81 + UrlInvitation.generate(url_invitation.inviter)
  82 + end
  83 + if ab_test(:email_converted_invitation)
  84 + Notifier.delay.converted_invitation(url_invitation.inviter, user)
  85 + end
  86 + end
  87 + track_bingo(:signed_up_action)
  88 + track_event(:sign_up, {:user_id => user.id,
  89 + :confirmed => user.confirmed?}.merge(tracking_properties))
  90 +
  91 + end
  92 +
56 93 # If there's a logged user, should track according to User#tracked?,
57 94 # otherwise should track if the cookie is not that of an untracked user.
58 95 def should_track?
12 app/controllers/auth_callback_controller.rb
@@ -74,10 +74,20 @@ def signup_with_provider
74 74 end
75 75 else
76 76 if session['sign_up_allowed']
  77 + if session['invitation_type'] == 'Invitation'
  78 + invitation_token = session['invitation_id']
  79 + auth_hash['invitation_token'] = invitation_token
  80 + end
  81 +
77 82 if user = User.create_with_provider(auth_hash)
78   - track_bingo(:signed_up_action)
  83 + invitation_type = session['invitation_type']
  84 + ref = session['invitation_id'] if invitation_type == "UrlInvitation"
  85 + slug = session["invitation_id"] if invitation_type == "GroupInvitation"
  86 + user.save_user_invitation(:url_invitation => ref,
  87 + :group_invitation => slug)
79 88
80 89 sign_in user
  90 + handle_signup_track(user, ref, slug)
81 91 redirect_to wizard_path("follow")
82 92 else
83 93 head(:unprocessable_entity)
11 app/controllers/comments_controller.rb
@@ -3,6 +3,9 @@ class CommentsController < ApplicationController
3 3 before_filter :find_scope
4 4 before_filter :check_permissions, :except => [:create]
5 5
  6 + # We need ApplicationHelper#markdown.
  7 + include ApplicationHelper
  8 +
6 9 def create
7 10 track_bingo(:commented)
8 11
@@ -114,11 +117,9 @@ def update
114 117 render :json => @comment.to_json, :status => :ok
115 118 end
116 119 format.js do
117   - render :json => {
118   - :message => notice,
119   - :success => true,
120   - :body => @comment.body
121   - }
  120 + render(:json => { :success => true,
  121 + :message => notice,
  122 + :body => markdown(@comment.body) } )
122 123 end
123 124 else
124 125 error = @comment.errors.full_messages.join(", ")
2  app/controllers/questions_controller.rb
@@ -176,6 +176,8 @@ def show
176 176
177 177 @bing_response = Support::Bing.search(@question.title)
178 178
  179 + @question.update_user_visit_info(current_user.id)
  180 +
179 181 respond_to do |format|
180 182 format.html
181 183 format.json { render :json => @question.to_json(:except => %w[_keywords slug watchers]) }
1  app/controllers/search_results_controller.rb
@@ -69,7 +69,6 @@ def destroy
69 69 @search_result.user.update_reputation(:delete_search_result, current_group)
70 70 end
71 71 @search_result.destroy
72   - @question.search_result_removed!
73 72
74 73 respond_to do |format|
75 74 format.html do
2  app/controllers/topics_controller.rb
@@ -129,6 +129,8 @@ def follow
129 129 followers_count = @topic.followers_count + 1
130 130 @topic.add_follower!(user)
131 131 user.remove_suggestion(@topic)
  132 + # After removing the suggestion the current user object is not correct
  133 + user.reload
132 134 user.populate_news_feed!(@topic)
133 135 user.save!
134 136
4 app/controllers/url_invitations_controller.rb
@@ -8,11 +8,13 @@ def show
8 8 :inviter_id => @url_invitation.inviter.id)
9 9 @url_invitation.increment_clicks
10 10 if @url_invitation.invites_left > 0
11   - @ref = params[:url_invitation]
  11 + @ref = params[:ref]
12 12 @user = User.new
13 13 @user.timezone = AppConfig.default_timezone
14 14 @signin_index, @signup_index = [6, 1]
15 15 session['sign_up_allowed'] = true
  16 + session['invitation_id'] = @ref
  17 + session['invitation_type'] = "UrlInvitation"
16 18 render 'welcome/landing', :layout => 'welcome'
17 19 else
18 20 flash[:notice] =
98 app/controllers/users_controller.rb
@@ -79,6 +79,14 @@ def new
79 79 @user.timezone = AppConfig.default_timezone
80 80 @signin_index, @signup_index = [6, 1]
81 81 session['sign_up_allowed'] = true
  82 + session['invitation_id'] = @invitation.try(:id) || @group_invitation.try(:slug)
  83 + session['invitation_type'] = if @invitation
  84 + "Invitation"
  85 + elsif @group_invitation
  86 + "GroupInvitation"
  87 + else
  88 + nil
  89 + end
82 90 render 'welcome/landing', :layout => 'welcome'
83 91 else
84 92 return redirect_to(root_path(:focus => "signup"))
@@ -86,7 +94,6 @@ def new
86 94 end
87 95
88 96 def create
89   - tracking_properties = {}
90 97 @user = User.new
91 98 @user.safe_update(%w[login email name password_confirmation password
92 99 preferred_languages website language timezone
@@ -100,87 +107,18 @@ def create
100 107 @user.birthday = build_date(params[:user], "birthday")
101 108 end
102 109
103   - @group_invitation = GroupInvitation.
104   - first(:slug => params[:group_invitation])
105 110 @user.confirmed_at = Time.now if @group_invitation
106 111
107   - if invitation = Invitation.find_by_invitation_token(@user.invitation_token)
108   - tracking_properties[:invited_by] = invitation.sender.email
109   -
110   - if m = invitation.recipient_email.match("^[a-z](\d{6})@dac.unicamp.br$")
111   - unicamp = University.find_by_short_name('Unicamp')
112   - affiliation = Affiliation.new(:user => @user,
113   - :university => unicamp,
114   - :email => invitation.recipient_email,
115   - :confirmed_at => Time.now)
116   - affiliation.save
117   - @user.affiliation_token = affiliation.affiliation_token
118   - end
119   - end
120   -
121 112 if @user.save
122   - if @url_invitation = UrlInvitation.find_by_ref(params[:ref])
123   - tracking_properties[:invited_by] = @url_invitation.inviter.id
124   - @url_invitation.add_invitee(@user)
125   - track_bingo(:signed_up_action)
126   - end
127   -
128   - if invitation && invitation.topics
129   - invitation.topics.each do |topic|
130   - topic.add_follower!(@user)
131   - end
132   - end
  113 + sign_in @user
133 114
134   - if @group_invitation
135   - @group_invitation.push(:user_ids => @user.id)
136   - tracking_properties[:invited_by] = @group_invitation.slug
137   - end
138   -
139   - if @user.affiliation_token.present?
140   - @affiliation = Affiliation.
141   - find_by_affiliation_token(@user.affiliation_token)
142   - @affiliation.confirmed_at ||= Time.now
143   - @user.affiliations << @affiliation
144   -
145   - # If student's code is known, link the affiliation to student model
146   - code = @affiliation.email.match(/^[a-z](\d{6})@dac.unicamp.br$/)
147   - if code
148   - unicamp = University.find_by_short_name("Unicamp")
149   - unless (student = Student.first(:code => code[1], :university_id => unicamp.id))
150   - student = Student.new
151   - student.code = code[1]
152   - student.university = unicamp
153   - student.name = @user.name
154   - student.save
155   - end
156   - @affiliation.student = student
157   - @affiliation.save
158   - end
159   -
160   - if (student = @affiliation.student) && student.academic_program_class
161   - @user.bio = "#{student.academic_program_class.academic_program.name} #{student.academic_program_class.year} #{student.university.short_name}"
162   - else
163   - @user.bio = @affiliation.university.short_name
164   - end
165   -
166   - @user.save
167   - end
168   -
169   - # FIXME: this is temporary code only for the incoming Unicamp students.
170   - # It should be removed after the occasion has passed.
171   - if @group_invitation && (@group_invitation.slug == 'bixounicamp' || @group_invitation.slug == 'tci')
172   - unicamp = University.find_by_short_name('Unicamp')
173   - affiliation = Affiliation.new(:user => @user, :university => unicamp,
174   - :confirmed_at => Time.now)
175   - affiliation.save(:validate => false)
176   - @user.affiliations << affiliation
177   - @user.bio = affiliation.university.short_name + ' 2011'
178   - @user.save
179   - end
  115 + group_invitation = params[:group_invitation]
  116 + url_invitation = params[:ref]
  117 + @user.save_user_invitation(:url_invitation => url_invitation,
  118 + :group_invitation => group_invitation)
  119 + handle_signup_track(@user, url_invitation, group_invitation)
180 120
181 121 current_group.add_member(@user)
182   - track_event(:sign_up, {:user_id => @user.id,
183   - :confirmed => @user.confirmed?}.merge(tracking_properties))
184 122 flash[:conversion] = true
185 123
186 124 # Protects against session fixation attacks, causes request forgery
@@ -193,13 +131,7 @@ def create
193 131 flash[:notice] = t("confirm", :scope => "users.create")
194 132 end
195 133
196   - # FIXME: temporary hack to allow as many users to signup as
197   - # quick as possible on the same machine during an event
198   - if @group_invitation && @group_invitation.slug == 'tci'
199   - redirect_to '/tci'
200   - else
201   - sign_in_and_redirect(:user, @user) # !! now logged in
202   - end
  134 + redirect_to wizard_path("connect")
203 135 else
204 136 flash[:error] = t("users.create.flash_error")
205 137 render :action => 'new', :layout => 'welcome'
11 app/mailers/notifier.rb
@@ -170,4 +170,15 @@ def survey(user)
170 170 mail(:to => user.email,
171 171 :subject => t(:subject, :scope => [:notifier, :survey]))
172 172 end
  173 +
  174 + def converted_invitation(user, invited_user)
  175 + @user = user
  176 + @invited_user = invited_user
  177 + @invited_user_link = user_url(@invited_user)
  178 +
  179 + subject = I18n.t("mailers.notifications.invitation_converted.subject")
  180 +
  181 + mail(:to => @user.email, :subject => subject)
  182 + end
  183 +
173 184 end
2  app/models/news_item.rb
@@ -18,6 +18,8 @@ class NewsItem
18 18
19 19 key :visible, Boolean, :default => true
20 20
  21 + key :entry_activity_at, Time
  22 +
21 23 validates_uniqueness_of :recipient_id, :scope => :news_update_id
22 24
23 25 ensure_index([[:recipient_id, 1], [:created_at, -1]])
19 app/models/question.rb
@@ -175,15 +175,10 @@ def answer_added!
175 175 end
176 176
177 177 def search_result_added!
178   - collection.update({ :_id => _id },
179   - { :$inc => { :search_results_count => 1 } },
180   - :upsert => true)
181   - end
182   -
183   - def search_result_removed!
184   - collection.update({ :_id => _id },
185   - { :$inc => { :search_results_count => -1 } },
186   - :upsert => true)
  178 + on_activity
  179 + self.reload
  180 + NewsItem.delay.set({:news_update_id => self.news_update.id},
  181 + :entry_activity_at => self.activity_at)
187 182 end
188 183
189 184 def answer_removed!
@@ -586,6 +581,12 @@ def decrement_user_topic_questions_count
586 581 UserTopicInfo.question_removed!(self)
587 582 end
588 583
  584 + def update_user_visit_info(user_id)
  585 + uqi = UserQuestionInfo.
  586 + find_or_create_by_user_id_and_question_id(user_id, self.id)
  587 + uqi.set(:last_visited_at => Time.zone.now)
  588 + end
  589 +
589 590 def topic_followers_count
590 591 @topic_followers_count ||= topics.map(&:followers_count).inject(0, :+)
591 592 end
2  app/models/search_result.rb
... ... @@ -1,3 +1,4 @@
  1 +# -*- coding: utf-8 -*-
1 2 class SearchResult
2 3 TITLE_SIZE = 100
3 4 SUMMARY_SIZE = 250
@@ -52,6 +53,7 @@ class SearchResult
52 53
53 54 after_create :create_news_update
54 55 after_create :notify_watchers, :unless => :has_answer?
  56 + after_create Proc.new { |sr| sr.question.search_result_added! }
55 57
56 58 before_destroy :unhide_news_update
57 59
3  app/models/url_invitation.rb
@@ -7,6 +7,8 @@ class UrlInvitation
7 7 key :sign_ups_count, Integer, :default => 0
8 8 key :ref, String, :unique => true, :required => true, :length => 4
9 9
  10 + key :active, Boolean, :default => true
  11 +
10 12 key :invitee_ids, Array
11 13 many :invitees, :in => :invitee_ids, :class_name => 'User'
12 14
@@ -19,6 +21,7 @@ class UrlInvitation
19 21
20 22 def self.generate(user)
21 23 begin ref = SecureRandom.hex(2); end while find_by_ref(ref)
  24 + UrlInvitation.set({:inviter_id => user.id}, {:active => false})
22 25 UrlInvitation.create(:inviter => user, :ref => ref)
23 26 end
24 27
33 app/models/user.rb
@@ -104,8 +104,6 @@ class Helper
104 104
105 105 key :last_read_notifications_at, Time
106 106
107   - has_one :url_invitation, :foreign_key => :inviter_id, :dependent => :destroy
108   -
109 107 before_create :create_friend_list, :create_notification_opts
110 108 before_create :generate_uuid
111 109 after_create Proc.new { |user| UrlInvitation.generate(user) }
@@ -147,6 +145,7 @@ class Helper
147 145 after_create :create_suggestion_list
148 146 after_create :create_contact_references
149 147 after_create :associate_with_waiting_users
  148 + after_create :add_invitation_topics, :if => :invitation_token?
150 149
151 150 scope :confirmed, where(:confirmed_at.ne => nil)
152 151 scope :unconfirmed, where(:confirmed_at => nil)
@@ -1029,7 +1028,8 @@ def self.create_with_provider(auth_hash)
1029 1028 :agrees_with_terms_of_service => true,
1030 1029 :name => user_info["name"],
1031 1030 :password => password,
1032   - :password_confirmation => password)
  1031 + :password_confirmation => password,
  1032 + :invitation_token => auth_hash["invitation_token"])
1033 1033
1034 1034 if user
1035 1035 UserExternalAccount.create(auth_hash.merge(:user => user))
@@ -1039,7 +1039,34 @@ def self.create_with_provider(auth_hash)
1039 1039 user
1040 1040 end
1041 1041
  1042 + def url_invitation
  1043 + UrlInvitation.first(:inviter_id => self.id, :active => true)
  1044 + end
  1045 +
  1046 + def save_user_invitation(options={})
  1047 + if ref = options[:url_invitation]
  1048 + UrlInvitation.find_by_ref(ref).try(:add_invitee, self)
  1049 + end
  1050 +
  1051 + if (slug = options[:group_invitation]).present?
  1052 + group_invitation = GroupInvitation.first(:slug => slug)
  1053 + self.set(:confirmed_at => Time.now)
  1054 + group_invitation.push(:user_ids => id)
  1055 + end
  1056 + end
  1057 +
  1058 + def add_invitation_topics
  1059 + invitation = Invitation.find_by_invitation_token(invitation_token)
  1060 + if invitation && invitation.topics
  1061 + invitation.topics.each do |topic|
  1062 + topic.add_follower!(self)
  1063 + end
  1064 + end
  1065 + end
  1066 + handle_asynchronously :add_invitation_topics
  1067 +
1042 1068 protected
  1069 +
1043 1070 def password_required?
1044 1071 (encrypted_password.blank? || !password.blank?)
1045 1072 end
11 app/models/user_question_info.rb
... ... @@ -0,0 +1,11 @@
  1 +class UserQuestionInfo
  2 + include MongoMapper::Document
  3 +
  4 + key :user_id, String, :required => true, :index => true
  5 + belongs_to :user
  6 +
  7 + key :question_id, String, :required => true, :index => true
  8 + belongs_to :question
  9 +
  10 + key :last_visited_at, Time
  11 +end
4 app/stylesheets/themes/plain/_base.sass
@@ -256,6 +256,10 @@ table
256 256 .navform.hidden
257 257 display: none
258 258
  259 +#new_search_result
  260 + .group.navform
  261 + text-align: right
  262 +
259 263 .invitation_form
260 264 .tip
261 265 color: #666666
1  app/stylesheets/themes/plain/_questions.sass
@@ -314,6 +314,7 @@ form
314 314 #bing_results_wrapper #provided_by
315 315 color: #666
316 316 margin-top: 2em
  317 + font-size: 1.2em
317 318
318 319 #bing_results_wrapper #no_results
319 320 color: #666
9 app/views/notifier/converted_invitation.html.haml
... ... @@ -0,0 +1,9 @@
  1 +%p
  2 + = t('notifier.global.hi', :user => h(@user.first_name))
  3 +%p
  4 + = t('notifier.invitation_converted.message_html',
  5 + :user => h(@invited_user.name),
  6 + :profile_link => @invited_user_link,
  7 + :first_name => h(@invited_user.first_name))
  8 +
  9 += simple_format(t('notifier.global.signature'))
8 app/views/notifier/converted_invitation.text.erb
... ... @@ -0,0 +1,8 @@
  1 +<%= t('notifier.global.hi', :user => h(@user.first_name)) %>
  2 +
  3 +<%= t('notifier.invitation_converted.message_text',
  4 + :user => h(@invited_user.name),
  5 + :profile_link => @invited_user_link,
  6 + :first_name => h(@invited_user.first_name)) %>
  7 +
  8 +<%= t('notifier.global.signature') %>
3  app/views/search_results/_search_result.html.haml
@@ -7,7 +7,8 @@
7 7 - title = search_result.title
8 8 - if title.blank? || title == search_result.url
9 9 - title = shorten_url(search_result.url, :length => 45)
10   - = link_to(title, trackable_search_result_url(search_result, Umamao::PAGE_MAPPING['questions#show']))
  10 + = link_to(title, trackable_search_result_url(search_result, Umamao::PAGE_MAPPING['questions#show']),
  11 + :target => "_blank")
11 12 .url
12 13 %p
13 14 = shorten_url(search_result.url)
2  app/views/shared/_comments.html.haml
@@ -16,7 +16,7 @@
16 16 :commentable => commentable,
17 17 :path_scope => path_scope, :inline => true}
18 18 - else
19   - %p= t("comments.shared.not_logged", :link => new_user_session_path)
  19 + %p= t("comments.shared.not_logged", :link => root_path)
20 20 - else
21 21 = render :partial => "comments/comment", :object => comments[0], :locals => {:source => source, :mini => true}
22 22 - comments = comments[1 .. comments.count]
3  app/views/welcome/_news_item.html.haml
@@ -15,7 +15,8 @@
15 15 %li.entry.item
16 16 -# News_item Summary
17 17 .summary
18   - %h3= link_to title, link, :title => truncate(entry.body, :length => 200)
  18 + %h3= link_to title, link, :title => truncate(entry.body, :length => 200),
  19 + :target => entry_type == "searchresult" ? "_blank" : ""
19 20 - if entry_type == "searchresult"
20 21 .search_result_url
21 22 %p
15 app/views/welcome/_sidebar.html.haml
@@ -4,7 +4,8 @@
4 4 .navigation= link_to t(".admin"), manage_properties_path
5 5 - if current_user.mod_of?(current_group)
6 6 .navigation= link_to t(".moderate"), moderate_path
7   - - if current_user.url_invitation
  7 + - url_invitation = current_user.url_invitation
  8 + - if url_invitation
8 9 .navigation
9 10 = t(:title, :scope => [:url_invitations, :sidebar])
10 11 #url_share
@@ -13,7 +14,7 @@
13 14 :scope => [:url_invitations,
14 15 :share,
15 16 :twitter]),
16   - :url => root_url(:ref => current_user.url_invitation.ref),
  17 + :url => root_url(:ref => url_invitation.ref),
17 18 :original_referer => root_url(:shared_url_invitation => :twitter))
18 19 #facebook
19 20 = facebook_button(:action => :feed,
@@ -26,21 +27,21 @@
26 27 :share,
27 28 :facebook]),
28 29 :redirect_uri => root_url(:shared_url_invitation => :facebook),
29   - :url => root_url(:ref => current_user.url_invitation.ref))
  30 + :url => root_url(:ref => url_invitation.ref))
30 31 #url_field_wrapper
31 32 = url_field_tag(:ref,
32   - root_url(:ref => current_user.url_invitation.ref),
  33 + root_url(:ref => url_invitation.ref),
33 34 :readonly => true,
34 35 :class => :url_field)
35 36 #url_invitation_information
36 37 %span.number
37   - = current_user.url_invitation.clicks_count
  38 + = url_invitation.clicks_count
38 39 = "#{t(:clicks, :scope => [:url_invitations, :sidebar])},"
39 40 %span.number
40   - = current_user.url_invitation.sign_ups_count
  41 + = url_invitation.sign_ups_count
41 42 = "#{t(:sign_ups, :scope => [:url_invitations, :sidebar])},"
42 43 %span.number
43   - = current_user.url_invitation.invites_left
  44 + = url_invitation.invites_left
44 45 = t(:left, :scope => [:url_invitations, :sidebar])
45 46
46 47 -# Topic suggestions
2  config/locales/mailers/en.yml
@@ -10,6 +10,8 @@ en:
10 10 give_advice:
11 11 friend_subject: "Your friend needs help: %{question_title}"
12 12 subject: "We need your advice: %{question_title}"
  13 + invitation_converted:
  14 + subject: Someone you invited just signed up!
13 15 new_answer:
14 16 subject_friend: Your friend %{name} answered the question %{title}
15 17 subject_other: "%{name} answered the question %{title}"
2  config/locales/mailers/pt-BR.yml
@@ -10,6 +10,8 @@ pt-BR:
10 10 give_advice:
11 11 friend_subject: "Precisamos da sua ajuda em: %{question_title}"
12 12 subject: "Precisamos da sua ajuda em: %{question_title}"
  13 + invitation_converted:
  14 + subject: "Alguém que você convidou acabou de se cadastrar!"
13 15 new_answer:
14 16 subject_friend: "%{name} respondeu à pergunta \"%{title}\""
15 17 subject_other: "%{name} respondeu à pergunta \"%{title}\""
7 config/locales/notifier/en.yml
@@ -31,6 +31,13 @@ en:
31 31 Umamao Team
32 32
33 33 user_page: profile page
  34 + invitation_converted:
  35 + message_html: |
  36 + %{user} just accepted your invitation and signed up to Umamao. You can go to <a href='%{profile_link}'>%{first_name}'s profile</a> to follow them and suggest topics to help them get started. Thanks a lot for helping us spread the word, keep 'em coming! :)
  37 + message_text: |
  38 + %{user} just accepted your invitation and signed up to Umamao. You can go to %{first_name}'s profile to follow them and suggest topics to help them get started. Thanks a lot for helping us spread the word, keep 'em coming! :)
  39 + You can find %{first_name}'s profile at %{profile_link}
  40 + subject: %{name} accepted your invitation to get in touch with Umamao
34 41 new_answer:
35 42 link_label: "To see the answer, see this link:"
36 43 message: "%{user} answered:"
6 config/locales/notifier/pt-BR.yml
@@ -28,6 +28,12 @@ pt-BR:
28 28 signature: "Abraço,\n\
29 29 Equipe Umamão\n"
30 30 user_page: perfil
  31 + invitation_converted:
  32 + message_html: |
  33 + "%{user} acabou de aceitar seu convite para se cadastrar no Umamao. Visite o <a href='%{profile_link}'>perfil de %{first_name}</a> no Umamao para segui-lo(a) e sugerir tópicos. Muitíssimo obrigado por ajudar no boca-a-boca, :)"
  34 + Para ir até o perfil de %{first_name} acesse %{profile_link}
  35 + message: |
  36 + subject: %{name} aceitou seu convite para conhecer o Umamao
31 37 new_answer:
32 38 author: "%{user} respondeu:"
33 39 link_label: "Para ver a resposta, visite o link:"
4 experiments/email_converted_invitation.rb
... ... @@ -0,0 +1,4 @@
  1 +ab_test 'Email converted invitation' do
  2 + metrics :converted_invitation
  3 + identify { |c| c.identifier(:inviter => true) }
  4 +end
3  experiments/metrics/converted_invitation.rb
... ... @@ -0,0 +1,3 @@
  1 +metric 'Converted invitation' do
  2 + description 'Tracks the influence of experiments in invitation conversion'
  3 +end
1  lib/support/response_fetcher.rb
@@ -8,6 +8,7 @@ class EmptyResponse; def body; nil; end; end
8 8 POSSIBLE_FETCH_ERRORS = [Errno::ECONNREFUSED,
9 9 Errno::ECONNRESET,
10 10 Errno::ETIMEDOUT,
  11 + Errno::EHOSTUNREACH,
11 12 Net::HTTPServerException,
12 13 Net::HTTPBadResponse,
13 14 Net::HTTPFatalError,
16 lib/tasks/news_items.rake
@@ -40,4 +40,20 @@ namespace :news_items do
40 40 end
41 41 puts i
42 42 end
  43 +
  44 + desc 'Update entry_activity_at field on news_items of question'
  45 + task :update_entry_activity_at => :environment do
  46 + activity_at_hash = {}
  47 + NewsUpdate.find_each(:entry_type => "Question") do |nu|
  48 + activity_at_hash[nu.id] = nu.entry.activity_at
  49 + end
  50 + NewsItem.find_each(:batch_size => 1_000,
  51 + :news_update_entry_type => "Question") do |ni|
  52 + print '.'
  53 + ni.set(:entry_activity_at => activity_at_hash[ni.news_update_id])
  54 + end
  55 + print "\nTotal news items of questions without entry_activity_at: "
  56 + puts NewsItem.count(:news_update_entry_type => 'Question',
  57 + :entry_activity_at => nil)
  58 + end
43 59 end
10 lib/tasks/questions.rake
... ... @@ -0,0 +1,10 @@
  1 +namespace :questions do
  2 + desc 'Update activity_at field'
  3 + task :update_activity_at => :environment do
  4 + Question.find_each do |q|
  5 + last_updated_at = q.search_results.map(&:created_at).max || q.created_at
  6 + q.set(:activity_at => last_updated_at)
  7 + print '.'
  8 + end
  9 + end
  10 +end
4 public/javascripts/modules/show_question.js
@@ -310,12 +310,12 @@ $(document).ready(function() {
310 310 $('.loader').show();
311 311 });
312 312
313   - $('.inline_comment .comment_text_area').focus(function() {
  313 + $('.inline_comment .comment_text_area').live('focus', function() {
314 314 $(this).addClass('focussed');
315 315 $(this).closest(".group").find(".navform.hidden").show();
316 316 });
317 317
318   - $('.inline_comment .comment_text_area').blur(function() {
  318 + $('.inline_comment .comment_text_area').live('blur', function() {
319 319 if(this.value == ""){
320 320 $(this).removeClass('focussed');
321 321 $(this).closest(".group").find(".navform.hidden").hide();

0 comments on commit 759418f

Please sign in to comment.
Something went wrong with that request. Please try again.