Version bump

This commit is contained in:
Neil Lalonde 2015-05-12 17:52:57 -04:00
commit cc4bfa7b45
404 changed files with 61427 additions and 103373 deletions

View File

@ -20,4 +20,5 @@ vendor/
test/javascripts/helpers/
test/javascripts/test_helper.js
test/javascripts/test_helper.js
app/assets/javascripts/ember-addons/

View File

@ -48,6 +48,7 @@
"parseHTML",
"deepEqual",
"notEqual",
"define",
"require",
"requirejs",
"hasModule",

View File

@ -42,7 +42,7 @@ gem 'active_model_serializers', '~> 0.8.3'
gem 'onebox'
gem 'ember-rails'
gem 'ember-source', '1.9.0.beta.4'
gem 'ember-source', '1.11.3.1'
gem 'handlebars-source', '2.0.0'
gem 'barber'
gem 'babel-transpiler'

View File

@ -45,10 +45,9 @@ GEM
babel-transpiler (0.6.0)
babel-source (>= 4.0, < 5)
execjs (~> 2.0)
barber (0.5.0)
ember-source
execjs
handlebars-source (>= 1.0.0.rc.4)
barber (0.9.0)
ember-source (>= 1.0, < 2)
execjs (>= 1.2, < 3)
better_errors (2.1.1)
coderay (>= 1.0.0)
erubis (>= 2.6.6)
@ -68,23 +67,23 @@ GEM
docile (1.1.5)
dotenv (1.0.2)
email_reply_parser (0.5.8)
ember-data-source (0.14)
ember-source
ember-rails (0.14.1)
ember-data-source (1.0.0.beta.16.1)
ember-source (~> 1.8)
ember-handlebars-template (0.1.5)
barber (>= 0.9.0)
sprockets (>= 2.1, < 3.1)
ember-rails (0.18.2)
active_model_serializers
barber (>= 0.4.1)
ember-data-source
ember-source
execjs (>= 1.2)
handlebars-source
ember-data-source (>= 1.0.0.beta.5)
ember-handlebars-template (>= 0.1.1, < 1.0)
ember-source (>= 1.1.0)
jquery-rails (>= 1.0.17)
railties (>= 3.1)
ember-source (1.9.0.beta.4)
handlebars-source (~> 2.0)
ember-source (1.11.3.1)
erubis (2.7.0)
eventmachine (1.0.7)
excon (0.44.4)
execjs (2.4.0)
execjs (2.5.2)
exifr (1.1.3)
fabrication (2.9.8)
fakeweb (1.3.0)
@ -220,7 +219,7 @@ GEM
method_source (0.8.2)
mime-types (1.25.1)
mini_portile (0.6.2)
minitest (5.6.0)
minitest (5.6.1)
mocha (1.1.0)
metaclass (~> 0.0.1)
mock_redis (0.14.0)
@ -271,7 +270,7 @@ GEM
omniauth-twitter (1.0.1)
multi_json (~> 1.3)
omniauth-oauth (~> 1.0)
onebox (1.5.16)
onebox (1.5.18)
moneta (~> 0.7)
multi_json (~> 1.7)
mustache (~> 0.99)
@ -295,7 +294,7 @@ GEM
qunit-rails (0.0.7)
railties
r2 (0.2.5)
rack (1.5.2)
rack (1.5.3)
rack-mini-profiler (0.9.3)
rack (>= 1.1.3)
rack-openid (1.3.1)
@ -467,7 +466,7 @@ DEPENDENCIES
certified
email_reply_parser
ember-rails
ember-source (= 1.9.0.beta.4)
ember-source (= 1.11.3.1)
eventmachine
fabrication (= 2.9.8)
fakeweb (~> 1.3.0)

0
Rakefile Normal file → Executable file
View File

2
Vagrantfile vendored
View File

@ -17,7 +17,7 @@ Vagrant.configure("2") do |config|
config.vm.provider :virtualbox do |v|
# This setting gives the VM 1024MB of RAM instead of the default 384.
v.customize ["modifyvm", :id, "--memory", [ENV['DISCOURSE_VM_MEM'].to_i, 2048].max]
v.customize ["modifyvm", :id, "--memory", [ENV['DISCOURSE_VM_MEM'].to_i, 1024].max]
# Who has a single core cpu these days anyways?
cpu_count = 2

View File

@ -3,8 +3,8 @@ export default Ember.ObjectController.extend({
savedIpAddress: null,
isRange: function() {
return this.get("ip_address").indexOf("/") > 0;
}.property("ip_address"),
return this.get("model.ip_address").indexOf("/") > 0;
}.property("model.ip_address"),
actions: {
allow: function(record) {
@ -19,14 +19,14 @@ export default Ember.ObjectController.extend({
edit: function() {
if (!this.get('editing')) {
this.savedIpAddress = this.get('ip_address');
this.savedIpAddress = this.get('model.ip_address');
}
this.set('editing', true);
},
cancel: function() {
if (this.get('savedIpAddress') && this.get('editing')) {
this.set('ip_address', this.get('savedIpAddress'));
this.set('model.ip_address', this.get('savedIpAddress'));
}
this.set('editing', false);
},

View File

@ -1,7 +1,6 @@
import Presence from 'discourse/mixins/presence';
import { outputExportResult } from 'discourse/lib/export-result';
export default Ember.ArrayController.extend(Presence, {
export default Ember.ArrayController.extend({
loading: false,
actions: {

View File

@ -1,7 +1,6 @@
import Presence from 'discourse/mixins/presence';
import { outputExportResult } from 'discourse/lib/export-result';
export default Ember.ArrayController.extend(Presence, {
export default Ember.ArrayController.extend({
loading: false,
itemController: 'admin-log-screened-ip-address',
filter: null,

View File

@ -1,7 +1,6 @@
import Presence from 'discourse/mixins/presence';
import { outputExportResult } from 'discourse/lib/export-result';
export default Ember.ArrayController.extend(Presence, {
export default Ember.ArrayController.extend({
loading: false,
show() {

View File

@ -1,7 +1,6 @@
import Presence from 'discourse/mixins/presence';
import { outputExportResult } from 'discourse/lib/export-result';
export default Ember.ArrayController.extend(Presence, {
export default Ember.ArrayController.extend({
loading: false,
filters: null,

View File

@ -1,11 +1,6 @@
/**
Represents an IP address that is watched for during account registration
(and possibly other times), and an action is taken.
@class ScreenedIpAddress
@extends Discourse.Model
@namespace Discourse
@module Discourse
**/
Discourse.ScreenedIpAddress = Discourse.Model.extend({
actionName: function() {
@ -17,21 +12,9 @@ Discourse.ScreenedIpAddress = Discourse.Model.extend({
}.property('action_name'),
actionIcon: function() {
if (this.get('action_name') === 'block') {
return this.get('blockIcon');
} else {
return this.get('doNothingIcon');
}
return (this.get('action_name') === 'block') ? 'ban' : 'check';
}.property('action_name'),
blockIcon: function() {
return 'fa-ban';
}.property(),
doNothingIcon: function() {
return 'fa-check';
}.property(),
save: function() {
return Discourse.ajax("/admin/logs/screened_ip_addresses" + (this.id ? '/' + this.id : '') + ".json", {
type: this.id ? 'PUT' : 'POST',

View File

@ -18,10 +18,10 @@
</div>
</div>
{{#loading-spinner condition=loading}}
{{#conditional-loading-spinner condition=loading}}
{{#if showHtml}}
{{{html_content}}}
{{else}}
<pre>{{{text_content}}}</pre>
{{/if}}
{{/loading-spinner}}
{{/conditional-loading-spinner}}

View File

@ -147,7 +147,7 @@
</tbody>
</table>
{{loading-spinner condition=view.loading}}
{{conditional-loading-spinner condition=view.loading}}
{{else}}
<p>{{i18n 'admin.flags.no_results'}}</p>
{{/if}}

View File

@ -4,7 +4,7 @@
</p>
<br>
{{#loading-spinner condition=loading}}
{{#conditional-loading-spinner condition=loading}}
{{#if model.length}}
<div class='table screened-emails'>
@ -25,4 +25,4 @@
{{else}}
{{i18n 'search.no_results'}}
{{/if}}
{{/loading-spinner}}
{{/conditional-loading-spinner}}

View File

@ -7,7 +7,7 @@
{{screened-ip-address-form action="recordAdded"}}
<br/>
{{#loading-spinner condition=loading}}
{{#conditional-loading-spinner condition=loading}}
{{#if model.length}}
<div class='table admin-logs-table screened-ip-addresses'>
@ -27,4 +27,4 @@
{{else}}
{{i18n 'search.no_results'}}
{{/if}}
{{/loading-spinner}}
{{/conditional-loading-spinner}}

View File

@ -1,35 +1,35 @@
<div class="col first ip_address">
{{#if editing}}
{{text-field value=ip_address autofocus="autofocus"}}
{{text-field value=model.ip_address autofocus="autofocus"}}
{{else}}
<span {{action "edit" this}}>
{{#if isRange}}
<strong>{{ip_address}}</strong>
<strong>{{model.ip_address}}</strong>
{{else}}
{{ip_address}}
{{model.ip_address}}
{{/if}}
</span>
{{/if}}
</div>
<div class="col action">
<i {{bind-attr class=":fa actionIcon"}}></i>
{{actionName}}
{{fa-icon model.actionIcon}}
{{model.actionName}}
</div>
<div class="col match_count">{{match_count}}</div>
<div class="col match_count">{{model.match_count}}</div>
<div class="col last_match_at">
{{#if last_match_at}}
{{age-with-tooltip last_match_at}}
{{#if model.last_match_at}}
{{age-with-tooltip model.last_match_at}}
{{/if}}
</div>
<div class="col created_at">{{age-with-tooltip created_at}}</div>
<div class="col created_at">{{age-with-tooltip model.created_at}}</div>
<div class="col actions">
{{#unless editing}}
<button class="btn btn-danger" {{action "destroy" this}}><i class="fa fa-trash-o"></i></button>
<button class="btn" {{action "edit" this}}><i class="fa fa-pencil"></i></button>
{{#if isBlocked}}
<button class="btn" {{action "allow" this}}><i {{bind-attr class=":fa doNothingIcon"}}></i> {{i18n 'admin.logs.screened_ips.actions.do_nothing'}}</button>
{{#if model.isBlocked}}
<button class="btn" {{action "allow" this}}>{{fa-icon "check"}} {{i18n 'admin.logs.screened_ips.actions.do_nothing'}}</button>
{{else}}
<button class="btn" {{action "block" this}}><i {{bind-attr class=":fa blockIcon"}}></i> {{i18n 'admin.logs.screened_ips.actions.block'}}</button>
<button class="btn" {{action "block" this}}>{{fa-icon "ban"}} {{i18n 'admin.logs.screened_ips.actions.block'}}</button>
{{/if}}
{{else}}
<button class="btn" {{action "save" this}}>{{i18n 'admin.logs.save'}}</button>

View File

@ -4,7 +4,7 @@
</p>
<br>
{{#loading-spinner condition=loading}}
{{#conditional-loading-spinner condition=loading}}
{{#if model.length}}
<div class='table screened-urls'>
<div class="heading-container">
@ -21,4 +21,4 @@
{{else}}
{{i18n 'search.no_results'}}
{{/if}}
{{/loading-spinner}}
{{/conditional-loading-spinner}}

View File

@ -33,7 +33,7 @@
</div>
<br>
<div class="staff-action-logs-instructions" {{bind-attr class=":staff-action-logs-instructions showInstructions::invisible"}}>
<div class="staff-action-logs-instructions {{unless showInstructions 'invisible'}}">
{{i18n 'admin.logs.staff_actions.instructions'}}
</div>
@ -48,11 +48,11 @@
<div class="clearfix"></div>
</div>
{{#loading-spinner condition=loading}}
{{#conditional-loading-spinner condition=loading}}
{{#if model.length}}
{{view "staff-action-logs-list" content=controller}}
{{else}}
{{i18n 'search.no_results'}}
{{/if}}
{{/loading-spinner}}
{{/conditional-loading-spinner}}
</div>

View File

@ -20,7 +20,7 @@
{{/if}}
</div>
{{#loading-spinner condition=refreshing}}
{{#conditional-loading-spinner condition=refreshing}}
<table class='table report'>
<tr>
<th>{{xaxis}}</th>
@ -43,4 +43,4 @@
</tr>
{{/each}}
</table>
{{/loading-spinner}}
{{/conditional-loading-spinner}}

View File

@ -6,7 +6,7 @@
</div>
</div>
{{#loading-spinner condition=loading}}
{{#conditional-loading-spinner condition=loading}}
<div class='admin-container user-badges'>
<h2>{{i18n 'admin.badges.grant_badge'}}</h2>
<br>
@ -67,4 +67,4 @@
{{/each}}
</table>
</div>
{{/loading-spinner}}
{{/conditional-loading-spinner}}

View File

@ -316,7 +316,7 @@
</div>
{{/if}}
<div class='display-row' {{bind-attr class=":display-row blocked:highlight-danger"}}>
<div {{bind-attr class=":display-row blocked:highlight-danger"}}>
<div class='field'>{{i18n 'admin.user.blocked'}}</div>
<div class='value'>{{blocked}}</div>
<div class='controls'>

View File

@ -1,7 +1,7 @@
{{#if hasSelection}}
<div id='selected-controls'>
<button {{action "approveUsers"}} class='btn'>{{countI18n admin.users.approved_selected count=selectedCount}}</button>
<button {{action "rejectUsers"}} class='btn btn-danger'>{{countI18n admin.users.reject_selected count=selectedCount}}</button>
<button {{action "approveUsers"}} class='btn'>{{count-i18n key="admin.users.approved_selected" count=selectedCount}}</button>
<button {{action "rejectUsers"}} class='btn btn-danger'>{{count-i18n key="admin.users.reject_selected" count=selectedCount}}</button>
</div>
{{/if}}
@ -19,7 +19,7 @@
{{/unless}}
</div>
{{#loading-spinner condition=refreshing}}
{{#conditional-loading-spinner condition=refreshing}}
{{#if model}}
<table class='table'>
<tr>
@ -81,4 +81,4 @@
{{else}}
<p>{{i18n 'search.no_results'}}</p>
{{/if}}
{{/loading-spinner}}
{{/conditional-loading-spinner}}

View File

@ -48,7 +48,7 @@
{{else}}
<span {{bind-attr class=":icon versionCheck.critical_updates:critical-updates-available:updates-available"}}>
{{#if versionCheck.behindByOneVersion}}
{{fa-icon "smile-o"}}
{{fa-icon "meh-o"}}
{{else}}
{{fa-icon "frown-o"}}
{{/if}}

View File

@ -1,5 +0,0 @@
Discourse.ScreenedEmailsListView = Ember.ListView.extend({
height: 700,
rowHeight: 32,
itemViewClass: Ember.ListItemView.extend({templateName: "admin/templates/logs/screened_emails_list_item"})
});

View File

@ -1,5 +0,0 @@
Discourse.ScreenedIpAddressesListView = Ember.ListView.extend({
height: 700,
rowHeight: 32,
itemViewClass: Ember.ListItemView.extend({templateName: "admin/templates/logs/screened_ip_addresses_list_item"})
});

View File

@ -1,5 +0,0 @@
Discourse.ScreenedUrlsListView = Ember.ListView.extend({
height: 700,
rowHeight: 32,
itemViewClass: Ember.ListItemView.extend({templateName: "admin/templates/logs/screened_urls_list_item"})
});

View File

@ -1,5 +0,0 @@
Discourse.StaffActionLogsListView = Ember.ListView.extend({
height: 700,
rowHeight: 75,
itemViewClass: Ember.ListItemView.extend({templateName: "admin/templates/logs/staff_action_logs_list_item"})
});

View File

@ -0,0 +1,8 @@
import ListView from 'ember-addons/list-view';
import ListItemView from 'ember-addons/list-item-view';
export default ListView.extend({
height: 700,
rowHeight: 32,
itemViewClass: ListItemView.extend({templateName: "admin/templates/logs/screened_emails_list_item"})
});

View File

@ -0,0 +1,8 @@
import ListView from 'ember-addons/list-view';
import ListItemView from 'ember-addons/list-item-view';
export default ListView.extend({
height: 700,
rowHeight: 32,
itemViewClass: ListItemView.extend({templateName: "admin/templates/logs/screened_ip_addresses_list_item"})
});

View File

@ -0,0 +1,8 @@
import ListView from 'ember-addons/list-view';
import ListItemView from 'ember-addons/list-item-view';
export default ListView.extend({
height: 700,
rowHeight: 32,
itemViewClass: ListItemView.extend({templateName: "admin/templates/logs/screened_urls_list_item"})
});

View File

@ -0,0 +1,8 @@
import ListView from 'ember-addons/list-view';
import ListItemView from 'ember-addons/list-item-view';
export default ListView.extend({
height: 700,
rowHeight: 75,
itemViewClass: ListItemView.extend({templateName: "admin/templates/logs/staff_action_logs_list_item"})
});

View File

@ -1,6 +1,11 @@
/*global Favcount:true*/
var DiscourseResolver = require('discourse/ember/resolver').default;
// Allow us to import Ember
define('ember', ['exports'], function(__exports__) {
__exports__["default"] = Ember;
});
window.Discourse = Ember.Application.createWithMixins(Discourse.Ajax, {
rootElement: '#main',
_docTitle: document.title,

View File

@ -15,21 +15,21 @@ export default Ember.Component.extend(StringBuffer, {
if (this.get('isIndexStream')) {
return !this.get('userActionType');
}
var content = this.get('content');
const content = this.get('content');
if (content) {
return parseInt(this.get('userActionType'), 10) === parseInt(Em.get(content, 'action_type'), 10);
}
}.property('userActionType', 'indexStream'),
}.property('userActionType', 'isIndexStream'),
activityCount: function() {
return this.get('content.count') || this.get('count') || 0;
}.property('content.count', 'count'),
typeKey: function() {
var actionType = this.get('content.action_type');
const actionType = this.get('content.action_type');
if (actionType === Discourse.UserAction.TYPES.messages_received) { return ""; }
var result = Discourse.UserAction.TYPES_INVERTED[actionType];
const result = Discourse.UserAction.TYPES_INVERTED[actionType];
if (!result) { return ""; }
// We like our URLS to have hyphens, not underscores
@ -44,9 +44,9 @@ export default Ember.Component.extend(StringBuffer, {
return this.get('content.description') || I18n.t("user.filters.all");
}.property('content.description'),
renderString: function(buffer) {
renderString(buffer) {
buffer.push("<a href='" + this.get('url') + "'>");
var icon = this.get('icon');
const icon = this.get('icon');
if (icon) {
buffer.push("<i class='glyph fa fa-" + icon + "'></i> ");
}

View File

@ -20,7 +20,7 @@ export default Ember.Component.extend({
}.observes("autoCloseTime", "limited"),
_isAutoCloseValid: function(autoCloseTime, limited) {
var t = (autoCloseTime || "").trim();
var t = (autoCloseTime || "").toString().trim();
if (t.length === 0) {
// "empty" is always valid
return true;

View File

@ -1,4 +1,4 @@
import ComboboxView from 'discourse/views/combo-box';
import ComboboxView from 'discourse/components/combo-box';
import { categoryBadgeHTML } from 'discourse/helpers/category-link';
export default ComboboxView.extend({
@ -41,7 +41,7 @@ export default ComboboxView.extend({
}
}.property(),
template(item) {
comboTemplate(item) {
let category;

View File

@ -2,10 +2,7 @@ var get = Ember.get;
export default Ember.Component.extend({
classNameBindings: ['category::no-category', 'categories:has-drop','categoryStyle'],
categoryStyle: function(){
return Discourse.SiteSettings.category_style;
}.property(),
categoryStyle: Discourse.computed.setting('category_style'),
tagName: 'li',
@ -50,11 +47,11 @@ export default Ember.Component.extend({
if (color) {
var style = "";
if (color) { style += "background-color: #" + color + ";" }
return style;
return style.htmlSafe();
}
}
return "background-color: #eee;";
return "background-color: #eee;".htmlSafe();
}.property('category'),
badgeStyle: function() {
@ -68,11 +65,11 @@ export default Ember.Component.extend({
var style = "";
if (color) { style += "background-color: #" + color + "; border-color: #" + color + ";"; }
if (textColor) { style += "color: #" + textColor + "; "; }
return style;
return style.htmlSafe();
}
}
return "background-color: #eee; color: #333";
return "background-color: #eee; color: #333".htmlSafe();
}.property('category'),
clickEventName: function() {

View File

@ -1,5 +1,4 @@
// This view handles rendering of a combobox
export default Discourse.View.extend({
export default Ember.Component.extend({
tagName: 'select',
attributeBindings: ['tabindex'],
classNames: ['combobox'],
@ -65,7 +64,7 @@ export default Discourse.View.extend({
o.selected = !!$(o).attr('selected');
});
$elem.select2({formatResult: this.template, minimumResultsForSearch: 5, width: 'resolve'});
$elem.select2({formatResult: this.comboTemplate, minimumResultsForSearch: 5, width: 'resolve'});
const castInteger = this.get('castInteger');
$elem.on("change", function (e) {

View File

@ -9,7 +9,7 @@ export default Ember.Component.extend({
if (this.get('condition')) {
buffer.push('<div class="spinner ' + this.get('size') + '"}}></div>');
} else {
return this._super();
return this._super(buffer);
}
},

View File

@ -0,0 +1,8 @@
export default Ember.Component.extend(Discourse.StringBuffer, {
tagName: 'span',
rerenderTriggers: ['count', 'suffix'],
renderString: function(buffer) {
buffer.push(I18n.t(this.get('key') + (this.get('suffix') || ''), { count: this.get('count') }));
}
});

View File

@ -26,7 +26,7 @@ export default Ember.Component.extend({
if (label) { buffer.push(label); }
} else {
// If no label or icon is present, yield
return this._super();
return this._super(buffer);
}
},

View File

@ -0,0 +1,17 @@
import StringBuffer from 'discourse/mixins/string-buffer';
import { iconHTML } from 'discourse/helpers/fa-icon';
export default Ember.Component.extend(StringBuffer, {
classNameBindings: [':tip', 'good', 'bad'],
rerenderTriggers: ['validation'],
bad: Em.computed.alias('validation.failed'),
good: Em.computed.not('bad'),
renderString(buffer) {
const reason = this.get('validation.reason');
if (reason) {
buffer.push(iconHTML(this.get('good') ? 'check' : 'times') + ' ' + reason);
}
}
});

View File

@ -1,22 +1,55 @@
const INVITED_TYPE = 8;
export default Ember.Component.extend({
tagName: 'li',
classNameBindings: ['notification.read', 'notification.is_warning'],
scope: function() {
return "notifications." + this.site.get("notificationLookup")[this.get("notification.notification_type")];
}.property("notification.notification_type"),
url: function() {
const it = this.get('notification');
const badgeId = it.get("data.badge_id");
if (badgeId) {
const badgeName = it.get("data.badge_name");
return Discourse.getURL('/badges/' + badgeId + '/' + badgeName.replace(/[^A-Za-z0-9_]+/g, '-').toLowerCase());
}
const topicId = it.get('topic_id');
if (topicId) {
return Discourse.Utilities.postUrl(it.get("slug"), topicId, it.get("post_number"));
}
if (it.get('notification_type') === INVITED_TYPE) {
return Discourse.getURL('/my/invited');
}
}.property("notification.data.{badge_id,badge_name}", "model.slug", "model.topic_id", "model.post_number"),
description: function() {
const badgeName = this.get("notification.data.badge_name");
if (badgeName) { return Handlebars.Utils.escapeExpression(badgeName); }
const title = this.get('notification.data.topic_title');
return Ember.isEmpty(title) ? "" : Handlebars.Utils.escapeExpression(title);
}.property("notification.data.{badge_name,topic_title}"),
_markRead: function(){
var self = this;
this.$('a').click(function(){
self.set('notification.read', true);
this.$('a').click(() => {
this.set('notification.read', true);
return true;
});
}.on('didInsertElement'),
render: function(buffer) {
var notification = this.get('notification'),
text = I18n.t(this.get('scope'), Em.getProperties(notification, 'description', 'username'));
render(buffer) {
const notification = this.get('notification');
const description = this.get('description');
const username = notification.get('data.display_username');
const text = I18n.t(this.get('scope'), {description, username});
var url = notification.get('url');
const url = this.get('url');
if (url) {
buffer.push('<a href="' + notification.get('url') + '">' + text + '</a>');
buffer.push('<a href="' + url + '">' + text + '</a>');
} else {
buffer.push(text);
}

View File

@ -1,23 +1,14 @@
/**
This view extends the functionality of InputTipView with these extra features:
* it can be dismissed
* it bounces when it's shown
* it's absolutely positioned beside the input element, with the help of
extra css you'll need to write to line it up correctly.
import StringBuffer from 'discourse/mixins/string-buffer';
import { iconHTML } from 'discourse/helpers/fa-icon';
@class PopupInputTipView
@extends Discourse.View
@namespace Discourse
@module Discourse
**/
Discourse.PopupInputTipView = Discourse.View.extend({
templateName: 'popup_input_tip',
export default Ember.Component.extend(StringBuffer, {
classNameBindings: [':popup-tip', 'good', 'bad', 'shownAt::hide'],
animateAttribute: null,
bouncePixels: 6,
bounceDelay: 100,
rerenderTriggers: ['validation.reason'],
click: function() {
click() {
this.set('shownAt', false);
},
@ -43,17 +34,23 @@ Discourse.PopupInputTipView = Discourse.View.extend({
}
}.observes('shownAt'),
bounceLeft: function($elem) {
renderString(buffer) {
const reason = this.get('validation.reason');
if (!reason) { return; }
buffer.push("<span class='close'>" + iconHTML('times-circle') + "</span>");
buffer.push(reason);
},
bounceLeft($elem) {
for( var i = 0; i < 5; i++ ) {
$elem.animate({ left: '+=' + this.bouncePixels }, this.bounceDelay).animate({ left: '-=' + this.bouncePixels }, this.bounceDelay);
}
},
bounceRight: function($elem) {
bounceRight($elem) {
for( var i = 0; i < 5; i++ ) {
$elem.animate({ right: '-=' + this.bouncePixels }, this.bounceDelay).animate({ right: '+=' + this.bouncePixels }, this.bounceDelay);
}
}
});
Discourse.View.registerHelper('popupInputTip', Discourse.PopupInputTipView);

View File

@ -1,11 +1,3 @@
/**
The controls for toggling the supression of deleted posts
@class ToggleDeletedComponent
@extends Ember.Component
@namespace Discourse
@module Discourse
**/
export default Ember.Component.extend({
layoutName: 'components/toggle-deleted',
tagName: 'section',

View File

@ -0,0 +1,3 @@
export default Ember.Component.extend({
classNames: ['user-stat']
});

View File

@ -4,9 +4,8 @@ export default Ember.Component.extend({
}.observes("visible"),
render: function(buffer){
if(!this.get("visible")){
return;
}
if (this._state !== 'inDOM' && this._state !== 'preRender' && this._state !== 'inBuffer') { return; }
if (!this.get("visible")) { return; }
return this._super(buffer);
}

View File

@ -1,6 +1,4 @@
import ObjectController from 'discourse/controllers/object';
export default ObjectController.extend({
export default Ember.Controller.extend({
faqOverriden: Ember.computed.gt('siteSettings.faq_url.length', 0),
contactInfo: function() {

View File

@ -1,25 +1,19 @@
import ObjectController from 'discourse/controllers/object';
/**
Controller for showing a particular badge.
@class BadgesShowController
@extends ObjectController
@namespace Discourse
@module Discourse
**/
export default ObjectController.extend({
noMoreBadges: false,
userBadges: null,
needs: ["application"],
actions: {
loadMore: function() {
var self = this;
var userBadges = this.get('userBadges');
loadMore() {
const self = this;
const userBadges = this.get('userBadges');
Discourse.UserBadge.findByBadgeId(this.get('model.id'), {
offset: userBadges.length
}).then(function(userBadges) {
self.get('userBadges').pushObjects(userBadges);
}).then(function(result) {
userBadges.pushObjects(result);
if(userBadges.length === 0){
self.set('noMoreBadges', true);
}

View File

@ -1,8 +1,10 @@
import Presence from 'discourse/mixins/presence';
import SelectedPostsCount from 'discourse/mixins/selected-posts-count';
import ModalFunctionality from 'discourse/mixins/modal-functionality';
import ObjectController from 'discourse/controllers/object';
// Modal related to changing the ownership of posts
export default ObjectController.extend(Discourse.SelectedPostsCount, ModalFunctionality, {
export default ObjectController.extend(Presence, SelectedPostsCount, ModalFunctionality, {
needs: ['topic'],
topicController: Em.computed.alias('controllers.topic'),

View File

@ -1,6 +1,6 @@
import DiscourseController from 'discourse/controllers/controller';
import Presence from 'discourse/mixins/presence';
export default DiscourseController.extend({
export default Ember.ObjectController.extend(Presence, {
needs: ['modal', 'topic', 'composer-messages', 'application'],
replyAsNewTopicDraft: Em.computed.equal('model.draftKey', Discourse.Composer.REPLY_AS_NEW_TOPIC_KEY),
@ -10,6 +10,14 @@ export default DiscourseController.extend({
editReason: null,
maxTitleLength: Discourse.computed.setting('max_topic_title_length'),
scopedCategoryId: null,
similarTopics: null,
similarTopicsMessage: null,
lastSimilaritySearch: null,
topic: null,
// TODO: Remove this, very bad
view: null,
_initializeSimilar: function() {
this.set('similarTopics', []);
@ -183,7 +191,7 @@ export default DiscourseController.extend({
// for now handle a very narrow use case
// if we are replying to a topic AND not on the topic pop the window up
if (!force && composer.get('replyingToTopic')) {
const topic = this.get('topic');
const topic = this.get('model.topic');
if (!topic || topic.get('id') !== composer.get('topic.id'))
{
const message = I18n.t("composer.posting_not_on_topic");
@ -226,7 +234,6 @@ export default DiscourseController.extend({
imageSizes: this.get('view').imageSizes(),
editReason: this.get("editReason")
}).then(function(result) {
if (result.responseJson.action === "enqueued") {
self.send('postWasEnqueued', result.responseJson);
self.destroyDraft();
@ -285,7 +292,7 @@ export default DiscourseController.extend({
// Checks to see if a reply has been typed.
// This is signaled by a keyUp event in a view.
checkReplyLength() {
if (this.present('model.reply')) {
if (!Ember.isEmpty('model.reply')) {
// Notify the composer messages controller that a reply has been typed. Some
// messages only appear after typing.
this.get('controllers.composer-messages').typedReply();
@ -441,6 +448,23 @@ export default DiscourseController.extend({
if (opts.topicCategoryId) {
this.set('model.categoryId', opts.topicCategoryId);
} else if (opts.topicCategory) {
const splitCategory = opts.topicCategory.split("/");
let category;
if (!splitCategory[1]) {
category = this.site.get('categories').findProperty('nameLower', splitCategory[0].toLowerCase());
} else {
const categories = Discourse.Category.list();
const mainCategory = categories.findProperty('nameLower', splitCategory[0].toLowerCase());
category = categories.find(function(item) {
return item && item.get('nameLower') === splitCategory[1].toLowerCase() && item.get('parent_category_id') === mainCategory.id;
});
}
if (category) {
this.set('model.categoryId', category.get('id'));
}
}
if (opts.topicBody) {
@ -452,7 +476,7 @@ export default DiscourseController.extend({
// View a new reply we've made
viewNewReply() {
Discourse.URL.routeTo(this.get('createdPost.url'));
Discourse.URL.routeTo(this.get('model.createdPost.url'));
this.close();
return false;
},

View File

@ -1,13 +1,11 @@
import ObjectController from 'discourse/controllers/object';
export default ObjectController.extend({
export default Ember.ObjectController.extend({
needs: ['navigation/category', 'discovery/topics', 'application'],
loading: false,
category: Em.computed.alias('controllers.navigation/category.category'),
noSubcategories: Em.computed.alias('controllers.navigation/category.noSubcategories'),
loadedAllItems: Em.computed.not("controllers.discovery/topics.canLoadMore"),
loadedAllItems: Em.computed.not("controllers.discovery/topics.model.canLoadMore"),
_showFooter: function() {
this.set("controllers.application.showFooter", this.get("loadedAllItems"));

View File

@ -3,7 +3,7 @@ import DiscoveryController from 'discourse/controllers/discovery';
export default DiscoveryController.extend({
needs: ['modal', 'discovery'],
withLogo: Em.computed.filterBy('categories', 'logo_url'),
withLogo: Em.computed.filterBy('model.categories', 'logo_url'),
showPostsColumn: Em.computed.empty('withLogo'),
actions: {
@ -35,7 +35,7 @@ export default DiscoveryController.extend({
}.property(),
latestTopicOnly: function() {
return this.get('categories').find(function(c) { return c.get('featuredTopics.length') > 1; }) === undefined;
}.property('categories.@each.featuredTopics.length')
return this.get('model.categories').find(function(c) { return c.get('featuredTopics.length') > 1; }) === undefined;
}.property('model.categories.@each.featuredTopics.length')
});

View File

@ -13,6 +13,8 @@ var controllerOpts = {
order: 'default',
ascending: false,
expandGloballyPinned: false,
expandAllPinned: false,
actions: {
@ -80,27 +82,28 @@ var controllerOpts = {
}.property(),
isFilterPage: function(filter, filterType) {
if (!filter) { return false; }
return filter.match(new RegExp(filterType + '$', 'gi')) ? true : false;
},
showDismissRead: function() {
return this.isFilterPage(this.get('filter'), 'unread') && this.get('topics.length') > 0;
}.property('filter', 'topics.length'),
return this.isFilterPage(this.get('model.filter'), 'unread') && this.get('model.topics.length') > 0;
}.property('model.filter', 'model.topics.length'),
showResetNew: function() {
return this.get('filter') === 'new' && this.get('topics.length') > 0;
}.property('filter', 'topics.length'),
return this.get('model.filter') === 'new' && this.get('model.topics.length') > 0;
}.property('model.filter', 'model.topics.length'),
showDismissAtTop: function() {
return (this.isFilterPage(this.get('filter'), 'new') ||
this.isFilterPage(this.get('filter'), 'unread')) &&
this.get('topics.length') >= 30;
}.property('filter', 'topics.length'),
return (this.isFilterPage(this.get('model.filter'), 'new') ||
this.isFilterPage(this.get('model.filter'), 'unread')) &&
this.get('model.topics.length') >= 30;
}.property('model.filter', 'model.topics.length'),
hasTopics: Em.computed.gt('topics.length', 0),
allLoaded: Em.computed.empty('more_topics_url'),
latest: Discourse.computed.endWith('filter', 'latest'),
new: Discourse.computed.endWith('filter', 'new'),
hasTopics: Em.computed.gt('model.topics.length', 0),
allLoaded: Em.computed.empty('model.more_topics_url'),
latest: Discourse.computed.endWith('model.filter', 'latest'),
new: Discourse.computed.endWith('model.filter', 'new'),
top: Em.computed.notEmpty('period'),
yearly: Em.computed.equal('period', 'yearly'),
monthly: Em.computed.equal('period', 'monthly'),
@ -114,8 +117,8 @@ var controllerOpts = {
if( category ) {
return I18n.t('topics.bottom.category', {category: category.get('name')});
} else {
var split = this.get('filter').split('/');
if (this.get('topics.length') === 0) {
var split = (this.get('model.filter') || '').split('/');
if (this.get('model.topics.length') === 0) {
return I18n.t("topics.none." + split[0], {
category: split[1]
});
@ -125,19 +128,19 @@ var controllerOpts = {
});
}
}
}.property('allLoaded', 'topics.length'),
}.property('allLoaded', 'model.topics.length'),
footerEducation: function() {
if (!this.get('allLoaded') || this.get('topics.length') > 0 || !Discourse.User.current()) { return; }
if (!this.get('allLoaded') || this.get('model.topics.length') > 0 || !Discourse.User.current()) { return; }
var split = this.get('filter').split('/');
var split = (this.get('model.filter') || '').split('/');
if (split[0] !== 'new' && split[0] !== 'unread') { return; }
return I18n.t("topics.none.educate." + split[0], {
userPrefsUrl: Discourse.getURL("/users/") + (Discourse.User.currentProp("username_lower")) + "/preferences"
});
}.property('allLoaded', 'topics.length'),
}.property('allLoaded', 'model.topics.length'),
loadMoreTopics() {
return this.get('model').loadMore();

View File

@ -9,20 +9,20 @@ export default ObjectController.extend(ModalFunctionality, {
setAutoCloseTime: function() {
var autoCloseTime = null;
if (this.get("details.auto_close_based_on_last_post")) {
autoCloseTime = this.get("details.auto_close_hours");
} else if (this.get("details.auto_close_at")) {
var closeTime = new Date(this.get("details.auto_close_at"));
if (this.get("model.details.auto_close_based_on_last_post")) {
autoCloseTime = this.get("model.details.auto_close_hours");
} else if (this.get("model.details.auto_close_at")) {
var closeTime = new Date(this.get("model.details.auto_close_at"));
if (closeTime > new Date()) {
autoCloseTime = moment(closeTime).format("YYYY-MM-DD HH:mm");
}
}
this.set("auto_close_time", autoCloseTime);
}.observes("details.{auto_close_at,auto_close_hours}"),
this.set("model.auto_close_time", autoCloseTime);
}.observes("model.details.{auto_close_at,auto_close_hours}"),
actions: {
saveAutoClose: function() { this.setAutoClose(this.get("auto_close_time")); },
saveAutoClose: function() { this.setAutoClose(this.get("model.auto_close_time")); },
removeAutoClose: function() { this.setAutoClose(null); }
},
@ -30,18 +30,18 @@ export default ObjectController.extend(ModalFunctionality, {
var self = this;
this.send('hideModal');
Discourse.ajax({
url: '/t/' + this.get('id') + '/autoclose',
url: '/t/' + this.get('model.id') + '/autoclose',
type: 'PUT',
dataType: 'json',
data: {
auto_close_time: time,
auto_close_based_on_last_post: this.get("details.auto_close_based_on_last_post"),
auto_close_based_on_last_post: this.get("model.details.auto_close_based_on_last_post"),
}
}).then(function(result){
if (result.success) {
self.send('closeModal');
self.set('details.auto_close_at', result.auto_close_at);
self.set('details.auto_close_hours', result.auto_close_hours);
self.set('model.details.auto_close_at', result.auto_close_at);
self.set('model.details.auto_close_hours', result.auto_close_hours);
} else {
bootbox.alert(I18n.t('composer.auto_close.error'), function() { self.send('reopenModal'); } );
}

View File

@ -11,14 +11,14 @@ export default ObjectController.extend(ModalFunctionality, {
bannerCount: 0,
categoryLink: function() {
return categoryLinkHTML(this.get("category"), { allowUncategorized: true });
}.property("category"),
return categoryLinkHTML(this.get("model.category"), { allowUncategorized: true });
}.property("model.category"),
unPinMessage: function() {
return this.get("pinned_globally") ?
return this.get("model.pinned_globally") ?
I18n.t("topic.feature_topic.unpin_globally") :
I18n.t("topic.feature_topic.unpin", { categoryLink: this.get("categoryLink") });
}.property("categoryLink", "pinned_globally"),
}.property("categoryLink", "model.pinned_globally"),
pinMessage: function() {
return I18n.t("topic.feature_topic.pin", { categoryLink: this.get("categoryLink") });
@ -32,7 +32,7 @@ export default ObjectController.extend(ModalFunctionality, {
this.set("loading", true);
return Discourse.ajax("/topics/feature_stats.json", {
data: { category_id: this.get("category.id") }
data: { category_id: this.get("model.category.id") }
}).then(result => {
if (result) {
this.setProperties({

View File

@ -7,22 +7,22 @@ export default ObjectController.extend({
message: Em.computed.alias('controllers.flag.message'),
customPlaceholder: function(){
return I18n.t("flagging.custom_placeholder_" + this.get('name_key'));
}.property('name_key'),
return I18n.t("flagging.custom_placeholder_" + this.get('model.name_key'));
}.property('model.name_key'),
formattedName: function(){
if (this.get("is_custom_flag")) {
return this.get('name').replace("{{username}}", this.get('controllers.flag.username'));
if (this.get("model.is_custom_flag")) {
return this.get('model.name').replace("{{username}}", this.get('controllers.flag.model.username'));
} else {
return I18n.t("flagging.formatted_name." + this.get('name_key'));
return I18n.t("flagging.formatted_name." + this.get('model.name_key'));
}
}.property('name', 'name_key', 'is_custom_flag'),
}.property('model.name', 'model.name_key', 'model.is_custom_flag'),
selected: function() {
return this.get('model') === this.get('controllers.flag.selected');
}.property('controllers.flag.selected'),
showMessageInput: Em.computed.and('is_custom_flag', 'selected'),
showMessageInput: Em.computed.and('model.is_custom_flag', 'selected'),
showDescription: Em.computed.not('showMessageInput'),
customMessageLengthClasses: function() {

View File

@ -2,8 +2,13 @@ import ModalFunctionality from 'discourse/mixins/modal-functionality';
import ObjectController from 'discourse/controllers/object';
export default ObjectController.extend(ModalFunctionality, {
userDetails: null,
selected: null,
flagTopic: null,
message: null,
topicActionByName: null,
onShow: function() {
onShow() {
this.set('selected', null);
},
@ -11,32 +16,31 @@ export default ObjectController.extend(ModalFunctionality, {
if (!this.get('flagTopic')) {
return this.get('model.flagsAvailable');
} else {
var self = this,
const self = this,
lookup = Em.Object.create();
_.each(this.get("actions_summary"),function(a) {
var actionSummary;
_.each(this.get("model.actions_summary"),function(a) {
a.flagTopic = self.get('model');
a.actionType = self.site.topicFlagTypeById(a.id);
actionSummary = Discourse.ActionSummary.create(a);
const actionSummary = Discourse.ActionSummary.create(a);
lookup.set(a.actionType.get('name_key'), actionSummary);
});
this.set('topicActionByName', lookup);
return this.site.get('topic_flag_types').filter(function(item) {
return _.any(self.get("actions_summary"), function(a) {
return _.any(self.get("model.actions_summary"), function(a) {
return (a.id === item.get('id') && a.can_act);
});
});
}
}.property('post', 'flagTopic', 'actions_summary.@each.can_act'),
}.property('post', 'flagTopic', 'model.actions_summary.@each.can_act'),
submitEnabled: function() {
var selected = this.get('selected');
const selected = this.get('selected');
if (!selected) return false;
if (selected.get('is_custom_flag')) {
var len = this.get('message.length') || 0;
const len = this.get('message.length') || 0;
return len >= Discourse.SiteSettings.min_private_message_post_length &&
len <= Discourse.PostActionType.MAX_MESSAGE_LENGTH;
}
@ -63,27 +67,29 @@ export default ObjectController.extend(ModalFunctionality, {
}.property('selected.is_custom_flag'),
actions: {
takeAction: function() {
takeAction() {
this.send('createFlag', {takeAction: true});
this.set('hidden', true);
this.set('model.hidden', true);
},
createFlag: function(opts) {
var self = this;
var postAction; // an instance of ActionSummary
createFlag(opts) {
const self = this;
let postAction; // an instance of ActionSummary
if (!this.get('flagTopic')) {
postAction = this.get('actionByName.' + this.get('selected.name_key'));
postAction = this.get('model.actionByName.' + this.get('selected.name_key'));
} else {
postAction = this.get('topicActionByName.' + this.get('selected.name_key'));
}
var params = this.get('selected.is_custom_flag') ? {message: this.get('message')} : {};
if (opts) params = $.extend(params, opts);
let params = this.get('selected.is_custom_flag') ? {message: this.get('message')} : {};
if (opts) { params = $.extend(params, opts); }
this.send('hideModal');
postAction.act(this.get('model'), params).then(function() {
self.send('closeModal');
if (params.message) {
self.set('message', '');
}
}, function(errors) {
self.send('closeModal');
if (errors && errors.responseText) {
@ -94,7 +100,7 @@ export default ObjectController.extend(ModalFunctionality, {
});
},
changePostActionType: function(action) {
changePostActionType(action) {
this.set('selected', action);
},
},
@ -112,12 +118,12 @@ export default ObjectController.extend(ModalFunctionality, {
usernameChanged: function() {
this.set('userDetails', null);
this.fetchUserDetails();
}.observes('username'),
}.observes('model.username'),
fetchUserDetails: function() {
if( Discourse.User.currentProp('staff') && this.get('username') ) {
var flagController = this;
Discourse.AdminUser.find(this.get('username').toLowerCase()).then(function(user){
if( Discourse.User.currentProp('staff') && this.get('model.username') ) {
const flagController = this;
Discourse.AdminUser.find(this.get('model.username').toLowerCase()).then(function(user){
flagController.set('userDetails', user);
});
}

View File

@ -41,10 +41,11 @@ const HeaderController = DiscourseController.extend({
if (self.get("loadingNotifications")) { return; }
self.set("loadingNotifications", true);
Discourse.NotificationContainer.loadRecent().then(function(result) {
this.store.find('notification', {recent: true}).then(function(notifications) {
self.setProperties({
'currentUser.unread_notifications': 0,
notifications: result
notifications
});
}).catch(function() {
self.setProperties({
@ -79,27 +80,18 @@ function addFlagProperty(prop) {
_flagProperties.pushObject(prop);
}
let _appliedFlagProps = false;
HeaderController.reopenClass({
create() {
// We only want to change the class the first time it's created
if (!_appliedFlagProps && _flagProperties.length) {
_appliedFlagProps = true;
const args = _flagProperties.slice();
args.push(function() {
let sum = 0;
_flagProperties.forEach((fp) => sum += (this.get(fp) || 0));
return sum;
});
HeaderController.reopen({ flaggedPostsCount: Ember.computed.apply(this, args) });
}
return this._super.apply(this, Array.prototype.slice.call(arguments));
}
});
function applyFlaggedProperties() {
const args = _flagProperties.slice();
args.push(function() {
let sum = 0;
_flagProperties.forEach((fp) => sum += (this.get(fp) || 0));
return sum;
});
HeaderController.reopen({ flaggedPostsCount: Ember.computed.apply(this, args) });
}
addFlagProperty('currentUser.site_flagged_posts_count');
addFlagProperty('currentUser.post_queue_new_count');
export { addFlagProperty };
export { addFlagProperty, applyFlaggedProperties };
export default HeaderController;

View File

@ -1,7 +1,8 @@
import Presence from 'discourse/mixins/presence';
import ModalFunctionality from 'discourse/mixins/modal-functionality';
import ObjectController from 'discourse/controllers/object';
export default ObjectController.extend(ModalFunctionality, {
export default ObjectController.extend(Presence, ModalFunctionality, {
needs: ['user-invited'],
// If this isn't defined, it will proxy to the user model on the preferences

View File

@ -1,8 +1,10 @@
import Presence from 'discourse/mixins/presence';
import SelectedPostsCount from 'discourse/mixins/selected-posts-count';
import ModalFunctionality from 'discourse/mixins/modal-functionality';
import ObjectController from 'discourse/controllers/object';
// Modal related to merging of topics
export default ObjectController.extend(Discourse.SelectedPostsCount, ModalFunctionality, {
export default ObjectController.extend(SelectedPostsCount, ModalFunctionality, Presence, {
needs: ['topic'],
topicController: Em.computed.alias('controllers.topic'),

View File

@ -1,40 +0,0 @@
import ObjectController from 'discourse/controllers/object';
const INVITED_TYPE = 8;
export default ObjectController.extend({
notificationUrl: function(it) {
var badgeId = it.get("data.badge_id");
if (badgeId) {
var badgeName = it.get("data.badge_name");
return Discourse.getURL('/badges/' + badgeId + '/' + badgeName.replace(/[^A-Za-z0-9_]+/g, '-').toLowerCase());
}
var topicId = it.get('topic_id');
if (topicId) {
return Discourse.Utilities.postUrl(it.get("slug"), topicId, it.get("post_number"));
}
if (it.get('notification_type') === INVITED_TYPE) {
return Discourse.getURL('/my/invited');
}
},
scope: function() {
return "notifications." + this.site.get("notificationLookup")[this.get("notification_type")];
}.property("notification_type"),
username: Em.computed.alias("data.display_username"),
url: function() {
return this.notificationUrl(this);
}.property("data.{badge_id,badge_name}", "slug", "topic_id", "post_number"),
description: function() {
const badgeName = this.get("data.badge_name");
if (badgeName) { return Handlebars.Utils.escapeExpression(badgeName); }
return this.blank("data.topic_title") ? "" : Handlebars.Utils.escapeExpression(this.get("data.topic_title"));
}.property("data.{badge_name,topic_title}")
});

View File

@ -1,6 +1,7 @@
import Presence from 'discourse/mixins/presence';
import ObjectController from 'discourse/controllers/object';
export default ObjectController.extend({
export default ObjectController.extend(Presence, {
taken: false,
saving: false,
error: false,

View File

@ -43,7 +43,7 @@ export default DiscourseController.extend({
if (this.get('buffer') === selectedText) return;
// we need to retrieve the post data from the posts collection in the topic controller
const postStream = this.get('controllers.topic.postStream');
const postStream = this.get('controllers.topic.model.postStream');
this.set('post', postStream.findLoadedPost(postId));
this.set('buffer', selectedText);

View File

@ -2,7 +2,7 @@ import Sharing from 'discourse/lib/sharing';
export default Ember.Controller.extend({
needs: ['topic'],
title: Ember.computed.alias('controllers.topic.title'),
title: Ember.computed.alias('controllers.topic.model.title'),
displayDate: function() {
return Discourse.Formatter.longDateNoYear(new Date(this.get('date')));

View File

@ -1,10 +1,10 @@
export default Ember.ObjectController.extend({
export default Ember.Controller.extend({
needs: ['site-map'],
unreadTotal: function() {
return parseInt(this.get('unreadTopics'), 10) +
parseInt(this.get('newTopics'), 10);
}.property('unreadTopics', 'newTopics'),
return parseInt(this.get('model.unreadTopics'), 10) +
parseInt(this.get('model.newTopics'), 10);
}.property('model.unreadTopics', 'model.newTopics'),
showTopicCount: Em.computed.not('currentUser')
});

View File

@ -1,8 +1,10 @@
import Presence from 'discourse/mixins/presence';
import SelectedPostsCount from 'discourse/mixins/selected-posts-count';
import ModalFunctionality from 'discourse/mixins/modal-functionality';
import ObjectController from 'discourse/controllers/object';
// Modal related to auto closing of topics
export default ObjectController.extend(Discourse.SelectedPostsCount, ModalFunctionality, {
export default ObjectController.extend(SelectedPostsCount, ModalFunctionality, Presence, {
needs: ['topic'],
topicController: Em.computed.alias('controllers.topic'),

View File

@ -1,9 +1,9 @@
export default Em.ObjectController.extend({
showLoginButton: Em.computed.equal('path', 'login'),
export default Ember.Controller.extend({
showLoginButton: Em.computed.equal('model.path', 'login'),
actions: {
markFaqRead: function() {
if (Discourse.User.current()) {
if (this.currentUser) {
Discourse.ajax("/users/read-faq", { method: "POST" });
}
}

View File

@ -3,8 +3,8 @@ import ObjectController from 'discourse/controllers/object';
// This controller supports the admin menu on topics
export default ObjectController.extend({
menuVisible: false,
showRecover: Em.computed.and('deleted', 'details.can_recover'),
isFeatured: Em.computed.or("pinned_at", "isBanner"),
showRecover: Em.computed.and('model.deleted', 'model.details.can_recover'),
isFeatured: Em.computed.or("model.pinned_at", "model.isBanner"),
actions: {
show: function() { this.set('menuVisible', true); },

View File

@ -17,7 +17,7 @@ function entranceDate(dt, showTime) {
);
}
export default Ember.ObjectController.extend({
export default Ember.Controller.extend({
position: null,
createdDate: function() {
@ -51,11 +51,11 @@ export default Ember.ObjectController.extend({
},
enterTop: function() {
Discourse.URL.routeTo(this.get('url'));
Discourse.URL.routeTo(this.get('model.url'));
},
enterBottom: function() {
Discourse.URL.routeTo(this.get('lastPostUrl'));
Discourse.URL.routeTo(this.get('model.lastPostUrl'));
}
}
});

View File

@ -1,40 +0,0 @@
import ObjectController from 'discourse/controllers/object';
// Handles displaying of a topic as a list item
export default ObjectController.extend({
needs: ['discovery/topics'],
canStar: Em.computed.alias('controllers.discovery/topics.currentUser.id'),
bulkSelectEnabled: Em.computed.alias('controllers.discovery/topics.bulkSelectEnabled'),
showTopicPostBadges: Em.computed.not('controllers.discovery/topics.new'),
checked: function(key, value) {
var selected = this.get('controllers.discovery/topics.selected'),
topic = this.get('model');
if (arguments.length > 1) {
if (value) {
selected.addObject(topic);
} else {
selected.removeObject(topic);
}
}
return selected.contains(topic);
}.property('controllers.discovery/topics.selected.length'),
titleColSpan: function() {
// Uncategorized pinned topics will span the title and category column in the topic list.
return (!this.get('controllers.discovery/topics.hideCategory') &&
this.get('model.isPinnedUncategorized') ? 2 : 1);
}.property('controllers.discovery/topics.hideCategory', 'model.isPinnedUncategorized'),
hideCategory: function() {
return this.get('controllers.discovery/topics.hideCategory') || this.get('titleColSpan') > 1;
}.property('controllers.discovery/topics.hideCategory', 'titleColSpan'),
actions: {
toggleStar: function() {
this.get('model').toggleStar();
}
}
});

View File

@ -24,11 +24,11 @@ export default Ember.ObjectController.extend({
if (isNaN(postIndex) || postIndex < 1) {
postIndex = 1;
}
if (postIndex > this.get('postStream.filteredPostsCount')) {
postIndex = this.get('postStream.filteredPostsCount');
if (postIndex > this.get('model.postStream.filteredPostsCount')) {
postIndex = this.get('model.postStream.filteredPostsCount');
}
this.set('toPostIndex', postIndex);
var stream = this.get('postStream'),
var stream = this.get('model.postStream'),
postId = stream.findPostIdForPostNumber(postIndex);
if (!postId) {
@ -65,36 +65,36 @@ export default Ember.ObjectController.extend({
},
streamPercentage: function() {
if (!this.get('postStream.loaded')) { return 0; }
if (this.get('postStream.highest_post_number') === 0) { return 0; }
var perc = this.get('progressPosition') / this.get('postStream.filteredPostsCount');
if (!this.get('model.postStream.loaded')) { return 0; }
if (this.get('model.postStream.highest_post_number') === 0) { return 0; }
var perc = this.get('progressPosition') / this.get('model.postStream.filteredPostsCount');
return (perc > 1.0) ? 1.0 : perc;
}.property('postStream.loaded', 'progressPosition', 'postStream.filteredPostsCount'),
}.property('model.postStream.loaded', 'progressPosition', 'model.postStream.filteredPostsCount'),
jumpTopDisabled: function() {
return this.get('progressPosition') <= 3;
}.property('progressPosition'),
filteredPostCountChanged: function(){
if(this.get('postStream.filteredPostsCount') < this.get('progressPosition')){
this.set('progressPosition', this.get('postStream.filteredPostsCount'));
if(this.get('model.postStream.filteredPostsCount') < this.get('progressPosition')){
this.set('progressPosition', this.get('model.postStream.filteredPostsCount'));
}
}.observes('postStream.filteredPostsCount'),
}.observes('model.postStream.filteredPostsCount'),
jumpBottomDisabled: function() {
return this.get('progressPosition') >= this.get('postStream.filteredPostsCount') ||
return this.get('progressPosition') >= this.get('model.postStream.filteredPostsCount') ||
this.get('progressPosition') >= this.get('highest_post_number');
}.property('postStream.filteredPostsCount', 'highest_post_number', 'progressPosition'),
}.property('model.postStream.filteredPostsCount', 'highest_post_number', 'progressPosition'),
hideProgress: function() {
if (!this.get('postStream.loaded')) return true;
if (!this.get('currentPost')) return true;
if (this.get('postStream.filteredPostsCount') < 2) return true;
if (!this.get('model.postStream.loaded')) return true;
if (!this.get('model.currentPost')) return true;
if (this.get('model.postStream.filteredPostsCount') < 2) return true;
return false;
}.property('postStream.loaded', 'currentPost', 'postStream.filteredPostsCount'),
}.property('model.postStream.loaded', 'model.currentPost', 'model.postStream.filteredPostsCount'),
hugeNumberOfPosts: function() {
return (this.get('postStream.filteredPostsCount') >= Discourse.SiteSettings.short_progress_text_threshold);
return (this.get('model.postStream.filteredPostsCount') >= Discourse.SiteSettings.short_progress_text_threshold);
}.property('highest_post_number'),
jumpToBottomTitle: function() {

View File

@ -1,9 +1,10 @@
import ObjectController from 'discourse/controllers/object';
import BufferedContent from 'discourse/mixins/buffered-content';
import SelectedPostsCount from 'discourse/mixins/selected-posts-count';
import { spinnerHTML } from 'discourse/helpers/loading-spinner';
import Topic from 'discourse/models/topic';
export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedContent, {
export default ObjectController.extend(SelectedPostsCount, BufferedContent, {
multiSelect: false,
needs: ['header', 'modal', 'composer', 'quote-button', 'search', 'topic-progress', 'application'],
allPostsSelected: false,
@ -12,6 +13,9 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
selectedReplies: null,
queryParams: ['filter', 'username_filters', 'show_deleted'],
searchHighlight: null,
loadedAllPosts: false,
enteredAt: null,
firstPostExpanded: false,
maxTitleLength: Discourse.computed.setting('max_topic_title_length'),
@ -20,14 +24,14 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
}.observes('topic'),
_titleChanged: function() {
const title = this.get('title');
const title = this.get('model.title');
if (!Ember.isEmpty(title)) {
// Note normally you don't have to trigger this, but topic titles can be updated
// and are sometimes lazily loaded.
this.send('refreshTitle');
}
}.observes('title', 'category'),
}.observes('model.title', 'category'),
termChanged: function() {
const dropdown = this.get('controllers.header.visibleDropdown');
@ -47,47 +51,47 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
// semantics of loaded all posts are slightly diff at topic level,
// it just means that we "once" loaded all posts, this means we don't
// keep re-rendering the suggested topics when new posts zoom in
let loaded = this.get('postStream.loadedAllPosts');
let loaded = this.get('model.postStream.loadedAllPosts');
if (loaded) {
this.set('loadedTopicId', this.get('model.id'));
this.set('model.loadedTopicId', this.get('model.id'));
} else {
loaded = this.get('loadedTopicId') === this.get('model.id');
loaded = this.get('model.loadedTopicId') === this.get('model.id');
}
this.set('loadedAllPosts', loaded);
}.observes('postStream', 'postStream.loadedAllPosts'),
}.observes('model.postStream', 'model.postStream.loadedAllPosts'),
show_deleted: function(key, value) {
const postStream = this.get('postStream');
const postStream = this.get('model.postStream');
if (!postStream) { return; }
if (arguments.length > 1) {
postStream.set('show_deleted', value);
}
return postStream.get('show_deleted') ? true : undefined;
}.property('postStream.summary'),
}.property('model.postStream.summary'),
filter: function(key, value) {
const postStream = this.get('postStream');
const postStream = this.get('model.postStream');
if (!postStream) { return; }
if (arguments.length > 1) {
postStream.set('summary', value === "summary");
}
return postStream.get('summary') ? "summary" : undefined;
}.property('postStream.summary'),
}.property('model.postStream.summary'),
username_filters: function(key, value) {
const postStream = this.get('postStream');
const postStream = this.get('model.postStream');
if (!postStream) { return; }
if (arguments.length > 1) {
postStream.set('streamFilters.username_filters', value);
}
return postStream.get('streamFilters.username_filters');
}.property('postStream.streamFilters.username_filters'),
}.property('model.postStream.streamFilters.username_filters'),
_clearSelected: function() {
this.set('selectedPosts', []);
@ -95,7 +99,7 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
}.on('init'),
_togglePinnedStates(property) {
const value = this.get('pinned_at') ? false : true,
const value = this.get('model.pinned_at') ? false : true,
topic = this.get('content');
// optimistic update
@ -189,7 +193,7 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
'class': 'btn-primary',
callback() {
Discourse.Post.deleteMany([post], [post]);
self.get('postStream.posts').forEach(function (p) {
self.get('model.postStream.posts').forEach(function (p) {
if (p === post || p.get('reply_to_post_number') === post.get('post_number')) {
p.setDeletedState(user);
}
@ -246,7 +250,7 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
},
selectAll() {
const posts = this.get('postStream.posts'),
const posts = this.get('model.postStream.posts'),
selectedPosts = this.get('selectedPosts');
if (posts) {
selectedPosts.addObjects(posts);
@ -261,11 +265,11 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
},
toggleParticipant(user) {
this.get('postStream').toggleParticipant(Em.get(user, 'username'));
this.get('model.postStream').toggleParticipant(Em.get(user, 'username'));
},
editTopic() {
if (!this.get('details.can_edit')) return false;
if (!this.get('model.details.can_edit')) return false;
this.set('editingTopic', true);
return false;
@ -326,7 +330,7 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
const selectedPosts = self.get('selectedPosts'),
selectedReplies = self.get('selectedReplies'),
postStream = self.get('postStream'),
postStream = self.get('model.postStream'),
toRemove = [];
Discourse.Post.deleteMany(selectedPosts, selectedReplies);
@ -365,7 +369,7 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
},
togglePinned() {
const value = this.get('pinned_at') ? false : true,
const value = this.get('model.pinned_at') ? false : true,
topic = this.get('content');
// optimistic update
@ -403,7 +407,7 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
},
togglePinnedForUser() {
if (this.get('pinned_at')) {
if (this.get('model.pinned_at')) {
if (this.get('pinned')) {
this.get('content').clearPin();
} else {
@ -428,7 +432,7 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
return Em.isEmpty(quotedText) ? Discourse.Post.loadQuote(post.get('id')) : quotedText;
}).then(function(q) {
const postUrl = "" + location.protocol + "//" + location.host + post.get('url'),
postLink = "[" + Handlebars.escapeExpression(self.get('title')) + "](" + postUrl + ")";
postLink = "[" + Handlebars.escapeExpression(self.get('model.title')) + "](" + postUrl + ")";
composerController.appendText(I18n.t("post.continue_discussion", { postLink: postLink }) + "\n\n" + q);
});
},
@ -448,7 +452,7 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
retryLoading() {
const self = this;
self.set('retrying', true);
this.get('postStream').refresh().then(function() {
this.get('model.postStream').refresh().then(function() {
self.set('retrying', false);
}, function() {
self.set('retrying', false);
@ -491,12 +495,12 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
}.property(),
canMergeTopic: function() {
if (!this.get('details.can_move_posts')) return false;
if (!this.get('model.details.can_move_posts')) return false;
return (this.get('selectedPostsCount') > 0);
}.property('selectedPostsCount'),
canSplitTopic: function() {
if (!this.get('details.can_move_posts')) return false;
if (!this.get('model.details.can_move_posts')) return false;
if (this.get('allPostsSelected')) return false;
return (this.get('selectedPostsCount') > 0);
}.property('selectedPostsCount'),
@ -533,7 +537,7 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
return canDelete;
}.property('selectedPostsCount'),
hasError: Ember.computed.or('notFoundHtml', 'message'),
hasError: Ember.computed.or('model.notFoundHtml', 'model.message'),
noErrorYet: Ember.computed.not('hasError'),
multiSelectChanged: function() {
@ -564,8 +568,8 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
},
showStarButton: function() {
return Discourse.User.current() && !this.get('isPrivateMessage');
}.property('isPrivateMessage'),
return Discourse.User.current() && !this.get('model.isPrivateMessage');
}.property('model.isPrivateMessage'),
loadingHTML: function() {
return spinnerHTML;
@ -586,7 +590,7 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
this.unsubscribe();
const self = this;
this.messageBus.subscribe("/topic/" + this.get('id'), function(data) {
this.messageBus.subscribe("/topic/" + this.get('model.id'), function(data) {
const topic = self.get('model');
if (data.notification_level_change) {
@ -595,7 +599,7 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
return;
}
const postStream = self.get('postStream');
const postStream = self.get('model.postStream');
switch (data.type) {
case "revised":
case "acted":
@ -643,27 +647,24 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
return false;
} else {
selectedPosts.addObject(post);
// If the user manually selects all posts, all posts are selected
if (selectedPosts.length === this.get('posts_count')) {
this.set('allPostsSelected', true);
}
this.set('allPostsSelected', selectedPosts.length === this.get('model.posts_count'));
return true;
}
},
// If our current post is changed, notify the router
_currentPostChanged: function() {
const currentPost = this.get('currentPost');
const currentPost = this.get('model.currentPost');
if (currentPost) {
this.send('postChangedRoute', currentPost);
}
}.observes('currentPost'),
}.observes('model.currentPost'),
readPosts(topicId, postNumbers) {
const postStream = this.get('postStream');
const postStream = this.get('model.postStream');
if(this.get('postStream.topic.id') === topicId){
if (postStream.get('topic.id') === topicId){
_.each(postStream.get('posts'), function(post){
// optimise heavy loop
// TODO identity map for postNumber
@ -673,8 +674,8 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
});
const max = _.max(postNumbers);
if(max > this.get('last_read_post_number')){
this.set('last_read_post_number', max);
if(max > this.get('model.last_read_post_number')){
this.set('model.sast_read_post_number', max);
}
}
},
@ -683,10 +684,10 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
topVisibleChanged(post) {
if (!post) { return; }
const postStream = this.get('postStream'),
firstLoadedPost = postStream.get('firstLoadedPost');
const postStream = this.get('model.postStream'),
firstLoadedPost = postStream.get('firstLoadedPost');
this.set('currentPost', post.get('post_number'));
this.set('model.currentPost', post.get('post_number'));
if (post.get('post_number') === 1) { return; }
@ -721,7 +722,7 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
bottomVisibleChanged(post) {
if (!post) { return; }
const postStream = this.get('postStream'),
const postStream = this.get('model.postStream'),
lastLoadedPost = postStream.get('lastLoadedPost');
this.set('controllers.topic-progress.progressPosition', postStream.progressIndexOfPost(post));
@ -732,7 +733,7 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
},
_showFooter: function() {
this.set("controllers.application.showFooter", this.get("postStream.loadedAllPosts"));
}.observes("postStream.loadedAllPosts")
this.set("controllers.application.showFooter", this.get("model.postStream.loadedAllPosts"));
}.observes("model.postStream.loadedAllPosts")
});

View File

@ -1,15 +1,16 @@
export default Ember.ObjectController.extend({
userActionType: null,
needs: ["application"],
_showFooter: function() {
var showFooter;
if (this.get("userActionType")) {
var stat = _.find(this.get("stats"), { action_type: this.get("userActionType") });
showFooter = stat && stat.count <= this.get("stream.itemsLoaded");
var stat = _.find(this.get("model.stats"), { action_type: this.get("userActionType") });
showFooter = stat && stat.count <= this.get("model.stream.itemsLoaded");
} else {
showFooter = this.get("statsCountNonPM") <= this.get("stream.itemsLoaded");
showFooter = this.get("model.statsCountNonPM") <= this.get("model.stream.itemsLoaded");
}
this.set("controllers.application.showFooter", showFooter);
}.observes("userActionType", "stream.itemsLoaded")
}.observes("userActionType", "model.stream.itemsLoaded")
});

View File

@ -1,6 +1,4 @@
import ObjectController from 'discourse/controllers/object';
export default ObjectController.extend({
export default Ember.Controller.extend({
needs: ['topic', 'application'],
visible: false,
user: null,
@ -13,7 +11,7 @@ export default ObjectController.extend({
// If inside a topic
topicPostCount: null,
postStream: Em.computed.alias('controllers.topic.postStream'),
postStream: Em.computed.alias('controllers.topic.model.postStream'),
enoughPostsForFiltering: Em.computed.gte('topicPostCount', 2),
viewingTopic: Em.computed.match('controllers.application.currentPath', /^topic\./),
viewingAdmin: Em.computed.match('controllers.application.currentPath', /^admin\./),
@ -47,7 +45,7 @@ export default ObjectController.extend({
const currentUsername = this.get('username'),
wasVisible = this.get('visible'),
post = this.get('viewingTopic') && postId ? this.get('controllers.topic.postStream').findLoadedPost(postId) : null;
post = this.get('viewingTopic') && postId ? this.get('postStream').findLoadedPost(postId) : null;
this.setProperties({ avatar: null, post: post, username: username });
@ -92,7 +90,7 @@ export default ObjectController.extend({
actions: {
togglePosts(user) {
const postStream = this.get('controllers.topic.postStream');
const postStream = this.get('postStream');
postStream.toggleParticipant(user.get('username'));
this.close();
},

View File

@ -1,43 +1,21 @@
export default Ember.ArrayController.extend({
needs: ['user-notifications', 'application'],
loading: false,
needs: ['application'],
_showFooter: function() {
this.set("controllers.application.showFooter", !this.get("canLoadMore"));
}.observes("canLoadMore"),
this.set("controllers.application.showFooter", !this.get("model.canLoadMore"));
}.observes("model.canLoadMore"),
showDismissButton: function() {
return this.get('user').total_unread_notifications > 0;
}.property('user'),
showDismissButton: Ember.computed.gt('user.total_unread_notifications', 0),
actions: {
resetNew: function() {
var self = this;
Discourse.NotificationContainer.resetNew().then(function() {
self.get('controllers.user-notifications').setEach('read', true);
Discourse.ajax('/notifications/mark-read', { method: 'PUT' }).then(() => {
this.setEach('read', true);
});
},
loadMore: function() {
if (this.get('canLoadMore') && !this.get('loading')) {
this.set('loading', true);
var self = this;
Discourse.NotificationContainer.loadHistory(
self.get('model.lastObject.created_at'),
self.get('user.username')).then(function(result) {
self.set('loading', false);
var notifications = result.get('content');
self.pushObjects(notifications);
// Stop trying if it's the end
if (notifications && (notifications.length === 0 || notifications.length < 60)) {
self.set('canLoadMore', false);
}
}).catch(function(error) {
self.set('loading', false);
Em.Logger.error(error);
});
}
this.get('model').loadMore();
}
}
});

View File

@ -7,8 +7,8 @@ export default ObjectController.extend({
showParticipants: false,
_showFooter: function() {
this.set("controllers.application.showFooter", !this.get("canLoadMore"));
}.observes("canLoadMore"),
this.set("controllers.application.showFooter", !this.get("model.canLoadMore"));
}.observes("model.canLoadMore"),
actions: {
loadMore: function() {

View File

@ -3,7 +3,9 @@ import CanCheckEmails from 'discourse/mixins/can-check-emails';
export default ObjectController.extend(CanCheckEmails, {
indexStream: false,
needs: ['user-notifications', 'user_topics_list'],
pmView: false,
userActionType: null,
needs: ['user-notifications', 'user-topics-list'],
viewingSelf: function() {
return this.get('content.username') === Discourse.User.currentProp('username');
@ -12,12 +14,16 @@ export default ObjectController.extend(CanCheckEmails, {
collapsedInfo: Em.computed.not('indexStream'),
websiteName: function() {
var website = this.get('website');
var website = this.get('model.website');
if (Em.isEmpty(website)) { return; }
return this.get('website').split("/")[2];
}.property('website'),
return website.split("/")[2];
}.property('model.website'),
linkWebsite: Em.computed.not('isBasic'),
linkWebsite: Em.computed.not('model.isBasic'),
removeNoFollow: function() {
return this.get('model.trust_level') > 2 && !this.siteSettings.tl3_links_no_follow;
}.property('model.trust_level'),
canSeePrivateMessages: Ember.computed.or('viewingSelf', 'currentUser.admin'),
canSeeNotificationHistory: Em.computed.alias('canSeePrivateMessages'),
@ -36,13 +42,13 @@ export default ObjectController.extend(CanCheckEmails, {
}.property(),
canDeleteUser: function() {
return this.get('can_be_deleted') && this.get('can_delete_all_posts');
}.property('can_be_deleted', 'can_delete_all_posts'),
return this.get('model.can_be_deleted') && this.get('model.can_delete_all_posts');
}.property('model.can_be_deleted', 'model.can_delete_all_posts'),
publicUserFields: function() {
var siteUserFields = this.site.get('user_fields');
if (!Ember.isEmpty(siteUserFields)) {
var userFields = this.get('user_fields');
var userFields = this.get('model.user_fields');
return siteUserFields.filterProperty('show_on_profile', true).sortBy('id').map(function(uf) {
var val = userFields ? userFields[uf.get('id').toString()] : null;
if (Ember.isEmpty(val)) {
@ -52,7 +58,7 @@ export default ObjectController.extend(CanCheckEmails, {
}
}).compact();
}
}.property('user_fields.@each.value'),
}.property('model.user_fields.@each.value'),
privateMessagesActive: Em.computed.equal('pmView', 'index'),
privateMessagesMineActive: Em.computed.equal('pmView', 'mine'),

View File

@ -0,0 +1,4 @@
export default Ember.Handlebars.makeBoundHelper(function(value) {
return ("border-color: #" + value).htmlSafe();
});

View File

@ -1,17 +0,0 @@
/**
Set up an i18n binding that will update as a count changes, complete with pluralization.
@method countI18n
@for Handlebars
**/
Ember.Handlebars.registerHelper('countI18n', function(key, options) {
var view = Discourse.View.extend(Discourse.StringBuffer, {
tagName: 'span',
rerenderTriggers: ['count', 'suffix'],
renderString: function(buffer) {
buffer.push(I18n.t(key + (this.get('suffix') || ''), { count: this.get('count') }));
}
});
return Ember.Handlebars.helpers.view.call(this, view, options);
});

View File

@ -1,9 +1,11 @@
Handlebars.registerHelper('custom-html', function(name, contextString, options) {
var html = Discourse.HTML.getCustomHTML(name);
Ember.HTMLBars._registerHelper('custom-html', function(params, hash, options, env) {
const name = params[0];
const html = Discourse.HTML.getCustomHTML(name);
if (html) { return html; }
var container = (options || contextString).data.view.container;
const contextString = params[1];
const container = (env || contextString).data.view.container;
if (container.lookup('template:' + name)) {
return Ember.Handlebars.helpers.partial.apply(this, arguments);
return env.helpers.partial.helperFunction.apply(this, arguments);
}
});

View File

@ -1,5 +1,3 @@
import ConditionalLoadingSpinner from 'discourse/components/conditional-loading-spinner';
function renderSpinner(cssClass) {
var html = "<div class='spinner";
if (cssClass) { html += ' ' + cssClass; }
@ -7,25 +5,9 @@ function renderSpinner(cssClass) {
}
var spinnerHTML = renderSpinner();
/**
If you use it as a regular helper {{loading-spinner}} you'll just get the
HTML for a spinner.
If you provide an `condition=xyz` parameter, it will be bound to that property
and only show when it's truthy.
If you use the block form `{{#loading-spinner}} ... {{/loading-spinner}`,
the contents will shown when the loading condition finishes.
**/
Handlebars.registerHelper('loading-spinner', function(options) {
var hash = options.hash;
if (hash && hash.condition) {
var types = options.hashTypes;
Discourse.Utilities.normalizeHash(hash, types);
return Ember.Handlebars.helpers.view.call(this, ConditionalLoadingSpinner, options);
} else {
return new Handlebars.SafeString(renderSpinner((hash && hash.size) ? hash.size : undefined));
}
Ember.Handlebars.registerHelper('loading-spinner', function(params) {
const hash = params.hash;
return new Handlebars.SafeString(renderSpinner((hash && hash.size) ? hash.size : undefined));
});
export { spinnerHTML, renderSpinner };

View File

@ -99,7 +99,9 @@ function buildConnectorCache() {
});
}
export default function(connectionName, options) {
Ember.HTMLBars._registerHelper('plugin-outlet', function(params, hash, options, env) {
const connectionName = params[0];
if (!_connectorCache) { buildConnectorCache(); }
if (_connectorCache[connectionName]) {
@ -110,9 +112,9 @@ export default function(connectionName, options) {
const viewClass = (childViews.length > 1) ? Ember.ContainerView : childViews[0];
delete options.fn; // we don't need the default template since we have a connector
Ember.Handlebars.helpers.view.call(this, viewClass, options);
env.helpers.view.helperFunction.call(this, [viewClass], hash, options, env);
const cvs = options.data.view._childViews;
const cvs = env.data.view._childViews;
if (childViews.length > 1 && cvs && cvs.length) {
const inserted = cvs[cvs.length-1];
if (inserted) {
@ -121,16 +123,5 @@ export default function(connectionName, options) {
});
}
}
} else if (options.fn) {
// If a block is passed, render its content.
return Ember.Handlebars.helpers.view.call(this,
Ember.View.extend({
isVirtual: true,
tagName: '',
template: function() {
return options.hash.template;
}.property()
}),
options);
}
}
});

View File

@ -22,11 +22,14 @@ function resolveParams(ctx, options) {
}
export default function registerUnbound(name, fn) {
Handlebars.registerHelper(name, function(property, options) {
const func = function(property, options) {
if (options.types && options.types[0] === "ID") {
property = get(this, property, options);
}
return fn.call(this, property, resolveParams(this, options));
});
};
Handlebars.registerHelper(name, func);
Ember.Handlebars.registerHelper(name, func);
}

View File

@ -0,0 +1,7 @@
import { applyFlaggedProperties } from 'discourse/controllers/header';
export default {
name: 'apply-flagged-properties',
after: 'register-discourse-location',
initialize: applyFlaggedProperties
};

View File

@ -3,6 +3,7 @@ export default {
after: "message-bus",
initialize(container) {
const banner = Em.Object.create(PreloadStore.get("banner")),
site = container.lookup('site:main');

View File

@ -38,13 +38,13 @@ export default {
app.register('session:main', Session.current(), { instantiate: false });
injectAll(app, 'session');
app.register('store:main', Store);
inject(app, 'store', 'route', 'controller');
app.register('current-user:main', Discourse.User.current(), { instantiate: false });
inject(app, 'currentUser', 'component', 'route', 'controller');
app.register('message-bus:main', window.MessageBus, { instantiate: false });
inject(app, 'messageBus', 'route', 'controller', 'view');
app.register('store:main', Store);
inject(app, 'store', 'route', 'controller');
}
};

View File

@ -0,0 +1,17 @@
import { mapRoutes } from 'discourse/router';
export default {
name: "map-routes",
after: 'inject-objects',
initialize(container, app) {
app.register('router:main', mapRoutes());
// HACK to fix: https://github.com/emberjs/ember.js/issues/10310
const originalBuildInstance = originalBuildInstance || Ember.Application.prototype.buildInstance;
Ember.Application.prototype.buildInstance = function() {
this.registry = this.buildRegistry();
return originalBuildInstance.apply(this);
};
}
};

View File

@ -1,8 +1,10 @@
import DiscourseLocation from 'discourse/lib/discourse-location';
export default {
name: "register-discourse-location",
after: 'inject-objects',
initialize: function(container, application) {
application.register('location:discourse-location', Ember.DiscourseLocation);
application.register('location:discourse-location', DiscourseLocation);
}
};

View File

@ -19,12 +19,14 @@ export default {
return "http://twitter.com/intent/tweet?url=" + encodeURIComponent(link) + "&text=" + encodeURIComponent(title);
},
shouldOpenInPopup: true,
title: I18n.t('share.twitter'),
popupHeight: 265
});
Sharing.addSource({
id: 'facebook',
faIcon: 'fa-facebook-square',
title: I18n.t('share.facebook'),
generateUrl: function(link, title) {
return "http://www.facebook.com/sharer.php?u=" + encodeURIComponent(link) + '&t=' + encodeURIComponent(title);
},
@ -34,6 +36,7 @@ export default {
Sharing.addSource({
id: 'google+',
faIcon: 'fa-google-plus-square',
title: I18n.t('share.google+'),
generateUrl: function(link) {
return "https://plus.google.com/share?url=" + encodeURIComponent(link);
},
@ -44,6 +47,7 @@ export default {
Sharing.addSource({
id: 'email',
faIcon: 'fa-envelope-square',
title: I18n.t('share.email'),
generateUrl: function(link, title) {
return "mailto:?to=&subject=" + encodeURIComponent('[' + Discourse.SiteSettings.title + '] ' + title) + "&body=" + encodeURIComponent(link);
}

View File

@ -1,25 +0,0 @@
var helpers = ['input-tip',
'category-chooser',
'combo-box',
'choose-topic',
'activity-filter'];
/**
Creates view helpers for some views. Many of these should probably be converted
into components in the long term as it's a better fit.
**/
export default {
name: 'view-hlpers',
initialize: function(container) {
helpers.forEach(function(h) {
Ember.Handlebars.registerHelper(h, function(options) {
var helper = container.lookupFactory('view:' + h),
hash = options.hash,
types = options.hashTypes;
Discourse.Utilities.normalizeHash(hash, types);
return Ember.Handlebars.helpers.view.call(this, helper, options);
});
});
}
};

View File

@ -1403,6 +1403,10 @@
xPosition += 25;
button.id = id + postfix;
button.title = title;
// we really should just use jquery here
if (button.setAttribute) {
button.setAttribute('aria-label', title);
}
if (textOp)
button.textOp = textOp;
setupButton(button, true);

View File

@ -7,20 +7,33 @@ function extractError(error) {
Ember.Logger.error(error);
}
let parsedError;
if (error.responseText) {
if (error.jqXHR) {
error = error.jqXHR;
}
let parsedError, parsedJSON;
if (error.responseJSON) {
parsedJSON = error.responseJSON;
}
if (!parsedJSON && error.responseText) {
try {
const parsedJSON = $.parseJSON(error.responseText);
if (parsedJSON.errors) {
parsedError = parsedJSON.errors[0];
} else if (parsedJSON.failed) {
parsedError = parsedJSON.message;
}
parsedJSON = $.parseJSON(error.responseText);
} catch(ex) {
// in case the JSON doesn't parse
Ember.Logger.error(ex.stack);
}
}
if (parsedJSON) {
if (parsedJSON.errors && parsedJSON.errors.length > 0) {
parsedError = parsedJSON.errors[0];
} else if (parsedJSON.failed) {
parsedError = parsedJSON.message;
}
}
return parsedError || I18n.t('generic_error');
}
@ -28,11 +41,10 @@ export function throwAjaxError(undoCallback) {
return function(error) {
// If we provided an `undo` callback
if (undoCallback) { undoCallback(error); }
throw extractError(error);
};
}
export function popupAjaxError(err) {
bootbox.alert(extractError(err));
export function popupAjaxError(error) {
bootbox.alert(extractError(error));
}

View File

@ -1,12 +1,14 @@
import CloakedCollectionView from 'discourse/views/cloaked-collection';
/**
@module Discourse
*/
var get = Ember.get, set = Ember.set;
var popstateFired = false;
var supportsHistoryState = window.history && 'state' in window.history;
const get = Ember.get, set = Ember.set;
let popstateFired = false;
const supportsHistoryState = window.history && 'state' in window.history;
var popstateCallbacks = [];
const popstateCallbacks = [];
/**
`Ember.DiscourseLocation` implements the location API using the browser's
@ -16,7 +18,7 @@ var popstateCallbacks = [];
@namespace Discourse
@extends Ember.Object
*/
Ember.DiscourseLocation = Ember.Object.extend({
const DiscourseLocation = Ember.Object.extend({
init: function() {
set(this, 'location', get(this, 'location') || window.location);
@ -226,7 +228,7 @@ Ember.DiscourseLocation = Ember.Object.extend({
eject itself when the popState occurs. This results in better back button
behavior.
**/
Ember.CloakedCollectionView.reopen({
CloakedCollectionView.reopen({
_watchForPopState: function() {
var self = this,
cb = function() {
@ -241,7 +243,7 @@ Ember.CloakedCollectionView.reopen({
// topic_route deactivate
$('.posts,#topic-title').hide();
self.cleanUp();
self.set('controller.postStream.loaded', false);
self.set('controller.model.postStream.loaded', false);
};
this.set('_callback', cb);
popstateCallbacks.addObject(cb);
@ -252,3 +254,5 @@ Ember.CloakedCollectionView.reopen({
this.set('_callback', null);
}.on('willDestroyElement')
});
export default DiscourseLocation;

View File

@ -22,6 +22,8 @@
RawHandlebars.helpers.get = function(context, options){
var firstContext = options.contexts[0];
var val = firstContext[context];
if (val && val.isDescriptor) { return Em.get(firstContext, context); }
val = val === undefined ? Em.get(firstContext, context): val;
return val;
};

View File

@ -196,7 +196,7 @@ Discourse.KeyboardShortcuts = Ember.Object.createWithMixins({
var selectedPostId = parseInt($('.topic-post.selected article.boxed').data('post-id'), 10);
if (selectedPostId) {
var topicController = container.lookup('controller:topic'),
post = topicController.get('postStream.posts').findBy('id', selectedPostId);
post = topicController.get('model.postStream.posts').findBy('id', selectedPostId);
if (post) {
topicController.send(action, post);
}

View File

@ -221,7 +221,7 @@ Discourse.URL = Ember.Object.createWithMixins({
var container = Discourse.__container__,
topicController = container.lookup('controller:topic'),
opts = {},
postStream = topicController.get('postStream');
postStream = topicController.get('model.postStream');
if (newMatches[3]) opts.nearPost = newMatches[3];
if (path.match(/last$/)) { opts.nearPost = topicController.get('highest_post_number'); }
@ -295,7 +295,7 @@ Discourse.URL = Ember.Object.createWithMixins({
**/
router: function() {
return Discourse.__container__.lookup('router:main');
}.property(),
}.property().volatile(),
/**
@private

View File

@ -15,7 +15,7 @@ export default {
if (categoryFullSlug) {
$('body').addClass('category-' + categoryFullSlug);
}
}.observes('categoryFullSlug'),
}.observes('categoryFullSlug').on('init'),
_leaveView: function() { this._removeClasses(); }.on('willDestroyElement')
};

View File

@ -60,7 +60,7 @@ Discourse.Ajax = Em.Mixin.create({
Ember.run(null, resolve, data);
};
args.error = function(xhr, textStatus) {
args.error = function(xhr, textStatus, errorThrown) {
// note: for bad CSRF we don't loop an extra request right away.
// this allows us to eliminate the possibility of having a loop.
if (xhr.status === 403 && xhr.responseText === "['BAD CSRF']") {
@ -74,7 +74,11 @@ Discourse.Ajax = Em.Mixin.create({
xhr.jqTextStatus = textStatus;
xhr.requestedUrl = url;
Ember.run(null, reject, xhr);
Ember.run(null, reject, {
jqXHR: xhr,
textStatus: textStatus,
errorThrown: errorThrown
});
};
// We default to JSON on GET. If we don't, sometimes if the server doesn't return the proper header

View File

@ -1,6 +1,6 @@
export default Ember.Mixin.create({
isOwnEmail: Discourse.computed.propertyEqual("id", "currentUser.id"),
showEmailOnProfile: Discourse.computed.setting("show_email_on_profile"),
isOwnEmail: Discourse.computed.propertyEqual("model.id", "currentUser.id"),
showEmailOnProfile: Discourse.computed.setting("model.show_email_on_profile"),
canStaffCheckEmails: Em.computed.and("showEmailOnProfile", "currentUser.staff"),
canAdminCheckEmails: Em.computed.alias("currentUser.admin"),
canCheckEmails: Em.computed.or("isOwnEmail", "canStaffCheckEmails", "canAdminCheckEmails"),

Some files were not shown because too many files have changed in this diff Show More