diff --git a/Gemfile.lock b/Gemfile.lock
index 53e5f44644f..59aaea207cc 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -62,7 +62,7 @@ GEM
builder (3.2.2)
byebug (5.0.0)
columnize (= 0.9.0)
- celluloid (0.16.1)
+ celluloid (0.16.0)
timers (~> 4.0.0)
certified (1.0.0)
coderay (1.1.0)
@@ -485,6 +485,3 @@ DEPENDENCIES
uglifier
unf
unicorn
-
-BUNDLED WITH
- 1.10.6
diff --git a/app/assets/javascripts/admin/controllers/admin-user-badges.js.es6 b/app/assets/javascripts/admin/controllers/admin-user-badges.js.es6
index e66d821621d..b85fbaf069d 100644
--- a/app/assets/javascripts/admin/controllers/admin-user-badges.js.es6
+++ b/app/assets/javascripts/admin/controllers/admin-user-badges.js.es6
@@ -1,3 +1,5 @@
+import UserBadge from 'discourse/models/user-badge';
+
export default Ember.ArrayController.extend({
needs: ["adminUser"],
user: Em.computed.alias('controllers.adminUser.model'),
@@ -86,7 +88,7 @@ export default Ember.ArrayController.extend({
**/
grantBadge: function(badgeId) {
var self = this;
- Discourse.UserBadge.grant(badgeId, this.get('user.username'), this.get('badgeReason')).then(function(userBadge) {
+ UserBadge.grant(badgeId, this.get('user.username'), this.get('badgeReason')).then(function(userBadge) {
self.set('badgeReason', '');
self.pushObject(userBadge);
Ember.run.next(function() {
@@ -102,12 +104,6 @@ export default Ember.ArrayController.extend({
});
},
- /**
- Revoke the selected userBadge.
-
- @method revokeBadge
- @param {Discourse.UserBadge} userBadge the `Discourse.UserBadge` instance that needs to be revoked.
- **/
revokeBadge: function(userBadge) {
var self = this;
return bootbox.confirm(I18n.t("admin.badges.revoke_confirm"), I18n.t("no_value"), I18n.t("yes_value"), function(result) {
diff --git a/app/assets/javascripts/admin/routes/admin-badges-show.js.es6 b/app/assets/javascripts/admin/routes/admin-badges-show.js.es6
index 648730d9b0a..c283ff737b0 100644
--- a/app/assets/javascripts/admin/routes/admin-badges-show.js.es6
+++ b/app/assets/javascripts/admin/routes/admin-badges-show.js.es6
@@ -1,3 +1,4 @@
+import Badge from 'discourse/models/badge';
import showModal from 'discourse/lib/show-modal';
export default Ember.Route.extend({
@@ -7,7 +8,7 @@ export default Ember.Route.extend({
model(params) {
if (params.badge_id === "new") {
- return Discourse.Badge.create({
+ return Badge.create({
name: I18n.t('admin.badges.new_badge')
});
}
diff --git a/app/assets/javascripts/admin/routes/admin-badges.js.es6 b/app/assets/javascripts/admin/routes/admin-badges.js.es6
index 3d417f53c2f..59274376699 100644
--- a/app/assets/javascripts/admin/routes/admin-badges.js.es6
+++ b/app/assets/javascripts/admin/routes/admin-badges.js.es6
@@ -1,3 +1,5 @@
+import Badge from 'discourse/models/badge';
+
export default Discourse.Route.extend({
_json: null,
@@ -5,7 +7,7 @@ export default Discourse.Route.extend({
var self = this;
return Discourse.ajax('/admin/badges.json').then(function(json) {
self._json = json;
- return Discourse.Badge.createFromJson(json);
+ return Badge.createFromJson(json);
});
},
diff --git a/app/assets/javascripts/admin/routes/admin-user-badges.js.es6 b/app/assets/javascripts/admin/routes/admin-user-badges.js.es6
new file mode 100644
index 00000000000..205c1647c9e
--- /dev/null
+++ b/app/assets/javascripts/admin/routes/admin-user-badges.js.es6
@@ -0,0 +1,26 @@
+import UserBadge from 'discourse/models/user-badge';
+import Badge from 'discourse/models/badge';
+
+export default Discourse.Route.extend({
+ model() {
+ const username = this.modelFor('adminUser').get('username');
+ return UserBadge.findByUsername(username);
+ },
+
+ setupController(controller, model) {
+ // Find all badges.
+ controller.set('loading', true);
+ Badge.findAll().then(function(badges) {
+ controller.set('badges', badges);
+ if (badges.length > 0) {
+ var grantableBadges = controller.get('grantableBadges');
+ if (grantableBadges.length > 0) {
+ controller.set('selectedBadgeId', grantableBadges[0].get('id'));
+ }
+ }
+ controller.set('loading', false);
+ });
+ // Set the model.
+ controller.set('model', model);
+ }
+});
diff --git a/app/assets/javascripts/admin/routes/admin_user_badges_route.js b/app/assets/javascripts/admin/routes/admin_user_badges_route.js
deleted file mode 100644
index f681e6f9192..00000000000
--- a/app/assets/javascripts/admin/routes/admin_user_badges_route.js
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- Shows all of the badges that have been granted to a user, and allow granting and
- revoking badges.
-
- @class AdminUserBadgesRoute
- @extends Discourse.Route
- @namespace Discourse
- @module Discourse
-**/
-Discourse.AdminUserBadgesRoute = Discourse.Route.extend({
- model: function() {
- var username = this.modelFor('adminUser').get('username');
- return Discourse.UserBadge.findByUsername(username);
- },
-
- setupController: function(controller, model) {
- // Find all badges.
- controller.set('loading', true);
- Discourse.Badge.findAll().then(function(badges) {
- controller.set('badges', badges);
- if (badges.length > 0) {
- var grantableBadges = controller.get('grantableBadges');
- if (grantableBadges.length > 0) {
- controller.set('selectedBadgeId', grantableBadges[0].get('id'));
- }
- }
- controller.set('loading', false);
- });
- // Set the model.
- controller.set('model', model);
- }
-});
diff --git a/app/assets/javascripts/discourse.js b/app/assets/javascripts/discourse.js
index 02b2bad0767..e12c8243d10 100644
--- a/app/assets/javascripts/discourse.js
+++ b/app/assets/javascripts/discourse.js
@@ -157,9 +157,6 @@ window.Discourse = Ember.Application.createWithMixins(Discourse.Ajax, {
})
});
-// TODO: Remove this, it is in for backwards compatibiltiy with plugins
-Discourse.HasCurrentUser = {};
-
function proxyDep(propName, moduleFunc, msg) {
if (Discourse.hasOwnProperty(propName)) { return; }
Object.defineProperty(Discourse, propName, {
diff --git a/app/assets/javascripts/discourse/components/d-button.js.es6 b/app/assets/javascripts/discourse/components/d-button.js.es6
index 45fe5effce1..5fcc00a0fc1 100644
--- a/app/assets/javascripts/discourse/components/d-button.js.es6
+++ b/app/assets/javascripts/discourse/components/d-button.js.es6
@@ -1,4 +1,5 @@
import { iconHTML } from 'discourse/helpers/fa-icon';
+import computed from 'ember-addons/ember-computed-decorators';
export default Ember.Component.extend({
tagName: 'button',
@@ -7,17 +8,15 @@ export default Ember.Component.extend({
noText: Ember.computed.empty('translatedLabel'),
- translatedTitle: function() {
- const title = this.get('title');
- return title ? I18n.t(title) : this.get('translatedLabel');
- }.property('title', 'translatedLabel'),
+ @computed("title", "translatedLabel")
+ translatedTitle(title, translatedLabel) {
+ return title ? I18n.t(title) : translatedLabel;
+ },
- translatedLabel: function() {
- const label = this.get('label');
- if (label) {
- return I18n.t(this.get('label'));
- }
- }.property('label'),
+ @computed("label")
+ translatedLabel(label) {
+ if (label) return I18n.t(label);
+ },
render(buffer) {
const label = this.get('translatedLabel'),
diff --git a/app/assets/javascripts/discourse/controllers/badges/show.js.es6 b/app/assets/javascripts/discourse/controllers/badges/show.js.es6
index 1083fd37f11..a5a899f6494 100644
--- a/app/assets/javascripts/discourse/controllers/badges/show.js.es6
+++ b/app/assets/javascripts/discourse/controllers/badges/show.js.es6
@@ -1,3 +1,5 @@
+import UserBadge from 'discourse/models/user-badge';
+
export default Ember.Controller.extend({
noMoreBadges: false,
userBadges: null,
@@ -8,7 +10,7 @@ export default Ember.Controller.extend({
const self = this;
const userBadges = this.get('userBadges');
- Discourse.UserBadge.findByBadgeId(this.get('model.id'), {
+ UserBadge.findByBadgeId(this.get('model.id'), {
offset: userBadges.length
}).then(function(result) {
userBadges.pushObjects(result);
diff --git a/app/assets/javascripts/discourse/controllers/history.js.es6 b/app/assets/javascripts/discourse/controllers/history.js.es6
index 4a04abb1af7..b0588750f95 100644
--- a/app/assets/javascripts/discourse/controllers/history.js.es6
+++ b/app/assets/javascripts/discourse/controllers/history.js.es6
@@ -1,6 +1,7 @@
import ModalFunctionality from 'discourse/mixins/modal-functionality';
import { categoryBadgeHTML } from 'discourse/helpers/category-link';
import computed from 'ember-addons/ember-computed-decorators';
+import { propertyGreaterThan, propertyLessThan } from 'discourse/lib/computed';
// This controller handles displaying of history
export default Ember.Controller.extend(ModalFunctionality, {
@@ -15,30 +16,28 @@ export default Ember.Controller.extend(ModalFunctionality, {
refresh(postId, postVersion) {
this.set("loading", true);
- var self = this;
- Discourse.Post.loadRevision(postId, postVersion).then(function (result) {
- self.setProperties({ loading: false, model: result });
+ Discourse.Post.loadRevision(postId, postVersion).then(result => {
+ this.setProperties({ loading: false, model: result });
});
},
hide(postId, postVersion) {
- var self = this;
- Discourse.Post.hideRevision(postId, postVersion).then(function () {
- self.refresh(postId, postVersion);
- });
+ Discourse.Post.hideRevision(postId, postVersion).then(() => this.refresh(postId, postVersion));
},
show(postId, postVersion) {
- var self = this;
- Discourse.Post.showRevision(postId, postVersion).then(function () {
- self.refresh(postId, postVersion);
- });
+ Discourse.Post.showRevision(postId, postVersion).then(() => this.refresh(postId, postVersion));
},
- createdAtDate: function() { return moment(this.get("created_at")).format("LLLL"); }.property("created_at"),
+ @computed('model.created_at')
+ createdAtDate(createdAt) {
+ return moment(createdAt).format("LLLL");
+ },
@computed('model.current_version')
- previousVersion(current) { return current - 1; },
+ previousVersion(current) {
+ return current - 1;
+ },
@computed('model.current_revision', 'model.previous_revision')
displayGoToPrevious(current, prev) {
@@ -46,18 +45,28 @@ export default Ember.Controller.extend(ModalFunctionality, {
},
displayRevisions: Ember.computed.gt("model.version_count", 2),
- displayGoToFirst: Ember.computed.gt('model.current_revision', 'model.first_revision'),
- displayGoToNext: Ember.computed.lt("model.current_revision", "model.next_revision"),
- displayGoToLast: Ember.computed.lt("model.current_revision", "model.next_revision"),
+ displayGoToFirst: propertyGreaterThan("model.current_revision", "model.first_revision"),
+ displayGoToNext: propertyLessThan("model.current_revision", "model.next_revision"),
+ displayGoToLast: propertyLessThan("model.current_revision", "model.next_revision"),
- @computed('model.previous_hidden', 'loading')
- displayShow: function(prevHidden, loading) {
- return prevHidden && this.currentUser.get('staff') && !loading;
+ hideGoToFirst: Ember.computed.not("displayGoToFirst"),
+ hideGoToPrevious: Ember.computed.not("displayGoToPrevious"),
+ hideGoToNext: Ember.computed.not("displayGoToNext"),
+ hideGoToLast: Ember.computed.not("displayGoToLast"),
+
+ loadFirstDisabled: Ember.computed.or("loading", "hideGoToFirst"),
+ loadPreviousDisabled: Ember.computed.or("loading", "hideGoToPrevious"),
+ loadNextDisabled: Ember.computed.or("loading", "hideGoToNext"),
+ loadLastDisabled: Ember.computed.or("loading", "hideGoToLast"),
+
+ @computed('model.previous_hidden')
+ displayShow(prevHidden) {
+ return prevHidden && this.currentUser.get('staff');
},
- @computed('model.previous_hidden', 'loading')
- displayHide: function(prevHidden, loading) {
- return !prevHidden && this.currentUser.get('staff') && !loading;
+ @computed('model.previous_hidden')
+ displayHide(prevHidden) {
+ return !prevHidden && this.currentUser.get('staff');
},
isEitherRevisionHidden: Ember.computed.or("model.previous_hidden", "model.current_hidden"),
@@ -78,6 +87,15 @@ export default Ember.Controller.extend(ModalFunctionality, {
displayingSideBySide: Em.computed.equal("viewMode", "side_by_side"),
displayingSideBySideMarkdown: Em.computed.equal("viewMode", "side_by_side_markdown"),
+ @computed("displayingInline")
+ inlineClass(displayingInline) { return displayingInline ? "btn-primary" : ""; },
+
+ @computed("displayingSideBySide")
+ sideBySideClass(displayingSideBySide) { return displayingSideBySide ? "btn-primary" : ""; },
+
+ @computed("displayingSideBySideMarkdown")
+ sideBySideMarkdownClass(displayingSideBySideMarkdown) { return displayingSideBySideMarkdown ? "btn-primary" : ""; },
+
@computed('model.category_id_changes')
previousCategory(changes) {
if (changes) {
@@ -116,16 +134,16 @@ export default Ember.Controller.extend(ModalFunctionality, {
},
actions: {
- loadFirstVersion: function() { this.refresh(this.get("model.post_id"), this.get("model.first_revision")); },
- loadPreviousVersion: function() { this.refresh(this.get("model.post_id"), this.get("model.previous_revision")); },
- loadNextVersion: function() { this.refresh(this.get("model.post_id"), this.get("model.next_revision")); },
- loadLastVersion: function() { this.refresh(this.get("model.post_id"), this.get("model.last_revision")); },
+ loadFirstVersion() { this.refresh(this.get("model.post_id"), this.get("model.first_revision")); },
+ loadPreviousVersion() { this.refresh(this.get("model.post_id"), this.get("model.previous_revision")); },
+ loadNextVersion() { this.refresh(this.get("model.post_id"), this.get("model.next_revision")); },
+ loadLastVersion() { this.refresh(this.get("model.post_id"), this.get("model.last_revision")); },
- hideVersion: function() { this.hide(this.get("model.post_id"), this.get("model.current_revision")); },
- showVersion: function() { this.show(this.get("model.post_id"), this.get("model.current_revision")); },
+ hideVersion() { this.hide(this.get("model.post_id"), this.get("model.current_revision")); },
+ showVersion() { this.show(this.get("model.post_id"), this.get("model.current_revision")); },
- displayInline: function() { this.set("viewMode", "inline"); },
- displaySideBySide: function() { this.set("viewMode", "side_by_side"); },
- displaySideBySideMarkdown: function() { this.set("viewMode", "side_by_side_markdown"); }
+ displayInline() { this.set("viewMode", "inline"); },
+ displaySideBySide() { this.set("viewMode", "side_by_side"); },
+ displaySideBySideMarkdown() { this.set("viewMode", "side_by_side_markdown"); }
}
});
diff --git a/app/assets/javascripts/discourse/lib/computed.js.es6 b/app/assets/javascripts/discourse/lib/computed.js.es6
index b1b7186b0e8..1fb7e745cb5 100644
--- a/app/assets/javascripts/discourse/lib/computed.js.es6
+++ b/app/assets/javascripts/discourse/lib/computed.js.es6
@@ -26,6 +26,18 @@ export function propertyNotEqual(p1, p2) {
}).property(p1, p2);
}
+export function propertyGreaterThan(p1, p2) {
+ return Ember.computed(function() {
+ return this.get(p1) > this.get(p2);
+ }).property(p1, p2);
+}
+
+export function propertyLessThan(p1, p2) {
+ return Ember.computed(function() {
+ return this.get(p1) < this.get(p2);
+ }).property(p1, p2);
+}
+
/**
Returns i18n version of a string based on a property.
diff --git a/app/assets/javascripts/discourse/mixins/badge-select-controller.js.es6 b/app/assets/javascripts/discourse/mixins/badge-select-controller.js.es6
index ea800461e85..5c57ffa425f 100644
--- a/app/assets/javascripts/discourse/mixins/badge-select-controller.js.es6
+++ b/app/assets/javascripts/discourse/mixins/badge-select-controller.js.es6
@@ -1,12 +1,14 @@
+import Badge from 'discourse/models/badge';
+
export default Ember.Mixin.create({
saving: false,
saved: false,
selectableUserBadges: function() {
- var items = this.get('filteredList');
+ let items = this.get('filteredList');
items = _.uniq(items, false, function(e) { return e.get('badge.name'); });
items.unshiftObject(Em.Object.create({
- badge: Discourse.Badge.create({name: I18n.t('badges.none')})
+ badge: Badge.create({name: I18n.t('badges.none')})
}));
return items;
}.property('filteredList'),
@@ -20,8 +22,8 @@ export default Ember.Mixin.create({
}.property('saving'),
selectedUserBadge: function() {
- var selectedUserBadgeId = parseInt(this.get('selectedUserBadgeId'));
- var selectedUserBadge = null;
+ const selectedUserBadgeId = parseInt(this.get('selectedUserBadgeId'));
+ let selectedUserBadge = null;
this.get('selectableUserBadges').forEach(function(userBadge) {
if (userBadge.get('id') === selectedUserBadgeId) {
selectedUserBadge = userBadge;
diff --git a/app/assets/javascripts/discourse/models/badge-grouping.js.es6 b/app/assets/javascripts/discourse/models/badge-grouping.js.es6
new file mode 100644
index 00000000000..605d4d6d2c9
--- /dev/null
+++ b/app/assets/javascripts/discourse/models/badge-grouping.js.es6
@@ -0,0 +1,16 @@
+import computed from 'ember-addons/ember-computed-decorators';
+import RestModel from 'discourse/models/rest';
+
+export default RestModel.extend({
+
+ @computed('name')
+ i18nNameKey() {
+ return this.get('name').toLowerCase().replace(/\s/g, '_');
+ },
+
+ @computed
+ displayName() {
+ const i18nKey = `badges.badge_grouping.${this.get('i18nNameKey')}.name`;
+ return I18n.t(i18nKey, {defaultValue: this.get('name')});
+ }
+});
diff --git a/app/assets/javascripts/discourse/models/badge.js b/app/assets/javascripts/discourse/models/badge.js.es6
similarity index 83%
rename from app/assets/javascripts/discourse/models/badge.js
rename to app/assets/javascripts/discourse/models/badge.js.es6
index 5ae1b6195a1..670a84111c5 100644
--- a/app/assets/javascripts/discourse/models/badge.js
+++ b/app/assets/javascripts/discourse/models/badge.js.es6
@@ -1,22 +1,12 @@
-/**
- A data model representing a badge on Discourse
+import BadgeGrouping from 'discourse/models/badge-grouping';
+import RestModel from 'discourse/models/rest';
- @class Badge
- @extends Discourse.Model
- @namespace Discourse
- @module Discourse
-**/
-Discourse.Badge = Discourse.Model.extend({
- /**
- Is this a new badge?
+const Badge = RestModel.extend({
- @property newBadge
- @type {String}
- **/
newBadge: Em.computed.none('id'),
hasQuery: function(){
- var query = this.get('query');
+ const query = this.get('query');
return query && query.trim().length > 0;
}.property('query'),
@@ -40,7 +30,7 @@ Discourse.Badge = Discourse.Model.extend({
@type {String}
**/
displayName: function() {
- var i18nKey = "badges.badge." + this.get('i18nNameKey') + ".name";
+ const i18nKey = "badges.badge." + this.get('i18nNameKey') + ".name";
return I18n.t(i18nKey, {defaultValue: this.get('name')});
}.property('name', 'i18nNameKey'),
@@ -52,8 +42,8 @@ Discourse.Badge = Discourse.Model.extend({
@type {String}
**/
translatedDescription: function() {
- var i18nKey = "badges.badge." + this.get('i18nNameKey') + ".description",
- translation = I18n.t(i18nKey);
+ const i18nKey = "badges.badge." + this.get('i18nNameKey') + ".description";
+ let translation = I18n.t(i18nKey);
if (translation.indexOf(i18nKey) !== -1) {
translation = null;
}
@@ -73,7 +63,7 @@ Discourse.Badge = Discourse.Model.extend({
@type {String}
**/
displayDescriptionHtml: function() {
- var translated = this.get('translatedDescription');
+ const translated = this.get('translatedDescription');
return (translated === null ? this.get('description') : translated) || "";
}.property('description', 'translatedDescription'),
@@ -84,7 +74,7 @@ Discourse.Badge = Discourse.Model.extend({
@param {Object} json The JSON response returned by the server
**/
updateFromJson: function(json) {
- var self = this;
+ const self = this;
if (json.badge) {
Object.keys(json.badge).forEach(function(key) {
self.set(key, json.badge[key]);
@@ -100,7 +90,7 @@ Discourse.Badge = Discourse.Model.extend({
},
badgeTypeClassName: function() {
- var type = this.get('badge_type.name') || "";
+ const type = this.get('badge_type.name') || "";
return "badge-type-" + type.toLowerCase();
}.property('badge_type.name'),
@@ -111,9 +101,9 @@ Discourse.Badge = Discourse.Model.extend({
@returns {Promise} A promise that resolves to the updated `Discourse.Badge`
**/
save: function(data) {
- var url = "/admin/badges",
- requestType = "POST",
- self = this;
+ let url = "/admin/badges",
+ requestType = "POST";
+ const self = this;
if (this.get('id')) {
// We are updating an existing badge.
@@ -146,7 +136,7 @@ Discourse.Badge = Discourse.Model.extend({
}
});
-Discourse.Badge.reopenClass({
+Badge.reopenClass({
/**
Create `Discourse.Badge` instances from the server JSON response.
@@ -156,29 +146,29 @@ Discourse.Badge.reopenClass({
**/
createFromJson: function(json) {
// Create BadgeType objects.
- var badgeTypes = {};
+ const badgeTypes = {};
if ('badge_types' in json) {
json.badge_types.forEach(function(badgeTypeJson) {
badgeTypes[badgeTypeJson.id] = Ember.Object.create(badgeTypeJson);
});
}
- var badgeGroupings = {};
+ const badgeGroupings = {};
if ('badge_groupings' in json) {
json.badge_groupings.forEach(function(badgeGroupingJson) {
- badgeGroupings[badgeGroupingJson.id] = Discourse.BadgeGrouping.create(badgeGroupingJson);
+ badgeGroupings[badgeGroupingJson.id] = BadgeGrouping.create(badgeGroupingJson);
});
}
// Create Badge objects.
- var badges = [];
+ let badges = [];
if ("badge" in json) {
badges = [json.badge];
} else {
badges = json.badges;
}
badges = badges.map(function(badgeJson) {
- var badge = Discourse.Badge.create(badgeJson);
+ const badge = Discourse.Badge.create(badgeJson);
badge.set('badge_type', badgeTypes[badge.get('badge_type_id')]);
badge.set('badge_grouping', badgeGroupings[badge.get('badge_grouping_id')]);
return badge;
@@ -198,7 +188,7 @@ Discourse.Badge.reopenClass({
@returns {Promise} a promise that resolves to an array of `Discourse.Badge`
**/
findAll: function(opts) {
- var listable = "";
+ let listable = "";
if(opts && opts.onlyListable){
listable = "?only_listable=true";
}
@@ -220,3 +210,6 @@ Discourse.Badge.reopenClass({
});
}
});
+
+export default Badge;
+
diff --git a/app/assets/javascripts/discourse/models/badge_grouping.js b/app/assets/javascripts/discourse/models/badge_grouping.js
deleted file mode 100644
index dd59d078fab..00000000000
--- a/app/assets/javascripts/discourse/models/badge_grouping.js
+++ /dev/null
@@ -1,10 +0,0 @@
-Discourse.BadgeGrouping= Discourse.Model.extend({
- i18nNameKey: function() {
- return this.get('name').toLowerCase().replace(/\s/g, '_');
- }.property('name'),
-
- displayName: function(){
- var i18nKey = "badges.badge_grouping." + this.get('i18nNameKey') + ".name";
- return I18n.t(i18nKey, {defaultValue: this.get('name')});
- }.property()
-});
diff --git a/app/assets/javascripts/discourse/models/composer.js.es6 b/app/assets/javascripts/discourse/models/composer.js.es6
index d9805f630a9..ae8f324b91f 100644
--- a/app/assets/javascripts/discourse/models/composer.js.es6
+++ b/app/assets/javascripts/discourse/models/composer.js.es6
@@ -504,7 +504,9 @@ const Composer = RestModel.extend({
return post.save(props).then(function(result) {
self.clearState();
return result;
- }).catch(rollback);
+ }).catch(function(error) {
+ throw error;
+ });
}).catch(rollback);
},
diff --git a/app/assets/javascripts/discourse/models/post.js.es6 b/app/assets/javascripts/discourse/models/post.js.es6
index c8c4b400ffd..51e586014ea 100644
--- a/app/assets/javascripts/discourse/models/post.js.es6
+++ b/app/assets/javascripts/discourse/models/post.js.es6
@@ -162,7 +162,9 @@ const Post = RestModel.extend({
// Recover a deleted post
recover() {
- const post = this;
+ const post = this,
+ initProperties = post.getProperties('deleted_at', 'deleted_by', 'user_deleted', 'can_delete');
+
post.setProperties({
deleted_at: null,
deleted_by: null,
@@ -178,6 +180,9 @@ const Post = RestModel.extend({
can_delete: true,
version: data.version
});
+ }).catch(function(error) {
+ popupAjaxError(error);
+ post.setProperties(initProperties);
});
},
@@ -220,6 +225,7 @@ const Post = RestModel.extend({
cooked: this.get('oldCooked'),
version: this.get('version') - 1,
can_recover: false,
+ can_delete: true,
user_deleted: false
});
}
diff --git a/app/assets/javascripts/discourse/models/topic.js.es6 b/app/assets/javascripts/discourse/models/topic.js.es6
index 0fab68fa108..07378493461 100644
--- a/app/assets/javascripts/discourse/models/topic.js.es6
+++ b/app/assets/javascripts/discourse/models/topic.js.es6
@@ -157,8 +157,12 @@ const Topic = RestModel.extend({
},
saveStatus(property, value, until) {
- if (property === 'closed' && value === true) {
- this.set('details.auto_close_at', null);
+ if (property === 'closed') {
+ this.incrementProperty('posts_count');
+
+ if (value === true) {
+ this.set('details.auto_close_at', null);
+ }
}
return Discourse.ajax(this.get('url') + "/status", {
type: 'PUT',
diff --git a/app/assets/javascripts/discourse/models/user_badge.js b/app/assets/javascripts/discourse/models/user-badge.js.es6
similarity index 86%
rename from app/assets/javascripts/discourse/models/user_badge.js
rename to app/assets/javascripts/discourse/models/user-badge.js.es6
index 158e70e46c3..f5e2209f95a 100644
--- a/app/assets/javascripts/discourse/models/user_badge.js
+++ b/app/assets/javascripts/discourse/models/user-badge.js.es6
@@ -1,12 +1,6 @@
-/**
- A data model representing a user badge grant on Discourse
+import Badge from 'discourse/models/badge';
- @class UserBadge
- @extends Discourse.Model
- @namespace Discourse
- @module Discourse
-**/
-Discourse.UserBadge = Discourse.Model.extend({
+const UserBadge = Discourse.Model.extend({
postUrl: function() {
if(this.get('topic_title')) {
return "/t/-/" + this.get('topic_id') + "/" + this.get('post_number');
@@ -25,14 +19,8 @@ Discourse.UserBadge = Discourse.Model.extend({
}
});
-Discourse.UserBadge.reopenClass({
- /**
- Create `Discourse.UserBadge` instances from the server JSON response.
+UserBadge.reopenClass({
- @method createFromJson
- @param {Object} json The JSON returned by the server
- @returns Array or instance of `Discourse.UserBadge` depending on the input JSON
- **/
createFromJson: function(json) {
// Create User objects.
if (json.users === undefined) { json.users = []; }
@@ -51,7 +39,7 @@ Discourse.UserBadge.reopenClass({
// Create the badges.
if (json.badges === undefined) { json.badges = []; }
var badges = {};
- Discourse.Badge.createFromJson(json).forEach(function(badge) {
+ Badge.createFromJson(json).forEach(function(badge) {
badges[badge.get('id')] = badge;
});
@@ -146,3 +134,5 @@ Discourse.UserBadge.reopenClass({
});
}
});
+
+export default UserBadge;
diff --git a/app/assets/javascripts/discourse/models/user.js.es6 b/app/assets/javascripts/discourse/models/user.js.es6
index e270aaff7a8..079c508286a 100644
--- a/app/assets/javascripts/discourse/models/user.js.es6
+++ b/app/assets/javascripts/discourse/models/user.js.es6
@@ -6,6 +6,8 @@ import UserPostsStream from 'discourse/models/user-posts-stream';
import Singleton from 'discourse/mixins/singleton';
import { longDate } from 'discourse/lib/formatter';
import computed from 'ember-addons/ember-computed-decorators';
+import Badge from 'discourse/models/badge';
+import UserBadge from 'discourse/models/user-badge';
const User = RestModel.extend({
@@ -299,8 +301,8 @@ const User = RestModel.extend({
}
if (!Em.isEmpty(json.user.featured_user_badge_ids)) {
- var userBadgesMap = {};
- Discourse.UserBadge.createFromJson(json).forEach(function(userBadge) {
+ const userBadgesMap = {};
+ UserBadge.createFromJson(json).forEach(function(userBadge) {
userBadgesMap[ userBadge.get('id') ] = userBadge;
});
json.user.featured_user_badges = json.user.featured_user_badge_ids.map(function(id) {
@@ -309,7 +311,7 @@ const User = RestModel.extend({
}
if (json.user.card_badge) {
- json.user.card_badge = Discourse.Badge.create(json.user.card_badge);
+ json.user.card_badge = Badge.create(json.user.card_badge);
}
user.setProperties(json.user);
diff --git a/app/assets/javascripts/discourse/routes/badges-index.js.es6 b/app/assets/javascripts/discourse/routes/badges-index.js.es6
index dfb207391f6..683033faa98 100644
--- a/app/assets/javascripts/discourse/routes/badges-index.js.es6
+++ b/app/assets/javascripts/discourse/routes/badges-index.js.es6
@@ -1,9 +1,11 @@
+import Badge from 'discourse/models/badge';
+
export default Discourse.Route.extend({
model() {
if (PreloadStore.get("badges")) {
- return PreloadStore.getAndRemove("badges").then(json => Discourse.Badge.createFromJson(json));
+ return PreloadStore.getAndRemove("badges").then(json => Badge.createFromJson(json));
} else {
- return Discourse.Badge.findAll({ onlyListable: true });
+ return Badge.findAll({ onlyListable: true });
}
},
diff --git a/app/assets/javascripts/discourse/routes/badges-show.js.es6 b/app/assets/javascripts/discourse/routes/badges-show.js.es6
index cc4cf0eaa98..5f835d76d00 100644
--- a/app/assets/javascripts/discourse/routes/badges-show.js.es6
+++ b/app/assets/javascripts/discourse/routes/badges-show.js.es6
@@ -1,3 +1,6 @@
+import UserBadge from 'discourse/models/user-badge';
+import Badge from 'discourse/models/badge';
+
export default Discourse.Route.extend({
actions: {
didTransition() {
@@ -15,14 +18,14 @@ export default Discourse.Route.extend({
model(params) {
if (PreloadStore.get("badge")) {
- return PreloadStore.getAndRemove("badge").then(json => Discourse.Badge.createFromJson(json));
+ return PreloadStore.getAndRemove("badge").then(json => Badge.createFromJson(json));
} else {
- return Discourse.Badge.findById(params.id);
+ return Badge.findById(params.id);
}
},
afterModel(model) {
- return Discourse.UserBadge.findByBadgeId(model.get("id")).then(userBadges => {
+ return UserBadge.findByBadgeId(model.get("id")).then(userBadges => {
this.userBadges = userBadges;
});
},
diff --git a/app/assets/javascripts/discourse/routes/discovery-categories-route.js.es6 b/app/assets/javascripts/discourse/routes/discovery-categories.js.es6
similarity index 94%
rename from app/assets/javascripts/discourse/routes/discovery-categories-route.js.es6
rename to app/assets/javascripts/discourse/routes/discovery-categories.js.es6
index 61fd853d109..5bbd7aa66a4 100644
--- a/app/assets/javascripts/discourse/routes/discovery-categories-route.js.es6
+++ b/app/assets/javascripts/discourse/routes/discovery-categories.js.es6
@@ -1,7 +1,7 @@
import showModal from "discourse/lib/show-modal";
import OpenComposer from "discourse/mixins/open-composer";
-Discourse.DiscoveryCategoriesRoute = Discourse.Route.extend(OpenComposer, {
+const DiscoveryCategoriesRoute = Discourse.Route.extend(OpenComposer, {
renderTemplate() {
this.render("navigation/categories", { outlet: "navigation-bar" });
this.render("discovery/categories", { outlet: "list-container" });
@@ -67,4 +67,4 @@ Discourse.DiscoveryCategoriesRoute = Discourse.Route.extend(OpenComposer, {
}
});
-export default Discourse.DiscoveryCategoriesRoute;
+export default DiscoveryCategoriesRoute;
diff --git a/app/assets/javascripts/discourse/routes/preferences-badge-title.js.es6 b/app/assets/javascripts/discourse/routes/preferences-badge-title.js.es6
index 0c7a22d8b40..141a852b999 100644
--- a/app/assets/javascripts/discourse/routes/preferences-badge-title.js.es6
+++ b/app/assets/javascripts/discourse/routes/preferences-badge-title.js.es6
@@ -1,8 +1,9 @@
+import UserBadge from 'discourse/models/badge';
import RestrictedUserRoute from "discourse/routes/restricted-user";
export default RestrictedUserRoute.extend({
model: function() {
- return Discourse.UserBadge.findByUsername(this.modelFor('user').get('username'));
+ return UserBadge.findByUsername(this.modelFor('user').get('username'));
},
renderTemplate: function() {
diff --git a/app/assets/javascripts/discourse/routes/preferences-card-badge.js.es6 b/app/assets/javascripts/discourse/routes/preferences-card-badge.js.es6
index 37ad604ec06..8ec81a95d1c 100644
--- a/app/assets/javascripts/discourse/routes/preferences-card-badge.js.es6
+++ b/app/assets/javascripts/discourse/routes/preferences-card-badge.js.es6
@@ -1,8 +1,9 @@
+import UserBadge from 'discourse/models/user-badge';
import RestrictedUserRoute from "discourse/routes/restricted-user";
export default RestrictedUserRoute.extend({
model: function() {
- return Discourse.UserBadge.findByUsername(this.modelFor('user').get('username'));
+ return UserBadge.findByUsername(this.modelFor('user').get('username'));
},
renderTemplate: function() {
diff --git a/app/assets/javascripts/discourse/routes/user-badges.js.es6 b/app/assets/javascripts/discourse/routes/user-badges.js.es6
index fcd099d7650..9a67afbb920 100644
--- a/app/assets/javascripts/discourse/routes/user-badges.js.es6
+++ b/app/assets/javascripts/discourse/routes/user-badges.js.es6
@@ -1,8 +1,9 @@
import ViewingActionType from "discourse/mixins/viewing-action-type";
+import UserBadge from 'discourse/models/user-badge';
export default Discourse.Route.extend(ViewingActionType, {
model() {
- return Discourse.UserBadge.findByUsername(this.modelFor("user").get("username_lower"), { grouped: true });
+ return UserBadge.findByUsername(this.modelFor("user").get("username_lower"), { grouped: true });
},
setupController(controller, model) {
diff --git a/app/assets/javascripts/discourse/templates/modal/history.hbs b/app/assets/javascripts/discourse/templates/modal/history.hbs
index cd790800c1a..bcf20fbc1ca 100644
--- a/app/assets/javascripts/discourse/templates/modal/history.hbs
+++ b/app/assets/javascripts/discourse/templates/modal/history.hbs
@@ -1,27 +1,27 @@
-
-
+ {{d-button action="loadFirstVersion" icon="fast-backward" title="post.revisions.controls.first" disabled=loadFirstDisabled}}
+ {{d-button action="loadPreviousVersion" icon="backward" title="post.revisions.controls.previous" disabled=loadPreviousDisabled}}
{{#conditional-loading-spinner condition=loading size="small"}}
{{boundI18n revisionsTextKey previousBinding="previousVersion" currentBinding="model.current_version" totalBinding="model.version_count"}}
{{/conditional-loading-spinner}}
-
-
+ {{d-button action="loadNextVersion" icon="forward" title="post.revisions.controls.next" disabled=loadNextDisabled}}
+ {{d-button action="loadLastVersion" icon="fast-forward" title="post.revisions.controls.last" disabled=loadLastDisabled}}
{{#if displayHide}}
-
+ {{d-button action="hideVersion" icon="trash-o" title="post.revisions.controls.hide" class="btn-danger" disabled=loading}}
{{/if}}
{{#if displayShow}}
-
+ {{d-button action="showVersion" icon="undo" title="post.revisions.controls.show" disabled=loading}}
{{/if}}
-
+ {{d-button action="displayInline" label="post.revisions.displays.inline.button" title="post.revisions.displays.inline.title" class=inlineClass}}
{{#unless site.mobileView}}
-
-
+ {{d-button action="displaySideBySide" label="post.revisions.displays.side_by_side.button" title="post.revisions.displays.side_by_side.title" class=sideBySideClass}}
+ {{d-button action="displaySideBySideMarkdown" label="post.revisions.displays.side_by_side_markdown.button" title="post.revisions.displays.side_by_side_markdown.title" class=sideBySideMarkdownClass}}
{{/unless}}
diff --git a/app/assets/javascripts/discourse/templates/modal/merge-topic.hbs b/app/assets/javascripts/discourse/templates/modal/merge-topic.hbs
index e4bf364fc2e..19160f99c07 100644
--- a/app/assets/javascripts/discourse/templates/modal/merge-topic.hbs
+++ b/app/assets/javascripts/discourse/templates/modal/merge-topic.hbs
@@ -2,7 +2,7 @@
{{{i18n 'topic.merge_topic.instructions' count=selectedPostsCount}}}
diff --git a/app/assets/javascripts/discourse/templates/post.hbs b/app/assets/javascripts/discourse/templates/post.hbs
index bcdc0f7f825..67903530253 100644
--- a/app/assets/javascripts/discourse/templates/post.hbs
+++ b/app/assets/javascripts/discourse/templates/post.hbs
@@ -25,6 +25,7 @@
diff --git a/app/assets/javascripts/discourse/views/choose-topic.js.es6 b/app/assets/javascripts/discourse/views/choose-topic.js.es6
index 7dbd8e42485..da4cbac2f48 100644
--- a/app/assets/javascripts/discourse/views/choose-topic.js.es6
+++ b/app/assets/javascripts/discourse/views/choose-topic.js.es6
@@ -5,14 +5,16 @@ export default Ember.View.extend({
templateName: 'choose_topic',
topicTitleChanged: function() {
- this.set('loading', true);
- this.set('noResults', true);
- this.set('selectedTopicId', null);
+ this.setProperties({
+ loading: true,
+ noResults: true,
+ selectedTopicId: null,
+ });
this.search(this.get('topicTitle'));
}.observes('topicTitle'),
topicsChanged: function() {
- var topics = this.get('topics');
+ const topics = this.get('topics');
if (topics) {
this.set('noResults', topics.length === 0);
}
@@ -20,14 +22,17 @@ export default Ember.View.extend({
}.observes('topics'),
search: debounce(function(title) {
- var self = this;
+ const self = this,
+ currentTopicId = this.get("currentTopicId");
+
if (Em.isEmpty(title)) {
self.setProperties({ topics: null, loading: false });
return;
}
- searchForTerm(title, {typeFilter: 'topic', searchForId: true}).then(function (results) {
+
+ searchForTerm(title, { typeFilter: 'topic', searchForId: true }).then(function (results) {
if (results && results.posts && results.posts.length > 0) {
- self.set('topics', results.posts.mapBy('topic'));
+ self.set('topics', results.posts.mapBy('topic').filter(t => t.get("id") !== currentTopicId));
} else {
self.setProperties({ topics: null, loading: false });
}
@@ -36,7 +41,7 @@ export default Ember.View.extend({
actions: {
chooseTopic: function (topic) {
- var topicId = Em.get(topic, 'id');
+ const topicId = Em.get(topic, 'id');
this.set('selectedTopicId', topicId);
Em.run.next(function () {
diff --git a/app/assets/javascripts/main_include.js b/app/assets/javascripts/main_include.js
index 0c02cfe9232..ce051e83d5b 100644
--- a/app/assets/javascripts/main_include.js
+++ b/app/assets/javascripts/main_include.js
@@ -25,6 +25,9 @@
//= require ./discourse/lib/eyeline
//= require ./discourse/helpers/register-unbound
//= require ./discourse/mixins/scrolling
+//= require ./discourse/models/rest
+//= require ./discourse/models/badge-grouping
+//= require ./discourse/models/badge
//= require_tree ./discourse/mixins
//= require ./discourse/lib/ajax-error
//= require ./discourse/lib/markdown
@@ -51,6 +54,7 @@
//= require ./discourse/models/draft
//= require ./discourse/models/composer
//= require ./discourse/models/invite
+//= require ./discourse/models/user-badge
//= require ./discourse/controllers/discovery-sortable
//= require ./discourse/controllers/navigation/default
//= require ./discourse/views/grouped
diff --git a/app/assets/stylesheets/common/base/alert.scss b/app/assets/stylesheets/common/base/alert.scss
index fe80bb2ebaf..1476a3f2338 100644
--- a/app/assets/stylesheets/common/base/alert.scss
+++ b/app/assets/stylesheets/common/base/alert.scss
@@ -11,7 +11,6 @@
float: right;
font-size: 1.429em;
font-weight: bold;
- line-height: 18px;
color: $primary;
opacity: 0.2;
filter: alpha(opacity = 20);
diff --git a/app/assets/stylesheets/common/base/directory.scss b/app/assets/stylesheets/common/base/directory.scss
index 7170b092188..ad4004264fc 100644
--- a/app/assets/stylesheets/common/base/directory.scss
+++ b/app/assets/stylesheets/common/base/directory.scss
@@ -26,7 +26,7 @@
.number, .time-read {
font-size: 1.4em;
- color: dark-light-diff($primary, $secondary, 50%, -50%);
+ color: dark-light-diff($primary, $secondary, 50%, -20%);
}
}
@@ -35,7 +35,7 @@
background-color: dark-light-diff($highlight, $secondary, 70%, -80%);
.username a, .name, .title, .number, .time-read {
- color: scale-color($highlight, $lightness: -50%);
+ color: dark-light-choose(scale-color($highlight, $lightness: -50%), scale-color($highlight, $lightness: 50%));
}
}
}
diff --git a/app/assets/stylesheets/common/base/discourse.scss b/app/assets/stylesheets/common/base/discourse.scss
index ce6ca6682c3..ae6aba12daf 100644
--- a/app/assets/stylesheets/common/base/discourse.scss
+++ b/app/assets/stylesheets/common/base/discourse.scss
@@ -32,8 +32,7 @@ small {
blockquote {
- border-left: 5px solid dark-light-diff($primary, $secondary, 90%, -75%);
- background-color: dark-light-diff($primary, $secondary, 97%, -65%);
+ @include post-aside;
overflow: hidden;
clear: both;
}
diff --git a/app/assets/stylesheets/common/base/emoji.scss b/app/assets/stylesheets/common/base/emoji.scss
index fb658ea4a5a..2d9061bffc3 100644
--- a/app/assets/stylesheets/common/base/emoji.scss
+++ b/app/assets/stylesheets/common/base/emoji.scss
@@ -51,9 +51,7 @@ body img.emoji {
}
.emoji-modal .toolbar {
- margin: 0;
- margin-top: 8px;
- margin-bottom: 5px
+ margin: 8px 0 5px;
}
.emoji-modal .toolbar li {
diff --git a/app/assets/stylesheets/common/base/magnific-popup.scss b/app/assets/stylesheets/common/base/magnific-popup.scss
index 98ead180b7b..3aa7f39eb4d 100644
--- a/app/assets/stylesheets/common/base/magnific-popup.scss
+++ b/app/assets/stylesheets/common/base/magnific-popup.scss
@@ -337,9 +337,8 @@ button {
@if $IE7support {
filter: unquote("alpha(opacity=#{$controls-opacity*100})");
}
- margin: 0;
top: 50%;
- margin-top: -55px;
+ margin: -55px 0 0;
padding: 0;
width: 90px;
height: 110px;
diff --git a/app/assets/stylesheets/common/base/modal.scss b/app/assets/stylesheets/common/base/modal.scss
index 0df5f7b412e..ee8de426b20 100644
--- a/app/assets/stylesheets/common/base/modal.scss
+++ b/app/assets/stylesheets/common/base/modal.scss
@@ -65,7 +65,6 @@
margin: 0 auto;
background-color: $secondary;
border: 1px solid dark-light-diff($primary, $secondary, 90%, -60%);
- border: 1px solid dark-light-diff($primary, $secondary, 90%, -60%);
box-shadow: 0 3px 7px rgba(0,0,0, .8);
background-clip: padding-box;
diff --git a/app/assets/stylesheets/common/base/onebox.scss b/app/assets/stylesheets/common/base/onebox.scss
index a533a15f1b5..390ac83d527 100644
--- a/app/assets/stylesheets/common/base/onebox.scss
+++ b/app/assets/stylesheets/common/base/onebox.scss
@@ -11,10 +11,10 @@ a.loading-onebox {
.onebox-result {
+ @include post-aside;
+
margin-top: 15px;
padding: 12px 25px 12px 12px;
- border-left: 5px solid dark-light-diff($primary, $secondary, 90%, -75%);
- background-color: dark-light-diff($primary, $secondary, 97%, -65%);
font-size: 1em;
> .source {
margin-bottom: 12px;
@@ -82,16 +82,15 @@ a.loading-onebox {
@mixin onebox-favicon($class, $image) {
&.#{$class} .source {
- background-image: image-url("favicons/#{$image}.png");
- background-repeat: no-repeat;
+ background: image-url("favicons/#{$image}.png") no-repeat;
padding-left: 20px;
}
}
aside.onebox {
+ @include post-aside;
+
padding: 12px 25px 12px 12px;
- border-left: 5px solid dark-light-diff($primary, $secondary, 90%, -75%);
- background-color: dark-light-diff($primary, $secondary, 97%, -65%);
font-size: 1em;
header {
@@ -148,8 +147,7 @@ aside.onebox .onebox-body .onebox-avatar {
blockquote {
aside.onebox {
- border-left: 5px solid dark-light-diff($primary, $secondary, 90%, -75%);
- background-color: dark-light-diff($primary, $secondary, 97%, -65%);
+ @include post-aside;
}
}
diff --git a/app/assets/stylesheets/common/base/pagedown.scss b/app/assets/stylesheets/common/base/pagedown.scss
index ec7b7c3757c..8cac61a14ff 100644
--- a/app/assets/stylesheets/common/base/pagedown.scss
+++ b/app/assets/stylesheets/common/base/pagedown.scss
@@ -13,10 +13,7 @@
}
.wmd-button-row {
- margin-left: 5px;
- margin-right: 5px;
- margin-bottom: 5px;
- margin-top: 5px;
+ margin: 5px;
padding: 0;
height: 20px;
overflow: hidden;
@@ -33,18 +30,9 @@
}
.wmd-button {
- width: 20px;
- height: 20px;
- padding-left: 2px;
- padding-right: 3px;
margin-right: 5px;
- background-repeat: no-repeat;
- background-position: 0 0;
border: 0;
- width: 20px;
- height: 20px;
position: relative;
- border: 0;
float: left;
font-family: FontAwesome;
font-weight: normal;
diff --git a/app/assets/stylesheets/common/base/topic-post.scss b/app/assets/stylesheets/common/base/topic-post.scss
index 0013fab8cd2..894e5db08f5 100644
--- a/app/assets/stylesheets/common/base/topic-post.scss
+++ b/app/assets/stylesheets/common/base/topic-post.scss
@@ -50,8 +50,8 @@ aside.quote {
.badge-wrapper { margin-left: 5px; }
.title {
- border-left: 5px solid dark-light-diff($primary, $secondary, 90%, -75%);
- background-color: dark-light-diff($primary, $secondary, 97%, -65%);
+ @include post-aside;
+
color: scale-color($primary, $lightness: 30%);
// IE will screw up the blockquote underneath if bottom padding is 0px
padding: 12px 12px 1px 12px;
@@ -129,6 +129,11 @@ aside.quote {
}
}
+.topic-body {
+ &.highlighted {
+ background-color: dark-light-diff($tertiary, $secondary, 85%, -65%);
+ }
+}
.wiki .topic-body {
background-color: dark-light-diff($wiki, $secondary, 95%, -50%);
}
@@ -164,7 +169,7 @@ pre {
display: block;
padding: 5px 10px;
color: $primary;
- background: dark-light-diff($primary, $secondary, 97%, -45%);
+ background: dark-light-diff($primary, $secondary, 97%, -65%);
max-height: 500px;
}
}
@@ -269,7 +274,7 @@ table.md-table {
font-size: 35px;
width: 45px;
text-align: center;
- color: lighten($primary, 75%);
+ color: dark-light-diff($primary, $secondary, 75%, 20%);
}
}
@@ -279,7 +284,7 @@ table.md-table {
text-transform: uppercase;
font-weight: bold;
font-size: 0.9em;
- color: lighten($primary, 50%);
+ color: dark-light-diff($primary, $secondary, 50%, 0%);
.custom-message {
text-transform: none;
diff --git a/app/assets/stylesheets/common/base/upload.scss b/app/assets/stylesheets/common/base/upload.scss
index 97fdf508ec4..6afba003ef1 100644
--- a/app/assets/stylesheets/common/base/upload.scss
+++ b/app/assets/stylesheets/common/base/upload.scss
@@ -1,5 +1,4 @@
.uploaded-image-preview {
- background-position: center center;
background-size: cover;
- background-color: $primary;
+ background: $primary center center;
}
diff --git a/app/assets/stylesheets/common/base/user-badges.scss b/app/assets/stylesheets/common/base/user-badges.scss
index 74b87c82651..e1bd5d0862c 100644
--- a/app/assets/stylesheets/common/base/user-badges.scss
+++ b/app/assets/stylesheets/common/base/user-badges.scss
@@ -4,10 +4,9 @@
color: $primary;
border: 1px solid dark-light-diff($primary, $secondary, 90%, -65%);
line-height: 19px;
- margin: 0;
display: inline-block;
background-color: $secondary;
- margin-bottom: 3px;
+ margin: 0 0 3px;
.fa {
padding-right: 3px;
@@ -52,8 +51,7 @@
img {
display: block;
- margin: auto;
- margin-bottom: 4px;
+ margin: auto auto 4px;
width: 55px;
height: 55px;
}
diff --git a/app/assets/stylesheets/common/base/username_tagsinput.scss b/app/assets/stylesheets/common/base/username_tagsinput.scss
index 8e0f60dca90..c18494c18c1 100644
--- a/app/assets/stylesheets/common/base/username_tagsinput.scss
+++ b/app/assets/stylesheets/common/base/username_tagsinput.scss
@@ -24,8 +24,17 @@ div.tagsinput span.tag {
}
div.tagsinput span.tag a { font-weight: bold; color: #82ad2b; text-decoration:none; font-size: 11px; }
-div.tagsinput input { width:80px; margin:0px; font-family: helvetica; font-size: 0.929em; border:1px solid transparent;
-padding:2px 5px; background: transparent; color: #000; outline:0px; margin-right:5px; margin-bottom:5px; }
+div.tagsinput input {
+ width:80px;
+ font-family: helvetica;
+ font-size: 0.929em;
+ border:1px solid transparent;
+ padding:2px 5px;
+ background: transparent;
+ color: #000;
+ outline:0px;
+ margin: 0px 5px 5px 0px;
+}
div.tagsinput div { display:block; float: left; }
.tags_clear { clear: both; width: 100%; height: 0; }
.not_valid {background: #FBD8DB !important; color: #90111A !important;}
diff --git a/app/assets/stylesheets/common/components/keyboard_shortcuts.css.scss b/app/assets/stylesheets/common/components/keyboard_shortcuts.css.scss
index 1aa845d8930..aeff2b77742 100644
--- a/app/assets/stylesheets/common/components/keyboard_shortcuts.css.scss
+++ b/app/assets/stylesheets/common/components/keyboard_shortcuts.css.scss
@@ -28,18 +28,13 @@
}
b {
- color: #000;
- background: #eee;
- border-style: solid;
- border-color: #ccc #aaa #888 #bbb;
padding: 2px 6px;
border-radius: 4px;
- box-shadow: 0 2px 0 rgba(0,0,0,0.2),0 0 0 1px #fff inset;
- background-color: #fafafa;
- border-color: #ccc #ccc #fff;
- border-style: solid solid none;
- border-width: 1px 1px medium;
- color: #444;
+ box-shadow: 0 2px 0 rgba(0,0,0,0.2),0 0 0 1px dark-light-choose(#fff,#000) inset;
+ background: dark-light-choose(#fafafa, #333);
+ border: 1px solid dark-light-choose(#ccc, #555);
+ border-bottom: medium none dark-light-choose(#fff, #000);
+ color: dark-light-choose(#444, #aaa);
white-space: nowrap;
display: inline-block;
}
diff --git a/app/assets/stylesheets/common/components/navs.css.scss b/app/assets/stylesheets/common/components/navs.css.scss
index 5934c2fb740..d29d05a3d02 100644
--- a/app/assets/stylesheets/common/components/navs.css.scss
+++ b/app/assets/stylesheets/common/components/navs.css.scss
@@ -75,10 +75,9 @@
{
left: 90%;
top: 33%;
- border: solid transparent;
content: " ";
position: absolute;
- border-width: 8px;
+ border: 8px solid transparent;
border-left-color: $secondary;
}
diff --git a/app/assets/stylesheets/common/foundation/mixins.scss b/app/assets/stylesheets/common/foundation/mixins.scss
index 60a682ac094..4b560cc46f9 100644
--- a/app/assets/stylesheets/common/foundation/mixins.scss
+++ b/app/assets/stylesheets/common/foundation/mixins.scss
@@ -60,6 +60,7 @@
// Linear gradient
+//noinspection CssOptimizeSimilarProperties
@mixin linear-gradient($start-color, $end-color) {
background-color: $start-color;
background-image: linear-gradient(to bottom, $start-color, $end-color);
@@ -92,3 +93,10 @@
-moz-user-select: none;
-ms-user-select: none;
}
+
+
+// Stuff we repeat
+@mixin post-aside {
+ border-left: 5px solid dark-light-diff($primary, $secondary, 90%, -85%);
+ background-color: dark-light-diff($primary, $secondary, 97%, -75%);
+}
diff --git a/app/assets/stylesheets/common/foundation/variables.scss b/app/assets/stylesheets/common/foundation/variables.scss
index 0c51ce420a6..e4b97ef9eda 100644
--- a/app/assets/stylesheets/common/foundation/variables.scss
+++ b/app/assets/stylesheets/common/foundation/variables.scss
@@ -19,6 +19,7 @@ $twitter: #00bced !default;
$yahoo: #810293 !default;
$github: #6d6d6d !default;
+
// Fonts
// --------------------------------------------------
diff --git a/app/assets/stylesheets/common/topic-entrance.scss b/app/assets/stylesheets/common/topic-entrance.scss
index 8316aae9eb9..847edc46a86 100644
--- a/app/assets/stylesheets/common/topic-entrance.scss
+++ b/app/assets/stylesheets/common/topic-entrance.scss
@@ -8,8 +8,6 @@
position: absolute;
width: 133px;
- padding: 5px;
-
@include unselectable;
button.full {
diff --git a/app/assets/stylesheets/desktop/discourse.scss b/app/assets/stylesheets/desktop/discourse.scss
index 0d117b4ae26..e25653136a8 100644
--- a/app/assets/stylesheets/desktop/discourse.scss
+++ b/app/assets/stylesheets/desktop/discourse.scss
@@ -77,12 +77,12 @@ body {
.grippie {
width: 100%;
- border: 1px solid dark-light-diff($primary, $secondary, 90%, -60%);
- border-width: 1px 0;
+ border: 1px solid;
+ border-right-width: 0;
+ border-left-width: 0;
cursor: row-resize;
height: 11px;
overflow: hidden;
- background-color: dark-light-diff($primary, $secondary, 90%, -60%);
display:block;
background: image-url("grippie.png") dark-light-diff($primary, $secondary, 90%, -60%) no-repeat center 3px;
}
diff --git a/app/assets/stylesheets/desktop/history.scss b/app/assets/stylesheets/desktop/history.scss
index 68cba56e30b..5becce0f32d 100644
--- a/app/assets/stylesheets/desktop/history.scss
+++ b/app/assets/stylesheets/desktop/history.scss
@@ -11,8 +11,12 @@
}
#revision-controls {
float: left;
- .btn[disabled]:hover {
- color: $primary;
+ .btn[disabled] {
+ cursor: not-allowed;
+ background-color: dark-light-diff($primary, $secondary, 90%, -60%);
+ }
+ .btn-danger[disabled] {
+ background-color: dark-light-diff($danger, $secondary, 30%, -60%);
}
}
#display-modes {
diff --git a/app/assets/stylesheets/desktop/modal.scss b/app/assets/stylesheets/desktop/modal.scss
index 8c527e21f76..c9d5b84c388 100644
--- a/app/assets/stylesheets/desktop/modal.scss
+++ b/app/assets/stylesheets/desktop/modal.scss
@@ -17,7 +17,6 @@
margin: -250px 0 0 -305px;
background-color: $secondary;
border: 1px solid dark-light-diff($primary, $secondary, 90%, -60%);
- border: 1px solid dark-light-diff($primary, $secondary, 90%, -60%);
box-shadow: 0 3px 7px rgba(0,0,0, .8);
background-clip: padding-box;
diff --git a/app/assets/stylesheets/desktop/topic-list.scss b/app/assets/stylesheets/desktop/topic-list.scss
index 2e90524da09..8d3a880b1c9 100644
--- a/app/assets/stylesheets/desktop/topic-list.scss
+++ b/app/assets/stylesheets/desktop/topic-list.scss
@@ -182,8 +182,7 @@
}
td.latest {
vertical-align: top;
- padding: 8px;
- padding-top: 0;
+ padding: 0 8px 8px;
}
.last-user-info {
font-size: 0.857em;
diff --git a/app/assets/stylesheets/desktop/topic-post.scss b/app/assets/stylesheets/desktop/topic-post.scss
index 181d0655116..736eb7ff60d 100644
--- a/app/assets/stylesheets/desktop/topic-post.scss
+++ b/app/assets/stylesheets/desktop/topic-post.scss
@@ -699,9 +699,6 @@ $topic-avatar-width: 45px;
z-index: 2;
border-top: 1px solid dark-light-diff($primary, $secondary, 90%, -75%);
padding: 12px $topic-body-width-padding 15px $topic-body-width-padding;
- &.highlighted {
- background-color: dark-light-diff($tertiary, $secondary, 85%, -65%);
- }
}
.topic-avatar {
border-top: 1px solid dark-light-diff($primary, $secondary, 90%, -75%);
@@ -800,7 +797,6 @@ $topic-avatar-width: 45px;
#selected-posts {
- padding-left: 20px;
margin-left: 330px;
width: 200px;
position: fixed;
diff --git a/app/assets/stylesheets/desktop/user.scss b/app/assets/stylesheets/desktop/user.scss
index 027beaa530a..2f5af264d6d 100644
--- a/app/assets/stylesheets/desktop/user.scss
+++ b/app/assets/stylesheets/desktop/user.scss
@@ -214,7 +214,6 @@
width: 100%;
margin-bottom: 10px;
overflow: hidden;
- color: $secondary;
&.group {
.details {
@@ -269,7 +268,7 @@
.details {
padding: 15px 15px 4px 15px;
margin-top: -200px;
- background: rgba($primary, .85);
+ background: dark-light-choose(rgba($primary, .85), rgba($secondary, .85));
transition: margin .15s linear;
h1 {
@@ -316,20 +315,20 @@
width: 100%;
position: relative;
float: left;
- color: $secondary;
+ color: dark-light-choose($secondary, lighten($primary, 10%));
h1 {font-weight: bold;}
.primary-textual {
padding: 3px;
a[href] {
- color: $secondary;
+ color: dark-light-choose($secondary, lighten($primary, 10%));
}
}
.bio {
- color: $secondary;
+ color: dark-light-choose($secondary, lighten($primary, 10%));
max-height: 300px;
overflow: auto;
max-width: 750px;
@@ -396,7 +395,7 @@
.details {
padding: 12px 15px 2px 15px;
margin-top: 0;
- background: rgba($primary, 1);
+ background: dark-light-choose(rgba($primary, 1), scale-color($secondary, $lightness: 40%));
.bio { display: none; }
.primary {
@@ -533,21 +532,21 @@
.staff-counters {
text-align: left;
background: $primary;
+ color: $secondary;
+ a {
+ color: $secondary;
+ }
> div {
margin: 0 10px 0 0;
display: inline-block;
padding: 5px 0;
&:first-of-type {
padding-left: 10px;
- }
+ }
span {
padding: 1px 5px;
border-radius: 10px;
- }
-
- }
- a {
- color: $secondary;
+ }
}
.active {
font-weight: bold;
diff --git a/app/assets/stylesheets/embed.css.scss b/app/assets/stylesheets/embed.css.scss
index 2f6b1ebf962..d7193d5533e 100644
--- a/app/assets/stylesheets/embed.css.scss
+++ b/app/assets/stylesheets/embed.css.scss
@@ -3,6 +3,7 @@
@import "./common/foundation/variables";
@import "./common/foundation/colors";
+@import "./common/foundation/mixins";
@import "./common/base/onebox";
article.post {
diff --git a/app/assets/stylesheets/mobile/discourse.scss b/app/assets/stylesheets/mobile/discourse.scss
index f4a32585b6b..4d56b037f17 100644
--- a/app/assets/stylesheets/mobile/discourse.scss
+++ b/app/assets/stylesheets/mobile/discourse.scss
@@ -88,8 +88,7 @@ blockquote {
// we must remove margins for text site titles
h2#site-text-logo
{
- margin: 0;
- margin-left: 10px;
+ margin: 0 0 0 10px;
}
// categories should not be bold on mobile; they fight with the topic title too much
diff --git a/app/assets/stylesheets/mobile/modal.scss b/app/assets/stylesheets/mobile/modal.scss
index 8c94edf25cd..e54ab173ce5 100644
--- a/app/assets/stylesheets/mobile/modal.scss
+++ b/app/assets/stylesheets/mobile/modal.scss
@@ -17,7 +17,6 @@
width: 100%;
height: auto;
background-color: $secondary;
- border: 1px solid scale-color($secondary, $lightness: 90%);
border: 1px solid rgba(0, 0, 0, 0.3);
@include border-radius-all (6px);
@@ -178,4 +177,4 @@
#search-help p {
margin: 5px;
-}
\ No newline at end of file
+}
diff --git a/app/assets/stylesheets/mobile/topic-list.scss b/app/assets/stylesheets/mobile/topic-list.scss
index 5f71cddcad2..47c69376bb5 100644
--- a/app/assets/stylesheets/mobile/topic-list.scss
+++ b/app/assets/stylesheets/mobile/topic-list.scss
@@ -268,17 +268,14 @@ tr.category-topic-link {
float: left;
width: 280px;
padding: 4px 0;
- margin: 1px 0 0;
list-style: none;
background-color: $secondary;
- border: 1px solid dark-light-diff($primary, $secondary, 90%, -60%);
- border: 1px solid rgba(0, 0, 0, 0.2);
+ border: 1px solid dark-light-choose(rgba(0, 0, 0, 0.2), scale-color($primary, $lightness: -60%));
border-radius: 5px;
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
background-clip: padding-box;
- margin-bottom: 20px;
-.title {font-weight: bold; display: block;}
-
+ margin: 1px 0 20px;
+ .title {font-weight: bold; display: block;}
}
.dropdown-menu a {
display: block;
diff --git a/app/assets/stylesheets/mobile/topic-post.scss b/app/assets/stylesheets/mobile/topic-post.scss
index 77b634c92e4..fbde0923a37 100644
--- a/app/assets/stylesheets/mobile/topic-post.scss
+++ b/app/assets/stylesheets/mobile/topic-post.scss
@@ -463,12 +463,8 @@ blockquote {
.posts-wrapper { position: relative; }
-.topic-body.highlighted {
- background-color: scale-color($tertiary, $lightness: 75%);
-}
-
span.highlighted {
- background-color: scale-color($highlight, $lightness: 70%);
+ background-color: dark-light-choose(scale-color($highlight, $lightness: 70%), $highlight);
}
.topic-avatar {
diff --git a/app/assets/stylesheets/mobile/topic.scss b/app/assets/stylesheets/mobile/topic.scss
index db76dd41f7d..9bc347b650c 100644
--- a/app/assets/stylesheets/mobile/topic.scss
+++ b/app/assets/stylesheets/mobile/topic.scss
@@ -69,7 +69,6 @@
position: absolute;
bottom: 34px;
width: 133px;
- padding: 5px;
button.full {
width: 100%;
diff --git a/app/assets/stylesheets/mobile/user.scss b/app/assets/stylesheets/mobile/user.scss
index 1f45f68ee85..03ec7fb16fc 100644
--- a/app/assets/stylesheets/mobile/user.scss
+++ b/app/assets/stylesheets/mobile/user.scss
@@ -221,9 +221,8 @@
}
.about {
- background-color: dark-light-diff($primary, $secondary, 0%, -75%);
background-size: cover;
- background-position: center center;
+ background: dark-light-diff($primary, $secondary, 0%, -75%) center center;
width: 100%;
margin-bottom: 10px;
overflow: hidden;
diff --git a/app/assets/stylesheets/vendor/bootstrap.scss b/app/assets/stylesheets/vendor/bootstrap.scss
index cf73b4d7d80..df21cf6c8a1 100644
--- a/app/assets/stylesheets/vendor/bootstrap.scss
+++ b/app/assets/stylesheets/vendor/bootstrap.scss
@@ -14,6 +14,7 @@
@import "common/foundation/mixins";
+//noinspection CssOverwrittenProperties
a:focus {
outline: thin dotted #333;
outline: 5px auto -webkit-focus-ring-color;
diff --git a/app/assets/stylesheets/vendor/pikaday.scss b/app/assets/stylesheets/vendor/pikaday.scss
index 3b2a3c0a80b..a12e8e54600 100644
--- a/app/assets/stylesheets/vendor/pikaday.scss
+++ b/app/assets/stylesheets/vendor/pikaday.scss
@@ -86,9 +86,7 @@
text-indent: 20px; // hide text using text-indent trick, using width value (it's enough)
white-space: nowrap;
overflow: hidden;
- background-color: transparent;
- background-position: center center;
- background-repeat: no-repeat;
+ background: transparent no-repeat center center;
background-size: 75% 75%;
opacity: .5;
*position: absolute;
diff --git a/app/assets/stylesheets/vendor/select2.scss b/app/assets/stylesheets/vendor/select2.scss
index aa3d61372ab..828c7185173 100644
--- a/app/assets/stylesheets/vendor/select2.scss
+++ b/app/assets/stylesheets/vendor/select2.scss
@@ -200,6 +200,7 @@ Version: @@ver@@ Timestamp: @@timestamp@@
white-space: nowrap;
}
+//noinspection CssOverwrittenProperties
.select2-search input {
width: 100%;
height: auto !important;
@@ -228,6 +229,7 @@ Version: @@ver@@ Timestamp: @@timestamp@@
margin-top: 4px;
}
+//noinspection CssOverwrittenProperties
.select2-search input.select2-active {
background: #fff asset-url('select2-spinner.gif') no-repeat 100%;
background: asset-url('select2-spinner.gif') no-repeat 100%, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, #fff), color-stop(0.99, #eee));
@@ -389,16 +391,14 @@ disabled look for disabled choices in the results dropdown
/* disabled styles */
.select2-container.select2-container-disabled .select2-choice {
- background-color: #f4f4f4;
- background-image: none;
- border: 1px solid #ddd;
- cursor: default;
+ background: #f4f4f4 none;
+ border: 1px solid #ddd;
+ cursor: default;
}
.select2-container.select2-container-disabled .select2-choice .select2-arrow {
- background-color: #f4f4f4;
- background-image: none;
- border-left: 0;
+ background: #f4f4f4 none;
+ border-left: 0;
}
.select2-container.select2-container-disabled .select2-choice abbr {
@@ -540,17 +540,15 @@ html[dir="rtl"] .select2-search-choice-close {
/* disabled styles */
.select2-container-multi.select2-container-disabled .select2-choices {
- background-color: #f4f4f4;
- background-image: none;
- border: 1px solid #ddd;
- cursor: default;
+ background: #f4f4f4 none;
+ border: 1px solid #ddd;
+ cursor: default;
}
.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice {
- padding: 3px 5px 3px 5px;
- border: 1px solid #ddd;
- background-image: none;
- background-color: #f4f4f4;
+ padding: 3px 5px 3px 5px;
+ border: 1px solid #ddd;
+ background: #f4f4f4 none;
}
.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice .select2-search-choice-close { display: none;
@@ -594,16 +592,15 @@ html[dir="rtl"] .select2-search-choice-close {
/* Retina-ize icons */
@media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-resolution: 2dppx) {
- .select2-search input,
- .select2-search-choice-close,
- .select2-container .select2-choice abbr,
- .select2-container .select2-choice .select2-arrow b {
- background-image: asset-url('select2x2.png') !important;
- background-repeat: no-repeat !important;
- background-size: 60px 40px !important;
- }
+ .select2-search input,
+ .select2-search-choice-close,
+ .select2-container .select2-choice abbr,
+ .select2-container .select2-choice .select2-arrow b {
+ background: asset-url('select2x2.png') no-repeat !important;
+ background-size: 60px 40px !important;
+ }
- .select2-search input {
- background-position: 100% -21px !important;
- }
+ .select2-search input {
+ background-position: 100% -21px !important;
+ }
}
diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb
index 7430d9f4100..4446a0af99e 100644
--- a/app/controllers/search_controller.rb
+++ b/app/controllers/search_controller.rb
@@ -12,13 +12,12 @@ class SearchController < ApplicationController
search = Search.new(params[:q], type_filter: 'topic', guardian: guardian, include_blurbs: true, blurb_length: 300)
result = search.execute
- serializer = serialize_data(result, GroupedSearchResultSerializer, :result => result)
+ serializer = serialize_data(result, GroupedSearchResultSerializer, result: result)
respond_to do |format|
format.html do
store_preloaded("search", MultiJson.dump(serializer))
end
-
format.json do
render_json_dump(serializer)
end
@@ -29,14 +28,14 @@ class SearchController < ApplicationController
def query
params.require(:term)
- search_args = {guardian: guardian}
- search_args[:type_filter] = params[:type_filter] if params[:type_filter].present?
- if params[:include_blurbs].present?
- search_args[:include_blurbs] = params[:include_blurbs] == "true"
- end
- search_args[:search_for_id] = true if params[:search_for_id].present?
+ search_args = { guardian: guardian }
+
+ search_args[:type_filter] = params[:type_filter] if params[:type_filter].present?
+ search_args[:include_blurbs] = params[:include_blurbs] == "true" if params[:include_blurbs].present?
+ search_args[:search_for_id] = true if params[:search_for_id].present?
search_context = params[:search_context]
+
if search_context.present?
raise Discourse::InvalidParameters.new(:search_context) unless SearchController.valid_context_types.include?(search_context[:type])
raise Discourse::InvalidParameters.new(:search_context) if search_context[:id].blank?
@@ -60,7 +59,7 @@ class SearchController < ApplicationController
search = Search.new(params[:term], search_args.symbolize_keys)
result = search.execute
- render_serialized(result, GroupedSearchResultSerializer, :result => result)
+ render_serialized(result, GroupedSearchResultSerializer, result: result)
end
end
diff --git a/app/models/category_user.rb b/app/models/category_user.rb
index d53fb804e29..1fb537f546e 100644
--- a/app/models/category_user.rb
+++ b/app/models/category_user.rb
@@ -15,18 +15,19 @@ class CategoryUser < ActiveRecord::Base
TopicUser.notification_levels
end
- def self.auto_track_new_topic(topic)
- apply_default_to_topic(topic,
- TopicUser.notification_levels[:tracking],
- TopicUser.notification_reasons[:auto_track_category]
- )
- end
+ %w{watch track}.each do |s|
+ define_singleton_method("auto_#{s}_new_topic") do |topic, new_category=nil|
+ category_id = topic.category_id
- def self.auto_watch_new_topic(topic)
- apply_default_to_topic(topic,
- TopicUser.notification_levels[:watching],
- TopicUser.notification_reasons[:auto_watch_category]
- )
+ if new_category && topic.created_at > 5.days.ago
+ # we want to apply default of the new category
+ category_id = new_category.id
+ # remove defaults from previous category
+ remove_default_from_topic(topic.id, TopicUser.notification_levels[:"#{s}ing"], TopicUser.notification_reasons[:"auto_#{s}_category"])
+ end
+
+ apply_default_to_topic(topic.id, category_id, TopicUser.notification_levels[:"#{s}ing"], TopicUser.notification_reasons[:"auto_#{s}_category"])
+ end
end
def self.batch_set(user, level, category_ids)
@@ -56,7 +57,7 @@ class CategoryUser < ActiveRecord::Base
end
end
- def self.apply_default_to_topic(topic, level, reason)
+ def self.apply_default_to_topic(topic_id, category_id, level, reason)
# Can not afford to slow down creation of topics when a pile of users are watching new topics, reverting to SQL for max perf here
sql = <<-SQL
INSERT INTO topic_users(user_id, topic_id, notification_level, notifications_reason_id)
@@ -68,14 +69,30 @@ class CategoryUser < ActiveRecord::Base
SQL
exec_sql(sql,
- topic_id: topic.id,
- category_id: topic.category_id,
+ topic_id: topic_id,
+ category_id: category_id,
level: level,
reason: reason
)
end
- private_class_method :apply_default_to_topic
+ def self.remove_default_from_topic(topic_id, level, reason)
+ sql = <<-SQL
+ DELETE FROM topic_users
+ WHERE topic_id = :topic_id
+ AND notifications_changed_at IS NULL
+ AND notification_level = :level
+ AND notifications_reason_id = :reason
+ SQL
+
+ exec_sql(sql,
+ topic_id: topic_id,
+ level: level,
+ reason: reason
+ )
+ end
+
+ private_class_method :apply_default_to_topic, :remove_default_from_topic
end
# == Schema Information
diff --git a/app/models/site.rb b/app/models/site.rb
index 4d3c1590515..719cf1d7123 100644
--- a/app/models/site.rb
+++ b/app/models/site.rb
@@ -80,7 +80,9 @@ class Site
return {
periods: TopTopic.periods.map(&:to_s),
filters: Discourse.filters.map(&:to_s),
- user_fields: UserField.all
+ user_fields: UserField.all.map do |userfield|
+ UserFieldSerializer.new(userfield, root: false, scope: guardian)
+ end
}.to_json
end
diff --git a/app/models/topic.rb b/app/models/topic.rb
index 30bd8fc343a..297f840be3c 100644
--- a/app/models/topic.rb
+++ b/app/models/topic.rb
@@ -482,8 +482,8 @@ class Topic < ActiveRecord::Base
Category.where(id: new_category.id).update_all("topic_count = topic_count + 1")
CategoryFeaturedTopic.feature_topics_for(old_category) unless @import_mode
CategoryFeaturedTopic.feature_topics_for(new_category) unless @import_mode || old_category.id == new_category.id
- CategoryUser.auto_watch_new_topic(self)
- CategoryUser.auto_track_new_topic(self)
+ CategoryUser.auto_watch_new_topic(self, new_category)
+ CategoryUser.auto_track_new_topic(self, new_category)
end
true
diff --git a/docs/INSTALL-cloud.md b/docs/INSTALL-cloud.md
index fc77821aed1..858f525bfef 100644
--- a/docs/INSTALL-cloud.md
+++ b/docs/INSTALL-cloud.md
@@ -77,7 +77,7 @@ After completing your edits, press
CtrlO then
Enter
- Already have a mail server? Great. Use your existing mail server credentials.
-- No existing mail server, or you don't know what it is? No problem, create a free account on [**Mandrill**][man] (or [Mailgun][gun], or [Mailjet][jet]), and use the credentials provided in the dashboard.
+- No existing mail server, or you don't know what it is? No problem, create a free account on [Mailgun][gun] (10,000 free emails per month), [Mailjet][jet] (200 free emails per day) or [**Mandrill**][man], and use the credentials provided in the dashboard.
- For proper email deliverability, you must set the [SPF and DKIM records](http://help.mandrill.com/entries/21751322-What-are-SPF-and-DKIM-and-do-I-need-to-set-them-up-) in your DNS. In Mandrill, that's under Sending Domains, View DKIM/SPF setup instructions.
diff --git a/lib/discourse_plugin_registry.rb b/lib/discourse_plugin_registry.rb
index f3003c4e333..45858362151 100644
--- a/lib/discourse_plugin_registry.rb
+++ b/lib/discourse_plugin_registry.rb
@@ -87,7 +87,11 @@ class DiscoursePluginRegistry
self.asset_globs.each do |g|
root, ext, options = *g
- next if options[:admin] && !each_options[:admin]
+ if options[:admin]
+ next unless each_options[:admin]
+ else
+ next if each_options[:admin]
+ end
Dir.glob("#{root}/**/*") do |f|
yield f, ext
diff --git a/spec/models/category_user_spec.rb b/spec/models/category_user_spec.rb
index 749368e64e7..34927139996 100644
--- a/spec/models/category_user_spec.rb
+++ b/spec/models/category_user_spec.rb
@@ -52,8 +52,8 @@ describe CategoryUser do
end
it "watches categories that have been changed" do
- watched_category = Fabricate(:category)
user = Fabricate(:user)
+ watched_category = Fabricate(:category)
CategoryUser.create!(user: user, category: watched_category, notification_level: CategoryUser.notification_levels[:watching])
post = create_post
@@ -65,6 +65,21 @@ describe CategoryUser do
expect(tu.notification_level).to eq TopicUser.notification_levels[:watching]
end
+ it "unwatches categories that have been changed" do
+ user = Fabricate(:user)
+ watched_category = Fabricate(:category)
+ CategoryUser.create!(user: user, category: watched_category, notification_level: CategoryUser.notification_levels[:watching])
+
+ post = create_post(category: watched_category)
+ tu = TopicUser.get(post.topic, user)
+ expect(tu.notification_level).to eq TopicUser.notification_levels[:watching]
+
+ # Now, change the topic's category
+ unwatched_category = Fabricate(:category)
+ post.topic.change_category_to_id(unwatched_category.id)
+ expect(TopicUser.get(post.topic, user)).to be_blank
+ end
+
end
end
diff --git a/test/javascripts/admin/controllers/admin-user-badges-test.js.es6 b/test/javascripts/admin/controllers/admin-user-badges-test.js.es6
index 8f1a61976fb..5c06c69c52f 100644
--- a/test/javascripts/admin/controllers/admin-user-badges-test.js.es6
+++ b/test/javascripts/admin/controllers/admin-user-badges-test.js.es6
@@ -1,16 +1,18 @@
+import Badge from 'discourse/models/badge';
+
moduleFor('controller:admin-user-badges', {
needs: ['controller:adminUser']
});
test("grantableBadges", function() {
- var badge_first = Discourse.Badge.create({id: 3, name: "A Badge"});
- var badge_middle = Discourse.Badge.create({id: 1, name: "My Badge"});
- var badge_last = Discourse.Badge.create({id: 2, name: "Zoo Badge"});
- var controller = this.subject({ badges: [badge_last, badge_first, badge_middle] });
- var sorted_names = [badge_first.name, badge_middle.name, badge_last.name];
- var badge_names = controller.get('grantableBadges').map(function(badge) {
+ const badgeFirst = Badge.create({id: 3, name: "A Badge"});
+ const badgeMiddle = Badge.create({id: 1, name: "My Badge"});
+ const badgeLast = Badge.create({id: 2, name: "Zoo Badge"});
+ const controller = this.subject({ badges: [badgeLast, badgeFirst, badgeMiddle] });
+ const sortedNames = [badgeFirst.name, badgeMiddle.name, badgeLast.name];
+ const badgeNames = controller.get('grantableBadges').map(function(badge) {
return badge.name;
});
- deepEqual(badge_names, sorted_names, "sorts badges by name");
+ deepEqual(badgeNames, sortedNames, "sorts badges by name");
});
diff --git a/test/javascripts/models/badge-test.js.es6 b/test/javascripts/models/badge-test.js.es6
index 5087321ee1f..a91b49ecd35 100644
--- a/test/javascripts/models/badge-test.js.es6
+++ b/test/javascripts/models/badge-test.js.es6
@@ -1,43 +1,45 @@
-module("Discourse.Badge");
+import Badge from 'discourse/models/badge';
+
+module("model:badge");
test('newBadge', function() {
- var badge1 = Discourse.Badge.create({name: "New Badge"}),
- badge2 = Discourse.Badge.create({id: 1, name: "Old Badge"});
+ const badge1 = Badge.create({name: "New Badge"}),
+ badge2 = Badge.create({id: 1, name: "Old Badge"});
ok(badge1.get('newBadge'), "badges without ids are new");
ok(!badge2.get('newBadge'), "badges with ids are not new");
});
test('displayName', function() {
- var badge1 = Discourse.Badge.create({id: 1, name: "Test Badge 1"});
+ const badge1 = Badge.create({id: 1, name: "Test Badge 1"});
equal(badge1.get('displayName'), "Test Badge 1", "falls back to the original name in the absence of a translation");
sandbox.stub(I18n, "t").returnsArg(0);
- var badge2 = Discourse.Badge.create({id: 2, name: "Test Badge 2"});
+ const badge2 = Badge.create({id: 2, name: "Test Badge 2"});
equal(badge2.get('displayName'), "badges.badge.test_badge_2.name", "uses translation when available");
});
test('translatedDescription', function() {
- var badge1 = Discourse.Badge.create({id: 1, name: "Test Badge 1", description: "TEST"});
+ const badge1 = Badge.create({id: 1, name: "Test Badge 1", description: "TEST"});
equal(badge1.get('translatedDescription'), null, "returns null when no translation exists");
- var badge2 = Discourse.Badge.create({id: 2, name: "Test Badge 2 **"});
+ const badge2 = Badge.create({id: 2, name: "Test Badge 2 **"});
sandbox.stub(I18n, "t").returns("description translation");
equal(badge2.get('translatedDescription'), "description translation", "users translated description");
});
test('displayDescription', function() {
- var badge1 = Discourse.Badge.create({id: 1, name: "Test Badge 1", description: "TEST"});
+ const badge1 = Badge.create({id: 1, name: "Test Badge 1", description: "TEST"});
equal(badge1.get('displayDescription'), "TEST", "returns original description when no translation exists");
- var badge2 = Discourse.Badge.create({id: 2, name: "Test Badge 2 **"});
+ const badge2 = Badge.create({id: 2, name: "Test Badge 2 **"});
sandbox.stub(I18n, "t").returns("description translation");
equal(badge2.get('displayDescription'), "description translation", "users translated description");
});
test('createFromJson array', function() {
- var badgesJson = {"badge_types":[{"id":6,"name":"Silver 1"}],"badges":[{"id":1126,"name":"Badge 1","description":null,"badge_type_id":6}]};
+ const badgesJson = {"badge_types":[{"id":6,"name":"Silver 1"}],"badges":[{"id":1126,"name":"Badge 1","description":null,"badge_type_id":6}]};
- var badges = Discourse.Badge.createFromJson(badgesJson);
+ const badges = Badge.createFromJson(badgesJson);
ok(Array.isArray(badges), "returns an array");
equal(badges[0].get('name'), "Badge 1", "badge details are set");
@@ -45,16 +47,16 @@ test('createFromJson array', function() {
});
test('createFromJson single', function() {
- var badgeJson = {"badge_types":[{"id":6,"name":"Silver 1"}],"badge":{"id":1126,"name":"Badge 1","description":null,"badge_type_id":6}};
+ const badgeJson = {"badge_types":[{"id":6,"name":"Silver 1"}],"badge":{"id":1126,"name":"Badge 1","description":null,"badge_type_id":6}};
- var badge = Discourse.Badge.createFromJson(badgeJson);
+ const badge = Badge.createFromJson(badgeJson);
ok(!Array.isArray(badge), "does not returns an array");
});
test('updateFromJson', function() {
- var badgeJson = {"badge_types":[{"id":6,"name":"Silver 1"}],"badge":{"id":1126,"name":"Badge 1","description":null,"badge_type_id":6}};
- var badge = Discourse.Badge.create({name: "Badge 1"});
+ const badgeJson = {"badge_types":[{"id":6,"name":"Silver 1"}],"badge":{"id":1126,"name":"Badge 1","description":null,"badge_type_id":6}};
+ const badge = Badge.create({name: "Badge 1"});
badge.updateFromJson(badgeJson);
equal(badge.get('id'), 1126, "id is set");
equal(badge.get('badge_type.name'), "Silver 1", "badge_type reference is set");
@@ -62,7 +64,7 @@ test('updateFromJson', function() {
test('save', function() {
sandbox.stub(Discourse, 'ajax').returns(Ember.RSVP.resolve({}));
- var badge = Discourse.Badge.create({name: "New Badge", description: "This is a new badge.", badge_type_id: 1});
+ const badge = Badge.create({name: "New Badge", description: "This is a new badge.", badge_type_id: 1});
// TODO: clean API
badge.save(["name", "description", "badge_type_id"]);
ok(Discourse.ajax.calledOnce, "saved badge");
@@ -70,7 +72,7 @@ test('save', function() {
test('destroy', function() {
sandbox.stub(Discourse, 'ajax');
- var badge = Discourse.Badge.create({name: "New Badge", description: "This is a new badge.", badge_type_id: 1});
+ const badge = Badge.create({name: "New Badge", description: "This is a new badge.", badge_type_id: 1});
badge.destroy();
ok(!Discourse.ajax.calledOnce, "no AJAX call for a new badge");
badge.set('id', 3);
diff --git a/test/javascripts/models/user-badge-test.js.es6 b/test/javascripts/models/user-badge-test.js.es6
index 70942b39581..bf2a5bf98a8 100644
--- a/test/javascripts/models/user-badge-test.js.es6
+++ b/test/javascripts/models/user-badge-test.js.es6
@@ -1,10 +1,12 @@
-module("Discourse.UserBadge");
+import UserBadge from 'discourse/models/user-badge';
-var singleBadgeJson = {"badges":[{"id":874,"name":"Badge 2","description":null,"badge_type_id":7}],"badge_types":[{"id":7,"name":"Silver 2"}],"users":[{"id":13470,"username":"anne3","avatar_template":"//www.gravatar.com/avatar/a4151b1fd72089c54e2374565a87da7f.png?s={size}\u0026r=pg\u0026d=identicon"}],"user_badge":{"id":665,"granted_at":"2014-03-09T20:30:01.190-04:00","badge_id":874,"granted_by_id":13470}},
+module("model:user-badge");
+
+const singleBadgeJson = {"badges":[{"id":874,"name":"Badge 2","description":null,"badge_type_id":7}],"badge_types":[{"id":7,"name":"Silver 2"}],"users":[{"id":13470,"username":"anne3","avatar_template":"//www.gravatar.com/avatar/a4151b1fd72089c54e2374565a87da7f.png?s={size}\u0026r=pg\u0026d=identicon"}],"user_badge":{"id":665,"granted_at":"2014-03-09T20:30:01.190-04:00","badge_id":874,"granted_by_id":13470}},
multipleBadgesJson = {"badges":[{"id":880,"name":"Badge 8","description":null,"badge_type_id":13}],"badge_types":[{"id":13,"name":"Silver 8"}],"users":[],"user_badges":[{"id":668,"granted_at":"2014-03-09T20:30:01.420-04:00","badge_id":880,"granted_by_id":null}]};
test('createFromJson single', function() {
- var userBadge = Discourse.UserBadge.createFromJson(singleBadgeJson);
+ const userBadge = UserBadge.createFromJson(singleBadgeJson);
ok(!Array.isArray(userBadge), "does not return an array");
equal(userBadge.get('badge.name'), "Badge 2", "badge reference is set");
equal(userBadge.get('badge.badge_type.name'), "Silver 2", "badge.badge_type reference is set");
@@ -12,7 +14,7 @@ test('createFromJson single', function() {
});
test('createFromJson array', function() {
- var userBadges = Discourse.UserBadge.createFromJson(multipleBadgesJson);
+ const userBadges = UserBadge.createFromJson(multipleBadgesJson);
ok(Array.isArray(userBadges), "returns an array");
equal(userBadges[0].get('granted_by'), null, "granted_by reference is not set when null");
});
@@ -20,7 +22,7 @@ test('createFromJson array', function() {
asyncTestDiscourse('findByUsername', function() {
expect(2);
sandbox.stub(Discourse, 'ajax').returns(Ember.RSVP.resolve(multipleBadgesJson));
- Discourse.UserBadge.findByUsername("anne3").then(function(badges) {
+ UserBadge.findByUsername("anne3").then(function(badges) {
ok(Array.isArray(badges), "returns an array");
start();
});
@@ -30,7 +32,7 @@ asyncTestDiscourse('findByUsername', function() {
asyncTestDiscourse('findByBadgeId', function() {
expect(2);
sandbox.stub(Discourse, 'ajax').returns(Ember.RSVP.resolve(multipleBadgesJson));
- Discourse.UserBadge.findByBadgeId(880).then(function(badges) {
+ UserBadge.findByBadgeId(880).then(function(badges) {
ok(Array.isArray(badges), "returns an array");
start();
});
@@ -40,7 +42,7 @@ asyncTestDiscourse('findByBadgeId', function() {
asyncTestDiscourse('grant', function() {
expect(2);
sandbox.stub(Discourse, 'ajax').returns(Ember.RSVP.resolve(singleBadgeJson));
- Discourse.UserBadge.grant(1, "username").then(function(userBadge) {
+ UserBadge.grant(1, "username").then(function(userBadge) {
ok(!Array.isArray(userBadge), "does not return an array");
start();
});
@@ -49,7 +51,7 @@ asyncTestDiscourse('grant', function() {
test('revoke', function() {
sandbox.stub(Discourse, 'ajax');
- var userBadge = Discourse.UserBadge.create({id: 1});
+ const userBadge = UserBadge.create({id: 1});
userBadge.revoke();
ok(Discourse.ajax.calledOnce, "makes an AJAX call");
});