From 439f393d89a20ab94d6a2ced12fa2a3ddea43a84 Mon Sep 17 00:00:00 2001 From: cpradio Date: Wed, 22 Oct 2014 18:31:19 -0400 Subject: [PATCH 01/83] Show dismiss posts/topics buttons on category filtered lists --- .../discourse/controllers/discovery/topics.js.es6 | 13 +++++++++---- app/assets/javascripts/discourse/models/topic.js | 6 ++++-- app/controllers/topics_controller.rb | 6 +++++- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/discourse/controllers/discovery/topics.js.es6 b/app/assets/javascripts/discourse/controllers/discovery/topics.js.es6 index a25502d663..a58dccc90a 100644 --- a/app/assets/javascripts/discourse/controllers/discovery/topics.js.es6 +++ b/app/assets/javascripts/discourse/controllers/discovery/topics.js.es6 @@ -85,7 +85,8 @@ var controllerOpts = { if (selected.length > 0) { promise = Discourse.Topic.bulkOperation(selected, operation); } else { - promise = Discourse.Topic.bulkOperationByFilter(this.get('filter'), operation); + var category = this.get('category'); + promise = Discourse.Topic.bulkOperationByFilter('unread', operation, category ? category.id : null); } promise.then(function(result) { if (result && result.topic_ids) { @@ -105,8 +106,12 @@ var controllerOpts = { return Discourse.TopicTrackingState.current(); }.property(), + isFilterPage: function(filter, filterType) { + return filter.match(new RegExp(filterType + '$', 'gi')) ? true : false; + }, + showDismissRead: function() { - return this.get('filter') === 'unread' && this.get('topics.length') > 0; + return this.isFilterPage(this.get('filter'), 'unread') && this.get('topics.length') > 0; }.property('filter', 'topics.length'), showResetNew: function() { @@ -114,8 +119,8 @@ var controllerOpts = { }.property('filter', 'topics.length'), showDismissAtTop: function() { - return (this.get('filter') === 'new' || - this.get('filter') === 'unread') && + return (this.isFilterPage(this.get('filter'), 'new') || + this.isFilterPage(this.get('filter'), 'unread')) && this.get('topics.length') >= 30; }.property('filter', 'topics.length'), diff --git a/app/assets/javascripts/discourse/models/topic.js b/app/assets/javascripts/discourse/models/topic.js index 40c0d9eb87..e0ddb17553 100644 --- a/app/assets/javascripts/discourse/models/topic.js +++ b/app/assets/javascripts/discourse/models/topic.js @@ -452,10 +452,12 @@ Discourse.Topic.reopenClass({ }); }, - bulkOperationByFilter: function(filter, operation) { + bulkOperationByFilter: function(filter, operation, categoryId) { + var data = { filter: filter, operation: operation }; + if (categoryId) data['categoryId'] = categoryId; return Discourse.ajax("/topics/bulk", { type: 'PUT', - data: { filter: filter, operation: operation } + data: data }); }, diff --git a/app/controllers/topics_controller.rb b/app/controllers/topics_controller.rb index f8cdbac396..6cf0501c5f 100644 --- a/app/controllers/topics_controller.rb +++ b/app/controllers/topics_controller.rb @@ -359,7 +359,11 @@ class TopicsController < ApplicationController topic_ids = params[:topic_ids].map {|t| t.to_i} elsif params[:filter] == 'unread' tq = TopicQuery.new(current_user) - topic_ids = TopicQuery.unread_filter(tq.joined_topic_user).listable_topics.pluck(:id) + if params[:categoryId] + topic_ids = TopicQuery.unread_filter(tq.joined_topic_user).listable_topics.where('category_id = ?', params[:categoryId]).pluck(:id) + else + topic_ids = TopicQuery.unread_filter(tq.joined_topic_user).listable_topics.pluck(:id) + end else raise ActionController::ParameterMissing.new(:topic_ids) end From c6e54741bbedc1189e32d36154c8e69339ebde1c Mon Sep 17 00:00:00 2001 From: cpradio Date: Fri, 24 Oct 2014 17:01:28 -0400 Subject: [PATCH 02/83] Apply comments from eviltrout, using this.get('category.id'), and use snake case for category_id --- .../javascripts/discourse/controllers/discovery/topics.js.es6 | 3 +-- app/assets/javascripts/discourse/models/topic.js | 2 +- app/controllers/topics_controller.rb | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/app/assets/javascripts/discourse/controllers/discovery/topics.js.es6 b/app/assets/javascripts/discourse/controllers/discovery/topics.js.es6 index a58dccc90a..bc2794173f 100644 --- a/app/assets/javascripts/discourse/controllers/discovery/topics.js.es6 +++ b/app/assets/javascripts/discourse/controllers/discovery/topics.js.es6 @@ -85,8 +85,7 @@ var controllerOpts = { if (selected.length > 0) { promise = Discourse.Topic.bulkOperation(selected, operation); } else { - var category = this.get('category'); - promise = Discourse.Topic.bulkOperationByFilter('unread', operation, category ? category.id : null); + promise = Discourse.Topic.bulkOperationByFilter('unread', operation, this.get('category.id')); } promise.then(function(result) { if (result && result.topic_ids) { diff --git a/app/assets/javascripts/discourse/models/topic.js b/app/assets/javascripts/discourse/models/topic.js index e0ddb17553..0b876a96d5 100644 --- a/app/assets/javascripts/discourse/models/topic.js +++ b/app/assets/javascripts/discourse/models/topic.js @@ -454,7 +454,7 @@ Discourse.Topic.reopenClass({ bulkOperationByFilter: function(filter, operation, categoryId) { var data = { filter: filter, operation: operation }; - if (categoryId) data['categoryId'] = categoryId; + if (categoryId) data['category_id'] = categoryId; return Discourse.ajax("/topics/bulk", { type: 'PUT', data: data diff --git a/app/controllers/topics_controller.rb b/app/controllers/topics_controller.rb index 6cf0501c5f..ee03c40d74 100644 --- a/app/controllers/topics_controller.rb +++ b/app/controllers/topics_controller.rb @@ -359,8 +359,8 @@ class TopicsController < ApplicationController topic_ids = params[:topic_ids].map {|t| t.to_i} elsif params[:filter] == 'unread' tq = TopicQuery.new(current_user) - if params[:categoryId] - topic_ids = TopicQuery.unread_filter(tq.joined_topic_user).listable_topics.where('category_id = ?', params[:categoryId]).pluck(:id) + if params[:category_id] + topic_ids = TopicQuery.unread_filter(tq.joined_topic_user).listable_topics.where('category_id = ?', params[:category_id]).pluck(:id) else topic_ids = TopicQuery.unread_filter(tq.joined_topic_user).listable_topics.pluck(:id) end From 50f7fbc36121c34a59b1a99c4565df093ccfd483 Mon Sep 17 00:00:00 2001 From: cpradio Date: Thu, 30 Oct 2014 10:19:49 -0400 Subject: [PATCH 03/83] Apply comment from @sam to consolidate logic --- app/controllers/topics_controller.rb | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/app/controllers/topics_controller.rb b/app/controllers/topics_controller.rb index ee03c40d74..e7b3978533 100644 --- a/app/controllers/topics_controller.rb +++ b/app/controllers/topics_controller.rb @@ -359,11 +359,9 @@ class TopicsController < ApplicationController topic_ids = params[:topic_ids].map {|t| t.to_i} elsif params[:filter] == 'unread' tq = TopicQuery.new(current_user) - if params[:category_id] - topic_ids = TopicQuery.unread_filter(tq.joined_topic_user).listable_topics.where('category_id = ?', params[:category_id]).pluck(:id) - else - topic_ids = TopicQuery.unread_filter(tq.joined_topic_user).listable_topics.pluck(:id) - end + topics = TopicQuery.unread_filter(tq.joined_topic_user).listable_topics + topics = topics.where('category_id = ?', params[:category_id]) if params[:category_id] + topic_ids = topics.pluck(:id) else raise ActionController::ParameterMissing.new(:topic_ids) end From 225187733211efbcb6e51fb28a02999e1842a945 Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 31 Oct 2014 09:40:35 +1100 Subject: [PATCH 04/83] FIX: "Dismiss Posts" corrupting read state REFACTOR: seen_post_count was a bad name, renamed to highest_seen_post_number --- app/models/topic.rb | 6 +++--- app/models/topic_user.rb | 14 +++++++------- .../20141030222425_rename_seen_post_count.rb | 5 +++++ lib/post_creator.rb | 2 +- lib/topics_bulk_action.rb | 2 +- lib/unread.rb | 6 +++--- spec/components/post_creator_spec.rb | 2 +- spec/components/post_destroyer_spec.rb | 2 +- spec/components/topics_bulk_action_spec.rb | 8 ++++++-- spec/components/unread_spec.rb | 14 +++++++------- spec/models/post_timing_spec.rb | 16 ++++++++-------- spec/models/topic_user_spec.rb | 5 +++-- 12 files changed, 46 insertions(+), 36 deletions(-) create mode 100644 db/migrate/20141030222425_rename_seen_post_count.rb diff --git a/app/models/topic.rb b/app/models/topic.rb index b80519aa00..b0ab6f24eb 100644 --- a/app/models/topic.rb +++ b/app/models/topic.rb @@ -421,9 +421,9 @@ class Topic < ActiveRecord::Base WHEN last_read_post_number > :highest THEN :highest ELSE last_read_post_number END, - seen_post_count = CASE - WHEN seen_post_count > :highest THEN :highest - ELSE seen_post_count + highest_seen_post_number = CASE + WHEN highest_seen_post_number > :highest THEN :highest + ELSE highest_seen_post_number END WHERE topic_id = :topic_id", highest: highest_post_number, diff --git a/app/models/topic_user.rb b/app/models/topic_user.rb index cc4497b2fa..b252eca730 100644 --- a/app/models/topic_user.rb +++ b/app/models/topic_user.rb @@ -152,8 +152,8 @@ class TopicUser < ActiveRecord::Base threshold: SiteSetting.auto_track_topics_after } - # In case anyone seens "seen_post_count" and gets confused, like I do. - # seen_post_count represents the highest_post_number of the topic when + # In case anyone seens "highest_seen_post_number" and gets confused, like I do. + # highest_seen_post_number represents the highest_post_number of the topic when # the user visited it. It may be out of alignment with last_read, meaning # ... user visited the topic but did not read the posts # @@ -161,7 +161,7 @@ class TopicUser < ActiveRecord::Base rows = exec_sql("UPDATE topic_users SET last_read_post_number = GREATEST(:post_number, tu.last_read_post_number), - seen_post_count = t.highest_post_number, + highest_seen_post_number = t.highest_post_number, total_msecs_viewed = LEAST(tu.total_msecs_viewed + :msecs,86400000), notification_level = case when tu.notifications_reason_id is null and (tu.total_msecs_viewed + :msecs) > @@ -210,7 +210,7 @@ class TopicUser < ActiveRecord::Base TopicTrackingState.publish_read(topic_id, post_number, user.id, args[:new_status]) user.update_posts_read!(post_number) - exec_sql("INSERT INTO topic_users (user_id, topic_id, last_read_post_number, seen_post_count, last_visited_at, first_visited_at, notification_level) + exec_sql("INSERT INTO topic_users (user_id, topic_id, last_read_post_number, highest_seen_post_number, last_visited_at, first_visited_at, notification_level) SELECT :user_id, :topic_id, :post_number, ft.highest_post_number, :now, :now, :new_status FROM topics AS ft JOIN users u on u.id = :user_id @@ -241,7 +241,7 @@ class TopicUser < ActiveRecord::Base UPDATE topic_users t SET last_read_post_number = LEAST(GREATEST(last_read, last_read_post_number), max_post_number), - seen_post_count = LEAST(max_post_number,GREATEST(t.seen_post_count, last_read)) + highest_seen_post_number = LEAST(max_post_number,GREATEST(t.highest_seen_post_number, last_read)) FROM ( SELECT topic_id, user_id, MAX(post_number) last_read FROM post_timings @@ -259,7 +259,7 @@ X.topic_id = t.topic_id AND X.user_id = t.user_id AND ( last_read_post_number <> LEAST(GREATEST(last_read, last_read_post_number), max_post_number) OR - seen_post_count <> LEAST(max_post_number,GREATEST(t.seen_post_count, last_read)) + highest_seen_post_number <> LEAST(max_post_number,GREATEST(t.highest_seen_post_number, last_read)) ) SQL @@ -281,7 +281,7 @@ end # starred :boolean default(FALSE), not null # posted :boolean default(FALSE), not null # last_read_post_number :integer -# seen_post_count :integer +# highest_seen_post_number :integer # starred_at :datetime # last_visited_at :datetime # first_visited_at :datetime diff --git a/db/migrate/20141030222425_rename_seen_post_count.rb b/db/migrate/20141030222425_rename_seen_post_count.rb new file mode 100644 index 0000000000..dac8d14467 --- /dev/null +++ b/db/migrate/20141030222425_rename_seen_post_count.rb @@ -0,0 +1,5 @@ +class RenameSeenPostCount < ActiveRecord::Migration + def change + rename_column :topic_users, :seen_post_count, :highest_seen_post_number + end +end diff --git a/lib/post_creator.rb b/lib/post_creator.rb index ae1c6fc178..af0a1c3e7f 100644 --- a/lib/post_creator.rb +++ b/lib/post_creator.rb @@ -298,7 +298,7 @@ class PostCreator @topic.id, posted: true, last_read_post_number: @post.post_number, - seen_post_count: @post.post_number) + highest_seen_post_number: @post.post_number) # assume it took us 5 seconds of reading time to make a post diff --git a/lib/topics_bulk_action.rb b/lib/topics_bulk_action.rb index fbdea22057..71defb0e3d 100644 --- a/lib/topics_bulk_action.rb +++ b/lib/topics_bulk_action.rb @@ -22,7 +22,7 @@ class TopicsBulkAction def dismiss_posts sql = " UPDATE topic_users tu - SET seen_post_count = t.posts_count , last_read_post_number = highest_post_number + SET highest_seen_post_number = t.highest_post_number , last_read_post_number = highest_post_number FROM topics t WHERE t.id = tu.topic_id AND tu.user_id = :user_id AND t.id IN (:topic_ids) " diff --git a/lib/unread.rb b/lib/unread.rb index 04c2002602..a5a062d319 100644 --- a/lib/unread.rb +++ b/lib/unread.rb @@ -10,17 +10,17 @@ class Unread def unread_posts return 0 if do_not_notify?(@topic_user.notification_level) - result = ((@topic_user.seen_post_count||0) - (@topic_user.last_read_post_number||0)) + result = ((@topic_user.highest_seen_post_number||0) - (@topic_user.last_read_post_number||0)) result = 0 if result < 0 result end def new_posts - return 0 if @topic_user.seen_post_count.blank? + return 0 if @topic_user.highest_seen_post_number.blank? return 0 if do_not_notify?(@topic_user.notification_level) return 0 if (@topic_user.last_read_post_number||0) > @topic.highest_post_number - new_posts = (@topic.highest_post_number - @topic_user.seen_post_count) + new_posts = (@topic.highest_post_number - @topic_user.highest_seen_post_number) new_posts = 0 if new_posts < 0 return new_posts end diff --git a/spec/components/post_creator_spec.rb b/spec/components/post_creator_spec.rb index 6591e726e5..1cdcda9dd6 100644 --- a/spec/components/post_creator_spec.rb +++ b/spec/components/post_creator_spec.rb @@ -178,7 +178,7 @@ describe PostCreator do topic_user.should be_present topic_user.should be_posted topic_user.last_read_post_number.should == first_post.post_number - topic_user.seen_post_count.should == first_post.post_number + topic_user.highest_seen_post_number.should == first_post.post_number user2 = Fabricate(:coding_horror) user2.user_stat.topic_reply_count.should == 0 diff --git a/spec/components/post_destroyer_spec.rb b/spec/components/post_destroyer_spec.rb index b067653398..7785c2ef6c 100644 --- a/spec/components/post_destroyer_spec.rb +++ b/spec/components/post_destroyer_spec.rb @@ -241,7 +241,7 @@ describe PostDestroyer do end it "sets the second user's last_read_post_number back to 1" do - topic_user.seen_post_count.should == 1 + topic_user.highest_seen_post_number.should == 1 end end diff --git a/spec/components/topics_bulk_action_spec.rb b/spec/components/topics_bulk_action_spec.rb index 325c6e402f..533c876a27 100644 --- a/spec/components/topics_bulk_action_spec.rb +++ b/spec/components/topics_bulk_action_spec.rb @@ -6,13 +6,17 @@ describe TopicsBulkAction do describe "dismiss_posts" do it "dismisses posts" do post1 = create_post + p = create_post(topic_id: post1.topic_id) create_post(topic_id: post1.topic_id) + PostDestroyer.new(Fabricate(:admin), p).destroy + TopicsBulkAction.new(post1.user, [post1.topic_id], type: 'dismiss_posts').perform! tu = TopicUser.find_by(user_id: post1.user_id, topic_id: post1.topic_id) - tu.last_read_post_number.should == 2 - tu.seen_post_count = 2 + + tu.last_read_post_number.should == 3 + tu.highest_seen_post_number.should == 3 end end diff --git a/spec/components/unread_spec.rb b/spec/components/unread_spec.rb index 45c11cc7f8..61a9b9bc4a 100644 --- a/spec/components/unread_spec.rb +++ b/spec/components/unread_spec.rb @@ -16,19 +16,19 @@ describe Unread do describe 'unread_posts' do it 'should have 0 unread posts if the user has seen all posts' do @topic_user.stubs(:last_read_post_number).returns(13) - @topic_user.stubs(:seen_post_count).returns(13) + @topic_user.stubs(:highest_seen_post_number).returns(13) @unread.unread_posts.should == 0 end it 'should have 6 unread posts if the user has seen all but 6 posts' do @topic_user.stubs(:last_read_post_number).returns(5) - @topic_user.stubs(:seen_post_count).returns(11) + @topic_user.stubs(:highest_seen_post_number).returns(11) @unread.unread_posts.should == 6 end it 'should have 0 unread posts if the user has seen more posts than exist (deleted)' do @topic_user.stubs(:last_read_post_number).returns(100) - @topic_user.stubs(:seen_post_count).returns(13) + @topic_user.stubs(:highest_seen_post_number).returns(13) @unread.unread_posts.should == 0 end end @@ -40,23 +40,23 @@ describe Unread do end it 'returns 0 when the topic is the same length as when you last saw it' do - @topic_user.stubs(:seen_post_count).returns(13) + @topic_user.stubs(:highest_seen_post_number).returns(13) @unread.new_posts.should == 0 end it 'has 3 new posts if the user has read 10 posts' do - @topic_user.stubs(:seen_post_count).returns(10) + @topic_user.stubs(:highest_seen_post_number).returns(10) @unread.new_posts.should == 3 end it 'has 0 new posts if the user has read 10 posts but is not tracking' do - @topic_user.stubs(:seen_post_count).returns(10) + @topic_user.stubs(:highest_seen_post_number).returns(10) @topic_user.stubs(:notification_level).returns(TopicUser.notification_levels[:regular]) @unread.new_posts.should == 0 end it 'has 0 new posts if the user read more posts than exist (deleted)' do - @topic_user.stubs(:seen_post_count).returns(16) + @topic_user.stubs(:highest_seen_post_number).returns(16) @unread.new_posts.should == 0 end diff --git a/spec/models/post_timing_spec.rb b/spec/models/post_timing_spec.rb index 3cdedc9ca3..c3694386d2 100644 --- a/spec/models/post_timing_spec.rb +++ b/spec/models/post_timing_spec.rb @@ -18,12 +18,12 @@ describe PostTiming do PostTiming.create!(topic_id: topic_id, user_id: user_id, post_number: post_number, msecs: 0) end - def topic_user(user_id, last_read_post_number, seen_post_count) + def topic_user(user_id, last_read_post_number, highest_seen_post_number) TopicUser.create!( topic_id: topic_id, user_id: user_id, last_read_post_number: last_read_post_number, - seen_post_count: seen_post_count + highest_seen_post_number: highest_seen_post_number ) end @@ -35,9 +35,9 @@ describe PostTiming do timing(3,2) timing(3,3) - tu_one = topic_user(1,1,1) - tu_two = topic_user(2,2,2) - tu_three = topic_user(3,3,3) + _tu_one = topic_user(1,1,1) + _tu_two = topic_user(2,2,2) + _tu_three = topic_user(3,3,3) PostTiming.pretend_read(topic_id, 2, 3) @@ -47,15 +47,15 @@ describe PostTiming do tu = TopicUser.find_by(topic_id: topic_id, user_id: 1) tu.last_read_post_number.should == 1 - tu.seen_post_count.should == 1 + tu.highest_seen_post_number.should == 1 tu = TopicUser.find_by(topic_id: topic_id, user_id: 2) tu.last_read_post_number.should == 3 - tu.seen_post_count.should == 3 + tu.highest_seen_post_number.should == 3 tu = TopicUser.find_by(topic_id: topic_id, user_id: 3) tu.last_read_post_number.should == 3 - tu.seen_post_count.should == 3 + tu.highest_seen_post_number.should == 3 end end diff --git a/spec/models/topic_user_spec.rb b/spec/models/topic_user_spec.rb index 382f8e3ef6..c1a62ac4d5 100644 --- a/spec/models/topic_user_spec.rb +++ b/spec/models/topic_user_spec.rb @@ -254,7 +254,7 @@ describe TopicUser do p2 = Fabricate(:post, user: p1.user, topic: p1.topic, post_number: 2) p1.topic.notifier.watch_topic!(p1.user_id) - TopicUser.exec_sql("UPDATE topic_users set seen_post_count=100, last_read_post_number=0 + TopicUser.exec_sql("UPDATE topic_users set highest_seen_post_number=1, last_read_post_number=0 WHERE topic_id = :topic_id AND user_id = :user_id", topic_id: p1.topic_id, user_id: p1.user_id) [p1,p2].each do |p| @@ -265,7 +265,8 @@ describe TopicUser do tu = TopicUser.find_by(user_id: p1.user_id, topic_id: p1.topic_id) tu.last_read_post_number.should == p2.post_number - tu.seen_post_count.should == 2 + tu.highest_seen_post_number.should == 2 + end describe "mailing_list_mode" do From 4cf33ca59a3e425ee2b268880fb5a54c9fd0a7c4 Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 31 Oct 2014 15:14:55 +1100 Subject: [PATCH 05/83] upgrade rails --- Gemfile.lock | 52 ++++++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 82c7c984d3..18cd36b58b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -6,31 +6,31 @@ PATH GEM remote: https://rubygems.org/ specs: - actionmailer (4.1.6) - actionpack (= 4.1.6) - actionview (= 4.1.6) + actionmailer (4.1.7) + actionpack (= 4.1.7) + actionview (= 4.1.7) mail (~> 2.5, >= 2.5.4) - actionpack (4.1.6) - actionview (= 4.1.6) - activesupport (= 4.1.6) + actionpack (4.1.7) + actionview (= 4.1.7) + activesupport (= 4.1.7) rack (~> 1.5.2) rack-test (~> 0.6.2) actionpack-action_caching (1.1.1) actionpack (>= 4.0.0, < 5.0) - actionview (4.1.6) - activesupport (= 4.1.6) + actionview (4.1.7) + activesupport (= 4.1.7) builder (~> 3.1) erubis (~> 2.7.0) active_model_serializers (0.8.2) activemodel (>= 3.0) - activemodel (4.1.6) - activesupport (= 4.1.6) + activemodel (4.1.7) + activesupport (= 4.1.7) builder (~> 3.1) - activerecord (4.1.6) - activemodel (= 4.1.6) - activesupport (= 4.1.6) + activerecord (4.1.7) + activemodel (= 4.1.7) + activesupport (= 4.1.7) arel (~> 5.0.0) - activesupport (4.1.6) + activesupport (4.1.7) i18n (~> 0.6, >= 0.6.9) json (~> 1.7, >= 1.7.7) minitest (~> 5.1) @@ -171,7 +171,7 @@ GEM method_source (0.8.2) mime-types (1.25.1) mini_portile (0.6.0) - minitest (5.4.1) + minitest (5.4.2) mocha (1.1.0) metaclass (~> 0.0.1) mock_redis (0.13.2) @@ -255,21 +255,21 @@ GEM rack rack-test (0.6.2) rack (>= 1.0) - rails (4.1.6) - actionmailer (= 4.1.6) - actionpack (= 4.1.6) - actionview (= 4.1.6) - activemodel (= 4.1.6) - activerecord (= 4.1.6) - activesupport (= 4.1.6) + rails (4.1.7) + actionmailer (= 4.1.7) + actionpack (= 4.1.7) + actionview (= 4.1.7) + activemodel (= 4.1.7) + activerecord (= 4.1.7) + activesupport (= 4.1.7) bundler (>= 1.3.0, < 2.0) - railties (= 4.1.6) + railties (= 4.1.7) sprockets-rails (~> 2.0) rails-observers (0.1.2) activemodel (~> 4.0) - railties (4.1.6) - actionpack (= 4.1.6) - activesupport (= 4.1.6) + railties (4.1.7) + actionpack (= 4.1.7) + activesupport (= 4.1.7) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) raindrops (0.13.0) From c3767dff6a3bbbcd4d74b3243fc72ce3303adbda Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 31 Oct 2014 15:15:40 +1100 Subject: [PATCH 06/83] FEATURE: stop watching for new suggested topics --- .../javascripts/discourse/views/topic.js.es6 | 29 ------------------- 1 file changed, 29 deletions(-) diff --git a/app/assets/javascripts/discourse/views/topic.js.es6 b/app/assets/javascripts/discourse/views/topic.js.es6 index 0cfc4a3d76..76733c7950 100644 --- a/app/assets/javascripts/discourse/views/topic.js.es6 +++ b/app/assets/javascripts/discourse/views/topic.js.es6 @@ -70,35 +70,6 @@ export default Discourse.View.extend(AddCategoryClass, Discourse.Scrolling, { }.on('willDestroyElement'), - debounceLoadSuggested: Discourse.debounce(function(){ - if (this.get('isDestroyed') || this.get('isDestroying')) { return; } - - var incoming = this.get('topicTrackingState.newIncoming'), - suggested = this.get('topic.details.suggested_topics'), - topicId = this.get('topic.id'); - - if(suggested) { - var existing = _.invoke(suggested, 'get', 'id'), - lookup = _.chain(incoming) - .last(Discourse.SiteSettings.suggested_topics) - .reverse() - .union(existing) - .uniq() - .without(topicId) - .first(Discourse.SiteSettings.suggested_topics) - .value(); - - Discourse.TopicList.loadTopics(lookup, "").then(function(topics){ - suggested.clear(); - suggested.pushObjects(topics); - }); - } - }, 1000), - - hasNewSuggested: function(){ - this.debounceLoadSuggested(); - }.observes('topicTrackingState.incomingCount'), - gotFocus: function(){ if (Discourse.get('hasFocus')){ this.scrolled(); From 1d4daca75d42dee697f1dc48b21fe74a14273dd2 Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 31 Oct 2014 15:16:08 +1100 Subject: [PATCH 07/83] add override for post creation in importer --- script/import_scripts/base.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/script/import_scripts/base.rb b/script/import_scripts/base.rb index 9016dea7ce..09d0c805b6 100644 --- a/script/import_scripts/base.rb +++ b/script/import_scripts/base.rb @@ -364,6 +364,10 @@ class ImportScripts::Base new_category end + def created_post(post) + # override if needed + end + # Iterates through a collection of posts to be imported. # It can create topics and replies. # Attributes will be passed to the PostCreator. @@ -396,6 +400,8 @@ class ImportScripts::Base url: new_post.url, } + created_post(new_post) + created += 1 else skipped += 1 From f6b20ada939fd48a41538bcf42cfe050bb1e61c6 Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 31 Oct 2014 15:19:27 +1100 Subject: [PATCH 08/83] a new bespoke importer, feel free to borrow ideas --- script/import_scripts/bespoke_1.rb | 282 +++++++++++++++++++++++++++++ 1 file changed, 282 insertions(+) create mode 100644 script/import_scripts/bespoke_1.rb diff --git a/script/import_scripts/bespoke_1.rb b/script/import_scripts/bespoke_1.rb new file mode 100644 index 0000000000..beed653802 --- /dev/null +++ b/script/import_scripts/bespoke_1.rb @@ -0,0 +1,282 @@ +# bespoke importer for a customer, feel free to borrow ideas +# +# +require 'csv' +require File.expand_path(File.dirname(__FILE__) + "/base.rb") + +# Call it like this: +# RAILS_ENV=production bundle exec ruby script/import_scripts/bespoke_1.rb +class ImportScripts::Bespoke < ImportScripts::Base + + BATCH_SIZE = 1000 + + def initialize(path) + @path = path + super() + @bbcode_to_md = true + + puts "loading post mappings..." + @post_number_map = {} + Post.pluck(:id, :post_number).each do |post_id, post_number| + @post_number_map[post_id] = post_number + end + end + + def created_post(post) + @post_number_map[post.id] = post.post_number + super + end + + def execute + import_users + import_categories + import_posts + + end + + class RowResolver + def load(row) + @row = row + end + + def self.create(cols) + Class.new(RowResolver).new(cols) + end + + def initialize(cols) + cols.each_with_index do |col,idx| + self.class.send(:define_method, col) do + @row[idx] + end + end + end + end + + def load_user_batch!(users, offset, total) + if users.length > 0 + create_users(users, offset: offset, total: total) do |user| + user + end + users.clear + end + end + + def csv_parse(name) + filename = "#{@path}/#{name}.csv" + first = true + row = nil + + current_row = ""; + double_quote_count = 0 + + File.open(filename).each_line do |line| + + # escaping is mental here + line.gsub!(/\\(.{1})/){|m| m[-1] == '"'? '""': m[-1]} + line.strip! + + current_row << "\n" unless current_row.empty? + current_row << line + + double_quote_count += line.scan('"').count + + if double_quote_count % 2 == 1 + next + end + + raw = begin + CSV.parse(current_row) + rescue CSV::MalformedCSVError => e + puts e.message + puts "*" * 100 + puts "Bad row skipped, line is: #{line}" + puts + puts current_row + puts + puts "double quote count is : #{double_quote_count}" + puts "*" * 100 + + current_row = "" + double_quote_count = 0 + next + end[0] + + if first + row = RowResolver.create(raw) + + current_row = "" + double_quote_count = 0 + first = false + next + end + + row.load(raw) + + yield row + + current_row = "" + double_quote_count = 0 + end + end + + def total_rows(table) + File.foreach("#{@path}/#{table}.csv").inject(0) {|c, line| c+1} - 1 + end + + def import_users + puts "", "creating users" + + count = 0 + users = [] + + total = total_rows("users") + + csv_parse("users") do |row| + + id = row.id + email = row.email + + # fake it + if row.email.blank? || row.email !~ /@/ + email = SecureRandom.hex << "@domain.com" + end + + name = row.display_name + username = row.key_custom + created_at = DateTime.parse(row.dcreate) + + username = name if username == "NULL" + + users << { + id: id, + email: email, + name: name, + username: username, + created_at: created_at + } + + count += 1 + if count % BATCH_SIZE == 0 + load_user_batch! users, count - users.length, total + end + + end + + load_user_batch! users, count, total + end + + def import_categories + rows = [] + csv_parse("categories") do |row| + rows << {id: row.id, name: row.name, description: row.description} + end + + create_categories(rows) do |row| + row + end + end + + def normalize_raw!(raw) + # purple and #1223f3 + raw.gsub!(/\[color=[#a-z0-9]+\]/i, "") + raw.gsub!(/\[\/color\]/i, "") + raw.gsub!(/\[signature\].+\[\/signature\]/i,"") + raw + end + + def import_post_batch!(posts, topics, offset, total) + create_posts(posts, total: total, offset: offset) do |post| + + mapped = {} + + mapped[:id] = post[:id] + mapped[:user_id] = user_id_from_imported_user_id(post[:user_id]) || -1 + mapped[:raw] = post[:body] + mapped[:created_at] = post[:created_at] + + topic = topics[post[:topic_id]] + + unless topic[:post_id] + mapped[:category] = category_from_imported_category_id(topic[:category_id]).try(:name) + mapped[:title] = post[:title] + topic[:post_id] = post[:id] + else + parent = topic_lookup_from_imported_post_id(topic[:post_id]) + mapped[:topic_id] = parent[:topic_id] + + reply_to_post_id = post_id_from_imported_post_id(post[:reply_id]) + if reply_to_post_id + reply_to_post_number = @post_number_map[reply_to_post_id] + if reply_to_post_number + mapped[:reply_to_post_number] = reply_to_post_number + end + end + end + + return nil if topic[:deleted] or post[:deleted] + + mapped + end + + posts.clear + end + + def import_posts + puts "", "creating topics and posts" + + topic_map = {} + + csv_parse("topics") do |topic| + topic_map[topic.id] = { + id: topic.id, + category_id: topic.forum_category_id, + deleted: topic.is_deleted.to_i == 1, + locked: topic.is_locked.to_i == 1, + pinned: topic.is_pinned.to_i == 1 + } + end + + total = total_rows("posts") + + posts = [] + count = 0 + csv_parse("posts") do |row| + + unless row.dcreate + puts "NO CREATION DATE FOR POST" + p row + next + end + + row = { + id: row.id, + topic_id: row.forum_topic_id, + reply_id: row.reply_id, + user_id: row.user_id, + title: row.title, + body: normalize_raw!(row.body), + deleted: row.is_deleted.to_i == 1, + created_at: DateTime.parse(row.dcreate) + } + posts << row + count+=1 + + if posts.length > 0 && posts.length % BATCH_SIZE == 0 + import_post_batch!(posts, topic_map, count - posts.length, total) + end + end + + import_post_batch!(posts, topic_map, count - posts.length, total) if posts.length > 0 + + exit + end + + +end + +unless ARGV[0] && Dir.exist?(ARGV[0]) + puts "", "Usage:", "", "bundle exec ruby script/import_scripts/bespoke_1.rb DIRNAME", "" + exit 1 +end + +ImportScripts::Bespoke.new(ARGV[0]).perform From cbaa8893ce75bfb60b6d49a5e8de8f1d3ea9810c Mon Sep 17 00:00:00 2001 From: vagrant Date: Fri, 31 Oct 2014 00:46:27 -0400 Subject: [PATCH 09/83] few small usercard fixes, more to come! --- .../discourse/templates/user-card.hbs | 18 +++++++++++++---- app/assets/stylesheets/desktop/user-card.scss | 20 +++++++++++++------ 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/app/assets/javascripts/discourse/templates/user-card.hbs b/app/assets/javascripts/discourse/templates/user-card.hbs index 62069b38db..600c370228 100644 --- a/app/assets/javascripts/discourse/templates/user-card.hbs +++ b/app/assets/javascripts/discourse/templates/user-card.hbs @@ -1,6 +1,6 @@ -{{#if username}} +
-
+{{#if username}} {{#link-to 'user' user}}{{bound-avatar avatar "huge"}}{{/link-to}} @@ -9,9 +9,19 @@

{{#link-to 'user' user}}{{username}}{{/link-to}}

- {{#if user.title}} -

{{user.title}}

+ + {{#if user.name}} +

{{user.name}}

{{/if}} + + {{#if user.title}} + {{#if user.name}} +

/ {{user.title}}

+ {{else}} +

{{user.title}}

+ {{/if}} + {{/if}} + {{#if showName}}

{{#link-to 'user' user}}{{name}}{{/link-to}}

{{/if}} diff --git a/app/assets/stylesheets/desktop/user-card.scss b/app/assets/stylesheets/desktop/user-card.scss index d03ec4eab4..175110b4a0 100644 --- a/app/assets/stylesheets/desktop/user-card.scss +++ b/app/assets/stylesheets/desktop/user-card.scss @@ -11,11 +11,13 @@ color: $secondary; background-size: cover; background-position: center center; + min-height: 175px; .card-content { padding: 12px; background: rgba($primary, .85); - margin-top: 100px; + margin-top: 85px; + &:after { content: ''; display: block; @@ -24,6 +26,8 @@ } &.no-bg { + min-height: 50px; + .card-content { margin-top: 0; } @@ -60,6 +64,7 @@ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + display: inline; a { color: $secondary; } @@ -101,8 +106,8 @@ } .bio { - max-height: 55px; - overflow: auto; + max-height: 60px; + overflow: hidden; float: left; margin: 10px 0; width: 70%; @@ -161,6 +166,7 @@ position: absolute; bottom: 0; display: block; + width: 250px; } } @@ -186,7 +192,6 @@ .more-user-badges { @extend .user-badge; padding: 3px 8px; - font-size: 13px; } } @@ -199,7 +204,10 @@ img { max-width: 100px; } - float: right; - margin-right: 5px; + position: absolute; + right: 12px; + bottom: 12px; + font-size: 30px; + i {color: $secondary;} } } From 9ffd7546eb5d895043d92d624cf018963d41874c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Fri, 31 Oct 2014 12:01:16 +0100 Subject: [PATCH 10/83] add 'plugins' directory to sublime's project file --- discourse.sublime-project | 1 + 1 file changed, 1 insertion(+) diff --git a/discourse.sublime-project b/discourse.sublime-project index 16bc37e55d..6ce1a8c6ea 100644 --- a/discourse.sublime-project +++ b/discourse.sublime-project @@ -12,6 +12,7 @@ "file_exclude_patterns": ["*.sqlite3"] }, { "path": "lib" }, + { "path": "plugins" }, { "path": "script" }, { "path": "spec" }, { "path": "test", From 22525ff74c821a9c7da2ee9ba73ce2e974f1d63d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Fri, 31 Oct 2014 12:01:47 +0100 Subject: [PATCH 11/83] FIX: changing title of a poll should close/open the poll --- .../poll/assets/javascripts/initializers/poll.js.es6 | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/plugins/poll/assets/javascripts/initializers/poll.js.es6 b/plugins/poll/assets/javascripts/initializers/poll.js.es6 index 2e7e2eaa6b..c55cac7eca 100644 --- a/plugins/poll/assets/javascripts/initializers/poll.js.es6 +++ b/plugins/poll/assets/javascripts/initializers/poll.js.es6 @@ -9,9 +9,12 @@ var Poll = Discourse.Model.extend({ this.updateFromJson(this.get('post.poll_details')); }.observes('post.poll_details'), - fetchNewPostDetails: function() { - this.get('post.topic.postStream').triggerChangedPost(this.get('post.id'), this.get('post.topic.updated_at')); - }.observes('post.topic.title'), + fetchNewPostDetails: Discourse.debounce(function() { + var self = this; + Discourse.debounce(function() { + self.get('post.topic.postStream').triggerChangedPost(self.get('post.id'), self.get('post.topic.updated_at')); + }, 500); + }).observes('post.topic.title'), updateFromJson: function(json) { var selectedOption = json["selected"]; @@ -24,8 +27,8 @@ var Poll = Discourse.Model.extend({ checked: (option === selectedOption) })); }); - this.set('options', options); + this.set('options', options); this.set('closed', json.closed); }, From 757cea05a4c3b3eed87393cb12dbce6557e443a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Fern=C3=A1ndez?= Date: Fri, 31 Oct 2014 15:21:43 -0200 Subject: [PATCH 12/83] Feature: Extended user stats in user export csv --- app/jobs/regular/export_csv_file.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/jobs/regular/export_csv_file.rb b/app/jobs/regular/export_csv_file.rb index 8c215bdef8..ae4f58d8f1 100644 --- a/app/jobs/regular/export_csv_file.rb +++ b/app/jobs/regular/export_csv_file.rb @@ -26,7 +26,10 @@ module Jobs user_data.each do |user| user_array = Array.new group_names = get_group_names(user).join(';') - user_array.push(user['id']).push(user['name']).push(user['username']).push(user['email']) + user_array.push(user['id']).push(user['name']).push(user['username']).push(user['email']).push(user['created_at']) + .push(user.user_stat['topics_entered']).push(user.user_stat['posts_read_count']).push(user.user_stat['time_read']) + .push(user.user_stat['topic_count']).push(user.user_stat['post_count']).push(user.user_stat['likes_given']) + .push(user.user_stat['likes_received']) user_array.push(group_names) if group_names != '' data.push(user_array) end From bdc7947cd6dbbbe47db9434e86933e2d94cb1fef Mon Sep 17 00:00:00 2001 From: "Jason W. May" Date: Fri, 31 Oct 2014 10:44:26 -0700 Subject: [PATCH 13/83] rspec expect...to deprecations --- spec/models/email_log_spec.rb | 2 +- spec/models/user_profile_spec.rb | 4 ++-- spec/models/user_spec.rb | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/spec/models/email_log_spec.rb b/spec/models/email_log_spec.rb index 789fe9d4a0..9db88984fd 100644 --- a/spec/models/email_log_spec.rb +++ b/spec/models/email_log_spec.rb @@ -49,7 +49,7 @@ describe EmailLog do context "when user's email does not exist email logs" do it "returns nil" do - expect(user.email_logs.last_sent_email_address).should == nil + expect(user.email_logs.last_sent_email_address).to be_nil end end end diff --git a/spec/models/user_profile_spec.rb b/spec/models/user_profile_spec.rb index 99c2042770..ad18eb8617 100644 --- a/spec/models/user_profile_spec.rb +++ b/spec/models/user_profile_spec.rb @@ -24,12 +24,12 @@ describe UserProfile do let(:user_profile) { Fabricate.build(:user_profile) } it 'is not valid without user' do - expect(user_profile.valid?).should == false + expect(user_profile.valid?).to be false end it 'is is valid with user' do user_profile.user = Fabricate.build(:user) - expect(user_profile.valid?).should == true + expect(user_profile.valid?).to be true end it "doesn't support really long bios" do diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 8cf56a5bd2..d2d1d79afe 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -179,7 +179,7 @@ describe User do expect(Post.where(id: @posts.map(&:id))).to be_empty @posts.each do |p| if p.post_number == 1 - expect(Topic.find_by(id: p.topic_id)).should == nil + expect(Topic.find_by(id: p.topic_id)).to be_nil end end end @@ -777,7 +777,7 @@ describe User do expect(found_user).to eq bob found_user = User.find_by_username_or_email('bob1') - expect(found_user).should == nil + expect(found_user).to be_nil found_user = User.find_by_email('bob@Example.com') expect(found_user).to eq bob @@ -786,7 +786,7 @@ describe User do expect(found_user).to eq bob found_user = User.find_by_email('bob') - expect(found_user).should == nil + expect(found_user).to be_nil found_user = User.find_by_username('bOb') expect(found_user).to eq bob From b732e9b1bb25611ec360c044fefe94cc36ed3a9a Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Fri, 31 Oct 2014 11:46:31 -0700 Subject: [PATCH 14/83] tighten up expansion left gutter --- app/assets/stylesheets/desktop/topic-post.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/desktop/topic-post.scss b/app/assets/stylesheets/desktop/topic-post.scss index ea3c2c0b6f..8cbb5dd38b 100644 --- a/app/assets/stylesheets/desktop/topic-post.scss +++ b/app/assets/stylesheets/desktop/topic-post.scss @@ -242,7 +242,7 @@ nav.post-controls { .topic-body { background: dark-light-diff($primary, $secondary, 90%, -65%) !important; - width: 86%; + width: 87%; padding-left: 1%; padding-right: 1%; border-top: none; @@ -253,7 +253,7 @@ nav.post-controls { .topic-avatar { width: 45px; - margin: 0 10px 0 15px; + margin: 0 5px 0 15px; border: none; } From f179c8a6fbe42cc63fae0effc7aeef9b0a41e294 Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Fri, 31 Oct 2014 12:07:09 -0700 Subject: [PATCH 15/83] make search help fit on mobile --- app/assets/stylesheets/mobile/modal.scss | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/app/assets/stylesheets/mobile/modal.scss b/app/assets/stylesheets/mobile/modal.scss index fc5e991f36..bfacdb308b 100644 --- a/app/assets/stylesheets/mobile/modal.scss +++ b/app/assets/stylesheets/mobile/modal.scss @@ -163,3 +163,16 @@ float: none !important; } } + +#search-help { + max-width: 300px; +} + +#search-help h2 { + margin: 0; + font-size: 12px; +} + +#search-help p { + margin: 5px; +} \ No newline at end of file From e822108d8572fc5410932c8f955896d84560e9f3 Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Fri, 31 Oct 2014 12:12:25 -0700 Subject: [PATCH 16/83] tighten up mobile search spacing --- app/assets/stylesheets/mobile/header.scss | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/assets/stylesheets/mobile/header.scss b/app/assets/stylesheets/mobile/header.scss index 59736598cb..70d32d064a 100644 --- a/app/assets/stylesheets/mobile/header.scss +++ b/app/assets/stylesheets/mobile/header.scss @@ -75,7 +75,6 @@ font-size: 14px; } -.d-dropdown .no-results .show-help { - display: none; -} - +.d-dropdown#search-dropdown .heading { + padding: 0; +} \ No newline at end of file From 28bd46d816faff574c2dfa5714acb4992cfcabb4 Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Fri, 31 Oct 2014 12:21:55 -0700 Subject: [PATCH 17/83] clean up user page mobile styles --- app/assets/stylesheets/mobile/user.scss | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/app/assets/stylesheets/mobile/user.scss b/app/assets/stylesheets/mobile/user.scss index 97cb40a72d..274414dcb5 100644 --- a/app/assets/stylesheets/mobile/user.scss +++ b/app/assets/stylesheets/mobile/user.scss @@ -151,12 +151,11 @@ .user-navigation { width: 100%; - margin-right: 1.8018%; margin-top: 20px; h3 { color: $primary; - margin: 20px 0 10px 0; + margin: 10px; } } @@ -241,7 +240,6 @@ .btn { padding: 3px 12px; } dl dd { - display: inline; margin: 0 15px 0 5px; padding: 0; } @@ -344,8 +342,7 @@ } .controls { - padding: 0 0 12px 0; - float: right; + width: 40%; ul {list-style-type: none;} a { padding: 5px 10px; @@ -355,7 +352,6 @@ .right { float: right; - margin-left: 5px; } } } @@ -411,6 +407,7 @@ .user-stream { + padding: 0 10px; .end-of-stream { width: auto; } From 0408960b74edae9dfda8327dcd622dc7e1628f61 Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Fri, 31 Oct 2014 12:26:46 -0700 Subject: [PATCH 18/83] tweak time field on user page stream --- app/assets/stylesheets/mobile/user.scss | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/assets/stylesheets/mobile/user.scss b/app/assets/stylesheets/mobile/user.scss index 274414dcb5..cf8863a2fb 100644 --- a/app/assets/stylesheets/mobile/user.scss +++ b/app/assets/stylesheets/mobile/user.scss @@ -442,8 +442,7 @@ .time, .delete-info { display: block; float: right; - color: $primary; - margin-right: 8px; + color: lighten($primary, 40%); font-size: 11px; } .delete-info i { From 811b8f32817e293f7116c1bc60b2d32048152d80 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Fri, 31 Oct 2014 17:34:45 -0400 Subject: [PATCH 19/83] FIX: On mobile you couldn't click on a post avatar --- app/assets/stylesheets/mobile/topic-post.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/mobile/topic-post.scss b/app/assets/stylesheets/mobile/topic-post.scss index 3194bbf977..19786feeb8 100644 --- a/app/assets/stylesheets/mobile/topic-post.scss +++ b/app/assets/stylesheets/mobile/topic-post.scss @@ -470,12 +470,13 @@ span.highlighted { } .topic-meta-data { + margin-left: 60px; white-space: nowrap; position: absolute; width: 100%; left: 0px; .names { - margin: 5px 0 0 65px; + margin: 5px 0 0 5px; line-height: 17px; span { display: block; From 5ddb82c9b687891280b26e9113ffbaaac36d4cc4 Mon Sep 17 00:00:00 2001 From: Sam Date: Sat, 1 Nov 2014 18:25:03 +1100 Subject: [PATCH 20/83] impor script fixes --- script/import_scripts/bespoke_1.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/script/import_scripts/bespoke_1.rb b/script/import_scripts/bespoke_1.rb index beed653802..e91a001a57 100644 --- a/script/import_scripts/bespoke_1.rb +++ b/script/import_scripts/bespoke_1.rb @@ -28,8 +28,8 @@ class ImportScripts::Bespoke < ImportScripts::Base end def execute - import_users - import_categories + #import_users + #import_categories import_posts end @@ -202,18 +202,20 @@ class ImportScripts::Bespoke < ImportScripts::Base topic[:post_id] = post[:id] else parent = topic_lookup_from_imported_post_id(topic[:post_id]) + next unless parent + mapped[:topic_id] = parent[:topic_id] reply_to_post_id = post_id_from_imported_post_id(post[:reply_id]) if reply_to_post_id reply_to_post_number = @post_number_map[reply_to_post_id] - if reply_to_post_number + if reply_to_post_number && reply_to_post_number > 1 mapped[:reply_to_post_number] = reply_to_post_number end end end - return nil if topic[:deleted] or post[:deleted] + next if topic[:deleted] or post[:deleted] mapped end From 9d43e0ae0cea073c34f25a821643efe493f43c91 Mon Sep 17 00:00:00 2001 From: Sam Date: Sat, 1 Nov 2014 18:30:47 +1100 Subject: [PATCH 21/83] FIX: regression, ESC is not captured correctly --- app/assets/javascripts/discourse/lib/autocomplete.js.es6 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/discourse/lib/autocomplete.js.es6 b/app/assets/javascripts/discourse/lib/autocomplete.js.es6 index a79d76c99a..c945e37a47 100644 --- a/app/assets/javascripts/discourse/lib/autocomplete.js.es6 +++ b/app/assets/javascripts/discourse/lib/autocomplete.js.es6 @@ -331,7 +331,7 @@ export default function(options) { } // ESC - if (e.which === keys.escape) { + if (e.which === keys.esc) { if (completeStart !== null) { closeAutocomplete(); return false; From 6a946712b388887b575345a640f78dda15fc11c2 Mon Sep 17 00:00:00 2001 From: riking Date: Sat, 1 Nov 2014 14:30:08 -0700 Subject: [PATCH 22/83] Add ?include_raw parameter to topic views --- app/controllers/topics_controller.rb | 2 +- app/serializers/post_stream_serializer_mixin.rb | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/controllers/topics_controller.rb b/app/controllers/topics_controller.rb index ade19cbd52..e4e31cb55b 100644 --- a/app/controllers/topics_controller.rb +++ b/app/controllers/topics_controller.rb @@ -441,7 +441,7 @@ class TopicsController < ApplicationController end def perform_show_response - topic_view_serializer = TopicViewSerializer.new(@topic_view, scope: guardian, root: false) + topic_view_serializer = TopicViewSerializer.new(@topic_view, scope: guardian, root: false, include_raw: !!params[:include_raw]) respond_to do |format| format.html do diff --git a/app/serializers/post_stream_serializer_mixin.rb b/app/serializers/post_stream_serializer_mixin.rb index f87edaa284..e5edb78978 100644 --- a/app/serializers/post_stream_serializer_mixin.rb +++ b/app/serializers/post_stream_serializer_mixin.rb @@ -21,6 +21,7 @@ module PostStreamSerializerMixin object.posts.each_with_index do |p, idx| highest_number_in_posts = p.post_number if p.post_number > highest_number_in_posts ps = PostSerializer.new(p, scope: scope, root: false) + ps.add_raw = true if @options[:include_raw] ps.topic_view = object p.topic = object.topic From 1f4e2d0fd63c56db77a9341229b427703704f2bc Mon Sep 17 00:00:00 2001 From: Kris Aubuchon Date: Sat, 1 Nov 2014 22:58:18 -0400 Subject: [PATCH 23/83] adding last post/joined to usercard --- .../discourse/templates/user-card.hbs | 65 ++++++++++--------- app/assets/stylesheets/desktop/user-card.scss | 54 +++++++++------ 2 files changed, 70 insertions(+), 49 deletions(-) diff --git a/app/assets/javascripts/discourse/templates/user-card.hbs b/app/assets/javascripts/discourse/templates/user-card.hbs index 600c370228..49b1830748 100644 --- a/app/assets/javascripts/discourse/templates/user-card.hbs +++ b/app/assets/javascripts/discourse/templates/user-card.hbs @@ -7,7 +7,7 @@

- {{#link-to 'user' user}}{{username}}{{/link-to}} + {{#link-to 'user' user}}{{username}}{{{user.statusIcon}}}{{/link-to}}

{{#if user.name}} @@ -15,11 +15,7 @@ {{/if}} {{#if user.title}} - {{#if user.name}} -

/ {{user.title}}

- {{else}} -

{{user.title}}

- {{/if}} +

{{user.title}}

{{/if}} {{#if showName}} @@ -30,29 +26,6 @@ {{#if user}} - {{#if isSuspended}} -
- {{fa-icon "ban"}} - {{i18n user.suspended_notice date="user.suspendedTillDate"}}
- {{i18n user.suspended_reason}} {{user.suspend_reason}} -
- {{else}} - {{#if user.bio_cooked}}
{{{user.bio_cooked}}}
{{/if}} - {{/if}} - -{{#if showBadges}} -
- {{#each user.featured_user_badges}} - {{user-badge badge=badge}} - {{/each}} - {{#if showMoreBadges}} - {{#link-to 'user.badges' user class="btn more-user-badges"}} - {{i18n badges.more_badges count=moreBadgesCount}} - {{/link-to}} - {{/if}} -
-{{/if}} - + {{#if isSuspended}} +
+ {{fa-icon "ban"}} + {{i18n user.suspended_notice date="user.suspendedTillDate"}}
+ {{i18n user.suspended_reason}} {{user.suspend_reason}} +
+ {{else}} + {{#if user.bio_cooked}}
{{{user.bio_cooked}}}
{{/if}} + {{/if}} + {{#if user.card_badge}} {{#link-to 'badges.show' user.card_badge class="card-badge"}} {{icon-or-image user.card_badge.image}} {{/link-to}} {{/if}} + + + {{#if showBadges}} +
+ {{#each user.featured_user_badges}} + {{user-badge badge=badge}} + {{/each}} + {{#if showMoreBadges}} + {{#link-to 'user.badges' user class="btn more-user-badges"}} + {{i18n badges.more_badges count=moreBadgesCount}} + {{/link-to}} + {{/if}} +
+ {{/if}}
+ {{else}}

{{i18n loading}}

{{/if}} diff --git a/app/assets/stylesheets/desktop/user-card.scss b/app/assets/stylesheets/desktop/user-card.scss index 175110b4a0..25758df71d 100644 --- a/app/assets/stylesheets/desktop/user-card.scss +++ b/app/assets/stylesheets/desktop/user-card.scss @@ -14,9 +14,9 @@ min-height: 175px; .card-content { - padding: 12px; + padding: 12px 12px 0 12px; background: rgba($primary, .85); - margin-top: 85px; + margin-top: 80px; &:after { content: ''; @@ -53,6 +53,10 @@ a { color: $secondary; } + i { + font-size: 18px; + color: $secondary; + } } h2 { @@ -64,7 +68,6 @@ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; - display: inline; a { color: $secondary; } @@ -93,11 +96,12 @@ } .metadata { - position: absolute; - right: 20px; - top: 10px; - max-width: 180px; - text-align: right; + width: 100%; + clear: both; + h3 {display: inline; margin-right: 5px; } + div {display: inline; color: scale-color($primary, $lightness: 50%); + .group-link {color: scale-color($primary, $lightness: 50%);} + } } .bottom { @@ -106,11 +110,8 @@ } .bio { - max-height: 60px; - overflow: hidden; - float: left; - margin: 10px 0; - width: 70%; + padding: 15px 0 0 0; + clear: left; a { color: $secondary; text-decoration: underline; @@ -118,12 +119,17 @@ img { max-width: 100%; } + + .overflow { + max-height: 60px; + overflow: hidden; + } } img.avatar { float: left; padding-right: 10px; - margin-top: -60px; + margin-top: -53px; } p { @@ -142,9 +148,14 @@ .usercard-controls { list-style-type: none; - margin: 0; - position: absolute; - right: 12px; + float: right; + + &:after { + content: ''; + display: block; + clear: both; + + } a {width: 100%; min-width: 150px;} } @@ -161,6 +172,7 @@ height: 60px; position: relative; width: 45%; + margin-top: 11px; span { position: absolute; @@ -172,13 +184,15 @@ } .badge-section { - margin-top: 10px; float: left; - width: 390px; + width: 500px; + padding-bottom: 10px; + margin-top: 5px; .user-badge { background: transparent; - color: $secondary; + color: scale-color($primary, $lightness: 50%); + border-color: scale-color($primary, $lightness: 50%); } From a54e33cd6b93a363aced52777baeff216d62d123 Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Sat, 1 Nov 2014 21:21:16 -0700 Subject: [PATCH 24/83] FIX: mobile date was pushed offscreen, w/ h-scroll --- app/assets/stylesheets/desktop/discourse.scss | 1 - app/assets/stylesheets/mobile/discourse.scss | 3 +-- app/assets/stylesheets/mobile/topic-post.scss | 6 +----- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/app/assets/stylesheets/desktop/discourse.scss b/app/assets/stylesheets/desktop/discourse.scss index ac611113ad..d346a4fec1 100644 --- a/app/assets/stylesheets/desktop/discourse.scss +++ b/app/assets/stylesheets/desktop/discourse.scss @@ -33,7 +33,6 @@ body { .boxed { height: 100%; - @include border-radius-all(5px); &.white { background-color: $secondary; } diff --git a/app/assets/stylesheets/mobile/discourse.scss b/app/assets/stylesheets/mobile/discourse.scss index 12262ba751..af61f9c35e 100644 --- a/app/assets/stylesheets/mobile/discourse.scss +++ b/app/assets/stylesheets/mobile/discourse.scss @@ -10,9 +10,8 @@ body { .boxed { height: 100%; - @include border-radius-all(5px); .contents { - padding: 10px 10px 10px 10px; + padding: 10px; } &.white { background-color: $secondary; diff --git a/app/assets/stylesheets/mobile/topic-post.scss b/app/assets/stylesheets/mobile/topic-post.scss index 19786feeb8..c605a3f2dc 100644 --- a/app/assets/stylesheets/mobile/topic-post.scss +++ b/app/assets/stylesheets/mobile/topic-post.scss @@ -364,10 +364,6 @@ iframe { display: none; } -.topic-meta-data { - margin-bottom: 10px; -} - .open>.dropdown-menu { display: block; } @@ -473,7 +469,7 @@ span.highlighted { margin-left: 60px; white-space: nowrap; position: absolute; - width: 100%; + width: 80%; left: 0px; .names { margin: 5px 0 0 5px; From 6f391052dc990bb0cabffb612ec4df2d5464bbd8 Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Sat, 1 Nov 2014 21:30:37 -0700 Subject: [PATCH 25/83] mobile user profile page tweak --- app/assets/stylesheets/mobile/user.scss | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app/assets/stylesheets/mobile/user.scss b/app/assets/stylesheets/mobile/user.scss index cf8863a2fb..aabdf5e264 100644 --- a/app/assets/stylesheets/mobile/user.scss +++ b/app/assets/stylesheets/mobile/user.scss @@ -343,15 +343,12 @@ .controls { width: 40%; + float:right; ul {list-style-type: none;} a { padding: 5px 10px; width: 140px; margin-bottom: 10px; - } - - .right { - float: right; } } } From 4e3a8b87e3d4debf6a71b723fa6728b859a174df Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Sat, 1 Nov 2014 21:44:56 -0700 Subject: [PATCH 26/83] mobile user profile page tweak --- app/assets/stylesheets/mobile/user.scss | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/mobile/user.scss b/app/assets/stylesheets/mobile/user.scss index aabdf5e264..7acd33b644 100644 --- a/app/assets/stylesheets/mobile/user.scss +++ b/app/assets/stylesheets/mobile/user.scss @@ -342,9 +342,12 @@ } .controls { - width: 40%; + width: 55%; float:right; - ul {list-style-type: none;} + ul { + list-style-type: none; + margin: 0; + } a { padding: 5px 10px; width: 140px; From 4d82a571fb57ce930ac66792acaa5d1e358ce9a3 Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 3 Nov 2014 11:23:00 +1100 Subject: [PATCH 27/83] FIX: restrict the size of small tags (when nested) --- app/assets/stylesheets/common/base/discourse.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/assets/stylesheets/common/base/discourse.scss b/app/assets/stylesheets/common/base/discourse.scss index 4d2e4137c8..83313f727f 100644 --- a/app/assets/stylesheets/common/base/discourse.scss +++ b/app/assets/stylesheets/common/base/discourse.scss @@ -15,6 +15,10 @@ big { font-size: 28px; } +small { + font-size: 10px; +} + blockquote { background-color: scale-color-diff(); border-left: 5px solid darken(scale-color-diff(), 10%); From 37dbc4b5e6a1d5693a9c1a4c5bfc5e32620b61f7 Mon Sep 17 00:00:00 2001 From: riking Date: Sun, 2 Nov 2014 16:51:23 -0800 Subject: [PATCH 28/83] Add archive.org to crawler list to serve no-js to --- lib/crawler_detection.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/crawler_detection.rb b/lib/crawler_detection.rb index c1bdc9e75d..d82cf443f6 100644 --- a/lib/crawler_detection.rb +++ b/lib/crawler_detection.rb @@ -1,5 +1,5 @@ module CrawlerDetection def self.crawler?(user_agent) - !/Googlebot|Mediapartners|AdsBot|curl|Twitterbot|facebookexternalhit|bingbot|Baiduspider/.match(user_agent).nil? + !/Googlebot|Mediapartners|AdsBot|curl|Twitterbot|facebookexternalhit|bingbot|Baiduspider|ia_archiver/.match(user_agent).nil? end end From b09ad870980b4ec29a0f3497680eda7778c4a11e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Mon, 3 Nov 2014 12:46:08 +0100 Subject: [PATCH 29/83] FIX: add 'show emails' button from moderators in user admin section --- .../admin/controllers/admin-users-list.js.es6 | 8 +++++-- .../admin/templates/users_list.hbs | 14 +++++++++---- .../stylesheets/common/admin/admin_base.scss | 4 ++++ app/controllers/admin/users_controller.rb | 10 +++++++-- app/serializers/admin_user_serializer.rb | 2 +- app/services/staff_action_logger.rb | 16 +++++++++++++- config/locales/client.en.yml | 1 + lib/admin_user_index_query.rb | 1 + lib/guardian.rb | 6 ++++++ .../admin/users_controller_spec.rb | 21 +++++++++++++++++++ 10 files changed, 73 insertions(+), 10 deletions(-) diff --git a/app/assets/javascripts/admin/controllers/admin-users-list.js.es6 b/app/assets/javascripts/admin/controllers/admin-users-list.js.es6 index 00940ddfb9..36209fc212 100644 --- a/app/assets/javascripts/admin/controllers/admin-users-list.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-users-list.js.es6 @@ -90,11 +90,11 @@ export default Ember.ArrayController.extend(Discourse.Presence, { @method refreshUsers **/ - refreshUsers: function() { + refreshUsers: function(showEmails) { var adminUsersListController = this; adminUsersListController.set('loading', true); - Discourse.AdminUser.findAll(this.get('query'), { filter: this.get('username') }).then(function (result) { + Discourse.AdminUser.findAll(this.get('query'), { filter: this.get('username'), show_emails: showEmails }).then(function (result) { adminUsersListController.set('content', result); adminUsersListController.set('loading', false); }); @@ -140,6 +140,10 @@ export default Ember.ArrayController.extend(Discourse.Presence, { bootbox.alert(message); controller.refreshUsers(); }); + }, + + showEmails: function() { + this.refreshUsers(true); } }); diff --git a/app/assets/javascripts/admin/templates/users_list.hbs b/app/assets/javascripts/admin/templates/users_list.hbs index 285182aebf..c9d760bcdb 100644 --- a/app/assets/javascripts/admin/templates/users_list.hbs +++ b/app/assets/javascripts/admin/templates/users_list.hbs @@ -28,9 +28,14 @@
{{/if}} -

{{title}}

- -
+
+
+

{{title}}

+
+
+ +
+
{{#if loading}}
@@ -43,6 +48,7 @@ {{/if}}   {{i18n username}} + {{i18n email}} {{i18n admin.users.last_emailed}} {{i18n last_seen}} {{i18n admin.user.topics_entered}} @@ -53,7 +59,6 @@ {{i18n admin.users.approved}} {{/if}}   - {{#each model}} @@ -67,6 +72,7 @@ {{/if}} {{#link-to 'adminUser' this}}{{avatar this imageSize="small"}}{{/link-to}} {{#link-to 'adminUser' this}}{{unbound username}}{{/link-to}} + {{{unbound email}}} {{{unbound last_emailed_age}}} {{{unbound last_seen_age}}} {{{unbound topics_entered}}} diff --git a/app/assets/stylesheets/common/admin/admin_base.scss b/app/assets/stylesheets/common/admin/admin_base.scss index ce887d58f5..79335b2fd0 100644 --- a/app/assets/stylesheets/common/admin/admin_base.scss +++ b/app/assets/stylesheets/common/admin/admin_base.scss @@ -89,6 +89,10 @@ td.flaggers td { margin-top: 20px; } +.admin-title { + height: 45px; +} + .admin-controls { background-color: dark-light-diff($primary, $secondary, 90%, -75%); padding: 10px 10px 3px 0; diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index e6750c2b19..ed69ad463f 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -25,8 +25,14 @@ class Admin::UsersController < Admin::AdminController :revoke_api_key] def index - query = ::AdminUserIndexQuery.new(params) - render_serialized(query.find_users, AdminUserSerializer) + users = ::AdminUserIndexQuery.new(params).find_users + + if params[:show_emails] == "true" + guardian.can_see_emails = true + StaffActionLogger.new(current_user).log_show_emails(users) + end + + render_serialized(users, AdminUserSerializer) end def show diff --git a/app/serializers/admin_user_serializer.rb b/app/serializers/admin_user_serializer.rb index 461bf67d20..4c9f309ce9 100644 --- a/app/serializers/admin_user_serializer.rb +++ b/app/serializers/admin_user_serializer.rb @@ -39,7 +39,7 @@ class AdminUserSerializer < BasicUserSerializer def include_email? # staff members can always see their email - scope.is_staff? && object.id == scope.user.id + (scope.is_staff? && object.id == scope.user.id) || scope.can_see_emails? end alias_method :include_associated_accounts?, :include_email? diff --git a/app/services/staff_action_logger.rb b/app/services/staff_action_logger.rb index bc99302b9b..509328f333 100644 --- a/app/services/staff_action_logger.rb +++ b/app/services/staff_action_logger.rb @@ -142,10 +142,24 @@ class StaffActionLogger })) end + def log_show_emails(users) + values = [] + + users.each do |user| + values << "(#{@admin.id}, #{UserHistory.actions[:check_email]}, #{user.id}, current_timestamp, current_timestamp)" + end + + # bulk insert + UserHistory.exec_sql <<-SQL + INSERT INTO user_histories (acting_user_id, action, target_user_id, created_at, updated_at) + VALUES #{values.join(",")} + SQL + end + private def params(opts) - {acting_user_id: @admin.id, context: opts[:context]} + { acting_user_id: @admin.id, context: opts[:context] } end end diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 2a74b7e102..376f1a4d1e 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -1861,6 +1861,7 @@ en: last_emailed: "Last Emailed" not_found: "Sorry, that username doesn't exist in our system." active: "Active" + show_emails: "Show Emails" nav: new: "New" active: "Active" diff --git a/lib/admin_user_index_query.rb b/lib/admin_user_index_query.rb index 6376640e0a..cf20f2103c 100644 --- a/lib/admin_user_index_query.rb +++ b/lib/admin_user_index_query.rb @@ -78,6 +78,7 @@ class AdminUserIndexQuery .includes(:github_user_info) .includes(:google_user_info) .includes(:oauth2_user_info) + .includes(:user_open_ids) .take(100) end end diff --git a/lib/guardian.rb b/lib/guardian.rb index 5fab43dfd4..f0998e0577 100644 --- a/lib/guardian.rb +++ b/lib/guardian.rb @@ -26,6 +26,8 @@ class Guardian def email; nil; end end + attr_accessor :can_see_emails + def initialize(user=nil) @user = user.presence || AnonymousUser.new end @@ -243,6 +245,10 @@ class Guardian (is_staff? || target.is_a?(Group) || !target.suspended?) end + def can_see_emails? + @can_see_emails + end + private def is_my_own?(obj) diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb index 3482d02201..767dc021f1 100644 --- a/spec/controllers/admin/users_controller_spec.rb +++ b/spec/controllers/admin/users_controller_spec.rb @@ -22,6 +22,27 @@ describe Admin::UsersController do xhr :get, :index ::JSON.parse(response.body).should be_present end + + context 'when showing emails' do + + it "returns email for all the users" do + xhr :get, :index, show_emails: "true" + data = ::JSON.parse(response.body) + data.each do |user| + user["email"].should be_present + end + end + + it "logs an enty for all email shown" do + UserHistory.where(action: UserHistory.actions[:check_email], acting_user_id: @user.id).count.should == 0 + + xhr :get, :index, show_emails: "true" + data = ::JSON.parse(response.body) + + UserHistory.where(action: UserHistory.actions[:check_email], acting_user_id: @user.id).count.should == data.length + end + + end end describe '.show' do From 52b3877b7045bc5d4840f09ef10071154c3e528e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Mon, 3 Nov 2014 15:31:11 +0100 Subject: [PATCH 30/83] FIX: don't create a new revision when there was an error while saving the post and/or topic --- lib/post_destroyer.rb | 6 ------ lib/post_revisor.rb | 20 +++++++++----------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/lib/post_destroyer.rb b/lib/post_destroyer.rb index 2835739554..9bba2444d4 100644 --- a/lib/post_destroyer.rb +++ b/lib/post_destroyer.rb @@ -101,9 +101,6 @@ class PostDestroyer @post.update_flagged_posts_count @post.topic_links.each(&:destroy) end - - # covered by PostRevisor - # @post.publish_change_to_clients! :revised end def user_recovered @@ -113,9 +110,6 @@ class PostDestroyer @post.revise(@user, { raw: @post.revisions.last.modifications["raw"][0] }, force_new_version: true) @post.update_flagged_posts_count end - - # covered by PostRevisor - # @post.publish_change_to_clients! :revised end diff --git a/lib/post_revisor.rb b/lib/post_revisor.rb index e100f4df0c..cb616389b3 100644 --- a/lib/post_revisor.rb +++ b/lib/post_revisor.rb @@ -67,7 +67,7 @@ class PostRevisor publish_changes grant_badge - @post_successfully_saved && @topic_successfully_saved + successfully_saved_post_and_topic end def cleanup_whitespaces(raw) @@ -93,11 +93,7 @@ class PostRevisor end def revise_post - if should_create_new_version? - revise_and_create_new_version - else - revise - end + should_create_new_version? ? revise_and_create_new_version : revise end def should_create_new_version? @@ -178,11 +174,9 @@ class PostRevisor end def create_or_update_revision - if @version_changed - create_revision - else - update_revision - end + # don't create an empty revision if something failed + return unless successfully_saved_post_and_topic + @version_changed ? create_revision : update_revision end def create_revision @@ -305,4 +299,8 @@ class PostRevisor BadgeGranter.queue_badge_grant(Badge::Trigger::PostRevision, post: @post) end + def successfully_saved_post_and_topic + @post_successfully_saved && @topic_successfully_saved + end + end From 61ff64d0118df349c4ea3ad3c37aca779d98f3e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Fern=C3=A1ndez?= Date: Mon, 3 Nov 2014 13:04:51 -0200 Subject: [PATCH 31/83] Resfactor: improve syntax extended user stats in user export csv --- app/jobs/regular/export_csv_file.rb | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/app/jobs/regular/export_csv_file.rb b/app/jobs/regular/export_csv_file.rb index ae4f58d8f1..dad9ee5540 100644 --- a/app/jobs/regular/export_csv_file.rb +++ b/app/jobs/regular/export_csv_file.rb @@ -26,10 +26,7 @@ module Jobs user_data.each do |user| user_array = Array.new group_names = get_group_names(user).join(';') - user_array.push(user['id']).push(user['name']).push(user['username']).push(user['email']).push(user['created_at']) - .push(user.user_stat['topics_entered']).push(user.user_stat['posts_read_count']).push(user.user_stat['time_read']) - .push(user.user_stat['topic_count']).push(user.user_stat['post_count']).push(user.user_stat['likes_given']) - .push(user.user_stat['likes_received']) + user_array = get_user_fields(user) user_array.push(group_names) if group_names != '' data.push(user_array) end @@ -54,6 +51,22 @@ module Jobs return group_names end + def get_user_fields(user) + csv_user_attrs = ['id','name','username','email','created_at'] + csv_user_stats_attr = ['topics_entered','posts_read_count','time_read','topic_count','post_count','likes_given','likes_received'] + user_array = [] + + csv_user_attrs.each do |user_attr| + user_array.push(user.attributes[user_attr]) + end + + csv_user_stats_attr.each do |user_stat_attr| + user_array.push(user.user_stat.attributes[user_stat_attr]) + end + + return user_array + end + def set_file_path @file_name = "export_#{SecureRandom.hex(4)}.csv" # ensure directory exists From 6ab7bfee14ffa01781d9537abd80e47e6dd14c89 Mon Sep 17 00:00:00 2001 From: Kris Aubuchon Date: Mon, 3 Nov 2014 10:16:10 -0500 Subject: [PATCH 32/83] fixing ban display on usercard and IE11 issue with user page controls --- app/assets/stylesheets/desktop/user-card.scss | 5 ++++- app/assets/stylesheets/desktop/user.scss | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/desktop/user-card.scss b/app/assets/stylesheets/desktop/user-card.scss index 25758df71d..0c474ff5d3 100644 --- a/app/assets/stylesheets/desktop/user-card.scss +++ b/app/assets/stylesheets/desktop/user-card.scss @@ -98,6 +98,7 @@ .metadata { width: 100%; clear: both; + padding-top: 5px; h3 {display: inline; margin-right: 5px; } div {display: inline; color: scale-color($primary, $lightness: 50%); .group-link {color: scale-color($primary, $lightness: 50%);} @@ -211,7 +212,9 @@ .suspended { color: $danger; - margin-bottom: 10px; + margin-bottom: 5px; + clear: left; + padding-top: 15px; } .card-badge { diff --git a/app/assets/stylesheets/desktop/user.scss b/app/assets/stylesheets/desktop/user.scss index 44975240f0..6b43b1dd30 100644 --- a/app/assets/stylesheets/desktop/user.scss +++ b/app/assets/stylesheets/desktop/user.scss @@ -304,6 +304,7 @@ .controls { padding: 0 0 12px 0; float: right; + width: 180px; ul {list-style-type: none;} a { padding: 5px 10px; From fd5677808cea3b4868c6bfe508eeea9a26ed49a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Mon, 3 Nov 2014 16:57:50 +0100 Subject: [PATCH 33/83] SPEC: make sure digest doesn't pick any topics in categories that are muted --- app/controllers/categories_controller.rb | 2 +- app/mailers/user_notifications.rb | 4 +- app/models/category_user.rb | 50 +++++++++++------------- spec/models/topic_spec.rb | 10 +++++ 4 files changed, 34 insertions(+), 32 deletions(-) diff --git a/app/controllers/categories_controller.rb b/app/controllers/categories_controller.rb index f9b8b6454c..c144648130 100644 --- a/app/controllers/categories_controller.rb +++ b/app/controllers/categories_controller.rb @@ -99,7 +99,7 @@ class CategoriesController < ApplicationController category_id = params[:category_id].to_i notification_level = params[:notification_level].to_i - CategoryUser.set_notification_level_for_category(current_user, notification_level , category_id) + CategoryUser.set_notification_level_for_category(current_user, notification_level, category_id) render json: success_json end diff --git a/app/mailers/user_notifications.rb b/app/mailers/user_notifications.rb index c76dbf2db1..476a090392 100644 --- a/app/mailers/user_notifications.rb +++ b/app/mailers/user_notifications.rb @@ -250,9 +250,7 @@ class UserNotifications < ActionMailer::Base ) template = "user_notifications.user_#{notification_type}" - if post.topic.private_message? - template << "_pm" - end + template << "_pm" if post.topic.private_message? email_opts = { topic_title: title, diff --git a/app/models/category_user.rb b/app/models/category_user.rb index 4ba4a130d1..d53fb804e2 100644 --- a/app/models/category_user.rb +++ b/app/models/category_user.rb @@ -16,19 +16,17 @@ class CategoryUser < ActiveRecord::Base end def self.auto_track_new_topic(topic) - apply_default_to_topic( - topic, - TopicUser.notification_levels[:tracking], - TopicUser.notification_reasons[:auto_track_category] - ) + apply_default_to_topic(topic, + TopicUser.notification_levels[:tracking], + TopicUser.notification_reasons[:auto_track_category] + ) end def self.auto_watch_new_topic(topic) - apply_default_to_topic( - topic, - TopicUser.notification_levels[:watching], - TopicUser.notification_reasons[:auto_watch_category] - ) + apply_default_to_topic(topic, + TopicUser.notification_levels[:watching], + TopicUser.notification_reasons[:auto_watch_category] + ) end def self.batch_set(user, level, category_ids) @@ -49,8 +47,6 @@ class CategoryUser < ActiveRecord::Base def self.set_notification_level_for_category(user, level, category_id) record = CategoryUser.where(user: user, category_id: category_id).first - # oder CategoryUser.where(user: user, category_id: category_id).destroy_all - # und danach mir create anlegen. if record.present? record.notification_level = level @@ -62,23 +58,21 @@ class CategoryUser < ActiveRecord::Base def self.apply_default_to_topic(topic, level, reason) # Can not afford to slow down creation of topics when a pile of users are watching new topics, reverting to SQL for max perf here - sql = < Date: Mon, 3 Nov 2014 11:59:29 -0500 Subject: [PATCH 34/83] FIX: Trim the profile text in the user card even if it has multiple lines. --- .../discourse/components/text-overflow.js.es6 | 9 ++++++++ .../discourse/templates/user-card.hbs | 7 ++---- app/assets/javascripts/vendor.js | 1 + app/assets/stylesheets/desktop/user-card.scss | 2 +- .../jquery.autoellipsis-1.0.10.min.js | 23 +++++++++++++++++++ 5 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 app/assets/javascripts/discourse/components/text-overflow.js.es6 create mode 100644 vendor/assets/javascripts/jquery.autoellipsis-1.0.10.min.js diff --git a/app/assets/javascripts/discourse/components/text-overflow.js.es6 b/app/assets/javascripts/discourse/components/text-overflow.js.es6 new file mode 100644 index 0000000000..ae4e2a8259 --- /dev/null +++ b/app/assets/javascripts/discourse/components/text-overflow.js.es6 @@ -0,0 +1,9 @@ +export default Ember.Component.extend({ + _parse: function() { + this.$().ellipsis(); + }.on('didInsertElement'), + + render: function(buffer) { + buffer.push(this.get('text')); + } +}); diff --git a/app/assets/javascripts/discourse/templates/user-card.hbs b/app/assets/javascripts/discourse/templates/user-card.hbs index 49b1830748..5387d52e5f 100644 --- a/app/assets/javascripts/discourse/templates/user-card.hbs +++ b/app/assets/javascripts/discourse/templates/user-card.hbs @@ -49,7 +49,7 @@ {{i18n user.suspended_reason}} {{user.suspend_reason}}
{{else}} - {{#if user.bio_cooked}}
{{{user.bio_cooked}}}
{{/if}} + {{#if user.bio_cooked}}
{{text-overflow class="overflow" text=user.bio_cooked}}
{{/if}} {{/if}} {{#if user.card_badge}} @@ -62,11 +62,8 @@

{{i18n last_post}} {{format-date path="user.last_posted_at" leaveAgo="true"}}

{{i18n joined}} {{format-date path="user.created_at" leaveAgo="true"}}

{{#if user.custom_groups}} - -

{{groups-list groups=user.custom_groups}}

- +

{{groups-list groups=user.custom_groups}}

{{/if}} - {{#if showBadges}} diff --git a/app/assets/javascripts/vendor.js b/app/assets/javascripts/vendor.js index 4e769ed9ba..a4258f2003 100644 --- a/app/assets/javascripts/vendor.js +++ b/app/assets/javascripts/vendor.js @@ -40,4 +40,5 @@ //= require ember-cloaking //= require break_string //= require buffered-proxy +//= require jquery.autoellipsis-1.0.10.min.js //= require_tree ./discourse/ember diff --git a/app/assets/stylesheets/desktop/user-card.scss b/app/assets/stylesheets/desktop/user-card.scss index 0c474ff5d3..a34230564a 100644 --- a/app/assets/stylesheets/desktop/user-card.scss +++ b/app/assets/stylesheets/desktop/user-card.scss @@ -124,7 +124,7 @@ .overflow { max-height: 60px; overflow: hidden; - } + } } img.avatar { diff --git a/vendor/assets/javascripts/jquery.autoellipsis-1.0.10.min.js b/vendor/assets/javascripts/jquery.autoellipsis-1.0.10.min.js new file mode 100644 index 0000000000..ffc82eb436 --- /dev/null +++ b/vendor/assets/javascripts/jquery.autoellipsis-1.0.10.min.js @@ -0,0 +1,23 @@ +/*! + + Copyright (c) 2011 Peter van der Spek + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + */(function(a){function m(){if(!d){d=!0;for(var c in b)a(c).each(function(){var d,e;d=a(this),e=d.data("jqae"),(e.containerWidth!=d.width()||e.containerHeight!=d.height())&&f(d,b[c])});d=!1}}function l(a){b[a]&&(delete b[a],b.length||c&&(window.clearInterval(c),c=undefined))}function k(a,d){b[a]=d,c||(c=window.setInterval(function(){m()},200))}function j(){return this.nodeType===3}function i(b){if(b.contents().length){var c=b.contents(),d=c.eq(c.length-1);if(d.filter(j).length){var e=d.get(0).nodeValue;e=a.trim(e);if(e==""){d.remove();return!0}return!1}while(i(d));if(d.contents().length)return!1;d.remove();return!0}return!1}function h(a){if(a.contents().length){var b=a.contents(),c=b.eq(b.length-1);return c.filter(j).length?c:h(c)}a.append("");var b=a.contents();return b.eq(b.length-1)}function g(b){var c=h(b);if(c.length){var d=c.get(0).nodeValue,e=d.lastIndexOf(" ");e>-1?(d=a.trim(d.substring(0,e)),c.get(0).nodeValue=d):c.get(0).nodeValue="";return!0}return!1}function f(b,c){var d=b.data("jqae");d||(d={});var e=d.wrapperElement;e||(e=b.wrapInner("
").find(">div"),e.css({margin:0,padding:0,border:0}));var f=e.data("jqae");f||(f={});var j=f.originalContent;j?e=f.originalContent.clone(!0).data("jqae",{originalContent:j}).replaceAll(e):e.data("jqae",{originalContent:e.clone(!0)}),b.data("jqae",{wrapperElement:e,containerWidth:b.width(),containerHeight:b.height()});var k=b.height(),l=(parseInt(b.css("padding-top"),10)||0)+(parseInt(b.css("border-top-width"),10)||0)-(e.offset().top-b.offset().top),m=!1,n=e;c.selector&&(n=a(e.find(c.selector).get().reverse())),n.each(function(){var b=a(this),d=b.text(),f=!1;if(e.innerHeight()-b.innerHeight()>k+l)b.remove();else{i(b);if(b.contents().length){m&&(h(b).get(0).nodeValue+=c.ellipsis,m=!1);while(e.innerHeight()>k+l){f=g(b);if(!f){m=!0,b.remove();break}i(b);if(b.contents().length)h(b).get(0).nodeValue+=c.ellipsis;else{m=!0,b.remove();break}}c.setTitle=="onEllipsis"&&f||c.setTitle=="always"?b.attr("title",d):c.setTitle!="never"&&b.removeAttr("title")}}})}var b={},c,d=!1,e={ellipsis:"...",setTitle:"never",live:!1};a.fn.ellipsis=function(b,c){var d,g;d=a(this),typeof b!="string"&&(c=b,b=undefined),g=a.extend({},e,c),g.selector=b,d.each(function(){var b=a(this);f(b,g)}),g.live?k(d.selector,g):l(d.selector);return this}})(jQuery) \ No newline at end of file From bdb78ce76a524f9eb05a0375c1d91c7a8ec3734e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Mon, 3 Nov 2014 19:54:10 +0100 Subject: [PATCH 35/83] FEATURE: consider SVG as an image when authorized --- .../javascripts/discourse/lib/utilities.js | 4 +-- app/models/optimized_image.rb | 11 ++++++-- app/models/upload.rb | 25 ++++++++++++----- spec/fixtures/images/image.svg | 3 +++ spec/models/upload_spec.rb | 27 ++++++++++++++++++- 5 files changed, 58 insertions(+), 12 deletions(-) create mode 100644 spec/fixtures/images/image.svg diff --git a/app/assets/javascripts/discourse/lib/utilities.js b/app/assets/javascripts/discourse/lib/utilities.js index 3ee9f2a981..2835ac843e 100644 --- a/app/assets/javascripts/discourse/lib/utilities.js +++ b/app/assets/javascripts/discourse/lib/utilities.js @@ -269,7 +269,7 @@ Discourse.Utilities = { @param {String} path The path **/ isAnImage: function(path) { - return (/\.(png|jpg|jpeg|gif|bmp|tif|tiff)$/i).test(path); + return (/\.(png|jpg|jpeg|gif|bmp|tif|tiff|svg|webp)$/i).test(path); }, /** @@ -279,7 +279,7 @@ Discourse.Utilities = { **/ allowsAttachments: function() { return Discourse.Utilities.authorizesAllExtensions() || - !(/((png|jpg|jpeg|gif|bmp|tif|tiff)(,\s)?)+$/i).test(Discourse.Utilities.authorizedExtensions()); + !(/((png|jpg|jpeg|gif|bmp|tif|tiff|svg|webp)(,\s)?)+$/i).test(Discourse.Utilities.authorizedExtensions()); }, displayErrorForUpload: function(data) { diff --git a/app/models/optimized_image.rb b/app/models/optimized_image.rb index 5f5ecb645d..7c7c42f53c 100644 --- a/app/models/optimized_image.rb +++ b/app/models/optimized_image.rb @@ -31,9 +31,16 @@ class OptimizedImage < ActiveRecord::Base extension = File.extname(original_path) temp_file = Tempfile.new(["discourse-thumbnail", extension]) temp_path = temp_file.path - original_path += "[0]" unless opts[:allow_animation] - if resize(original_path, temp_path, width, height) + if extension =~ /\.svg$/i + FileUtils.cp(original_path, temp_path) + resized = true + else + original_path << "[0]" unless opts[:allow_animation] + resized = resize(original_path, temp_path, width, height) + end + + if resized thumbnail = OptimizedImage.create!( upload_id: upload.id, sha1: Digest::SHA1.file(temp_path).hexdigest, diff --git a/app/models/upload.rb b/app/models/upload.rb index 8b6455769a..d9a5437df4 100644 --- a/app/models/upload.rb +++ b/app/models/upload.rb @@ -75,13 +75,24 @@ class Upload < ActiveRecord::Base # deal with width & height for images if FileHelper.is_image?(filename) begin - # fix orientation first - Upload.fix_image_orientation(file.path) - # retrieve image info - image_info = FastImage.new(file, raise_on_failure: true) - # compute image aspect ratio - upload.width, upload.height = ImageSizer.resize(*image_info.size) - # make sure we're at the beginning of the file (FastImage moves the pointer) + if filename =~ /\.svg$/i + svg = Nokogiri::XML(file).at_css("svg") + width, height = svg["width"].to_i, svg["height"].to_i + if width == 0 || height == 0 + upload.errors.add(:base, I18n.t("upload.images.size_not_found")) + else + upload.width, upload.height = ImageSizer.resize(width, height) + end + else + # fix orientation first + Upload.fix_image_orientation(file.path) + # retrieve image info + image_info = FastImage.new(file, raise_on_failure: true) + # compute image aspect ratio + upload.width, upload.height = ImageSizer.resize(*image_info.size) + end + # make sure we're at the beginning of the file + # (FastImage and Nokogiri move the pointer) file.rewind rescue FastImage::ImageFetchFailure upload.errors.add(:base, I18n.t("upload.images.fetch_failure")) diff --git a/spec/fixtures/images/image.svg b/spec/fixtures/images/image.svg new file mode 100644 index 0000000000..b95c0f4a79 --- /dev/null +++ b/spec/fixtures/images/image.svg @@ -0,0 +1,3 @@ + + Discourse + diff --git a/spec/models/upload_spec.rb b/spec/models/upload_spec.rb index d55b0df42a..573d53191a 100644 --- a/spec/models/upload_spec.rb +++ b/spec/models/upload_spec.rb @@ -15,11 +15,15 @@ describe Upload do let(:user_id) { 1 } let(:url) { "http://domain.com" } - let(:image) { file_from_fixtures("logo.png") } let(:image_filename) { "logo.png" } + let(:image) { file_from_fixtures(image_filename) } let(:image_filesize) { File.size(image) } let(:image_sha1) { Digest::SHA1.file(image).hexdigest } + let(:image_svg_filename) { "image.svg" } + let(:image_svg) { file_from_fixtures(image_svg_filename) } + let(:image_svg_filesize) { File.size(image_svg) } + let(:attachment_path) { __FILE__ } let(:attachment) { File.new(attachment_path) } let(:attachment_filename) { File.basename(attachment_path) } @@ -96,6 +100,27 @@ describe Upload do upload.url.should == url end + context "when svg is authorized" do + + before { SiteSetting.stubs(:authorized_extensions).returns("svg") } + + it "consider SVG as an image" do + store = {} + Discourse.expects(:store).returns(store) + store.expects(:store_upload).returns(url) + + upload = Upload.create_for(user_id, image_svg, image_svg_filename, image_svg_filesize) + + upload.user_id.should == user_id + upload.original_filename.should == image_svg_filename + upload.filesize.should == image_svg_filesize + upload.width.should == 100 + upload.height.should == 50 + upload.url.should == url + end + + end + end context ".get_from_url" do From 407f3abffb55d63321dc3100e6f11401718b5f0c Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Mon, 3 Nov 2014 13:56:10 -0500 Subject: [PATCH 36/83] Simple API for registering custom emoji --- plugins/emoji/assets/javascripts/emoji.js.erb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/plugins/emoji/assets/javascripts/emoji.js.erb b/plugins/emoji/assets/javascripts/emoji.js.erb index 8e3ce36003..cceea054b0 100644 --- a/plugins/emoji/assets/javascripts/emoji.js.erb +++ b/plugins/emoji/assets/javascripts/emoji.js.erb @@ -1,10 +1,20 @@ (function() { var emoji = <%= Dir.glob(File.expand_path("../../../public/images/*.png", __FILE__)).map{|f| File.basename(f).split(".")[0]}.inspect %>; + var _extendedEmoji = {}; + Discourse.Dialect.registerEmoji = function(code, url) { + _extendedEmoji[code] = url; + }; + function imageFor(code) { if (emoji.indexOf(code) !== -1) { var url = Discourse.getURL('/plugins/emoji/images/' + code + '.png'); return ['img', {href: url, title: ':' + code + ':', 'class': 'emoji', alt: code}]; + } else { + var url = _extendedEmoji[code]; + if (url) { + return ['img', {href: url, title: ':' + code + ':', 'class': 'emoji', alt: code}]; + } } } From 2c7b47fc40a5c45534c3783d5261212232704aa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Mon, 3 Nov 2014 20:42:13 +0100 Subject: [PATCH 37/83] fix the build --- app/models/optimized_image.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/optimized_image.rb b/app/models/optimized_image.rb index 7c7c42f53c..137679455f 100644 --- a/app/models/optimized_image.rb +++ b/app/models/optimized_image.rb @@ -36,7 +36,6 @@ class OptimizedImage < ActiveRecord::Base FileUtils.cp(original_path, temp_path) resized = true else - original_path << "[0]" unless opts[:allow_animation] resized = resize(original_path, temp_path, width, height) end @@ -80,6 +79,7 @@ class OptimizedImage < ActiveRecord::Base end def self.resize(from, to, width, height) + from << "[0]" unless opts[:allow_animation] # NOTE: ORDER is important! instructions = %W{ #{from} From f361adef449e0a3ea88dcdeaf82e6cb328ef14d3 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Mon, 3 Nov 2014 15:26:06 -0500 Subject: [PATCH 38/83] Make plugin load order deterministic --- lib/plugin/instance.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/plugin/instance.rb b/lib/plugin/instance.rb index bedda0757a..a3e008d734 100644 --- a/lib/plugin/instance.rb +++ b/lib/plugin/instance.rb @@ -11,7 +11,7 @@ class Plugin::Instance def self.find_all(parent_path) [].tap { |plugins| # also follows symlinks - http://stackoverflow.com/q/357754 - Dir["#{parent_path}/**/*/**/plugin.rb"].each do |path| + Dir["#{parent_path}/**/*/**/plugin.rb"].sort.each do |path| source = File.read(path) metadata = Plugin::Metadata.parse(source) plugins << self.new(metadata, path) From 862c8a19a31756a32a961970e79859e00c6f2d91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Mon, 3 Nov 2014 22:03:06 +0100 Subject: [PATCH 39/83] FEATURE: use img's title attribute in overlay information when provided --- app/models/optimized_image.rb | 6 ++--- lib/cooked_post_processor.rb | 4 +-- spec/components/cooked_post_processor_spec.rb | 27 +++++++++++++++++++ spec/fabricators/post_fabricator.rb | 4 +++ 4 files changed, 36 insertions(+), 5 deletions(-) diff --git a/app/models/optimized_image.rb b/app/models/optimized_image.rb index 137679455f..5df84700ea 100644 --- a/app/models/optimized_image.rb +++ b/app/models/optimized_image.rb @@ -36,7 +36,7 @@ class OptimizedImage < ActiveRecord::Base FileUtils.cp(original_path, temp_path) resized = true else - resized = resize(original_path, temp_path, width, height) + resized = resize(original_path, temp_path, width, height, opts[:allow_animation]) end if resized @@ -78,8 +78,8 @@ class OptimizedImage < ActiveRecord::Base end end - def self.resize(from, to, width, height) - from << "[0]" unless opts[:allow_animation] + def self.resize(from, to, width, height, allow_animation=false) + from << "[0]" unless allow_animation # NOTE: ORDER is important! instructions = %W{ #{from} diff --git a/lib/cooked_post_processor.rb b/lib/cooked_post_processor.rb index b58bc33797..57af1276d5 100644 --- a/lib/cooked_post_processor.rb +++ b/lib/cooked_post_processor.rb @@ -181,9 +181,9 @@ class CookedPostProcessor informations = "#{original_width}x#{original_height}" informations << " #{number_to_human_size(upload.filesize)}" if upload - a["title"] = filename + a["title"] = img["title"] || filename - meta.add_child create_span_node("filename", filename) + meta.add_child create_span_node("filename", img["title"] || filename) meta.add_child create_span_node("informations", informations) meta.add_child create_span_node("expand") end diff --git a/spec/components/cooked_post_processor_spec.rb b/spec/components/cooked_post_processor_spec.rb index fd8ba3a733..ae90bf3569 100644 --- a/spec/components/cooked_post_processor_spec.rb +++ b/spec/components/cooked_post_processor_spec.rb @@ -98,6 +98,33 @@ describe CookedPostProcessor do end + context "with title" do + + let(:upload) { Fabricate(:upload) } + let(:post) { build(:post_with_large_image_and_title) } + let(:cpp) { CookedPostProcessor.new(post) } + + before do + SiteSetting.max_image_height = 2000 + SiteSetting.create_thumbnails = true + + Upload.expects(:get_from_url).returns(upload) + FastImage.stubs(:size).returns([1000, 2000]) + + # hmmm this should be done in a cleaner way + OptimizedImage.expects(:resize).returns(true) + end + + it "generates overlay information" do + cpp.post_process_images + cpp.html.should match_html '' + cpp.should be_dirty + end + + end + context "topic image" do let(:topic) { build(:topic, id: 1) } diff --git a/spec/fabricators/post_fabricator.rb b/spec/fabricators/post_fabricator.rb index 2ff3bb7921..cab02e02ed 100644 --- a/spec/fabricators/post_fabricator.rb +++ b/spec/fabricators/post_fabricator.rb @@ -76,6 +76,10 @@ Fabricator(:post_with_large_image, from: :post) do cooked '' end +Fabricator(:post_with_large_image_and_title, from: :post) do + cooked '' +end + Fabricator(:post_with_uploads, from: :post) do cooked ' Link From 558f9d4e6e34b68837ff8043926e4bacd9f76da7 Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Mon, 3 Nov 2014 13:58:54 -0800 Subject: [PATCH 40/83] shorten user filter removal copy --- config/locales/client.en.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 376f1a4d1e..8c9ea3cd18 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -1003,7 +1003,7 @@ en: n_posts: one: "1 post" other: "{{count}} posts" - cancel: "Show all posts in this topic again." + cancel: "Remove filter" split_topic: title: "Move to New Topic" From e14e2b815834857b199c25e527f870d84e1e6559 Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 4 Nov 2014 13:33:26 +1100 Subject: [PATCH 41/83] remove custom groups, they are not needed --- app/assets/javascripts/discourse/templates/user-card.hbs | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/assets/javascripts/discourse/templates/user-card.hbs b/app/assets/javascripts/discourse/templates/user-card.hbs index 5387d52e5f..f81ec37ea7 100644 --- a/app/assets/javascripts/discourse/templates/user-card.hbs +++ b/app/assets/javascripts/discourse/templates/user-card.hbs @@ -61,8 +61,6 @@ From 95c518329a077997cd859a36fe3558dae325890b Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 4 Nov 2014 13:33:58 +1100 Subject: [PATCH 42/83] FEATURE: display github profile links on user card and profile Can be disabled by unchecking "public_github_screen_name" --- app/assets/javascripts/discourse/models/user.js | 10 +++++++++- .../javascripts/discourse/templates/user-card.hbs | 2 ++ .../javascripts/discourse/templates/user/user.hbs | 4 ++++ app/assets/stylesheets/desktop/user.scss | 3 +++ app/assets/stylesheets/mobile/user.scss | 3 +++ app/serializers/user_serializer.rb | 8 +++++++- config/locales/server.en.yml | 1 + config/site_settings.yml | 1 + 8 files changed, 30 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/discourse/models/user.js b/app/assets/javascripts/discourse/models/user.js index 7088433927..f7be1f75a4 100644 --- a/app/assets/javascripts/discourse/models/user.js +++ b/app/assets/javascripts/discourse/models/user.js @@ -437,7 +437,15 @@ Discourse.User = Discourse.Model.extend({ }); } }, function () {}); - } + }, + + githubProfileUrl: function(){ + + var screenName = this.get('github_screen_name'); + if(screenName) { + return "https://github.com/" + screenName; + } + }.property() }); diff --git a/app/assets/javascripts/discourse/templates/user-card.hbs b/app/assets/javascripts/discourse/templates/user-card.hbs index f81ec37ea7..5b90e0d6a9 100644 --- a/app/assets/javascripts/discourse/templates/user-card.hbs +++ b/app/assets/javascripts/discourse/templates/user-card.hbs @@ -61,6 +61,8 @@ diff --git a/app/assets/javascripts/discourse/templates/user/user.hbs b/app/assets/javascripts/discourse/templates/user/user.hbs index 5659561b6d..0eb73f8d95 100644 --- a/app/assets/javascripts/discourse/templates/user/user.hbs +++ b/app/assets/javascripts/discourse/templates/user/user.hbs @@ -150,6 +150,10 @@ {{/if}} {{/if}} + {{#if github_screen_name}} +
GitHub
+
{{github_screen_name}}
+ {{/if}} {{plugin-outlet "user-profile-secondary"}}
diff --git a/app/assets/stylesheets/desktop/user.scss b/app/assets/stylesheets/desktop/user.scss index 6b43b1dd30..bf5823f702 100644 --- a/app/assets/stylesheets/desktop/user.scss +++ b/app/assets/stylesheets/desktop/user.scss @@ -215,6 +215,9 @@ overflow: hidden; text-overflow: ellipsis; color: $primary; + a { + color: $primary; + } } dt { diff --git a/app/assets/stylesheets/mobile/user.scss b/app/assets/stylesheets/mobile/user.scss index 7acd33b644..33071abf67 100644 --- a/app/assets/stylesheets/mobile/user.scss +++ b/app/assets/stylesheets/mobile/user.scss @@ -257,6 +257,9 @@ overflow: hidden; text-overflow: ellipsis; color: $primary; + a { + color: $primary; + } } dt { diff --git a/app/serializers/user_serializer.rb b/app/serializers/user_serializer.rb index 00d5f27dce..1b17b06eb8 100644 --- a/app/serializers/user_serializer.rb +++ b/app/serializers/user_serializer.rb @@ -48,7 +48,8 @@ class UserSerializer < BasicUserSerializer :has_title_badges, :edit_history_public, :custom_fields, - :user_fields + :user_fields, + :github_screen_name has_one :invited_by, embed: :object, serializer: BasicUserSerializer has_many :custom_groups, embed: :object, serializer: BasicGroupSerializer @@ -88,6 +89,11 @@ class UserSerializer < BasicUserSerializer ### ### ATTRIBUTES ### + def github_screen_name + if SiteSetting.public_github_screen_name + object.github_user_info && object.github_user_info.screen_name + end + end def include_email? object.id && object.id == scope.user.try(:id) diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 67a2df56b8..0dac266c15 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -808,6 +808,7 @@ en: facebook_app_secret: "App secret for Facebook authentication, registered at https://developers.facebook.com/apps" enable_github_logins: "Enable Github authentication, requires github_client_id and github_client_secret" + public_github_screen_name: "Display Github screen names publicly in the forum (on user page and profile)" github_client_id: "Client id for Github authentication, registered at https://github.com/settings/applications" github_client_secret: "Client secret for Github authentication, registered at https://github.com/settings/applications" diff --git a/config/site_settings.yml b/config/site_settings.yml index 2274aa2350..b97fae5f7f 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -212,6 +212,7 @@ login: enable_github_logins: client: true default: false + public_github_screen_name: true github_client_id: default: '' regex: "^[a-f0-9]*$" From 03c3221ea8be8f24fbeb30e41e81868b5791df44 Mon Sep 17 00:00:00 2001 From: Kris Aubuchon Date: Tue, 4 Nov 2014 10:57:30 -0500 Subject: [PATCH 43/83] quick fix on collapsed user profile controls --- app/assets/stylesheets/desktop/user.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/app/assets/stylesheets/desktop/user.scss b/app/assets/stylesheets/desktop/user.scss index bf5823f702..7df2422183 100644 --- a/app/assets/stylesheets/desktop/user.scss +++ b/app/assets/stylesheets/desktop/user.scss @@ -325,6 +325,7 @@ .about.collapsed-info { .controls { margin-top: 0; + width: auto; ul { li {display: inline;} a { From 1302a0a276ef714046f19aadae1c53e111c3b76c Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Tue, 4 Nov 2014 12:19:35 -0500 Subject: [PATCH 44/83] Allow Emoji to be overwritten. Show added Emoji in search. --- plugins/emoji/assets/javascripts/emoji.js.erb | 77 +++++++++++-------- 1 file changed, 43 insertions(+), 34 deletions(-) diff --git a/plugins/emoji/assets/javascripts/emoji.js.erb b/plugins/emoji/assets/javascripts/emoji.js.erb index cceea054b0..147f91ca34 100644 --- a/plugins/emoji/assets/javascripts/emoji.js.erb +++ b/plugins/emoji/assets/javascripts/emoji.js.erb @@ -6,15 +6,18 @@ _extendedEmoji[code] = url; }; + function urlFor(code) { + var url = _extendedEmoji[code]; + if (!url && emoji.indexOf(code) !== -1) { + url = Discourse.getURL('/plugins/emoji/images/' + code + '.png'); + } + return url; + } + function imageFor(code) { - if (emoji.indexOf(code) !== -1) { - var url = Discourse.getURL('/plugins/emoji/images/' + code + '.png'); + var url = urlFor(code); + if (url) { return ['img', {href: url, title: ':' + code + ':', 'class': 'emoji', alt: code}]; - } else { - var url = _extendedEmoji[code]; - if (url) { - return ['img', {href: url, title: ':' + code + ':', 'class': 'emoji', alt: code}]; - } } } @@ -125,49 +128,55 @@ "{{#each options}}" + "
  • " + "" + - " " + - "{{this}}" + + " " + + "{{code}}" + "
  • " + "{{/each}}" + "" + ""); + var toSearch = emoji.concat(Object.keys(_extendedEmoji)); $('#wmd-input').autocomplete({ template: template, key: ":", - transformComplete: function(v){ return v + ":"; }, + transformComplete: function(v){ return v.code + ":"; }, dataSource: function(term){ + return new Ember.RSVP.Promise(function(resolve) { + var full = ":" + term; + term = term.toLowerCase(); - var full = ":" + term; - term = term.toLowerCase(); - - if (term === "") { - return Ember.RSVP.resolve(["smile", "smiley", "wink", "sunny", "blush"]); - } - - if (translations[full]) { - return Ember.RSVP.resolve([translations[full]]); - } - - var options = []; - var i; - for (i=0; i < emoji.length; i++) { - if (emoji[i].indexOf(term) === 0) { - options.push(emoji[i]); - if(options.length > 4) { break; } + if (term === "") { + return resolve(["smile", "smiley", "wink", "sunny", "blush"]); } - } - if (options.length <= 4) { - for (i=0; i < emoji.length; i++) { - if (emoji[i].indexOf(term) > 0) { - options.push(emoji[i]); + if (translations[full]) { + return resolve([translations[full]]); + } + + var options = []; + var i; + for (i=0; i < toSearch.length; i++) { + if (toSearch[i].indexOf(term) === 0) { + options.push(toSearch[i]); if(options.length > 4) { break; } } } - } - return Ember.RSVP.resolve(options); + if (options.length <= 4) { + for (i=0; i < toSearch.length; i++) { + if (toSearch[i].indexOf(term) > 0) { + options.push(toSearch[i]); + if(options.length > 4) { break; } + } + } + } + + return resolve(options); + }).then(function(list) { + return list.map(function(i) { + return {code: i, src: urlFor(i)}; + }); + }); } }); }); From 7a8811882e17c76dbe9f6b7c8dbdf460e79360c8 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Tue, 4 Nov 2014 12:53:02 -0500 Subject: [PATCH 45/83] UX: Remove icons, add black/white colors to attributes in user card --- app/assets/javascripts/discourse/templates/user-card.hbs | 6 +++--- app/assets/stylesheets/desktop/user-card.scss | 8 +++++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/discourse/templates/user-card.hbs b/app/assets/javascripts/discourse/templates/user-card.hbs index 5b90e0d6a9..4cadb9ce69 100644 --- a/app/assets/javascripts/discourse/templates/user-card.hbs +++ b/app/assets/javascripts/discourse/templates/user-card.hbs @@ -59,10 +59,10 @@ {{/if}} diff --git a/app/assets/stylesheets/desktop/user-card.scss b/app/assets/stylesheets/desktop/user-card.scss index a34230564a..4686995e97 100644 --- a/app/assets/stylesheets/desktop/user-card.scss +++ b/app/assets/stylesheets/desktop/user-card.scss @@ -99,7 +99,13 @@ width: 100%; clear: both; padding-top: 5px; - h3 {display: inline; margin-right: 5px; } + h3 { + display: inline; + margin-right: 5px; + .desc, a { + color: scale-color($primary, $lightness: 70%); + } + } div {display: inline; color: scale-color($primary, $lightness: 50%); .group-link {color: scale-color($primary, $lightness: 50%);} } From 91966422214d5712c145c89d20ecac69c064fa5d Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Tue, 4 Nov 2014 13:01:41 -0500 Subject: [PATCH 46/83] UX: Add title to user card image, shrink to avoid bleeding into text. --- app/assets/javascripts/discourse/templates/user-card.hbs | 4 ++-- app/assets/stylesheets/desktop/user-card.scss | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/discourse/templates/user-card.hbs b/app/assets/javascripts/discourse/templates/user-card.hbs index 4cadb9ce69..6ddd56f6c4 100644 --- a/app/assets/javascripts/discourse/templates/user-card.hbs +++ b/app/assets/javascripts/discourse/templates/user-card.hbs @@ -53,8 +53,8 @@ {{/if}} {{#if user.card_badge}} - {{#link-to 'badges.show' user.card_badge class="card-badge"}} - {{icon-or-image user.card_badge.image}} + {{#link-to 'badges.show' user.card_badge class="card-badge" title=user.card_badge.name}} + {{icon-or-image user.card_badge.image title=user.card_badge.name}} {{/link-to}} {{/if}} diff --git a/app/assets/stylesheets/desktop/user-card.scss b/app/assets/stylesheets/desktop/user-card.scss index 4686995e97..dfa9c89d33 100644 --- a/app/assets/stylesheets/desktop/user-card.scss +++ b/app/assets/stylesheets/desktop/user-card.scss @@ -225,7 +225,7 @@ .card-badge { img { - max-width: 100px; + max-width: 40px; } position: absolute; right: 12px; From ac25a24b4098ff63b946481b44ce8c83ed5799ff Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Tue, 4 Nov 2014 13:08:32 -0500 Subject: [PATCH 47/83] Missing i18n key --- app/assets/javascripts/discourse/templates/user-card.hbs | 2 +- config/locales/client.en.yml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/discourse/templates/user-card.hbs b/app/assets/javascripts/discourse/templates/user-card.hbs index 6ddd56f6c4..339662b8c0 100644 --- a/app/assets/javascripts/discourse/templates/user-card.hbs +++ b/app/assets/javascripts/discourse/templates/user-card.hbs @@ -62,7 +62,7 @@

    {{i18n last_post}} {{format-date path="user.last_posted_at" leaveAgo="true"}}

    {{i18n joined}} {{format-date path="user.created_at" leaveAgo="true"}}

    {{#if user.githubProfileUrl}} -

    GitHub

    +

    {{i18n user.github_profile}}

    {{/if}} diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 8c9ea3cd18..0f0477a97b 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -310,6 +310,7 @@ en: admin_tooltip: "This user is an admin" suspended_notice: "This user is suspended until {{date}}." suspended_reason: "Reason: " + github_profile: "Github" mailing_list_mode: "Receive an email for every new post (unless you mute the topic or category)" watched_categories: "Watched" watched_categories_instructions: "You will automatically watch all new topics in these categories. You will be notified of all new posts and topics, plus the count of unread and new posts will also appear next to the topic's listing." From e96630c6f4a809266d18aac0fe629cf073202ddf Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Tue, 4 Nov 2014 13:13:45 -0500 Subject: [PATCH 48/83] FIX: Malformed user card HTML --- .../discourse/templates/user-card.hbs | 126 +++++++++--------- app/assets/stylesheets/desktop/user-card.scss | 5 - 2 files changed, 61 insertions(+), 70 deletions(-) diff --git a/app/assets/javascripts/discourse/templates/user-card.hbs b/app/assets/javascripts/discourse/templates/user-card.hbs index 339662b8c0..078e61f038 100644 --- a/app/assets/javascripts/discourse/templates/user-card.hbs +++ b/app/assets/javascripts/discourse/templates/user-card.hbs @@ -1,86 +1,82 @@
    {{#if username}} - {{#link-to 'user' user}}{{bound-avatar avatar "huge"}}{{/link-to}}
    -

    - {{#link-to 'user' user}}{{username}}{{{user.statusIcon}}}{{/link-to}} -

    +

    + {{#link-to 'user' user}}{{username}}{{{user.statusIcon}}}{{/link-to}} +

    - {{#if user.name}} -

    {{user.name}}

    - {{/if}} + {{#if user.name}} +

    {{user.name}}

    + {{/if}} - {{#if user.title}} -

    {{user.title}}

    - {{/if}} + {{#if user.title}} +

    {{user.title}}

    + {{/if}} - {{#if showName}} -

    {{#link-to 'user' user}}{{name}}{{/link-to}}

    - {{/if}} -
    + {{#if showName}} +

    {{#link-to 'user' user}}{{name}}{{/link-to}}

    + {{/if}} +
    {{#if user}} - - - - {{#if isSuspended}} -
    - {{fa-icon "ban"}} - {{i18n user.suspended_notice date="user.suspendedTillDate"}}
    - {{i18n user.suspended_reason}} {{user.suspend_reason}} -
    - {{else}} - {{#if user.bio_cooked}}
    {{text-overflow class="overflow" text=user.bio_cooked}}
    {{/if}} + + + {{#if isSuspended}} +
    + {{fa-icon "ban"}} + {{i18n user.suspended_notice date="user.suspendedTillDate"}}
    + {{i18n user.suspended_reason}} {{user.suspend_reason}} +
    + {{else}} + {{#if user.bio_cooked}}
    {{text-overflow class="overflow" text=user.bio_cooked}}
    {{/if}} + {{/if}} + + {{#if user.card_badge}} + {{#link-to 'badges.show' user.card_badge class="card-badge" title=user.card_badge.name}} + {{icon-or-image user.card_badge.image title=user.card_badge.name}} + {{/link-to}} + {{/if}} + + + + {{#if showBadges}} +
    + {{#each user.featured_user_badges}} + {{user-badge badge=badge}} + {{/each}} + {{#if showMoreBadges}} + {{#link-to 'user.badges' user class="btn more-user-badges"}} + {{i18n badges.more_badges count=moreBadgesCount}} + {{/link-to}} {{/if}}
    - - {{#if showBadges}} -
    - {{#each user.featured_user_badges}} - {{user-badge badge=badge}} - {{/each}} - {{#if showMoreBadges}} - {{#link-to 'user.badges' user class="btn more-user-badges"}} - {{i18n badges.more_badges count=moreBadgesCount}} - {{/link-to}} - {{/if}} -
    - {{/if}} -
    - - {{else}} -

    {{i18n loading}}

    + {{/if}} {{/if}} {{/if}} + + diff --git a/app/assets/stylesheets/desktop/user-card.scss b/app/assets/stylesheets/desktop/user-card.scss index dfa9c89d33..1ffc369d6d 100644 --- a/app/assets/stylesheets/desktop/user-card.scss +++ b/app/assets/stylesheets/desktop/user-card.scss @@ -143,11 +143,6 @@ margin: 0 0 5px 0; } - p.loading { - margin-top: 45px; - color: $primary; - } - .btn { margin: 0 0 7px 0; box-sizing: border-box; From d4d282e9194ee94e235dbddb59b99ed997c835c3 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Tue, 4 Nov 2014 13:26:33 -0500 Subject: [PATCH 49/83] UX: Let's try a fade in when the user card is shown --- .../discourse/controllers/user-card.js.es6 | 5 +- .../discourse/templates/user-card.hbs | 96 +++++++++---------- .../discourse/views/user-card.js.es6 | 15 ++- 3 files changed, 61 insertions(+), 55 deletions(-) diff --git a/app/assets/javascripts/discourse/controllers/user-card.js.es6 b/app/assets/javascripts/discourse/controllers/user-card.js.es6 index a2058d9356..70678bdb5b 100644 --- a/app/assets/javascripts/discourse/controllers/user-card.js.es6 +++ b/app/assets/javascripts/discourse/controllers/user-card.js.es6 @@ -48,7 +48,7 @@ export default ObjectController.extend({ this.set('avatar', null); } - this.setProperties({visible: true, username: username}); + this.set('username', username); // If we click the avatar again, close it. if (username === currentUsername && wasVisible) { @@ -67,8 +67,7 @@ export default ObjectController.extend({ var self = this; self.set('user', null); Discourse.User.findByUsername(username).then(function (user) { - self.set('user', user); - self.set('avatar', user); + self.setProperties({ user: user, avatar: user, visible: true}); }); }, diff --git a/app/assets/javascripts/discourse/templates/user-card.hbs b/app/assets/javascripts/discourse/templates/user-card.hbs index 078e61f038..b5105ba2d5 100644 --- a/app/assets/javascripts/discourse/templates/user-card.hbs +++ b/app/assets/javascripts/discourse/templates/user-card.hbs @@ -1,6 +1,4 @@
    - -{{#if username}} {{#link-to 'user' user}}{{bound-avatar avatar "huge"}}{{/link-to}}
    @@ -23,60 +21,56 @@
    - {{#if user}} - - - {{#if isSuspended}} -
    - {{fa-icon "ban"}} - {{i18n user.suspended_notice date="user.suspendedTillDate"}}
    - {{i18n user.suspended_reason}} {{user.suspend_reason}} -
    - {{else}} - {{#if user.bio_cooked}}
    {{text-overflow class="overflow" text=user.bio_cooked}}
    {{/if}} + + + {{#if isSuspended}} +
    + {{fa-icon "ban"}} + {{i18n user.suspended_notice date="user.suspendedTillDate"}}
    + {{i18n user.suspended_reason}} {{user.suspend_reason}} +
    + {{else}} + {{#if user.bio_cooked}}
    {{text-overflow class="overflow" text=user.bio_cooked}}
    {{/if}} + {{/if}} + + {{#if user.card_badge}} + {{#link-to 'badges.show' user.card_badge class="card-badge" title=user.card_badge.name}} + {{icon-or-image user.card_badge.image title=user.card_badge.name}} + {{/link-to}} + {{/if}} + + + + {{#if showBadges}} +
    + {{#each user.featured_user_badges}} + {{user-badge badge=badge}} + {{/each}} + {{#if showMoreBadges}} + {{#link-to 'user.badges' user class="btn more-user-badges"}} + {{i18n badges.more_badges count=moreBadgesCount}} + {{/link-to}} {{/if}}
    - - {{#if showBadges}} -
    - {{#each user.featured_user_badges}} - {{user-badge badge=badge}} - {{/each}} - {{#if showMoreBadges}} - {{#link-to 'user.badges' user class="btn more-user-badges"}} - {{i18n badges.more_badges count=moreBadgesCount}} - {{/link-to}} - {{/if}} -
    - {{/if}} {{/if}} -{{/if}} -
    diff --git a/app/assets/javascripts/discourse/views/user-card.js.es6 b/app/assets/javascripts/discourse/views/user-card.js.es6 index a9e05e5855..a5aff4c8e7 100644 --- a/app/assets/javascripts/discourse/views/user-card.js.es6 +++ b/app/assets/javascripts/discourse/views/user-card.js.es6 @@ -6,9 +6,22 @@ var clickOutsideEventName = "mousedown.outside-user-card", export default Discourse.View.extend(CleansUp, { elementId: 'user-card', - classNameBindings: ['controller.visible::hidden', 'controller.showBadges'], + classNameBindings: ['controller.showBadges'], allowBackgrounds: Discourse.computed.setting('allow_profile_backgrounds'), + _visibleChanged: function() { + var vis = this.get('controller.visible'), + $this = this.$(); + + if (!$this) { return; } + + if (vis) { + this.$().fadeIn('fast'); + } else { + this.$().fadeOut('fast'); + } + }.observes('controller.visible'), + addBackground: function() { var url = this.get('controller.user.card_background'); if (!this.get('allowBackgrounds')) { return; } From 9161b8529f5976ca95fd0e23d3ccddb55e8b1e06 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Tue, 4 Nov 2014 13:46:35 -0500 Subject: [PATCH 50/83] Broken tests, let's try popping in/out now to see how that works. --- .../discourse/controllers/topic.js.es6 | 1 + .../discourse/controllers/user-card.js.es6 | 1 - .../javascripts/discourse/views/user-card.js.es6 | 15 +-------------- 3 files changed, 2 insertions(+), 15 deletions(-) diff --git a/app/assets/javascripts/discourse/controllers/topic.js.es6 b/app/assets/javascripts/discourse/controllers/topic.js.es6 index ebfbc01e73..4009ea471b 100644 --- a/app/assets/javascripts/discourse/controllers/topic.js.es6 +++ b/app/assets/javascripts/discourse/controllers/topic.js.es6 @@ -8,6 +8,7 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, { selectedPosts: null, selectedReplies: null, queryParams: ['filter', 'username_filters', 'show_deleted'], + searchHighlight: null, maxTitleLength: Discourse.computed.setting('max_topic_title_length'), diff --git a/app/assets/javascripts/discourse/controllers/user-card.js.es6 b/app/assets/javascripts/discourse/controllers/user-card.js.es6 index 70678bdb5b..02ded4c8d9 100644 --- a/app/assets/javascripts/discourse/controllers/user-card.js.es6 +++ b/app/assets/javascripts/discourse/controllers/user-card.js.es6 @@ -47,7 +47,6 @@ export default ObjectController.extend({ } else { this.set('avatar', null); } - this.set('username', username); // If we click the avatar again, close it. diff --git a/app/assets/javascripts/discourse/views/user-card.js.es6 b/app/assets/javascripts/discourse/views/user-card.js.es6 index a5aff4c8e7..a9e05e5855 100644 --- a/app/assets/javascripts/discourse/views/user-card.js.es6 +++ b/app/assets/javascripts/discourse/views/user-card.js.es6 @@ -6,22 +6,9 @@ var clickOutsideEventName = "mousedown.outside-user-card", export default Discourse.View.extend(CleansUp, { elementId: 'user-card', - classNameBindings: ['controller.showBadges'], + classNameBindings: ['controller.visible::hidden', 'controller.showBadges'], allowBackgrounds: Discourse.computed.setting('allow_profile_backgrounds'), - _visibleChanged: function() { - var vis = this.get('controller.visible'), - $this = this.$(); - - if (!$this) { return; } - - if (vis) { - this.$().fadeIn('fast'); - } else { - this.$().fadeOut('fast'); - } - }.observes('controller.visible'), - addBackground: function() { var url = this.get('controller.user.card_background'); if (!this.get('allowBackgrounds')) { return; } From 74e2b8f6746afbb3404fade83cd157327daa8a51 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Tue, 4 Nov 2014 14:25:35 -0500 Subject: [PATCH 51/83] FIX: User card @mentions looked pretty bad --- .../javascripts/discourse/templates/user/user.hbs | 10 +++++----- app/assets/stylesheets/desktop/topic-post.scss | 10 +++++----- app/assets/stylesheets/desktop/user-card.scss | 3 +++ app/assets/stylesheets/desktop/user.scss | 4 ++++ 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/app/assets/javascripts/discourse/templates/user/user.hbs b/app/assets/javascripts/discourse/templates/user/user.hbs index 0eb73f8d95..75f4fd5b73 100644 --- a/app/assets/javascripts/discourse/templates/user/user.hbs +++ b/app/assets/javascripts/discourse/templates/user/user.hbs @@ -94,11 +94,11 @@ {{#if websiteName}} - {{#if linkWebsite}} - {{websiteName}} - {{else}} - {{websiteName}} - {{/if}} + {{#if linkWebsite}} + {{websiteName}} + {{else}} + {{websiteName}} + {{/if}} {{/if}} diff --git a/app/assets/stylesheets/desktop/topic-post.scss b/app/assets/stylesheets/desktop/topic-post.scss index 8cbb5dd38b..a889c97026 100644 --- a/app/assets/stylesheets/desktop/topic-post.scss +++ b/app/assets/stylesheets/desktop/topic-post.scss @@ -614,11 +614,11 @@ iframe { position: relative; } - a.mention { - padding: 2px 4px; - color: $primary; - background: scale-color-diff(); - } +a.mention { + padding: 2px 4px; + color: $primary; + background: scale-color-diff(); +} .modal-body { diff --git a/app/assets/stylesheets/desktop/user-card.scss b/app/assets/stylesheets/desktop/user-card.scss index 1ffc369d6d..acc129a2bb 100644 --- a/app/assets/stylesheets/desktop/user-card.scss +++ b/app/assets/stylesheets/desktop/user-card.scss @@ -127,6 +127,9 @@ max-width: 100%; } + a.mention { + background-color: dark-light-diff($secondary, $primary, 50%, -60%); + } .overflow { max-height: 60px; overflow: hidden; diff --git a/app/assets/stylesheets/desktop/user.scss b/app/assets/stylesheets/desktop/user.scss index 7df2422183..da9d689aee 100644 --- a/app/assets/stylesheets/desktop/user.scss +++ b/app/assets/stylesheets/desktop/user.scss @@ -292,6 +292,10 @@ overflow: auto; max-width: 750px; + a.mention { + background-color: dark-light-diff($secondary, $primary, 50%, -60%); + } + a[href] { color: dark-light-diff($secondary, $primary, 75%, 0%); text-decoration: underline; From e8a750e27aad7fc185d2f10184025a22617a34d5 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Tue, 4 Nov 2014 15:22:55 -0500 Subject: [PATCH 52/83] FIX: Missing dates on user card --- app/assets/javascripts/discourse/templates/user-card.hbs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/assets/javascripts/discourse/templates/user-card.hbs b/app/assets/javascripts/discourse/templates/user-card.hbs index b5105ba2d5..ebf1de9c21 100644 --- a/app/assets/javascripts/discourse/templates/user-card.hbs +++ b/app/assets/javascripts/discourse/templates/user-card.hbs @@ -53,6 +53,7 @@ {{/link-to}} {{/if}} + {{#if user}} + {{/if}} {{#if showBadges}}
    From cc9b040502f8a2e4ad07c3efd4bbf3488dc10c5b Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 5 Nov 2014 07:45:35 +1100 Subject: [PATCH 53/83] Revert "FEATURE: display github profile links on user card and profile" This reverts commit 95c518329a077997cd859a36fe3558dae325890b. --- app/assets/javascripts/discourse/models/user.js | 10 +--------- .../javascripts/discourse/templates/user-card.hbs | 3 --- .../javascripts/discourse/templates/user/user.hbs | 4 ---- app/assets/stylesheets/desktop/user.scss | 3 --- app/assets/stylesheets/mobile/user.scss | 3 --- app/serializers/user_serializer.rb | 8 +------- config/locales/server.en.yml | 1 - config/site_settings.yml | 1 - 8 files changed, 2 insertions(+), 31 deletions(-) diff --git a/app/assets/javascripts/discourse/models/user.js b/app/assets/javascripts/discourse/models/user.js index f7be1f75a4..7088433927 100644 --- a/app/assets/javascripts/discourse/models/user.js +++ b/app/assets/javascripts/discourse/models/user.js @@ -437,15 +437,7 @@ Discourse.User = Discourse.Model.extend({ }); } }, function () {}); - }, - - githubProfileUrl: function(){ - - var screenName = this.get('github_screen_name'); - if(screenName) { - return "https://github.com/" + screenName; - } - }.property() + } }); diff --git a/app/assets/javascripts/discourse/templates/user-card.hbs b/app/assets/javascripts/discourse/templates/user-card.hbs index ebf1de9c21..b20a196bad 100644 --- a/app/assets/javascripts/discourse/templates/user-card.hbs +++ b/app/assets/javascripts/discourse/templates/user-card.hbs @@ -57,9 +57,6 @@ {{/if}} diff --git a/app/assets/javascripts/discourse/templates/user/user.hbs b/app/assets/javascripts/discourse/templates/user/user.hbs index 75f4fd5b73..062fa51f9f 100644 --- a/app/assets/javascripts/discourse/templates/user/user.hbs +++ b/app/assets/javascripts/discourse/templates/user/user.hbs @@ -150,10 +150,6 @@ {{/if}} {{/if}} - {{#if github_screen_name}} -
    GitHub
    -
    {{github_screen_name}}
    - {{/if}} {{plugin-outlet "user-profile-secondary"}}
    diff --git a/app/assets/stylesheets/desktop/user.scss b/app/assets/stylesheets/desktop/user.scss index da9d689aee..c06d52a22a 100644 --- a/app/assets/stylesheets/desktop/user.scss +++ b/app/assets/stylesheets/desktop/user.scss @@ -215,9 +215,6 @@ overflow: hidden; text-overflow: ellipsis; color: $primary; - a { - color: $primary; - } } dt { diff --git a/app/assets/stylesheets/mobile/user.scss b/app/assets/stylesheets/mobile/user.scss index 33071abf67..7acd33b644 100644 --- a/app/assets/stylesheets/mobile/user.scss +++ b/app/assets/stylesheets/mobile/user.scss @@ -257,9 +257,6 @@ overflow: hidden; text-overflow: ellipsis; color: $primary; - a { - color: $primary; - } } dt { diff --git a/app/serializers/user_serializer.rb b/app/serializers/user_serializer.rb index 1b17b06eb8..00d5f27dce 100644 --- a/app/serializers/user_serializer.rb +++ b/app/serializers/user_serializer.rb @@ -48,8 +48,7 @@ class UserSerializer < BasicUserSerializer :has_title_badges, :edit_history_public, :custom_fields, - :user_fields, - :github_screen_name + :user_fields has_one :invited_by, embed: :object, serializer: BasicUserSerializer has_many :custom_groups, embed: :object, serializer: BasicGroupSerializer @@ -89,11 +88,6 @@ class UserSerializer < BasicUserSerializer ### ### ATTRIBUTES ### - def github_screen_name - if SiteSetting.public_github_screen_name - object.github_user_info && object.github_user_info.screen_name - end - end def include_email? object.id && object.id == scope.user.try(:id) diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 0dac266c15..67a2df56b8 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -808,7 +808,6 @@ en: facebook_app_secret: "App secret for Facebook authentication, registered at https://developers.facebook.com/apps" enable_github_logins: "Enable Github authentication, requires github_client_id and github_client_secret" - public_github_screen_name: "Display Github screen names publicly in the forum (on user page and profile)" github_client_id: "Client id for Github authentication, registered at https://github.com/settings/applications" github_client_secret: "Client secret for Github authentication, registered at https://github.com/settings/applications" diff --git a/config/site_settings.yml b/config/site_settings.yml index b97fae5f7f..2274aa2350 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -212,7 +212,6 @@ login: enable_github_logins: client: true default: false - public_github_screen_name: true github_client_id: default: '' regex: "^[a-f0-9]*$" From c9eb809dad740ded8416add77203b762e17e15f9 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Tue, 4 Nov 2014 15:47:32 -0500 Subject: [PATCH 54/83] FIX: The text to users who signed up when approval was required was misleading. --- app/controllers/users_controller.rb | 3 ++- app/views/users/account_created.html.erb | 4 +--- spec/controllers/users_controller_spec.rb | 12 ++++++------ 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 521f1b6170..00b2e61862 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -245,7 +245,7 @@ class UsersController < ApplicationController activation.finish # save user email in session, to show on account-created page - session["user_created_email"] = user.email + session["user_created_message"] = activation.message render json: { success: true, @@ -364,6 +364,7 @@ class UsersController < ApplicationController end def account_created + @message = session['user_created_message'] expires_now render layout: 'no_js' end diff --git a/app/views/users/account_created.html.erb b/app/views/users/account_created.html.erb index ea8cf7d9ed..0bc9c8ccdb 100644 --- a/app/views/users/account_created.html.erb +++ b/app/views/users/account_created.html.erb @@ -1,5 +1,3 @@
    - <% if session["user_created_email"] %> - <%= t('login.activate_email', email: session["user_created_email"]).html_safe %> - <% end %> + <%= @message.html_safe %>
    diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index 2d4b23e0ab..d5e1db7974 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -357,8 +357,8 @@ describe UsersController do expect(JSON.parse(response.body)['active']).to be_falsey - # should save user_created_email in session - session["user_created_email"].should == @user.email + # should save user_created_message in session + session["user_created_message"].should be_present end context "and 'must approve users' site setting is enabled" do @@ -393,8 +393,8 @@ describe UsersController do User.any_instance.expects(:enqueue_welcome_message).with('welcome_user') post_user - # should save user_created_email in session - session["user_created_email"].should == @user.email + # should save user_created_message in session + session["user_created_message"].should be_present end it "shows the 'active' message" do @@ -479,7 +479,7 @@ describe UsersController do json["success"].should == true # should not change the session - session["user_created_email"].should be_blank + session["user_created_message"].should be_blank end end @@ -523,7 +523,7 @@ describe UsersController do json["success"].should_not == true # should not change the session - session["user_created_email"].should be_blank + session["user_created_message"].should be_blank end end From a78048f3fb999c5665858d5a74b745d2b33c25d7 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Tue, 4 Nov 2014 16:12:42 -0500 Subject: [PATCH 55/83] FIX: Filter user button shouldn't appear on card when there is already a filter active. --- app/assets/javascripts/discourse/models/post_stream.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/discourse/models/post_stream.js b/app/assets/javascripts/discourse/models/post_stream.js index 6955fdce12..026b84e3cf 100644 --- a/app/assets/javascripts/discourse/models/post_stream.js +++ b/app/assets/javascripts/discourse/models/post_stream.js @@ -130,7 +130,7 @@ Discourse.PostStream = Em.Object.extend({ hasNoFilters: function() { var streamFilters = this.get('streamFilters'); - return !(streamFilters && ((streamFilters.filter === 'summary') || streamFilters.userFilters)); + return !(streamFilters && ((streamFilters.filter === 'summary') || streamFilters.username_filters)); }.property('streamFilters.[]', 'topic.posts_count', 'posts.length'), /** From 6a424cf46951d948185e9941e1fbaa43539e920b Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Tue, 4 Nov 2014 16:30:46 -0500 Subject: [PATCH 56/83] UX: Make the user card badge image larger. If present, make text have a smaller width to accomodate the larger image. --- .../javascripts/discourse/controllers/user-card.js.es6 | 5 +++++ app/assets/javascripts/discourse/views/user-card.js.es6 | 2 +- app/assets/stylesheets/desktop/user-card.scss | 8 ++++++-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/discourse/controllers/user-card.js.es6 b/app/assets/javascripts/discourse/controllers/user-card.js.es6 index 02ded4c8d9..e79d159c17 100644 --- a/app/assets/javascripts/discourse/controllers/user-card.js.es6 +++ b/app/assets/javascripts/discourse/controllers/user-card.js.es6 @@ -28,6 +28,11 @@ export default ObjectController.extend({ showMoreBadges: Em.computed.gt('moreBadgesCount', 0), + hasCardBadgeImage: function() { + var img = this.get('user.card_badge.image'); + return img && img.indexOf('fa-') === -1; + }.property('user.card_badge.image'), + show: function(username, uploadedAvatarId) { // XSS protection (should be encapsulated) username = username.replace(/[^A-Za-z0-9_]/g, ""); diff --git a/app/assets/javascripts/discourse/views/user-card.js.es6 b/app/assets/javascripts/discourse/views/user-card.js.es6 index a9e05e5855..9c438fc0ba 100644 --- a/app/assets/javascripts/discourse/views/user-card.js.es6 +++ b/app/assets/javascripts/discourse/views/user-card.js.es6 @@ -6,7 +6,7 @@ var clickOutsideEventName = "mousedown.outside-user-card", export default Discourse.View.extend(CleansUp, { elementId: 'user-card', - classNameBindings: ['controller.visible::hidden', 'controller.showBadges'], + classNameBindings: ['controller.visible::hidden', 'controller.showBadges', 'controller.hasCardBadgeImage'], allowBackgrounds: Discourse.computed.setting('allow_profile_backgrounds'), addBackground: function() { diff --git a/app/assets/stylesheets/desktop/user-card.scss b/app/assets/stylesheets/desktop/user-card.scss index acc129a2bb..13e6c5fad7 100644 --- a/app/assets/stylesheets/desktop/user-card.scss +++ b/app/assets/stylesheets/desktop/user-card.scss @@ -116,9 +116,14 @@ padding-top: 10px; } + &.has-card-badge-image .bio { + width: 75%; + } + .bio { padding: 15px 0 0 0; clear: left; + a { color: $secondary; text-decoration: underline; @@ -198,7 +203,6 @@ background: transparent; color: scale-color($primary, $lightness: 50%); border-color: scale-color($primary, $lightness: 50%); - } h3 { @@ -223,7 +227,7 @@ .card-badge { img { - max-width: 40px; + max-width: 120px; } position: absolute; right: 12px; From 73a1d168e3be307b5b3b68b4c966555e67756ef1 Mon Sep 17 00:00:00 2001 From: Jeff Atwood Date: Tue, 4 Nov 2014 15:04:08 -0800 Subject: [PATCH 57/83] remove profile button from user card --- app/assets/javascripts/discourse/templates/user-card.hbs | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/assets/javascripts/discourse/templates/user-card.hbs b/app/assets/javascripts/discourse/templates/user-card.hbs index b20a196bad..b5dc6f39c5 100644 --- a/app/assets/javascripts/discourse/templates/user-card.hbs +++ b/app/assets/javascripts/discourse/templates/user-card.hbs @@ -26,8 +26,6 @@
  • {{fa-icon "envelope"}}{{i18n user.private_message}}
  • {{/if}} -
  • {{#link-to 'user' user class="btn"}}{{fa-icon "user"}}{{i18n user.profile}}{{/link-to}}
  • - {{#if showFilter}}
  • {{fa-icon "filter"}}{{i18n topic.filter_to username="username" post_count="participant.post_count"}}
  • {{/if}} From 568033f6236f1ded3babd647ff3064f809b819c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Wed, 5 Nov 2014 12:09:22 +0100 Subject: [PATCH 58/83] FIX: escape topic status' title --- .../javascripts/discourse/components/topic-status.js.es6 | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/discourse/components/topic-status.js.es6 b/app/assets/javascripts/discourse/components/topic-status.js.es6 index fe99221da5..1d0883f2e7 100644 --- a/app/assets/javascripts/discourse/components/topic-status.js.es6 +++ b/app/assets/javascripts/discourse/components/topic-status.js.es6 @@ -40,13 +40,11 @@ export default Ember.Component.extend({ var renderIconIf = function(conditionProp, name, key, actionable) { if (!self.get(conditionProp)) { return; } - var title = I18n.t("topic_statuses." + key + ".help"); - + var title = Handlebars.Utils.escapeExpression(I18n.t("topic_statuses." + key + ".help")); var startTag = actionable ? "a href='#'" : "span"; var endTag = actionable ? "a" : "span"; - buffer.push("<" + startTag + - " title='" + title +"' class='topic-status'>"); + buffer.push("<" + startTag + " title='" + title + "' class='topic-status'>"); }; // Allow a plugin to add a custom icon to a topic From 59e0c1b9cdaef9599f7d86f3822a093f56e4b481 Mon Sep 17 00:00:00 2001 From: Kris Aubuchon Date: Tue, 4 Nov 2014 22:57:13 -0500 Subject: [PATCH 59/83] trying out a new css-only loading spinner --- .../admin/templates/email_preview_digest.hbs | 2 +- .../javascripts/admin/templates/flags.hbs | 4 +- .../admin/templates/logs/screened_emails.hbs | 2 +- .../templates/logs/screened_ip_addresses.hbs | 2 +- .../admin/templates/logs/screened_urls.hbs | 2 +- .../templates/logs/staff_action_logs.hbs | 2 +- .../admin/templates/user_badges.hbs | 2 +- .../admin/templates/users_list.hbs | 2 +- .../discourse/controllers/topic.js.es6 | 4 +- .../discourse/helpers/loading-spinner.es6 | 4 ++ .../discourse/templates/badges/show.hbs | 4 +- .../templates/components/basic-topic-list.hbs | 2 +- .../discourse/templates/composer.hbs | 2 +- .../discourse/templates/discovery.hbs | 2 +- .../mobile/components/basic-topic-list.hbs | 2 +- .../javascripts/discourse/templates/topic.hbs | 8 +-- .../discourse/templates/user/invited.hbs | 2 +- .../templates/user/notifications.hbs | 2 +- .../discourse/templates/user/posts.hbs | 2 +- .../discourse/templates/user/stream.hbs | 2 +- .../stylesheets/common/base/discourse.scss | 54 ++++++++++++------- 21 files changed, 66 insertions(+), 42 deletions(-) create mode 100644 app/assets/javascripts/discourse/helpers/loading-spinner.es6 diff --git a/app/assets/javascripts/admin/templates/email_preview_digest.hbs b/app/assets/javascripts/admin/templates/email_preview_digest.hbs index abd414dbdc..1e56304d85 100644 --- a/app/assets/javascripts/admin/templates/email_preview_digest.hbs +++ b/app/assets/javascripts/admin/templates/email_preview_digest.hbs @@ -19,7 +19,7 @@ {{#if loading}} -
    + {{loading-spinner}} {{else}} {{#if showHtml}} {{{html_content}}} diff --git a/app/assets/javascripts/admin/templates/flags.hbs b/app/assets/javascripts/admin/templates/flags.hbs index 2b34210b59..d5756938b1 100644 --- a/app/assets/javascripts/admin/templates/flags.hbs +++ b/app/assets/javascripts/admin/templates/flags.hbs @@ -9,7 +9,7 @@
    {{#if loading}} -
    + {{loading-spinner}} {{else}} {{#if length}} @@ -160,7 +160,7 @@
    {{#if view.loading}} -
    + {{loading-spinner}} {{/if}} {{else}} diff --git a/app/assets/javascripts/admin/templates/logs/screened_emails.hbs b/app/assets/javascripts/admin/templates/logs/screened_emails.hbs index 68704f8ee0..a3a2bb8bd4 100644 --- a/app/assets/javascripts/admin/templates/logs/screened_emails.hbs +++ b/app/assets/javascripts/admin/templates/logs/screened_emails.hbs @@ -1,7 +1,7 @@

    {{i18n admin.logs.screened_emails.description}}

    {{#if loading}} -
    + {{loading-spinner}} {{else}} {{#if model.length}} diff --git a/app/assets/javascripts/admin/templates/logs/screened_ip_addresses.hbs b/app/assets/javascripts/admin/templates/logs/screened_ip_addresses.hbs index 042116be1f..7b61f357bf 100644 --- a/app/assets/javascripts/admin/templates/logs/screened_ip_addresses.hbs +++ b/app/assets/javascripts/admin/templates/logs/screened_ip_addresses.hbs @@ -4,7 +4,7 @@
    {{#if loading}} -
    + {{loading-spinner}} {{else}} {{#if model.length}} diff --git a/app/assets/javascripts/admin/templates/logs/screened_urls.hbs b/app/assets/javascripts/admin/templates/logs/screened_urls.hbs index e20c57ed26..077ae151ed 100644 --- a/app/assets/javascripts/admin/templates/logs/screened_urls.hbs +++ b/app/assets/javascripts/admin/templates/logs/screened_urls.hbs @@ -1,7 +1,7 @@

    {{i18n admin.logs.screened_urls.description}}

    {{#if loading}} -
    + {{loading-spinner}} {{else}} {{#if model.length}} diff --git a/app/assets/javascripts/admin/templates/logs/staff_action_logs.hbs b/app/assets/javascripts/admin/templates/logs/staff_action_logs.hbs index 9216c394af..e422308d62 100644 --- a/app/assets/javascripts/admin/templates/logs/staff_action_logs.hbs +++ b/app/assets/javascripts/admin/templates/logs/staff_action_logs.hbs @@ -45,7 +45,7 @@ {{#if loading}}
    -
    + {{loading-spinner}} {{else}} {{#if model.length}} {{view "staff-action-logs-list" content=controller}} diff --git a/app/assets/javascripts/admin/templates/user_badges.hbs b/app/assets/javascripts/admin/templates/user_badges.hbs index 5b24155378..dfe9ed4690 100644 --- a/app/assets/javascripts/admin/templates/user_badges.hbs +++ b/app/assets/javascripts/admin/templates/user_badges.hbs @@ -7,7 +7,7 @@
    {{#if loading}} -
    + {{loading-spinner}} {{else}}

    {{i18n admin.badges.grant_badge}}

    diff --git a/app/assets/javascripts/admin/templates/users_list.hbs b/app/assets/javascripts/admin/templates/users_list.hbs index c9d760bcdb..352f15b3ab 100644 --- a/app/assets/javascripts/admin/templates/users_list.hbs +++ b/app/assets/javascripts/admin/templates/users_list.hbs @@ -38,7 +38,7 @@
    {{#if loading}} -
    + {{loading-spinner}} {{else}} {{#if model.length}} diff --git a/app/assets/javascripts/discourse/controllers/topic.js.es6 b/app/assets/javascripts/discourse/controllers/topic.js.es6 index 4009ea471b..144337c36f 100644 --- a/app/assets/javascripts/discourse/controllers/topic.js.es6 +++ b/app/assets/javascripts/discourse/controllers/topic.js.es6 @@ -510,9 +510,11 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, { }.property('isPrivateMessage'), loadingHTML: function() { - return "
    "; + return "{{loading-spinner}}"; }.property(), + + recoverTopic: function() { this.get('content').recover(); }, diff --git a/app/assets/javascripts/discourse/helpers/loading-spinner.es6 b/app/assets/javascripts/discourse/helpers/loading-spinner.es6 new file mode 100644 index 0000000000..7ff1438cd3 --- /dev/null +++ b/app/assets/javascripts/discourse/helpers/loading-spinner.es6 @@ -0,0 +1,4 @@ +Handlebars.registerHelper('loading-spinner', function(property, options) { + var spinner = "
    "; + return new Handlebars.SafeString(spinner); +}); diff --git a/app/assets/javascripts/discourse/templates/badges/show.hbs b/app/assets/javascripts/discourse/templates/badges/show.hbs index 5f68cf2ddf..9c585d855e 100644 --- a/app/assets/javascripts/discourse/templates/badges/show.hbs +++ b/app/assets/javascripts/discourse/templates/badges/show.hbs @@ -36,11 +36,11 @@ {{/each}} {{#if canLoadMore}} -
    + {{loading-spinner}} {{/if}} {{else}} {{#unless userBadgesLoaded}} -
    + {{loading-spinner}} {{/unless}} {{/if}} diff --git a/app/assets/javascripts/discourse/templates/components/basic-topic-list.hbs b/app/assets/javascripts/discourse/templates/components/basic-topic-list.hbs index 02d90715ae..32802cca7a 100644 --- a/app/assets/javascripts/discourse/templates/components/basic-topic-list.hbs +++ b/app/assets/javascripts/discourse/templates/components/basic-topic-list.hbs @@ -56,5 +56,5 @@ {{/if}} {{else}} -
    + {{loading-spinner}} {{/if}} diff --git a/app/assets/javascripts/discourse/templates/composer.hbs b/app/assets/javascripts/discourse/templates/composer.hbs index 0a12c2389b..7dd2e37f04 100644 --- a/app/assets/javascripts/discourse/templates/composer.hbs +++ b/app/assets/javascripts/discourse/templates/composer.hbs @@ -1,4 +1,4 @@ -
    +{{loading-spinner}}
    diff --git a/app/assets/javascripts/discourse/templates/discovery.hbs b/app/assets/javascripts/discourse/templates/discovery.hbs index 3c143c9780..eb3a50edb5 100644 --- a/app/assets/javascripts/discourse/templates/discovery.hbs +++ b/app/assets/javascripts/discourse/templates/discovery.hbs @@ -11,7 +11,7 @@
    -
    + {{loading-spinner}}
    diff --git a/app/assets/javascripts/discourse/templates/mobile/components/basic-topic-list.hbs b/app/assets/javascripts/discourse/templates/mobile/components/basic-topic-list.hbs index e24b34855b..0c168e6521 100644 --- a/app/assets/javascripts/discourse/templates/mobile/components/basic-topic-list.hbs +++ b/app/assets/javascripts/discourse/templates/mobile/components/basic-topic-list.hbs @@ -53,5 +53,5 @@ {{/if}} {{else}} -
    + {{loading-spinner}} {{/if}} diff --git a/app/assets/javascripts/discourse/templates/topic.hbs b/app/assets/javascripts/discourse/templates/topic.hbs index 5f251277bd..d64415a92c 100644 --- a/app/assets/javascripts/discourse/templates/topic.hbs +++ b/app/assets/javascripts/discourse/templates/topic.hbs @@ -73,7 +73,7 @@ {{render 'topic-progress'}} {{#if postStream.loadingAbove}} -
    + {{loading-spinner}} {{/if}} {{#unless postStream.loadingFilter}} @@ -90,7 +90,7 @@ {{/unless}} {{#if postStream.loadingBelow}} -
    + {{loading-spinner}} {{/if}}
    @@ -137,13 +137,13 @@ {{/if}} {{#if retrying}} -
    + {{loading-spinner}} {{/if}} {{/if}} {{else}}
    -
    + {{loading-spinner}}
    {{/if}} {{/if}} diff --git a/app/assets/javascripts/discourse/templates/user/invited.hbs b/app/assets/javascripts/discourse/templates/user/invited.hbs index 6432bad3f4..17802c836c 100644 --- a/app/assets/javascripts/discourse/templates/user/invited.hbs +++ b/app/assets/javascripts/discourse/templates/user/invited.hbs @@ -70,7 +70,7 @@ {{/each}}
    {{#if invitesLoading}} -
    + {{loading-spinner}} {{/if}} {{else}} diff --git a/app/assets/javascripts/discourse/templates/user/notifications.hbs b/app/assets/javascripts/discourse/templates/user/notifications.hbs index e1c280b530..52427b1a34 100644 --- a/app/assets/javascripts/discourse/templates/user/notifications.hbs +++ b/app/assets/javascripts/discourse/templates/user/notifications.hbs @@ -23,7 +23,7 @@ {{/each}} {{#if loading}} -
    + {{loading-spinner}} {{/if}} {{#unless canLoadMore}}
    diff --git a/app/assets/javascripts/discourse/templates/user/posts.hbs b/app/assets/javascripts/discourse/templates/user/posts.hbs index 46c4313e7f..42220d4899 100644 --- a/app/assets/javascripts/discourse/templates/user/posts.hbs +++ b/app/assets/javascripts/discourse/templates/user/posts.hbs @@ -27,5 +27,5 @@ {{/each}} {{#if loading}} -
    + {{loading-spinner}} {{/if}} diff --git a/app/assets/javascripts/discourse/templates/user/stream.hbs b/app/assets/javascripts/discourse/templates/user/stream.hbs index c3e5da2672..eda7c8b145 100644 --- a/app/assets/javascripts/discourse/templates/user/stream.hbs +++ b/app/assets/javascripts/discourse/templates/user/stream.hbs @@ -26,5 +26,5 @@ {{/grouped-each}} {{#if loading}} -
    + {{loading-spinner}} {{/if}} diff --git a/app/assets/stylesheets/common/base/discourse.scss b/app/assets/stylesheets/common/base/discourse.scss index 83313f727f..0f425c28f2 100644 --- a/app/assets/stylesheets/common/base/discourse.scss +++ b/app/assets/stylesheets/common/base/discourse.scss @@ -148,26 +148,44 @@ body { } .spinner { - width: 20px; - margin: 0 auto; - padding: 25px 15px; - background: { - image: image-url("spinner_96.gif"); - repeat: no-repeat; - size: 50px; - }; - -webkit-animation: spinner .25s; - animation: spinner .25s; - margin-top: 10px; + height: 50px; width: 50px; + margin: 20px auto 0 auto; + -webkit-animation: spin 1s steps(12, end) infinite; + animation: spin 1s steps(12, end) infinite; } -//loading spinner fade-in -@-webkit-keyframes spinner { - from {opacity: 0} - to {opacity: 1} -}@keyframes fade { - from {opacity: 0} - to {opacity: 1} +.spinner i { + height: 12px; width: 4px; + margin-left: -2px; + display: block; + position: absolute; + left: 50%; + transform-origin: center 25px; + background: $primary; + border-radius: 10px; +} + +.spinner i:nth-child(1) { opacity: 0.08; } +.spinner i:nth-child(2) { transform: rotate(30deg); opacity: 0.167; } +.spinner i:nth-child(3) { transform: rotate(60deg); opacity: 0.25; } +.spinner i:nth-child(4) { transform: rotate(90deg); opacity: 0.33; } +.spinner i:nth-child(5) { transform: rotate(120deg); opacity: 0.4167; } +.spinner i:nth-child(6) { transform: rotate(150deg); opacity: 0.5; } +.spinner i:nth-child(7) { transform: rotate(180deg); opacity: 0.583; } +.spinner i:nth-child(8) { transform: rotate(210deg); opacity: 0.67; } +.spinner i:nth-child(9) { transform: rotate(240deg); opacity: 0.75; } +.spinner i:nth-child(10) { transform: rotate(270deg); opacity: 0.833; } +.spinner i:nth-child(11) { transform: rotate(300deg); opacity: 0.9167; } +.spinner i:nth-child(12) { transform: rotate(330deg); opacity: 1; } + +@-webkit-keyframes spin { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } +} + +@keyframes spin { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } } .avatar-wrapper { From ea0c6df839393febc45d5aff6d6b9499f85e17a6 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Wed, 5 Nov 2014 11:52:22 -0500 Subject: [PATCH 60/83] FIX: Only look for `fa-` at the beginning of the field. Allows urls with that combination in it. Thanks @riking --- app/assets/javascripts/discourse/controllers/user-card.js.es6 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/discourse/controllers/user-card.js.es6 b/app/assets/javascripts/discourse/controllers/user-card.js.es6 index e79d159c17..52ef582c4a 100644 --- a/app/assets/javascripts/discourse/controllers/user-card.js.es6 +++ b/app/assets/javascripts/discourse/controllers/user-card.js.es6 @@ -30,7 +30,7 @@ export default ObjectController.extend({ hasCardBadgeImage: function() { var img = this.get('user.card_badge.image'); - return img && img.indexOf('fa-') === -1; + return img && img.indexOf('fa-') === 0; }.property('user.card_badge.image'), show: function(username, uploadedAvatarId) { From acfd99ebf2fffc53607d5d275cbaa6e1172840a8 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Wed, 5 Nov 2014 12:04:35 -0500 Subject: [PATCH 61/83] FIX: Use proper HTML for spinner rather than handlebars --- .../javascripts/discourse/controllers/topic.js.es6 | 5 ++--- .../javascripts/discourse/helpers/loading-spinner.es6 | 9 ++++++--- app/assets/javascripts/main_include.js | 1 + 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/app/assets/javascripts/discourse/controllers/topic.js.es6 b/app/assets/javascripts/discourse/controllers/topic.js.es6 index 144337c36f..7e027eff33 100644 --- a/app/assets/javascripts/discourse/controllers/topic.js.es6 +++ b/app/assets/javascripts/discourse/controllers/topic.js.es6 @@ -1,4 +1,5 @@ import ObjectController from 'discourse/controllers/object'; +import { spinnerHTML } from 'discourse/helpers/loading-spinner'; export default ObjectController.extend(Discourse.SelectedPostsCount, { multiSelect: false, @@ -510,11 +511,9 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, { }.property('isPrivateMessage'), loadingHTML: function() { - return "{{loading-spinner}}"; + return spinnerHTML; }.property(), - - recoverTopic: function() { this.get('content').recover(); }, diff --git a/app/assets/javascripts/discourse/helpers/loading-spinner.es6 b/app/assets/javascripts/discourse/helpers/loading-spinner.es6 index 7ff1438cd3..e43c88f019 100644 --- a/app/assets/javascripts/discourse/helpers/loading-spinner.es6 +++ b/app/assets/javascripts/discourse/helpers/loading-spinner.es6 @@ -1,4 +1,7 @@ -Handlebars.registerHelper('loading-spinner', function(property, options) { - var spinner = "
    "; - return new Handlebars.SafeString(spinner); +var spinnerHTML = "
    "; + +Handlebars.registerHelper('loading-spinner', function() { + return new Handlebars.SafeString(spinnerHTML); }); + +export { spinnerHTML }; diff --git a/app/assets/javascripts/main_include.js b/app/assets/javascripts/main_include.js index 79c2e6e1ec..0423e7040a 100644 --- a/app/assets/javascripts/main_include.js +++ b/app/assets/javascripts/main_include.js @@ -45,6 +45,7 @@ //= require ./discourse/components/visible //= require ./discourse/helpers/user-avatar //= require ./discourse/helpers/cold-age-class +//= require ./discourse/helpers/loading-spinner //= require ./discourse/dialects/dialect //= require_tree ./discourse/dialects From fde5e739c910e35e6245c5b646554d13ec2fccb6 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Tue, 4 Nov 2014 17:08:39 -0500 Subject: [PATCH 62/83] Work in progress (up till about?) --- app/controllers/admin/reports_controller.rb | 9 +++- app/models/post.rb | 4 +- app/models/report.rb | 56 +++++++++++++-------- app/models/topic.rb | 4 +- app/models/user_visit.rb | 4 +- 5 files changed, 47 insertions(+), 30 deletions(-) diff --git a/app/controllers/admin/reports_controller.rb b/app/controllers/admin/reports_controller.rb index 8b2d4f066f..fe6408ccd8 100644 --- a/app/controllers/admin/reports_controller.rb +++ b/app/controllers/admin/reports_controller.rb @@ -3,12 +3,17 @@ require_dependency 'report' class Admin::ReportsController < Admin::AdminController def show - report_type = params[:type] raise Discourse::NotFound.new unless report_type =~ /^[a-z0-9\_]+$/ - report = Report.find(report_type) + start_date = 1.month.ago + start_date = Time.parse(params[:start_date]) if params[:start_date].present? + + end_date = start_date + 1.month + end_date = Time.parse(params[:end_date]) if params[:end_date].present? + + report = Report.find(report_type, {start_date: start_date, end_date: end_date}) raise Discourse::NotFound.new if report.blank? render_json_dump(report: report) diff --git a/app/models/post.rb b/app/models/post.rb index 82fa47a262..8ce68da34d 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -460,8 +460,8 @@ class Post < ActiveRecord::Base Jobs.enqueue(:process_post, args) end - def self.public_posts_count_per_day(since_days_ago=30) - public_posts.where('posts.created_at > ?', since_days_ago.days.ago).group('date(posts.created_at)').order('date(posts.created_at)').count + def self.public_posts_count_per_day(start_date, end_date) + public_posts.where('posts.created_at >= ? AND posts.created_at <= ?', start_date, end_date).group('date(posts.created_at)').order('date(posts.created_at)').count end def self.private_messages_count_per_day(since_days_ago, topic_subtype) diff --git a/app/models/report.rb b/app/models/report.rb index bf6b04b26f..b2cea24c2a 100644 --- a/app/models/report.rb +++ b/app/models/report.rb @@ -2,39 +2,51 @@ require_dependency 'topic_subtype' class Report - attr_accessor :type, :data, :total, :prev30Days + attr_accessor :type, :data, :total, :prev30Days, :start_date, :end_date + + def self.default_days + 30 + end def initialize(type) @type = type @data = nil @total = nil @prev30Days = nil + @start_date ||= 1.month.ago + @end_date ||= Time.now end def as_json(options = nil) { - type: self.type, - title: I18n.t("reports.#{self.type}.title"), - xaxis: I18n.t("reports.#{self.type}.xaxis"), - yaxis: I18n.t("reports.#{self.type}.yaxis"), - data: self.data, - total: self.total, + type: type, + title: I18n.t("reports.#{type}.title"), + xaxis: I18n.t("reports.#{type}.xaxis"), + yaxis: I18n.t("reports.#{type}.yaxis"), + data: data, + total: total, + start_date: start_date, + end_date: end_date, prev30Days: self.prev30Days } end - def self.find(type, opts={}) + def self.find(type, opts=nil) + opts ||= {} report_method = :"report_#{type}" return nil unless respond_to?(report_method) # Load the report report = Report.new(type) + + report.start_date = opts[:start_date] + report.end_date = opts[:end_date] send(report_method, report) report end def self.report_visits(report) - basic_report_about report, UserVisit, :by_day + basic_report_about report, UserVisit, :by_day, report.start_date, report.end_date end def self.report_signups(report) @@ -42,15 +54,15 @@ class Report end def self.report_topics(report) - basic_report_about report, Topic, :listable_count_per_day + basic_report_about report, Topic, :listable_count_per_day, report.start_date, report.end_date report.total = Topic.listable_topics.count - report.prev30Days = Topic.listable_topics.where('created_at > ? and created_at < ?', 60.days.ago, 30.days.ago).count + report.prev30Days = Topic.listable_topics.where('created_at > ? and created_at < ?', report.start_date - 30.days, report.start_date).count end def self.report_posts(report) - basic_report_about report, Post, :public_posts_count_per_day + basic_report_about report, Post, :public_posts_count_per_day, report.start_date, report.end_date report.total = Post.public_posts.count - report.prev30Days = Post.public_posts.where('posts.created_at > ? and posts.created_at < ?', 60.days.ago, 30.days.ago).count + report.prev30Days = Post.public_posts.where('posts.created_at > ? and posts.created_at < ?', report.start_date - 30.days, report.start_date).count end def self.report_emails(report) @@ -58,20 +70,20 @@ class Report end def self.report_about(report, subject_class, report_method = :count_per_day) - basic_report_about report, subject_class, report_method + basic_report_about report, subject_class, report_method, default_days add_counts(report, subject_class) end def self.basic_report_about(report, subject_class, report_method, *args) report.data = [] - subject_class.send(report_method, 30, *args).each do |date, count| + subject_class.send(report_method, *args).each do |date, count| report.data << {x: date, y: count} end end def self.add_counts(report, subject_class) report.total = subject_class.count - report.prev30Days = subject_class.where('created_at > ? and created_at < ?', 60.days.ago, 30.days.ago).count + report.prev30Days = subject_class.where('created_at > ? and created_at < ?', report.start_date - 30.days, report.start_date).count end def self.report_users_by_trust_level(report) @@ -82,10 +94,10 @@ class Report end def self.report_starred(report) - basic_report_about report, Topic, :starred_counts_per_day + basic_report_about report, Topic, :starred_counts_per_day, default_days query = TopicUser.where(starred: true) report.total = query.count - report.prev30Days = query.where('starred_at > ? and starred_at < ?', 60.days.ago, 30.days.ago).count + report.prev30Days = query.where('starred_at > ? and starred_at < ?', report.start_date - 30.days, report.start_date).count end # Post action counts: @@ -102,7 +114,7 @@ class Report end flagsQuery = PostAction.where(post_action_type_id: PostActionType.flag_types.values) report.total = flagsQuery.count - report.prev30Days = flagsQuery.where('created_at > ? and created_at < ?', 60.days.ago, 30.days.ago).count + report.prev30Days = flagsQuery.where('created_at > ? and created_at < ?', report.start_date - 30.days, report.start_date).count end def self.report_likes(report) @@ -120,15 +132,15 @@ class Report end query = PostAction.unscoped.where(post_action_type_id: post_action_type) report.total = query.count - report.prev30Days = query.where('created_at > ? and created_at < ?', 60.days.ago, 30.days.ago).count + report.prev30Days = query.where('created_at > ? and created_at < ?', report.start_date - 30.days, report.start_date).count end # Private messages counts: def self.private_messages_report(report, topic_subtype) - basic_report_about report, Post, :private_messages_count_per_day, topic_subtype + basic_report_about report, Post, :private_messages_count_per_day, default_days, topic_subtype report.total = Post.private_posts.with_topic_subtype(topic_subtype).count - report.prev30Days = Post.private_posts.with_topic_subtype(topic_subtype).where('posts.created_at > ? and posts.created_at < ?', 60.days.ago, 30.days.ago).count + report.prev30Days = Post.private_posts.with_topic_subtype(topic_subtype).where('posts.created_at > ? and posts.created_at < ?', report.start_date - 30.days, report.start_date).count end def self.report_user_to_user_private_messages(report) diff --git a/app/models/topic.rb b/app/models/topic.rb index b0ab6f24eb..a2a35d229d 100644 --- a/app/models/topic.rb +++ b/app/models/topic.rb @@ -345,8 +345,8 @@ class Topic < ActiveRecord::Base custom_fields[key.to_s] end - def self.listable_count_per_day(sinceDaysAgo=30) - listable_topics.where('created_at > ?', sinceDaysAgo.days.ago).group('date(created_at)').order('date(created_at)').count + def self.listable_count_per_day(start_date, end_date) + listable_topics.where('created_at >= ? and created_at < ?', start_date, end_date).group('date(created_at)').order('date(created_at)').count end def private_message? diff --git a/app/models/user_visit.rb b/app/models/user_visit.rb index 6af6bf8efc..669a4d1843 100644 --- a/app/models/user_visit.rb +++ b/app/models/user_visit.rb @@ -1,8 +1,8 @@ class UserVisit < ActiveRecord::Base # A count of visits in the last month by day - def self.by_day(sinceDaysAgo=30) - where("visited_at >= ?", sinceDaysAgo.days.ago).group(:visited_at).order(:visited_at).count + def self.by_day(start_date, end_date) + where("visited_at >= ? and visited_at < ?", start_date, end_date).group(:visited_at).order(:visited_at).count end def self.ensure_consistency! From 068d22e9b32ab8c84b4149600e73fd47f51b2ee9 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Wed, 5 Nov 2014 13:11:23 -0500 Subject: [PATCH 63/83] Add API support for querying admin reports by date range --- app/models/email_log.rb | 7 +++---- app/models/report.rb | 6 +++--- app/models/user.rb | 4 ++-- spec/controllers/admin/reports_controller_spec.rb | 4 ++-- spec/models/email_log_spec.rb | 2 +- 5 files changed, 11 insertions(+), 12 deletions(-) diff --git a/app/models/email_log.rb b/app/models/email_log.rb index a9b94c8cf1..bb324e1a73 100644 --- a/app/models/email_log.rb +++ b/app/models/email_log.rb @@ -13,8 +13,8 @@ class EmailLog < ActiveRecord::Base User.where(id: user_id).update_all("last_emailed_at = CURRENT_TIMESTAMP") if user_id.present? and !skipped end - def self.count_per_day(sinceDaysAgo = 30) - where('created_at > ? and skipped = false', sinceDaysAgo.days.ago).group('date(created_at)').order('date(created_at)').count + def self.count_per_day(start_date, end_date) + where('created_at >= ? and created_at < ? AND skipped = false', start_date, end_date).group('date(created_at)').order('date(created_at)').count end def self.for(reply_key) @@ -22,8 +22,7 @@ class EmailLog < ActiveRecord::Base end def self.last_sent_email_address - where(email_type: 'signup').order('created_at DESC') - .first.try(:to_address) + where(email_type: 'signup').order('created_at DESC').first.try(:to_address) end end diff --git a/app/models/report.rb b/app/models/report.rb index b2cea24c2a..1ddf45cf13 100644 --- a/app/models/report.rb +++ b/app/models/report.rb @@ -39,8 +39,8 @@ class Report # Load the report report = Report.new(type) - report.start_date = opts[:start_date] - report.end_date = opts[:end_date] + report.start_date = opts[:start_date] if opts[:start_date] + report.end_date = opts[:end_date] if opts[:end_date] send(report_method, report) report end @@ -70,7 +70,7 @@ class Report end def self.report_about(report, subject_class, report_method = :count_per_day) - basic_report_about report, subject_class, report_method, default_days + basic_report_about report, subject_class, report_method, report.start_date, report.end_date add_counts(report, subject_class) end diff --git a/app/models/user.rb b/app/models/user.rb index 5c5de05877..cafd7ddc3e 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -531,8 +531,8 @@ class User < ActiveRecord::Base .limit(3) end - def self.count_by_signup_date(sinceDaysAgo=30) - where('created_at > ?', sinceDaysAgo.days.ago).group('date(created_at)').order('date(created_at)').count + def self.count_by_signup_date(start_date, end_date) + where('created_at >= ? and created_at < ?', start_date, end_date).group('date(created_at)').order('date(created_at)').count end diff --git a/spec/controllers/admin/reports_controller_spec.rb b/spec/controllers/admin/reports_controller_spec.rb index 2412319fd3..5a3cf0ab03 100644 --- a/spec/controllers/admin/reports_controller_spec.rb +++ b/spec/controllers/admin/reports_controller_spec.rb @@ -31,7 +31,7 @@ describe Admin::ReportsController do context 'missing report' do before do - Report.expects(:find).with('active').returns(nil) + Report.expects(:find).with('active', instance_of(Hash)).returns(nil) xhr :get, :show, type: 'active' end @@ -42,7 +42,7 @@ describe Admin::ReportsController do context 'a report is found' do before do - Report.expects(:find).with('active').returns(Report.new('active')) + Report.expects(:find).with('active', instance_of(Hash)).returns(Report.new('active')) xhr :get, :show, type: 'active' end diff --git a/spec/models/email_log_spec.rb b/spec/models/email_log_spec.rb index 9db88984fd..4cabf766d5 100644 --- a/spec/models/email_log_spec.rb +++ b/spec/models/email_log_spec.rb @@ -30,7 +30,7 @@ describe EmailLog do it "counts sent emails" do user.email_logs.create(email_type: 'blah', to_address: user.email) user.email_logs.create(email_type: 'blah', to_address: user.email, skipped: true) - described_class.count_per_day.first[1].should == 1 + described_class.count_per_day(1.day.ago, Time.now).first[1].should == 1 end end From e79c1c23d9935f53d421496ece82f3302dc002e9 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Wed, 5 Nov 2014 14:05:16 -0500 Subject: [PATCH 64/83] Show CSV groups on user profile --- .../discourse/components/groups-list.js.es6 | 12 ------------ .../discourse/templates/components/groups-list.hbs | 6 ------ .../javascripts/discourse/templates/user/user.hbs | 8 ++++++++ app/assets/stylesheets/desktop/user.scss | 9 +++++++++ 4 files changed, 17 insertions(+), 18 deletions(-) delete mode 100644 app/assets/javascripts/discourse/components/groups-list.js.es6 delete mode 100644 app/assets/javascripts/discourse/templates/components/groups-list.hbs diff --git a/app/assets/javascripts/discourse/components/groups-list.js.es6 b/app/assets/javascripts/discourse/components/groups-list.js.es6 deleted file mode 100644 index 09ac3c3dba..0000000000 --- a/app/assets/javascripts/discourse/components/groups-list.js.es6 +++ /dev/null @@ -1,12 +0,0 @@ -/** - Displays a list of groups that a user belongs to. - - @class Discourse.GroupsListComponent - @extends Ember.Component - @namespace Discourse - @module Discourse -**/ -export default Em.Component.extend({ - classNames: ['groups'] -}); - diff --git a/app/assets/javascripts/discourse/templates/components/groups-list.hbs b/app/assets/javascripts/discourse/templates/components/groups-list.hbs deleted file mode 100644 index 95a012d1e1..0000000000 --- a/app/assets/javascripts/discourse/templates/components/groups-list.hbs +++ /dev/null @@ -1,6 +0,0 @@ -{{#if groups}} - {{i18n groups.title count=groups.length}}: - {{#each groups}} - {{#link-to 'group' this class="group-link"}}{{name}}{{/link-to}} - {{/each}} -{{/if}} diff --git a/app/assets/javascripts/discourse/templates/user/user.hbs b/app/assets/javascripts/discourse/templates/user/user.hbs index 062fa51f9f..b9174f93b4 100644 --- a/app/assets/javascripts/discourse/templates/user/user.hbs +++ b/app/assets/javascripts/discourse/templates/user/user.hbs @@ -150,6 +150,14 @@ {{/if}} {{/if}} + {{#if custom_groups}} +
    {{i18n groups.title count=custom_groups.length}}
    +
    + {{#each custom_groups}} + {{#link-to 'group' this class="group-link"}}{{name}}{{/link-to}} + {{/each}} +
    + {{/if}} {{plugin-outlet "user-profile-secondary"}} diff --git a/app/assets/stylesheets/desktop/user.scss b/app/assets/stylesheets/desktop/user.scss index c06d52a22a..3ab82cade1 100644 --- a/app/assets/stylesheets/desktop/user.scss +++ b/app/assets/stylesheets/desktop/user.scss @@ -217,6 +217,15 @@ color: $primary; } + dd.groups { + span:after { + content: ',' + } + span:last-of-type:after { + content:'' + } + } + dt { color: dark-light-diff($secondary, $primary, 50%, -40%); margin: 0; From a5616146eb2cff1113c75bef6495d0952125da6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Wed, 5 Nov 2014 20:37:00 +0100 Subject: [PATCH 65/83] FIX: remove meta data from lightbox in both excerpt (html & text) --- app/helpers/user_notifications_helper.rb | 24 ++++---------------- app/views/email/_post.html.erb | 2 +- app/views/user_notifications/digest.text.erb | 5 +--- lib/pretty_text.rb | 5 ++++ spec/components/pretty_text_spec.rb | 11 ++++++--- 5 files changed, 20 insertions(+), 27 deletions(-) diff --git a/app/helpers/user_notifications_helper.rb b/app/helpers/user_notifications_helper.rb index a86871ba5b..99807e2db4 100644 --- a/app/helpers/user_notifications_helper.rb +++ b/app/helpers/user_notifications_helper.rb @@ -1,13 +1,5 @@ module UserNotificationsHelper - def self.sanitize_options - return @sanitize_options if @sanitize_options - @sanitize_options = Sanitize::Config::RELAXED.deep_dup - @sanitize_options[:elements] << 'aside' << 'div' - @sanitize_options[:attributes][:all] << 'class' - @sanitize_options - end - def indent(text, by=2) spacer = " " * by result = "" @@ -57,21 +49,15 @@ module UserNotificationsHelper end def email_excerpt(html, posts_count) - # If there's only one post, include the whole thing. - if posts_count == 1 - raw Sanitize.clean(html, UserNotificationsHelper.sanitize_options) - else - # Otherwise, try just the first paragraph. - para = first_paragraph_from(html) - raw Sanitize.clean(para.to_s, UserNotificationsHelper.sanitize_options) - end + # only include 1st paragraph when more than 1 posts + html = first_paragraph_from(html).to_s if posts_count > 1 + raw format_for_email(html) end - def cooked_post_for_email(post) - PrettyText.format_for_email(post.cooked).html_safe + def format_for_email(html) + PrettyText.format_for_email(html).html_safe end - def email_category(category, opts=nil) opts = opts || {} diff --git a/app/views/email/_post.html.erb b/app/views/email/_post.html.erb index 1a10b57f77..8cacf66f99 100644 --- a/app/views/email/_post.html.erb +++ b/app/views/email/_post.html.erb @@ -10,7 +10,7 @@ - <%= cooked_post_for_email(post) %> + <%= format_for_email(post.cooked) %> diff --git a/app/views/user_notifications/digest.text.erb b/app/views/user_notifications/digest.text.erb index f05ed22ff4..b0735a373a 100644 --- a/app/views/user_notifications/digest.text.erb +++ b/app/views/user_notifications/digest.text.erb @@ -10,10 +10,7 @@ <%= raw(@markdown_linker.create(t.title, t.relative_url)) %> <%- if t.best_post.present? %> - <%= raw(t.best_post.excerpt(1000, - strip_links: true, - text_entities: true, - markdown_images: true)) %> + <%= raw(t.best_post.excerpt(1000, strip_links: true, text_entities: true, markdown_images: true)) %> -------------------------------------------------------------------------------- <%- end %> diff --git a/lib/pretty_text.rb b/lib/pretty_text.rb index 6a1625b2df..2c4622815c 100644 --- a/lib/pretty_text.rb +++ b/lib/pretty_text.rb @@ -241,6 +241,11 @@ module PrettyText end def self.excerpt(html, max_length, options={}) + # TODO: properly fix this HACK in ExcerptParser without introducing XSS + doc = Nokogiri::HTML.fragment(html) + strip_image_wrapping(doc) + html = doc.to_html + ExcerptParser.get_excerpt(html, max_length, options) end diff --git a/spec/components/pretty_text_spec.rb b/spec/components/pretty_text_spec.rb index 0a6857d69c..76e5122121 100644 --- a/spec/components/pretty_text_spec.rb +++ b/spec/components/pretty_text_spec.rb @@ -3,6 +3,9 @@ require 'pretty_text' describe PrettyText do + let(:wrapped_image) { "" } + let(:wrapped_image_excerpt) { } + describe "Cooking" do describe "with avatar" do @@ -111,6 +114,10 @@ describe PrettyText do PrettyText.excerpt("
    ", 100).should match_html "[image]" PrettyText.excerpt("spoiler", 100).should match_html "spoiler" end + + it "should remove meta informations" do + PrettyText.excerpt(wrapped_image, 100).should match_html "[image]" + end end it "should have an option to strip links" do @@ -276,10 +283,8 @@ describe PrettyText do strip_image_wrapping(html).should == html end - let(:wrapped_image) { "" } - it "strips the metadata" do - strip_image_wrapping(wrapped_image).should == "
    " + strip_image_wrapping(wrapped_image).should match_html "
    " end end From 2d9187cd9d3c20794f6b5c68e77d5f3957ff7623 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Wed, 5 Nov 2014 14:46:27 -0500 Subject: [PATCH 66/83] Admin controls to select a date range for reports --- .../admin/controllers/admin-reports.js.es6 | 16 +++++++-- app/assets/javascripts/admin/models/report.js | 13 +++++--- .../admin/routes/admin_reports_route.js | 8 +++++ .../javascripts/admin/templates/reports.hbs | 33 ++++++++++++------- .../stylesheets/common/admin/admin_base.scss | 3 ++ config/locales/client.en.yml | 7 ++-- 6 files changed, 59 insertions(+), 21 deletions(-) diff --git a/app/assets/javascripts/admin/controllers/admin-reports.js.es6 b/app/assets/javascripts/admin/controllers/admin-reports.js.es6 index 9a8a4555c8..6bb36989b7 100644 --- a/app/assets/javascripts/admin/controllers/admin-reports.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-reports.js.es6 @@ -2,17 +2,27 @@ export default Ember.ObjectController.extend({ viewMode: 'table', viewingTable: Em.computed.equal('viewMode', 'table'), viewingBarChart: Em.computed.equal('viewMode', 'barChart'), + startDate: null, + endDate: null, + refreshing: false, actions: { - // Changes the current view mode to 'table' + refreshReport: function() { + var self = this; + this.set('refreshing', true); + Discourse.Report.find(this.get('type'), this.get('startDate'), this.get('endDate')).then(function(r) { + self.set('model', r); + }).finally(function() { + self.set('refreshing', false); + }); + }, + viewAsTable: function() { this.set('viewMode', 'table'); }, - // Changes the current view mode to 'barChart' viewAsBarChart: function() { this.set('viewMode', 'barChart'); } } - }); diff --git a/app/assets/javascripts/admin/models/report.js b/app/assets/javascripts/admin/models/report.js index a182d84681..8157adc0b4 100644 --- a/app/assets/javascripts/admin/models/report.js +++ b/app/assets/javascripts/admin/models/report.js @@ -140,9 +140,12 @@ Discourse.Report = Discourse.Model.extend({ }); Discourse.Report.reopenClass({ - find: function(type) { - var model = Discourse.Report.create({type: type}); - Discourse.ajax("/admin/reports/" + type).then(function (json) { + find: function(type, startDate, endDate) { + + return Discourse.ajax("/admin/reports/" + type, {data: { + start_date: startDate, + end_date: endDate + }}).then(function (json) { // Add a percent field to each tuple var maxY = 0; json.report.data.forEach(function (row) { @@ -153,9 +156,9 @@ Discourse.Report.reopenClass({ row.percentage = Math.round((row.y / maxY) * 100); }); } + var model = Discourse.Report.create({type: type}); model.setProperties(json.report); - model.set('loaded', true); + return model; }); - return(model); } }); diff --git a/app/assets/javascripts/admin/routes/admin_reports_route.js b/app/assets/javascripts/admin/routes/admin_reports_route.js index cdc06720ec..21b842a9f8 100644 --- a/app/assets/javascripts/admin/routes/admin_reports_route.js +++ b/app/assets/javascripts/admin/routes/admin_reports_route.js @@ -9,5 +9,13 @@ Discourse.AdminReportsRoute = Discourse.Route.extend({ model: function(params) { return Discourse.Report.find(params.type); + }, + + setupController: function(controller, model) { + controller.setProperties({ + model: model, + startDate: moment(model.get('start_date')).format('YYYY-MM-DD'), + endDate: moment(model.get('end_date')).format('YYYY-MM-DD') + }); } }); diff --git a/app/assets/javascripts/admin/templates/reports.hbs b/app/assets/javascripts/admin/templates/reports.hbs index 6fbfeb39ce..a675a74480 100644 --- a/app/assets/javascripts/admin/templates/reports.hbs +++ b/app/assets/javascripts/admin/templates/reports.hbs @@ -1,14 +1,28 @@ -{{#if loaded}} -

    {{title}}

    +

    {{title}}

    - +
    + {{i18n admin.dashboard.reports.start_date}} {{input type="date" value=startDate}} + {{i18n admin.dashboard.reports.end_date}} {{input type="date" value=endDate}} + +
    - +
    + {{#if viewingTable}} + {{i18n admin.dashboard.reports.view_table}} + {{else}} + {{i18n admin.dashboard.reports.view_table}} + {{/if}} + | + {{#if viewingBarChart}} + {{i18n admin.dashboard.reports.view_chart}} + {{else}} + {{i18n admin.dashboard.reports.view_chart}} + {{/if}} +
    +{{#if refreshing}} + {{loading-spinner}} +{{else}} @@ -31,7 +45,4 @@ {{/each}}
    {{xaxis}}
    - -{{else}} - {{i18n loading}} {{/if}} diff --git a/app/assets/stylesheets/common/admin/admin_base.scss b/app/assets/stylesheets/common/admin/admin_base.scss index 79335b2fd0..03b4b10853 100644 --- a/app/assets/stylesheets/common/admin/admin_base.scss +++ b/app/assets/stylesheets/common/admin/admin_base.scss @@ -39,6 +39,9 @@ td.flaggers td { } } + .view-options { + float: right; + } table.report { margin-top: 20px; tr { diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 0f0477a97b..81ac6b1224 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -1508,8 +1508,11 @@ en: 7_days_ago: "7 Days Ago" 30_days_ago: "30 Days Ago" all: "All" - view_table: "View as Table" - view_chart: "View as Bar Chart" + view_table: "table" + view_chart: "bar chart" + refresh_report: "Refresh Report" + start_date: "Start Date" + end_date: "End Date" commits: latest_changes: "Latest changes: please update often!" From a0e92ef57ab2943fddfbf6bcc5e2301bf7afc512 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Wed, 5 Nov 2014 15:15:58 -0500 Subject: [PATCH 67/83] FIX: PageTracker was not sending the proper page title through at all times. --- app/assets/javascripts/discourse/lib/page_tracker.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/discourse/lib/page_tracker.js b/app/assets/javascripts/discourse/lib/page_tracker.js index b94b5ab81e..842f2d9b11 100644 --- a/app/assets/javascripts/discourse/lib/page_tracker.js +++ b/app/assets/javascripts/discourse/lib/page_tracker.js @@ -19,7 +19,13 @@ Discourse.PageTracker = Ember.Object.extend(Ember.Evented, { router.on('didTransition', function() { this.send('refreshTitle'); - self.trigger('change', this.get('url'), Discourse.get('_docTitle')); + var url = this.get('url'); + + // Refreshing the title is debounced, so we need to trigger this in the + // next runloop to have the correct title. + Em.run.next(function() { + self.trigger('change', url, Discourse.get('_docTitle')); + }); }); this.set('started', true); } From b328d47628cb9dfd362dd6538c99c2c304cd05b7 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Wed, 5 Nov 2014 15:19:08 -0500 Subject: [PATCH 68/83] FIX: fa- check was incorrect on user card --- app/assets/javascripts/discourse/controllers/user-card.js.es6 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/discourse/controllers/user-card.js.es6 b/app/assets/javascripts/discourse/controllers/user-card.js.es6 index 52ef582c4a..b29f1abee7 100644 --- a/app/assets/javascripts/discourse/controllers/user-card.js.es6 +++ b/app/assets/javascripts/discourse/controllers/user-card.js.es6 @@ -30,7 +30,7 @@ export default ObjectController.extend({ hasCardBadgeImage: function() { var img = this.get('user.card_badge.image'); - return img && img.indexOf('fa-') === 0; + return img && img.indexOf('fa-') !== 0; }.property('user.card_badge.image'), show: function(username, uploadedAvatarId) { From d104b72aee82e8d2fd6651227e3b8aedd11adb59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Wed, 5 Nov 2014 21:53:16 +0100 Subject: [PATCH 69/83] UX: remove 'ago' from user streams --- app/assets/javascripts/discourse/templates/user/stream.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/discourse/templates/user/stream.hbs b/app/assets/javascripts/discourse/templates/user/stream.hbs index eda7c8b145..34b4677351 100644 --- a/app/assets/javascripts/discourse/templates/user/stream.hbs +++ b/app/assets/javascripts/discourse/templates/user/stream.hbs @@ -2,7 +2,7 @@