2
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2025-09-06 10:50:21 +08:00

Merge branch 'master' of github.com:discourse/discourse

This commit is contained in:
Sam Saffron 2013-02-12 13:45:24 +11:00
commit ad88487a32
27 changed files with 443 additions and 293 deletions

View file

@ -10,7 +10,7 @@ on Discourse with:
1. Install VirtualBox: https://www.virtualbox.org/wiki/Downloads 1. Install VirtualBox: https://www.virtualbox.org/wiki/Downloads
2. Install Vagrant: https://www.vagrantup.com/ 2. Install Vagrant: https://www.vagrantup.com/
3. Open a terminal 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` 5. Enter the project directory: `cd discourse`
### Using Vagrant ### 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! 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 ### Guard + Rspec
@ -71,6 +71,12 @@ Wait a minute while it runs all our unit tests. Once it has completed, live relo
### Sending Email ### 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 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: To start mailcatcher, run the following command in the vagrant image:

View file

@ -63,34 +63,34 @@ PATH
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
actionmailer (3.2.11) actionmailer (3.2.12)
actionpack (= 3.2.11) actionpack (= 3.2.12)
mail (~> 2.4.4) mail (~> 2.4.4)
actionpack (3.2.11) actionpack (3.2.12)
activemodel (= 3.2.11) activemodel (= 3.2.12)
activesupport (= 3.2.11) activesupport (= 3.2.12)
builder (~> 3.0.0) builder (~> 3.0.0)
erubis (~> 2.7.0) erubis (~> 2.7.0)
journey (~> 1.0.4) journey (~> 1.0.4)
rack (~> 1.4.0) rack (~> 1.4.5)
rack-cache (~> 1.2) rack-cache (~> 1.2)
rack-test (~> 0.6.1) rack-test (~> 0.6.1)
sprockets (~> 2.2.1) sprockets (~> 2.2.1)
activemodel (3.2.11) activemodel (3.2.12)
activesupport (= 3.2.11) activesupport (= 3.2.12)
builder (~> 3.0.0) builder (~> 3.0.0)
activerecord (3.2.11) activerecord (3.2.12)
activemodel (= 3.2.11) activemodel (= 3.2.12)
activesupport (= 3.2.11) activesupport (= 3.2.12)
arel (~> 3.0.2) arel (~> 3.0.2)
tzinfo (~> 0.3.29) tzinfo (~> 0.3.29)
activerecord-postgres-hstore (0.7.1) activerecord-postgres-hstore (0.7.1)
rails rails
rake rake
activeresource (3.2.11) activeresource (3.2.12)
activemodel (= 3.2.11) activemodel (= 3.2.12)
activesupport (= 3.2.11) activesupport (= 3.2.12)
activesupport (3.2.11) activesupport (3.2.12)
i18n (~> 0.6) i18n (~> 0.6)
multi_json (~> 1.0) multi_json (~> 1.0)
acts_as_paranoid (0.4.1) acts_as_paranoid (0.4.1)
@ -194,7 +194,7 @@ GEM
jquery-rails (2.2.0) jquery-rails (2.2.0)
railties (>= 3.0, < 5.0) railties (>= 3.0, < 5.0)
thor (>= 0.14, < 2.0) thor (>= 0.14, < 2.0)
json (1.7.6) json (1.7.7)
koala (1.6.0) koala (1.6.0)
addressable (~> 2.2) addressable (~> 2.2)
faraday (~> 0.8) faraday (~> 0.8)
@ -208,10 +208,10 @@ GEM
treetop (~> 1.4.8) treetop (~> 1.4.8)
metaclass (0.0.1) metaclass (0.0.1)
method_source (0.8.1) method_source (0.8.1)
mime-types (1.20.1) mime-types (1.21)
mocha (0.10.5) mocha (0.10.5)
metaclass (~> 0.0.1) metaclass (~> 0.0.1)
multi_json (1.5.0) multi_json (1.5.1)
multipart-post (1.1.5) multipart-post (1.1.5)
mustache (0.99.4) mustache (0.99.4)
net-scp (1.0.4) net-scp (1.0.4)
@ -239,17 +239,17 @@ GEM
rack rack
rack-test (0.6.2) rack-test (0.6.2)
rack (>= 1.0) rack (>= 1.0)
rails (3.2.11) rails (3.2.12)
actionmailer (= 3.2.11) actionmailer (= 3.2.12)
actionpack (= 3.2.11) actionpack (= 3.2.12)
activerecord (= 3.2.11) activerecord (= 3.2.12)
activeresource (= 3.2.11) activeresource (= 3.2.12)
activesupport (= 3.2.11) activesupport (= 3.2.12)
bundler (~> 1.0) bundler (~> 1.0)
railties (= 3.2.11) railties (= 3.2.12)
railties (3.2.11) railties (3.2.12)
actionpack (= 3.2.11) actionpack (= 3.2.12)
activesupport (= 3.2.11) activesupport (= 3.2.12)
rack-ssl (~> 1.3.2) rack-ssl (~> 1.3.2)
rake (>= 0.8.7) rake (>= 0.8.7)
rdoc (~> 3.4) rdoc (~> 3.4)
@ -258,7 +258,7 @@ GEM
rb-fsevent (0.9.3) rb-fsevent (0.9.3)
rb-inotify (0.8.8) rb-inotify (0.8.8)
ffi (>= 0.5.0) ffi (>= 0.5.0)
rdoc (3.12) rdoc (3.12.1)
json (~> 1.4) json (~> 1.4)
redis (3.0.2) redis (3.0.2)
redis-actionpack (3.2.3) redis-actionpack (3.2.3)

View file

@ -84,9 +84,11 @@
ul = div.find('ul') ul = div.find('ul')
selectedOption = 0 selectedOption = 0
markSelected() markSelected()
ul.find('li').click -> ul.find('li').click ->
selectedOption = ul.find('li').index(this) selectedOption = ul.find('li').index(this)
completeTerm(autocompleteOptions[selectedOption]) completeTerm(autocompleteOptions[selectedOption])
false
pos = null pos = null
if isInput if isInput

View file

@ -1,5 +1,4 @@
<h3><i class='icon icon-lock'></i> {{i18n private_message_info.title}}</h3> <h3><i class='icon icon-lock'></i> {{i18n private_message_info.title}}</h3>
<p>{{{i18n private_message_info.description}}}</p>
<div class='participants clearfix'> <div class='participants clearfix'>
{{#each content.allowed_users}} {{#each content.allowed_users}}
<div class='user'> <div class='user'>

View file

@ -38,6 +38,7 @@ window.Discourse.TopicFooterButtonsView = Ember.ContainerView.extend
@addObject Discourse.ButtonView.createWithMixins @addObject Discourse.ButtonView.createWithMixins
classNames: ['btn', 'btn-primary', 'create'] classNames: ['btn', 'btn-primary', 'create']
attributeBindings: ['disabled']
text: (-> text: (->
archetype = @get('controller.content.archetype') archetype = @get('controller.content.archetype')
return customTitle if customTitle = @get("parentView.replyButtonText#{archetype.capitalize()}") return customTitle if customTitle = @get("parentView.replyButtonText#{archetype.capitalize()}")
@ -46,6 +47,7 @@ window.Discourse.TopicFooterButtonsView = Ember.ContainerView.extend
renderIcon: (buffer) -> buffer.push("<i class='icon icon-plus'></i>") renderIcon: (buffer) -> buffer.push("<i class='icon icon-plus'></i>")
click: -> @get('controller').reply() click: -> @get('controller').reply()
helpKey: 'topic.reply.help' helpKey: 'topic.reply.help'
disabled: !@get('controller.content.can_create_post')
unless topic.get('isPrivateMessage') unless topic.get('isPrivateMessage')
@addObject Discourse.DropdownButtonView.createWithMixins @addObject Discourse.DropdownButtonView.createWithMixins

View file

@ -387,7 +387,7 @@ kbd {
z-index: 1; z-index: 1;
} }
button { button {
padding: 0; padding: 0 1px;
cursor: pointer; cursor: pointer;
z-index: 1000; z-index: 1000;
position: absolute; position: absolute;

View file

@ -66,12 +66,12 @@ class ApplicationController < ActionController::Base
# for now do a simple remap, we may look at cleaner ways of doing the render # for now do a simple remap, we may look at cleaner ways of doing the render
raise ActiveRecord::RecordNotFound raise ActiveRecord::RecordNotFound
else else
render file: 'public/404', layout: false, status: 404 render file: 'public/404', formats: [:html], layout: false, status: 404
end end
end end
rescue_from Discourse::InvalidAccess do rescue_from Discourse::InvalidAccess do
render file: 'public/403', layout: false, status: 403 render file: 'public/403', formats: [:html], layout: false, status: 403
end end
def store_preloaded(key, json) def store_preloaded(key, json)

View file

@ -22,9 +22,14 @@ class SessionController < ApplicationController
# If their password is correct # If their password is correct
if @user.confirm_password?(params[:password]) if @user.confirm_password?(params[:password])
if @user.email_confirmed?
log_on_user(@user) log_on_user(@user)
render_serialized(@user, UserSerializer) render_serialized(@user, UserSerializer)
return return
else
render :json => {error: I18n.t("login.not_activated")}
return
end
end end
end end

View file

@ -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

View file

@ -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

View file

@ -342,6 +342,7 @@ class Post < ActiveRecord::Base
# This calculates the geometric mean of the post timings and stores it along with # This calculates the geometric mean of the post timings and stores it along with
# each post. # each post.
def self.calculate_avg_time def self.calculate_avg_time
retry_lock_error do
exec_sql("UPDATE posts exec_sql("UPDATE posts
SET avg_time = (x.gmean / 1000) SET avg_time = (x.gmean / 1000)
FROM (SELECT post_timings.topic_id, FROM (SELECT post_timings.topic_id,
@ -356,6 +357,7 @@ class Post < ActiveRecord::Base
WHERE x.topic_id = posts.topic_id WHERE x.topic_id = posts.topic_id
AND x.post_number = posts.post_number") AND x.post_number = posts.post_number")
end end
end
before_save do before_save do
self.last_editor_id ||= self.user_id self.last_editor_id ||= self.user_id

View file

@ -47,6 +47,10 @@ class TopicLink < ActiveRecord::Base
internal = true internal = true
route = Rails.application.routes.recognize_path(parsed.path) 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] topic_id = route[:topic_id]
post_number = route[:post_number] || 1 post_number = route[:post_number] || 1
end end

View file

@ -380,6 +380,10 @@ class User < ActiveRecord::Base
end end
end end
def email_confirmed?
email_tokens.where(email: self.email, confirmed: true).present?
end
protected protected

View file

@ -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%>
<script>
Discourse.CDN = '<%= Rails.configuration.action_controller.asset_host %>';
Discourse.BaseUrl = '<%= RailsMultisite::ConnectionManagement.current_hostname %>';
Discourse.Environment = '<%= Rails.env %>';
window.Discourse.Router.map(function() {
return Discourse.routeBuilder.call(this);
});
Discourse.start()
Discourse.initialize()
</script>

View file

@ -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])%>

View file

@ -0,0 +1,14 @@
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', '<%= SiteSetting.ga_tracking_code %>']);
_gaq.push(['_setCustomVar', 1, 'Anonymous', <%= !current_user %>, 2]);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>

View file

@ -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}" %>
<style>
@font-face {
font-family: 'FontAwesome';
src: url('<%=asset_path "fontawesome-webfont.eot" %>?<%= font_domain %>');
src: url('<%=asset_path "fontawesome-webfont.eot" %>?<%= font_domain %>#iefix') format('embedded-opentype'),
url('<%=asset_path "fontawesome-webfont.woff" %>?<%= font_domain %>') format('woff'),
url('<%=asset_path "fontawesome-webfont.ttf" %>?<%= font_domain %>') format('truetype');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'zocial';
src: url('/assets/zocial-regular-webfont.woff?<%= font_domain %>') format('woff'),
url('/assets/zocial-regular-webfont.ttf?<%= font_domain %>') format('truetype'),
url('/assets/zocial-regular-webfont.svg?<%= font_domain %>#zocialregular') format('svg');
font-weight: normal;
font-style: normal;
}
</style>

View file

@ -19,48 +19,11 @@
<%= javascript_include_tag "preload_store" %> <%= 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}" %>
<style>
@font-face {
font-family: 'FontAwesome';
src: url('<%=asset_path "fontawesome-webfont.eot" %>?<%= font_domain %>');
src: url('<%=asset_path "fontawesome-webfont.eot" %>?<%= font_domain %>#iefix') format('embedded-opentype'),
url('<%=asset_path "fontawesome-webfont.woff" %>?<%= font_domain %>') format('woff'),
url('<%=asset_path "fontawesome-webfont.ttf" %>?<%= font_domain %>') format('truetype');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'zocial';
src: url('/assets/zocial-regular-webfont.woff?<%= font_domain %>') format('woff'),
url('/assets/zocial-regular-webfont.ttf?<%= font_domain %>') format('truetype'),
url('/assets/zocial-regular-webfont.svg?<%= font_domain %>#zocialregular') format('svg');
font-weight: normal;
font-style: normal;
}
</style>
<%- 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%> <%=csrf_meta_tags%>
</head> </head>
@ -98,48 +61,8 @@
<footer id='bottom'></footer> <footer id='bottom'></footer>
<%- if mini_profiler_enabled? %> <%= render :partial => "common/discourse_javascript" %>
<%- Rack::MiniProfiler.step "application" do %> <%= render_google_analytics_code %>
<%= 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%>
<script>
Discourse.CDN = '<%= Rails.configuration.action_controller.asset_host %>';
Discourse.BaseUrl = '<%= RailsMultisite::ConnectionManagement.current_hostname %>';
Discourse.Environment = '<%= Rails.env %>';
window.Discourse.Router.map(function() {
return Discourse.routeBuilder.call(this);
});
Discourse.start()
Discourse.initialize()
</script>
<%- if Rails.env == "production" and SiteSetting.ga_tracking_code.present? %>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', '<%= SiteSetting.ga_tracking_code %>']);
_gaq.push(['_setCustomVar', 1, 'Anonymous', <%= !current_user %>, 2]);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
<%-end%>
</body> </body>
</html> </html>

View file

@ -1,5 +1,6 @@
<h2> <h2>
<a href="<%= @topic_view.topic.relative_url %>"><%= @topic_view.topic.title %></a> <%= render_topic_title(@topic_view.topic) %>
</h2> </h2>
<% (@post ? [@post] : @topic_view.posts).each do |post| %> <% (@post ? [@post] : @topic_view.posts).each do |post| %>
<div class='creator'> <div class='creator'>
@ -12,7 +13,7 @@
<% if @next_page%> <% if @next_page%>
<p> <p>
<b><a href="<%= @topic_view.topic.relative_url %>?page=<%= @next_page%>">next page</a></b> <b><%= render_topic_next_page_link(@topic_view.topic, @next_page) %></b>
</p> </p>
<% end %> <% end %>

View file

@ -596,7 +596,6 @@ en:
private_message_info: private_message_info:
title: "Private Conversation" title: "Private Conversation"
description: "Participants in this private conversation"
invite: "Invite Others..." invite: "Invite Others..."
email: 'Email' email: 'Email'
@ -1039,6 +1038,7 @@ en:
wait_approval: "Thanks for signing up. We will notify you when your account has been approved." wait_approval: "Thanks for signing up. We will notify you when your account has been approved."
active: "Your account is active and ready." active: "Your account is active and ready."
activate_email: "You're almost done! We sent an activation email to <b>%{email}</b>. Please follow the instructions in the email to activate your account." activate_email: "You're almost done! We sent an activation email to <b>%{email}</b>. Please follow the instructions in the email to activate your account."
not_activated: "You can't log in yet. We sent an activation email to you. Please follow the instructions in the email to activate your account."
errors: "Failed to create account: %{errors}" errors: "Failed to create account: %{errors}"
not_available: "Not available. Try %{suggestion}?" not_available: "Not available. Try %{suggestion}?"

View file

@ -21,9 +21,9 @@ module CurrentUser
@not_logged_in = session[:current_user_id].blank? @not_logged_in = session[:current_user_id].blank?
if @current_user if @current_user
@current_user.update_last_seen! @current_user.update_last_seen!
if @current_user.ip_address != request.remote_ip if (@current_user.ip_address != request.remote_ip) and request.remote_ip.present?
@current_user.ip_address = request.remote_ip @current_user.ip_address = request.remote_ip
User.exec_sql('update users set ip_address = ? where id = ?', request.remote_ip, @current_user.id) @current_user.update_column(:ip_address, request.remote_ip)
end end
end end
@current_user @current_user

View file

@ -9,10 +9,21 @@ class DiscoursePluginRegistry
attr_accessor :stylesheets attr_accessor :stylesheets
end end
def register_js(filename, options={}) # Default accessor values
self.class.javascripts ||= Set.new #
self.class.server_side_javascripts ||= Set.new def self.stylesheets
@stylesheets ||= Set.new
end
def self.javascripts
@javascripts ||= Set.new
end
def self.server_side_javascripts
@server_side_javascripts ||= Set.new
end
def register_js(filename, options={})
# If we have a server side option, add that too. # If we have a server side option, add that too.
self.class.server_side_javascripts << options[:server_side] if options[:server_side].present? self.class.server_side_javascripts << options[:server_side] if options[:server_side].present?
@ -20,12 +31,11 @@ class DiscoursePluginRegistry
end end
def register_css(filename) def register_css(filename)
self.class.stylesheets ||= Set.new
self.class.stylesheets << filename self.class.stylesheets << filename
end end
def stylesheets def stylesheets
self.class.stylesheets || Set.new self.class.stylesheets
end end
def register_archetype(name, options={}) def register_archetype(name, options={})
@ -33,17 +43,17 @@ class DiscoursePluginRegistry
end end
def server_side_javascripts def server_side_javascripts
self.class.javascripts || Set.new self.class.javascripts
end end
def javascripts def javascripts
self.class.javascripts || Set.new self.class.javascripts
end end
def self.clear def self.clear
self.stylesheets = Set.new self.stylesheets = nil
self.server_side_javascripts = Set.new self.server_side_javascripts = nil
self.javascripts = Set.new self.javascripts = nil
end end
def self.setup(plugin_class) def self.setup(plugin_class)
@ -52,6 +62,4 @@ class DiscoursePluginRegistry
plugin.setup plugin.setup
end end
end end

View file

@ -15,6 +15,24 @@ class ActiveRecord::Base
ActiveRecord::Base.exec_sql(*args) ActiveRecord::Base.exec_sql(*args)
end end
# Executes the given block +retries+ times (or forever, if explicitly given nil),
# catching and retrying SQL Deadlock errors.
#
# Thanks to: http://stackoverflow.com/a/7427186/165668
#
def self.retry_lock_error(retries=5, &block)
begin
yield
rescue ActiveRecord::StatementInvalid => e
if e.message =~ /Deadlock found when trying to get lock/ and (retries.nil? || retries > 0)
retry_lock_error(retries ? retries - 1 : nil, &block)
else
raise e
end
end
end
# Support for psql. If we want to support multiple RDBMs in the future we can # Support for psql. If we want to support multiple RDBMs in the future we can
# split this. # split this.
def exec_sql_row_count(*args) def exec_sql_row_count(*args)

View file

@ -5,6 +5,27 @@ describe DiscoursePluginRegistry do
let(:registry) { DiscoursePluginRegistry.new } let(:registry) { DiscoursePluginRegistry.new }
context '#stylesheets' do
it 'defaults to an empty Set' do
DiscoursePluginRegistry.stylesheets = nil
DiscoursePluginRegistry.stylesheets.should == Set.new
end
end
context '#javascripts' do
it 'defaults to an empty Set' do
DiscoursePluginRegistry.javascripts = nil
DiscoursePluginRegistry.javascripts.should == Set.new
end
end
context '#server_side_javascripts' do
it 'defaults to an empty Set' do
DiscoursePluginRegistry.server_side_javascripts = nil
DiscoursePluginRegistry.server_side_javascripts.should == Set.new
end
end
context '.register_css' do context '.register_css' do
before do before do
registry.register_css('hello.css') registry.register_css('hello.css')

View file

@ -6,17 +6,21 @@ describe SessionController do
let(:user) { Fabricate(:user) } let(:user) { Fabricate(:user) }
context 'when email is confirmed' do
before do
token = user.email_tokens.where(email: user.email).first
EmailToken.confirm(token.token)
end
it "raises an error when the login isn't present" do it "raises an error when the login isn't present" do
lambda { xhr :post, :create }.should raise_error(Discourse::InvalidParameters) lambda { xhr :post, :create }.should raise_error(Discourse::InvalidParameters)
end end
describe 'invalid password' do describe 'invalid password' do
it "should return an error with an invalid password" do it "should return an error with an invalid password" do
xhr :post, :create, login: user.username, password: 'sssss' xhr :post, :create, login: user.username, password: 'sssss'
::JSON.parse(response.body)['error'].should be_present ::JSON.parse(response.body)['error'].should be_present
end end
end end
describe 'success by username' do describe 'success by username' do
@ -72,11 +76,23 @@ describe SessionController do
it "doesn't log in the user" do it "doesn't log in the user" do
session[:current_user_id].should be_blank session[:current_user_id].should be_blank
end end
end
end
end end
context 'when email has not been confirmed' do
before do
xhr :post, :create, login: user.email, password: 'myawesomepassword'
end end
it "doesn't log in the user" do
session[:current_user_id].should be_blank
end
it 'returns an error message' do
::JSON.parse(response.body)['error'].should be_present
end
end
end end
describe '.destroy' do describe '.destroy' do

View file

@ -44,31 +44,9 @@ describe TopicLink do
end end
describe 'domain-less link' do
let(:post) { @topic.posts.create(user: @user, raw: "<a href='/users'>hello</a>") }
let!(:link) do
TopicLink.extract_from(post)
@topic.topic_links.first
end
it 'is extracted' do
link.should be_present
end
it 'has the correct domain' do
link.domain.should == test_uri.host
end
it "is not destroyed when we call extract from again" do
TopicLink.extract_from(post)
link.reload
link.should be_present
end
end
describe 'internal links' do describe 'internal links' do
context 'topic link' do
before do before do
@other_topic = Fabricate(:topic, user: @user) @other_topic = Fabricate(:topic, user: @user)
@other_post = @other_topic.posts.create(user: @user, raw: "some content") @other_post = @other_topic.posts.create(user: @user, raw: "some content")
@ -165,6 +143,42 @@ describe TopicLink do
end end
context "link to a user on discourse" do
let(:post) { @topic.posts.create(user: @user, raw: "<a href='/users/#{@user.username_lower}'>user</a>") }
before do
TopicLink.extract_from(post)
end
it 'does not extract a link' do
@topic.topic_links.should be_blank
end
end
context "link to a discourse resource like a FAQ" do
let(:post) { @topic.posts.create(user: @user, raw: "<a href='/faq'>faq link here</a>") }
before do
TopicLink.extract_from(post)
end
it 'does not extract a link' do
@topic.topic_links.should be_present
end
end
context "@mention links" do
let(:post) { @topic.posts.create(user: @user, raw: "Hey @#{@user.username_lower}") }
before do
TopicLink.extract_from(post)
end
it 'does not extract a link' do
@topic.topic_links.should be_blank
end
end
end
describe 'internal link from pm' do describe 'internal link from pm' do
before do before do
@pm = Fabricate(:topic, user: @user, archetype: 'private_message') @pm = Fabricate(:topic, user: @user, archetype: 'private_message')

View file

@ -617,4 +617,30 @@ describe User do
it { should_not be_active } it { should_not be_active }
end end
describe 'email_confirmed?' do
let(:user) { Fabricate(:user) }
context 'when email has not been confirmed yet' do
it 'should return false' do
user.email_confirmed?.should be_false
end
end
context 'when email has been confirmed' do
it 'should return true' do
token = user.email_tokens.where(email: user.email).first
EmailToken.confirm(token.token)
user.email_confirmed?.should be_true
end
end
context 'when user has no email tokens for some reason' do
it 'should return false' do
user.email_tokens.each {|t| t.destroy}
user.reload
user.email_confirmed?.should be_false
end
end
end
end end