mirror of
https://github.com/codeninjasllc/discourse.git
synced 2024-11-30 10:58:31 -05:00
FEATURE: Prompt users when they are entering duplicate links
This commit is contained in:
parent
67303d7679
commit
431179dd25
17 changed files with 168 additions and 42 deletions
|
@ -459,6 +459,7 @@ export default Ember.Component.extend({
|
||||||
// Paint oneboxes
|
// Paint oneboxes
|
||||||
$('a.onebox', $preview).each((i, e) => Discourse.Onebox.load(e, refresh));
|
$('a.onebox', $preview).each((i, e) => Discourse.Onebox.load(e, refresh));
|
||||||
this.trigger('previewRefreshed', $preview);
|
this.trigger('previewRefreshed', $preview);
|
||||||
|
this.sendAction('afterRefresh', $preview);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,7 +5,7 @@ export default Ember.Component.extend({
|
||||||
|
|
||||||
@computed('message.templateName')
|
@computed('message.templateName')
|
||||||
defaultLayout(templateName) {
|
defaultLayout(templateName) {
|
||||||
return this.container.lookup(`template:${templateName}`)
|
return this.container.lookup(`template:composer/${templateName}`)
|
||||||
},
|
},
|
||||||
|
|
||||||
didInsertElement() {
|
didInsertElement() {
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import LinkLookup from 'discourse/lib/link-lookup';
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Ember.Component.extend({
|
||||||
classNameBindings: [':composer-popup-container', 'hidden'],
|
classNameBindings: [':composer-popup-container', 'hidden'],
|
||||||
checkedMessages: false,
|
checkedMessages: false,
|
||||||
|
@ -17,6 +19,7 @@ export default Ember.Component.extend({
|
||||||
this.appEvents.on('composer:opened', this, this._findMessages);
|
this.appEvents.on('composer:opened', this, this._findMessages);
|
||||||
this.appEvents.on('composer:find-similar', this, this._findSimilar);
|
this.appEvents.on('composer:find-similar', this, this._findSimilar);
|
||||||
this.appEvents.on('composer-messages:close', this, this._closeTop);
|
this.appEvents.on('composer-messages:close', this, this._closeTop);
|
||||||
|
this.appEvents.on('composer-messages:create', this, this._create);
|
||||||
},
|
},
|
||||||
|
|
||||||
willDestroyElement() {
|
willDestroyElement() {
|
||||||
|
@ -24,6 +27,7 @@ export default Ember.Component.extend({
|
||||||
this.appEvents.off('composer:opened', this, this._findMessages);
|
this.appEvents.off('composer:opened', this, this._findMessages);
|
||||||
this.appEvents.off('composer:find-similar', this, this._findSimilar);
|
this.appEvents.off('composer:find-similar', this, this._findSimilar);
|
||||||
this.appEvents.off('composer-messages:close', this, this._closeTop);
|
this.appEvents.off('composer-messages:close', this, this._closeTop);
|
||||||
|
this.appEvents.off('composer-messages:create', this, this._create);
|
||||||
},
|
},
|
||||||
|
|
||||||
_closeTop() {
|
_closeTop() {
|
||||||
|
@ -82,20 +86,21 @@ export default Ember.Component.extend({
|
||||||
this.get('queuedForTyping').forEach(msg => this.send("popup", msg));
|
this.get('queuedForTyping').forEach(msg => this.send("popup", msg));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_create(info) {
|
||||||
|
this.reset();
|
||||||
|
this.send('popup', Ember.Object.create(info));
|
||||||
|
},
|
||||||
|
|
||||||
groupsMentioned(groups) {
|
groupsMentioned(groups) {
|
||||||
// reset existing messages, this should always win it is critical
|
// reset existing messages, this should always win it is critical
|
||||||
this.reset();
|
this.reset();
|
||||||
groups.forEach(group => {
|
groups.forEach(group => {
|
||||||
const msg = I18n.t('composer.group_mentioned', {
|
const body = I18n.t('composer.group_mentioned', {
|
||||||
group: "@" + group.name,
|
group: "@" + group.name,
|
||||||
count: group.user_count,
|
count: group.user_count,
|
||||||
group_link: Discourse.getURL(`/group/${group.name}/members`)
|
group_link: Discourse.getURL(`/group/${group.name}/members`)
|
||||||
});
|
});
|
||||||
this.send("popup",
|
this.send("popup", Ember.Object.create({ templateName: 'custom-body', body }));
|
||||||
Em.Object.create({
|
|
||||||
templateName: 'composer/group-mentioned',
|
|
||||||
body: msg})
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -123,7 +128,7 @@ export default Ember.Component.extend({
|
||||||
const similarTopics = this.get('similarTopics');
|
const similarTopics = this.get('similarTopics');
|
||||||
const message = this._similarTopicsMessage || composer.store.createRecord('composer-message', {
|
const message = this._similarTopicsMessage || composer.store.createRecord('composer-message', {
|
||||||
id: 'similar_topics',
|
id: 'similar_topics',
|
||||||
templateName: 'composer/similar-topics',
|
templateName: 'similar-topics',
|
||||||
extraClass: 'similar-topics'
|
extraClass: 'similar-topics'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -147,7 +152,7 @@ export default Ember.Component.extend({
|
||||||
if (this.get('checkedMessages')) { return; }
|
if (this.get('checkedMessages')) { return; }
|
||||||
|
|
||||||
const composer = this.get('composer');
|
const composer = this.get('composer');
|
||||||
const args = { composerAction: composer.get('action') };
|
const args = { composer_action: composer.get('action') };
|
||||||
const topicId = composer.get('topic.id');
|
const topicId = composer.get('topic.id');
|
||||||
const postId = composer.get('post.id');
|
const postId = composer.get('post.id');
|
||||||
|
|
||||||
|
@ -156,6 +161,13 @@ export default Ember.Component.extend({
|
||||||
|
|
||||||
const queuedForTyping = this.get('queuedForTyping');
|
const queuedForTyping = this.get('queuedForTyping');
|
||||||
composer.store.find('composer-message', args).then(messages => {
|
composer.store.find('composer-message', args).then(messages => {
|
||||||
|
|
||||||
|
// Checking composer messages on replies can give us a list of links to check for
|
||||||
|
// duplicates
|
||||||
|
if (messages.extras && messages.extras.duplicate_lookup) {
|
||||||
|
this.sendAction('addLinkLookup', new LinkLookup(messages.extras.duplicate_lookup));
|
||||||
|
}
|
||||||
|
|
||||||
this.set('checkedMessages', true);
|
this.set('checkedMessages', true);
|
||||||
messages.forEach(msg => msg.wait_for_typing ? queuedForTyping.addObject(msg) : this.send('popup', msg));
|
messages.forEach(msg => msg.wait_for_typing ? queuedForTyping.addObject(msg) : this.send('popup', msg));
|
||||||
});
|
});
|
||||||
|
|
|
@ -561,5 +561,4 @@ export default Ember.Component.extend({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,6 +3,7 @@ import Quote from 'discourse/lib/quote';
|
||||||
import Draft from 'discourse/models/draft';
|
import Draft from 'discourse/models/draft';
|
||||||
import Composer from 'discourse/models/composer';
|
import Composer from 'discourse/models/composer';
|
||||||
import { default as computed, observes } from 'ember-addons/ember-computed-decorators';
|
import { default as computed, observes } from 'ember-addons/ember-computed-decorators';
|
||||||
|
import { relativeAge } from 'discourse/lib/formatter';
|
||||||
|
|
||||||
function loadDraft(store, opts) {
|
function loadDraft(store, opts) {
|
||||||
opts = opts || {};
|
opts = opts || {};
|
||||||
|
@ -46,7 +47,6 @@ export default Ember.Controller.extend({
|
||||||
replyAsNewTopicDraft: Em.computed.equal('model.draftKey', Composer.REPLY_AS_NEW_TOPIC_KEY),
|
replyAsNewTopicDraft: Em.computed.equal('model.draftKey', Composer.REPLY_AS_NEW_TOPIC_KEY),
|
||||||
checkedMessages: false,
|
checkedMessages: false,
|
||||||
messageCount: null,
|
messageCount: null,
|
||||||
|
|
||||||
showEditReason: false,
|
showEditReason: false,
|
||||||
editReason: null,
|
editReason: null,
|
||||||
scopedCategoryId: null,
|
scopedCategoryId: null,
|
||||||
|
@ -54,6 +54,8 @@ export default Ember.Controller.extend({
|
||||||
lastValidatedAt: null,
|
lastValidatedAt: null,
|
||||||
isUploading: false,
|
isUploading: false,
|
||||||
topic: null,
|
topic: null,
|
||||||
|
linkLookup: null,
|
||||||
|
|
||||||
showToolbar: Em.computed({
|
showToolbar: Em.computed({
|
||||||
get(){
|
get(){
|
||||||
const keyValueStore = this.container.lookup('key-value-store:main');
|
const keyValueStore = this.container.lookup('key-value-store:main');
|
||||||
|
@ -104,6 +106,39 @@ export default Ember.Controller.extend({
|
||||||
}.property('model.creatingPrivateMessage', 'model.targetUsernames'),
|
}.property('model.creatingPrivateMessage', 'model.targetUsernames'),
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
|
addLinkLookup(linkLookup) {
|
||||||
|
this.set('linkLookup', linkLookup);
|
||||||
|
},
|
||||||
|
|
||||||
|
afterRefresh($preview) {
|
||||||
|
const linkLookup = this.get('linkLookup');
|
||||||
|
if (linkLookup) {
|
||||||
|
const $links = $('a[href]', $preview);
|
||||||
|
|
||||||
|
$links.each((idx, l) => {
|
||||||
|
const href = $(l).prop('href');
|
||||||
|
if (href && href.length) {
|
||||||
|
const [warn, info] = linkLookup.check(href);
|
||||||
|
|
||||||
|
if (warn) {
|
||||||
|
const body = I18n.t('composer.duplicate_link', {
|
||||||
|
domain: info.domain,
|
||||||
|
username: info.username,
|
||||||
|
ago: relativeAge(new Date(info.posted_at), { format: 'medium' }),
|
||||||
|
href
|
||||||
|
});
|
||||||
|
this.appEvents.trigger('composer-messages:create', {
|
||||||
|
extraClass: 'custom-body',
|
||||||
|
templateName: 'custom-body',
|
||||||
|
body
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
toggleWhisper() {
|
toggleWhisper() {
|
||||||
this.toggleProperty('model.whisper');
|
this.toggleProperty('model.whisper');
|
||||||
|
@ -435,6 +470,8 @@ export default Ember.Controller.extend({
|
||||||
|
|
||||||
// Given a potential instance and options, set the model for this composer.
|
// Given a potential instance and options, set the model for this composer.
|
||||||
_setModel(composerModel, opts) {
|
_setModel(composerModel, opts) {
|
||||||
|
this.set('linkList', null);
|
||||||
|
|
||||||
if (opts.draft) {
|
if (opts.draft) {
|
||||||
composerModel = loadDraft(this.store, opts);
|
composerModel = loadDraft(this.store, opts);
|
||||||
if (composerModel) {
|
if (composerModel) {
|
||||||
|
|
24
app/assets/javascripts/discourse/lib/link-lookup.js.es6
Normal file
24
app/assets/javascripts/discourse/lib/link-lookup.js.es6
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
const _warned = {};
|
||||||
|
|
||||||
|
export default class LinkLookup {
|
||||||
|
|
||||||
|
constructor(links) {
|
||||||
|
this._links = links;
|
||||||
|
}
|
||||||
|
|
||||||
|
check(href) {
|
||||||
|
if (_warned[href]) { return [false, null]; }
|
||||||
|
|
||||||
|
const normalized = href.replace(/^https?:\/\//, '');
|
||||||
|
if (_warned[normalized]) { return [false, null]; }
|
||||||
|
|
||||||
|
const linkInfo = this._links[normalized];
|
||||||
|
if (linkInfo) {
|
||||||
|
_warned[href] = true;
|
||||||
|
_warned[normalized] = true;
|
||||||
|
return [true, linkInfo];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [false, null];
|
||||||
|
}
|
||||||
|
};
|
|
@ -9,7 +9,7 @@
|
||||||
{{/popup-menu}}
|
{{/popup-menu}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{composer-messages composer=model messageCount=messageCount}}
|
{{composer-messages composer=model messageCount=messageCount addLinkLookup="addLinkLookup"}}
|
||||||
|
|
||||||
<div class='control'>
|
<div class='control'>
|
||||||
{{#if site.mobileView}}
|
{{#if site.mobileView}}
|
||||||
|
@ -92,7 +92,8 @@
|
||||||
importQuote="importQuote"
|
importQuote="importQuote"
|
||||||
showOptions="showOptions"
|
showOptions="showOptions"
|
||||||
showToolbar=showToolbar
|
showToolbar=showToolbar
|
||||||
showUploadSelector="showUploadSelector"}}
|
showUploadSelector="showUploadSelector"
|
||||||
|
afterRefresh="afterRefresh"}}
|
||||||
|
|
||||||
{{#if currentUser}}
|
{{#if currentUser}}
|
||||||
<div class='submit-panel'>
|
<div class='submit-panel'>
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
<a href {{action "closeMessage"}} class='close'>{{fa-icon "close"}}</a>
|
||||||
|
<p>{{{message.body}}}</p>
|
|
@ -78,6 +78,13 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.custom-body {
|
||||||
|
background-color: dark-light-diff($tertiary, $secondary, 85%, -65%);
|
||||||
|
p {
|
||||||
|
max-width: 98%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.similar-topics {
|
.similar-topics {
|
||||||
background-color: dark-light-diff($tertiary, $secondary, 85%, -65%);
|
background-color: dark-light-diff($tertiary, $secondary, 85%, -65%);
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,16 @@ class ComposerMessagesController < ApplicationController
|
||||||
before_filter :ensure_logged_in
|
before_filter :ensure_logged_in
|
||||||
|
|
||||||
def index
|
def index
|
||||||
finder = ComposerMessagesFinder.new(current_user, params.slice(:composerAction, :topic_id, :post_id))
|
finder = ComposerMessagesFinder.new(current_user, params.slice(:composer_action, :topic_id, :post_id))
|
||||||
json = { composer_messages: [finder.find].compact }
|
json = { composer_messages: [finder.find].compact }
|
||||||
|
|
||||||
|
if params[:composer_action] == "reply" && params[:topic_id].present?
|
||||||
|
topic = Topic.where(id: params[:topic_id]).first
|
||||||
|
if guardian.can_see?(topic)
|
||||||
|
json[:extras] = {duplicate_lookup: TopicLink.duplicate_lookup(topic)}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
render_json_dump(json, rest_serializer: true)
|
render_json_dump(json, rest_serializer: true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -214,6 +214,27 @@ class TopicLink < ActiveRecord::Base
|
||||||
def crawl_link_title
|
def crawl_link_title
|
||||||
Jobs.enqueue(:crawl_topic_link, topic_link_id: id)
|
Jobs.enqueue(:crawl_topic_link, topic_link_id: id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.duplicate_lookup(topic)
|
||||||
|
builder = SqlBuilder.new("SELECT tl.url, tl.domain, u.username_lower, p.created_at
|
||||||
|
FROM topic_links AS tl
|
||||||
|
INNER JOIN posts AS p ON p.id = tl.post_id
|
||||||
|
INNER JOIN users AS u ON p.user_id = u.id
|
||||||
|
/*where*/
|
||||||
|
ORDER BY p.created_at DESC
|
||||||
|
LIMIT 200")
|
||||||
|
|
||||||
|
builder.where('tl.topic_id = :topic_id', topic_id: topic.id)
|
||||||
|
|
||||||
|
lookup = {}
|
||||||
|
|
||||||
|
builder.exec.to_a.each do |row|
|
||||||
|
normalized = row['url'].downcase.sub(/^https?:\/\//, '')
|
||||||
|
lookup[normalized] = {domain: row['domain'], username: row['username_lower'], posted_at: row['created_at']}
|
||||||
|
end
|
||||||
|
|
||||||
|
lookup
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# == Schema Information
|
# == Schema Information
|
||||||
|
|
|
@ -989,6 +989,7 @@ en:
|
||||||
drafts_offline: "drafts offline"
|
drafts_offline: "drafts offline"
|
||||||
|
|
||||||
group_mentioned: "By mentioning {{group}}, you are about to notify <a href='{{group_link}}'>{{count}} people</a>."
|
group_mentioned: "By mentioning {{group}}, you are about to notify <a href='{{group_link}}'>{{count}} people</a>."
|
||||||
|
duplicate_link: "It looks like your link to <b>{{domain}}</b> was already posted in the topic by <b>@{{username}}</b> in <a href='{{href}}'>an earlier reply on {{ago}}</a>"
|
||||||
|
|
||||||
error:
|
error:
|
||||||
title_missing: "Title is required"
|
title_missing: "Title is required"
|
||||||
|
|
|
@ -29,7 +29,7 @@ class ComposerMessagesFinder
|
||||||
education_posts_text = I18n.t('education.until_posts', count: SiteSetting.educate_until_posts)
|
education_posts_text = I18n.t('education.until_posts', count: SiteSetting.educate_until_posts)
|
||||||
return {
|
return {
|
||||||
id: 'education',
|
id: 'education',
|
||||||
templateName: 'composer/education',
|
templateName: 'education',
|
||||||
wait_for_typing: true,
|
wait_for_typing: true,
|
||||||
body: PrettyText.cook(I18n.t(education_key, education_posts_text: education_posts_text, site_name: SiteSetting.title))
|
body: PrettyText.cook(I18n.t(education_key, education_posts_text: education_posts_text, site_name: SiteSetting.title))
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ class ComposerMessagesFinder
|
||||||
|
|
||||||
{
|
{
|
||||||
id: 'too_many_replies',
|
id: 'too_many_replies',
|
||||||
templateName: 'composer/education',
|
templateName: 'education',
|
||||||
body: PrettyText.cook(I18n.t('education.too_many_replies', newuser_max_replies_per_topic: SiteSetting.newuser_max_replies_per_topic))
|
body: PrettyText.cook(I18n.t('education.too_many_replies', newuser_max_replies_per_topic: SiteSetting.newuser_max_replies_per_topic))
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -70,7 +70,7 @@ class ComposerMessagesFinder
|
||||||
# Return the message
|
# Return the message
|
||||||
{
|
{
|
||||||
id: 'avatar',
|
id: 'avatar',
|
||||||
templateName: 'composer/education',
|
templateName: 'education',
|
||||||
body: PrettyText.cook(I18n.t('education.avatar', profile_path: "/users/#{@user.username_lower}"))
|
body: PrettyText.cook(I18n.t('education.avatar', profile_path: "/users/#{@user.username_lower}"))
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -108,7 +108,7 @@ class ComposerMessagesFinder
|
||||||
|
|
||||||
{
|
{
|
||||||
id: 'sequential_replies',
|
id: 'sequential_replies',
|
||||||
templateName: 'composer/education',
|
templateName: 'education',
|
||||||
wait_for_typing: true,
|
wait_for_typing: true,
|
||||||
extraClass: 'education-message',
|
extraClass: 'education-message',
|
||||||
body: PrettyText.cook(I18n.t('education.sequential_replies'))
|
body: PrettyText.cook(I18n.t('education.sequential_replies'))
|
||||||
|
@ -140,7 +140,7 @@ class ComposerMessagesFinder
|
||||||
|
|
||||||
{
|
{
|
||||||
id: 'dominating_topic',
|
id: 'dominating_topic',
|
||||||
templateName: 'composer/education',
|
templateName: 'education',
|
||||||
wait_for_typing: true,
|
wait_for_typing: true,
|
||||||
extraClass: 'education-message',
|
extraClass: 'education-message',
|
||||||
body: PrettyText.cook(I18n.t('education.dominating_topic', percent: (ratio * 100).round))
|
body: PrettyText.cook(I18n.t('education.dominating_topic', percent: (ratio * 100).round))
|
||||||
|
@ -156,7 +156,7 @@ class ComposerMessagesFinder
|
||||||
|
|
||||||
{
|
{
|
||||||
id: 'reviving_old',
|
id: 'reviving_old',
|
||||||
templateName: 'composer/education',
|
templateName: 'education',
|
||||||
wait_for_typing: false,
|
wait_for_typing: false,
|
||||||
extraClass: 'education-message',
|
extraClass: 'education-message',
|
||||||
body: PrettyText.cook(I18n.t('education.reviving_old_topic', days: (Time.zone.now - @topic.last_posted_at).round / 1.day))
|
body: PrettyText.cook(I18n.t('education.reviving_old_topic', days: (Time.zone.now - @topic.last_posted_at).round / 1.day))
|
||||||
|
@ -166,11 +166,11 @@ class ComposerMessagesFinder
|
||||||
private
|
private
|
||||||
|
|
||||||
def creating_topic?
|
def creating_topic?
|
||||||
@details[:composerAction] == "createTopic"
|
@details[:composer_action] == "createTopic"
|
||||||
end
|
end
|
||||||
|
|
||||||
def replying?
|
def replying?
|
||||||
@details[:composerAction] == "reply"
|
@details[:composer_action] == "reply"
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,7 +6,7 @@ describe ComposerMessagesFinder do
|
||||||
|
|
||||||
context "delegates work" do
|
context "delegates work" do
|
||||||
let(:user) { Fabricate.build(:user) }
|
let(:user) { Fabricate.build(:user) }
|
||||||
let(:finder) { ComposerMessagesFinder.new(user, composerAction: 'createTopic') }
|
let(:finder) { ComposerMessagesFinder.new(user, composer_action: 'createTopic') }
|
||||||
|
|
||||||
it "calls all the message finders" do
|
it "calls all the message finders" do
|
||||||
finder.expects(:check_education_message).once
|
finder.expects(:check_education_message).once
|
||||||
|
@ -24,7 +24,7 @@ describe ComposerMessagesFinder do
|
||||||
let(:user) { Fabricate.build(:user) }
|
let(:user) { Fabricate.build(:user) }
|
||||||
|
|
||||||
context 'creating topic' do
|
context 'creating topic' do
|
||||||
let(:finder) { ComposerMessagesFinder.new(user, composerAction: 'createTopic') }
|
let(:finder) { ComposerMessagesFinder.new(user, composer_action: 'createTopic') }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
SiteSetting.stubs(:educate_until_posts).returns(10)
|
SiteSetting.stubs(:educate_until_posts).returns(10)
|
||||||
|
@ -42,7 +42,7 @@ describe ComposerMessagesFinder do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'creating reply' do
|
context 'creating reply' do
|
||||||
let(:finder) { ComposerMessagesFinder.new(user, composerAction: 'reply') }
|
let(:finder) { ComposerMessagesFinder.new(user, composer_action: 'reply') }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
SiteSetting.stubs(:educate_until_posts).returns(10)
|
SiteSetting.stubs(:educate_until_posts).returns(10)
|
||||||
|
@ -64,7 +64,7 @@ describe ComposerMessagesFinder do
|
||||||
let(:user) { Fabricate.build(:user) }
|
let(:user) { Fabricate.build(:user) }
|
||||||
|
|
||||||
context 'replying' do
|
context 'replying' do
|
||||||
let(:finder) { ComposerMessagesFinder.new(user, composerAction: 'reply') }
|
let(:finder) { ComposerMessagesFinder.new(user, composer_action: 'reply') }
|
||||||
|
|
||||||
it "has no message when `posted_too_much_in_topic?` is false" do
|
it "has no message when `posted_too_much_in_topic?` is false" do
|
||||||
user.expects(:posted_too_much_in_topic?).returns(false)
|
user.expects(:posted_too_much_in_topic?).returns(false)
|
||||||
|
@ -80,7 +80,7 @@ describe ComposerMessagesFinder do
|
||||||
end
|
end
|
||||||
|
|
||||||
context '.check_avatar_notification' do
|
context '.check_avatar_notification' do
|
||||||
let(:finder) { ComposerMessagesFinder.new(user, composerAction: 'createTopic') }
|
let(:finder) { ComposerMessagesFinder.new(user, composer_action: 'createTopic') }
|
||||||
let(:user) { Fabricate(:user) }
|
let(:user) { Fabricate(:user) }
|
||||||
|
|
||||||
context "success" do
|
context "success" do
|
||||||
|
@ -141,16 +141,16 @@ describe ComposerMessagesFinder do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "does not give a message for new topics" do
|
it "does not give a message for new topics" do
|
||||||
finder = ComposerMessagesFinder.new(user, composerAction: 'createTopic')
|
finder = ComposerMessagesFinder.new(user, composer_action: 'createTopic')
|
||||||
expect(finder.check_sequential_replies).to be_blank
|
expect(finder.check_sequential_replies).to be_blank
|
||||||
end
|
end
|
||||||
|
|
||||||
it "does not give a message without a topic id" do
|
it "does not give a message without a topic id" do
|
||||||
expect(ComposerMessagesFinder.new(user, composerAction: 'reply').check_sequential_replies).to be_blank
|
expect(ComposerMessagesFinder.new(user, composer_action: 'reply').check_sequential_replies).to be_blank
|
||||||
end
|
end
|
||||||
|
|
||||||
context "reply" do
|
context "reply" do
|
||||||
let(:finder) { ComposerMessagesFinder.new(user, composerAction: 'reply', topic_id: topic.id) }
|
let(:finder) { ComposerMessagesFinder.new(user, composer_action: 'reply', topic_id: topic.id) }
|
||||||
|
|
||||||
it "does not give a message to users who are still in the 'education' phase" do
|
it "does not give a message to users who are still in the 'education' phase" do
|
||||||
user.stubs(:post_count).returns(9)
|
user.stubs(:post_count).returns(9)
|
||||||
|
@ -216,16 +216,16 @@ describe ComposerMessagesFinder do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "does not give a message for new topics" do
|
it "does not give a message for new topics" do
|
||||||
finder = ComposerMessagesFinder.new(user, composerAction: 'createTopic')
|
finder = ComposerMessagesFinder.new(user, composer_action: 'createTopic')
|
||||||
expect(finder.check_dominating_topic).to be_blank
|
expect(finder.check_dominating_topic).to be_blank
|
||||||
end
|
end
|
||||||
|
|
||||||
it "does not give a message without a topic id" do
|
it "does not give a message without a topic id" do
|
||||||
expect(ComposerMessagesFinder.new(user, composerAction: 'reply').check_dominating_topic).to be_blank
|
expect(ComposerMessagesFinder.new(user, composer_action: 'reply').check_dominating_topic).to be_blank
|
||||||
end
|
end
|
||||||
|
|
||||||
context "reply" do
|
context "reply" do
|
||||||
let(:finder) { ComposerMessagesFinder.new(user, composerAction: 'reply', topic_id: topic.id) }
|
let(:finder) { ComposerMessagesFinder.new(user, composer_action: 'reply', topic_id: topic.id) }
|
||||||
|
|
||||||
it "does not give a message to users who are still in the 'education' phase" do
|
it "does not give a message to users who are still in the 'education' phase" do
|
||||||
user.stubs(:post_count).returns(9)
|
user.stubs(:post_count).returns(9)
|
||||||
|
@ -288,8 +288,8 @@ describe ComposerMessagesFinder do
|
||||||
let(:topic) { Fabricate(:topic) }
|
let(:topic) { Fabricate(:topic) }
|
||||||
|
|
||||||
it "does not give a message without a topic id" do
|
it "does not give a message without a topic id" do
|
||||||
expect(described_class.new(user, composerAction: 'createTopic').check_reviving_old_topic).to be_blank
|
expect(described_class.new(user, composer_action: 'createTopic').check_reviving_old_topic).to be_blank
|
||||||
expect(described_class.new(user, composerAction: 'reply').check_reviving_old_topic).to be_blank
|
expect(described_class.new(user, composer_action: 'reply').check_reviving_old_topic).to be_blank
|
||||||
end
|
end
|
||||||
|
|
||||||
context "a reply" do
|
context "a reply" do
|
||||||
|
@ -300,12 +300,12 @@ describe ComposerMessagesFinder do
|
||||||
|
|
||||||
it "does not notify if last post is recent" do
|
it "does not notify if last post is recent" do
|
||||||
topic = Fabricate(:topic, last_posted_at: 1.hour.ago)
|
topic = Fabricate(:topic, last_posted_at: 1.hour.ago)
|
||||||
expect(described_class.new(user, composerAction: 'reply', topic_id: topic.id).check_reviving_old_topic).to be_blank
|
expect(described_class.new(user, composer_action: 'reply', topic_id: topic.id).check_reviving_old_topic).to be_blank
|
||||||
end
|
end
|
||||||
|
|
||||||
it "notifies if last post is old" do
|
it "notifies if last post is old" do
|
||||||
topic = Fabricate(:topic, last_posted_at: 181.days.ago)
|
topic = Fabricate(:topic, last_posted_at: 181.days.ago)
|
||||||
expect(described_class.new(user, composerAction: 'reply', topic_id: topic.id).check_reviving_old_topic).not_to be_blank
|
expect(described_class.new(user, composer_action: 'reply', topic_id: topic.id).check_reviving_old_topic).not_to be_blank
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -316,12 +316,12 @@ describe ComposerMessagesFinder do
|
||||||
|
|
||||||
it "does not notify if last post is new" do
|
it "does not notify if last post is new" do
|
||||||
topic = Fabricate(:topic, last_posted_at: 1.hour.ago)
|
topic = Fabricate(:topic, last_posted_at: 1.hour.ago)
|
||||||
expect(described_class.new(user, composerAction: 'reply', topic_id: topic.id).check_reviving_old_topic).to be_blank
|
expect(described_class.new(user, composer_action: 'reply', topic_id: topic.id).check_reviving_old_topic).to be_blank
|
||||||
end
|
end
|
||||||
|
|
||||||
it "does not notify if last post is old" do
|
it "does not notify if last post is old" do
|
||||||
topic = Fabricate(:topic, last_posted_at: 365.days.ago)
|
topic = Fabricate(:topic, last_posted_at: 365.days.ago)
|
||||||
expect(described_class.new(user, composerAction: 'reply', topic_id: topic.id).check_reviving_old_topic).to be_blank
|
expect(described_class.new(user, composer_action: 'reply', topic_id: topic.id).check_reviving_old_topic).to be_blank
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,7 +10,7 @@ describe ComposerMessagesController do
|
||||||
|
|
||||||
context 'when logged in' do
|
context 'when logged in' do
|
||||||
let!(:user) { log_in }
|
let!(:user) { log_in }
|
||||||
let(:args) { {'topic_id' => '123', 'post_id' => '333', 'composerAction' => 'reply'} }
|
let(:args) { {'topic_id' => '123', 'post_id' => '333', 'composer_action' => 'reply'} }
|
||||||
|
|
||||||
it 'redirects to your user preferences' do
|
it 'redirects to your user preferences' do
|
||||||
xhr :get, :index
|
xhr :get, :index
|
||||||
|
|
|
@ -110,6 +110,7 @@ Fabricator(:post_with_external_links, from: :post) do
|
||||||
raw "
|
raw "
|
||||||
Here's a link to twitter: http://twitter.com
|
Here's a link to twitter: http://twitter.com
|
||||||
And a link to google: http://google.com
|
And a link to google: http://google.com
|
||||||
|
And a secure link to google: https://google.com
|
||||||
And a markdown link: [forumwarz](http://forumwarz.com)
|
And a markdown link: [forumwarz](http://forumwarz.com)
|
||||||
And a markdown link with a period after it [codinghorror](http://www.codinghorror.com/blog).
|
And a markdown link with a period after it [codinghorror](http://www.codinghorror.com/blog).
|
||||||
"
|
"
|
||||||
|
|
|
@ -258,7 +258,7 @@ http://b.com/#{'a'*500}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'counts_for and topic_map' do
|
describe 'query methods' do
|
||||||
it 'returns blank without posts' do
|
it 'returns blank without posts' do
|
||||||
expect(TopicLink.counts_for(Guardian.new, nil, nil)).to be_blank
|
expect(TopicLink.counts_for(Guardian.new, nil, nil)).to be_blank
|
||||||
end
|
end
|
||||||
|
@ -274,6 +274,19 @@ http://b.com/#{'a'*500}
|
||||||
TopicLink.counts_for(Guardian.new, post.topic, [post])
|
TopicLink.counts_for(Guardian.new, post.topic, [post])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'creates a valid topic lookup' do
|
||||||
|
TopicLink.extract_from(post)
|
||||||
|
|
||||||
|
lookup = TopicLink.duplicate_lookup(post.topic)
|
||||||
|
expect(lookup).to be_present
|
||||||
|
expect(lookup['google.com']).to be_present
|
||||||
|
|
||||||
|
ch = lookup['www.codinghorror.com/blog']
|
||||||
|
expect(ch).to be_present
|
||||||
|
expect(ch[:domain]).to eq('www.codinghorror.com')
|
||||||
|
expect(ch[:username]).to eq(post.username)
|
||||||
|
expect(ch[:posted_at]).to be_present
|
||||||
|
end
|
||||||
|
|
||||||
it 'has the correct results' do
|
it 'has the correct results' do
|
||||||
TopicLink.extract_from(post)
|
TopicLink.extract_from(post)
|
||||||
|
@ -285,7 +298,7 @@ http://b.com/#{'a'*500}
|
||||||
expect(counts_for[post.id].first[:clicks]).to eq(1)
|
expect(counts_for[post.id].first[:clicks]).to eq(1)
|
||||||
|
|
||||||
array = TopicLink.topic_map(Guardian.new, post.topic_id)
|
array = TopicLink.topic_map(Guardian.new, post.topic_id)
|
||||||
expect(array.length).to eq(4)
|
expect(array.length).to eq(5)
|
||||||
expect(array[0]["clicks"]).to eq("1")
|
expect(array[0]["clicks"]).to eq("1")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue