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 @@ 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 @@
{{poster-name post=this}} + {{plugin-outlet "poster-name-right"}} diff --git a/app/assets/javascripts/discourse/templates/user-card.hbs b/app/assets/javascripts/discourse/templates/user-card.hbs index cdd2179867a..ce4fdb92818 100644 --- a/app/assets/javascripts/discourse/templates/user-card.hbs +++ b/app/assets/javascripts/discourse/templates/user-card.hbs @@ -16,10 +16,8 @@ {{#if user.title}}

{{user.title}}

{{/if}} - - {{#if showName}} -

{{name}}

- {{/if}} + + {{plugin-outlet "user-card-post-names"}}
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