diff --git a/Gemfile_rails4.lock b/Gemfile_rails4.lock index 32c1f959485..1b382ef181b 100644 --- a/Gemfile_rails4.lock +++ b/Gemfile_rails4.lock @@ -1,6 +1,6 @@ GIT remote: https://github.com/dysania/onebox.git - revision: a02540f9b1460277e7132fbbd71a7d1dbeb486c2 + revision: 3753be3252dbb811f2ce94b56b55b5de54475a6c specs: onebox (1.1.0) hexpress (~> 1.2) diff --git a/app/assets/javascripts/admin/controllers/admin_suspend_user_controller.js b/app/assets/javascripts/admin/controllers/admin_suspend_user_controller.js index 0f4c1625ffd..14d9c862a98 100644 --- a/app/assets/javascripts/admin/controllers/admin_suspend_user_controller.js +++ b/app/assets/javascripts/admin/controllers/admin_suspend_user_controller.js @@ -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; diff --git a/app/assets/javascripts/admin/templates/modal/admin_suspend_user.js.handlebars b/app/assets/javascripts/admin/templates/modal/admin_suspend_user.js.handlebars index 3bd276926ca..34a414cd072 100644 --- a/app/assets/javascripts/admin/templates/modal/admin_suspend_user.js.handlebars +++ b/app/assets/javascripts/admin/templates/modal/admin_suspend_user.js.handlebars @@ -10,6 +10,6 @@ diff --git a/app/assets/javascripts/defer/html-sanitizer-bundle.js b/app/assets/javascripts/defer/html-sanitizer-bundle.js index f8bcc95e87a..90f9536e519 100644 --- a/app/assets/javascripts/defer/html-sanitizer-bundle.js +++ b/app/assets/javascripts/defer/html-sanitizer-bundle.js @@ -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', diff --git a/app/assets/javascripts/discourse/components/keyboard_shortcuts_component.js b/app/assets/javascripts/discourse/components/keyboard_shortcuts_component.js index eea7f0ceac3..4be9ac9a9da 100644 --- a/app/assets/javascripts/discourse/components/keyboard_shortcuts_component.js +++ b/app/assets/javascripts/discourse/components/keyboard_shortcuts_component.js @@ -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) { diff --git a/app/assets/javascripts/discourse/controllers/composer_controller.js b/app/assets/javascripts/discourse/controllers/composer_controller.js index 2ecbe46b255..1f32fb28a5b 100644 --- a/app/assets/javascripts/discourse/controllers/composer_controller.js +++ b/app/assets/javascripts/discourse/controllers/composer_controller.js @@ -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(); } }); }, diff --git a/app/assets/javascripts/discourse/controllers/search_controller.js b/app/assets/javascripts/discourse/controllers/search_controller.js index c6db93b9ed1..ccb8c4c8e59 100644 --- a/app/assets/javascripts/discourse/controllers/search_controller.js +++ b/app/assets/javascripts/discourse/controllers/search_controller.js @@ -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), diff --git a/app/assets/javascripts/discourse/models/category.js b/app/assets/javascripts/discourse/models/category.js index ca03fdc61b3..2df38bcdb5d 100644 --- a/app/assets/javascripts/discourse/models/category.js +++ b/app/assets/javascripts/discourse/models/category.js @@ -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) { diff --git a/app/assets/javascripts/discourse/routes/discovery_route.js b/app/assets/javascripts/discourse/routes/discovery_route.js index d83f0378e18..9ed230f969e 100644 --- a/app/assets/javascripts/discourse/routes/discovery_route.js +++ b/app/assets/javascripts/discourse/routes/discovery_route.js @@ -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') }); diff --git a/app/assets/javascripts/discourse/routes/discovery_route_builders.js b/app/assets/javascripts/discourse/routes/discovery_route_builders.js index c9f75994d23..0ec08b890b4 100644 --- a/app/assets/javascripts/discourse/routes/discovery_route_builders.js +++ b/app/assets/javascripts/discourse/routes/discovery_route_builders.js @@ -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')); }, diff --git a/app/assets/javascripts/discourse/templates/discovery/topics.js.handlebars b/app/assets/javascripts/discourse/templates/discovery/topics.js.handlebars index 87b31ce47ad..9ab7faf4aa4 100644 --- a/app/assets/javascripts/discourse/templates/discovery/topics.js.handlebars +++ b/app/assets/javascripts/discourse/templates/discovery/topics.js.handlebars @@ -9,11 +9,13 @@ + {{#if currentUser}} + {{/if}} {{#sortable-heading sortBy="default" sortOrder=sortOrder}} {{i18n topic.title}} {{/sortable-heading}} diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb index 58039f27aa9..69172b1c514 100644 --- a/app/controllers/posts_controller.rb +++ b/app/controllers/posts_controller.rb @@ -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 diff --git a/app/jobs/regular/user_email.rb b/app/jobs/regular/user_email.rb index 69538e3a3e4..ab7bc548e19 100644 --- a/app/jobs/regular/user_email.rb +++ b/app/jobs/regular/user_email.rb @@ -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 diff --git a/app/mailers/user_notifications.rb b/app/mailers/user_notifications.rb index 895c80318a3..f2d5cd661bc 100644 --- a/app/mailers/user_notifications.rb +++ b/app/mailers/user_notifications.rb @@ -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 = "" diff --git a/app/models/post_alert_observer.rb b/app/models/post_alert_observer.rb index 290fcd8f3b1..0b6b4beff86 100644 --- a/app/models/post_alert_observer.rb +++ b/app/models/post_alert_observer.rb @@ -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 diff --git a/config/unicorn_launcher b/config/unicorn_launcher index e0c277c9c77..60e20cf898b 100755 --- a/config/unicorn_launcher +++ b/config/unicorn_launcher @@ -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 diff --git a/db/fixtures/999_topics.rb b/db/fixtures/999_topics.rb index 472b596cef3..c244e8a888c 100644 --- a/db/fixtures/999_topics.rb +++ b/db/fixtures/999_topics.rb @@ -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 diff --git a/docs/WELCOME-TO-DISCOURSE.md b/docs/WELCOME-TO-DISCOURSE.md new file mode 100644 index 00000000000..9b4edcb6b72 --- /dev/null +++ b/docs/WELCOME-TO-DISCOURSE.md @@ -0,0 +1 @@ +This is a placeholder for the welcome topic, to be filled by @codinghorror diff --git a/lib/guardian.rb b/lib/guardian.rb index a22340fc93c..d6709b40cf0 100644 --- a/lib/guardian.rb +++ b/lib/guardian.rb @@ -8,7 +8,7 @@ class Guardian include CategoryGuardian include PostGuardain include TopicGuardian - + class AnonymousUser def blank?; true; end def admin?; false; end diff --git a/lib/guardian/post_guardian.rb b/lib/guardian/post_guardian.rb index 9c9f660209b..7c1d041627d 100644 --- a/lib/guardian/post_guardian.rb +++ b/lib/guardian/post_guardian.rb @@ -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={}) diff --git a/lib/pretty_text.rb b/lib/pretty_text.rb index e430931f5a2..ff36fd5c167 100644 --- a/lib/pretty_text.rb +++ b/lib/pretty_text.rb @@ -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 diff --git a/lib/suggested_topics_builder.rb b/lib/suggested_topics_builder.rb index 9aec8a1daa6..bd4c3e54c6a 100644 --- a/lib/suggested_topics_builder.rb +++ b/lib/suggested_topics_builder.rb @@ -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) } diff --git a/lib/topic_query.rb b/lib/topic_query.rb index a1faa1cb50f..cee6faf2bdd 100644 --- a/lib/topic_query.rb +++ b/lib/topic_query.rb @@ -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? diff --git a/spec/components/suggested_topics_builder_spec.rb b/spec/components/suggested_topics_builder_spec.rb index dc2f17bfa9c..a10e3eddd76 100644 --- a/spec/components/suggested_topics_builder_spec.rb +++ b/spec/components/suggested_topics_builder_spec.rb @@ -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 diff --git a/spec/components/topic_query_spec.rb b/spec/components/topic_query_spec.rb index 76f5f82a11a..a530aa87baf 100644 --- a/spec/components/topic_query_spec.rb +++ b/spec/components/topic_query_spec.rb @@ -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 diff --git a/spec/controllers/posts_controller_spec.rb b/spec/controllers/posts_controller_spec.rb index 745f51b32ad..1780423346c 100644 --- a/spec/controllers/posts_controller_spec.rb +++ b/spec/controllers/posts_controller_spec.rb @@ -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 diff --git a/spec/fabricators/post_revision_fabricator.rb b/spec/fabricators/post_revision_fabricator.rb new file mode 100644 index 00000000000..5a977b23829 --- /dev/null +++ b/spec/fabricators/post_revision_fabricator.rb @@ -0,0 +1,8 @@ +Fabricator(:post_revision) do + post + user + number 3 + modifications do + { "cooked" => ["

BEFORE

", "

AFTER

"], "raw" => ["BEFORE", "AFTER"] } + end +end diff --git a/spec/jobs/user_email_spec.rb b/spec/jobs/user_email_spec.rb index 7dc726d136f..ce5d9ec7645 100644 --- a/spec/jobs/user_email_spec.rb +++ b/spec/jobs/user_email_spec.rb @@ -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 diff --git a/spec/mailers/user_notifications_spec.rb b/spec/mailers/user_notifications_spec.rb index 836ffd675d4..57462825239 100644 --- a/spec/mailers/user_notifications_spec.rb +++ b/spec/mailers/user_notifications_spec.rb @@ -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 diff --git a/spec/models/notification_spec.rb b/spec/models/notification_spec.rb index 0c5cf4d0d3a..c206206dfbd 100644 --- a/spec/models/notification_spec.rb +++ b/spec/models/notification_spec.rb @@ -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 diff --git a/spec/models/post_revision_spec.rb b/spec/models/post_revision_spec.rb new file mode 100644 index 00000000000..25c8e47bcee --- /dev/null +++ b/spec/models/post_revision_spec.rb @@ -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 diff --git a/spec/models/topic_revision_spec.rb b/spec/models/topic_revision_spec.rb new file mode 100644 index 00000000000..f1717ea9ee2 --- /dev/null +++ b/spec/models/topic_revision_spec.rb @@ -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 diff --git a/test/javascripts/lib/markdown_test.js b/test/javascripts/lib/markdown_test.js index a36f0f37547..a318ccd7bee 100644 --- a/test/javascripts/lib/markdown_test.js +++ b/test/javascripts/lib/markdown_test.js @@ -349,6 +349,8 @@ test("sanitize", function() { cooked("", "", "it allows iframe to google maps"); + equal(sanitize(""), "hullo"); + equal(sanitize(""), "press me!"); }); test("URLs in BBCode tags", function() { diff --git a/test/javascripts/models/category_test.js b/test/javascripts/models/category_test.js index 04830483874..19a5dd313e7 100644 --- a/test/javascripts/models/category_test.js +++ b/test/javascripts/models/category_test.js @@ -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}), diff --git a/vendor/assets/javascripts/browser-update.js.erb b/vendor/assets/javascripts/browser-update.js.erb index 76dd581cd76..6a27df4c949 100644 --- a/vendor/assets/javascripts/browser-update.js.erb +++ b/vendor/assets/javascripts/browser-update.js.erb @@ -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
{{#if canBulkSelect}} {{/if}}