mirror of
https://github.com/discourse/discourse.git
synced 2024-11-27 23:03:38 +08:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
3ae7c14b85
|
@ -1,6 +1,6 @@
|
|||
GIT
|
||||
remote: https://github.com/dysania/onebox.git
|
||||
revision: a02540f9b1460277e7132fbbd71a7d1dbeb486c2
|
||||
revision: 3753be3252dbb811f2ce94b56b55b5de54475a6c
|
||||
specs:
|
||||
onebox (1.1.0)
|
||||
hexpress (~> 1.2)
|
||||
|
|
|
@ -9,8 +9,13 @@
|
|||
**/
|
||||
Discourse.AdminSuspendUserController = Discourse.ObjectController.extend(Discourse.ModalFunctionality, {
|
||||
|
||||
submitDisabled: function() {
|
||||
return (!this.get('reason') || this.get('reason').length < 1);
|
||||
}.property('reason'),
|
||||
|
||||
actions: {
|
||||
suspend: function() {
|
||||
if (this.get('submitDisabled')) return;
|
||||
var duration = parseInt(this.get('duration'), 10);
|
||||
if (duration > 0) {
|
||||
var self = this;
|
||||
|
|
|
@ -10,6 +10,6 @@
|
|||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class='btn btn-danger' {{action suspend}}><i class='fa fa-ban'></i>{{i18n admin.user.suspend}}</button>
|
||||
<button class='btn btn-danger' {{action suspend}} {{bind-attr disabled="submitDisabled"}}><i class='fa fa-ban'></i>{{i18n admin.user.suspend}}</button>
|
||||
<a {{action closeModal}}>{{i18n cancel}}</a>
|
||||
</div>
|
||||
|
|
|
@ -831,14 +831,6 @@ html4.ATTRIBS = {
|
|||
'bdo::dir': 0,
|
||||
'blockquote::cite': 1,
|
||||
'br::clear': 0,
|
||||
'button::accesskey': 0,
|
||||
'button::disabled': 0,
|
||||
'button::name': 8,
|
||||
'button::onblur': 2,
|
||||
'button::onfocus': 2,
|
||||
'button::tabindex': 0,
|
||||
'button::type': 0,
|
||||
'button::value': 0,
|
||||
'canvas::height': 0,
|
||||
'canvas::width': 0,
|
||||
'caption::align': 0,
|
||||
|
@ -987,19 +979,6 @@ html4.ATTRIBS = {
|
|||
'select::size': 0,
|
||||
'select::tabindex': 0,
|
||||
'source::type': 0,
|
||||
'textarea::accesskey': 0,
|
||||
'textarea::autocomplete': 0,
|
||||
'textarea::disabled': 0,
|
||||
'textarea::inputmode': 0,
|
||||
'textarea::name': 8,
|
||||
'textarea::onblur': 2,
|
||||
'textarea::onchange': 2,
|
||||
'textarea::onfocus': 2,
|
||||
'textarea::onselect': 2,
|
||||
'textarea::placeholder': 0,
|
||||
'textarea::readonly': 0,
|
||||
'textarea::tabindex': 0,
|
||||
'textarea::wrap': 0,
|
||||
'track::default': 0,
|
||||
'track::kind': 0,
|
||||
'track::label': 0,
|
||||
|
@ -1048,7 +1027,6 @@ html4.ELEMENTS = {
|
|||
'blockquote': 0,
|
||||
'body': 305,
|
||||
'br': 2,
|
||||
'button': 0,
|
||||
'canvas': 0,
|
||||
'caption': 0,
|
||||
'cite': 0,
|
||||
|
@ -1136,7 +1114,6 @@ html4.ELEMENTS = {
|
|||
'table': 272,
|
||||
'tbody': 273,
|
||||
'td': 273,
|
||||
'textarea': 8,
|
||||
'tfoot': 1,
|
||||
'th': 273,
|
||||
'thead': 273,
|
||||
|
@ -1171,7 +1148,6 @@ html4.ELEMENT_DOM_INTERFACES = {
|
|||
'blockquote': 'HTMLQuoteElement',
|
||||
'body': 'HTMLBodyElement',
|
||||
'br': 'HTMLBRElement',
|
||||
'button': 'HTMLButtonElement',
|
||||
'canvas': 'HTMLCanvasElement',
|
||||
'caption': 'HTMLTableCaptionElement',
|
||||
'cite': 'HTMLElement',
|
||||
|
@ -1259,7 +1235,6 @@ html4.ELEMENT_DOM_INTERFACES = {
|
|||
'table': 'HTMLTableElement',
|
||||
'tbody': 'HTMLTableSectionElement',
|
||||
'td': 'HTMLTableDataCellElement',
|
||||
'textarea': 'HTMLTextAreaElement',
|
||||
'tfoot': 'HTMLTableSectionElement',
|
||||
'th': 'HTMLTableHeaderCellElement',
|
||||
'thead': 'HTMLTableSectionElement',
|
||||
|
|
|
@ -41,6 +41,8 @@ Discourse.KeyboardShortcuts = Ember.Object.createWithMixins({
|
|||
},
|
||||
|
||||
FUNCTION_BINDINGS: {
|
||||
'home': 'goToFirstPost',
|
||||
'end': 'goToLastPost',
|
||||
'j': 'selectDown',
|
||||
'k': 'selectUp',
|
||||
'u': 'goBack',
|
||||
|
@ -56,6 +58,14 @@ Discourse.KeyboardShortcuts = Ember.Object.createWithMixins({
|
|||
_.each(this.FUNCTION_BINDINGS, this._bindToFunction, this);
|
||||
},
|
||||
|
||||
goToFirstPost: function() {
|
||||
Discourse.__container__.lookup('controller:topic').send('jumpTop');
|
||||
},
|
||||
|
||||
goToLastPost: function() {
|
||||
Discourse.__container__.lookup('controller:topic').send('jumpBottom');
|
||||
},
|
||||
|
||||
selectDown: function() {
|
||||
this._moveSelection(1);
|
||||
},
|
||||
|
@ -77,7 +87,7 @@ Discourse.KeyboardShortcuts = Ember.Object.createWithMixins({
|
|||
},
|
||||
|
||||
showHelpModal: function() {
|
||||
Discourse.__container__.lookup('controller:application').send("showKeyboardShortcutsHelp");
|
||||
Discourse.__container__.lookup('controller:application').send('showKeyboardShortcutsHelp');
|
||||
},
|
||||
|
||||
_bindToPath: function(path, binding) {
|
||||
|
|
|
@ -333,14 +333,14 @@ Discourse.ComposerController = Discourse.Controller.extend({
|
|||
cancelComposer: function() {
|
||||
var self = this;
|
||||
|
||||
return Ember.Deferred.promise(function (promise) {
|
||||
return new Ember.RSVP.Promise(function (resolve) {
|
||||
if (self.get('model.hasMetaData') || self.get('model.replyDirty')) {
|
||||
bootbox.confirm(I18n.t("post.abandon"), I18n.t("no_value"), I18n.t("yes_value"), function(result) {
|
||||
if (result) {
|
||||
self.destroyDraft();
|
||||
self.get('model').clearState();
|
||||
self.close();
|
||||
promise.resolve();
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
@ -348,7 +348,7 @@ Discourse.ComposerController = Discourse.Controller.extend({
|
|||
self.destroyDraft();
|
||||
self.get('model').clearState();
|
||||
self.close();
|
||||
promise.resolve();
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
|
|
@ -11,8 +11,8 @@ Discourse.SearchController = Em.ArrayController.extend(Discourse.Presence, {
|
|||
// If we need to perform another search
|
||||
newSearchNeeded: function() {
|
||||
this.set('noResults', false);
|
||||
var term = this.get('term');
|
||||
if (term && term.length >= Discourse.SiteSettings.min_search_term_length) {
|
||||
var term = (this.get('term') || '').trim();
|
||||
if (term.length >= Discourse.SiteSettings.min_search_term_length) {
|
||||
this.set('loading', true);
|
||||
this.searchTerm(term, this.get('typeFilter'));
|
||||
} else {
|
||||
|
@ -57,6 +57,8 @@ Discourse.SearchController = Em.ArrayController.extend(Discourse.Presence, {
|
|||
self.set('urls', urls);
|
||||
}
|
||||
|
||||
self.set('loading', false);
|
||||
}).catch(function() {
|
||||
self.set('loading', false);
|
||||
});
|
||||
}, 300),
|
||||
|
|
|
@ -209,9 +209,14 @@ Discourse.Category.reopenClass({
|
|||
},
|
||||
|
||||
findByIds: function(ids){
|
||||
return ids.map(function(id){
|
||||
return Discourse.Category.findById(id);
|
||||
var categories = [];
|
||||
_.each(ids, function(id){
|
||||
var found = Discourse.Category.findById(id);
|
||||
if(found){
|
||||
categories.push(found);
|
||||
}
|
||||
});
|
||||
return categories;
|
||||
},
|
||||
|
||||
findBySlug: function(slug, parentSlug) {
|
||||
|
|
|
@ -31,7 +31,6 @@ Discourse.DiscoveryRoute = Discourse.Route.extend({
|
|||
this.controllerFor('composer').open({
|
||||
categoryId: topicsController.get('category.id'),
|
||||
action: Discourse.Composer.CREATE_TOPIC,
|
||||
draft: topicsController.get('draft'),
|
||||
draftKey: topicsController.get('draft_key'),
|
||||
draftSequence: topicsController.get('draft_sequence')
|
||||
});
|
||||
|
|
|
@ -28,6 +28,17 @@ function buildTopicRoute(filter) {
|
|||
Discourse.set('title', I18n.t('filters.with_topics', {filter: filterText}));
|
||||
|
||||
this.controllerFor('discoveryTopics').setProperties({ model: model, category: null, period: period });
|
||||
|
||||
// If there's a draft, open the create topic composer
|
||||
if (model.draft) {
|
||||
this.controllerFor('composer').open({
|
||||
action: Discourse.Composer.CREATE_TOPIC,
|
||||
draft: model.draft,
|
||||
draftKey: model.draft_key,
|
||||
draftSequence: model.draft_sequence
|
||||
});
|
||||
}
|
||||
|
||||
this.controllerFor('navigationDefault').set('canCreateTopic', model.get('can_create_topic'));
|
||||
},
|
||||
|
||||
|
|
|
@ -9,11 +9,13 @@
|
|||
<table id='topic-list'>
|
||||
<thead>
|
||||
<tr>
|
||||
{{#if currentUser}}
|
||||
<th>
|
||||
{{#if canBulkSelect}}
|
||||
<button class='btn bulk-select' {{action toggleBulkSelect}} title="{{i18n topics.bulk.toggle}}"><i class='fa fa-list'></i></button>
|
||||
{{/if}}
|
||||
</th>
|
||||
{{/if}}
|
||||
{{#sortable-heading sortBy="default" sortOrder=sortOrder}}
|
||||
{{i18n topic.title}}
|
||||
{{/sortable-heading}}
|
||||
|
|
|
@ -216,6 +216,8 @@ class PostsController < ApplicationController
|
|||
raise Discourse::InvalidParameters.new(:revision) if revision < 2
|
||||
|
||||
post_revision = PostRevision.where(post_id: post_id, number: revision).first
|
||||
post_revision.post = find_post_from_params
|
||||
|
||||
guardian.ensure_can_see!(post_revision)
|
||||
post_revision
|
||||
end
|
||||
|
|
|
@ -41,6 +41,7 @@ module Jobs
|
|||
return if seen_recently && !user.suspended?
|
||||
|
||||
# Load the post if present
|
||||
email_args[:post] ||= Post.where(id: notification.data_hash[:original_post_id].to_i).first
|
||||
email_args[:post] ||= notification.post
|
||||
email_args[:notification] = notification
|
||||
|
||||
|
|
|
@ -127,7 +127,7 @@ class UserNotifications < ActionMailer::Base
|
|||
return unless @notification = opts[:notification]
|
||||
return unless @post = opts[:post]
|
||||
|
||||
username = @notification.data_hash[:display_username]
|
||||
username = @notification.data_hash[:original_username]
|
||||
notification_type = opts[:notification_type] || Notification.types[@notification.notification_type].to_s
|
||||
|
||||
context = ""
|
||||
|
|
|
@ -140,6 +140,7 @@ class PostAlertObserver < ActiveRecord::Observer
|
|||
end
|
||||
|
||||
original_post = post
|
||||
original_username = opts[:display_username] || post.username
|
||||
|
||||
if collapsed
|
||||
post = first_unread_post(user,post.topic) || post
|
||||
|
@ -155,6 +156,8 @@ class PostAlertObserver < ActiveRecord::Observer
|
|||
post_number: post.post_number,
|
||||
post_action_id: opts[:post_action_id],
|
||||
data: { topic_title: post.topic.title,
|
||||
original_post_id: original_post.id,
|
||||
original_username: original_username,
|
||||
display_username: opts[:display_username] || post.user.username }.to_json)
|
||||
end
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ UNICORN_PID=$!
|
|||
|
||||
echo "supervisor pid: $UNICORN_SUPERVISOR_PID unicorn pid: $UNICORN_PID"
|
||||
|
||||
while [ -e /proc/$UNICORN_PID ]
|
||||
while kill -0 $UNICORN_PID
|
||||
do
|
||||
sleep 0.1
|
||||
sleep 1
|
||||
done
|
||||
|
|
|
@ -3,8 +3,12 @@ Topic.reset_column_information
|
|||
Post.reset_column_information
|
||||
|
||||
if Topic.where('id NOT IN (SELECT topic_id from categories where topic_id is not null)').count == 0 && !Rails.env.test?
|
||||
# seed welcome topic
|
||||
puts "Seeding welcome topic"
|
||||
puts "Seeding welcome topics"
|
||||
|
||||
welcome = File.read(Rails.root + 'docs/ADMIN-QUICK-START-GUIDE.md')
|
||||
PostCreator.create(Discourse.system_user, raw: welcome, title: "Discourse Admin Quick Start Guide" ,skip_validations: true)
|
||||
|
||||
welcome = File.read(Rails.root + 'docs/WELCOME-TO-DISCOURSE.md')
|
||||
post = PostCreator.create(Discourse.system_user, category: 'Meta', raw: welcome, title: "Welcome to Discourse", skip_validations: true)
|
||||
post.topic.update_pinned(true)
|
||||
end
|
||||
|
|
1
docs/WELCOME-TO-DISCOURSE.md
Normal file
1
docs/WELCOME-TO-DISCOURSE.md
Normal file
|
@ -0,0 +1 @@
|
|||
This is a placeholder for the welcome topic, to be filled by @codinghorror
|
|
@ -8,7 +8,7 @@ class Guardian
|
|||
include CategoryGuardian
|
||||
include PostGuardain
|
||||
include TopicGuardian
|
||||
|
||||
|
||||
class AnonymousUser
|
||||
def blank?; true; end
|
||||
def admin?; false; end
|
||||
|
|
|
@ -104,7 +104,9 @@ module PostGuardain
|
|||
end
|
||||
|
||||
def can_see_post_revision?(post_revision)
|
||||
post_revision.present? && (is_staff? || can_see_post?(post_revision.post))
|
||||
return false if post_revision.nil?
|
||||
return true if SiteSetting.edit_history_visible_to_public
|
||||
authenticated? && (is_staff? || can_see_post?(post_revision.post))
|
||||
end
|
||||
|
||||
def can_vote?(post, opts={})
|
||||
|
|
|
@ -236,12 +236,12 @@ module PrettyText
|
|||
def self.make_all_links_absolute(html)
|
||||
site_uri = nil
|
||||
doc = Nokogiri::HTML.fragment(html)
|
||||
doc.css("a").each do |l|
|
||||
href = l["href"].to_s
|
||||
doc.css("a").each do |link|
|
||||
href = link["href"].to_s
|
||||
begin
|
||||
uri = URI(href)
|
||||
site_uri ||= URI(Discourse.base_url)
|
||||
l["href"] = "#{site_uri}#{l['href']}" unless uri.host.present?
|
||||
link["href"] = "#{site_uri}#{link['href']}" unless uri.host.present?
|
||||
rescue URI::InvalidURIError
|
||||
# leave it
|
||||
end
|
||||
|
|
|
@ -19,7 +19,7 @@ class SuggestedTopicsBuilder
|
|||
|
||||
# Only add results if we don't have those topic ids already
|
||||
results = results.where('topics.id NOT IN (?)', @excluded_topic_ids)
|
||||
.where(closed: false, archived: false, visible: true)
|
||||
.where(visible: true)
|
||||
.to_a
|
||||
.reject { |topic| @category_topic_ids.include?(topic.id) }
|
||||
|
||||
|
|
|
@ -121,8 +121,7 @@ class TopicQuery
|
|||
end
|
||||
|
||||
def list_category(category)
|
||||
create_list(:category, unordered: true) do |list|
|
||||
list = list.where(category_id: category.id)
|
||||
create_list(:category, unordered: true, category: category.id) do |list|
|
||||
if @user
|
||||
list.order(TopicQuerySQL.order_with_pinned_sql)
|
||||
else
|
||||
|
@ -132,10 +131,8 @@ class TopicQuery
|
|||
end
|
||||
|
||||
def list_new_in_category(category)
|
||||
create_list(:new_in_category, unordered: true) do |list|
|
||||
list.where(category_id: category.id)
|
||||
.by_newest
|
||||
.first(25)
|
||||
create_list(:new_in_category, unordered: true, category: category.id) do |list|
|
||||
list.by_newest.first(25)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -242,6 +239,11 @@ class TopicQuery
|
|||
result = result.listable_topics.includes(category: :topic_only_relative_url)
|
||||
result = result.where('categories.name is null or categories.name <> ?', options[:exclude_category]).references(:categories) if options[:exclude_category]
|
||||
|
||||
# Don't include the category topic unless restricted to that category
|
||||
if options[:category].blank?
|
||||
result = result.where('COALESCE(categories.topic_id, 0) <> topics.id')
|
||||
end
|
||||
|
||||
result = result.limit(options[:per_page]) unless options[:limit] == false
|
||||
result = result.visible if options[:visible] || @user.nil? || @user.regular?
|
||||
result = result.where.not(topics: {id: options[:except_topic_ids]}).references(:topics) if options[:except_topic_ids]
|
||||
|
@ -309,7 +311,7 @@ class TopicQuery
|
|||
end
|
||||
|
||||
def random_suggested(topic, count, excluded_topic_ids=[])
|
||||
result = default_results(unordered: true, per_page: count)
|
||||
result = default_results(unordered: true, per_page: count).where(closed: false, archived: false)
|
||||
excluded_topic_ids += Category.pluck(:topic_id).compact
|
||||
result = result.where("topics.id NOT IN (?)", excluded_topic_ids) unless excluded_topic_ids.empty?
|
||||
|
||||
|
|
|
@ -83,14 +83,14 @@ describe SuggestedTopicsBuilder do
|
|||
|
||||
end
|
||||
|
||||
context "adding invalid status topics" do
|
||||
context "adding topics that are not open" do
|
||||
let!(:archived_topic) { Fabricate(:topic, archived: true)}
|
||||
let!(:closed_topic) { Fabricate(:topic, closed: true)}
|
||||
let!(:invisible_topic) { Fabricate(:topic, visible: false)}
|
||||
|
||||
it "doesn't add archived, closed or invisible topics" do
|
||||
it "adds archived and closed, but not invisible topics" do
|
||||
builder.add_results(Topic)
|
||||
builder.size.should == 0
|
||||
builder.size.should == 2
|
||||
builder.should_not be_full
|
||||
end
|
||||
end
|
||||
|
|
|
@ -28,12 +28,12 @@ describe TopicQuery do
|
|||
Topic.recent(10).count.should == 0
|
||||
|
||||
# mods can see every group and hidden topics
|
||||
TopicQuery.new(moderator).list_latest.topics.count.should == 3
|
||||
TopicQuery.new(moderator).list_latest.topics.count.should == 2
|
||||
|
||||
group.add(user)
|
||||
group.save
|
||||
|
||||
TopicQuery.new(user).list_latest.topics.count.should == 2
|
||||
TopicQuery.new(user).list_latest.topics.count.should == 1
|
||||
|
||||
end
|
||||
|
||||
|
@ -366,7 +366,7 @@ describe TopicQuery do
|
|||
end
|
||||
end
|
||||
|
||||
context "anonymously browswing with invisible, closed and archived" do
|
||||
context "anonymously browsing with invisible, closed and archived" do
|
||||
let!(:topic) { Fabricate(:topic) }
|
||||
let!(:regular_topic) { Fabricate(:post, user: creator).topic }
|
||||
let!(:closed_topic) { Fabricate(:topic, user: creator, closed: true) }
|
||||
|
@ -394,12 +394,20 @@ describe TopicQuery do
|
|||
let!(:closed_topic) { Fabricate(:topic, user: creator, closed: true) }
|
||||
let!(:archived_topic) { Fabricate(:topic, user: creator, archived: true) }
|
||||
let!(:invisible_topic) { Fabricate(:topic, user: creator, visible: false) }
|
||||
let!(:fully_read_closed) { Fabricate(:post, user: creator).topic }
|
||||
let!(:fully_read_archived) { Fabricate(:post, user: creator).topic }
|
||||
|
||||
before do
|
||||
user.auto_track_topics_after_msecs = 0
|
||||
user.save
|
||||
TopicUser.update_last_read(user, partially_read.id, 0, 0)
|
||||
TopicUser.update_last_read(user, fully_read.id, 1, 0)
|
||||
TopicUser.update_last_read(user, fully_read_closed.id, 1, 0)
|
||||
TopicUser.update_last_read(user, fully_read_archived.id, 1, 0)
|
||||
fully_read_closed.closed = true
|
||||
fully_read_closed.save
|
||||
fully_read_archived.archived = true
|
||||
fully_read_archived.save
|
||||
end
|
||||
|
||||
it "won't return new or fully read if there are enough partially read topics" do
|
||||
|
@ -407,14 +415,22 @@ describe TopicQuery do
|
|||
suggested_topics.should == [partially_read.id]
|
||||
end
|
||||
|
||||
it "won't fully read if there are enough partially read topics and new topics" do
|
||||
SiteSetting.stubs(:suggested_topics).returns(2)
|
||||
suggested_topics.should == [partially_read.id, new_topic.id]
|
||||
it "won't return fully read if there are enough partially read topics and new topics" do
|
||||
SiteSetting.stubs(:suggested_topics).returns(4)
|
||||
suggested_topics[0].should == partially_read.id
|
||||
suggested_topics[1,3].should include(new_topic.id)
|
||||
suggested_topics[1,3].should include(closed_topic.id)
|
||||
suggested_topics[1,3].should include(archived_topic.id)
|
||||
end
|
||||
|
||||
it "returns unread, then new, then random" do
|
||||
SiteSetting.stubs(:suggested_topics).returns(3)
|
||||
suggested_topics.should == [partially_read.id, new_topic.id, fully_read.id]
|
||||
SiteSetting.stubs(:suggested_topics).returns(7)
|
||||
suggested_topics[0].should == partially_read.id
|
||||
suggested_topics[1,3].should include(new_topic.id)
|
||||
suggested_topics[1,3].should include(closed_topic.id)
|
||||
suggested_topics[1,3].should include(archived_topic.id)
|
||||
suggested_topics[4].should == fully_read.id
|
||||
# random doesn't include closed and archived
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -2,7 +2,6 @@ require 'spec_helper'
|
|||
|
||||
describe PostsController do
|
||||
|
||||
|
||||
describe 'short_link' do
|
||||
it 'logs the incoming link once' do
|
||||
IncomingLink.expects(:add).once.returns(true)
|
||||
|
@ -386,4 +385,64 @@ describe PostsController do
|
|||
end
|
||||
end
|
||||
|
||||
describe "revisions" do
|
||||
|
||||
let(:post_revision) { Fabricate(:post_revision) }
|
||||
|
||||
it "throws an exception when revision is < 2" do
|
||||
expect {
|
||||
xhr :get, :revisions, post_id: post_revision.post_id, revision: 1
|
||||
}.to raise_error(Discourse::InvalidParameters)
|
||||
end
|
||||
|
||||
context "when edit history is not visible to the public" do
|
||||
|
||||
before { SiteSetting.stubs(:edit_history_visible_to_public).returns(false) }
|
||||
|
||||
it "ensures anonymous can not see the revisions" do
|
||||
xhr :get, :revisions, post_id: post_revision.post_id, revision: post_revision.number
|
||||
response.should be_forbidden
|
||||
end
|
||||
|
||||
it "ensures staff can see the revisions" do
|
||||
log_in(:admin)
|
||||
xhr :get, :revisions, post_id: post_revision.post_id, revision: post_revision.number
|
||||
response.should be_success
|
||||
end
|
||||
|
||||
it "ensures poster can see the revisions" do
|
||||
user = log_in(:active_user)
|
||||
pr = Fabricate(:post_revision, user: user)
|
||||
xhr :get, :revisions, post_id: pr.post_id, revision: pr.number
|
||||
response.should be_success
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context "when edit history is visible to everyone" do
|
||||
|
||||
before { SiteSetting.stubs(:edit_history_visible_to_public).returns(true) }
|
||||
|
||||
it "ensures anyone can see the revisions" do
|
||||
xhr :get, :revisions, post_id: post_revision.post_id, revision: post_revision.number
|
||||
response.should be_success
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context "deleted post" do
|
||||
let(:admin) { log_in(:admin) }
|
||||
let(:deleted_post) { Fabricate(:post, user: admin) }
|
||||
let(:deleted_post_revision) { Fabricate(:post_revision, user: admin, post: deleted_post) }
|
||||
|
||||
before { deleted_post.trash!(admin) }
|
||||
|
||||
it "also work on deleted post" do
|
||||
xhr :get, :revisions, post_id: deleted_post_revision.post_id, revision: deleted_post_revision.number
|
||||
response.should be_success
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
8
spec/fabricators/post_revision_fabricator.rb
Normal file
8
spec/fabricators/post_revision_fabricator.rb
Normal file
|
@ -0,0 +1,8 @@
|
|||
Fabricator(:post_revision) do
|
||||
post
|
||||
user
|
||||
number 3
|
||||
modifications do
|
||||
{ "cooked" => ["<p>BEFORE</p>", "<p>AFTER</p>"], "raw" => ["BEFORE", "AFTER"] }
|
||||
end
|
||||
end
|
|
@ -101,7 +101,16 @@ describe Jobs::UserEmail do
|
|||
|
||||
context 'notification' do
|
||||
let(:post) { Fabricate(:post, user: user) }
|
||||
let!(:notification) { Fabricate(:notification, user: user, topic: post.topic, post_number: post.post_number)}
|
||||
let!(:notification) {
|
||||
Fabricate(:notification,
|
||||
user: user,
|
||||
topic: post.topic,
|
||||
post_number: post.post_number,
|
||||
data: {
|
||||
original_post_id: post.id
|
||||
}.to_json
|
||||
)
|
||||
}
|
||||
|
||||
it 'passes a notification as an argument when a notification_id is present' do
|
||||
Email::Sender.any_instance.expects(:send)
|
||||
|
@ -131,11 +140,17 @@ describe Jobs::UserEmail do
|
|||
before do
|
||||
@pm_from_staff = Fabricate(:post, user: Fabricate(:moderator))
|
||||
@pm_from_staff.topic.topic_allowed_users.create!(user_id: suspended.id)
|
||||
@pm_notification = Fabricate(:notification, user: suspended, topic: @pm_from_staff.topic, post_number: @pm_from_staff.post_number)
|
||||
@pm_notification = Fabricate(:notification,
|
||||
user: suspended,
|
||||
topic: @pm_from_staff.topic,
|
||||
post_number: @pm_from_staff.post_number,
|
||||
data: { original_post_id: @pm_from_staff.id }.to_json
|
||||
)
|
||||
UserNotifications.expects(:user_private_message).with(suspended, notification: @pm_notification, post: @pm_from_staff).returns(mailer)
|
||||
end
|
||||
|
||||
subject(:execute_user_email_job) { Jobs::UserEmail.new.execute(type: :user_private_message, user_id: suspended.id, notification_id: @pm_notification.id) }
|
||||
subject(:execute_user_email_job) {
|
||||
Jobs::UserEmail.new.execute(type: :user_private_message, user_id: suspended.id, notification_id: @pm_notification.id) }
|
||||
|
||||
it "sends an email" do
|
||||
execute_user_email_job
|
||||
|
|
|
@ -123,7 +123,7 @@ describe UserNotifications do
|
|||
topic: post.topic,
|
||||
notification_type: Notification.types[notification_type],
|
||||
post_number: post.post_number,
|
||||
data: {display_username: username}.to_json )
|
||||
data: {original_username: username}.to_json )
|
||||
end
|
||||
|
||||
describe '.user_mentioned' do
|
||||
|
|
|
@ -173,14 +173,14 @@ describe Notification do
|
|||
user_id: user.id,
|
||||
topic_id: 2,
|
||||
post_number: 1,
|
||||
data: '[]',
|
||||
data: '{}',
|
||||
notification_type: Notification.types[:private_message])
|
||||
|
||||
other = Notification.create!(read: false,
|
||||
user_id: user.id,
|
||||
topic_id: 2,
|
||||
post_number: 1,
|
||||
data: '[]',
|
||||
data: '{}',
|
||||
notification_type: Notification.types[:mentioned])
|
||||
|
||||
|
||||
|
@ -197,9 +197,9 @@ describe Notification do
|
|||
user = Fabricate(:user)
|
||||
|
||||
(1..3).map do |i|
|
||||
Notification.create!(read: false, user_id: user.id, topic_id: 2, post_number: i, data: '[]', notification_type: 1)
|
||||
Notification.create!(read: false, user_id: user.id, topic_id: 2, post_number: i, data: '{}', notification_type: 1)
|
||||
end
|
||||
Notification.create!(read: true, user_id: user.id, topic_id: 2, post_number: 4, data: '[]', notification_type: 1)
|
||||
Notification.create!(read: true, user_id: user.id, topic_id: 2, post_number: 4, data: '{}', notification_type: 1)
|
||||
|
||||
Notification.mark_posts_read(user,2,[1,2,3,4]).should == 3
|
||||
end
|
||||
|
|
9
spec/models/post_revision_spec.rb
Normal file
9
spec/models/post_revision_spec.rb
Normal file
|
@ -0,0 +1,9 @@
|
|||
require 'spec_helper'
|
||||
require_dependency 'post_revision'
|
||||
|
||||
describe PostRevision do
|
||||
|
||||
it { should belong_to :user }
|
||||
it { should belong_to :post }
|
||||
|
||||
end
|
9
spec/models/topic_revision_spec.rb
Normal file
9
spec/models/topic_revision_spec.rb
Normal file
|
@ -0,0 +1,9 @@
|
|||
require 'spec_helper'
|
||||
require_dependency 'topic_revision'
|
||||
|
||||
describe TopicRevision do
|
||||
|
||||
it { should belong_to :user }
|
||||
it { should belong_to :topic }
|
||||
|
||||
end
|
|
@ -349,6 +349,8 @@ test("sanitize", function() {
|
|||
cooked("<iframe src=\"https://www.google.com/maps/embed?pb=!1m10!1m8!1m3!1d2624.9983685732213!2d2.29432085!3d48.85824149999999!3m2!1i1024!2i768!4f13.1!5e0!3m2!1sen!2s!4v1385737436368\" width=\"100\" height=\"42\"></iframe>",
|
||||
"<iframe src=\"https://www.google.com/maps/embed?pb=!1m10!1m8!1m3!1d2624.9983685732213!2d2.29432085!3d48.85824149999999!3m2!1i1024!2i768!4f13.1!5e0!3m2!1sen!2s!4v1385737436368\" width=\"100\" height=\"42\"></iframe>",
|
||||
"it allows iframe to google maps");
|
||||
equal(sanitize("<textarea>hullo</textarea>"), "hullo");
|
||||
equal(sanitize("<button>press me!</button>"), "press me!");
|
||||
});
|
||||
|
||||
test("URLs in BBCode tags", function() {
|
||||
|
|
|
@ -39,6 +39,15 @@ test('findBySlug', function() {
|
|||
blank(Discourse.Category.findBySlug('luke', 'leia'), 'luke is blank with an incorrect parent');
|
||||
});
|
||||
|
||||
test('findByIds', function(){
|
||||
var categories = [
|
||||
Discourse.Category.create({id: 1}),
|
||||
Discourse.Category.create({id: 2})];
|
||||
|
||||
this.stub(Discourse.Category, 'list').returns(categories);
|
||||
deepEqual(Discourse.Category.findByIds([1,2,3]), categories);
|
||||
});
|
||||
|
||||
test('postCountStats', function() {
|
||||
var category1 = Discourse.Category.create({id: 1, slug: 'unloved', posts_year: 2, posts_month: 0, posts_week: 0, posts_day: 0}),
|
||||
category2 = Discourse.Category.create({id: 2, slug: 'hasbeen', posts_year: 50, posts_month: 4, posts_week: 0, posts_day: 0}),
|
||||
|
|
|
@ -19,12 +19,12 @@ var $buo = function() {
|
|||
|
||||
// sam: my main concern here is mobile, but its an outlier, for now we support ie9, set conditionally and stuff with pushState
|
||||
if (window.ie === "new" || (window.history && window.history.pushState && !badAndroid)) {
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
// we don't ask Googlebot to update their browser
|
||||
if (ua.indexOf('Googlebot') >= 0) {
|
||||
return;
|
||||
if (ua.indexOf('Googlebot') >= 0 || ua.indexOf('Mediapartners') >= 0 || ua.indexOf('AdsBot') >= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// retrieve localized browser upgrade text
|
||||
|
|
Loading…
Reference in New Issue
Block a user