Revert "Revert "Revert Ember 2.10+ for a short while""

This reverts commit 76bbc481cb.
This commit is contained in:
Robin Ward
2016-12-16 10:29:30 -05:00
parent 76bbc481cb
commit ddd299f4aa
101 changed files with 1273 additions and 1866 deletions

View File

@@ -36,7 +36,6 @@ export default Ember.Component.extend({
loadScript("/javascripts/ace/ace.js", { scriptTag: true }).then(() => {
window.ace.require(['ace/ace'], loadedAce => {
if (!this.element || this.isDestroying || this.isDestroyed) { return; }
const editor = loadedAce.edit(this.$('.ace')[0]);
editor.setTheme("ace/theme/chrome");

View File

@@ -68,7 +68,7 @@ export default Ember.Controller.extend(BufferedContent, {
this.get('model').save(data).then(() => {
if (newBadge) {
const adminBadges = this.get('adminBadges.model');
if (!adminBadges.includes(model)) {
if (!adminBadges.contains(model)) {
adminBadges.pushObject(model);
}
this.transitionToRoute('adminBadges.show', model.get('id'));

View File

@@ -23,7 +23,7 @@
{{nav-item route='admin.backups' label='admin.backups.title'}}
{{/if}}
{{nav-item route='adminPlugins' label='admin.plugins.title'}}
{{plugin-outlet name="admin-menu" connectorTagName="li"}}
{{plugin-outlet "admin-menu" tagName="li"}}
</ul>
<div class='boxed white admin-content'>

View File

@@ -1,4 +1,4 @@
{{plugin-outlet name="admin-dashboard-top"}}
{{plugin-outlet "admin-dashboard-top"}}
{{#conditional-loading-spinner condition=loading}}
<div class="dashboard-left">

View File

@@ -108,7 +108,7 @@
{{#if siteSettings.email_in}}
<label for="incoming_email">{{i18n 'admin.groups.incoming_email'}}</label>
{{text-field name="incoming_email" value=model.incoming_email placeholderKey="admin.groups.incoming_email_placeholder"}}
{{plugin-outlet name="group-email-in" args=(hash model=model)}}
{{plugin-outlet "group-email-in"}}
{{/if}}
{{/unless}}

View File

@@ -1,7 +1,7 @@
<div class="dashboard-stats version-check {{if versionCheck.critical_updates 'critical' 'normal'}}">
<table class="table table-condensed table-hover">
<thead>
{{custom-html name="upgrade-header"}}
{{custom-html 'upgrade-header'}}
<tr>
<th>&nbsp;</th>
<th>{{i18n 'admin.dashboard.installed_version'}}</th>

View File

@@ -89,6 +89,7 @@
//= require_tree ./discourse/models
//= require_tree ./discourse/components
//= require_tree ./discourse/raw-views
//= require_tree ./discourse/views
//= require_tree ./discourse/helpers
//= require_tree ./discourse/templates
//= require_tree ./discourse/routes

View File

@@ -10,23 +10,19 @@ export function setResolverOption(name, value) {
_options[name] = value;
}
export function getResolverOption(name) {
return _options[name];
}
function parseName(fullName) {
const nameParts = fullName.split(":");
const type = nameParts[0];
let fullNameWithoutType = nameParts[1];
const namespace = get(this, 'namespace');
const root = namespace;
const nameParts = fullName.split(":"),
type = nameParts[0], fullNameWithoutType = nameParts[1],
name = fullNameWithoutType,
namespace = get(this, 'namespace'),
root = namespace;
return {
fullName,
type,
fullNameWithoutType,
name: fullNameWithoutType,
root,
fullName: fullName,
type: type,
fullNameWithoutType: fullNameWithoutType,
name: name,
root: root,
resolveMethodName: "resolve" + classify(type)
};
}
@@ -129,21 +125,12 @@ export function buildResolver(baseName) {
}
},
findConnectorTemplate(parsedName) {
const full = parsedName.fullNameWithoutType.replace('components/', '');
if (full.indexOf('connectors') === 0) {
return Ember.TEMPLATES[`javascripts/${full}`];
}
},
resolveTemplate(parsedName) {
return this.findPluginMobileTemplate(parsedName) ||
this.findPluginTemplate(parsedName) ||
this.findMobileTemplate(parsedName) ||
this.findTemplate(parsedName) ||
this.findLoadingTemplate(parsedName) ||
this.findConnectorTemplate(parsedName) ||
Ember.TEMPLATES.not_found;
},

View File

@@ -6,7 +6,7 @@ const _pluginCallbacks = [];
const Discourse = Ember.Application.extend({
rootElement: '#main',
_docTitle: document.title,
RAW_TEMPLATES: {},
__TAGS_INCLUDED__: true,
getURL(url) {
if (!url) return url;

View File

@@ -1,5 +1,5 @@
import { on, observes, default as computed } from 'ember-addons/ember-computed-decorators';
import { findRawTemplate } from 'discourse/lib/raw-templates';
import { getOwner } from 'discourse-common/lib/get-owner';
export default Ember.Component.extend({
@computed('placeholderKey')
@@ -18,6 +18,7 @@ export default Ember.Component.extend({
var self = this;
var selectedBadges;
var template = getOwner(this).lookup('template:badge-selector-autocomplete.raw');
self.$('input').autocomplete({
allowAny: false,
items: _.isArray(this.get('badgeNames')) ? this.get('badgeNames') : [this.get('badgeNames')],
@@ -42,7 +43,7 @@ export default Ember.Component.extend({
});
});
},
template: findRawTemplate('badge-selector-autocomplete')
template: template
});
}
});

View File

@@ -1,7 +1,7 @@
import { categoryBadgeHTML } from 'discourse/helpers/category-link';
import Category from 'discourse/models/category';
import { on, observes } from 'ember-addons/ember-computed-decorators';
import { findRawTemplate } from 'discourse/lib/raw-templates';
import { getOwner } from 'discourse-common/lib/get-owner';
export default Ember.Component.extend({
@observes('categories')
@@ -13,6 +13,7 @@ export default Ember.Component.extend({
@on('didInsertElement')
_initializeAutocomplete(opts) {
const self = this,
template = getOwner(this).lookup('template:category-selector-autocomplete.raw'),
regexp = new RegExp(`href=['\"]${Discourse.getURL('/c/')}([^'\"]+)`);
this.$('input').autocomplete({
@@ -40,7 +41,7 @@ export default Ember.Component.extend({
self.set('categories', categories);
});
},
template: findRawTemplate('category-selector-autocomplete'),
template,
transformComplete(category) {
return categoryBadgeHTML(category, {allowUncategorized: true});
}

View File

@@ -6,7 +6,7 @@ import { linkSeenTagHashtags, fetchUnseenTagHashtags } from 'discourse/lib/link-
import { load } from 'pretty-text/oneboxer';
import { ajax } from 'discourse/lib/ajax';
import InputValidation from 'discourse/models/input-validation';
import { findRawTemplate } from 'discourse/lib/raw-templates';
import { getOwner } from 'discourse-common/lib/get-owner';
import { tinyAvatar,
displayErrorForUpload,
getUploadMarkdown,
@@ -62,9 +62,10 @@ export default Ember.Component.extend({
@on('didInsertElement')
_composerEditorInit() {
const topicId = this.get('topic.id');
const template = getOwner(this).lookup('template:user-selector-autocomplete.raw');
const $input = this.$('.d-editor-input');
$input.autocomplete({
template: findRawTemplate('user-selector-autocomplete'),
template,
dataSource: term => userSearch({ term, topicId, includeGroups: true }),
key: "@",
transformComplete: v => v.username || v.name

View File

@@ -51,8 +51,6 @@ export default Ember.Component.extend({
},
_checkForUrl() {
if (!this.element || this.isDestroying || this.isDestroyed) { return; }
if (this.get('isAbsoluteUrl') && (this.get('composer.reply')||"").length === 0) {
// Try to onebox. If success, update post body and title.

View File

@@ -1,20 +0,0 @@
import { getCustomHTML } from 'discourse/helpers/custom-html';
import { getOwner } from 'discourse-common/lib/get-owner';
export default Ember.Component.extend({
init() {
this._super();
const name = this.get('name');
const html = getCustomHTML(name);
if (html) {
this.set('html', html);
this.set('layoutName', 'components/custom-html-container');
} else {
const template = getOwner(this).lookup(`template:${name}`);
if (template) {
this.set('layoutName', name);
}
}
}
});

View File

@@ -11,7 +11,6 @@ import { translations } from 'pretty-text/emoji/data';
import { emojiSearch } from 'pretty-text/emoji';
import { emojiUrlFor } from 'discourse/lib/text';
import { getRegister } from 'discourse-common/lib/get-owner';
import { findRawTemplate } from 'discourse/lib/raw-templates';
import deprecated from 'discourse-common/lib/deprecated';
// Our head can be a static string or a function that returns a string
@@ -298,10 +297,11 @@ export default Ember.Component.extend({
},
_applyCategoryHashtagAutocomplete() {
const template = this.register.lookup('template:category-tag-autocomplete.raw');
const siteSettings = this.siteSettings;
this.$('.d-editor-input').autocomplete({
template: findRawTemplate('category-tag-autocomplete'),
template: template,
key: '#',
transformComplete(obj) {
if (obj.model) {
@@ -323,10 +323,11 @@ export default Ember.Component.extend({
if (!this.siteSettings.enable_emoji) { return; }
const register = this.register;
const template = this.register.lookup('template:emoji-selector-autocomplete.raw');
const self = this;
$editorInput.autocomplete({
template: findRawTemplate('emoji-selector-autocomplete'),
template: template,
key: ":",
afterComplete(text) {
self.set('value', text);

View File

@@ -1,5 +1,5 @@
import { on, observes, default as computed } from 'ember-addons/ember-computed-decorators';
import { findRawTemplate } from 'discourse/lib/raw-templates';
import { getOwner } from 'discourse-common/lib/get-owner';
export default Ember.Component.extend({
@computed('placeholderKey')
@@ -19,6 +19,7 @@ export default Ember.Component.extend({
var selectedGroups;
var groupNames = this.get('groupNames');
var template = getOwner(this).lookup('template:group-selector-autocomplete.raw');
self.$('input').autocomplete({
allowAny: false,
items: _.isArray(groupNames) ? groupNames : (Ember.isEmpty(groupNames)) ? [] : [groupNames],
@@ -43,7 +44,7 @@ export default Ember.Component.extend({
});
});
},
template: findRawTemplate('group-selector-autocomplete')
template: template
});
}
});

View File

@@ -23,6 +23,8 @@ export default Ember.Component.extend({
this._super();
const name = this.get('widget');
(this.get('delegated') || []).forEach(m => this.set(m, m));
this.register = getRegister(this);
this._widgetClass = queryRegistry(name) || this.register.lookupFactory(`widget:${name}`);
@@ -31,6 +33,7 @@ export default Ember.Component.extend({
console.error(`Error: Could not find widget: ${name}`);
}
this._childEvents = [];
this._connected = [];
this._dispatched = [];

View File

@@ -1,22 +0,0 @@
export default Ember.Component.extend({
init() {
this._super();
const connector = this.get('connector');
this.set('layoutName', connector.templateName);
const args = this.get('args') || {};
Object.keys(args).forEach(key => this.set(key, args[key]));
const connectorClass = this.get('connector.connectorClass');
connectorClass.setupComponent.call(this, args, this);
},
send(name, ...args) {
const connectorClass = this.get('connector.connectorClass');
const action = connectorClass.actions[name];
return action ? action.call(this, ...args) : this._super(name, ...args);
}
});

View File

@@ -1,51 +0,0 @@
/**
A plugin outlet is an extension point for templates where other templates can
be inserted by plugins.
## Usage
If your handlebars template has:
```handlebars
{{plugin-outlet name="evil-trout"}}
```
Then any handlebars files you create in the `connectors/evil-trout` directory
will automatically be appended. For example:
plugins/hello/assets/javascripts/discourse/templates/connectors/evil-trout/hello.hbs
With the contents:
```handlebars
<b>Hello World</b>
```
Will insert <b>Hello World</b> at that point in the template.
## Disabling
If a plugin returns a disabled status, the outlets will not be wired up for it.
The list of disabled plugins is returned via the `Site` singleton.
**/
import { connectorsFor } from 'discourse/lib/plugin-connectors';
export default Ember.Component.extend({
tagName: 'span',
connectors: null,
init() {
this._super();
const name = this.get('name');
if (name) {
const args = this.get('args');
const connectors = connectorsFor(name).filter(con => {
return con.connectorClass.shouldRender(args, this);
});
this.set('connectors', connectors);
}
}
});

View File

@@ -48,15 +48,12 @@ export default Em.Component.extend({
init() {
this._super();
Ember.run.scheduleOnce('afterRender', () => {
this._init();
this._update();
});
this._init();
this._update();
},
@observes('searchTerm')
_updateOptions() {
this._update();
Ember.run.debounce(this, this._update, 250);
},

View File

@@ -8,17 +8,10 @@ export default Ember.TextField.extend({
classNameBindings: [':tag-chooser'],
attributeBindings: ['tabIndex', 'placeholderKey', 'categoryId'],
init() {
this._super();
_initValue: function() {
const tags = this.get('tags') || [];
this.set('value', tags.join(", "));
if (this.get('allowCreate') !== false) {
this.set('allowCreate', this.site.get('can_create_tag'));
}
this.set('termMatchesForbidden', false);
},
}.on('init'),
_valueChanged: function() {
const tags = this.get('value').split(',').map(v => v.trim()).reject(v => v.length === 0).uniq();
@@ -39,13 +32,18 @@ export default Ember.TextField.extend({
}
}.observes('tags'),
didInsertElement() {
this._super();
_initializeTags: function() {
const site = this.site,
self = this,
filterRegexp = new RegExp(this.site.tags_filter_regexp, "g");
const self = this;
const filterRegexp = new RegExp(this.site.tags_filter_regexp, "g");
var limit = this.siteSettings.max_tags_per_topic;
let limit = this.siteSettings.max_tags_per_topic;
if (this.get('allowCreate') !== false) {
this.set('allowCreate', site.get('can_create_tag'));
}
this.set('termMatchesForbidden', false);
if (this.get('unlimitedTagCount')) {
limit = null;
@@ -79,7 +77,7 @@ export default Ember.TextField.extend({
callback(data);
},
createSearchChoice(term, data) {
createSearchChoice: function(term, data) {
term = term.replace(filterRegexp, '').trim().toLowerCase();
// No empty terms, make sure the user has permission to create the tag
@@ -91,14 +89,14 @@ export default Ember.TextField.extend({
return { id: term, text: term };
}
},
createSearchChoicePosition(list, item) {
createSearchChoicePosition: function(list, item) {
// Search term goes on the bottom
list.push(item);
},
formatSelection(data) {
formatSelection: function (data) {
return data ? renderTag(this.text(data)) : undefined;
},
formatSelectionCssClass() {
formatSelectionCssClass: function(){
return "discourse-tag-select2";
},
formatResult: formatTag,
@@ -129,11 +127,10 @@ export default Ember.TextField.extend({
}
},
});
},
}.on('didInsertElement'),
willDestroyElement() {
this._super();
_destroyTags: function() {
this.$().select2('destroy');
}
}.on('willDestroyElement')
});

View File

@@ -1,11 +1,17 @@
import computed from 'ember-addons/ember-computed-decorators';
import DelegatedActions from 'discourse/mixins/delegated-actions';
export default Ember.Component.extend({
export default Ember.Component.extend(DelegatedActions, {
elementId: 'topic-footer-buttons',
// Allow us to extend it
layoutName: 'components/topic-footer-buttons',
init() {
this._super();
this.delegateAll(this.get('topicDelegated'));
},
@computed('topic.details.can_invite_to')
canInviteTo(result) {
return !this.site.mobileView && result;

View File

@@ -1,6 +1,6 @@
import computed from 'ember-addons/ember-computed-decorators';
import { bufferedRender } from 'discourse-common/lib/buffered-render';
import { findRawTemplate } from 'discourse/lib/raw-templates';
import { getOwner } from 'discourse-common/lib/get-owner';
export function showEntrance(e) {
let target = $(e.target);
@@ -32,7 +32,7 @@ export default Ember.Component.extend(bufferedRender({
},
buildBuffer(buffer) {
const template = findRawTemplate('list/topic-list-item');
const template = getOwner(this).lookup('template:list/topic-list-item.raw');
if (template) {
buffer.push(template(this));
}

View File

@@ -3,11 +3,17 @@ import { default as computed, observes } from 'ember-addons/ember-computed-decor
export default Ember.Component.extend({
elementId: 'topic-progress-wrapper',
classNameBindings: ['docked'],
expanded: false,
docked: false,
progressPosition: null,
postStream: Ember.computed.alias('topic.postStream'),
_streamPercentage: null,
init() {
this._super();
(this.get('delegated') || []).forEach(m => this.set(m, m));
},
@computed('progressPosition')
jumpTopDisabled(progressPosition) {
return progressPosition <= 3;

View File

@@ -1,7 +1,7 @@
import { observes } from 'ember-addons/ember-computed-decorators';
import TextField from 'discourse/components/text-field';
import userSearch from 'discourse/lib/user-search';
import { findRawTemplate } from 'discourse/lib/raw-templates';
import { getOwner } from 'discourse-common/lib/get-owner';
export default TextField.extend({
@observes('usernames')
@@ -31,7 +31,7 @@ export default TextField.extend({
}
this.$().val(this.get('usernames')).autocomplete({
template: findRawTemplate('user-selector-autocomplete'),
template: getOwner(this).lookup('template:user-selector-autocomplete.raw'),
disabled: this.get('disabled'),
single: this.get('single'),
allowAny: this.get('allowAny'),

View File

@@ -15,10 +15,4 @@ export default Ember.Controller.extend({
loginRequired() {
return Discourse.SiteSettings.login_required && !Discourse.User.current();
},
actions: {
appRouteAction(name) {
return this.send(name);
}
}
});

View File

@@ -114,7 +114,7 @@ export default Ember.Controller.extend(ModalFunctionality, {
email = this.get("accountEmail");
if (this.get('rejectedEmails').includes(email)) {
if (this.get('rejectedEmails').contains(email)) {
return InputValidation.create({
failed: true,
reason: I18n.t('user.email.invalid')
@@ -314,7 +314,7 @@ export default Ember.Controller.extend(ModalFunctionality, {
});
}
if (this.get('rejectedPasswords').includes(password)) {
if (this.get('rejectedPasswords').contains(password)) {
return InputValidation.create({
failed: true,
reason: I18n.t('user.password.common')

View File

@@ -16,7 +16,7 @@ if (customNavItemHref) {
if (navItem.get('tagId')) {
var name = navItem.get('name');
if ( !Discourse.Site.currentProp('filters').includes(name) ) {
if ( !Discourse.Site.currentProp('filters').contains(name) ) {
return null;
}

View File

@@ -32,6 +32,30 @@ export default Ember.Controller.extend(SelectedPostsCount, BufferedContent, {
filter: null,
quoteState: null,
topicDelegated: [
'toggleMultiSelect',
'deleteTopic',
'recoverTopic',
'toggleClosed',
'showAutoClose',
'showFeatureTopic',
'showChangeTimestamp',
'toggleArchived',
'toggleVisibility',
'convertToPublicTopic',
'convertToPrivateMessage',
'jumpTop',
'jumpToPost',
'jumpToPostPrompt',
'jumpToIndex',
'jumpBottom',
'replyToPost',
'toggleArchiveMessage',
'showInvite',
'toggleBookmark',
'showFlagTopic'
],
updateQueryParams() {
const postStream = this.get('model.postStream');
this.setProperties(postStream.get('streamFilters'));
@@ -151,22 +175,6 @@ export default Ember.Controller.extend(SelectedPostsCount, BufferedContent, {
actions: {
showPostFlags(post) {
return this.send('showFlags', post);
},
topicRouteAction(name, model) {
return this.send(name, model);
},
openAutoClose() {
this.send('showAutoClose');
},
openFeatureTopic() {
this.send('showFeatureTopic');
},
deselectText() {
this.get('quoteState').setProperties({ buffer: null, postId: null });
},
@@ -810,7 +818,7 @@ export default Ember.Controller.extend(SelectedPostsCount, BufferedContent, {
postSelected(post) {
if (this.get('allPostsSelected')) { return true; }
if (this.get('selectedPosts').includes(post)) { return true; }
if (this.get('selectedPosts').contains(post)) { return true; }
if (this.get('selectedReplies').findBy('post_number', post.get('reply_to_post_number'))) { return true; }
return false;

View File

@@ -2,9 +2,6 @@ import { categoryLinkHTML } from 'discourse/helpers/category-link';
import { registerUnbound } from 'discourse-common/lib/helpers';
registerUnbound('category-badge', function(cat, options) {
return categoryLinkHTML(cat, {
hideParent: options.hideParent,
allowUncategorized: options.allowUncategorized,
link: false
});
options.link = false;
return categoryLinkHTML(cat, options);
});

View File

@@ -1,3 +1,5 @@
const { registerKeyword } = Ember.__loader.require("ember-htmlbars/keywords");
const { internal } = Ember.__loader.require('htmlbars-runtime');
import PreloadStore from 'preload-store';
let _customizations = {};
@@ -22,3 +24,36 @@ export function clearHTMLCache() {
export function setCustomHTML(key, html) {
_customizations[key] = html;
}
registerKeyword('custom-html', {
setupState(state, env, scope, params) {
return { htmlKey: env.hooks.getValue(params[0]) };
},
render(renderNode, env, scope, params, hash, template, inverse, visitor) {
let state = renderNode.getState();
if (!state.htmlKey) { return true; }
const html = getCustomHTML(state.htmlKey);
if (html) {
const htmlHash = { html };
env.hooks.component(renderNode,
env,
scope,
'custom-html-container',
params,
htmlHash,
{ default: template, inverse },
visitor);
return true;
}
template = env.owner.lookup(`template:${state.htmlKey}`);
if (template) {
internal.hostBlock(renderNode, env, scope, template.raw, null, null, visitor, function(options) {
options.templates.template.yield();
});
}
return true;
}
});

View File

@@ -0,0 +1,138 @@
/**
A plugin outlet is an extension point for templates where other templates can
be inserted by plugins.
## Usage
If your handlebars template has:
```handlebars
{{plugin-outlet "evil-trout"}}
```
Then any handlebars files you create in the `connectors/evil-trout` directory
will automatically be appended. For example:
plugins/hello/assets/javascripts/discourse/templates/connectors/evil-trout/hello.hbs
With the contents:
```handlebars
<b>Hello World</b>
```
Will insert <b>Hello World</b> at that point in the template.
## Disabling
If a plugin returns a disabled status, the outlets will not be wired up for it.
The list of disabled plugins is returned via the `Site` singleton.
**/
let _connectorCache, _templateCache;
function findOutlets(collection, callback) {
const disabledPlugins = Discourse.Site.currentProp('disabled_plugins') || [];
Object.keys(collection).forEach(function(res) {
if (res.indexOf("/connectors/") !== -1) {
// Skip any disabled plugins
for (let i=0; i<disabledPlugins.length; i++) {
if (res.indexOf("/" + disabledPlugins[i] + "/") !== -1) {
return;
}
}
const segments = res.split("/");
let outletName = segments[segments.length-2];
const uniqueName = segments[segments.length-1];
callback(outletName, res, uniqueName);
}
});
}
export function clearCache() {
_templateCache = null;
_connectorCache = null;
}
function buildConnectorCache() {
_connectorCache = {};
_templateCache = [];
findOutlets(Ember.TEMPLATES, function(outletName, resource, uniqueName) {
_connectorCache[outletName] = _connectorCache[outletName] || [];
_connectorCache[outletName].push({
templateName: resource.replace('javascripts/', ''),
template: Ember.TEMPLATES[resource],
classNames: `${outletName}-outlet ${uniqueName}`
});
});
Object.keys(_connectorCache).forEach(outletName => {
const connector = _connectorCache[outletName];
(connector || []).forEach(s => {
_templateCache.push(s.template);
s.templateId = parseInt(_templateCache.length - 1);
});
});
}
// unbound version of outlets, only has a template
Handlebars.registerHelper('plugin-outlet', function(name) {
if (!_connectorCache) { buildConnectorCache(); }
const connector = _connectorCache[name];
if (connector && connector.length) {
const output = connector.map(c => c.template({context: this}));
return new Handlebars.SafeString(output.join(""));
}
});
const { registerKeyword } = Ember.__loader.require("ember-htmlbars/keywords");
const { internal } = Ember.__loader.require('htmlbars-runtime');
registerKeyword('plugin-outlet', {
setupState(state, env, scope, params) {
if (!_connectorCache) { buildConnectorCache(); }
return { outletName: env.hooks.getValue(params[0]) };
},
render(renderNode, env, scope, params, hash, template, inverse, visitor) {
let state = renderNode.getState();
if (!state.outletName) { return true; }
const connector = _connectorCache[state.outletName];
if (!connector || connector.length === 0) { return true; }
const listTemplate = Ember.TEMPLATES['outlet-list'];
listTemplate.raw.locals = ['templateId', 'outletClasses', 'tagName'];
internal.hostBlock(renderNode, env, scope, listTemplate.raw, null, null, visitor, function(options) {
connector.forEach(source => {
const tid = source.templateId;
options.templates.template.yieldItem(`d-outlet-${tid}`, [
tid,
source.classNames,
hash.tagName || 'div'
]);
});
});
return true;
}
});
registerKeyword('connector', function(morph, env, scope, params, hash, template, inverse, visitor) {
template = _templateCache[parseInt(env.hooks.getValue(hash.templateId))];
env.hooks.component(morph,
env,
scope,
'connector-container',
params,
hash,
{ default: template.raw, inverse },
visitor);
return true;
});

View File

@@ -1,9 +0,0 @@
import { connectorsFor } from 'discourse/lib/plugin-connectors';
Handlebars.registerHelper('raw-plugin-outlet', function(args) {
const connectors = connectorsFor(args.hash.name);
if (connectors.length) {
const output = connectors.map(c => c.template({context: this}));
return new Handlebars.SafeString(output.join(""));
}
});

View File

@@ -1,10 +1,8 @@
import { registerUnbound } from 'discourse-common/lib/helpers';
import { findRawTemplate } from 'discourse/lib/raw-templates';
let _injections;
function renderRaw(ctx, container, template, templateName, params) {
params = jQuery.extend({}, params);
params.parent = params.parent || ctx;
if (!params.view) {
@@ -34,9 +32,9 @@ registerUnbound('raw', function(templateName, params) {
templateName = templateName.replace('.', '/');
const container = Discourse.__container__;
const template = findRawTemplate(templateName);
var template = container.lookup('template:' + templateName + '.raw');
if (!template) {
console.warn('Could not find raw template: ' + templateName);
Ember.warn('Could not find raw template: ' + templateName);
return;
}
return renderRaw(this, container, template, templateName, params);

View File

@@ -2,7 +2,6 @@ import groups from 'discourse/lib/emoji/groups';
import KeyValueStore from "discourse/lib/key-value-store";
import { emojiList } from 'pretty-text/emoji';
import { emojiUrlFor } from 'discourse/lib/text';
import { findRawTemplate } from 'discourse/lib/raw-templates';
const keyValueStore = new KeyValueStore("discourse_emojis_");
const EMOJI_USAGE = "emojiUsage";
@@ -152,7 +151,7 @@ function render(page, offset, options) {
};
$('.emoji-modal', options.appendTo).remove();
const template = findRawTemplate('emoji-toolbar');
const template = options.register.lookup('template:emoji-toolbar.raw');
options.appendTo.append(template(model));
bindEvents(page, offset, options);

View File

@@ -5,16 +5,13 @@ const _loading = {};
function loadWithTag(path, cb) {
const head = document.getElementsByTagName('head')[0];
let finished = false;
let s = document.createElement('script');
s.src = path;
if (Ember.Test) {
Ember.Test.registerWaiter(() => finished);
}
if (Ember.Test) { Ember.Test.pendingAjaxRequests++; }
head.appendChild(s);
s.onload = s.onreadystatechange = function(_, abort) {
finished = true;
if (Ember.Test) { Ember.Test.pendingAjaxRequests--; }
if (abort || !s.readyState || s.readyState === "loaded" || s.readyState === "complete") {
s = s.onload = s.onreadystatechange = null;
if (!abort) {

View File

@@ -11,7 +11,6 @@ import { preventCloak } from 'discourse/widgets/post-stream';
import { h } from 'virtual-dom';
import { addFlagProperty } from 'discourse/components/site-header';
import { addPopupMenuOptionsCallback } from 'discourse/controllers/composer';
import { extraConnectorClass } from 'discourse/lib/plugin-connectors';
class PluginApi {
constructor(version, container) {
@@ -331,33 +330,12 @@ class PluginApi {
addStorePluralization(thing, plural) {
this.container.lookup("store:main").addPluralization(thing, plural);
}
/**
* Register a Connector class for a particular outlet and connector.
*
* For example, if the outlet is `user-profile-primary` and your connector
* template is called `my-connector.hbs`:
*
* ```javascript
* api.registerConnectorClass('user-profile-primary', 'my-connector', {
* shouldRender(args, component) {
* return component.siteSettings.my_plugin_enabled;
* }
* });
* ```
*
* For more information on connector classes, see:
* https://meta.discourse.org/t/important-changes-to-plugin-outlets-for-ember-2-10/54136
**/
registerConnectorClass(outletName, connectorName, klass) {
extraConnectorClass(`${outletName}/${connectorName}`, klass);
}
}
let _pluginv01;
function getPluginApi(version) {
version = parseFloat(version);
if (version <= 0.6) {
if (version <= 0.5) {
if (!_pluginv01) {
_pluginv01 = new PluginApi(version, Discourse.__container__);
}

View File

@@ -1,81 +0,0 @@
let _connectorCache;
let _extraConnectorClasses = {};
let _classPaths;
export function resetExtraClasses() {
_extraConnectorClasses = {};
_classPaths = undefined;
}
// Note: In plugins, define a class by path and it will be wired up automatically
// eg: discourse/connectors/<OUTLET NAME>/<CONNECTOR NAME>.js.es6
export function extraConnectorClass(name, obj) {
_extraConnectorClasses[name] = obj;
}
const DefaultConnectorClass = {
actions: {},
shouldRender: () => true,
setupComponent() { }
};
function findOutlets(collection, callback) {
const disabledPlugins = Discourse.Site.currentProp('disabled_plugins') || [];
Object.keys(collection).forEach(function(res) {
if (res.indexOf("/connectors/") !== -1) {
// Skip any disabled plugins
for (let i=0; i<disabledPlugins.length; i++) {
if (res.indexOf("/" + disabledPlugins[i] + "/") !== -1) {
return;
}
}
const segments = res.split("/");
let outletName = segments[segments.length-2];
const uniqueName = segments[segments.length-1];
callback(outletName, res, uniqueName);
}
});
}
export function clearCache() {
_connectorCache = null;
}
function findClass(outletName, uniqueName) {
if (!_classPaths) {
_classPaths = {};
findOutlets(require._eak_seen, (outlet, res, un) => {
_classPaths[`${outlet}/${un}`] = require(res).default;
});
}
const id = `${outletName}/${uniqueName}`;
let foundClass = _extraConnectorClasses[id] || _classPaths[id];
return foundClass ?
jQuery.extend({}, DefaultConnectorClass, foundClass) :
DefaultConnectorClass;
}
function buildConnectorCache() {
_connectorCache = {};
findOutlets(Ember.TEMPLATES, function(outletName, resource, uniqueName) {
_connectorCache[outletName] = _connectorCache[outletName] || [];
_connectorCache[outletName].push({
templateName: resource.replace('javascripts/', ''),
template: Ember.TEMPLATES[resource],
classNames: `${outletName}-outlet ${uniqueName}`,
connectorClass: findClass(outletName, uniqueName)
});
});
}
export function connectorsFor(outletName) {
if (!_connectorCache) { buildConnectorCache(); }
return _connectorCache[outletName] || [];
}

View File

@@ -1,10 +0,0 @@
import { getResolverOption } from 'discourse-common/resolver';
export function findRawTemplate(name) {
if (getResolverOption('mobileView')) {
return Discourse.RAW_TEMPLATES[`mobile/${name}`] ||
Discourse.RAW_TEMPLATES[name];
}
return Discourse.RAW_TEMPLATES[name];
}

View File

@@ -11,22 +11,26 @@ export default function(name, opts) {
const controllerName = opts.admin ? `modals/${name}` : name;
const viewClass = container.lookupFactory('view:' + name);
const controller = container.lookup('controller:' + controllerName);
const templateName = opts.templateName || Ember.String.dasherize(name);
if (viewClass) {
route.render(name, { into: 'modal', outlet: 'modalBody' });
} else {
const templateName = opts.templateName || Ember.String.dasherize(name);
const renderArgs = { into: 'modal', outlet: 'modalBody'};
if (controller) { renderArgs.controller = controllerName; }
const renderArgs = { into: 'modal', outlet: 'modalBody'};
if (controller) { renderArgs.controller = controllerName; }
if (opts.addModalBodyView) {
renderArgs.view = 'modal-body';
}
if (opts.addModalBodyView) {
renderArgs.view = 'modal-body';
}
const modalName = `modal/${templateName}`;
const fullName = opts.admin ? `admin/templates/${modalName}` : modalName;
route.render(fullName, renderArgs);
if (opts.title) {
modalController.set('title', I18n.t(opts.title));
const modalName = `modal/${templateName}`;
const fullName = opts.admin ? `admin/templates/${modalName}` : modalName;
route.render(fullName, renderArgs);
if (opts.title) {
modalController.set('title', I18n.t(opts.title));
}
}
if (controller) {

View File

@@ -1,14 +1,8 @@
import { defaultHomepage } from 'discourse/lib/utilities';
const rootURL = Discourse.BaseUri;
const BareRouter = Ember.Router.extend({
rootURL,
location: Ember.testing ? 'none': 'discourse-location',
handleURL(url) {
if (url === "/") { url = defaultHomepage(); }
return this._super(url);
}
location: Ember.testing ? 'none': 'discourse-location'
});
// Ember's router can't be extended. We need to allow plugins to add routes to routes that were defined
@@ -73,8 +67,7 @@ class RouteNode {
if (paths.length > 1) {
paths.filter(p => p !== this.opts.path).forEach(path => {
const newOpts = jQuery.extend({}, this.opts, { path });
console.log(`warning: we can't have duplicate route names anymore`, newOpts);
// router.route(this.name, newOpts, builder);
router.route(this.name, newOpts, builder);
});
}
}

View File

@@ -0,0 +1,6 @@
export default Ember.Mixin.create({
init() {
this._super();
(this.get('delegated') || []).forEach(m => this.set(m, m));
},
});

View File

@@ -0,0 +1,12 @@
export default Ember.Mixin.create({
delegateAll(actionNames) {
actionNames = actionNames || [];
this.actions = this.actions || {};
actionNames.forEach(m => {
this.actions[m] = function() { this.sendAction(m); };
this.set(m, m);
});
}
});

View File

@@ -86,9 +86,9 @@ NavItem.reopenClass({
testName = name.split("/")[0],
anonymous = !Discourse.User.current();
if (anonymous && !Discourse.Site.currentProp('anonymous_top_menu_items').includes(testName)) return null;
if (anonymous && !Discourse.Site.currentProp('anonymous_top_menu_items').contains(testName)) return null;
if (!Discourse.Category.list() && testName === "categories") return null;
if (!Discourse.Site.currentProp('top_menu_items').includes(testName)) return null;
if (!Discourse.Site.currentProp('top_menu_items').contains(testName)) return null;
var args = { name: name, hasIcon: name === "unread" }, extra = null, self = this;
if (opts.category) { args.category = opts.category; }

View File

@@ -168,7 +168,7 @@ export default RestModel.extend({
this.set('summary', false);
let jump = false;
if (userFilters.includes(username)) {
if (userFilters.contains(username)) {
userFilters.removeObject(username);
} else {
userFilters.addObject(username);
@@ -256,7 +256,7 @@ export default RestModel.extend({
return this.findPostsByIds(gap).then(posts => {
posts.forEach(p => {
const stored = this.storePost(p);
if (!currentPosts.includes(stored)) {
if (!currentPosts.contains(stored)) {
currentPosts.insertAt(postIdx++, stored);
}
});
@@ -410,7 +410,7 @@ export default RestModel.extend({
if (stored) {
const posts = this.get('posts');
if (!posts.includes(stored)) {
if (!posts.contains(stored)) {
if (!this.get('loadingBelow')) {
this.get('postsWithPlaceholders').appendPost(() => posts.pushObject(stored));
} else {

View File

@@ -17,7 +17,7 @@ function inject() {
}
function injectAll(app, name) {
inject(app, name, 'controller', 'component', 'route', 'model', 'adapter');
inject(app, name, 'controller', 'component', 'route', 'view', 'model', 'adapter');
}
export default {

View File

@@ -1,3 +1,5 @@
import { defaultHomepage } from 'discourse/lib/utilities';
export default function() {
// Error page
this.route('exception', { path: '/exception' });
@@ -43,6 +45,9 @@ export default function() {
this.route('categoryNone', { path: '/c/:slug/none' });
this.route('category', { path: '/c/:parentSlug/:slug' });
this.route('categoryWithID', { path: '/c/:parentSlug/:slug/:id' });
// homepage
this.route(defaultHomepage(), { path: '/' });
});
this.route('groups', { resetNamespace: true });

View File

@@ -0,0 +1 @@
{{!-- THIS IS AN EMPTY TEMPLATE THAT NEEDS TO BE OVERWRITTEN --}}

View File

@@ -1,17 +1,17 @@
{{plugin-outlet name="above-site-header"}}
{{plugin-outlet "above-site-header"}}
{{site-header canSignUp=canSignUp
showCreateAccount=(action "appRouteAction" "showCreateAccount")
showLogin=(action "appRouteAction" "showLogin")
showKeyboard=(action "appRouteAction" "showKeyboardShortcutsHelp")
toggleMobileView=(action "appRouteAction" "toggleMobileView")
toggleAnonymous=(action "appRouteAction" "toggleAnonymous")
logout=(action "appRouteAction" "logout")}}
{{plugin-outlet name="below-site-header"}}
showCreateAccount="showCreateAccount"
showLogin="showLogin"
showKeyboard="showKeyboardShortcutsHelp"
toggleMobileView="toggleMobileView"
toggleAnonymous="toggleAnonymous"
logout="logout"}}
{{plugin-outlet "below-site-header"}}
<div id="main-outlet" class="wrap">
<div class="container">
{{#if showTop}}
{{custom-html name="top"}}
{{custom-html "top"}}
{{/if}}
{{global-notice}}
{{create-topics-notice}}
@@ -20,11 +20,11 @@
{{outlet "user-card"}}
</div>
{{plugin-outlet name="above-footer"}}
{{plugin-outlet "above-footer"}}
{{#if showFooter}}
{{custom-html name="footer"}}
{{custom-html "footer"}}
{{/if}}
{{plugin-outlet name="below-footer"}}
{{plugin-outlet "below-footer"}}
{{outlet "modal"}}
{{topic-entrance}}

View File

@@ -8,6 +8,6 @@
{{tag-drop firstCategory=firstCategory secondCategory=secondCategory tagId=tagId}}
{{/if}}
{{plugin-outlet name="bread-crumbs-right" connectorTagName="li"}}
{{plugin-outlet "bread-crumbs-right" tagName="li"}}
<div class='clear'></div>

View File

@@ -56,7 +56,7 @@
</label>
</section>
{{plugin-outlet name="category-email-in" args=(hash category=category)}}
{{plugin-outlet "category-email-in"}}
{{/if}}
{{#if showPositionInput}}
@@ -82,4 +82,4 @@
</section>
{{/unless}}
{{plugin-outlet name="category-custom-settings" args=(hash category=category)}}
{{plugin-outlet "category-custom-settings"}}

View File

@@ -1,5 +1,5 @@
{{#each navItems as |navItem|}}
{{navigation-item content=navItem filterMode=filterMode}}
{{/each}}
{{custom-html name="extraNavItem"}}
{{plugin-outlet name="extra-nav-item" connectorTagName="li"}}
{{custom-html "extraNavItem"}}
{{plugin-outlet "extra-nav-item" tagName="li"}}

View File

@@ -1,3 +0,0 @@
{{#each connectors as |c|}}
{{plugin-connector connector=c args=args class=c.classNames tagName=connectorTagName}}
{{/each}}

View File

@@ -6,7 +6,7 @@
<a href={{item.postUrl}}>{{{item.title}}}</a>
</span>
<div class="category">{{category-link item.category}}</div>
{{plugin-outlet name="user-stream-item-header" args=(hash item=item)}}
{{plugin-outlet "user-stream-item-header"}}
</div>
{{#if actionDescription}}

View File

@@ -14,4 +14,4 @@
{{topic-featured-link topic}}
{{/if}}
</div>
{{plugin-outlet name="topic-category" args=(hash topic=topic category=topic.category)}}
{{plugin-outlet "topic-category"}}

View File

@@ -1,18 +1,5 @@
{{#if showAdminButton}}
{{topic-admin-menu-button
topic=topic
openUpwards="true"
toggleMultiSelect=toggleMultiSelect
deleteTopic=deleteTopic
recoverTopic=recoverTopic
toggleClosed=toggleClosed
toggleArchived=toggleArchived
toggleVisibility=toggleVisibility
showAutoClose=showAutoClose
showFeatureTopic=showFeatureTopic
showChangeTimestamp=showChangeTimestamp
convertToPublicTopic=convertToPublicTopic
convertToPrivateMessage=convertToPrivateMessage}}
{{topic-admin-menu-button topic=topic delegated=topicDelegated openUpwards="true"}}
{{/if}}
{{#unless topic.isPrivateMessage}}
@@ -23,7 +10,7 @@
title=bookmarkTitle
label=bookmarkLabel
icon="bookmark"
action=toggleBookmark}}
action="toggleBookmark"}}
<button class="btn share" data-share-url={{topic.shareUrl}} title={{i18n "topic.share.help"}}>
{{fa-icon "link"}}
@@ -35,7 +22,7 @@
title="topic.flag_topic.help"
label="topic.flag_topic.title"
icon="flag"
action=showFlagTopic}}
action="showFlagTopic"}}
{{/if}}
{{/if}}
@@ -46,7 +33,7 @@
title="topic.invite_reply.help"
label="topic.invite_reply.title"
icon="users"
action=showInvite
action="showInvite"
disabled=inviteDisabled}}
{{/if}}
@@ -55,7 +42,7 @@
title=archiveTitle
label=archiveLabel
icon=archiveIcon
action=toggleArchiveMessage}}
action="toggleArchiveMessage"}}
{{/if}}
{{plugin-outlet "topic-footer-main-buttons-before-create" tagName="span"}}
@@ -63,17 +50,13 @@
{{#if topic.details.can_create_post}}
{{d-button class="btn-primary create"
icon="reply"
action=replyToPost
action="replyToPost"
label="topic.reply.title"
title="topic.reply.help"}}
{{/if}}
{{plugin-outlet name="after-topic-footer-main-buttons"
args=(hash topic=topic)
connectorTagName="span"}}
{{plugin-outlet "after-topic-footer-main-buttons" tagName="span"}}
{{pinned-button topic=topic}}
{{topic-notifications-button topic=topic}}
{{plugin-outlet name="after-topic-footer-buttons"
args=(hash topic=topic)
connectorTagName="span"}}
{{pinned-button topic=topic topicDelegated=topicDelegated}}
{{topic-notifications-button topic=topic topicDelegated=topicDelegated}}
{{plugin-outlet "after-topic-footer-buttons" tagName="span"}}

View File

@@ -4,4 +4,5 @@
<span>/</span>
<h4>{{postStream.filteredPostsCount}}</h4></span>
</div>
<i class="fa {{unless expanded 'fa-sort'}}"></i>
</nav>

View File

@@ -30,7 +30,7 @@
<h2>{{user.title}}</h2>
{{/if}}
{{plugin-outlet name="user-card-post-names" args=(hash user=user)}}
{{plugin-outlet "user-card-post-names"}}
</span>
</div>
@@ -85,7 +85,7 @@
</span>
{{/if}}
{{plugin-outlet name="user-card-location-and-website" args=(hash user=user)}}
{{plugin-outlet "user-card-location-and-website"}}
</div>
{{/if}}
@@ -95,7 +95,7 @@
<h3><span class='desc'>{{i18n 'last_post'}}</span> {{format-date user.last_posted_at leaveAgo="true"}}</h3>
{{/if}}
<h3><span class='desc'>{{i18n 'joined'}}</span> {{format-date user.created_at leaveAgo="true"}}</h3>
{{plugin-outlet name="user-card-metadata" args=(hash user=user)}}
{{plugin-outlet "user-card-metadata"}}
</div>
{{/if}}

View File

@@ -30,7 +30,7 @@
{{#if model.viewOpen}}
<div class='control-row reply-area'>
<div class='composer-fields'>
{{plugin-outlet name="composer-open" args=(hash model=model)}}
{{plugin-outlet "composer-open"}}
<div class='reply-to'>
{{{model.actionTitle}}}
@@ -78,10 +78,11 @@
{{#if model.archetype.hasOptions}}
<button class='btn' {{action "showOptions"}}>{{i18n 'topic.options'}}</button>
{{/if}}
{{render "additional-composer-buttons" model}}
{{/if}}
</div>
{{/if}}
{{plugin-outlet name="composer-fields" args=(hash model=model)}}
{{plugin-outlet "composer-fields"}}
</div>
{{composer-editor topic=topic
@@ -103,7 +104,7 @@
{{#if currentUser}}
<div class='submit-panel'>
{{plugin-outlet name="composer-fields-below" args=(hash model=model)}}
{{plugin-outlet "composer-fields-below"}}
{{#if canEditTags}}
{{tag-chooser tags=model.tags tabIndex="4" categoryId=model.categoryId}}
{{/if}}

View File

@@ -21,11 +21,11 @@
<div class="row">
<div class="full-width">
<div id="list-area">
{{plugin-outlet name="discovery-list-container-top"}}
{{plugin-outlet "discovery-list-container-top"}}
{{outlet "list-container"}}
</div>
</div>
</div>
</div>
{{plugin-outlet name="discovery-below"}}
{{plugin-outlet "discovery-below"}}

View File

@@ -94,7 +94,7 @@
{{#each result.topic.tags as |tag|}}
{{discourse-tag tag}}
{{/each}}
{{plugin-outlet name="full-page-search-category" args=(hash result=result)}}
{{plugin-outlet "full-page-search-category"}}
</div>
</div>

View File

@@ -10,7 +10,7 @@
{{#if topic.featured_link}}
{{topic-featured-link topic}}
{{/if}}
{{raw-plugin-outlet name="topic-list-after-title"}}
{{plugin-outlet "topic-list-after-title"}}
{{#if showTopicPostBadges}}
{{raw "topic-post-badges" unread=topic.unread newPosts=topic.displayNewPosts unseen=topic.unseen url=topic.lastUnreadUrl}}
{{/if}}
@@ -21,7 +21,7 @@
{{/each}}
</div>
{{/if}}
{{raw-plugin-outlet name="topic-list-tags"}}
{{plugin-outlet "topic-list-tags"}}
{{#if expandPinned}}
{{raw "list/topic-excerpt" topic=topic}}
{{/if}}

View File

@@ -40,7 +40,7 @@
</div>
{{/if}}
{{raw-plugin-outlet name="topic-list-tags"}}
{{plugin-outlet "topic-list-tags"}}
<div class="pull-right">
<div class='num activity last'>

View File

@@ -52,11 +52,7 @@
</tr>
{{/if}}
{{plugin-outlet name="create-account-before-password"
args=(hash accountName=accountName
accountUsername=accountUsername
accountPassword=accountPassword
userFields=userFields)}}
{{plugin-outlet "create-account-before-password"}}
{{#if passwordRequired}}
<tr class="input">

View File

@@ -94,7 +94,7 @@
</div>
{{/if}}
{{plugin-outlet name="post-revisions" args=(hash model=model)}}
{{plugin-outlet "post-revisions"}}
{{#links-redirect class="row"}}
{{{bodyDiff}}}

View File

@@ -248,7 +248,7 @@
{{preference-checkbox labelKey="user.enable_quoting" checked=model.user_option.enable_quoting}}
{{preference-checkbox labelKey="user.dynamic_favicon" checked=model.user_option.dynamic_favicon}}
{{preference-checkbox labelKey="user.disable_jump_reply" checked=model.user_option.disable_jump_reply}}
{{plugin-outlet name="user-custom-preferences" args=(hash model=model)}}
{{plugin-outlet "user-custom-preferences"}}
</div>
<div class="control-group category">
@@ -350,7 +350,7 @@
</div>
{{/if}}
{{plugin-outlet name="user-custom-controls" args=(hash model=model)}}
{{plugin-outlet "user-custom-controls"}}
<div class="control-group save-button">
<div class="controls">

View File

@@ -1,7 +1,7 @@
{{#d-section bodyClass=bodyClass class="container"}}
{{#watch-read action="markFaqRead" path=model.path}}
<div class='contents clearfix body-page'>
{{plugin-outlet name="above-static"}}
{{plugin-outlet "above-static"}}
{{{model.html}}}
{{#if showSignupButton}}

View File

@@ -42,7 +42,7 @@
</div>
</div>
{{plugin-outlet name="discovery-list-container-top"}}
{{plugin-outlet "discovery-list-container-top"}}
<footer class='topic-list-bottom'>
{{conditional-loading-spinner condition=loading}}

View File

@@ -6,7 +6,7 @@
</div>
{{/if}}
{{plugin-outlet name="topic-above-post-stream" args=(hash model=model)}}
{{plugin-outlet "topic-above-post-stream"}}
{{#if model.postStream.loaded}}
{{#if model.postStream.firstPostPresent}}
@@ -30,7 +30,7 @@
{{tag-chooser tags=buffered.tags categoryId=buffered.category_id}}
{{/if}}
{{plugin-outlet name="edit-topic" args=(hash model=model buffered=buffered)}}
{{plugin-outlet "edit-topic"}}
{{d-button action="finishedEditingTopic" class="btn-primary btn-small submit-edit" icon="check"}}
{{d-button action="cancelEditingTopic" class="btn-small cancel-edit" icon="times"}}
@@ -59,7 +59,7 @@
{{/unless}}
{{/if}}
</div>
{{plugin-outlet name="topic-title" args=(hash model=model)}}
{{plugin-outlet "topic-title"}}
</div>
</div>
{{/if}}
@@ -70,53 +70,24 @@
</div>
{{#topic-navigation jumpToIndex="jumpToIndex" as |info|}}
{{#if info.renderAdminMenuButton}}
{{topic-admin-menu-button
topic=model
fixed="true"
toggleMultiSelect=(action "toggleMultiSelect")
deleteTopic=(action "deleteTopic")
recoverTopic=(action "recoverTopic")
toggleClosed=(action "toggleClosed")
toggleArchived=(action "toggleArchived")
toggleVisibility=(action "toggleVisibility")
showAutoClose=(action "topicRouteAction" "showAutoClose")
showFeatureTopic=(action "topicRouteAction" "showFeatureTopic")
showChangeTimestamp=(action "topicRouteAction" "showChangeTimestamp")
convertToPublicTopic=(action "convertToPublicTopic")
convertToPrivateMessage=(action "convertToPrivateMessage")}}
{{topic-admin-menu-button topic=model fixed="true" delegated=topicDelegated}}
{{/if}}
{{#if info.renderTimeline}}
{{topic-timeline
topic=model
prevEvent=info.prevEvent
fullscreen=info.topicProgressExpanded
enteredIndex=enteredIndex
loading=model.postStream.loading
jumpToPost=(action "jumpToPost")
jumpTop=(action "jumpTop")
jumpBottom=(action "jumpBottom")
jumpToPostPrompt=(action "jumpToPostPrompt")
jumpToIndex=(action "jumpToIndex")
replyToPost=(action "replyToPost")
toggleMultiSelect=(action "toggleMultiSelect")
deleteTopic=(action "deleteTopic")
recoverTopic=(action "recoverTopic")
toggleClosed=(action "toggleClosed")
toggleArchived=(action "toggleArchived")
toggleVisibility=(action "toggleVisibility")
showAutoClose=(action "topicRouteAction" "showAutoClose")
showFeatureTopic=(action "topicRouteAction" "showFeatureTopic")
showChangeTimestamp=(action "topicRouteAction" "showChangeTimestamp")
convertToPublicTopic=(action "convertToPublicTopic")
convertToPrivateMessage=(action "convertToPrivateMessage")}}
{{topic-timeline topic=model
prevEvent=info.prevEvent
fullscreen=info.topicProgressExpanded
enteredIndex=enteredIndex
loading=model.postStream.loading
delegated=topicDelegated}}
{{else}}
{{topic-progress
prevEvent=info.prevEvent
topic=model
expanded=info.topicProgressExpanded}}
{{topic-progress prevEvent=info.prevEvent topic=model delegated=topicDelegated expanded=info.topicProgressExpanded}}
{{/if}}
{{/topic-navigation}}
<div class="row">
@@ -125,7 +96,7 @@
<div class="posts-wrapper">
{{conditional-loading-spinner condition=model.postStream.loadingAbove}}
{{plugin-outlet name="topic-above-posts" args=(hash model=model)}}
{{plugin-outlet "topic-above-posts"}}
{{#unless model.postStream.loadingFilter}}
{{scrolling-post-stream
@@ -135,34 +106,35 @@
selectedPostsCount=selectedPostsCount
selectedQuery=selectedQuery
gaps=model.postStream.gaps
showFlags=(action "showPostFlags")
editPost=(action "editPost")
showHistory=(action "topicRouteAction" "showHistory")
showLogin=(action "topicRouteAction" "showLogin")
showRawEmail=(action "topicRouteAction" "showRawEmail")
deletePost=(action "deletePost")
recoverPost=(action "recoverPost")
expandHidden=(action "expandHidden")
newTopicAction=(action "replyAsNewTopic")
toggleBookmark=(action "toggleBookmark")
togglePostType=(action "togglePostType")
rebakePost=(action "rebakePost")
changePostOwner=(action "changePostOwner")
unhidePost=(action "unhidePost")
replyToPost=(action "replyToPost")
toggleWiki=(action "toggleWiki")
toggleSummary=(action "toggleSummary")
removeAllowedUser=(action "removeAllowedUser")
removeAllowedGroup=(action "removeAllowedGroup")
showInvite=(action "topicRouteAction" "showInvite")
topVisibleChanged=(action "topVisibleChanged")
currentPostChanged=(action "currentPostChanged")
currentPostScrolled=(action "currentPostScrolled")
bottomVisibleChanged=(action "bottomVisibleChanged")
selectPost=(action "toggledSelectedPost")
selectReplies=(action "toggledSelectedPostReplies")
fillGapBefore=(action "fillGapBefore")
fillGapAfter=(action "fillGapAfter")}}
showFlags="showFlags"
editPost="editPost"
showHistory="showHistory"
showLogin="showLogin"
showRawEmail="showRawEmail"
deletePost="deletePost"
recoverPost="recoverPost"
expandHidden="expandHidden"
newTopicAction="replyAsNewTopic"
expandFirstPost="expandFirstPost"
toggleBookmark="toggleBookmark"
togglePostType="togglePostType"
rebakePost="rebakePost"
changePostOwner="changePostOwner"
unhidePost="unhidePost"
replyToPost="replyToPost"
toggleWiki="toggleWiki"
toggleSummary="toggleSummary"
removeAllowedUser="removeAllowedUser"
removeAllowedGroup="removeAllowedGroup"
showInvite="showInvite"
topVisibleChanged="topVisibleChanged"
currentPostChanged="currentPostChanged"
currentPostScrolled="currentPostScrolled"
bottomVisibleChanged="bottomVisibleChanged"
selectPost="toggledSelectedPost"
selectReplies="toggledSelectedPostReplies"
fillGapBefore="fillGapBefore"
fillGapAfter="fillGapAfter"}}
{{/unless}}
{{conditional-loading-spinner condition=model.postStream.loadingBelow}}
@@ -178,25 +150,7 @@
{{signup-cta}}
{{else}}
{{#if currentUser}}
{{topic-footer-buttons
topic=model
toggleMultiSelect=(action "toggleMultiSelect")
deleteTopic=(action "deleteTopic")
recoverTopic=(action "recoverTopic")
toggleClosed=(action "toggleClosed")
toggleArchived=(action "toggleArchived")
toggleVisibility=(action "toggleVisibility")
showAutoClose=(action "topicRouteAction" "showAutoClose")
showFeatureTopic=(action "topicRouteAction" "showFeatureTopic")
showChangeTimestamp=(action "topicRouteAction" "showChangeTimestamp")
convertToPublicTopic=(action "convertToPublicTopic")
convertToPrivateMessage=(action "convertToPrivateMessage")
toggleBookmark=(action "toggleBookmark")
showFlagTopic=(action "topicRouteAction" "showFlagTopic")
showInvite=(action "topicRouteAction" "showInvite")
toggleArchiveMessage=(action "toggleArchiveMessage")
replyToPost=(action "replyToPost")
}}
{{topic-footer-buttons topic=model topicDelegated=topicDelegated}}
{{else}}
{{d-button icon="reply" class="btn-primary" action="showLogin" label="topic.reply.title"}}
{{/if}}
@@ -221,7 +175,7 @@
</div>
{{/if}}
{{plugin-outlet name="topic-above-suggested" args=(hash model=model)}}
{{plugin-outlet "topic-above-suggested"}}
{{#if model.details.suggested_topics.length}}
<div id="suggested-topics">

View File

@@ -47,9 +47,7 @@
{{#if currentUser.staff}}
<li><a href={{model.adminPath}} class="btn">{{fa-icon "wrench"}}{{i18n 'admin.user.show_admin_profile'}}</a></li>
{{/if}}
{{plugin-outlet name="user-profile-controls"
connectorTagName="li"
args=(hash model=model)}}
{{plugin-outlet "user-profile-controls" tagName="li"}}
{{#if collapsedInfo}}
{{#if viewingSelf}}
@@ -66,7 +64,7 @@
{{#if model.title}}
<h3>{{model.title}}</h3>
{{/if}}
{{plugin-outlet name="user-post-names" args=(hash model=model)}}
{{plugin-outlet "user-post-names"}}
<h3>
{{#if model.location}}{{fa-icon "map-marker"}} {{model.location}}{{/if}}
{{#if model.website_name}}
@@ -77,7 +75,7 @@
<span title={{model.website}}>{{model.website_name}}</span>
{{/if}}
{{/if}}
{{plugin-outlet name="user-location-and-website" args=(hash model=model)}}
{{plugin-outlet "user-location-and-website"}}
</h3>
<div class='bio'>
@@ -104,13 +102,12 @@
{{/if}}
{{/each}}
{{plugin-outlet name="user-profile-public-fields"
args=(hash publicUserFields=publicUserFields
model=model)}}
{{plugin-outlet "user-profile-public-fields"}}
</div>
{{/if}}
{{plugin-outlet name="user-profile-primary" args=(hash model=model)}}
{{plugin-outlet "user-profile-primary"}}
</div>
</div>
<div style='clear: both'></div>
@@ -156,7 +153,7 @@
{{d-button action="adminDelete" icon="exclamation-triangle" label="user.admin_delete" class="btn-danger"}}
{{/if}}
</dl>
{{plugin-outlet name="user-profile-secondary" args=(hash model=model)}}
{{plugin-outlet "user-profile-secondary"}}
</div>
{{/unless}}
</section>

View File

@@ -23,9 +23,7 @@
{{/link-to}}
</li>
{{/if}}
{{plugin-outlet name="user-activity-bottom"
connectorTagName='li'
args=(hash model=model)}}
{{plugin-outlet "user-activity-bottom" tagName='li'}}
{{/mobile-nav}}
{{#if viewingSelf}}

View File

@@ -37,9 +37,7 @@
<li>
{{user-stat value=model.likes_received label="user.summary.likes_received"}}
</li>
{{plugin-outlet name="user-summary-stat"
connectorTagName="li"
args=(hash model=model)}}
{{plugin-outlet "user-summary-stat" tagName="li"}}
</ul>
</div>

View File

@@ -0,0 +1,40 @@
import deprecated from 'discourse-common/lib/deprecated';
export default Ember.View.extend({
focusInput: true,
didInsertElement() {
this._super();
deprecated('ModalBodyView is deprecated. Use the `d-modal-body` component instead');
$('#modal-alert').hide();
$('#discourse-modal').modal('show');
Ember.run.scheduleOnce('afterRender', this, this._afterFirstRender);
this.appEvents.on('modal-body:flash', msg => this._flash(msg));
},
willDestroyElement() {
this._super();
this.appEvents.off('modal-body:flash');
},
_afterFirstRender() {
if (!this.site.mobileView && this.get('focusInput')) {
this.$('input:first').focus();
}
const title = this.get('title');
if (title) {
this.set('controller.modal.title', title);
}
},
_flash(msg) {
$('#modal-alert').hide()
.removeClass('alert-error', 'alert-success')
.addClass(`alert alert-${msg.messageClass || 'success'}`).html(msg.text || '')
.fadeIn();
}
});

View File

@@ -53,12 +53,9 @@ export default createWidget('header-topic-info', {
}
}
if (this.siteSettings.topic_featured_link_enabled) {
const featured = topicFeaturedLinkNode(attrs.topic);
if (featured) {
extra.push(featured);
}
extra.push(topicFeaturedLinkNode(attrs.topic));
}
if (extra.length) {
if (extra) {
title.push(h('div.topic-header-extra', extra));
}
}

View File

@@ -11,7 +11,7 @@ function renderParticipants(userFilters, participants) {
userFilters = userFilters || [];
return participants.map(p => {
return this.attach('topic-participant', p, { state: { toggled: userFilters.includes(p.username) } });
return this.attach('topic-participant', p, { state: { toggled: userFilters.contains(p.username) } });
});
}

View File

@@ -19,18 +19,16 @@ createWidget('timeline-last-read', {
return { style: `height: 40px; top: ${attrs.top}px` };
},
html(attrs) {
const result = [ iconNode('minus', { class: 'progress' }) ];
if (attrs.showButton) {
result.push(this.attach('button', {
html() {
return [
iconNode('circle', { class: 'progress' }),
this.attach('button', {
className: 'btn btn-primary btn-small',
label: 'topic.timeline.back',
title: 'topic.timeline.back_description',
action: 'goBack'
}));
}
return result;
})
];
},
goBack() {
@@ -161,12 +159,8 @@ createWidget('timeline-scrollarea', {
if (position.lastRead && position.lastRead !== position.total) {
const lastReadTop = Math.round(position.lastReadPercentage * SCROLLAREA_HEIGHT);
if (lastReadTop > (before + SCROLLER_HEIGHT * 0.5)) {
result.push(this.attach('timeline-last-read', {
top: lastReadTop,
lastRead: position.lastRead,
showButton: (lastReadTop > (before + SCROLLER_HEIGHT)) && (lastReadTop < (SCROLLAREA_HEIGHT - SCROLLER_HEIGHT))
}));
if ((lastReadTop > (before + SCROLLER_HEIGHT)) && (lastReadTop < (SCROLLAREA_HEIGHT - SCROLLER_HEIGHT))) {
result.push(this.attach('timeline-last-read', { top: lastReadTop, lastRead: position.lastRead }));
}
}

View File

@@ -256,24 +256,31 @@ export default class Widget {
}
_sendComponentAction(name, param) {
let promise;
const view = this._findAncestorWithProperty('_emberView');
const view = this._findView();
let promise;
if (view) {
const method = view.get(name);
if (!method) {
console.warn(`${name} not found`);
// Peek into ember internals to allow us to return promises from actions
const ev = view._emberView;
const target = ev.get('targetObject');
const actionName = ev.get(name);
if (!actionName) {
Ember.warn(`${name} not found`);
return;
}
if (typeof method === "string") {
view.sendAction(method, param);
promise = Ember.RSVP.resolve();
} else {
const target = view.get('targetObject');
promise = method.call(target, param);
if (!promise || !promise.then) {
promise = Ember.RSVP.resolve(promise);
if (target) {
// TODO: Use ember closure actions
const actions = target.actions || target.actionHooks || {};
const method = actions[actionName];
if (method) {
promise = method.call(target, param);
if (!promise || !promise.then) {
promise = Ember.RSVP.resolve(promise);
}
} else {
return ev.sendAction(name, param);
}
}
}

View File

@@ -1,3 +1,4 @@
window.ENV = { };
window.EmberENV = window.EmberENV || {};
window.EmberENV.FORCE_JQUERY = true;
window.EmberENV._ENABLE_LEGACY_VIEW_SUPPORT = true;