diff --git a/app/assets/javascripts/discourse/models/post.js b/app/assets/javascripts/discourse/models/post.js
old mode 100644
new mode 100755
index f5ed905b0..258588a6c
--- a/app/assets/javascripts/discourse/models/post.js
+++ b/app/assets/javascripts/discourse/models/post.js
@@ -8,6 +8,10 @@
**/
Discourse.Post = Discourse.Model.extend({
+ shareUrl: function(){
+ var user = Discourse.get('currentUser');
+ return '/p/' + this.get('id') + (user ? '/' + user.get('id') : '');
+ }.property('id'),
new_user:(function(){
return this.get('trust_level') === 0;
diff --git a/app/assets/javascripts/discourse/models/topic.js b/app/assets/javascripts/discourse/models/topic.js
old mode 100644
new mode 100755
index 3060364dd..45009941a
--- a/app/assets/javascripts/discourse/models/topic.js
+++ b/app/assets/javascripts/discourse/models/topic.js
@@ -36,13 +36,18 @@ Discourse.Topic = Discourse.Model.extend({
}
}).property('categoryName', 'categories'),
- url: (function() {
+ shareUrl: function(){
+ var user = Discourse.get('currentUser');
+ return '/st/' + this.get('id') + (user ? '/' + user.get('id') : '');
+ }.property('id'),
+
+ url: function() {
var slug = this.get('slug');
if (slug.isBlank()) {
slug = "topic";
}
return Discourse.getURL("/t/") + slug + "/" + (this.get('id'));
- }).property('id', 'slug'),
+ }.property('id', 'slug'),
// Helper to build a Url with a post number
urlForPostNumber: function(postNumber) {
@@ -53,20 +58,20 @@ Discourse.Topic = Discourse.Model.extend({
return url;
},
- lastReadUrl: (function() {
+ lastReadUrl: function() {
return this.urlForPostNumber(this.get('last_read_post_number'));
- }).property('url', 'last_read_post_number'),
+ }.property('url', 'last_read_post_number'),
- lastPostUrl: (function() {
+ lastPostUrl: function() {
return this.urlForPostNumber(this.get('highest_post_number'));
- }).property('url', 'highest_post_number'),
+ }.property('url', 'highest_post_number'),
// The last post in the topic
lastPost: function() {
return this.get('posts').last();
},
- postsChanged: (function() {
+ postsChanged: function() {
var last, posts;
posts = this.get('posts');
last = posts.last();
@@ -76,12 +81,12 @@ Discourse.Topic = Discourse.Model.extend({
});
last.set('lastPost', true);
return true;
- }).observes('posts.@each', 'posts'),
+ }.observes('posts.@each', 'posts'),
// The amount of new posts to display. It might be different than what the server
// tells us if we are still asynchronously flushing our "recently read" data.
// So take what the browser has seen into consideration.
- displayNewPosts: (function() {
+ displayNewPosts: function() {
var delta, highestSeen, result;
if (highestSeen = Discourse.get('highestSeenByTopic')[this.get('id')]) {
delta = highestSeen - this.get('last_read_post_number');
@@ -94,10 +99,10 @@ Discourse.Topic = Discourse.Model.extend({
}
}
return this.get('new_posts');
- }).property('new_posts', 'id'),
+ }.property('new_posts', 'id'),
// The coldmap class for the age of the topic
- ageCold: (function() {
+ ageCold: function() {
var createdAt, createdAtDays, daysSinceEpoch, lastPost, nowDays;
if (!(lastPost = this.get('last_posted_at'))) return;
if (!(createdAt = this.get('created_at'))) return;
@@ -115,7 +120,7 @@ Discourse.Topic = Discourse.Model.extend({
if (createdAtDays < nowDays - 14) return 'coldmap-low';
}
return null;
- }).property('age', 'created_at'),
+ }.property('age', 'created_at'),
viewsHeat: function() {
var v = this.get('views');
diff --git a/app/assets/javascripts/discourse/templates/post.js.handlebars b/app/assets/javascripts/discourse/templates/post.js.handlebars
old mode 100644
new mode 100755
index 9020603ad..533329ac2
--- a/app/assets/javascripts/discourse/templates/post.js.handlebars
+++ b/app/assets/javascripts/discourse/templates/post.js.handlebars
@@ -32,7 +32,7 @@
{{#if hasHistory}}
diff --git a/app/assets/javascripts/discourse/views/post_menu_view.js b/app/assets/javascripts/discourse/views/post_menu_view.js
old mode 100644
new mode 100755
index 1540788f4..08d7399a8
--- a/app/assets/javascripts/discourse/views/post_menu_view.js
+++ b/app/assets/javascripts/discourse/views/post_menu_view.js
@@ -132,7 +132,7 @@ Discourse.PostMenuView = Discourse.View.extend({
renderShare: function(post, buffer) {
buffer.push("");
+ "\" data-share-url=\"" + (post.get('shareUrl')) + "\">");
},
// Reply button
diff --git a/app/assets/javascripts/discourse/views/topic_footer_buttons_view.js b/app/assets/javascripts/discourse/views/topic_footer_buttons_view.js
old mode 100644
new mode 100755
index e0647ee1c..edeb5c39d
--- a/app/assets/javascripts/discourse/views/topic_footer_buttons_view.js
+++ b/app/assets/javascripts/discourse/views/topic_footer_buttons_view.js
@@ -62,7 +62,7 @@ Discourse.TopicFooterButtonsView = Ember.ContainerView.extend({
this.addObject(Discourse.ButtonView.create({
textKey: 'topic.share.title',
helpKey: 'topic.share.help',
- 'data-share-url': topic.get('url'),
+ 'data-share-url': topic.get('shareUrl'),
renderIcon: function(buffer) {
buffer.push("");
diff --git a/app/assets/stylesheets/application/compose.css.scss b/app/assets/stylesheets/application/compose.css.scss
index 1d6b8e948..72215d1db 100755
--- a/app/assets/stylesheets/application/compose.css.scss
+++ b/app/assets/stylesheets/application/compose.css.scss
@@ -3,6 +3,10 @@
@import "foundation/variables";
@import "foundation/mixins";
+// hack, this needs to be done cleaner
+.private-message input.span8 {
+ width: 47%;
+}
.composer-popup {
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 3684d2103..52f506f8f 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -240,12 +240,7 @@ class ApplicationController < ActionController::Base
alias :requires_parameter :requires_parameters
def store_incoming_links
- if request.referer.present?
- parsed = URI.parse(request.referer)
- if parsed.host != request.host
- IncomingLink.create(url: request.url, referer: request.referer[0..999])
- end
- end
+ IncomingLink.add(request)
end
def check_xhr
diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb
index bdc23d53a..1fc6376eb 100644
--- a/app/controllers/posts_controller.rb
+++ b/app/controllers/posts_controller.rb
@@ -4,8 +4,17 @@ require_dependency 'post_destroyer'
class PostsController < ApplicationController
# Need to be logged in for all actions here
- before_filter :ensure_logged_in, except: [:show, :replies, :by_number]
+ before_filter :ensure_logged_in, except: [:show, :replies, :by_number, :short_link]
+ skip_before_filter :store_incoming_links, only: [:short_link]
+ skip_before_filter :check_xhr, only: [:short_link]
+
+ def short_link
+ post = Post.find(params[:post_id].to_i)
+ user = User.select(:id).where(id: params[:user_id].to_i).first
+ IncomingLink.add(request, user ? user.id : nil)
+ redirect_to post.url
+ end
def create
requires_parameter(:post)
diff --git a/app/controllers/topics_controller.rb b/app/controllers/topics_controller.rb
index 6fdb1bab2..88626c313 100644
--- a/app/controllers/topics_controller.rb
+++ b/app/controllers/topics_controller.rb
@@ -19,9 +19,16 @@ class TopicsController < ApplicationController
before_filter :consider_user_for_promotion, only: :show
- skip_before_filter :check_xhr, only: [:avatar, :show, :feed]
+ skip_before_filter :check_xhr, only: [:avatar, :show, :feed, :short_link]
+ skip_before_filter :store_incoming_links, only: [:short_link]
caches_action :avatar, cache_path: Proc.new {|c| "#{c.params[:post_number]}-#{c.params[:topic_id]}" }
+ def short_link
+ topic = Topic.find(params[:topic_id].to_i)
+ user = User.select(:id).where(id: params[:user_id].to_i).first
+ IncomingLink.add(request, user ? user.id : nil)
+ redirect_to topic.relative_url
+ end
def show
create_topic_view
diff --git a/app/models/incoming_link.rb b/app/models/incoming_link.rb
index 8d7c9cbb3..f10f606a5 100644
--- a/app/models/incoming_link.rb
+++ b/app/models/incoming_link.rb
@@ -9,6 +9,20 @@ class IncomingLink < ActiveRecord::Base
before_validation :extract_topic_and_post
after_create :update_link_counts
+ def self.add(request, user_id = nil)
+ host, referer = nil
+
+ if request.referer.present?
+ host = URI.parse(request.referer).host
+ referer = request.referer[0..999]
+
+ if host != request.host
+ IncomingLink.create(url: request.url, referer: referer, user_id: user_id)
+ end
+ end
+
+ end
+
# Internal: Extract the domain from link.
def extract_domain
if referer.present?
diff --git a/app/models/post.rb b/app/models/post.rb
index 464e5e6cb..91b2c70f2 100644
--- a/app/models/post.rb
+++ b/app/models/post.rb
@@ -270,11 +270,11 @@ class Post < ActiveRecord::Base
end
def url
- Post.url(topic.title, topic.id, post_number)
+ Post.url(topic.slug, topic.id, post_number)
end
- def self.url(title, topic_id, post_number)
- "/t/#{Slug.for(title)}/#{topic_id}/#{post_number}"
+ def self.url(slug, topic_id, post_number)
+ "/t/#{slug}/#{topic_id}/#{post_number}"
end
def self.urls(post_ids)
@@ -282,12 +282,12 @@ class Post < ActiveRecord::Base
if ids.length > 0
urls = {}
Topic.joins(:posts).where('posts.id' => ids).
- select(['posts.id as post_id','post_number', 'topics.title', 'topics.id']).
+ select(['posts.id as post_id','post_number', 'topics.slug', 'topics.title', 'topics.id']).
each do |t|
- urls[t.post_id.to_i] = url(t.title, t.id, t.post_number)
+ urls[t.post_id.to_i] = url(t.slug, t.id, t.post_number)
end
urls
- else
+ else
{}
end
end
diff --git a/app/models/site_setting.rb b/app/models/site_setting.rb
index cc0edd69c..831458250 100755
--- a/app/models/site_setting.rb
+++ b/app/models/site_setting.rb
@@ -17,7 +17,7 @@ class SiteSetting < ActiveRecord::Base
setting(:company_domain, 'www.example.com')
setting(:api_key, '')
client_setting(:traditional_markdown_linebreaks, false)
- client_setting(:top_menu, 'latest|hot|new|unread|favorited|categories')
+ client_setting(:top_menu, 'latest|new|unread|favorited|categories')
client_setting(:post_menu, 'like|edit|flag|delete|share|bookmark|reply')
client_setting(:share_links, 'twitter|facebook|google+|email')
client_setting(:track_external_right_clicks, false)
diff --git a/config/routes.rb b/config/routes.rb
index 1312a0462..408b4bcd9 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -129,6 +129,9 @@ Discourse::Application.routes.draw do
end
end
+ get 'p/:post_id/:user_id' => 'posts#short_link'
+ get 'st/:topic_id/:user_id' => 'topics#short_link'
+
resources :notifications
resources :categories
diff --git a/db/migrate/20130424055025_add_user_id_to_incoming_links.rb b/db/migrate/20130424055025_add_user_id_to_incoming_links.rb
new file mode 100644
index 000000000..623e5af91
--- /dev/null
+++ b/db/migrate/20130424055025_add_user_id_to_incoming_links.rb
@@ -0,0 +1,5 @@
+class AddUserIdToIncomingLinks < ActiveRecord::Migration
+ def change
+ add_column :incoming_links, :user_id, :integer
+ end
+end
diff --git a/spec/controllers/posts_controller_spec.rb b/spec/controllers/posts_controller_spec.rb
index c194dd4f2..9d067207f 100644
--- a/spec/controllers/posts_controller_spec.rb
+++ b/spec/controllers/posts_controller_spec.rb
@@ -3,6 +3,15 @@ require 'spec_helper'
describe PostsController do
+ describe 'short_link' do
+ it 'logs the incoming link once' do
+ IncomingLink.expects(:add).once.returns(true)
+ p = Fabricate(:post)
+ get :short_link, post_id: p.id, user_id: 999
+ response.should be_redirect
+ end
+ end
+
describe 'show' do
let(:post) { Fabricate(:post, user: log_in) }
diff --git a/spec/models/incoming_link_spec.rb b/spec/models/incoming_link_spec.rb
index b5c7e10aa..ae6ea2202 100644
--- a/spec/models/incoming_link_spec.rb
+++ b/spec/models/incoming_link_spec.rb
@@ -8,68 +8,75 @@ describe IncomingLink do
it { should ensure_length_of(:referer).is_at_least(3).is_at_most(1000) }
it { should ensure_length_of(:domain).is_at_least(1).is_at_most(100) }
+ let :post do
+ Fabricate(:post)
+ end
+
+ let :topic do
+ post.topic
+ end
+
+ let :incoming_link do
+ IncomingLink.create(url: "/t/slug/#{topic.id}/#{post.post_number}",
+ referer: "http://twitter.com")
+ end
+
describe 'local topic link' do
it 'should validate properly' do
Fabricate.build(:incoming_link).should be_valid
end
+ describe 'tracking link counts' do
+ it "increases the incoming link counts" do
+ incoming_link
+ lambda { post.reload }.should change(post, :incoming_link_count).by(1)
+ lambda { topic.reload }.should change(topic, :incoming_link_count).by(1)
+ end
+ end
+
describe 'saving local link' do
-
- before do
- @post = Fabricate(:post)
- @topic = @post.topic
- @incoming_link = IncomingLink.create(url: "/t/slug/#{@topic.id}/#{@post.post_number}",
- referer: "http://twitter.com")
+ it 'has correct info set' do
+ incoming_link.domain.should == "twitter.com"
+ incoming_link.topic_id.should == topic.id
+ incoming_link.post_number.should == post.post_number
end
- describe 'incoming link counts' do
- it "increases the post's incoming link count" do
- lambda { @incoming_link.save; @post.reload }.should change(@post, :incoming_link_count).by(1)
- end
+ end
+ end
- it "increases the topic's incoming link count" do
- lambda { @incoming_link.save; @topic.reload }.should change(@topic, :incoming_link_count).by(1)
- end
+ describe 'add' do
+ it "does nothing if referer is empty" do
+ env = Rack::MockRequest.env_for("http://somesite.com")
+ request = Rack::Request.new(env)
+ IncomingLink.expects(:create).never
+ IncomingLink.add(request)
+ end
- end
-
- describe 'after save' do
- before do
- @incoming_link.save
- end
-
- it 'has a domain' do
- @incoming_link.domain.should == "twitter.com"
- end
-
- it 'has the topic_id' do
- @incoming_link.topic_id.should == @topic.id
- end
-
- it 'has the post_number' do
- @incoming_link.post_number.should == @post.post_number
- end
- end
+ it "does nothing if referer is same as host" do
+ env = Rack::MockRequest.env_for("http://somesite.com")
+ env['HTTP_REFERER'] = 'http://somesite.com'
+ request = Rack::Request.new(env)
+ IncomingLink.expects(:create).never
+ IncomingLink.add(request)
+ end
+ it "expects to be called with referer and user id" do
+ env = Rack::MockRequest.env_for("http://somesite.com")
+ env['HTTP_REFERER'] = 'http://some.other.site.com'
+ request = Rack::Request.new(env)
+ IncomingLink.expects(:create).once.returns(true)
+ IncomingLink.add(request, 100)
end
end
describe 'non-topic url' do
-
- before do
- @link = Fabricate(:incoming_link_not_topic)
- end
-
- it 'has no topic_id' do
- @link.topic_id.should be_blank
- end
-
- it 'has no post_number' do
- @link.topic_id.should be_blank
+ it 'has nothing set' do
+ link = Fabricate(:incoming_link_not_topic)
+ link.topic_id.should be_blank
+ link.user_id.should be_blank
end
end
-
end