Merge pull request #4522 from fantasticfears/featured-link

FEATURE: Allow posting a link with the topic
This commit is contained in:
Neil Lalonde
2016-12-05 16:05:38 -05:00
committed by GitHub
50 changed files with 503 additions and 77 deletions

View File

@@ -13,7 +13,8 @@ export default Ember.Component.extend({
'composer.canEditTitle:edit-title',
'composer.createdPost:created-post',
'composer.creatingTopic:topic',
'composer.whisper:composing-whisper'],
'composer.whisper:composing-whisper',
'composer.showComposerEditor::topic-featured-link-only'],
@computed('composer.composeState')
composeState(composeState) {
@@ -27,7 +28,7 @@ export default Ember.Component.extend({
this.appEvents.trigger("composer:resized");
},
@observes('composeState', 'composer.action')
@observes('composeState', 'composer.action', 'composer.canEditTopicFeaturedLink')
resize() {
Ember.run.scheduleOnce('afterRender', () => {
if (!this.element || this.isDestroying || this.isDestroyed) { return; }

View File

@@ -21,6 +21,9 @@ export default Ember.Controller.extend(ModalFunctionality, {
if (this.site.mobileView) { this.set("viewMode", "inline"); }
}.on("init"),
previousFeaturedLink: Em.computed.alias('model.featured_link_changes.previous'),
currentFeaturedLink: Em.computed.alias('model.featured_link_changes.current'),
previousTagChanges: customTagArray('model.tags_changes.previous'),
currentTagChanges: customTagArray('model.tags_changes.current'),

View File

@@ -160,6 +160,14 @@ export default Ember.Controller.extend(SelectedPostsCount, BufferedContent, {
return post => this.postSelected(post);
}.property(),
@computed('model.isPrivateMessage', 'model.category.id')
canEditTopicFeaturedLink(isPrivateMessage, categoryId) {
if (!this.siteSettings.topic_featured_link_enabled || isPrivateMessage) { return false; }
const categoryIds = this.site.get('topic_featured_link_allowed_category_ids');
return categoryIds === undefined || !categoryIds.length || categoryIds.indexOf(categoryId) !== -1;
},
@computed('model.isPrivateMessage')
canEditTags(isPrivateMessage) {
return !isPrivateMessage && this.site.get('can_tag_topics');

View File

@@ -0,0 +1,6 @@
import { registerUnbound } from 'discourse-common/lib/helpers';
import renderTopicFeaturedLink from 'discourse/lib/render-topic-featured-link';
export default registerUnbound('topic-featured-link', function(topic, params) {
return new Handlebars.SafeString(renderTopicFeaturedLink(topic, params));
});

View File

@@ -0,0 +1,46 @@
import { extractDomainFromUrl } from 'discourse/lib/utilities';
import { h } from 'virtual-dom';
const _decorators = [];
export function addFeaturedLinkMetaDecorator(decorator) {
_decorators.push(decorator);
}
function extractLinkMeta(topic) {
const href = topic.featured_link, target = Discourse.SiteSettings.open_topic_featured_link_in_external_window ? '_blank' : '';
if (!href) { return; }
let domain = extractDomainFromUrl(href);
if (!domain) { return; }
// www appears frequently, so we truncate it
if (domain && domain.substr(0, 4) === 'www.') {
domain = domain.substring(4);
}
const meta = { target, href, domain, rel: 'nofollow' };
if (_decorators.length) {
_decorators.forEach(cb => cb(meta));
}
return meta;
}
export default function renderTopicFeaturedLink(topic) {
const meta = extractLinkMeta(topic);
if (meta) {
return `<a class="topic-featured-link" rel="${meta.rel}" target="${meta.target}" href="${meta.href}">${meta.domain}</a>`;
} else {
return '';
}
};
export function topicFeaturedLinkNode(topic) {
const meta = extractLinkMeta(topic);
if (meta) {
return h('a.topic-featured-link', {
attributes: { href: meta.href, rel: meta.rel, target: meta.target }
}, meta.domain);
}
}

View File

@@ -169,6 +169,18 @@ const Category = RestModel.extend({
@computed("id")
isUncategorizedCategory(id) {
return id === Discourse.Site.currentProp("uncategorized_category_id");
},
@computed('custom_fields.topic_featured_link_allowed')
topicFeaturedLinkAllowed: {
get(allowed) {
return allowed === "true";
},
set(value) {
value = value ? "true" : "false";
this.set("custom_fields.topic_featured_link_allowed", value);
return value;
}
}
});

View File

@@ -32,13 +32,15 @@ const CLOSED = 'closed',
target_usernames: 'targetUsernames',
typing_duration_msecs: 'typingTime',
composer_open_duration_msecs: 'composerTime',
tags: 'tags'
tags: 'tags',
featured_link: 'featuredLink'
},
_edit_topic_serializer = {
title: 'topic.title',
categoryId: 'topic.category.id',
tags: 'topic.tags'
tags: 'topic.tags',
featuredLink: 'topic.featured_link'
};
const Composer = RestModel.extend({
@@ -136,6 +138,14 @@ const Composer = RestModel.extend({
canEditTitle: Em.computed.or('creatingTopic', 'creatingPrivateMessage', 'editingFirstPost'),
canCategorize: Em.computed.and('canEditTitle', 'notCreatingPrivateMessage'),
@computed('canEditTitle', 'creatingPrivateMessage', 'categoryId')
canEditTopicFeaturedLink(canEditTitle, creatingPrivateMessage, categoryId) {
if (!this.siteSettings.topic_featured_link_enabled || !canEditTitle || creatingPrivateMessage) { return false; }
const categoryIds = this.site.get('topic_featured_link_allowed_category_ids');
return categoryIds === undefined || !categoryIds.length || categoryIds.indexOf(categoryId) !== -1;
},
// Determine the appropriate title for this action
actionTitle: function() {
const topic = this.get('topic');
@@ -180,6 +190,10 @@ const Composer = RestModel.extend({
}.property('action', 'post', 'topic', 'topic.title'),
@computed('canEditTopicFeaturedLink')
showComposerEditor(canEditTopicFeaturedLink) {
return canEditTopicFeaturedLink ? !this.siteSettings.topic_featured_link_onebox : true;
},
// whether to disable the post button
cantSubmitPost: function() {
@@ -269,11 +283,12 @@ const Composer = RestModel.extend({
}
}.property('privateMessage'),
missingReplyCharacters: function() {
const postType = this.get('post.post_type');
if (postType === this.site.get('post_types.small_action')) { return 0; }
return this.get('minimumPostLength') - this.get('replyLength');
}.property('minimumPostLength', 'replyLength'),
@computed('minimumPostLength', 'replyLength', 'canEditTopicFeaturedLink')
missingReplyCharacters(minimumPostLength, replyLength, canEditTopicFeaturedLink) {
if (this.get('post.post_type') === this.site.get('post_types.small_action') ||
canEditTopicFeaturedLink && this.siteSettings.topic_featured_link_onebox) { return 0; }
return minimumPostLength - replyLength;
},
/**
Minimum number of characters for a post body to be valid.
@@ -492,6 +507,14 @@ const Composer = RestModel.extend({
save(opts) {
if (!this.get('cantSubmitPost')) {
// change category may result in some effect for topic featured link
if (this.get('canEditTopicFeaturedLink')) {
if (this.siteSettings.topic_featured_link_onebox) { this.set('reply', null); }
} else {
this.set('featuredLink', null);
}
return this.get('editingPost') ? this.editPost(opts) : this.createPost(opts);
}
},
@@ -512,7 +535,8 @@ const Composer = RestModel.extend({
stagedPost: false,
typingTime: 0,
composerOpened: null,
composerTotalOpened: 0
composerTotalOpened: 0,
featuredLink: null
});
},

View File

@@ -19,6 +19,17 @@
</label>
</section>
{{#if siteSettings.topic_featured_link_enabled}}
<section class='field'>
<div class="allowed-topic-featured-link-category">
<label class="checkbox-label">
{{input type="checkbox" checked=category.topicFeaturedLinkAllowed}}
{{i18n 'category.topic_featured_link_allowed'}}
</label>
</div>
</section>
{{/if}}
<section class="field">
<label>
{{i18n "category.sort_order"}}

View File

@@ -2,12 +2,16 @@
{{bound-category-link topic.category.parentCategory}}
{{/if}}
{{bound-category-link topic.category hideParent=true}}
{{#if siteSettings.tagging_enabled}}
<div class="list-tags">
{{#each topic.tags as |t|}}
{{discourse-tag t}}
{{/each}}
</div>
{{/if}}
<div class="topic-header-extra">
{{#if siteSettings.tagging_enabled}}
<div class="list-tags">
{{#each topic.tags as |t|}}
{{discourse-tag t}}
{{/each}}
</div>
{{/if}}
{{#if siteSettings.topic_featured_link_enabled}}
{{topic-featured-link topic}}
{{/if}}
</div>
{{plugin-outlet "topic-category"}}

View File

@@ -80,9 +80,13 @@
{{/if}}
{{render "additional-composer-buttons" model}}
{{/if}}
{{#if model.canEditTopicFeaturedLink}}
<div class="topic-featured-link-input">
{{text-field tabindex="4" type="url" value=model.featuredLink id='topic-featured-link' placeholderKey="composer.topic_featured_link_placeholder"}}
</div>
{{/if}}
</div>
{{/if}}
{{plugin-outlet "composer-fields"}}
</div>

View File

@@ -7,6 +7,9 @@
<td class='main-link clearfix' colspan="{{titleColSpan}}">
{{raw "topic-status" topic=topic}}
{{topic-link topic}}
{{#if topic.featured_link}}
{{topic-featured-link topic}}
{{/if}}
{{plugin-outlet "topic-list-after-title"}}
{{#if showTopicPostBadges}}
{{raw "topic-post-badges" unread=topic.unread newPosts=topic.displayNewPosts unseen=topic.unseen url=topic.lastUnreadUrl}}

View File

@@ -86,6 +86,13 @@
{{/each}}
</div>
{{/if}}
{{#if model.featured_link_changes}}
<div class='row'>
{{model.featured_link_changes.previous}}
&rarr;
{{model.featured_link_changes.current}}
</div>
{{/if}}
{{plugin-outlet "post-revisions"}}

View File

@@ -25,6 +25,9 @@
{{category-chooser valueAttribute="id" value=buffered.category_id}}
{{/if}}
{{#if canEditTopicFeaturedLink}}
{{text-field type="url" value=buffered.featured_link id='topic-featured-link' placeholderKey="composer.topic_featured_link_placeholder"}}
{{/if}}
{{#if canEditTags}}
<br>
{{tag-chooser tags=buffered.tags categoryId=buffered.category_id}}

View File

@@ -4,6 +4,7 @@ import { iconNode } from 'discourse/helpers/fa-icon-node';
import DiscourseURL from 'discourse/lib/url';
import RawHtml from 'discourse/widgets/raw-html';
import { tagNode } from 'discourse/lib/render-tag';
import { topicFeaturedLinkNode } from 'discourse/lib/render-topic-featured-link';
export default createWidget('header-topic-info', {
tagName: 'div.extra-info-wrapper',
@@ -44,12 +45,19 @@ export default createWidget('header-topic-info', {
title.push(this.attach('category-link', { category }));
}
const extra = [];
if (this.siteSettings.tagging_enabled) {
const tags = topic.get('tags') || [];
if (tags.length) {
title.push(h('div.list-tags', tags.map(tagNode)));
extra.push(h('div.list-tags', tags.map(tagNode)));
}
}
if (this.siteSettings.topic_featured_link_enabled) {
extra.push(topicFeaturedLinkNode(attrs.topic));
}
if (extra) {
title.push(h('div.topic-header-extra', extra));
}
}
const contents = h('div.title-wrapper', title);