diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 5b2f654383c..a1888006db1 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -64,6 +64,7 @@ //= require ./discourse/models/draft //= require ./discourse/models/composer //= require ./discourse/models/user-badge +//= require_tree ./discourse/lib //= require_tree ./discourse/mixins //= require ./discourse/models/invite //= require ./discourse/controllers/discovery-sortable @@ -87,7 +88,6 @@ //= require ./discourse/helpers/loading-spinner //= require ./discourse/helpers/category-link //= require ./discourse/lib/export-result -//= require_tree ./discourse/lib //= require ./discourse/mapping-router //= require_tree ./discourse/controllers diff --git a/app/assets/javascripts/discourse/components/d-editor.js.es6 b/app/assets/javascripts/discourse/components/d-editor.js.es6 index 498dca8b39b..26d48d9b582 100644 --- a/app/assets/javascripts/discourse/components/d-editor.js.es6 +++ b/app/assets/javascripts/discourse/components/d-editor.js.es6 @@ -260,7 +260,14 @@ export default Ember.Component.extend({ // disable clicking on links in the preview this.$('.d-editor-preview').on('click.preview', e => { if (wantsNewWindow(e)) { return; } - if ($(e.target).is("a")) { + const $target = $(e.target); + if ($target.is("a.mention")) { + this.appEvents.trigger('click.discourse-preview-user-card-mention', $target); + } + if ($target.is("a.mention-group")) { + this.appEvents.trigger('click.discourse-preview-group-card-mention-group', $target); + } + if ($target.is("a")) { e.preventDefault(); return false; } diff --git a/app/assets/javascripts/discourse/components/group-card-contents.js.es6 b/app/assets/javascripts/discourse/components/group-card-contents.js.es6 new file mode 100644 index 00000000000..e21410fa511 --- /dev/null +++ b/app/assets/javascripts/discourse/components/group-card-contents.js.es6 @@ -0,0 +1,83 @@ +import { setting } from 'discourse/lib/computed'; +import { default as computed } from 'ember-addons/ember-computed-decorators'; +import CardContentsBase from 'discourse/mixins/card-contents-base'; +import CleansUp from 'discourse/mixins/cleans-up'; + +const maxMembersToDisplay = 10; + +export default Ember.Component.extend(CardContentsBase, CleansUp, { + elementId: 'group-card', + triggeringLinkClass: 'mention-group', + classNames: ['no-bg'], + classNameBindings: ['visible:show', 'showBadges', 'hasCardBadgeImage', 'isFixed:fixed'], + allowBackgrounds: setting('allow_profile_backgrounds'), + showBadges: setting('enable_badges'), + + postStream: Ember.computed.alias('topic.postStream'), + viewingTopic: Ember.computed.match('currentPath', /^topic\./), + + showMoreMembers: Ember.computed.gt('moreMembersCount', 0), + + group: null, + + @computed('group.user_count', 'group.members.length') + moreMembersCount: (memberCount, maxMemberDisplay) => memberCount - maxMemberDisplay, + + @computed('group') + groupPath(group) { + return `${Discourse.BaseUri}/groups/${group.name}`; + }, + + _showCallback(username, $target) { + this.store.find("group", username).then(group => { + this.setProperties({ group, visible: true }); + this._positionCard($target); + if(!group.flair_url && !group.flair_bg_color) { + group.set('flair_url', 'fa-users'); + } + group.set('limit', maxMembersToDisplay); + return group.findMembers(); + }).catch(() => this._close()).finally(() => this.set('loading', null)); + }, + + didInsertElement() { + this._super(); + }, + + _close() { + this._super(); + this.setProperties({ + group: null, + }); + }, + + cleanUp() { + this._close(); + }, + + actions: { + close() { + this._close(); + }, + + cancelFilter() { + const postStream = this.get('postStream'); + postStream.cancelFilter(); + postStream.refresh(); + this._close(); + }, + + composePrivateMessage(...args) { + this.sendAction('composePrivateMessage', ...args); + }, + + messageGroup() { + this.sendAction('createNewMessageViaParams', this.get('group.name')); + }, + + showGroup() { + this.sendAction('showGroup', this.get('group')); + this._close(); + } + } +}); diff --git a/app/assets/javascripts/discourse/components/user-card-contents.js.es6 b/app/assets/javascripts/discourse/components/user-card-contents.js.es6 index c8c869e160a..0d80e962e14 100644 --- a/app/assets/javascripts/discourse/components/user-card-contents.js.es6 +++ b/app/assets/javascripts/discourse/components/user-card-contents.js.es6 @@ -1,50 +1,83 @@ -import { wantsNewWindow } from 'discourse/lib/intercept-click'; -import CleansUp from 'discourse/mixins/cleans-up'; -import afterTransition from 'discourse/lib/after-transition'; -import { setting } from 'discourse/lib/computed'; import { default as computed, observes } from 'ember-addons/ember-computed-decorators'; -import DiscourseURL from 'discourse/lib/url'; import User from 'discourse/models/user'; -import { userPath } from 'discourse/lib/url'; +import { propertyNotEqual, setting } from 'discourse/lib/computed'; +import { durationTiny } from 'discourse/lib/formatter'; +import CanCheckEmails from 'discourse/mixins/can-check-emails'; +import CardContentsBase from 'discourse/mixins/card-contents-base'; +import CleansUp from 'discourse/mixins/cleans-up'; -const clickOutsideEventName = "mousedown.outside-user-card"; -const clickDataExpand = "click.discourse-user-card"; -const clickMention = "click.discourse-user-mention"; -const groupClickMention = "click.discourse-group-mention"; -const groupClickDataExpand = "click.discourse-group-card"; - -const maxMembersToDisplay = 10; - -export default Ember.Component.extend(CleansUp, { +export default Ember.Component.extend(CardContentsBase, CanCheckEmails, CleansUp, { elementId: 'user-card', - classNameBindings: ['visible:show', 'showBadges', 'hasCardBadgeImage', 'user.card_background::no-bg'], + triggeringLinkClass: 'mention', + classNameBindings: ['visible:show', 'showBadges', 'hasCardBadgeImage', 'user.card_background::no-bg', 'isFixed:fixed'], allowBackgrounds: setting('allow_profile_backgrounds'), showBadges: setting('enable_badges'), postStream: Ember.computed.alias('topic.postStream'), - viewingTopic: Ember.computed.match('currentPath', /^topic\./), + enoughPostsForFiltering: Ember.computed.gte('topicPostCount', 2), + showFilter: Ember.computed.and('viewingTopic', 'postStream.hasNoFilters', 'enoughPostsForFiltering'), + showName: propertyNotEqual('user.name', 'user.username'), + hasUserFilters: Ember.computed.gt('postStream.userFilters.length', 0), + isSuspended: Ember.computed.notEmpty('user.suspend_reason'), + showMoreBadges: Ember.computed.gt('moreBadgesCount', 0), + showDelete: Ember.computed.and("viewingAdmin", "showName", "user.canBeDeleted"), + linkWebsite: Ember.computed.not('user.isBasic'), + hasLocationOrWebsite: Ember.computed.or('user.location', 'user.website_name'), + showCheckEmail: Ember.computed.and('user.staged', 'canCheckEmails'), - visible: false, user: null, - group: null, - username: null, - avatar: null, - userLoading: null, - cardTarget: null, - post: null, - cardType: null, // If inside a topic topicPostCount: null, - @computed('cardType') - isUserShown(cardType) { - return cardType === 'user'; + @computed('user.name') + nameFirst(name) { + return !this.siteSettings.prioritize_username_in_ux && name && name.trim().length > 0; }, - @computed('cardType') - isGroupShown(cardType) { - return cardType === 'group'; + @computed('username', 'topicPostCount') + togglePostsLabel(username, count) { + return I18n.t("topic.filter_to", { username, count }); + }, + + @computed('user.user_fields.@each.value') + publicUserFields() { + const siteUserFields = this.site.get('user_fields'); + if (!Ember.isEmpty(siteUserFields)) { + const userFields = this.get('user.user_fields'); + return siteUserFields.filterBy('show_on_user_card', true).sortBy('position').map(field => { + Ember.set(field, 'dasherized_name', field.get('name').dasherize()); + const value = userFields ? userFields[field.get('id')] : null; + return Ember.isEmpty(value) ? null : Ember.Object.create({ value, field }); + }).compact(); + } + }, + + @computed("user.trust_level") + removeNoFollow(trustLevel) { + return trustLevel > 2 && !this.siteSettings.tl3_links_no_follow; + }, + + @computed('user.badge_count', 'user.featured_user_badges.length') + moreBadgesCount: (badgeCount, badgeLength) => badgeCount - badgeLength, + + @computed('user.time_read', 'user.recent_time_read') + showRecentTimeRead(timeRead, recentTimeRead) { + return timeRead !== recentTimeRead && recentTimeRead !== 0; + }, + + @computed('user.recent_time_read') + recentTimeRead(recentTimeReadSeconds) { + return durationTiny(recentTimeReadSeconds); + }, + + @computed('showRecentTimeRead', 'user.time_read', 'recentTimeRead') + timeReadTooltip(showRecent, timeRead, recentTimeRead) { + if (showRecent) { + return I18n.t('time_read_recently_tooltip', {time_read: durationTiny(timeRead), recent_time_read: recentTimeRead}); + } else { + return I18n.t('time_read_tooltip', {time_read: durationTiny(timeRead)}); + } }, @observes('user.card_background') @@ -62,180 +95,28 @@ export default Ember.Component.extend(CleansUp, { @computed('user.card_badge.image') hasCardBadgeImage: image => image && image.indexOf('fa-') !== 0, - _showUser(username, $target) { + _showCallback(username, $target) { const args = { stats: false }; args.include_post_count_for = this.get('topic.id'); - User.findByUsername(username, args).then(user => { if (user.topic_post_count) { this.set('topicPostCount', user.topic_post_count[args.include_post_count_for]); } - this.setProperties({ user, avatar: user, visible: true, cardType: 'user' }); - this._positionCard($target); - }).catch(() => this._close()).finally(() => this.set('userLoading', null)); - }, + this.setProperties({ user, visible: true }); - _showGroup(groupname, $target) { - this.store.find("group", groupname).then(group => { - this.setProperties({ group, avatar: group, visible: true, cardType: 'group' }); - this._positionCard($target); - if(!group.flair_url && !group.flair_bg_color) { - group.set('flair_url', 'fa-users'); - } - group.set('limit', maxMembersToDisplay); - return group.findMembers(); - }).catch(() => this._close()).finally(() => this.set('userLoading', null)); - }, - - _show(username, $target, userCardType) { - // No user card for anon - if (this.siteSettings.hide_user_profiles_from_public && !this.currentUser) { - return false; - } - - username = Ember.Handlebars.Utils.escapeExpression(username.toString()); - - // Don't show on mobile - if (this.site.mobileView) { - DiscourseURL.routeTo(userPath(username)); - return false; - } - - const currentUsername = this.get('username'); - if (username === currentUsername && this.get('userLoading') === username) { - return; - } - - const postId = $target.parents('article').data('post-id'); - - const wasVisible = this.get('visible'); - const previousTarget = this.get('cardTarget'); - const target = $target[0]; - if (wasVisible) { - this._close(); - if (target === previousTarget) { return; } - } - - const post = this.get('viewingTopic') && postId ? this.get('postStream').findLoadedPost(postId) : null; - this.setProperties({ username, userLoading: username, cardTarget: target, post }); - - if(userCardType === 'group') { - this._showGroup(username, $target); - } - else if(userCardType === 'user') { - this._showUser(username, $target); - } - - - return false; + }).catch(() => this._close()).finally(() => this.set('loading', null)); }, didInsertElement() { this._super(); - afterTransition(this.$(), this._hide.bind(this)); - - $('html').off(clickOutsideEventName) - .on(clickOutsideEventName, (e) => { - if (this.get('visible')) { - const $target = $(e.target); - if ($target.closest('[data-user-card]').data('userCard') || - $target.closest('a.mention').length > 0 || - $target.closest('#user-card').length > 0) { - return; - } - - this._close(); - } - - return true; - }); - - $('#main-outlet').on(clickDataExpand, '[data-user-card]', (e) => { - if (wantsNewWindow(e)) { return; } - const $target = $(e.currentTarget); - return this._show($target.data('user-card'), $target, 'user'); - }); - - $('#main-outlet').on(clickMention, 'a.mention', (e) => { - if (wantsNewWindow(e)) { return; } - const $target = $(e.target); - return this._show($target.text().replace(/^@/, ''), $target, 'user'); - }); - - $('#main-outlet').on(groupClickDataExpand, '[data-group-card]', (e) => { - if (wantsNewWindow(e)) { return; } - const $target = $(e.currentTarget); - return this._show($target.data('group-card'), $target, 'group'); - }); - - $('#main-outlet').on(groupClickMention, 'a.mention-group', (e) => { - if (wantsNewWindow(e)) { return; } - const $target = $(e.target); - return this._show($target.text().replace(/^@/, ''), $target, 'group'); - }); - }, - - _positionCard(target) { - const rtl = ($('html').css('direction')) === 'rtl'; - if (!target) { return; } - const width = this.$().width(); - - Ember.run.schedule('afterRender', () => { - if (target) { - let position = target.offset(); - if (position) { - - if (rtl) { // The site direction is rtl - position.right = $(window).width() - position.left + 10; - position.left = 'auto'; - let overage = ($(window).width() - 50) - (position.right + width); - if (overage < 0) { - position.right += overage; - position.top += target.height() + 48; - } - } else { // The site direction is ltr - position.left += target.width() + 10; - - let overage = ($(window).width() - 50) - (position.left + width); - if (overage < 0) { - position.left += overage; - position.top += target.height() + 48; - } - } - - position.top -= $('#main-outlet').offset().top; - this.$().css(position); - } - - // After the card is shown, focus on the first link - // - // note: we DO NOT use afterRender here cause _positionCard may - // run afterwards, if we allowed this to happen the usercard - // may be offscreen and we may scroll all the way to it on focus - Ember.run.next(null, () => this.$('a:first').focus() ); - } - }); - }, - - _hide() { - if (!this.get('visible')) { - this.$().css({left: -9999, top: -9999}); - } }, _close() { + this._super(); this.setProperties({ - visible: false, user: null, - group: null, - username: null, - avatar: null, - userLoading: null, - cardTarget: null, - post: null, topicPostCount: null, - cardType: null }); }, @@ -243,20 +124,6 @@ export default Ember.Component.extend(CleansUp, { this._close(); }, - keyUp(e) { - if (e.keyCode === 27) { // ESC - const target = this.get('cardTarget'); - this._close(); - target.focus(); - } - }, - - willDestroyElement() { - this._super(); - $('html').off(clickOutsideEventName); - $('#main').off(clickDataExpand).off(clickMention).off(groupClickMention).off(groupClickDataExpand); - }, - actions: { close() { this._close(); @@ -273,10 +140,6 @@ export default Ember.Component.extend(CleansUp, { this.sendAction('composePrivateMessage', ...args); }, - messageGroup() { - this.sendAction('createNewMessageViaParams', this.get('group.name')); - }, - togglePosts() { this.sendAction('togglePosts', this.get('user')); this._close(); @@ -291,11 +154,6 @@ export default Ember.Component.extend(CleansUp, { this._close(); }, - showGroup() { - this.sendAction('showGroup', this.get('group')); - this._close(); - }, - checkEmail(user) { user.checkEmail(); } diff --git a/app/assets/javascripts/discourse/components/user-card-group-contents.js.es6 b/app/assets/javascripts/discourse/components/user-card-group-contents.js.es6 deleted file mode 100644 index 7f085d301fc..00000000000 --- a/app/assets/javascripts/discourse/components/user-card-group-contents.js.es6 +++ /dev/null @@ -1,32 +0,0 @@ -import { default as computed } from 'ember-addons/ember-computed-decorators'; - -export default Ember.Component.extend({ - group: null, - - showMoreMembers: Ember.computed.gt('moreMembersCount', 0), - - @computed('group.user_count', 'group.members.length') - moreMembersCount: (memberCount, maxMemberDisplay) => memberCount - maxMemberDisplay, - - @computed('group') - groupPath(group) { - return `${Discourse.BaseUri}/groups/${group.name}`; - }, - - actions: { - close() { - this.sendAction('close'); - }, - - messageGroup() { - this.sendAction('messageGroup'); - }, - - showGroup() { - this.sendAction('showGroup'); - }, - showUser(user) { - this.sendAction('showUser', user); - }, - } -}); diff --git a/app/assets/javascripts/discourse/components/user-card-user-contents.js.es6 b/app/assets/javascripts/discourse/components/user-card-user-contents.js.es6 deleted file mode 100644 index a4cbec5f282..00000000000 --- a/app/assets/javascripts/discourse/components/user-card-user-contents.js.es6 +++ /dev/null @@ -1,101 +0,0 @@ -import { default as computed } from 'ember-addons/ember-computed-decorators'; -import { propertyNotEqual, setting } from 'discourse/lib/computed'; -import { durationTiny } from 'discourse/lib/formatter'; -import CanCheckEmails from 'discourse/mixins/can-check-emails'; - -export default Ember.Component.extend(CanCheckEmails, { - - allowBackgrounds: setting('allow_profile_backgrounds'), - showBadges: setting('enable_badges'), - - enoughPostsForFiltering: Ember.computed.gte('topicPostCount', 2), - showFilter: Ember.computed.and('viewingTopic', 'postStream.hasNoFilters', 'enoughPostsForFiltering'), - showName: propertyNotEqual('user.name', 'user.username'), - hasUserFilters: Ember.computed.gt('postStream.userFilters.length', 0), - isSuspended: Ember.computed.notEmpty('user.suspend_reason'), - showMoreBadges: Ember.computed.gt('moreBadgesCount', 0), - showDelete: Ember.computed.and("viewingAdmin", "showName", "user.canBeDeleted"), - linkWebsite: Ember.computed.not('user.isBasic'), - hasLocationOrWebsite: Ember.computed.or('user.location', 'user.website_name'), - showCheckEmail: Ember.computed.and('user.staged', 'canCheckEmails'), - - @computed('user.name') - nameFirst(name) { - return !this.siteSettings.prioritize_username_in_ux && name && name.trim().length > 0; - }, - - @computed('username', 'topicPostCount') - togglePostsLabel(username, count) { - return I18n.t("topic.filter_to", { username, count }); - }, - - @computed('user.user_fields.@each.value') - publicUserFields() { - const siteUserFields = this.site.get('user_fields'); - if (!Ember.isEmpty(siteUserFields)) { - const userFields = this.get('user.user_fields'); - return siteUserFields.filterBy('show_on_user_card', true).sortBy('position').map(field => { - Ember.set(field, 'dasherized_name', field.get('name').dasherize()); - const value = userFields ? userFields[field.get('id')] : null; - return Ember.isEmpty(value) ? null : Ember.Object.create({ value, field }); - }).compact(); - } - }, - - @computed("user.trust_level") - removeNoFollow(trustLevel) { - return trustLevel > 2 && !this.siteSettings.tl3_links_no_follow; - }, - - @computed('user.badge_count', 'user.featured_user_badges.length') - moreBadgesCount: (badgeCount, badgeLength) => badgeCount - badgeLength, - - @computed('user.time_read', 'user.recent_time_read') - showRecentTimeRead(timeRead, recentTimeRead) { - return timeRead !== recentTimeRead && recentTimeRead !== 0; - }, - - @computed('user.recent_time_read') - recentTimeRead(recentTimeReadSeconds) { - return durationTiny(recentTimeReadSeconds); - }, - - @computed('showRecentTimeRead', 'user.time_read', 'recentTimeRead') - timeReadTooltip(showRecent, timeRead, recentTimeRead) { - if (showRecent) { - return I18n.t('time_read_recently_tooltip', {time_read: durationTiny(timeRead), recent_time_read: recentTimeRead}); - } else { - return I18n.t('time_read_tooltip', {time_read: durationTiny(timeRead)}); - } - }, - - actions: { - close() { - this.sendAction('close'); - }, - - cancelFilter() { - this.sendAction('cancelFilter'); - }, - - composePrivateMessage(...args) { - this.sendAction('composePrivateMessage', ...args); - }, - - togglePosts() { - this.sendAction('togglePosts'); - }, - - deleteUser() { - this.sendAction('deleteUser'); - }, - - showUser() { - this.sendAction('showUser'); - }, - - checkEmail(user) { - this.sendAction('showUser', user); - } - } -}); diff --git a/app/assets/javascripts/discourse/mixins/card-contents-base.js.es6 b/app/assets/javascripts/discourse/mixins/card-contents-base.js.es6 new file mode 100644 index 00000000000..77a8fac7f47 --- /dev/null +++ b/app/assets/javascripts/discourse/mixins/card-contents-base.js.es6 @@ -0,0 +1,200 @@ +import { wantsNewWindow } from 'discourse/lib/intercept-click'; +import afterTransition from 'discourse/lib/after-transition'; +import DiscourseURL from 'discourse/lib/url'; +import { userPath } from 'discourse/lib/url'; + +export default Ember.Mixin.create({ + elementId: null, //click detection added for data-{elementId} + triggeringLinkClass: null, //the classname where this card should appear + _showCallback: null, //username, $target - load up data for when show is called, should call this._positionCard($target) when it's done. + + postStream: Ember.computed.alias('topic.postStream'), + viewingTopic: Ember.computed.match('currentPath', /^topic\./), + + visible: false, + username: null, + loading: null, + cardTarget: null, + post: null, + isFixed: false, + + _show(username, $target) { + // No user card for anon + if (this.siteSettings.hide_user_profiles_from_public && !this.currentUser) { + return false; + } + + username = Ember.Handlebars.Utils.escapeExpression(username.toString()); + + // Don't show on mobile + if (this.site.mobileView) { + DiscourseURL.routeTo(userPath(username)); + return false; + } + + const currentUsername = this.get('username'); + if (username === currentUsername && this.get('loading') === username) { + return; + } + + const postId = $target.parents('article').data('post-id'); + + const wasVisible = this.get('visible'); + const previousTarget = this.get('cardTarget'); + const target = $target[0]; + if (wasVisible) { + this._close(); + if (target === previousTarget) { return; } + } + + const post = this.get('viewingTopic') && postId ? this.get('postStream').findLoadedPost(postId) : null; + this.setProperties({ username, loading: username, cardTarget: target, post }); + + this._showCallback(username, $target); + + return false; + }, + + didInsertElement() { + this._super(); + afterTransition(this.$(), this._hide.bind(this)); + const id = this.get('elementId'); + const triggeringLinkClass = this.get('triggeringLinkClass'); + const clickOutsideEventName = `mousedown.outside-${id}`; + const clickDataExpand = `click.discourse-${id}`; + const clickMention = `click.discourse-${id}-${triggeringLinkClass}`; + const previewClickEvent = `click.discourse-preview-${id}-${triggeringLinkClass}`; + + this.setProperties({ clickOutsideEventName, clickDataExpand, clickMention, previewClickEvent }); + + $('html').off(clickOutsideEventName) + .on(clickOutsideEventName, (e) => { + if (this.get('visible')) { + const $target = $(e.target); + if ($target.closest(`[data-${id}]`).data(id) || + $target.closest(`a.${triggeringLinkClass}`).length > 0 || + $target.closest(`#${id}`).length > 0) { + return; + } + + this._close(); + } + + return true; + }); + + $('#main-outlet').on(clickDataExpand, `[data-${id}]`, (e) => { + if (wantsNewWindow(e)) { return; } + const $target = $(e.currentTarget); + return this._show($target.data(id), $target); + }); + + $('#main-outlet').on(clickMention, `a.${triggeringLinkClass}`, (e) => { + if (wantsNewWindow(e)) { return; } + const $target = $(e.target); + return this._show($target.text().replace(/^@/, ''), $target); + }); + + this.appEvents.on(previewClickEvent, $target => { + this.set('isFixed', true); + return this._show($target.text().replace(/^@/, ''), $target); + }); + }, + + _positionCard(target) { + const rtl = ($('html').css('direction')) === 'rtl'; + if (!target) { return; } + const width = this.$().width(); + const height = 175; + const isFixed = this.get('isFixed'); + + let verticalAdjustments = 0; + + Ember.run.schedule('afterRender', () => { + if (target) { + let position = target.offset(); + if (position) { + position.bottom = 'unset'; + + if (rtl) { // The site direction is rtl + position.right = $(window).width() - position.left + 10; + position.left = 'auto'; + let overage = ($(window).width() - 50) - (position.right + width); + if (overage < 0) { + position.right += overage; + position.top += target.height() + 48; + verticalAdjustments += target.height() + 48; + } + } else { // The site direction is ltr + position.left += target.width() + 10; + + let overage = ($(window).width() - 50) - (position.left + width); + if (overage < 0) { + position.left += overage; + position.top += target.height() + 48; + verticalAdjustments += target.height() + 48; + } + } + + position.top -= $('#main-outlet').offset().top; + if(isFixed) { + position.top -= $('html').scrollTop(); + //if content is fixed and will be cut off on the bottom, display it above... + if(position.top + height + verticalAdjustments > $(window).height() - 50) { + console.log("adjusting... by " + (height + verticalAdjustments + 48)); + position.bottom = $(window).height() - (target.offset().top - $('html').scrollTop()); + if(verticalAdjustments > 0) { + position.bottom += 48; + } + position.top = 'unset'; + } + } + this.$().css(position); + } + + // After the card is shown, focus on the first link + // + // note: we DO NOT use afterRender here cause _positionCard may + // run afterwards, if we allowed this to happen the usercard + // may be offscreen and we may scroll all the way to it on focus + Ember.run.next(null, () => this.$('a:first').focus() ); + } + }); + }, + + _hide() { + if (!this.get('visible')) { + this.$().css({left: -9999, top: -9999}); + } + }, + + _close() { + this.setProperties({ + visible: false, + username: null, + loading: null, + cardTarget: null, + post: null, + isFixed: false + }); + }, + + willDestroyElement() { + this._super(); + const clickOutsideEventName = this.get('clickOutsideEventName'); + const clickDataExpand = this.get('clickDataExpand'); + const clickMention = this.get('clickMention'); + const previewClickEvent = this.get('previewClickEvent'); + $('html').off(clickOutsideEventName); + $('#main').off(clickDataExpand).off(clickMention); + this.appEvents.off(previewClickEvent); + }, + + keyUp(e) { + if (e.keyCode === 27) { // ESC + const target = this.get('cardTarget'); + this._close(); + target.focus(); + } + } +}); diff --git a/app/assets/javascripts/discourse/templates/components/group-card-contents.hbs b/app/assets/javascripts/discourse/templates/components/group-card-contents.hbs new file mode 100644 index 00000000000..4ae2f03a3d9 --- /dev/null +++ b/app/assets/javascripts/discourse/templates/components/group-card-contents.hbs @@ -0,0 +1,54 @@ +{{#if visible}} +
+
+ + {{avatar-flair + flairURL=group.flair_url + flairBgColor=group.flair_bg_color + flairColor=group.flair_color + groupName=group.name}} + +
+ +
+ +

+ {{ group.name }} +

+ {{#if group.full_name}} +

{{group.full_name}}

+ {{else}} +

{{group.name}}

+ {{/if}} +
+
+ +
+ {{group-membership-button + model=group + showLogin='showLogin'}} + + {{#if group.messageable}} + {{d-button + action="messageGroup" + class="btn-primary group-message-button inline" + icon="envelope" + label="groups.message"}} + {{/if}} +
+ +
+

{{ group.user_count }} {{i18n 'groups.user_count'}}

+
+
+ + {{#each group.members as |user|}} + {{bound-avatar user "tiny"}} + {{/each}} + {{#if showMoreMembers}} + +{{ moreMembersCount }} {{i18n "more"}} + {{/if}} + +
+
+{{/if}} diff --git a/app/assets/javascripts/discourse/templates/components/user-card-contents.hbs b/app/assets/javascripts/discourse/templates/components/user-card-contents.hbs index 989801e9dc4..d38a81bcc6f 100644 --- a/app/assets/javascripts/discourse/templates/components/user-card-contents.hbs +++ b/app/assets/javascripts/discourse/templates/components/user-card-contents.hbs @@ -1,33 +1,176 @@ {{#if visible}}
- {{#if isUserShown}} - {{user-card-user-contents - model=model - viewingTopic=viewingTopic - topicPostCount=topicPostCount - user=user - username=username - userLoading=userLoading - cardTarget=cardTarget - avatar=avatar - postStream=postStream - close="close" - cancelFilter="cancelFilter" - composePrivateMessage="composePrivateMessage" - togglePosts="togglePosts" - deleteUser="deleteUser" - showUser="showUser" - checkEmail="checkEmail" - }} +
+ {{bound-avatar user "huge"}} + {{#if user.primary_group_name}} + {{avatar-flair + flairURL=user.primary_group_flair_url + flairBgColor=user.primary_group_flair_bg_color + flairColor=user.primary_group_flair_color + groupName=user.primary_group_name}} + {{/if}} + {{plugin-outlet name="user-card-avatar-flair" args=(hash user=user) tagName='div'}} +
+ +
+ +

+ {{if nameFirst user.name (format-username username)}} {{user-status user currentUser=currentUser}} +

+ {{plugin-outlet name="user-card-after-username" args=(hash user=user) tagName=''}} + + {{#unless nameFirst}} + {{#if user.name}} +

{{user.name}}

+ {{/if}} +{{else}} +

{{username}}

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

{{user.title}}

+ {{/if}} + + {{#if user.staged}} +

{{i18n 'user.staged'}}

+ {{/if}} + + {{plugin-outlet name="user-card-post-names" args=(hash user=user) tagName='div'}} +
+
+ + + {{plugin-outlet + name="user-card-additional-controls" + args=(hash user=user close=(action "close")) + tagName=""}} + + {{#if isSuspended}} +
+ {{d-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_excerpt}}
{{/if}} {{/if}} - {{#if isGroupShown}} - {{user-card-group-contents - group=group - close="close" - messageGroup="messageGroup" - showGroup="showGroup" - showUser="showUser" - }} + + {{#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 hasLocationOrWebsite}} +
+ {{#if user.location}} + {{d-icon "map-marker"}} {{user.location}} + {{/if}} + + {{#if user.website_name}} + + {{d-icon "globe"}} + {{#if linkWebsite}} + {{user.website_name}} + {{else}} + {{user.website_name}} + {{/if}} + + {{/if}} + + {{plugin-outlet name="user-card-location-and-website" args=(hash user=user)}} +
+ {{/if}} + + {{#if user}} +
+ {{#if user.last_posted_at}} +

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

+ {{/if}} +

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

+

+ {{i18n 'time_read'}} + {{format-duration user.time_read}} + {{#if showRecentTimeRead}} + ({{i18n 'time_read_recently' time_read=recentTimeRead}}) + {{/if}} +

+ {{#if showCheckEmail}} + + {{/if}} + {{plugin-outlet name="user-card-metadata" args=(hash user=user)}} +
+ {{/if}} + + {{#if publicUserFields}} +
+ {{#each publicUserFields as |uf|}} + {{#if uf.value}} +
+ {{uf.field.name}}: + {{uf.value}} +
+ {{/if}} + {{/each}} +
+ {{/if}} + + {{#if showBadges}} +
+ {{#each user.featured_user_badges as |ub|}} + {{user-badge badge=ub.badge user=user}} + {{/each}} + {{#if showMoreBadges}} + {{#link-to 'user.badges' user class="btn more-user-badges"}} + {{i18n 'badges.more_badges' count=moreBadgesCount}} + {{/link-to}} + {{/if}} +
{{/if}}
{{/if}} diff --git a/app/assets/javascripts/discourse/templates/components/user-card-group-contents.hbs b/app/assets/javascripts/discourse/templates/components/user-card-group-contents.hbs deleted file mode 100644 index 50062615f54..00000000000 --- a/app/assets/javascripts/discourse/templates/components/user-card-group-contents.hbs +++ /dev/null @@ -1,50 +0,0 @@ -
- - {{avatar-flair - flairURL=group.flair_url - flairBgColor=group.flair_bg_color - flairColor=group.flair_color - groupName=group.name}} - -
- -
- -

- {{ group.name }} -

- {{#if group.full_name}} -

{{group.full_name}}

- {{else}} -

{{group.name}}

- {{/if}} -
-
- -
- {{group-membership-button - model=group - showLogin='showLogin'}} - - {{#if group.messageable}} - {{d-button - action="messageGroup" - class="btn-primary group-message-button inline" - icon="envelope" - label="groups.message"}} - {{/if}} -
- -
-

{{ group.user_count }} {{i18n 'groups.user_count'}}

-
-
- - {{#each group.members as |user|}} - {{bound-avatar user "tiny"}} - {{/each}} - {{#if showMoreMembers}} - +{{ moreMembersCount }} {{i18n "more"}} - {{/if}} - -
diff --git a/app/assets/javascripts/discourse/templates/components/user-card-user-contents.hbs b/app/assets/javascripts/discourse/templates/components/user-card-user-contents.hbs deleted file mode 100644 index b103563eb6b..00000000000 --- a/app/assets/javascripts/discourse/templates/components/user-card-user-contents.hbs +++ /dev/null @@ -1,172 +0,0 @@ -
- {{bound-avatar avatar "huge"}} - {{#if user.primary_group_name}} - {{avatar-flair - flairURL=user.primary_group_flair_url - flairBgColor=user.primary_group_flair_bg_color - flairColor=user.primary_group_flair_color - groupName=user.primary_group_name}} - {{/if}} - {{plugin-outlet name="user-card-avatar-flair" args=(hash user=user) tagName='div'}} -
- -
- -

- {{if nameFirst user.name (format-username username)}} {{user-status user currentUser=currentUser}} -

- {{plugin-outlet name="user-card-after-username" args=(hash user=user) tagName=''}} - - {{#unless nameFirst}} - {{#if user.name}} -

{{user.name}}

- {{/if}} - {{else}} -

{{username}}

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

{{user.title}}

- {{/if}} - - {{#if user.staged}} -

{{i18n 'user.staged'}}

- {{/if}} - - {{plugin-outlet name="user-card-post-names" args=(hash user=user) tagName='div'}} -
-
- - -{{plugin-outlet - name="user-card-additional-controls" - args=(hash user=user close=(action "close")) - tagName=""}} - -{{#if isSuspended}} -
- {{d-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_excerpt}}
{{/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 hasLocationOrWebsite}} -
- {{#if user.location}} - {{d-icon "map-marker"}} {{user.location}} - {{/if}} - - {{#if user.website_name}} - - {{d-icon "globe"}} - {{#if linkWebsite}} - {{user.website_name}} - {{else}} - {{user.website_name}} - {{/if}} - - {{/if}} - - {{plugin-outlet name="user-card-location-and-website" args=(hash user=user)}} -
-{{/if}} - -{{#if user}} -
- {{#if user.last_posted_at}} -

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

- {{/if}} -

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

-

- {{i18n 'time_read'}} - {{format-duration user.time_read}} - {{#if showRecentTimeRead}} - ({{i18n 'time_read_recently' time_read=recentTimeRead}}) - {{/if}} -

- {{#if showCheckEmail}} -

- {{d-icon "envelope-o" title="user.email.title"}} - {{#if user.email}} - {{user.email}} - {{else}} - {{d-button action="checkEmail" actionParam=user icon="envelope-o" label="admin.users.check_email.text" class="btn-primary"}} - {{/if}} -

- {{/if}} - {{plugin-outlet name="user-card-metadata" args=(hash user=user)}} -
-{{/if}} - -{{#if publicUserFields}} -
- {{#each publicUserFields as |uf|}} - {{#if uf.value}} -
- {{uf.field.name}}: - {{uf.value}} -
- {{/if}} - {{/each}} -
-{{/if}} - -{{#if showBadges}} -
- {{#each user.featured_user_badges as |ub|}} - {{user-badge badge=ub.badge user=user}} - {{/each}} - {{#if showMoreBadges}} - {{#link-to 'user.badges' user class="btn more-user-badges"}} - {{i18n 'badges.more_badges' count=moreBadgesCount}} - {{/link-to}} - {{/if}} -
-{{/if}} diff --git a/app/assets/javascripts/discourse/templates/user-card.hbs b/app/assets/javascripts/discourse/templates/user-card.hbs index 9136a63713a..f2ad8f600db 100644 --- a/app/assets/javascripts/discourse/templates/user-card.hbs +++ b/app/assets/javascripts/discourse/templates/user-card.hbs @@ -6,3 +6,12 @@ composePrivateMessage="composePrivateMessage" createNewMessageViaParams="createNewMessageViaParams" deleteUser="deleteUser"}} + +{{group-card-contents + currentPath=application.currentPath + topic=topic.model + showUser="showUser" + togglePosts="togglePosts" + composePrivateMessage="composePrivateMessage" + createNewMessageViaParams="createNewMessageViaParams" + deleteUser="deleteUser"}} diff --git a/app/assets/stylesheets/desktop/user-card.scss b/app/assets/stylesheets/desktop/user-card.scss index 1589b94a220..eef85fa0cb0 100644 --- a/app/assets/stylesheets/desktop/user-card.scss +++ b/app/assets/stylesheets/desktop/user-card.scss @@ -4,7 +4,7 @@ $user_card_primary: $primary; $user_card_background: $secondary; -#user-card { +#user-card, #group-card { position: absolute; width: 500px; left: -9999px; @@ -27,6 +27,11 @@ $user_card_background: $secondary; @include transform(scale(1)); } + &.fixed { + position: fixed; + z-index: 1000; + } + .card-content { padding: 12px 12px 0 12px; background: rgba($user_card_background, .85); diff --git a/test/javascripts/acceptance/user-card-test.js.es6 b/test/javascripts/acceptance/user-card-test.js.es6 index e19d96836b7..e14132d9b7f 100644 --- a/test/javascripts/acceptance/user-card-test.js.es6 +++ b/test/javascripts/acceptance/user-card-test.js.es6 @@ -11,4 +11,17 @@ QUnit.test("card", assert => { assert.ok(visible('#user-card'), 'card should appear'); }); -}); \ No newline at end of file +}); + + +QUnit.test("group card", assert => { + visit('/t/301/1'); + + assert.ok(invisible('#group-card'), 'user card is invisible by default'); + click('a.mention-group:first'); + + andThen(() => { + assert.ok(visible('#group-card'), 'card should appear'); + }); + +}); diff --git a/test/javascripts/fixtures/topic.js.es6 b/test/javascripts/fixtures/topic.js.es6 index c1a75bba168..74aa4fa02e9 100644 --- a/test/javascripts/fixtures/topic.js.es6 +++ b/test/javascripts/fixtures/topic.js.es6 @@ -3,4 +3,5 @@ export default {"/t/280/1.json": {"post_stream":{"posts":[{"id":398,"name":"Uwe "/t/28830/1.json": {"post_stream":{"posts":[{"id":118591,"name":"spends too much time on WTDWTF","username":"RaceProUK","avatar_template":"/images/avatar.png","uploaded_avatar_id":40071,"created_at":"2015-05-14T20:18:17.954Z","cooked":"

Normally, actions such as Liking are rate-limited, and when you hit the limit, you get a message telling you you've hit the limit. However, in 1.3.0beta9, it seems those popups are no longer appearing.

\n\n

Edit: Possibly linked to this issue?

","post_number":1,"post_type":1,"updated_at":"2015-05-14T20:21:42.825Z","like_count":6,"reply_count":0,"reply_to_post_number":null,"quote_count":0,"avg_time":null,"incoming_link_count":14,"reads":24,"score":224.6,"yours":false,"topic_id":28830,"topic_slug":"1-3-0beta9-no-rate-limit-popups","display_username":"spends too much time on WTDWTF","primary_group_name":null,"version":1,"can_edit":false,"can_delete":false,"can_recover":false,"link_counts":[{"url":"https://meta.discourse.org/t/post-reply-on-different-topic-no-longer-works/28825","internal":true,"reflection":false,"title":"Post reply on different topic no longer works","clicks":6}],"read":true,"user_title":"Contributor","actions_summary":[{"id":2,"count":6,"hidden":false,"can_act":false},{"id":3,"count":0,"hidden":false,"can_act":false},{"id":4,"count":0,"hidden":false,"can_act":false},{"id":5,"count":0,"hidden":true,"can_act":false},{"id":6,"count":0,"hidden":false,"can_act":false},{"id":7,"count":0,"hidden":false,"can_act":false},{"id":8,"count":0,"hidden":false,"can_act":false}],"moderator":false,"admin":false,"staff":false,"user_id":14169,"hidden":false,"hidden_reason_id":null,"trust_level":2,"deleted_at":null,"user_deleted":false,"edit_reason":"","can_view_edit_history":true,"wiki":false},{"id":118597,"name":"Sam","username":"Yuun","avatar_template":"/images/avatar.png","uploaded_avatar_id":null,"created_at":"2015-05-14T20:35:03.793Z","cooked":"

I'm seeing this issue as well. When you hit the rate limit, any further likes look like the forum is attempting and failing to apply them - the text saying 'you liked this' comes into place before quickly being removed.

\n\n

This makes it look (to the user) like the forum software is running into errors instead of said user hitting an intentional limit, which is a bit unfortunate.

","post_number":2,"post_type":1,"updated_at":"2015-05-14T20:35:03.793Z","like_count":0,"reply_count":0,"reply_to_post_number":null,"quote_count":0,"avg_time":null,"incoming_link_count":6,"reads":22,"score":34.2,"yours":false,"topic_id":28830,"topic_slug":"1-3-0beta9-no-rate-limit-popups","display_username":"Sam","primary_group_name":null,"version":1,"can_edit":false,"can_delete":false,"can_recover":false,"read":true,"user_title":null,"actions_summary":[{"id":2,"count":0,"hidden":false,"can_act":false},{"id":3,"count":0,"hidden":false,"can_act":false},{"id":4,"count":0,"hidden":false,"can_act":false},{"id":5,"count":0,"hidden":true,"can_act":false},{"id":6,"count":0,"hidden":false,"can_act":false},{"id":7,"count":0,"hidden":false,"can_act":false},{"id":8,"count":0,"hidden":false,"can_act":false}],"moderator":false,"admin":false,"staff":false,"user_id":14795,"hidden":false,"hidden_reason_id":null,"trust_level":2,"deleted_at":null,"user_deleted":false,"edit_reason":null,"can_view_edit_history":true,"wiki":false},{"id":118601,"name":"Kane York","username":"riking","avatar_template":"/images/avatar.png","uploaded_avatar_id":40212,"created_at":"2015-05-14T21:05:19.837Z","cooked":"

I'm going to guess that the bootbox library got broken somehow?

","post_number":3,"post_type":1,"updated_at":"2015-05-14T21:05:19.837Z","like_count":0,"reply_count":1,"reply_to_post_number":null,"quote_count":0,"avg_time":null,"incoming_link_count":0,"reads":14,"score":7.2,"yours":false,"topic_id":28830,"topic_slug":"1-3-0beta9-no-rate-limit-popups","display_username":"Kane York","primary_group_name":null,"version":1,"can_edit":false,"can_delete":false,"can_recover":false,"read":true,"user_title":"team summer intern 2014","actions_summary":[{"id":2,"count":0,"hidden":false,"can_act":false},{"id":3,"count":0,"hidden":false,"can_act":false},{"id":4,"count":0,"hidden":false,"can_act":false},{"id":5,"count":0,"hidden":true,"can_act":false},{"id":6,"count":0,"hidden":false,"can_act":false},{"id":7,"count":0,"hidden":false,"can_act":false},{"id":8,"count":0,"hidden":false,"can_act":false}],"moderator":false,"admin":false,"staff":false,"user_id":6626,"hidden":false,"hidden_reason_id":null,"trust_level":4,"deleted_at":null,"user_deleted":false,"edit_reason":null,"can_view_edit_history":true,"wiki":false},{"id":118606,"name":"Jeff Atwood","username":"codinghorror","avatar_template":"/images/avatar.png","uploaded_avatar_id":5297,"created_at":"2015-05-14T21:15:41.612Z","cooked":"

Yeah maybe another Ember 1.10 regression for @eviltrout ?

","post_number":4,"post_type":1,"updated_at":"2015-05-14T21:15:41.612Z","like_count":0,"reply_count":0,"reply_to_post_number":null,"quote_count":0,"avg_time":null,"incoming_link_count":6,"reads":12,"score":31.6,"yours":false,"topic_id":28830,"topic_slug":"1-3-0beta9-no-rate-limit-popups","display_username":"Jeff Atwood","primary_group_name":"discourse","version":1,"can_edit":false,"can_delete":false,"can_recover":false,"read":true,"user_title":"co-founder","actions_summary":[{"id":2,"count":0,"hidden":false,"can_act":false},{"id":3,"count":0,"hidden":false,"can_act":false},{"id":4,"count":0,"hidden":false,"can_act":false},{"id":5,"count":0,"hidden":true,"can_act":false},{"id":6,"count":0,"hidden":false,"can_act":false},{"id":7,"count":0,"hidden":false,"can_act":false},{"id":8,"count":0,"hidden":false,"can_act":false}],"moderator":true,"admin":true,"staff":true,"user_id":32,"hidden":false,"hidden_reason_id":null,"trust_level":4,"deleted_at":null,"user_deleted":false,"edit_reason":null,"can_view_edit_history":true,"wiki":false},{"id":118612,"name":"TDWTF member","username":"Onyx","avatar_template":"/images/avatar.png","uploaded_avatar_id":33015,"created_at":"2015-05-14T21:23:09.562Z","cooked":"\n\n

You mean the popup box library, guessing by the name? Still shows up when you want to cancel a post, so it's not all popups it seems.

","post_number":5,"post_type":1,"updated_at":"2015-05-14T21:23:09.562Z","like_count":1,"reply_count":0,"reply_to_post_number":3,"quote_count":1,"avg_time":null,"incoming_link_count":0,"reads":11,"score":16.0,"yours":false,"topic_id":28830,"topic_slug":"1-3-0beta9-no-rate-limit-popups","display_username":"TDWTF member","primary_group_name":null,"version":1,"can_edit":false,"can_delete":false,"can_recover":false,"read":true,"user_title":null,"actions_summary":[{"id":2,"count":1,"hidden":false,"can_act":false},{"id":3,"count":0,"hidden":false,"can_act":false},{"id":4,"count":0,"hidden":false,"can_act":false},{"id":5,"count":0,"hidden":true,"can_act":false},{"id":6,"count":0,"hidden":false,"can_act":false},{"id":7,"count":0,"hidden":false,"can_act":false},{"id":8,"count":0,"hidden":false,"can_act":false}],"moderator":false,"admin":false,"staff":false,"user_id":10886,"hidden":false,"hidden_reason_id":null,"trust_level":2,"deleted_at":null,"user_deleted":false,"edit_reason":null,"can_view_edit_history":true,"wiki":false}],"stream":[118591,118597,118601,118606,118612]},"id":28830,"title":"1.3.0beta9: No rate-limit popups","fancy_title":"1.3.0beta9: No rate-limit popups","posts_count":5,"created_at":"2015-05-14T20:18:17.877Z","views":38,"reply_count":1,"participant_count":5,"like_count":7,"last_posted_at":"2015-05-14T21:23:09.562Z","visible":true,"closed":false,"archived":false,"has_summary":false,"archetype":"regular","slug":"1-3-0beta9-no-rate-limit-popups","category_id":1,"word_count":198,"deleted_at":null,"pending_posts_count":0,"draft":null,"draft_key":"topic_28830","draft_sequence":null,"unpinned":null,"pinned_globally":false,"pinned":false,"pinned_at":null,"details":{"auto_close_at":null,"auto_close_hours":null,"auto_close_based_on_last_post":false,"created_by":{"id":14169,"username":"RaceProUK","uploaded_avatar_id":40071,"avatar_template":"/images/avatar.png"},"last_poster":{"id":10886,"username":"Onyx","uploaded_avatar_id":33015,"avatar_template":"/images/avatar.png"},"participants":[{"id":14795,"username":"Yuun","uploaded_avatar_id":null,"avatar_template":"/images/avatar.png","post_count":1},{"id":10886,"username":"Onyx","uploaded_avatar_id":33015,"avatar_template":"/images/avatar.png","post_count":1},{"id":14169,"username":"RaceProUK","uploaded_avatar_id":40071,"avatar_template":"/images/avatar.png","post_count":1},{"id":6626,"username":"riking","uploaded_avatar_id":40212,"avatar_template":"/images/avatar.png","post_count":1},{"id":32,"username":"codinghorror","uploaded_avatar_id":5297,"avatar_template":"/images/avatar.png","post_count":1}],"suggested_topics":[{"id":2890,"title":"Expanded quoted text not highlighting when text is formatted","fancy_title":"Expanded quoted text not highlighting when text is formatted","slug":"expanded-quoted-text-not-highlighting-when-text-is-formatted","posts_count":8,"reply_count":5,"highest_post_number":8,"image_url":null,"created_at":"2013-02-12T12:18:02.181Z","last_posted_at":"2013-02-14T15:59:40.014Z","bumped":true,"bumped_at":"2013-02-14T15:59:40.014Z","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"archetype":"regular","like_count":3,"views":361,"category_id":1},{"id":14213,"title":"Plugins not being parsed in correct javascript context when loaded for jobs","fancy_title":"Plugins not being parsed in correct javascript context when loaded for jobs","slug":"plugins-not-being-parsed-in-correct-javascript-context-when-loaded-for-jobs","posts_count":2,"reply_count":0,"highest_post_number":2,"image_url":"/plugins/emoji/images/frowning.png","created_at":"2014-03-27T23:57:00.974Z","last_posted_at":"2015-03-20T04:56:03.982Z","bumped":true,"bumped_at":"2015-03-20T04:56:03.982Z","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"archetype":"regular","like_count":0,"views":156,"category_id":1},{"id":22544,"title":"Like count on profile off by one","fancy_title":"Like count on profile off by one","slug":"like-count-on-profile-off-by-one","posts_count":7,"reply_count":2,"highest_post_number":7,"image_url":null,"created_at":"2014-11-26T08:15:39.802Z","last_posted_at":"2014-11-27T07:23:37.638Z","bumped":true,"bumped_at":"2014-11-27T07:23:37.638Z","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"archetype":"regular","like_count":18,"views":192,"category_id":1},{"id":27670,"title":"Using back still shows unread indicator on the topic","fancy_title":"Using back still shows unread indicator on the topic","slug":"using-back-still-shows-unread-indicator-on-the-topic","posts_count":2,"reply_count":0,"highest_post_number":2,"image_url":null,"created_at":"2015-04-16T23:21:42.739Z","last_posted_at":"2015-04-17T02:43:08.447Z","bumped":true,"bumped_at":"2015-04-17T02:43:08.447Z","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"archetype":"regular","like_count":1,"views":85,"category_id":1},{"id":26628,"title":"Embed blacklist selector is broken","fancy_title":"Embed blacklist selector is broken","slug":"embed-blacklist-selector-is-broken","posts_count":11,"reply_count":7,"highest_post_number":11,"image_url":null,"created_at":"2015-03-22T11:21:14.825Z","last_posted_at":"2015-04-20T09:11:38.999Z","bumped":true,"bumped_at":"2015-04-20T09:11:38.999Z","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"archetype":"regular","like_count":1,"views":247,"category_id":1},{"id":18027,"title":"Minor: delete/undelete needs a rate limit","fancy_title":"Minor: delete/undelete needs a rate limit","slug":"minor-delete-undelete-needs-a-rate-limit","posts_count":4,"reply_count":1,"highest_post_number":4,"image_url":null,"created_at":"2014-07-25T02:51:41.158Z","last_posted_at":"2014-07-25T04:01:15.343Z","bumped":true,"bumped_at":"2014-07-25T11:06:46.213Z","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"archetype":"regular","like_count":1,"views":165,"category_id":1},{"id":17396,"title":"Bad Reply Key when pulling Autoforwarded Emails to Discourse","fancy_title":"Bad Reply Key when pulling Autoforwarded Emails to Discourse","slug":"bad-reply-key-when-pulling-autoforwarded-emails-to-discourse","posts_count":20,"reply_count":15,"highest_post_number":20,"image_url":null,"created_at":"2014-07-09T18:34:57.114Z","last_posted_at":"2014-10-21T15:08:50.441Z","bumped":true,"bumped_at":"2014-10-21T15:08:50.441Z","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"archetype":"regular","like_count":7,"views":542,"category_id":1}],"links":[{"url":"https://meta.discourse.org/t/post-reply-on-different-topic-no-longer-works/28825","title":"Post reply on different topic no longer works","fancy_title":null,"internal":true,"reflection":false,"clicks":6,"user_id":14169,"domain":"meta.discourse.org"}],"notification_level":1,"can_flag_topic":false},"highest_post_number":5,"deleted_by":null,"actions_summary":[{"id":4,"count":0,"hidden":false,"can_act":false},{"id":7,"count":0,"hidden":false,"can_act":false},{"id":8,"count":0,"hidden":false,"can_act":false}],"chunk_size":20,"bookmarked":null,"tags":null}, "/t/9/1.json": {"post_stream":{"posts":[{"id":18,"username":"eviltrout","avatar_template":"/images/avatar.png","name":"Evil Trout","uploaded_avatar_id":9,"created_at":"2015-08-13T14:49:11.840Z","cooked":"

This is the first post.

","post_number":1,"post_type":1,"updated_at":"2015-08-13T14:49:11.840Z","reply_count":0,"reply_to_post_number":null,"quote_count":0,"avg_time":null,"incoming_link_count":0,"reads":1,"score":0,"yours":true,"topic_id":9,"topic_slug":"this-is-a-test-topic","display_username":"","primary_group_name":null,"version":1,"can_edit":true,"can_delete":false,"can_recover":true,"read":true,"user_title":null,"actions_summary":[{"id":3,"can_act":true},{"id":4,"can_act":true},{"id":5,"hidden":true,"can_act":true},{"id":7,"can_act":true},{"id":8,"can_act":true}],"moderator":false,"admin":true,"staff":true,"user_id":1,"hidden":false,"hidden_reason_id":null,"trust_level":4,"deleted_at":null,"user_deleted":false,"edit_reason":null,"can_view_edit_history":true,"wiki":false},{"id":19,"username":"eviltrout","avatar_template":"/images/avatar.png","name":"Evil Trout","uploaded_avatar_id":9,"created_at":"2015-08-13T14:49:18.231Z","cooked":"

This is the second post.

","post_number":2,"post_type":1,"updated_at":"2015-08-13T14:49:18.231Z","reply_count":0,"reply_to_post_number":null,"quote_count":0,"avg_time":null,"incoming_link_count":0,"reads":1,"score":0,"yours":true,"topic_id":9,"topic_slug":"this-is-a-test-topic","display_username":"","primary_group_name":null,"version":1,"can_edit":true,"can_delete":true,"can_recover":true,"read":true,"user_title":null,"actions_summary":[{"id":3,"can_act":true},{"id":4,"can_act":true},{"id":5,"hidden":true,"can_act":true},{"id":7,"can_act":true},{"id":8,"can_act":true}],"moderator":false,"admin":true,"staff":true,"user_id":1,"hidden":false,"hidden_reason_id":null,"trust_level":4,"deleted_at":null,"user_deleted":false,"edit_reason":null,"can_view_edit_history":true,"wiki":false},{"id":20,"username":"eviltrout","avatar_template":"/images/avatar.png","name":"Evil Trout","uploaded_avatar_id":9,"created_at":"2015-08-13T14:49:23.927Z","cooked":"

This is the third post.

","post_number":3,"post_type":1,"updated_at":"2015-08-13T14:49:23.927Z","reply_count":0,"reply_to_post_number":null,"quote_count":0,"avg_time":null,"incoming_link_count":0,"reads":1,"score":0,"yours":true,"topic_id":9,"topic_slug":"this-is-a-test-topic","display_username":"","primary_group_name":null,"version":1,"can_edit":true,"can_delete":true,"can_recover":true,"read":true,"user_title":null,"actions_summary":[{"id":3,"can_act":true},{"id":4,"can_act":true},{"id":5,"hidden":true,"can_act":true},{"id":7,"can_act":true},{"id":8,"can_act":true}],"moderator":false,"admin":true,"staff":true,"user_id":1,"hidden":false,"hidden_reason_id":null,"trust_level":4,"deleted_at":null,"user_deleted":false,"edit_reason":null,"can_view_edit_history":true,"wiki":false}],"stream":[18,19,20]},"id":9,"title":"This is a test topic!","fancy_title":"This is a test topic!","posts_count":3,"created_at":"2015-08-13T14:49:11.720Z","views":1,"reply_count":0,"participant_count":1,"like_count":0,"last_posted_at":"2015-08-13T14:49:23.927Z","visible":true,"closed":false,"archived":false,"has_summary":false,"archetype":"regular","slug":"this-is-a-test-topic","category_id":24,"word_count":15,"deleted_at":null,"pending_posts_count":0,"user_id":1,"draft":null,"draft_key":"topic_9","draft_sequence":3,"posted":true,"unpinned":null,"pinned_globally":false,"pinned":false,"pinned_at":null,"pinned_until":null,"details":{"auto_close_at":null,"auto_close_hours":null,"auto_close_based_on_last_post":false,"created_by":{"id":1,"username":"tgxworld","uploaded_avatar_id":9,"avatar_template":"/images/avatar.png"},"last_poster":{"id":1,"username":"tgxworld","uploaded_avatar_id":9,"avatar_template":"/images/avatar.png"},"participants":[{"id":1,"username":"tgxworld","uploaded_avatar_id":9,"avatar_template":"/images/avatar.png","post_count":3}],"suggested_topics":[{"id":8,"title":"This is a new and awesome topic!","fancy_title":"This is a new and awesome topic!","slug":"this-is-a-new-and-awesome-topic","posts_count":3,"reply_count":0,"highest_post_number":5,"image_url":null,"created_at":"2015-08-13T05:17:00.000Z","last_posted_at":"2015-08-13T10:14:34.799Z","bumped":true,"bumped_at":"2015-08-13T10:14:34.799Z","unseen":false,"last_read_post_number":5,"unread":0,"new_posts":0,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"notification_level":2,"bookmarked":false,"liked":false,"archetype":"regular","like_count":0,"views":2,"category_id":1},{"id":7,"title":"This is a test category!","fancy_title":"This is a test category!","slug":"this-is-a-test-category","posts_count":3,"reply_count":0,"highest_post_number":3,"image_url":null,"created_at":"2015-08-10T13:40:38.439Z","last_posted_at":"2015-08-13T01:59:44.928Z","bumped":true,"bumped_at":"2015-08-13T01:58:35.206Z","unseen":false,"last_read_post_number":3,"unread":0,"new_posts":0,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"notification_level":3,"bookmarked":false,"liked":false,"archetype":"regular","like_count":0,"views":2,"category_id":1}],"notification_level":3,"notifications_reason_id":1,"can_move_posts":true,"can_edit":true,"can_delete":true,"can_recover":true,"can_remove_allowed_users":true,"can_invite_to":true,"can_create_post":true,"can_reply_as_new_topic":true,"can_flag_topic":true},"highest_post_number":3,"last_read_post_number":3,"deleted_by":null,"has_deleted":false,"actions_summary":[{"id":4,"count":0,"hidden":false,"can_act":true},{"id":7,"count":0,"hidden":false,"can_act":true},{"id":8,"count":0,"hidden":false,"can_act":true}],"chunk_size":20,"bookmarked":false,"destination_category_id":3}, "/t/12/1.json": {"post_stream":{"posts":[{"id":15,"name":null,"username":"test","avatar_template":"/images/avatar.png","created_at":"2017-01-27T03:53:58.394Z","cooked":"

I have a pen, I have an apple

","post_number":1,"post_type":1,"updated_at":"2017-01-27T03:53:58.394Z","reply_count":0,"reply_to_post_number":null,"quote_count":0,"avg_time":null,"incoming_link_count":0,"reads":1,"score":0,"yours":true,"topic_id":12,"topic_slug":"pm-for-testing","display_username":null,"primary_group_name":null,"primary_group_flair_url":null,"primary_group_flair_bg_color":null,"primary_group_flair_color":null,"version":1,"can_edit":true,"can_delete":false,"can_recover":true,"can_wiki":true,"read":true,"user_title":null,"actions_summary":[{"id":3,"can_act":true},{"id":4,"can_act":true},{"id":5,"hidden":true,"can_act":true},{"id":7,"can_act":true},{"id":8,"can_act":true}],"moderator":false,"admin":true,"staff":true,"user_id":1,"hidden":false,"hidden_reason_id":null,"trust_level":4,"deleted_at":null,"user_deleted":false,"edit_reason":null,"can_view_edit_history":true,"wiki":false,"can_translate":false},{"id":16,"name":null,"username":"test","avatar_template":"/images/avatar.png","created_at":"2017-01-27T04:10:02.941Z","cooked":"","post_number":2,"post_type":3,"updated_at":"2017-01-27T04:10:02.941Z","reply_count":0,"reply_to_post_number":null,"quote_count":0,"avg_time":null,"incoming_link_count":0,"reads":1,"score":0,"yours":true,"topic_id":12,"topic_slug":"pm-for-testing","display_username":null,"primary_group_name":null,"primary_group_flair_url":null,"primary_group_flair_bg_color":null,"primary_group_flair_color":null,"version":1,"can_edit":true,"can_delete":true,"can_recover":true,"can_wiki":true,"read":true,"user_title":null,"actions_summary":[{"id":3,"can_act":true},{"id":4,"can_act":true},{"id":5,"hidden":true,"can_act":true},{"id":7,"can_act":true},{"id":8,"can_act":true}],"moderator":false,"admin":true,"staff":true,"user_id":1,"hidden":false,"hidden_reason_id":null,"trust_level":4,"deleted_at":null,"user_deleted":false,"edit_reason":null,"can_view_edit_history":true,"wiki":false,"action_code":"invited_group","action_code_who":"Group","can_translate":false}],"stream":[15,16]},"timeline_lookup":[[1,0]],"id":12,"title":"PM for testing","fancy_title":"PM for testing","posts_count":2,"created_at":"2017-01-27T03:53:58.360Z","views":1,"reply_count":0,"participant_count":1,"like_count":0,"last_posted_at":"2017-01-27T04:10:02.941Z","visible":true,"closed":false,"archived":false,"has_summary":false,"archetype":"private_message","slug":"pm-for-testing","category_id":null,"word_count":8,"deleted_at":null,"user_id":1,"draft":null,"draft_key":"topic_12","draft_sequence":2,"posted":true,"unpinned":null,"pinned_globally":false,"pinned":false,"pinned_at":null,"pinned_until":null,"details":{"auto_close_at":null,"auto_close_hours":null,"auto_close_based_on_last_post":false,"created_by":{"id":1,"username":"test","avatar_template":"/images/avatar.png"},"last_poster":{"id":1,"username":"test","avatar_template":"/images/avatar.png"},"allowed_groups":[{"id":41,"automatic":false,"name":"Group","user_count":0,"alias_level":99,"visible":true,"automatic_membership_email_domains":"","automatic_membership_retroactive":false,"primary_group":false,"title":null,"grant_trust_level":null,"incoming_email":null,"has_messages":false,"flair_url":null,"flair_bg_color":null,"flair_color":null,"bio_raw":null,"bio_cooked":null,"public":false,"allow_membership_requests":false,"full_name":null}],"allowed_users":[{"id":2,"username":"someguy","avatar_template":"/images/avatar.png"},{"id":1,"username":"test","avatar_template":"/images/avatar.png"}],"participants":[{"id":1,"username":"test","avatar_template":"/images/avatar.png","post_count":2,"primary_group_name":null,"primary_group_flair_url":null,"primary_group_flair_color":null,"primary_group_flair_bg_color":null}],"suggested_topics":[{"id":11,"title":"This is a very important announcement","fancy_title":"This is a very important announcement","slug":"this-is-a-very-important-announcement","posts_count":1,"reply_count":0,"highest_post_number":1,"image_url":null,"created_at":"2017-01-27T03:52:02.061Z","last_posted_at":"2017-01-27T03:52:02.119Z","bumped":true,"bumped_at":"2017-01-27T03:52:02.119Z","unseen":false,"last_read_post_number":1,"unread":0,"new_posts":0,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"notification_level":3,"bookmarked":false,"liked":false,"archetype":"private_message","like_count":0,"views":1,"category_id":null,"featured_link":null,"posters":[{"extras":"latest single","description":"Original Poster, Most Recent Poster","user":{"id":1,"username":"test","avatar_template":"/images/avatar.png"}}]}],"notification_level":3,"notifications_reason_id":1,"can_move_posts":true,"can_edit":true,"can_delete":true,"can_recover":true,"can_remove_allowed_users":true,"can_invite_to":true,"can_create_post":true,"can_reply_as_new_topic":true,"can_flag_topic":true},"highest_post_number":2,"last_read_post_number":2,"last_read_post_id":16,"deleted_by":null,"has_deleted":false,"actions_summary":[{"id":4,"count":0,"hidden":false,"can_act":true},{"id":7,"count":0,"hidden":false,"can_act":true},{"id":8,"count":0,"hidden":false,"can_act":true}],"chunk_size":20,"bookmarked":false,"message_archived":false,"featured_link":null}, -"/t/299/1.json": {"post_stream":{"posts":[{"id":18,"username":"eviltrout","avatar_template":"/images/avatar.png","name":"Evil Trout","uploaded_avatar_id":9,"created_at":"2015-08-13T14:49:11.840Z","cooked":"

This is the first post.

","post_number":1,"post_type":1,"updated_at":"2015-08-13T14:49:11.840Z","reply_count":0,"reply_to_post_number":null,"quote_count":0,"avg_time":null,"incoming_link_count":0,"reads":1,"score":0,"yours":true,"topic_id":9,"topic_slug":"this-is-a-test-topic","display_username":"","primary_group_name":null,"version":1,"can_edit":true,"can_delete":false,"can_recover":true,"read":true,"user_title":null,"actions_summary":[{"id":3,"can_act":true},{"id":4,"can_act":true},{"id":5,"hidden":true,"can_act":true},{"id":7,"can_act":true},{"id":8,"can_act":true}],"moderator":false,"admin":true,"staff":true,"user_id":1,"hidden":false,"hidden_reason_id":null,"trust_level":4,"deleted_at":null,"user_deleted":false,"edit_reason":null,"can_view_edit_history":true,"wiki":false},{"id":19,"username":"eviltrout","avatar_template":"/images/avatar.png","name":"Evil Trout","uploaded_avatar_id":9,"created_at":"2015-08-13T14:49:18.231Z","cooked":"

This is the second post.

","post_number":2,"post_type":1,"updated_at":"2015-08-13T14:49:18.231Z","reply_count":0,"reply_to_post_number":null,"quote_count":0,"avg_time":null,"incoming_link_count":0,"reads":1,"score":0,"yours":true,"topic_id":9,"topic_slug":"this-is-a-test-topic","display_username":"","primary_group_name":null,"version":1,"can_edit":true,"can_delete":true,"can_recover":true,"read":true,"user_title":null,"actions_summary":[{"id":3,"can_act":true},{"id":4,"can_act":true},{"id":5,"hidden":true,"can_act":true},{"id":7,"can_act":true},{"id":8,"can_act":true}],"moderator":false,"admin":true,"staff":true,"user_id":1,"hidden":false,"hidden_reason_id":null,"trust_level":4,"deleted_at":null,"user_deleted":false,"edit_reason":null,"can_view_edit_history":true,"wiki":false},{"id":20,"username":"eviltrout","avatar_template":"/images/avatar.png","name":"Evil Trout","uploaded_avatar_id":9,"created_at":"2015-08-13T14:49:23.927Z","cooked":"

This is the third post.

","post_number":3,"post_type":1,"updated_at":"2015-08-13T14:49:23.927Z","reply_count":0,"reply_to_post_number":null,"quote_count":0,"avg_time":null,"incoming_link_count":0,"reads":1,"score":0,"yours":true,"topic_id":9,"topic_slug":"this-is-a-test-topic","display_username":"","primary_group_name":null,"version":1,"can_edit":true,"can_delete":true,"can_recover":true,"read":true,"user_title":null,"actions_summary":[{"id":3,"can_act":true},{"id":4,"can_act":true},{"id":5,"hidden":true,"can_act":true},{"id":7,"can_act":true},{"id":8,"can_act":true}],"moderator":false,"admin":true,"staff":true,"user_id":1,"hidden":false,"hidden_reason_id":null,"trust_level":4,"deleted_at":null,"user_deleted":false,"edit_reason":null,"can_view_edit_history":true,"wiki":false}],"stream":[18,19,20]},"id":299,"title":"Look at this link","fancy_title":"Look at this link","posts_count":3,"created_at":"2015-08-13T14:49:11.720Z","views":1,"reply_count":0,"participant_count":1,"like_count":0,"last_posted_at":"2015-08-13T14:49:23.927Z","visible":true,"closed":false,"archived":false,"has_summary":false,"archetype":"regular","slug":"this-is-a-test-topic","category_id":1,"word_count":15,"deleted_at":null,"pending_posts_count":0,"user_id":1,"draft":null,"draft_key":"topic_9","draft_sequence":3,"posted":true,"unpinned":null,"pinned_globally":false,"pinned":false,"pinned_at":null,"pinned_until":null,featured_link:"http://www.example.com/has-title.html","details":{"auto_close_at":null,"auto_close_hours":null,"auto_close_based_on_last_post":false,"created_by":{"id":1,"username":"tgxworld","uploaded_avatar_id":9,"avatar_template":"/images/avatar.png"},"last_poster":{"id":1,"username":"tgxworld","uploaded_avatar_id":9,"avatar_template":"/images/avatar.png"},"participants":[{"id":1,"username":"tgxworld","uploaded_avatar_id":9,"avatar_template":"/images/avatar.png","post_count":3}],"suggested_topics":[{"id":8,"title":"This is a new and awesome topic!","fancy_title":"This is a new and awesome topic!","slug":"this-is-a-new-and-awesome-topic","posts_count":3,"reply_count":0,"highest_post_number":5,"image_url":null,"created_at":"2015-08-13T05:17:00.000Z","last_posted_at":"2015-08-13T10:14:34.799Z","bumped":true,"bumped_at":"2015-08-13T10:14:34.799Z","unseen":false,"last_read_post_number":5,"unread":0,"new_posts":0,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"notification_level":2,"bookmarked":false,"liked":false,"archetype":"regular","like_count":0,"views":2,"category_id":1},{"id":7,"title":"This is a test category!","fancy_title":"This is a test category!","slug":"this-is-a-test-category","posts_count":3,"reply_count":0,"highest_post_number":3,"image_url":null,"created_at":"2015-08-10T13:40:38.439Z","last_posted_at":"2015-08-13T01:59:44.928Z","bumped":true,"bumped_at":"2015-08-13T01:58:35.206Z","unseen":false,"last_read_post_number":3,"unread":0,"new_posts":0,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"notification_level":3,"bookmarked":false,"liked":false,"archetype":"regular","like_count":0,"views":2,"category_id":1}],"notification_level":3,"notifications_reason_id":1,"can_move_posts":true,"can_edit":true,"can_delete":true,"can_recover":true,"can_remove_allowed_users":true,"can_invite_to":true,"can_create_post":true,"can_reply_as_new_topic":true,"can_flag_topic":true},"highest_post_number":3,"last_read_post_number":3,"deleted_by":null,"has_deleted":false,"actions_summary":[{"id":4,"count":0,"hidden":false,"can_act":true},{"id":7,"count":0,"hidden":false,"can_act":true},{"id":8,"count":0,"hidden":false,"can_act":true}],"chunk_size":20,"bookmarked":false} }; +"/t/299/1.json": {"post_stream":{"posts":[{"id":18,"username":"eviltrout","avatar_template":"/images/avatar.png","name":"Evil Trout","uploaded_avatar_id":9,"created_at":"2015-08-13T14:49:11.840Z","cooked":"

This is the first post.

","post_number":1,"post_type":1,"updated_at":"2015-08-13T14:49:11.840Z","reply_count":0,"reply_to_post_number":null,"quote_count":0,"avg_time":null,"incoming_link_count":0,"reads":1,"score":0,"yours":true,"topic_id":9,"topic_slug":"this-is-a-test-topic","display_username":"","primary_group_name":null,"version":1,"can_edit":true,"can_delete":false,"can_recover":true,"read":true,"user_title":null,"actions_summary":[{"id":3,"can_act":true},{"id":4,"can_act":true},{"id":5,"hidden":true,"can_act":true},{"id":7,"can_act":true},{"id":8,"can_act":true}],"moderator":false,"admin":true,"staff":true,"user_id":1,"hidden":false,"hidden_reason_id":null,"trust_level":4,"deleted_at":null,"user_deleted":false,"edit_reason":null,"can_view_edit_history":true,"wiki":false},{"id":19,"username":"eviltrout","avatar_template":"/images/avatar.png","name":"Evil Trout","uploaded_avatar_id":9,"created_at":"2015-08-13T14:49:18.231Z","cooked":"

This is the second post.

","post_number":2,"post_type":1,"updated_at":"2015-08-13T14:49:18.231Z","reply_count":0,"reply_to_post_number":null,"quote_count":0,"avg_time":null,"incoming_link_count":0,"reads":1,"score":0,"yours":true,"topic_id":9,"topic_slug":"this-is-a-test-topic","display_username":"","primary_group_name":null,"version":1,"can_edit":true,"can_delete":true,"can_recover":true,"read":true,"user_title":null,"actions_summary":[{"id":3,"can_act":true},{"id":4,"can_act":true},{"id":5,"hidden":true,"can_act":true},{"id":7,"can_act":true},{"id":8,"can_act":true}],"moderator":false,"admin":true,"staff":true,"user_id":1,"hidden":false,"hidden_reason_id":null,"trust_level":4,"deleted_at":null,"user_deleted":false,"edit_reason":null,"can_view_edit_history":true,"wiki":false},{"id":20,"username":"eviltrout","avatar_template":"/images/avatar.png","name":"Evil Trout","uploaded_avatar_id":9,"created_at":"2015-08-13T14:49:23.927Z","cooked":"

This is the third post.

","post_number":3,"post_type":1,"updated_at":"2015-08-13T14:49:23.927Z","reply_count":0,"reply_to_post_number":null,"quote_count":0,"avg_time":null,"incoming_link_count":0,"reads":1,"score":0,"yours":true,"topic_id":9,"topic_slug":"this-is-a-test-topic","display_username":"","primary_group_name":null,"version":1,"can_edit":true,"can_delete":true,"can_recover":true,"read":true,"user_title":null,"actions_summary":[{"id":3,"can_act":true},{"id":4,"can_act":true},{"id":5,"hidden":true,"can_act":true},{"id":7,"can_act":true},{"id":8,"can_act":true}],"moderator":false,"admin":true,"staff":true,"user_id":1,"hidden":false,"hidden_reason_id":null,"trust_level":4,"deleted_at":null,"user_deleted":false,"edit_reason":null,"can_view_edit_history":true,"wiki":false}],"stream":[18,19,20]},"id":299,"title":"Look at this link","fancy_title":"Look at this link","posts_count":3,"created_at":"2015-08-13T14:49:11.720Z","views":1,"reply_count":0,"participant_count":1,"like_count":0,"last_posted_at":"2015-08-13T14:49:23.927Z","visible":true,"closed":false,"archived":false,"has_summary":false,"archetype":"regular","slug":"this-is-a-test-topic","category_id":1,"word_count":15,"deleted_at":null,"pending_posts_count":0,"user_id":1,"draft":null,"draft_key":"topic_9","draft_sequence":3,"posted":true,"unpinned":null,"pinned_globally":false,"pinned":false,"pinned_at":null,"pinned_until":null,featured_link:"http://www.example.com/has-title.html","details":{"auto_close_at":null,"auto_close_hours":null,"auto_close_based_on_last_post":false,"created_by":{"id":1,"username":"tgxworld","uploaded_avatar_id":9,"avatar_template":"/images/avatar.png"},"last_poster":{"id":1,"username":"tgxworld","uploaded_avatar_id":9,"avatar_template":"/images/avatar.png"},"participants":[{"id":1,"username":"tgxworld","uploaded_avatar_id":9,"avatar_template":"/images/avatar.png","post_count":3}],"suggested_topics":[{"id":8,"title":"This is a new and awesome topic!","fancy_title":"This is a new and awesome topic!","slug":"this-is-a-new-and-awesome-topic","posts_count":3,"reply_count":0,"highest_post_number":5,"image_url":null,"created_at":"2015-08-13T05:17:00.000Z","last_posted_at":"2015-08-13T10:14:34.799Z","bumped":true,"bumped_at":"2015-08-13T10:14:34.799Z","unseen":false,"last_read_post_number":5,"unread":0,"new_posts":0,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"notification_level":2,"bookmarked":false,"liked":false,"archetype":"regular","like_count":0,"views":2,"category_id":1},{"id":7,"title":"This is a test category!","fancy_title":"This is a test category!","slug":"this-is-a-test-category","posts_count":3,"reply_count":0,"highest_post_number":3,"image_url":null,"created_at":"2015-08-10T13:40:38.439Z","last_posted_at":"2015-08-13T01:59:44.928Z","bumped":true,"bumped_at":"2015-08-13T01:58:35.206Z","unseen":false,"last_read_post_number":3,"unread":0,"new_posts":0,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"notification_level":3,"bookmarked":false,"liked":false,"archetype":"regular","like_count":0,"views":2,"category_id":1}],"notification_level":3,"notifications_reason_id":1,"can_move_posts":true,"can_edit":true,"can_delete":true,"can_recover":true,"can_remove_allowed_users":true,"can_invite_to":true,"can_create_post":true,"can_reply_as_new_topic":true,"can_flag_topic":true},"highest_post_number":3,"last_read_post_number":3,"deleted_by":null,"has_deleted":false,"actions_summary":[{"id":4,"count":0,"hidden":false,"can_act":true},{"id":7,"count":0,"hidden":false,"can_act":true},{"id":8,"count":0,"hidden":false,"can_act":true}],"chunk_size":20,"bookmarked":false}, +"/t/301/1.json": {"post_stream":{"posts":[{"id":18,"username":"eviltrout","avatar_template":"/images/avatar.png","name":"Evil Trout","uploaded_avatar_id":9,"created_at":"2015-08-13T14:49:11.840Z","cooked":"

This is the first post. @discourse

","post_number":1,"post_type":1,"updated_at":"2015-08-13T14:49:11.840Z","reply_count":0,"reply_to_post_number":null,"quote_count":0,"avg_time":null,"incoming_link_count":0,"reads":1,"score":0,"yours":true,"topic_id":9,"topic_slug":"this-is-a-test-topic","display_username":"","primary_group_name":null,"version":1,"can_edit":true,"can_delete":false,"can_recover":true,"read":true,"user_title":null,"actions_summary":[{"id":3,"can_act":true},{"id":4,"can_act":true},{"id":5,"hidden":true,"can_act":true},{"id":7,"can_act":true},{"id":8,"can_act":true}],"moderator":false,"admin":true,"staff":true,"user_id":1,"hidden":false,"hidden_reason_id":null,"trust_level":4,"deleted_at":null,"user_deleted":false,"edit_reason":null,"can_view_edit_history":true,"wiki":false},{"id":19,"username":"eviltrout","avatar_template":"/images/avatar.png","name":"Evil Trout","uploaded_avatar_id":9,"created_at":"2015-08-13T14:49:18.231Z","cooked":"

This is the second post.

","post_number":2,"post_type":1,"updated_at":"2015-08-13T14:49:18.231Z","reply_count":0,"reply_to_post_number":null,"quote_count":0,"avg_time":null,"incoming_link_count":0,"reads":1,"score":0,"yours":true,"topic_id":9,"topic_slug":"this-is-a-test-topic","display_username":"","primary_group_name":null,"version":1,"can_edit":true,"can_delete":true,"can_recover":true,"read":true,"user_title":null,"actions_summary":[{"id":3,"can_act":true},{"id":4,"can_act":true},{"id":5,"hidden":true,"can_act":true},{"id":7,"can_act":true},{"id":8,"can_act":true}],"moderator":false,"admin":true,"staff":true,"user_id":1,"hidden":false,"hidden_reason_id":null,"trust_level":4,"deleted_at":null,"user_deleted":false,"edit_reason":null,"can_view_edit_history":true,"wiki":false},{"id":20,"username":"eviltrout","avatar_template":"/images/avatar.png","name":"Evil Trout","uploaded_avatar_id":9,"created_at":"2015-08-13T14:49:23.927Z","cooked":"

This is the third post.

","post_number":3,"post_type":1,"updated_at":"2015-08-13T14:49:23.927Z","reply_count":0,"reply_to_post_number":null,"quote_count":0,"avg_time":null,"incoming_link_count":0,"reads":1,"score":0,"yours":true,"topic_id":9,"topic_slug":"this-is-a-test-topic","display_username":"","primary_group_name":null,"version":1,"can_edit":true,"can_delete":true,"can_recover":true,"read":true,"user_title":null,"actions_summary":[{"id":3,"can_act":true},{"id":4,"can_act":true},{"id":5,"hidden":true,"can_act":true},{"id":7,"can_act":true},{"id":8,"can_act":true}],"moderator":false,"admin":true,"staff":true,"user_id":1,"hidden":false,"hidden_reason_id":null,"trust_level":4,"deleted_at":null,"user_deleted":false,"edit_reason":null,"can_view_edit_history":true,"wiki":false}],"stream":[18,19,20]},"id":299,"title":"Look at this link","fancy_title":"Look at this link","posts_count":3,"created_at":"2015-08-13T14:49:11.720Z","views":1,"reply_count":0,"participant_count":1,"like_count":0,"last_posted_at":"2015-08-13T14:49:23.927Z","visible":true,"closed":false,"archived":false,"has_summary":false,"archetype":"regular","slug":"this-is-a-test-topic","category_id":1,"word_count":15,"deleted_at":null,"pending_posts_count":0,"user_id":1,"draft":null,"draft_key":"topic_9","draft_sequence":3,"posted":true,"unpinned":null,"pinned_globally":false,"pinned":false,"pinned_at":null,"pinned_until":null,featured_link:"http://www.example.com/has-title.html","details":{"auto_close_at":null,"auto_close_hours":null,"auto_close_based_on_last_post":false,"created_by":{"id":1,"username":"tgxworld","uploaded_avatar_id":9,"avatar_template":"/images/avatar.png"},"last_poster":{"id":1,"username":"tgxworld","uploaded_avatar_id":9,"avatar_template":"/images/avatar.png"},"participants":[{"id":1,"username":"tgxworld","uploaded_avatar_id":9,"avatar_template":"/images/avatar.png","post_count":3}],"suggested_topics":[{"id":8,"title":"This is a new and awesome topic!","fancy_title":"This is a new and awesome topic!","slug":"this-is-a-new-and-awesome-topic","posts_count":3,"reply_count":0,"highest_post_number":5,"image_url":null,"created_at":"2015-08-13T05:17:00.000Z","last_posted_at":"2015-08-13T10:14:34.799Z","bumped":true,"bumped_at":"2015-08-13T10:14:34.799Z","unseen":false,"last_read_post_number":5,"unread":0,"new_posts":0,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"notification_level":2,"bookmarked":false,"liked":false,"archetype":"regular","like_count":0,"views":2,"category_id":1},{"id":7,"title":"This is a test category!","fancy_title":"This is a test category!","slug":"this-is-a-test-category","posts_count":3,"reply_count":0,"highest_post_number":3,"image_url":null,"created_at":"2015-08-10T13:40:38.439Z","last_posted_at":"2015-08-13T01:59:44.928Z","bumped":true,"bumped_at":"2015-08-13T01:58:35.206Z","unseen":false,"last_read_post_number":3,"unread":0,"new_posts":0,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"notification_level":3,"bookmarked":false,"liked":false,"archetype":"regular","like_count":0,"views":2,"category_id":1}],"notification_level":3,"notifications_reason_id":1,"can_move_posts":true,"can_edit":true,"can_delete":true,"can_recover":true,"can_remove_allowed_users":true,"can_invite_to":true,"can_create_post":true,"can_reply_as_new_topic":true,"can_flag_topic":true},"highest_post_number":3,"last_read_post_number":3,"deleted_by":null,"has_deleted":false,"actions_summary":[{"id":4,"count":0,"hidden":false,"can_act":true},{"id":7,"count":0,"hidden":false,"can_act":true},{"id":8,"count":0,"hidden":false,"can_act":true}],"chunk_size":20,"bookmarked":false} };