mirror of
https://github.com/codeninjasllc/discourse.git
synced 2025-03-23 13:26:22 -04:00
Merge branch 'master' of github.com:discourse/discourse
This commit is contained in:
commit
f5f09933df
31 changed files with 325 additions and 82 deletions
app
assets
javascripts/discourse
controllers
dialects
routes
templates/user
stylesheets
common/foundation
desktop
mobile
controllers
models
views
config
lib
spec
components
integration
test/javascripts/fixtures
|
@ -31,7 +31,7 @@ Discourse.CreateAccountController = Discourse.Controller.extend(Discourse.ModalF
|
|||
}.property('authOptions.auth_provider'),
|
||||
|
||||
passwordInstructions: function() {
|
||||
return I18n.t('user.password.instructions', {count: 6}); // TODO: soon to be configurable
|
||||
return I18n.t('user.password.instructions', {count: Discourse.SiteSettings.min_password_length});
|
||||
}.property(),
|
||||
|
||||
// Validate the name
|
||||
|
@ -273,7 +273,7 @@ Discourse.CreateAccountController = Discourse.Controller.extend(Discourse.ModalF
|
|||
}
|
||||
|
||||
// If too short
|
||||
if (password.length < 6) {
|
||||
if (password.length < Discourse.SiteSettings.min_password_length) {
|
||||
return Discourse.InputValidation.create({
|
||||
failed: true,
|
||||
reason: I18n.t('user.password.too_short')
|
||||
|
|
|
@ -17,7 +17,6 @@ var parser = window.BetterMarkdown,
|
|||
@method initializeDialects
|
||||
**/
|
||||
function initializeDialects() {
|
||||
Discourse.Dialect.trigger('register', {dialect: dialect, MD: MD});
|
||||
MD.buildBlockOrder(dialect.block);
|
||||
MD.buildInlinePatterns(dialect.inline);
|
||||
initialized = true;
|
||||
|
|
|
@ -128,10 +128,13 @@ Discourse.TopicRoute = Discourse.Route.extend({
|
|||
editingTopic: false
|
||||
});
|
||||
|
||||
Discourse.TopicRoute.trigger('setupTopicController', this);
|
||||
|
||||
this.controllerFor('header').setProperties({
|
||||
topic: model,
|
||||
showExtraInfo: false
|
||||
});
|
||||
|
||||
this.controllerFor('composer').set('topic', model);
|
||||
Discourse.TopicTrackingState.current().trackIncoming('all');
|
||||
controller.subscribe();
|
||||
|
@ -142,4 +145,4 @@ Discourse.TopicRoute = Discourse.Route.extend({
|
|||
|
||||
});
|
||||
|
||||
|
||||
RSVP.EventTarget.mixin(Discourse.TopicRoute);
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
<h1>{{username}} {{{statusIcon}}}</h1>
|
||||
<h2>{{name}}</h2>
|
||||
|
||||
<div class='bio'>{{{bio_excerpt}}}</div>
|
||||
<div class='bio'>{{{bio_cooked}}}</div>
|
||||
|
||||
{{#if isSuspended}}
|
||||
<div class='suspended'>
|
||||
|
|
|
@ -41,17 +41,11 @@
|
|||
}
|
||||
}
|
||||
|
||||
@mixin portrait {
|
||||
@media only screen and (max-width : 320px) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin landscape {
|
||||
@media only screen and (min-width : 321px) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
@mixin mobile-portrait { @media only screen and (max-width : 320px) { @content; } }
|
||||
@mixin not-mobile-portrait { @media only screen and (min-width : 321px) { @content; } }
|
||||
@mixin mobile-landscape { @media only screen and (min-width : 321px) and (max-width : 959px) { @content; } }
|
||||
@mixin not-tablet-landscape { @media only screen and (max-width : 959px) { @content; } }
|
||||
@mixin tablet-landscape { @media only screen and (min-width : 960px) { @content; } }
|
||||
|
||||
// CSS3 properties
|
||||
// --------------------------------------------------
|
||||
|
|
|
@ -18,8 +18,12 @@
|
|||
|
||||
@include box-shadow(3px 3px 3px rgba($black, 0.14));
|
||||
|
||||
h3 {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0 0 10px 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
a.close {
|
||||
|
|
|
@ -16,7 +16,7 @@ a.loading-onebox {
|
|||
.onebox-result {
|
||||
|
||||
margin-top: 15px;
|
||||
padding: 12px 25px 12px 12px;
|
||||
padding: 12px 25px 12px 12px;
|
||||
border-left: 5px solid #bebebe;
|
||||
background: #eee;
|
||||
font-size: 14px;
|
||||
|
@ -43,7 +43,10 @@ a.loading-onebox {
|
|||
.onebox-result-body {
|
||||
padding-top: 5px;
|
||||
img {
|
||||
max-width:200px;
|
||||
max-width: 100px;
|
||||
max-height: 80%;
|
||||
float: left;
|
||||
margin-right: 10px;
|
||||
}
|
||||
h3, h4 {
|
||||
margin: 0px !important;
|
||||
|
@ -55,12 +58,6 @@ a.loading-onebox {
|
|||
code {
|
||||
max-height: 400px;
|
||||
}
|
||||
img {
|
||||
max-width: 30%;
|
||||
max-height: 80%;
|
||||
float: left;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.metrics {
|
||||
clear: both;
|
||||
padding-bottom: 25px;
|
||||
|
@ -76,8 +73,8 @@ a.loading-onebox {
|
|||
// RottenTomatoes Onebox
|
||||
.onebox-result {
|
||||
.onebox-result-body {
|
||||
img.verdict {
|
||||
float: none;
|
||||
img.verdict {
|
||||
float: none;
|
||||
margin-right: 7px;
|
||||
}
|
||||
img.popcorn {
|
||||
|
|
|
@ -13,10 +13,6 @@
|
|||
padding: 12px 12px 5px 12px;
|
||||
max-width: 350px;
|
||||
|
||||
h1.new-user a {
|
||||
color: $dark_gray;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 30px;
|
||||
line-height: 33px;
|
||||
|
@ -67,4 +63,8 @@
|
|||
.btn {
|
||||
margin: 0 0 7px 0;
|
||||
}
|
||||
|
||||
.new-user a {
|
||||
color: $dark_gray;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -572,6 +572,10 @@ iframe {
|
|||
font-size: 36px;
|
||||
}
|
||||
|
||||
.new-user a {
|
||||
color: $dark_gray;
|
||||
}
|
||||
|
||||
.staff a {
|
||||
padding: 4px;
|
||||
margin: -4px 0 0 0;
|
||||
|
|
|
@ -39,7 +39,10 @@ a.loading-onebox {
|
|||
padding-top: 5px;
|
||||
font-family: Georgia, Times, "Times New Roman", serif;
|
||||
img {
|
||||
max-width:200px;
|
||||
max-width: 100px;
|
||||
max-height: 80%;
|
||||
float: left;
|
||||
margin-right: 10px;
|
||||
}
|
||||
h3, h4 {
|
||||
margin: 0px !important;
|
||||
|
@ -51,12 +54,6 @@ a.loading-onebox {
|
|||
code {
|
||||
max-height: 400px;
|
||||
}
|
||||
img {
|
||||
max-width: 30%;
|
||||
max-height: 80%;
|
||||
float: left;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.metrics {
|
||||
clear: both;
|
||||
padding-bottom: 25px;
|
||||
|
@ -72,8 +69,8 @@ a.loading-onebox {
|
|||
// RottenTomatoes Onebox
|
||||
.onebox-result {
|
||||
.onebox-result-body {
|
||||
img.verdict {
|
||||
float: none;
|
||||
img.verdict {
|
||||
float: none;
|
||||
margin-right: 7px;
|
||||
}
|
||||
img.popcorn {
|
||||
|
|
|
@ -50,7 +50,7 @@
|
|||
border-top: 1px solid #ddd;
|
||||
border-bottom: 1px solid #ddd;
|
||||
|
||||
tbody tr {
|
||||
> tbody > tr {
|
||||
background-color: $white;
|
||||
&:nth-child(even) {
|
||||
background-color: darken($white, 4%);
|
||||
|
@ -163,19 +163,27 @@
|
|||
// --------------------------------------------------
|
||||
|
||||
#topic-list.categories {
|
||||
.badge-category {
|
||||
display: inline-block;
|
||||
margin-top: 1px;
|
||||
}
|
||||
.category-description {
|
||||
margin-top: 8px;
|
||||
}
|
||||
.featured-users {
|
||||
@include portrait {
|
||||
@include mobile-portrait {
|
||||
margin-bottom: 10px;
|
||||
padding-top: 8px;
|
||||
clear: left;
|
||||
}
|
||||
@include landscape {
|
||||
@include not-mobile-portrait {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
.latest {
|
||||
@include portrait { width: 150px; }
|
||||
@include landscape { width: 270px; }
|
||||
@include mobile-portrait { width: 150px; }
|
||||
@include mobile-landscape { width: 270px; }
|
||||
@include tablet-landscape { width: 450px; }
|
||||
.featured-topic {
|
||||
margin: 8px 0;
|
||||
a.last-posted-at, a.last-posted-at:visited {
|
||||
|
@ -184,7 +192,22 @@
|
|||
}
|
||||
}
|
||||
.stats {
|
||||
display: none;
|
||||
@include not-tablet-landscape { display: none; }
|
||||
@include tablet-landscape { min-width: 80px; }
|
||||
}
|
||||
td.stats {
|
||||
.unit {
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
table.categoryStats {
|
||||
td {
|
||||
padding: 2px;
|
||||
vertical-align: bottom;
|
||||
line-height: 17px;
|
||||
&.value { text-align: right; }
|
||||
&.unit { text-align: left; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -433,7 +433,7 @@ iframe {
|
|||
background-color: lighten(yellow, 35%);
|
||||
}
|
||||
|
||||
h3.new-user a[href] {
|
||||
.new-user a {
|
||||
color: $dark_gray;
|
||||
}
|
||||
|
||||
|
|
|
@ -172,6 +172,7 @@ class UsersController < ApplicationController
|
|||
elsif request.put?
|
||||
raise Discourse::InvalidParameters.new(:password) unless params[:password].present?
|
||||
@user.password = params[:password]
|
||||
@user.password_required!
|
||||
logon_after_password_reset if @user.save
|
||||
end
|
||||
render layout: 'no_js'
|
||||
|
|
|
@ -22,7 +22,7 @@ TopicStatusUpdate = Struct.new(:topic, :user) do
|
|||
topic.update_column status.name, status.enabled?
|
||||
end
|
||||
|
||||
if status.manually_closing_topic? && topic.auto_close_at
|
||||
if topic.auto_close_at && (status.reopening_topic? || status.manually_closing_topic?)
|
||||
topic.reload.set_auto_close(nil).save
|
||||
end
|
||||
|
||||
|
|
|
@ -143,6 +143,7 @@ class User < ActiveRecord::Base
|
|||
where(username_lower: username.downcase).first
|
||||
end
|
||||
|
||||
|
||||
def enqueue_welcome_message(message_type)
|
||||
return unless SiteSetting.send_welcome_message?
|
||||
Jobs.enqueue(:send_system_message, user_id: id, message_type: message_type)
|
||||
|
@ -245,11 +246,23 @@ class User < ActiveRecord::Base
|
|||
@raw_password = password unless password.blank?
|
||||
end
|
||||
|
||||
def password
|
||||
'' # so that validator doesn't complain that a password attribute doesn't exist
|
||||
end
|
||||
|
||||
# Indicate that this is NOT a passwordless account for the purposes of validation
|
||||
def password_required!
|
||||
@password_required = true
|
||||
end
|
||||
|
||||
def password_required?
|
||||
!!@password_required
|
||||
end
|
||||
|
||||
def password_validator
|
||||
PasswordValidator.new(attributes: :password).validate_each(self, :password, @raw_password)
|
||||
end
|
||||
|
||||
def confirm_password?(password)
|
||||
return false unless password_hash && salt
|
||||
self.password_hash == hash_password(password, salt)
|
||||
|
@ -340,6 +353,10 @@ class User < ActiveRecord::Base
|
|||
topics_allowed.where(archetype: Archetype.private_message).count
|
||||
end
|
||||
|
||||
def posted_too_much_in_topic?(topic_id)
|
||||
trust_level == TrustLevel.levels[:newuser] && (Post.where(topic_id: topic_id, user_id: id).count >= SiteSetting.newuser_max_replies_per_topic)
|
||||
end
|
||||
|
||||
def bio_excerpt
|
||||
excerpt = PrettyText.excerpt(bio_cooked, 350)
|
||||
return excerpt if excerpt.blank? || has_trust_level?(:basic)
|
||||
|
@ -556,12 +573,6 @@ class User < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
|
||||
def password_validator
|
||||
if (@raw_password && @raw_password.length < 6) || (@password_required && !@raw_password)
|
||||
errors.add(:password, "must be 6 letters or longer")
|
||||
end
|
||||
end
|
||||
|
||||
def send_approval_email
|
||||
Jobs.enqueue(:user_email,
|
||||
type: :signup_after_approval,
|
||||
|
|
|
@ -2,22 +2,21 @@
|
|||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||
<channel>
|
||||
<% lang = SiteSetting.find_by_name('default_locale').try(:value) %>
|
||||
<% site_email = SiteSetting.find_by_name('contact_email').try(:value) %>
|
||||
<title><%= @title %></title>
|
||||
<link><%= @link %></link>
|
||||
<description><%= @description %></description>
|
||||
<% if lang %>
|
||||
<language><%= lang %></language>
|
||||
<% end %>
|
||||
<%= "<language>#{lang}</language>" if lang %>
|
||||
<lastBuildDate><%= @topic_list.topics.first.created_at.rfc2822 %></lastBuildDate>
|
||||
<atom:link href="<%= @atom_link %>" rel="self" type="application/rss+xml" />
|
||||
<% @topic_list.topics.each do |topic| %>
|
||||
<% topic_url = Discourse.base_url + topic.relative_url -%>
|
||||
<item>
|
||||
<title><%= topic.title %></title>
|
||||
<author><%= "@#{topic.user.username} (#{topic.user.name})" -%></author>
|
||||
<author><%= "#{site_email} (@#{topic.user.username}#{" #{topic.user.name}" if topic.user.name.present?})" -%></author>
|
||||
<category><%= topic.category.name %></category>
|
||||
<description><.\n"
|
||||
text_body_template: "Новые пользователи ожидают подтверждения (или отказа) перед тем, как они получат доступ до форума.\n\n[Проверьте список в секции администрирования](%{base_url}/admin/users/list/pending).\n"
|
||||
unsubscribe_link: 'Для того, чтобы отписаться от подобных сообщений, перейдите в [настройки профиля](%{user_preferences_url}).'
|
||||
user_notifications:
|
||||
previous_discussion: 'Предыдущие ответы'
|
||||
|
@ -836,6 +853,11 @@ ru:
|
|||
click_here: 'нажмите здесь'
|
||||
from: 'Cводка новостей сайта %{site_name}'
|
||||
read_more: 'Читать еще'
|
||||
posts:
|
||||
one: '1 сообщение'
|
||||
other: '%{count} сообщений'
|
||||
few: '%{count} сообщения'
|
||||
many: '%{count} сообщений'
|
||||
forgot_password:
|
||||
subject_template: '[%{site_name}] Сброс пароля'
|
||||
text_body_template: "Кто-то запросил смену вашего пароля на сайте [%{site_name}](%{base_url}).\n\nЕсли это были не вы, спокойно проигнорируйте это письмо.\n\nПройдите по следующей ссылке, чтобы задать новый пароль:\n%{base_url}/users/password-reset/%{email_token}\n"
|
||||
|
|
|
@ -140,7 +140,7 @@ Discourse::Application.routes.draw do
|
|||
get 'user_preferences' => 'users#user_preferences_redirect'
|
||||
get 'users/:username/private-messages' => 'user_actions#private_messages', constraints: {username: USERNAME_ROUTE_FORMAT}
|
||||
get 'users/:username/private-messages/:filter' => 'user_actions#private_messages', constraints: {username: USERNAME_ROUTE_FORMAT}
|
||||
get 'users/:username' => 'users#show', constraints: {username: USERNAME_ROUTE_FORMAT}
|
||||
get 'users/:username' => 'users#show', as: 'userpage', constraints: {username: USERNAME_ROUTE_FORMAT}
|
||||
put 'users/:username' => 'users#update', constraints: {username: USERNAME_ROUTE_FORMAT}
|
||||
get 'users/:username/preferences' => 'users#preferences', constraints: {username: USERNAME_ROUTE_FORMAT}, as: :email_preferences
|
||||
get 'users/:username/preferences/email' => 'users#preferences', constraints: {username: USERNAME_ROUTE_FORMAT}
|
||||
|
|
|
@ -76,6 +76,9 @@ users:
|
|||
must_approve_users:
|
||||
client: true
|
||||
default: false
|
||||
min_password_length:
|
||||
client: true
|
||||
default: 8
|
||||
enable_google_logins:
|
||||
client: true
|
||||
default: true
|
||||
|
@ -177,6 +180,7 @@ posting:
|
|||
default: true
|
||||
post_undo_action_window_mins: 10
|
||||
max_mentions_per_post: 10
|
||||
newuser_max_replies_per_topic: 3
|
||||
newuser_max_mentions_per_post: 2
|
||||
onebox_max_chars: 5000
|
||||
title_min_entropy: 10
|
||||
|
|
|
@ -7,6 +7,7 @@ class ComposerMessagesFinder
|
|||
|
||||
def find
|
||||
check_education_message ||
|
||||
check_new_user_many_replies ||
|
||||
check_avatar_notification ||
|
||||
check_sequential_replies ||
|
||||
check_dominating_topic
|
||||
|
@ -32,6 +33,12 @@ class ComposerMessagesFinder
|
|||
nil
|
||||
end
|
||||
|
||||
# New users have a limited number of replies in a topic
|
||||
def check_new_user_many_replies
|
||||
return unless replying? && @user.posted_too_much_in_topic?(@details[:topic_id])
|
||||
{templateName: 'composer/education', body: PrettyText.cook(I18n.t('education.too_many_replies', newuser_max_replies_per_topic: SiteSetting.newuser_max_replies_per_topic)) }
|
||||
end
|
||||
|
||||
# Should a user be contacted to update their avatar?
|
||||
def check_avatar_notification
|
||||
|
||||
|
|
12
lib/validators/password_validator.rb
Normal file
12
lib/validators/password_validator.rb
Normal file
|
@ -0,0 +1,12 @@
|
|||
class PasswordValidator < ActiveModel::EachValidator
|
||||
|
||||
def validate_each(record, attribute, value)
|
||||
return unless record.password_required?
|
||||
if value.nil?
|
||||
record.errors.add(attribute, :blank)
|
||||
elsif value.length < SiteSetting.min_password_length
|
||||
record.errors.add(attribute, :too_short, count: SiteSetting.min_password_length)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -5,6 +5,7 @@ class Validators::PostValidator < ActiveModel::Validator
|
|||
presence(record)
|
||||
stripped_length(record)
|
||||
raw_quality(record)
|
||||
max_posts_validator(record)
|
||||
max_mention_validator(record)
|
||||
max_images_validator(record)
|
||||
max_attachments_validator(record)
|
||||
|
@ -40,6 +41,12 @@ class Validators::PostValidator < ActiveModel::Validator
|
|||
end
|
||||
end
|
||||
|
||||
def max_posts_validator(post)
|
||||
if post.acting_user.present? && post.acting_user.posted_too_much_in_topic?(post.topic_id)
|
||||
post.errors.add(:base, I18n.t(:too_many_replies))
|
||||
end
|
||||
end
|
||||
|
||||
# Ensure new users can not put too many images in a post
|
||||
def max_images_validator(post)
|
||||
add_error_if_count_exceeded(post, :too_many_images, post.image_count, SiteSetting.newuser_max_images) unless acting_user_is_trusted?(post)
|
||||
|
|
|
@ -10,6 +10,7 @@ describe ComposerMessagesFinder do
|
|||
|
||||
it "calls all the message finders" do
|
||||
finder.expects(:check_education_message).once
|
||||
finder.expects(:check_new_user_many_replies).once
|
||||
finder.expects(:check_avatar_notification).once
|
||||
finder.expects(:check_sequential_replies).once
|
||||
finder.expects(:check_dominating_topic).once
|
||||
|
@ -56,6 +57,24 @@ describe ComposerMessagesFinder do
|
|||
finder.check_education_message.should be_blank
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context '.check_new_user_many_replies' do
|
||||
let(:user) { Fabricate.build(:user) }
|
||||
|
||||
context 'replying' do
|
||||
let(:finder) { ComposerMessagesFinder.new(user, composerAction: 'reply') }
|
||||
|
||||
it "has no message when `posted_too_much_in_topic?` is false" do
|
||||
user.expects(:posted_too_much_in_topic?).returns(false)
|
||||
finder.check_new_user_many_replies.should be_blank
|
||||
end
|
||||
|
||||
it "has a message when a user has posted too much" do
|
||||
user.expects(:posted_too_much_in_topic?).returns(true)
|
||||
finder.check_new_user_many_replies.should be_present
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
|
60
spec/components/validators/password_validator_spec.rb
Normal file
60
spec/components/validators/password_validator_spec.rb
Normal file
|
@ -0,0 +1,60 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe PasswordValidator do
|
||||
|
||||
let(:validator) { described_class.new({attributes: :password}) }
|
||||
subject(:validate) { validator.validate_each(record,:password,@password) }
|
||||
|
||||
context "password required" do
|
||||
let(:record) { u = Fabricate.build(:user, password: @password); u.password_required!; u }
|
||||
|
||||
context "min password length is 8" do
|
||||
before { SiteSetting.stubs(:min_password_length).returns(8) }
|
||||
|
||||
it "doesn't add an error when password is good" do
|
||||
@password = "weron235alsfn234"
|
||||
validate
|
||||
record.errors[:password].should_not be_present
|
||||
end
|
||||
|
||||
it "adds an error when password is too short" do
|
||||
@password = "p"
|
||||
validate
|
||||
record.errors[:password].should be_present
|
||||
end
|
||||
|
||||
it "adds an error when password is blank" do
|
||||
@password = ''
|
||||
validate
|
||||
record.errors[:password].should be_present
|
||||
end
|
||||
|
||||
it "adds an error when password is nil" do
|
||||
@password = nil
|
||||
validate
|
||||
record.errors[:password].should be_present
|
||||
end
|
||||
end
|
||||
|
||||
context "min password length is 12" do
|
||||
before { SiteSetting.stubs(:min_password_length).returns(12) }
|
||||
|
||||
it "adds an error when password length is 11" do
|
||||
@password = "gt38sdt92bv"
|
||||
validate
|
||||
record.errors[:password].should be_present
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "password not required" do
|
||||
let(:record) { Fabricate.build(:user, password: @password) }
|
||||
|
||||
it "doesn't add an error if password is not required" do
|
||||
@password = nil
|
||||
validate
|
||||
record.errors[:password].should_not be_present
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -24,6 +24,20 @@ describe Validators::PostValidator do
|
|||
end
|
||||
end
|
||||
|
||||
context "too_many_posts" do
|
||||
it "should be invalid when the user has posted too much" do
|
||||
post.user.expects(:posted_too_much_in_topic?).returns(true)
|
||||
validator.max_posts_validator(post)
|
||||
expect(post.errors.count).to be > 0
|
||||
end
|
||||
|
||||
it "should be valid when the user hasn't posted too much" do
|
||||
post.user.expects(:posted_too_much_in_topic?).returns(false)
|
||||
validator.max_posts_validator(post)
|
||||
expect(post.errors.count).to be(0)
|
||||
end
|
||||
end
|
||||
|
||||
context "invalid post" do
|
||||
it "should be invalid" do
|
||||
validator.validate(post)
|
||||
|
|
|
@ -87,6 +87,14 @@ describe Topic do
|
|||
Then { scheduled_jobs_for(:close_topic).should have(2).jobs }
|
||||
end
|
||||
end
|
||||
|
||||
context 'a topic that has been auto-closed' do
|
||||
Given(:admin) { Fabricate(:admin) }
|
||||
Given!(:auto_closed_topic) { Fabricate(:topic, user: admin, closed: true, auto_close_at: 1.day.ago, auto_close_user_id: admin.id, auto_close_started_at: 6.days.ago) }
|
||||
When { auto_closed_topic.update_status('closed', false, admin) }
|
||||
Then { auto_closed_topic.reload.auto_close_at.should be_nil }
|
||||
And { auto_closed_topic.auto_close_started_at.should be_nil }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
/*jshint maxlen:10000000 */
|
||||
Discourse.SiteSettingsOriginal = {"title":"Discourse Meta","logo_url":"/assets/logo.png","logo_small_url":"/assets/logo-single.png","traditional_markdown_linebreaks":false,"top_menu":"latest|new|unread|read|favorited|categories","post_menu":"like|edit|flag|delete|share|bookmark|reply","share_links":"twitter|facebook|google+|email","track_external_right_clicks":false,"must_approve_users":false,"ga_tracking_code":"UA-33736483-2","ga_domain_name":"","enable_long_polling":true,"polling_interval":3000,"anon_polling_interval":30000,"min_post_length":20,"max_post_length":16000,"min_topic_title_length":15,"max_topic_title_length":255,"min_private_message_title_length":2,"allow_uncategorized_topics":true,"min_search_term_length":3,"flush_timings_secs":5,"suppress_reply_directly_below":true,"email_domains_blacklist":"mailinator.com","email_domains_whitelist":null,"version_checks":true,"min_title_similar_length":10,"min_body_similar_length":15,"category_colors":"BF1E2E|F1592A|F7941D|9EB83B|3AB54A|12A89D|25AAE2|0E76BD|652D90|92278F|ED207B|8C6238|231F20|808281|B3B5B4|283890","max_upload_size_kb":1024,"category_featured_topics":6,"favicon_url":"/assets/favicon.ico","dynamic_favicon":false,"uncategorized_name":"uncategorized","uncategorized_color":"AB9364","uncategorized_text_color":"FFFFFF","invite_only":false,"login_required":false,"enable_local_logins":true,"enable_local_account_create":true,"enable_google_logins":true,"enable_yahoo_logins":true,"enable_twitter_logins":true,"enable_facebook_logins":true,"enable_cas_logins":false,"enable_github_logins":true,"enable_persona_logins":true,"educate_until_posts":2,"topic_views_heat_low":1000,"topic_views_heat_medium":2000,"topic_views_heat_high":5000,"min_private_message_post_length":5,"faq_url":"","tos_url":"","privacy_policy_url":"","authorized_extensions":".jpg|.jpeg|.png|.gif|.txt","relative_date_duration":14,"delete_removed_posts_after":24,"delete_user_max_age":7, "default_code_lang": "lang-auto"};
|
||||
Discourse.SiteSettingsOriginal = {"title":"Discourse Meta","logo_url":"/assets/logo.png","logo_small_url":"/assets/logo-single.png","traditional_markdown_linebreaks":false,"top_menu":"latest|new|unread|read|favorited|categories","post_menu":"like|edit|flag|delete|share|bookmark|reply","share_links":"twitter|facebook|google+|email","track_external_right_clicks":false,"must_approve_users":false,"ga_tracking_code":"UA-33736483-2","ga_domain_name":"","enable_long_polling":true,"polling_interval":3000,"anon_polling_interval":30000,"min_post_length":20,"max_post_length":16000,"min_topic_title_length":15,"max_topic_title_length":255,"min_private_message_title_length":2,"allow_uncategorized_topics":true,"min_search_term_length":3,"flush_timings_secs":5,"suppress_reply_directly_below":true,"email_domains_blacklist":"mailinator.com","email_domains_whitelist":null,"version_checks":true,"min_title_similar_length":10,"min_body_similar_length":15,"category_colors":"BF1E2E|F1592A|F7941D|9EB83B|3AB54A|12A89D|25AAE2|0E76BD|652D90|92278F|ED207B|8C6238|231F20|808281|B3B5B4|283890","max_upload_size_kb":1024,"category_featured_topics":6,"favicon_url":"/assets/favicon.ico","dynamic_favicon":false,"uncategorized_name":"uncategorized","uncategorized_color":"AB9364","uncategorized_text_color":"FFFFFF","invite_only":false,"login_required":false,"min_password_length":8,"enable_local_logins":true,"enable_local_account_create":true,"enable_google_logins":true,"enable_yahoo_logins":true,"enable_twitter_logins":true,"enable_facebook_logins":true,"enable_cas_logins":false,"enable_github_logins":true,"enable_persona_logins":true,"educate_until_posts":2,"topic_views_heat_low":1000,"topic_views_heat_medium":2000,"topic_views_heat_high":5000,"min_private_message_post_length":5,"faq_url":"","tos_url":"","privacy_policy_url":"","authorized_extensions":".jpg|.jpeg|.png|.gif|.txt","relative_date_duration":14,"delete_removed_posts_after":24,"delete_user_max_age":7, "default_code_lang": "lang-auto"};
|
||||
Discourse.SiteSettings = jQuery.extend(true, {}, Discourse.SiteSettingsOriginal);
|
||||
|
|
Loading…
Reference in a new issue