diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md
index e590127d13d..5f71482907f 100644
--- a/DEVELOPMENT.md
+++ b/DEVELOPMENT.md
@@ -10,7 +10,7 @@ on Discourse with:
1. Install VirtualBox: https://www.virtualbox.org/wiki/Downloads
2. Install Vagrant: https://www.vagrantup.com/
3. Open a terminal
-4. Clone the project: `git@github.com:discourse/discourse.git`
+4. Clone the project: `git clone git@github.com:discourse/discourse.git`
5. Enter the project directory: `cd discourse`
### Using Vagrant
@@ -51,7 +51,7 @@ bundle exec rails server
In a few seconds, rails will start server pages. To access them, open a web browser to http://localhost:4000 - if it all worked you should see discourse! Congratulations, you are ready to start working!
-You can now edit files on your local file system, using your favorite text editor or IDE. When you reload your web browser, it should have the latest changed.
+You can now edit files on your local file system, using your favorite text editor or IDE. When you reload your web browser, it should have the latest changes.
### Guard + Rspec
@@ -71,6 +71,12 @@ Wait a minute while it runs all our unit tests. Once it has completed, live relo
### Sending Email
+Mail is sent asynchronously by Sidekiq, so you'll need to have sidekiq running to process jobs. Run it with this command:
+
+```
+bundle exec sidekiq
+```
+
Mailcatcher is used to avoid the whole issue of actually sending emails: https://github.com/sj26/mailcatcher
To start mailcatcher, run the following command in the vagrant image:
diff --git a/Gemfile.lock b/Gemfile.lock
index 9e24d9e1bbd..5b15df96bb6 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -63,34 +63,34 @@ PATH
GEM
remote: https://rubygems.org/
specs:
- actionmailer (3.2.11)
- actionpack (= 3.2.11)
+ actionmailer (3.2.12)
+ actionpack (= 3.2.12)
mail (~> 2.4.4)
- actionpack (3.2.11)
- activemodel (= 3.2.11)
- activesupport (= 3.2.11)
+ actionpack (3.2.12)
+ activemodel (= 3.2.12)
+ activesupport (= 3.2.12)
builder (~> 3.0.0)
erubis (~> 2.7.0)
journey (~> 1.0.4)
- rack (~> 1.4.0)
+ rack (~> 1.4.5)
rack-cache (~> 1.2)
rack-test (~> 0.6.1)
sprockets (~> 2.2.1)
- activemodel (3.2.11)
- activesupport (= 3.2.11)
+ activemodel (3.2.12)
+ activesupport (= 3.2.12)
builder (~> 3.0.0)
- activerecord (3.2.11)
- activemodel (= 3.2.11)
- activesupport (= 3.2.11)
+ activerecord (3.2.12)
+ activemodel (= 3.2.12)
+ activesupport (= 3.2.12)
arel (~> 3.0.2)
tzinfo (~> 0.3.29)
activerecord-postgres-hstore (0.7.1)
rails
rake
- activeresource (3.2.11)
- activemodel (= 3.2.11)
- activesupport (= 3.2.11)
- activesupport (3.2.11)
+ activeresource (3.2.12)
+ activemodel (= 3.2.12)
+ activesupport (= 3.2.12)
+ activesupport (3.2.12)
i18n (~> 0.6)
multi_json (~> 1.0)
acts_as_paranoid (0.4.1)
@@ -194,7 +194,7 @@ GEM
jquery-rails (2.2.0)
railties (>= 3.0, < 5.0)
thor (>= 0.14, < 2.0)
- json (1.7.6)
+ json (1.7.7)
koala (1.6.0)
addressable (~> 2.2)
faraday (~> 0.8)
@@ -208,10 +208,10 @@ GEM
treetop (~> 1.4.8)
metaclass (0.0.1)
method_source (0.8.1)
- mime-types (1.20.1)
+ mime-types (1.21)
mocha (0.10.5)
metaclass (~> 0.0.1)
- multi_json (1.5.0)
+ multi_json (1.5.1)
multipart-post (1.1.5)
mustache (0.99.4)
net-scp (1.0.4)
@@ -239,17 +239,17 @@ GEM
rack
rack-test (0.6.2)
rack (>= 1.0)
- rails (3.2.11)
- actionmailer (= 3.2.11)
- actionpack (= 3.2.11)
- activerecord (= 3.2.11)
- activeresource (= 3.2.11)
- activesupport (= 3.2.11)
+ rails (3.2.12)
+ actionmailer (= 3.2.12)
+ actionpack (= 3.2.12)
+ activerecord (= 3.2.12)
+ activeresource (= 3.2.12)
+ activesupport (= 3.2.12)
bundler (~> 1.0)
- railties (= 3.2.11)
- railties (3.2.11)
- actionpack (= 3.2.11)
- activesupport (= 3.2.11)
+ railties (= 3.2.12)
+ railties (3.2.12)
+ actionpack (= 3.2.12)
+ activesupport (= 3.2.12)
rack-ssl (~> 1.3.2)
rake (>= 0.8.7)
rdoc (~> 3.4)
@@ -258,7 +258,7 @@ GEM
rb-fsevent (0.9.3)
rb-inotify (0.8.8)
ffi (>= 0.5.0)
- rdoc (3.12)
+ rdoc (3.12.1)
json (~> 1.4)
redis (3.0.2)
redis-actionpack (3.2.3)
diff --git a/app/assets/javascripts/discourse/components/autocomplete.js.coffee b/app/assets/javascripts/discourse/components/autocomplete.js.coffee
index bc6e3dabd96..dc13f2bc187 100644
--- a/app/assets/javascripts/discourse/components/autocomplete.js.coffee
+++ b/app/assets/javascripts/discourse/components/autocomplete.js.coffee
@@ -84,9 +84,11 @@
ul = div.find('ul')
selectedOption = 0
markSelected()
+
ul.find('li').click ->
selectedOption = ul.find('li').index(this)
completeTerm(autocompleteOptions[selectedOption])
+ false
pos = null
if isInput
diff --git a/app/assets/javascripts/discourse/templates/topic_summary/private_message.js.handlebars b/app/assets/javascripts/discourse/templates/topic_summary/private_message.js.handlebars
index b61798b62ad..550f7118f91 100644
--- a/app/assets/javascripts/discourse/templates/topic_summary/private_message.js.handlebars
+++ b/app/assets/javascripts/discourse/templates/topic_summary/private_message.js.handlebars
@@ -1,5 +1,4 @@
{{i18n private_message_info.title}}
-{{{i18n private_message_info.description}}}
{{#each content.allowed_users}}
diff --git a/app/assets/javascripts/discourse/views/topic_footer_buttons_view.js.coffee b/app/assets/javascripts/discourse/views/topic_footer_buttons_view.js.coffee
index 4c9d4c604ee..f2b41fc8bf9 100644
--- a/app/assets/javascripts/discourse/views/topic_footer_buttons_view.js.coffee
+++ b/app/assets/javascripts/discourse/views/topic_footer_buttons_view.js.coffee
@@ -38,6 +38,7 @@ window.Discourse.TopicFooterButtonsView = Ember.ContainerView.extend
@addObject Discourse.ButtonView.createWithMixins
classNames: ['btn', 'btn-primary', 'create']
+ attributeBindings: ['disabled']
text: (->
archetype = @get('controller.content.archetype')
return customTitle if customTitle = @get("parentView.replyButtonText#{archetype.capitalize()}")
@@ -46,6 +47,7 @@ window.Discourse.TopicFooterButtonsView = Ember.ContainerView.extend
renderIcon: (buffer) -> buffer.push("")
click: -> @get('controller').reply()
helpKey: 'topic.reply.help'
+ disabled: !@get('controller.content.can_create_post')
unless topic.get('isPrivateMessage')
@addObject Discourse.DropdownButtonView.createWithMixins
diff --git a/app/assets/stylesheets/application/topic.css.scss b/app/assets/stylesheets/application/topic.css.scss
index a63f9235393..6b5a30472e8 100644
--- a/app/assets/stylesheets/application/topic.css.scss
+++ b/app/assets/stylesheets/application/topic.css.scss
@@ -387,7 +387,7 @@ kbd {
z-index: 1;
}
button {
- padding: 0;
+ padding: 0 1px;
cursor: pointer;
z-index: 1000;
position: absolute;
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index d01c3ab3481..db118b323c0 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -66,12 +66,12 @@ class ApplicationController < ActionController::Base
# for now do a simple remap, we may look at cleaner ways of doing the render
raise ActiveRecord::RecordNotFound
else
- render file: 'public/404', layout: false, status: 404
+ render file: 'public/404', formats: [:html], layout: false, status: 404
end
end
rescue_from Discourse::InvalidAccess do
- render file: 'public/403', layout: false, status: 403
+ render file: 'public/403', formats: [:html], layout: false, status: 403
end
def store_preloaded(key, json)
diff --git a/app/controllers/session_controller.rb b/app/controllers/session_controller.rb
index 6aef4abba2a..b8f4c7ff972 100644
--- a/app/controllers/session_controller.rb
+++ b/app/controllers/session_controller.rb
@@ -22,9 +22,14 @@ class SessionController < ApplicationController
# If their password is correct
if @user.confirm_password?(params[:password])
- log_on_user(@user)
- render_serialized(@user, UserSerializer)
- return
+ if @user.email_confirmed?
+ log_on_user(@user)
+ render_serialized(@user, UserSerializer)
+ return
+ else
+ render :json => {error: I18n.t("login.not_activated")}
+ return
+ end
end
end
diff --git a/app/helpers/common_helper.rb b/app/helpers/common_helper.rb
new file mode 100644
index 00000000000..83ee98efa5b
--- /dev/null
+++ b/app/helpers/common_helper.rb
@@ -0,0 +1,7 @@
+module CommonHelper
+ def render_google_analytics_code
+ if Rails.env == "production" && SiteSetting.ga_tracking_code.present?
+ render :partial => "common/google_analytics"
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/helpers/topics_helper.rb b/app/helpers/topics_helper.rb
new file mode 100644
index 00000000000..978b118d5b1
--- /dev/null
+++ b/app/helpers/topics_helper.rb
@@ -0,0 +1,10 @@
+module TopicsHelper
+
+ def render_topic_title(topic)
+ link_to(topic.title,topic.relative_url)
+ end
+
+ def render_topic_next_page_link(topic, next_page)
+ link_to("next page", "#{topic.relative_url}?page=#{next_page}")
+ end
+end
diff --git a/app/models/post.rb b/app/models/post.rb
index 066cac96ba7..496224570c3 100644
--- a/app/models/post.rb
+++ b/app/models/post.rb
@@ -342,19 +342,21 @@ class Post < ActiveRecord::Base
# This calculates the geometric mean of the post timings and stores it along with
# each post.
def self.calculate_avg_time
- exec_sql("UPDATE posts
- SET avg_time = (x.gmean / 1000)
- FROM (SELECT post_timings.topic_id,
- post_timings.post_number,
- round(exp(avg(ln(msecs)))) AS gmean
- FROM post_timings
- INNER JOIN posts AS p2
- ON p2.post_number = post_timings.post_number
- AND p2.topic_id = post_timings.topic_id
- AND p2.user_id <> post_timings.user_id
- GROUP BY post_timings.topic_id, post_timings.post_number) AS x
- WHERE x.topic_id = posts.topic_id
- AND x.post_number = posts.post_number")
+ retry_lock_error do
+ exec_sql("UPDATE posts
+ SET avg_time = (x.gmean / 1000)
+ FROM (SELECT post_timings.topic_id,
+ post_timings.post_number,
+ round(exp(avg(ln(msecs)))) AS gmean
+ FROM post_timings
+ INNER JOIN posts AS p2
+ ON p2.post_number = post_timings.post_number
+ AND p2.topic_id = post_timings.topic_id
+ AND p2.user_id <> post_timings.user_id
+ GROUP BY post_timings.topic_id, post_timings.post_number) AS x
+ WHERE x.topic_id = posts.topic_id
+ AND x.post_number = posts.post_number")
+ end
end
before_save do
diff --git a/app/models/topic_link.rb b/app/models/topic_link.rb
index da3f5be4f27..d4215ae8128 100644
--- a/app/models/topic_link.rb
+++ b/app/models/topic_link.rb
@@ -47,6 +47,10 @@ class TopicLink < ActiveRecord::Base
internal = true
route = Rails.application.routes.recognize_path(parsed.path)
+
+ # We aren't interested in tracking internal links to users
+ next if route[:controller] == 'users'
+
topic_id = route[:topic_id]
post_number = route[:post_number] || 1
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 2f5593f1877..41dac47d71c 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -380,6 +380,10 @@ class User < ActiveRecord::Base
end
end
+ def email_confirmed?
+ email_tokens.where(email: self.email, confirmed: true).present?
+ end
+
protected
diff --git a/app/views/common/_discourse_javascript.html.erb b/app/views/common/_discourse_javascript.html.erb
new file mode 100644
index 00000000000..8c54a574241
--- /dev/null
+++ b/app/views/common/_discourse_javascript.html.erb
@@ -0,0 +1,25 @@
+ <%- if mini_profiler_enabled? %>
+ <%- Rack::MiniProfiler.step "application" do %>
+ <%= javascript_include_tag "application" %>
+ <%-end%>
+
+ <%- Rack::MiniProfiler.step "admin" do %>
+ <%= javascript_include_tag "admin"%>
+ <%-end%>
+ <%- else %>
+ <%= javascript_include_tag "application" %>
+ <%- if admin? %>
+ <%= javascript_include_tag "admin"%>
+ <%- end %>
+ <%- end%>
+
+
\ No newline at end of file
diff --git a/app/views/common/_discourse_stylesheet.html.erb b/app/views/common/_discourse_stylesheet.html.erb
new file mode 100644
index 00000000000..6f81b70913f
--- /dev/null
+++ b/app/views/common/_discourse_stylesheet.html.erb
@@ -0,0 +1,12 @@
+ <%- unless SiteCustomization.override_default_style(session[:preview_style]) %>
+ <%=stylesheet_link_tag "application"%>
+ <%- end %>
+
+ <%- if mini_profiler_enabled? %>
+ <%- Rack::MiniProfiler.step "stylsheet" do%>
+ <%= stylesheet_link_tag "admin"%>
+ <%-end%>
+ <%- elsif admin? %>
+ <%= stylesheet_link_tag "admin"%>
+ <%-end%>
+ <%=SiteCustomization.custom_stylesheet(session[:preview_style])%>
\ No newline at end of file
diff --git a/app/views/common/_google_analytics.html.erb b/app/views/common/_google_analytics.html.erb
new file mode 100644
index 00000000000..79f2414e848
--- /dev/null
+++ b/app/views/common/_google_analytics.html.erb
@@ -0,0 +1,14 @@
+
\ No newline at end of file
diff --git a/app/views/common/_special_font_face.html.erb b/app/views/common/_special_font_face.html.erb
new file mode 100644
index 00000000000..bad6846bf53
--- /dev/null
+++ b/app/views/common/_special_font_face.html.erb
@@ -0,0 +1,31 @@
+
+ <%#
+ The fonts are loaded outside of the stylesheet so that we can dynamically change
+ the path. This is to get around CDN caching on the Origin:
+
+ https://forums.aws.amazon.com/thread.jspa?threadID=114646
+ %>
+
+<% font_domain = "#{request.protocol}#{request.host_with_port}" %>
+
+
\ No newline at end of file
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
index 106abd5f98d..f314b2819a0 100644
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -19,48 +19,11 @@
<%= javascript_include_tag "preload_store" %>
- <%#
- The fonts are loaded outside of the stylesheet so that we can dynamically change
- the path. This is to get around CDN caching on the Origin:
- https://forums.aws.amazon.com/thread.jspa?threadID=114646
- %>
+ <%= render :partial => "common/special_font_face" %>
+ <%= render :partial => "common/discourse_stylesheet" %>
- <%- font_domain = "#{request.protocol}#{request.host_with_port}" %>
-
-
- <%- unless SiteCustomization.override_default_style(session[:preview_style]) %>
- <%=stylesheet_link_tag "application"%>
- <%- end %>
-
- <%- if mini_profiler_enabled? %>
- <%- Rack::MiniProfiler.step "stylsheet" do%>
- <%= stylesheet_link_tag "admin"%>
- <%-end%>
- <%- elsif admin? %>
- <%= stylesheet_link_tag "admin"%>
- <%-end%>
- <%=SiteCustomization.custom_stylesheet(session[:preview_style])%>
<%=csrf_meta_tags%>
@@ -98,48 +61,8 @@
- <%- if mini_profiler_enabled? %>
- <%- Rack::MiniProfiler.step "application" do %>
- <%= javascript_include_tag "application" %>
- <%-end%>
-
- <%- Rack::MiniProfiler.step "admin" do %>
- <%= javascript_include_tag "admin"%>
- <%-end%>
- <%- else %>
- <%= javascript_include_tag "application" %>
- <%- if admin? %>
- <%= javascript_include_tag "admin"%>
- <%- end %>
- <%- end%>
-
-
-
- <%- if Rails.env == "production" and SiteSetting.ga_tracking_code.present? %>
-
- <%-end%>
+ <%= render :partial => "common/discourse_javascript" %>
+ <%= render_google_analytics_code %>