Merge branch 'master' of github.com:discourse/discourse
This commit is contained in:
commit
dd3c685d39
|
@ -62,7 +62,7 @@ GEM
|
||||||
builder (3.2.2)
|
builder (3.2.2)
|
||||||
byebug (5.0.0)
|
byebug (5.0.0)
|
||||||
columnize (= 0.9.0)
|
columnize (= 0.9.0)
|
||||||
celluloid (0.16.1)
|
celluloid (0.16.0)
|
||||||
timers (~> 4.0.0)
|
timers (~> 4.0.0)
|
||||||
certified (1.0.0)
|
certified (1.0.0)
|
||||||
coderay (1.1.0)
|
coderay (1.1.0)
|
||||||
|
@ -485,6 +485,3 @@ DEPENDENCIES
|
||||||
uglifier
|
uglifier
|
||||||
unf
|
unf
|
||||||
unicorn
|
unicorn
|
||||||
|
|
||||||
BUNDLED WITH
|
|
||||||
1.10.6
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import UserBadge from 'discourse/models/user-badge';
|
||||||
|
|
||||||
export default Ember.ArrayController.extend({
|
export default Ember.ArrayController.extend({
|
||||||
needs: ["adminUser"],
|
needs: ["adminUser"],
|
||||||
user: Em.computed.alias('controllers.adminUser.model'),
|
user: Em.computed.alias('controllers.adminUser.model'),
|
||||||
|
@ -86,7 +88,7 @@ export default Ember.ArrayController.extend({
|
||||||
**/
|
**/
|
||||||
grantBadge: function(badgeId) {
|
grantBadge: function(badgeId) {
|
||||||
var self = this;
|
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.set('badgeReason', '');
|
||||||
self.pushObject(userBadge);
|
self.pushObject(userBadge);
|
||||||
Ember.run.next(function() {
|
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) {
|
revokeBadge: function(userBadge) {
|
||||||
var self = this;
|
var self = this;
|
||||||
return bootbox.confirm(I18n.t("admin.badges.revoke_confirm"), I18n.t("no_value"), I18n.t("yes_value"), function(result) {
|
return bootbox.confirm(I18n.t("admin.badges.revoke_confirm"), I18n.t("no_value"), I18n.t("yes_value"), function(result) {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import Badge from 'discourse/models/badge';
|
||||||
import showModal from 'discourse/lib/show-modal';
|
import showModal from 'discourse/lib/show-modal';
|
||||||
|
|
||||||
export default Ember.Route.extend({
|
export default Ember.Route.extend({
|
||||||
|
@ -7,7 +8,7 @@ export default Ember.Route.extend({
|
||||||
|
|
||||||
model(params) {
|
model(params) {
|
||||||
if (params.badge_id === "new") {
|
if (params.badge_id === "new") {
|
||||||
return Discourse.Badge.create({
|
return Badge.create({
|
||||||
name: I18n.t('admin.badges.new_badge')
|
name: I18n.t('admin.badges.new_badge')
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import Badge from 'discourse/models/badge';
|
||||||
|
|
||||||
export default Discourse.Route.extend({
|
export default Discourse.Route.extend({
|
||||||
_json: null,
|
_json: null,
|
||||||
|
|
||||||
|
@ -5,7 +7,7 @@ export default Discourse.Route.extend({
|
||||||
var self = this;
|
var self = this;
|
||||||
return Discourse.ajax('/admin/badges.json').then(function(json) {
|
return Discourse.ajax('/admin/badges.json').then(function(json) {
|
||||||
self._json = json;
|
self._json = json;
|
||||||
return Discourse.Badge.createFromJson(json);
|
return Badge.createFromJson(json);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
});
|
|
@ -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);
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -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) {
|
function proxyDep(propName, moduleFunc, msg) {
|
||||||
if (Discourse.hasOwnProperty(propName)) { return; }
|
if (Discourse.hasOwnProperty(propName)) { return; }
|
||||||
Object.defineProperty(Discourse, propName, {
|
Object.defineProperty(Discourse, propName, {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { iconHTML } from 'discourse/helpers/fa-icon';
|
import { iconHTML } from 'discourse/helpers/fa-icon';
|
||||||
|
import computed from 'ember-addons/ember-computed-decorators';
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Ember.Component.extend({
|
||||||
tagName: 'button',
|
tagName: 'button',
|
||||||
|
@ -7,17 +8,15 @@ export default Ember.Component.extend({
|
||||||
|
|
||||||
noText: Ember.computed.empty('translatedLabel'),
|
noText: Ember.computed.empty('translatedLabel'),
|
||||||
|
|
||||||
translatedTitle: function() {
|
@computed("title", "translatedLabel")
|
||||||
const title = this.get('title');
|
translatedTitle(title, translatedLabel) {
|
||||||
return title ? I18n.t(title) : this.get('translatedLabel');
|
return title ? I18n.t(title) : translatedLabel;
|
||||||
}.property('title', 'translatedLabel'),
|
},
|
||||||
|
|
||||||
translatedLabel: function() {
|
@computed("label")
|
||||||
const label = this.get('label');
|
translatedLabel(label) {
|
||||||
if (label) {
|
if (label) return I18n.t(label);
|
||||||
return I18n.t(this.get('label'));
|
},
|
||||||
}
|
|
||||||
}.property('label'),
|
|
||||||
|
|
||||||
render(buffer) {
|
render(buffer) {
|
||||||
const label = this.get('translatedLabel'),
|
const label = this.get('translatedLabel'),
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import UserBadge from 'discourse/models/user-badge';
|
||||||
|
|
||||||
export default Ember.Controller.extend({
|
export default Ember.Controller.extend({
|
||||||
noMoreBadges: false,
|
noMoreBadges: false,
|
||||||
userBadges: null,
|
userBadges: null,
|
||||||
|
@ -8,7 +10,7 @@ export default Ember.Controller.extend({
|
||||||
const self = this;
|
const self = this;
|
||||||
const userBadges = this.get('userBadges');
|
const userBadges = this.get('userBadges');
|
||||||
|
|
||||||
Discourse.UserBadge.findByBadgeId(this.get('model.id'), {
|
UserBadge.findByBadgeId(this.get('model.id'), {
|
||||||
offset: userBadges.length
|
offset: userBadges.length
|
||||||
}).then(function(result) {
|
}).then(function(result) {
|
||||||
userBadges.pushObjects(result);
|
userBadges.pushObjects(result);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import ModalFunctionality from 'discourse/mixins/modal-functionality';
|
import ModalFunctionality from 'discourse/mixins/modal-functionality';
|
||||||
import { categoryBadgeHTML } from 'discourse/helpers/category-link';
|
import { categoryBadgeHTML } from 'discourse/helpers/category-link';
|
||||||
import computed from 'ember-addons/ember-computed-decorators';
|
import computed from 'ember-addons/ember-computed-decorators';
|
||||||
|
import { propertyGreaterThan, propertyLessThan } from 'discourse/lib/computed';
|
||||||
|
|
||||||
// This controller handles displaying of history
|
// This controller handles displaying of history
|
||||||
export default Ember.Controller.extend(ModalFunctionality, {
|
export default Ember.Controller.extend(ModalFunctionality, {
|
||||||
|
@ -15,30 +16,28 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
||||||
refresh(postId, postVersion) {
|
refresh(postId, postVersion) {
|
||||||
this.set("loading", true);
|
this.set("loading", true);
|
||||||
|
|
||||||
var self = this;
|
Discourse.Post.loadRevision(postId, postVersion).then(result => {
|
||||||
Discourse.Post.loadRevision(postId, postVersion).then(function (result) {
|
this.setProperties({ loading: false, model: result });
|
||||||
self.setProperties({ loading: false, model: result });
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
hide(postId, postVersion) {
|
hide(postId, postVersion) {
|
||||||
var self = this;
|
Discourse.Post.hideRevision(postId, postVersion).then(() => this.refresh(postId, postVersion));
|
||||||
Discourse.Post.hideRevision(postId, postVersion).then(function () {
|
|
||||||
self.refresh(postId, postVersion);
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
show(postId, postVersion) {
|
show(postId, postVersion) {
|
||||||
var self = this;
|
Discourse.Post.showRevision(postId, postVersion).then(() => this.refresh(postId, postVersion));
|
||||||
Discourse.Post.showRevision(postId, postVersion).then(function () {
|
|
||||||
self.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')
|
@computed('model.current_version')
|
||||||
previousVersion(current) { return current - 1; },
|
previousVersion(current) {
|
||||||
|
return current - 1;
|
||||||
|
},
|
||||||
|
|
||||||
@computed('model.current_revision', 'model.previous_revision')
|
@computed('model.current_revision', 'model.previous_revision')
|
||||||
displayGoToPrevious(current, prev) {
|
displayGoToPrevious(current, prev) {
|
||||||
|
@ -46,18 +45,28 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
||||||
},
|
},
|
||||||
|
|
||||||
displayRevisions: Ember.computed.gt("model.version_count", 2),
|
displayRevisions: Ember.computed.gt("model.version_count", 2),
|
||||||
displayGoToFirst: Ember.computed.gt('model.current_revision', 'model.first_revision'),
|
displayGoToFirst: propertyGreaterThan("model.current_revision", "model.first_revision"),
|
||||||
displayGoToNext: Ember.computed.lt("model.current_revision", "model.next_revision"),
|
displayGoToNext: propertyLessThan("model.current_revision", "model.next_revision"),
|
||||||
displayGoToLast: Ember.computed.lt("model.current_revision", "model.next_revision"),
|
displayGoToLast: propertyLessThan("model.current_revision", "model.next_revision"),
|
||||||
|
|
||||||
@computed('model.previous_hidden', 'loading')
|
hideGoToFirst: Ember.computed.not("displayGoToFirst"),
|
||||||
displayShow: function(prevHidden, loading) {
|
hideGoToPrevious: Ember.computed.not("displayGoToPrevious"),
|
||||||
return prevHidden && this.currentUser.get('staff') && !loading;
|
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')
|
@computed('model.previous_hidden')
|
||||||
displayHide: function(prevHidden, loading) {
|
displayHide(prevHidden) {
|
||||||
return !prevHidden && this.currentUser.get('staff') && !loading;
|
return !prevHidden && this.currentUser.get('staff');
|
||||||
},
|
},
|
||||||
|
|
||||||
isEitherRevisionHidden: Ember.computed.or("model.previous_hidden", "model.current_hidden"),
|
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"),
|
displayingSideBySide: Em.computed.equal("viewMode", "side_by_side"),
|
||||||
displayingSideBySideMarkdown: Em.computed.equal("viewMode", "side_by_side_markdown"),
|
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')
|
@computed('model.category_id_changes')
|
||||||
previousCategory(changes) {
|
previousCategory(changes) {
|
||||||
if (changes) {
|
if (changes) {
|
||||||
|
@ -116,16 +134,16 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
loadFirstVersion: function() { this.refresh(this.get("model.post_id"), this.get("model.first_revision")); },
|
loadFirstVersion() { 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")); },
|
loadPreviousVersion() { 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")); },
|
loadNextVersion() { 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")); },
|
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")); },
|
hideVersion() { 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")); },
|
showVersion() { this.show(this.get("model.post_id"), this.get("model.current_revision")); },
|
||||||
|
|
||||||
displayInline: function() { this.set("viewMode", "inline"); },
|
displayInline() { this.set("viewMode", "inline"); },
|
||||||
displaySideBySide: function() { this.set("viewMode", "side_by_side"); },
|
displaySideBySide() { this.set("viewMode", "side_by_side"); },
|
||||||
displaySideBySideMarkdown: function() { this.set("viewMode", "side_by_side_markdown"); }
|
displaySideBySideMarkdown() { this.set("viewMode", "side_by_side_markdown"); }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -26,6 +26,18 @@ export function propertyNotEqual(p1, p2) {
|
||||||
}).property(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.
|
Returns i18n version of a string based on a property.
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
|
import Badge from 'discourse/models/badge';
|
||||||
|
|
||||||
export default Ember.Mixin.create({
|
export default Ember.Mixin.create({
|
||||||
saving: false,
|
saving: false,
|
||||||
saved: false,
|
saved: false,
|
||||||
|
|
||||||
selectableUserBadges: function() {
|
selectableUserBadges: function() {
|
||||||
var items = this.get('filteredList');
|
let items = this.get('filteredList');
|
||||||
items = _.uniq(items, false, function(e) { return e.get('badge.name'); });
|
items = _.uniq(items, false, function(e) { return e.get('badge.name'); });
|
||||||
items.unshiftObject(Em.Object.create({
|
items.unshiftObject(Em.Object.create({
|
||||||
badge: Discourse.Badge.create({name: I18n.t('badges.none')})
|
badge: Badge.create({name: I18n.t('badges.none')})
|
||||||
}));
|
}));
|
||||||
return items;
|
return items;
|
||||||
}.property('filteredList'),
|
}.property('filteredList'),
|
||||||
|
@ -20,8 +22,8 @@ export default Ember.Mixin.create({
|
||||||
}.property('saving'),
|
}.property('saving'),
|
||||||
|
|
||||||
selectedUserBadge: function() {
|
selectedUserBadge: function() {
|
||||||
var selectedUserBadgeId = parseInt(this.get('selectedUserBadgeId'));
|
const selectedUserBadgeId = parseInt(this.get('selectedUserBadgeId'));
|
||||||
var selectedUserBadge = null;
|
let selectedUserBadge = null;
|
||||||
this.get('selectableUserBadges').forEach(function(userBadge) {
|
this.get('selectableUserBadges').forEach(function(userBadge) {
|
||||||
if (userBadge.get('id') === selectedUserBadgeId) {
|
if (userBadge.get('id') === selectedUserBadgeId) {
|
||||||
selectedUserBadge = userBadge;
|
selectedUserBadge = userBadge;
|
||||||
|
|
|
@ -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')});
|
||||||
|
}
|
||||||
|
});
|
|
@ -1,22 +1,12 @@
|
||||||
/**
|
import BadgeGrouping from 'discourse/models/badge-grouping';
|
||||||
A data model representing a badge on Discourse
|
import RestModel from 'discourse/models/rest';
|
||||||
|
|
||||||
@class Badge
|
const Badge = RestModel.extend({
|
||||||
@extends Discourse.Model
|
|
||||||
@namespace Discourse
|
|
||||||
@module Discourse
|
|
||||||
**/
|
|
||||||
Discourse.Badge = Discourse.Model.extend({
|
|
||||||
/**
|
|
||||||
Is this a new badge?
|
|
||||||
|
|
||||||
@property newBadge
|
|
||||||
@type {String}
|
|
||||||
**/
|
|
||||||
newBadge: Em.computed.none('id'),
|
newBadge: Em.computed.none('id'),
|
||||||
|
|
||||||
hasQuery: function(){
|
hasQuery: function(){
|
||||||
var query = this.get('query');
|
const query = this.get('query');
|
||||||
return query && query.trim().length > 0;
|
return query && query.trim().length > 0;
|
||||||
}.property('query'),
|
}.property('query'),
|
||||||
|
|
||||||
|
@ -40,7 +30,7 @@ Discourse.Badge = Discourse.Model.extend({
|
||||||
@type {String}
|
@type {String}
|
||||||
**/
|
**/
|
||||||
displayName: function() {
|
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')});
|
return I18n.t(i18nKey, {defaultValue: this.get('name')});
|
||||||
}.property('name', 'i18nNameKey'),
|
}.property('name', 'i18nNameKey'),
|
||||||
|
|
||||||
|
@ -52,8 +42,8 @@ Discourse.Badge = Discourse.Model.extend({
|
||||||
@type {String}
|
@type {String}
|
||||||
**/
|
**/
|
||||||
translatedDescription: function() {
|
translatedDescription: function() {
|
||||||
var i18nKey = "badges.badge." + this.get('i18nNameKey') + ".description",
|
const i18nKey = "badges.badge." + this.get('i18nNameKey') + ".description";
|
||||||
translation = I18n.t(i18nKey);
|
let translation = I18n.t(i18nKey);
|
||||||
if (translation.indexOf(i18nKey) !== -1) {
|
if (translation.indexOf(i18nKey) !== -1) {
|
||||||
translation = null;
|
translation = null;
|
||||||
}
|
}
|
||||||
|
@ -73,7 +63,7 @@ Discourse.Badge = Discourse.Model.extend({
|
||||||
@type {String}
|
@type {String}
|
||||||
**/
|
**/
|
||||||
displayDescriptionHtml: function() {
|
displayDescriptionHtml: function() {
|
||||||
var translated = this.get('translatedDescription');
|
const translated = this.get('translatedDescription');
|
||||||
return (translated === null ? this.get('description') : translated) || "";
|
return (translated === null ? this.get('description') : translated) || "";
|
||||||
}.property('description', 'translatedDescription'),
|
}.property('description', 'translatedDescription'),
|
||||||
|
|
||||||
|
@ -84,7 +74,7 @@ Discourse.Badge = Discourse.Model.extend({
|
||||||
@param {Object} json The JSON response returned by the server
|
@param {Object} json The JSON response returned by the server
|
||||||
**/
|
**/
|
||||||
updateFromJson: function(json) {
|
updateFromJson: function(json) {
|
||||||
var self = this;
|
const self = this;
|
||||||
if (json.badge) {
|
if (json.badge) {
|
||||||
Object.keys(json.badge).forEach(function(key) {
|
Object.keys(json.badge).forEach(function(key) {
|
||||||
self.set(key, json.badge[key]);
|
self.set(key, json.badge[key]);
|
||||||
|
@ -100,7 +90,7 @@ Discourse.Badge = Discourse.Model.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
badgeTypeClassName: function() {
|
badgeTypeClassName: function() {
|
||||||
var type = this.get('badge_type.name') || "";
|
const type = this.get('badge_type.name') || "";
|
||||||
return "badge-type-" + type.toLowerCase();
|
return "badge-type-" + type.toLowerCase();
|
||||||
}.property('badge_type.name'),
|
}.property('badge_type.name'),
|
||||||
|
|
||||||
|
@ -111,9 +101,9 @@ Discourse.Badge = Discourse.Model.extend({
|
||||||
@returns {Promise} A promise that resolves to the updated `Discourse.Badge`
|
@returns {Promise} A promise that resolves to the updated `Discourse.Badge`
|
||||||
**/
|
**/
|
||||||
save: function(data) {
|
save: function(data) {
|
||||||
var url = "/admin/badges",
|
let url = "/admin/badges",
|
||||||
requestType = "POST",
|
requestType = "POST";
|
||||||
self = this;
|
const self = this;
|
||||||
|
|
||||||
if (this.get('id')) {
|
if (this.get('id')) {
|
||||||
// We are updating an existing badge.
|
// 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.
|
Create `Discourse.Badge` instances from the server JSON response.
|
||||||
|
|
||||||
|
@ -156,29 +146,29 @@ Discourse.Badge.reopenClass({
|
||||||
**/
|
**/
|
||||||
createFromJson: function(json) {
|
createFromJson: function(json) {
|
||||||
// Create BadgeType objects.
|
// Create BadgeType objects.
|
||||||
var badgeTypes = {};
|
const badgeTypes = {};
|
||||||
if ('badge_types' in json) {
|
if ('badge_types' in json) {
|
||||||
json.badge_types.forEach(function(badgeTypeJson) {
|
json.badge_types.forEach(function(badgeTypeJson) {
|
||||||
badgeTypes[badgeTypeJson.id] = Ember.Object.create(badgeTypeJson);
|
badgeTypes[badgeTypeJson.id] = Ember.Object.create(badgeTypeJson);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var badgeGroupings = {};
|
const badgeGroupings = {};
|
||||||
if ('badge_groupings' in json) {
|
if ('badge_groupings' in json) {
|
||||||
json.badge_groupings.forEach(function(badgeGroupingJson) {
|
json.badge_groupings.forEach(function(badgeGroupingJson) {
|
||||||
badgeGroupings[badgeGroupingJson.id] = Discourse.BadgeGrouping.create(badgeGroupingJson);
|
badgeGroupings[badgeGroupingJson.id] = BadgeGrouping.create(badgeGroupingJson);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create Badge objects.
|
// Create Badge objects.
|
||||||
var badges = [];
|
let badges = [];
|
||||||
if ("badge" in json) {
|
if ("badge" in json) {
|
||||||
badges = [json.badge];
|
badges = [json.badge];
|
||||||
} else {
|
} else {
|
||||||
badges = json.badges;
|
badges = json.badges;
|
||||||
}
|
}
|
||||||
badges = badges.map(function(badgeJson) {
|
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_type', badgeTypes[badge.get('badge_type_id')]);
|
||||||
badge.set('badge_grouping', badgeGroupings[badge.get('badge_grouping_id')]);
|
badge.set('badge_grouping', badgeGroupings[badge.get('badge_grouping_id')]);
|
||||||
return badge;
|
return badge;
|
||||||
|
@ -198,7 +188,7 @@ Discourse.Badge.reopenClass({
|
||||||
@returns {Promise} a promise that resolves to an array of `Discourse.Badge`
|
@returns {Promise} a promise that resolves to an array of `Discourse.Badge`
|
||||||
**/
|
**/
|
||||||
findAll: function(opts) {
|
findAll: function(opts) {
|
||||||
var listable = "";
|
let listable = "";
|
||||||
if(opts && opts.onlyListable){
|
if(opts && opts.onlyListable){
|
||||||
listable = "?only_listable=true";
|
listable = "?only_listable=true";
|
||||||
}
|
}
|
||||||
|
@ -220,3 +210,6 @@ Discourse.Badge.reopenClass({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export default Badge;
|
||||||
|
|
|
@ -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()
|
|
||||||
});
|
|
|
@ -504,7 +504,9 @@ const Composer = RestModel.extend({
|
||||||
return post.save(props).then(function(result) {
|
return post.save(props).then(function(result) {
|
||||||
self.clearState();
|
self.clearState();
|
||||||
return result;
|
return result;
|
||||||
}).catch(rollback);
|
}).catch(function(error) {
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
}).catch(rollback);
|
}).catch(rollback);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -162,7 +162,9 @@ const Post = RestModel.extend({
|
||||||
|
|
||||||
// Recover a deleted post
|
// Recover a deleted post
|
||||||
recover() {
|
recover() {
|
||||||
const post = this;
|
const post = this,
|
||||||
|
initProperties = post.getProperties('deleted_at', 'deleted_by', 'user_deleted', 'can_delete');
|
||||||
|
|
||||||
post.setProperties({
|
post.setProperties({
|
||||||
deleted_at: null,
|
deleted_at: null,
|
||||||
deleted_by: null,
|
deleted_by: null,
|
||||||
|
@ -178,6 +180,9 @@ const Post = RestModel.extend({
|
||||||
can_delete: true,
|
can_delete: true,
|
||||||
version: data.version
|
version: data.version
|
||||||
});
|
});
|
||||||
|
}).catch(function(error) {
|
||||||
|
popupAjaxError(error);
|
||||||
|
post.setProperties(initProperties);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -220,6 +225,7 @@ const Post = RestModel.extend({
|
||||||
cooked: this.get('oldCooked'),
|
cooked: this.get('oldCooked'),
|
||||||
version: this.get('version') - 1,
|
version: this.get('version') - 1,
|
||||||
can_recover: false,
|
can_recover: false,
|
||||||
|
can_delete: true,
|
||||||
user_deleted: false
|
user_deleted: false
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -157,9 +157,13 @@ const Topic = RestModel.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
saveStatus(property, value, until) {
|
saveStatus(property, value, until) {
|
||||||
if (property === 'closed' && value === true) {
|
if (property === 'closed') {
|
||||||
|
this.incrementProperty('posts_count');
|
||||||
|
|
||||||
|
if (value === true) {
|
||||||
this.set('details.auto_close_at', null);
|
this.set('details.auto_close_at', null);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return Discourse.ajax(this.get('url') + "/status", {
|
return Discourse.ajax(this.get('url') + "/status", {
|
||||||
type: 'PUT',
|
type: 'PUT',
|
||||||
data: {
|
data: {
|
||||||
|
|
|
@ -1,12 +1,6 @@
|
||||||
/**
|
import Badge from 'discourse/models/badge';
|
||||||
A data model representing a user badge grant on Discourse
|
|
||||||
|
|
||||||
@class UserBadge
|
const UserBadge = Discourse.Model.extend({
|
||||||
@extends Discourse.Model
|
|
||||||
@namespace Discourse
|
|
||||||
@module Discourse
|
|
||||||
**/
|
|
||||||
Discourse.UserBadge = Discourse.Model.extend({
|
|
||||||
postUrl: function() {
|
postUrl: function() {
|
||||||
if(this.get('topic_title')) {
|
if(this.get('topic_title')) {
|
||||||
return "/t/-/" + this.get('topic_id') + "/" + this.get('post_number');
|
return "/t/-/" + this.get('topic_id') + "/" + this.get('post_number');
|
||||||
|
@ -25,14 +19,8 @@ Discourse.UserBadge = Discourse.Model.extend({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Discourse.UserBadge.reopenClass({
|
UserBadge.reopenClass({
|
||||||
/**
|
|
||||||
Create `Discourse.UserBadge` instances from the server JSON response.
|
|
||||||
|
|
||||||
@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) {
|
createFromJson: function(json) {
|
||||||
// Create User objects.
|
// Create User objects.
|
||||||
if (json.users === undefined) { json.users = []; }
|
if (json.users === undefined) { json.users = []; }
|
||||||
|
@ -51,7 +39,7 @@ Discourse.UserBadge.reopenClass({
|
||||||
// Create the badges.
|
// Create the badges.
|
||||||
if (json.badges === undefined) { json.badges = []; }
|
if (json.badges === undefined) { json.badges = []; }
|
||||||
var badges = {};
|
var badges = {};
|
||||||
Discourse.Badge.createFromJson(json).forEach(function(badge) {
|
Badge.createFromJson(json).forEach(function(badge) {
|
||||||
badges[badge.get('id')] = badge;
|
badges[badge.get('id')] = badge;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -146,3 +134,5 @@ Discourse.UserBadge.reopenClass({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export default UserBadge;
|
|
@ -6,6 +6,8 @@ import UserPostsStream from 'discourse/models/user-posts-stream';
|
||||||
import Singleton from 'discourse/mixins/singleton';
|
import Singleton from 'discourse/mixins/singleton';
|
||||||
import { longDate } from 'discourse/lib/formatter';
|
import { longDate } from 'discourse/lib/formatter';
|
||||||
import computed from 'ember-addons/ember-computed-decorators';
|
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({
|
const User = RestModel.extend({
|
||||||
|
|
||||||
|
@ -299,8 +301,8 @@ const User = RestModel.extend({
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Em.isEmpty(json.user.featured_user_badge_ids)) {
|
if (!Em.isEmpty(json.user.featured_user_badge_ids)) {
|
||||||
var userBadgesMap = {};
|
const userBadgesMap = {};
|
||||||
Discourse.UserBadge.createFromJson(json).forEach(function(userBadge) {
|
UserBadge.createFromJson(json).forEach(function(userBadge) {
|
||||||
userBadgesMap[ userBadge.get('id') ] = userBadge;
|
userBadgesMap[ userBadge.get('id') ] = userBadge;
|
||||||
});
|
});
|
||||||
json.user.featured_user_badges = json.user.featured_user_badge_ids.map(function(id) {
|
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) {
|
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);
|
user.setProperties(json.user);
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
|
import Badge from 'discourse/models/badge';
|
||||||
|
|
||||||
export default Discourse.Route.extend({
|
export default Discourse.Route.extend({
|
||||||
model() {
|
model() {
|
||||||
if (PreloadStore.get("badges")) {
|
if (PreloadStore.get("badges")) {
|
||||||
return PreloadStore.getAndRemove("badges").then(json => Discourse.Badge.createFromJson(json));
|
return PreloadStore.getAndRemove("badges").then(json => Badge.createFromJson(json));
|
||||||
} else {
|
} else {
|
||||||
return Discourse.Badge.findAll({ onlyListable: true });
|
return Badge.findAll({ onlyListable: true });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
import UserBadge from 'discourse/models/user-badge';
|
||||||
|
import Badge from 'discourse/models/badge';
|
||||||
|
|
||||||
export default Discourse.Route.extend({
|
export default Discourse.Route.extend({
|
||||||
actions: {
|
actions: {
|
||||||
didTransition() {
|
didTransition() {
|
||||||
|
@ -15,14 +18,14 @@ export default Discourse.Route.extend({
|
||||||
|
|
||||||
model(params) {
|
model(params) {
|
||||||
if (PreloadStore.get("badge")) {
|
if (PreloadStore.get("badge")) {
|
||||||
return PreloadStore.getAndRemove("badge").then(json => Discourse.Badge.createFromJson(json));
|
return PreloadStore.getAndRemove("badge").then(json => Badge.createFromJson(json));
|
||||||
} else {
|
} else {
|
||||||
return Discourse.Badge.findById(params.id);
|
return Badge.findById(params.id);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
afterModel(model) {
|
afterModel(model) {
|
||||||
return Discourse.UserBadge.findByBadgeId(model.get("id")).then(userBadges => {
|
return UserBadge.findByBadgeId(model.get("id")).then(userBadges => {
|
||||||
this.userBadges = userBadges;
|
this.userBadges = userBadges;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import showModal from "discourse/lib/show-modal";
|
import showModal from "discourse/lib/show-modal";
|
||||||
import OpenComposer from "discourse/mixins/open-composer";
|
import OpenComposer from "discourse/mixins/open-composer";
|
||||||
|
|
||||||
Discourse.DiscoveryCategoriesRoute = Discourse.Route.extend(OpenComposer, {
|
const DiscoveryCategoriesRoute = Discourse.Route.extend(OpenComposer, {
|
||||||
renderTemplate() {
|
renderTemplate() {
|
||||||
this.render("navigation/categories", { outlet: "navigation-bar" });
|
this.render("navigation/categories", { outlet: "navigation-bar" });
|
||||||
this.render("discovery/categories", { outlet: "list-container" });
|
this.render("discovery/categories", { outlet: "list-container" });
|
||||||
|
@ -67,4 +67,4 @@ Discourse.DiscoveryCategoriesRoute = Discourse.Route.extend(OpenComposer, {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export default Discourse.DiscoveryCategoriesRoute;
|
export default DiscoveryCategoriesRoute;
|
|
@ -1,8 +1,9 @@
|
||||||
|
import UserBadge from 'discourse/models/badge';
|
||||||
import RestrictedUserRoute from "discourse/routes/restricted-user";
|
import RestrictedUserRoute from "discourse/routes/restricted-user";
|
||||||
|
|
||||||
export default RestrictedUserRoute.extend({
|
export default RestrictedUserRoute.extend({
|
||||||
model: function() {
|
model: function() {
|
||||||
return Discourse.UserBadge.findByUsername(this.modelFor('user').get('username'));
|
return UserBadge.findByUsername(this.modelFor('user').get('username'));
|
||||||
},
|
},
|
||||||
|
|
||||||
renderTemplate: function() {
|
renderTemplate: function() {
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
|
import UserBadge from 'discourse/models/user-badge';
|
||||||
import RestrictedUserRoute from "discourse/routes/restricted-user";
|
import RestrictedUserRoute from "discourse/routes/restricted-user";
|
||||||
|
|
||||||
export default RestrictedUserRoute.extend({
|
export default RestrictedUserRoute.extend({
|
||||||
model: function() {
|
model: function() {
|
||||||
return Discourse.UserBadge.findByUsername(this.modelFor('user').get('username'));
|
return UserBadge.findByUsername(this.modelFor('user').get('username'));
|
||||||
},
|
},
|
||||||
|
|
||||||
renderTemplate: function() {
|
renderTemplate: function() {
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import ViewingActionType from "discourse/mixins/viewing-action-type";
|
import ViewingActionType from "discourse/mixins/viewing-action-type";
|
||||||
|
import UserBadge from 'discourse/models/user-badge';
|
||||||
|
|
||||||
export default Discourse.Route.extend(ViewingActionType, {
|
export default Discourse.Route.extend(ViewingActionType, {
|
||||||
model() {
|
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) {
|
setupController(controller, model) {
|
||||||
|
|
|
@ -1,27 +1,27 @@
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div>
|
<div>
|
||||||
<div id="revision-controls">
|
<div id="revision-controls">
|
||||||
<button title="{{i18n 'post.revisions.controls.first'}}" {{bind-attr class=":btn :standard :no-text displayGoToFirst::invisible" disabled=loading}} {{action "loadFirstVersion"}}><i class="fa fa-fast-backward"></i></button>
|
{{d-button action="loadFirstVersion" icon="fast-backward" title="post.revisions.controls.first" disabled=loadFirstDisabled}}
|
||||||
<button title="{{i18n 'post.revisions.controls.previous'}}" {{bind-attr class=":btn :standard :no-text displayGoToPrevious::invisible" disabled=loading}} {{action "loadPreviousVersion"}}><i class="fa fa-backward"></i></button>
|
{{d-button action="loadPreviousVersion" icon="backward" title="post.revisions.controls.previous" disabled=loadPreviousDisabled}}
|
||||||
<div id="revision-numbers" {{bind-attr class="displayRevisions::invisible"}}>
|
<div id="revision-numbers" {{bind-attr class="displayRevisions::invisible"}}>
|
||||||
{{#conditional-loading-spinner condition=loading size="small"}}
|
{{#conditional-loading-spinner condition=loading size="small"}}
|
||||||
{{boundI18n revisionsTextKey previousBinding="previousVersion" currentBinding="model.current_version" totalBinding="model.version_count"}}
|
{{boundI18n revisionsTextKey previousBinding="previousVersion" currentBinding="model.current_version" totalBinding="model.version_count"}}
|
||||||
{{/conditional-loading-spinner}}
|
{{/conditional-loading-spinner}}
|
||||||
</div>
|
</div>
|
||||||
<button title="{{i18n 'post.revisions.controls.next'}}" {{bind-attr class=":btn :standard :no-text displayGoToNext::invisible" disabled=loading}} {{action "loadNextVersion"}}><i class="fa fa-forward"></i></button>
|
{{d-button action="loadNextVersion" icon="forward" title="post.revisions.controls.next" disabled=loadNextDisabled}}
|
||||||
<button title="{{i18n 'post.revisions.controls.last'}}" {{bind-attr class=":btn :standard :no-text displayGoToLast::invisible" disabled=loading}} {{action "loadLastVersion"}}><i class="fa fa-fast-forward"></i></button>
|
{{d-button action="loadLastVersion" icon="fast-forward" title="post.revisions.controls.last" disabled=loadLastDisabled}}
|
||||||
{{#if displayHide}}
|
{{#if displayHide}}
|
||||||
<button title="{{i18n 'post.revisions.controls.hide'}}" {{bind-attr class=":btn :standard :no-text :btn-danger" disabled=loading}} {{action "hideVersion"}}><i class="fa fa-trash-o"></i></button>
|
{{d-button action="hideVersion" icon="trash-o" title="post.revisions.controls.hide" class="btn-danger" disabled=loading}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if displayShow}}
|
{{#if displayShow}}
|
||||||
<button title="{{i18n 'post.revisions.controls.show'}}" {{bind-attr class=":btn :standard :no-text" disabled=loading}} {{action "showVersion"}}><i class="fa fa-undo"></i></button>
|
{{d-button action="showVersion" icon="undo" title="post.revisions.controls.show" disabled=loading}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
<div id="display-modes">
|
<div id="display-modes">
|
||||||
<button {{bind-attr class=":btn displayingInline:btn-primary:standard"}} title="{{i18n 'post.revisions.displays.inline.title'}}" {{action "displayInline"}}>{{{i18n 'post.revisions.displays.inline.button'}}}</button>
|
{{d-button action="displayInline" label="post.revisions.displays.inline.button" title="post.revisions.displays.inline.title" class=inlineClass}}
|
||||||
{{#unless site.mobileView}}
|
{{#unless site.mobileView}}
|
||||||
<button {{bind-attr class=":btn displayingSideBySide:btn-primary:standard"}} title="{{i18n 'post.revisions.displays.side_by_side.title'}}" {{action "displaySideBySide"}}>{{{i18n 'post.revisions.displays.side_by_side.button'}}}</button>
|
{{d-button action="displaySideBySide" label="post.revisions.displays.side_by_side.button" title="post.revisions.displays.side_by_side.title" class=sideBySideClass}}
|
||||||
<button {{bind-attr class=":btn displayingSideBySideMarkdown:btn-primary:standard"}} title="{{i18n 'post.revisions.displays.side_by_side_markdown.title'}}" {{action "displaySideBySideMarkdown"}}>{{{i18n 'post.revisions.displays.side_by_side_markdown.button'}}}</button>
|
{{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}}
|
{{/unless}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<p>{{{i18n 'topic.merge_topic.instructions' count=selectedPostsCount}}}</p>
|
<p>{{{i18n 'topic.merge_topic.instructions' count=selectedPostsCount}}}</p>
|
||||||
|
|
||||||
<form>
|
<form>
|
||||||
{{view "choose-topic" selectedTopicId=selectedTopicId}}
|
{{view "choose-topic" currentTopicId=model.id selectedTopicId=selectedTopicId}}
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
<div class='topic-body'>
|
<div class='topic-body'>
|
||||||
<div class='topic-meta-data'>
|
<div class='topic-meta-data'>
|
||||||
{{poster-name post=this}}
|
{{poster-name post=this}}
|
||||||
|
{{plugin-outlet "poster-name-right"}}
|
||||||
<div class='post-info'>
|
<div class='post-info'>
|
||||||
<a class='post-date' {{bind-attr href="shareUrl" data-share-url="shareUrl" data-post-number="post_number"}}>{{age-with-tooltip created_at}}</a>
|
<a class='post-date' {{bind-attr href="shareUrl" data-share-url="shareUrl" data-post-number="post_number"}}>{{age-with-tooltip created_at}}</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -17,9 +17,7 @@
|
||||||
<h2>{{user.title}}</h2>
|
<h2>{{user.title}}</h2>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if showName}}
|
{{plugin-outlet "user-card-post-names"}}
|
||||||
<h2><a href={{user.path}} {{action "showUser"}}>{{name}}</a></h2>
|
|
||||||
{{/if}}
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -5,14 +5,16 @@ export default Ember.View.extend({
|
||||||
templateName: 'choose_topic',
|
templateName: 'choose_topic',
|
||||||
|
|
||||||
topicTitleChanged: function() {
|
topicTitleChanged: function() {
|
||||||
this.set('loading', true);
|
this.setProperties({
|
||||||
this.set('noResults', true);
|
loading: true,
|
||||||
this.set('selectedTopicId', null);
|
noResults: true,
|
||||||
|
selectedTopicId: null,
|
||||||
|
});
|
||||||
this.search(this.get('topicTitle'));
|
this.search(this.get('topicTitle'));
|
||||||
}.observes('topicTitle'),
|
}.observes('topicTitle'),
|
||||||
|
|
||||||
topicsChanged: function() {
|
topicsChanged: function() {
|
||||||
var topics = this.get('topics');
|
const topics = this.get('topics');
|
||||||
if (topics) {
|
if (topics) {
|
||||||
this.set('noResults', topics.length === 0);
|
this.set('noResults', topics.length === 0);
|
||||||
}
|
}
|
||||||
|
@ -20,14 +22,17 @@ export default Ember.View.extend({
|
||||||
}.observes('topics'),
|
}.observes('topics'),
|
||||||
|
|
||||||
search: debounce(function(title) {
|
search: debounce(function(title) {
|
||||||
var self = this;
|
const self = this,
|
||||||
|
currentTopicId = this.get("currentTopicId");
|
||||||
|
|
||||||
if (Em.isEmpty(title)) {
|
if (Em.isEmpty(title)) {
|
||||||
self.setProperties({ topics: null, loading: false });
|
self.setProperties({ topics: null, loading: false });
|
||||||
return;
|
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) {
|
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 {
|
} else {
|
||||||
self.setProperties({ topics: null, loading: false });
|
self.setProperties({ topics: null, loading: false });
|
||||||
}
|
}
|
||||||
|
@ -36,7 +41,7 @@ export default Ember.View.extend({
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
chooseTopic: function (topic) {
|
chooseTopic: function (topic) {
|
||||||
var topicId = Em.get(topic, 'id');
|
const topicId = Em.get(topic, 'id');
|
||||||
this.set('selectedTopicId', topicId);
|
this.set('selectedTopicId', topicId);
|
||||||
|
|
||||||
Em.run.next(function () {
|
Em.run.next(function () {
|
||||||
|
|
|
@ -25,6 +25,9 @@
|
||||||
//= require ./discourse/lib/eyeline
|
//= require ./discourse/lib/eyeline
|
||||||
//= require ./discourse/helpers/register-unbound
|
//= require ./discourse/helpers/register-unbound
|
||||||
//= require ./discourse/mixins/scrolling
|
//= require ./discourse/mixins/scrolling
|
||||||
|
//= require ./discourse/models/rest
|
||||||
|
//= require ./discourse/models/badge-grouping
|
||||||
|
//= require ./discourse/models/badge
|
||||||
//= require_tree ./discourse/mixins
|
//= require_tree ./discourse/mixins
|
||||||
//= require ./discourse/lib/ajax-error
|
//= require ./discourse/lib/ajax-error
|
||||||
//= require ./discourse/lib/markdown
|
//= require ./discourse/lib/markdown
|
||||||
|
@ -51,6 +54,7 @@
|
||||||
//= require ./discourse/models/draft
|
//= require ./discourse/models/draft
|
||||||
//= require ./discourse/models/composer
|
//= require ./discourse/models/composer
|
||||||
//= require ./discourse/models/invite
|
//= require ./discourse/models/invite
|
||||||
|
//= require ./discourse/models/user-badge
|
||||||
//= require ./discourse/controllers/discovery-sortable
|
//= require ./discourse/controllers/discovery-sortable
|
||||||
//= require ./discourse/controllers/navigation/default
|
//= require ./discourse/controllers/navigation/default
|
||||||
//= require ./discourse/views/grouped
|
//= require ./discourse/views/grouped
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
float: right;
|
float: right;
|
||||||
font-size: 1.429em;
|
font-size: 1.429em;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
line-height: 18px;
|
|
||||||
color: $primary;
|
color: $primary;
|
||||||
opacity: 0.2;
|
opacity: 0.2;
|
||||||
filter: alpha(opacity = 20);
|
filter: alpha(opacity = 20);
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
|
|
||||||
.number, .time-read {
|
.number, .time-read {
|
||||||
font-size: 1.4em;
|
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%);
|
background-color: dark-light-diff($highlight, $secondary, 70%, -80%);
|
||||||
|
|
||||||
.username a, .name, .title, .number, .time-read {
|
.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%));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,8 +32,7 @@ small {
|
||||||
|
|
||||||
|
|
||||||
blockquote {
|
blockquote {
|
||||||
border-left: 5px solid dark-light-diff($primary, $secondary, 90%, -75%);
|
@include post-aside;
|
||||||
background-color: dark-light-diff($primary, $secondary, 97%, -65%);
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
clear: both;
|
clear: both;
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,9 +51,7 @@ body img.emoji {
|
||||||
}
|
}
|
||||||
|
|
||||||
.emoji-modal .toolbar {
|
.emoji-modal .toolbar {
|
||||||
margin: 0;
|
margin: 8px 0 5px;
|
||||||
margin-top: 8px;
|
|
||||||
margin-bottom: 5px
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.emoji-modal .toolbar li {
|
.emoji-modal .toolbar li {
|
||||||
|
|
|
@ -337,9 +337,8 @@ button {
|
||||||
@if $IE7support {
|
@if $IE7support {
|
||||||
filter: unquote("alpha(opacity=#{$controls-opacity*100})");
|
filter: unquote("alpha(opacity=#{$controls-opacity*100})");
|
||||||
}
|
}
|
||||||
margin: 0;
|
|
||||||
top: 50%;
|
top: 50%;
|
||||||
margin-top: -55px;
|
margin: -55px 0 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
width: 90px;
|
width: 90px;
|
||||||
height: 110px;
|
height: 110px;
|
||||||
|
|
|
@ -65,7 +65,6 @@
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
background-color: $secondary;
|
background-color: $secondary;
|
||||||
border: 1px solid dark-light-diff($primary, $secondary, 90%, -60%);
|
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);
|
box-shadow: 0 3px 7px rgba(0,0,0, .8);
|
||||||
background-clip: padding-box;
|
background-clip: padding-box;
|
||||||
|
|
|
@ -11,10 +11,10 @@ a.loading-onebox {
|
||||||
|
|
||||||
|
|
||||||
.onebox-result {
|
.onebox-result {
|
||||||
|
@include post-aside;
|
||||||
|
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
padding: 12px 25px 12px 12px;
|
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;
|
font-size: 1em;
|
||||||
> .source {
|
> .source {
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
|
@ -82,16 +82,15 @@ a.loading-onebox {
|
||||||
|
|
||||||
@mixin onebox-favicon($class, $image) {
|
@mixin onebox-favicon($class, $image) {
|
||||||
&.#{$class} .source {
|
&.#{$class} .source {
|
||||||
background-image: image-url("favicons/#{$image}.png");
|
background: image-url("favicons/#{$image}.png") no-repeat;
|
||||||
background-repeat: no-repeat;
|
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
aside.onebox {
|
aside.onebox {
|
||||||
|
@include post-aside;
|
||||||
|
|
||||||
padding: 12px 25px 12px 12px;
|
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;
|
font-size: 1em;
|
||||||
|
|
||||||
header {
|
header {
|
||||||
|
@ -148,8 +147,7 @@ aside.onebox .onebox-body .onebox-avatar {
|
||||||
|
|
||||||
blockquote {
|
blockquote {
|
||||||
aside.onebox {
|
aside.onebox {
|
||||||
border-left: 5px solid dark-light-diff($primary, $secondary, 90%, -75%);
|
@include post-aside;
|
||||||
background-color: dark-light-diff($primary, $secondary, 97%, -65%);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,10 +13,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.wmd-button-row {
|
.wmd-button-row {
|
||||||
margin-left: 5px;
|
margin: 5px;
|
||||||
margin-right: 5px;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
margin-top: 5px;
|
|
||||||
padding: 0;
|
padding: 0;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
@ -33,18 +30,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.wmd-button {
|
.wmd-button {
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
padding-left: 2px;
|
|
||||||
padding-right: 3px;
|
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: 0 0;
|
|
||||||
border: 0;
|
border: 0;
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
border: 0;
|
|
||||||
float: left;
|
float: left;
|
||||||
font-family: FontAwesome;
|
font-family: FontAwesome;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
|
|
|
@ -50,8 +50,8 @@ aside.quote {
|
||||||
.badge-wrapper { margin-left: 5px; }
|
.badge-wrapper { margin-left: 5px; }
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
border-left: 5px solid dark-light-diff($primary, $secondary, 90%, -75%);
|
@include post-aside;
|
||||||
background-color: dark-light-diff($primary, $secondary, 97%, -65%);
|
|
||||||
color: scale-color($primary, $lightness: 30%);
|
color: scale-color($primary, $lightness: 30%);
|
||||||
// IE will screw up the blockquote underneath if bottom padding is 0px
|
// IE will screw up the blockquote underneath if bottom padding is 0px
|
||||||
padding: 12px 12px 1px 12px;
|
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 {
|
.wiki .topic-body {
|
||||||
background-color: dark-light-diff($wiki, $secondary, 95%, -50%);
|
background-color: dark-light-diff($wiki, $secondary, 95%, -50%);
|
||||||
}
|
}
|
||||||
|
@ -164,7 +169,7 @@ pre {
|
||||||
display: block;
|
display: block;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
color: $primary;
|
color: $primary;
|
||||||
background: dark-light-diff($primary, $secondary, 97%, -45%);
|
background: dark-light-diff($primary, $secondary, 97%, -65%);
|
||||||
max-height: 500px;
|
max-height: 500px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -269,7 +274,7 @@ table.md-table {
|
||||||
font-size: 35px;
|
font-size: 35px;
|
||||||
width: 45px;
|
width: 45px;
|
||||||
text-align: center;
|
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;
|
text-transform: uppercase;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
color: lighten($primary, 50%);
|
color: dark-light-diff($primary, $secondary, 50%, 0%);
|
||||||
|
|
||||||
.custom-message {
|
.custom-message {
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
.uploaded-image-preview {
|
.uploaded-image-preview {
|
||||||
background-position: center center;
|
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
background-color: $primary;
|
background: $primary center center;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,9 @@
|
||||||
color: $primary;
|
color: $primary;
|
||||||
border: 1px solid dark-light-diff($primary, $secondary, 90%, -65%);
|
border: 1px solid dark-light-diff($primary, $secondary, 90%, -65%);
|
||||||
line-height: 19px;
|
line-height: 19px;
|
||||||
margin: 0;
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
background-color: $secondary;
|
background-color: $secondary;
|
||||||
margin-bottom: 3px;
|
margin: 0 0 3px;
|
||||||
|
|
||||||
.fa {
|
.fa {
|
||||||
padding-right: 3px;
|
padding-right: 3px;
|
||||||
|
@ -52,8 +51,7 @@
|
||||||
|
|
||||||
img {
|
img {
|
||||||
display: block;
|
display: block;
|
||||||
margin: auto;
|
margin: auto auto 4px;
|
||||||
margin-bottom: 4px;
|
|
||||||
width: 55px;
|
width: 55px;
|
||||||
height: 55px;
|
height: 55px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 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;
|
div.tagsinput input {
|
||||||
padding:2px 5px; background: transparent; color: #000; outline:0px; margin-right:5px; margin-bottom:5px; }
|
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; }
|
div.tagsinput div { display:block; float: left; }
|
||||||
.tags_clear { clear: both; width: 100%; height: 0; }
|
.tags_clear { clear: both; width: 100%; height: 0; }
|
||||||
.not_valid {background: #FBD8DB !important; color: #90111A !important;}
|
.not_valid {background: #FBD8DB !important; color: #90111A !important;}
|
||||||
|
|
|
@ -28,18 +28,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
b {
|
b {
|
||||||
color: #000;
|
|
||||||
background: #eee;
|
|
||||||
border-style: solid;
|
|
||||||
border-color: #ccc #aaa #888 #bbb;
|
|
||||||
padding: 2px 6px;
|
padding: 2px 6px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
box-shadow: 0 2px 0 rgba(0,0,0,0.2),0 0 0 1px #fff inset;
|
box-shadow: 0 2px 0 rgba(0,0,0,0.2),0 0 0 1px dark-light-choose(#fff,#000) inset;
|
||||||
background-color: #fafafa;
|
background: dark-light-choose(#fafafa, #333);
|
||||||
border-color: #ccc #ccc #fff;
|
border: 1px solid dark-light-choose(#ccc, #555);
|
||||||
border-style: solid solid none;
|
border-bottom: medium none dark-light-choose(#fff, #000);
|
||||||
border-width: 1px 1px medium;
|
color: dark-light-choose(#444, #aaa);
|
||||||
color: #444;
|
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,10 +75,9 @@
|
||||||
{
|
{
|
||||||
left: 90%;
|
left: 90%;
|
||||||
top: 33%;
|
top: 33%;
|
||||||
border: solid transparent;
|
|
||||||
content: " ";
|
content: " ";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
border-width: 8px;
|
border: 8px solid transparent;
|
||||||
border-left-color: $secondary;
|
border-left-color: $secondary;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,6 +60,7 @@
|
||||||
|
|
||||||
// Linear gradient
|
// Linear gradient
|
||||||
|
|
||||||
|
//noinspection CssOptimizeSimilarProperties
|
||||||
@mixin linear-gradient($start-color, $end-color) {
|
@mixin linear-gradient($start-color, $end-color) {
|
||||||
background-color: $start-color;
|
background-color: $start-color;
|
||||||
background-image: linear-gradient(to bottom, $start-color, $end-color);
|
background-image: linear-gradient(to bottom, $start-color, $end-color);
|
||||||
|
@ -92,3 +93,10 @@
|
||||||
-moz-user-select: none;
|
-moz-user-select: none;
|
||||||
-ms-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%);
|
||||||
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ $twitter: #00bced !default;
|
||||||
$yahoo: #810293 !default;
|
$yahoo: #810293 !default;
|
||||||
$github: #6d6d6d !default;
|
$github: #6d6d6d !default;
|
||||||
|
|
||||||
|
|
||||||
// Fonts
|
// Fonts
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,6 @@
|
||||||
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 133px;
|
width: 133px;
|
||||||
padding: 5px;
|
|
||||||
|
|
||||||
@include unselectable;
|
@include unselectable;
|
||||||
|
|
||||||
button.full {
|
button.full {
|
||||||
|
|
|
@ -77,12 +77,12 @@ body {
|
||||||
|
|
||||||
.grippie {
|
.grippie {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border: 1px solid dark-light-diff($primary, $secondary, 90%, -60%);
|
border: 1px solid;
|
||||||
border-width: 1px 0;
|
border-right-width: 0;
|
||||||
|
border-left-width: 0;
|
||||||
cursor: row-resize;
|
cursor: row-resize;
|
||||||
height: 11px;
|
height: 11px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background-color: dark-light-diff($primary, $secondary, 90%, -60%);
|
|
||||||
display:block;
|
display:block;
|
||||||
background: image-url("grippie.png") dark-light-diff($primary, $secondary, 90%, -60%) no-repeat center 3px;
|
background: image-url("grippie.png") dark-light-diff($primary, $secondary, 90%, -60%) no-repeat center 3px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,8 +11,12 @@
|
||||||
}
|
}
|
||||||
#revision-controls {
|
#revision-controls {
|
||||||
float: left;
|
float: left;
|
||||||
.btn[disabled]:hover {
|
.btn[disabled] {
|
||||||
color: $primary;
|
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 {
|
#display-modes {
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
margin: -250px 0 0 -305px;
|
margin: -250px 0 0 -305px;
|
||||||
background-color: $secondary;
|
background-color: $secondary;
|
||||||
border: 1px solid dark-light-diff($primary, $secondary, 90%, -60%);
|
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);
|
box-shadow: 0 3px 7px rgba(0,0,0, .8);
|
||||||
background-clip: padding-box;
|
background-clip: padding-box;
|
||||||
|
|
|
@ -182,8 +182,7 @@
|
||||||
}
|
}
|
||||||
td.latest {
|
td.latest {
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
padding: 8px;
|
padding: 0 8px 8px;
|
||||||
padding-top: 0;
|
|
||||||
}
|
}
|
||||||
.last-user-info {
|
.last-user-info {
|
||||||
font-size: 0.857em;
|
font-size: 0.857em;
|
||||||
|
|
|
@ -699,9 +699,6 @@ $topic-avatar-width: 45px;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
border-top: 1px solid dark-light-diff($primary, $secondary, 90%, -75%);
|
border-top: 1px solid dark-light-diff($primary, $secondary, 90%, -75%);
|
||||||
padding: 12px $topic-body-width-padding 15px $topic-body-width-padding;
|
padding: 12px $topic-body-width-padding 15px $topic-body-width-padding;
|
||||||
&.highlighted {
|
|
||||||
background-color: dark-light-diff($tertiary, $secondary, 85%, -65%);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.topic-avatar {
|
.topic-avatar {
|
||||||
border-top: 1px solid dark-light-diff($primary, $secondary, 90%, -75%);
|
border-top: 1px solid dark-light-diff($primary, $secondary, 90%, -75%);
|
||||||
|
@ -800,7 +797,6 @@ $topic-avatar-width: 45px;
|
||||||
|
|
||||||
#selected-posts {
|
#selected-posts {
|
||||||
|
|
||||||
padding-left: 20px;
|
|
||||||
margin-left: 330px;
|
margin-left: 330px;
|
||||||
width: 200px;
|
width: 200px;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
|
|
@ -214,7 +214,6 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
color: $secondary;
|
|
||||||
|
|
||||||
&.group {
|
&.group {
|
||||||
.details {
|
.details {
|
||||||
|
@ -269,7 +268,7 @@
|
||||||
.details {
|
.details {
|
||||||
padding: 15px 15px 4px 15px;
|
padding: 15px 15px 4px 15px;
|
||||||
margin-top: -200px;
|
margin-top: -200px;
|
||||||
background: rgba($primary, .85);
|
background: dark-light-choose(rgba($primary, .85), rgba($secondary, .85));
|
||||||
transition: margin .15s linear;
|
transition: margin .15s linear;
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
|
@ -316,20 +315,20 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
float: left;
|
float: left;
|
||||||
color: $secondary;
|
color: dark-light-choose($secondary, lighten($primary, 10%));
|
||||||
|
|
||||||
h1 {font-weight: bold;}
|
h1 {font-weight: bold;}
|
||||||
|
|
||||||
.primary-textual {
|
.primary-textual {
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
a[href] {
|
a[href] {
|
||||||
color: $secondary;
|
color: dark-light-choose($secondary, lighten($primary, 10%));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.bio {
|
.bio {
|
||||||
color: $secondary;
|
color: dark-light-choose($secondary, lighten($primary, 10%));
|
||||||
max-height: 300px;
|
max-height: 300px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
max-width: 750px;
|
max-width: 750px;
|
||||||
|
@ -396,7 +395,7 @@
|
||||||
.details {
|
.details {
|
||||||
padding: 12px 15px 2px 15px;
|
padding: 12px 15px 2px 15px;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
background: rgba($primary, 1);
|
background: dark-light-choose(rgba($primary, 1), scale-color($secondary, $lightness: 40%));
|
||||||
.bio { display: none; }
|
.bio { display: none; }
|
||||||
|
|
||||||
.primary {
|
.primary {
|
||||||
|
@ -533,6 +532,10 @@
|
||||||
.staff-counters {
|
.staff-counters {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
background: $primary;
|
background: $primary;
|
||||||
|
color: $secondary;
|
||||||
|
a {
|
||||||
|
color: $secondary;
|
||||||
|
}
|
||||||
> div {
|
> div {
|
||||||
margin: 0 10px 0 0;
|
margin: 0 10px 0 0;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
@ -544,10 +547,6 @@
|
||||||
padding: 1px 5px;
|
padding: 1px 5px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
color: $secondary;
|
|
||||||
}
|
}
|
||||||
.active {
|
.active {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
@import "./common/foundation/variables";
|
@import "./common/foundation/variables";
|
||||||
@import "./common/foundation/colors";
|
@import "./common/foundation/colors";
|
||||||
|
@import "./common/foundation/mixins";
|
||||||
@import "./common/base/onebox";
|
@import "./common/base/onebox";
|
||||||
|
|
||||||
article.post {
|
article.post {
|
||||||
|
|
|
@ -88,8 +88,7 @@ blockquote {
|
||||||
// we must remove margins for text site titles
|
// we must remove margins for text site titles
|
||||||
h2#site-text-logo
|
h2#site-text-logo
|
||||||
{
|
{
|
||||||
margin: 0;
|
margin: 0 0 0 10px;
|
||||||
margin-left: 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// categories should not be bold on mobile; they fight with the topic title too much
|
// categories should not be bold on mobile; they fight with the topic title too much
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
background-color: $secondary;
|
background-color: $secondary;
|
||||||
border: 1px solid scale-color($secondary, $lightness: 90%);
|
|
||||||
border: 1px solid rgba(0, 0, 0, 0.3);
|
border: 1px solid rgba(0, 0, 0, 0.3);
|
||||||
|
|
||||||
@include border-radius-all (6px);
|
@include border-radius-all (6px);
|
||||||
|
|
|
@ -268,17 +268,14 @@ tr.category-topic-link {
|
||||||
float: left;
|
float: left;
|
||||||
width: 280px;
|
width: 280px;
|
||||||
padding: 4px 0;
|
padding: 4px 0;
|
||||||
margin: 1px 0 0;
|
|
||||||
list-style: none;
|
list-style: none;
|
||||||
background-color: $secondary;
|
background-color: $secondary;
|
||||||
border: 1px solid dark-light-diff($primary, $secondary, 90%, -60%);
|
border: 1px solid dark-light-choose(rgba(0, 0, 0, 0.2), scale-color($primary, $lightness: -60%));
|
||||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
||||||
background-clip: padding-box;
|
background-clip: padding-box;
|
||||||
margin-bottom: 20px;
|
margin: 1px 0 20px;
|
||||||
.title {font-weight: bold; display: block;}
|
.title {font-weight: bold; display: block;}
|
||||||
|
|
||||||
}
|
}
|
||||||
.dropdown-menu a {
|
.dropdown-menu a {
|
||||||
display: block;
|
display: block;
|
||||||
|
|
|
@ -463,12 +463,8 @@ blockquote {
|
||||||
|
|
||||||
.posts-wrapper { position: relative; }
|
.posts-wrapper { position: relative; }
|
||||||
|
|
||||||
.topic-body.highlighted {
|
|
||||||
background-color: scale-color($tertiary, $lightness: 75%);
|
|
||||||
}
|
|
||||||
|
|
||||||
span.highlighted {
|
span.highlighted {
|
||||||
background-color: scale-color($highlight, $lightness: 70%);
|
background-color: dark-light-choose(scale-color($highlight, $lightness: 70%), $highlight);
|
||||||
}
|
}
|
||||||
|
|
||||||
.topic-avatar {
|
.topic-avatar {
|
||||||
|
|
|
@ -69,7 +69,6 @@
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 34px;
|
bottom: 34px;
|
||||||
width: 133px;
|
width: 133px;
|
||||||
padding: 5px;
|
|
||||||
|
|
||||||
button.full {
|
button.full {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -221,9 +221,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.about {
|
.about {
|
||||||
background-color: dark-light-diff($primary, $secondary, 0%, -75%);
|
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
background-position: center center;
|
background: dark-light-diff($primary, $secondary, 0%, -75%) center center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
@import "common/foundation/mixins";
|
@import "common/foundation/mixins";
|
||||||
|
|
||||||
|
//noinspection CssOverwrittenProperties
|
||||||
a:focus {
|
a:focus {
|
||||||
outline: thin dotted #333;
|
outline: thin dotted #333;
|
||||||
outline: 5px auto -webkit-focus-ring-color;
|
outline: 5px auto -webkit-focus-ring-color;
|
||||||
|
|
|
@ -86,9 +86,7 @@
|
||||||
text-indent: 20px; // hide text using text-indent trick, using width value (it's enough)
|
text-indent: 20px; // hide text using text-indent trick, using width value (it's enough)
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background-color: transparent;
|
background: transparent no-repeat center center;
|
||||||
background-position: center center;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: 75% 75%;
|
background-size: 75% 75%;
|
||||||
opacity: .5;
|
opacity: .5;
|
||||||
*position: absolute;
|
*position: absolute;
|
||||||
|
|
|
@ -200,6 +200,7 @@ Version: @@ver@@ Timestamp: @@timestamp@@
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//noinspection CssOverwrittenProperties
|
||||||
.select2-search input {
|
.select2-search input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: auto !important;
|
height: auto !important;
|
||||||
|
@ -228,6 +229,7 @@ Version: @@ver@@ Timestamp: @@timestamp@@
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//noinspection CssOverwrittenProperties
|
||||||
.select2-search input.select2-active {
|
.select2-search input.select2-active {
|
||||||
background: #fff asset-url('select2-spinner.gif') no-repeat 100%;
|
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));
|
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,15 +391,13 @@ disabled look for disabled choices in the results dropdown
|
||||||
/* disabled styles */
|
/* disabled styles */
|
||||||
|
|
||||||
.select2-container.select2-container-disabled .select2-choice {
|
.select2-container.select2-container-disabled .select2-choice {
|
||||||
background-color: #f4f4f4;
|
background: #f4f4f4 none;
|
||||||
background-image: none;
|
|
||||||
border: 1px solid #ddd;
|
border: 1px solid #ddd;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
.select2-container.select2-container-disabled .select2-choice .select2-arrow {
|
.select2-container.select2-container-disabled .select2-choice .select2-arrow {
|
||||||
background-color: #f4f4f4;
|
background: #f4f4f4 none;
|
||||||
background-image: none;
|
|
||||||
border-left: 0;
|
border-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -540,8 +540,7 @@ html[dir="rtl"] .select2-search-choice-close {
|
||||||
|
|
||||||
/* disabled styles */
|
/* disabled styles */
|
||||||
.select2-container-multi.select2-container-disabled .select2-choices {
|
.select2-container-multi.select2-container-disabled .select2-choices {
|
||||||
background-color: #f4f4f4;
|
background: #f4f4f4 none;
|
||||||
background-image: none;
|
|
||||||
border: 1px solid #ddd;
|
border: 1px solid #ddd;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
@ -549,8 +548,7 @@ html[dir="rtl"] .select2-search-choice-close {
|
||||||
.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice {
|
.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice {
|
||||||
padding: 3px 5px 3px 5px;
|
padding: 3px 5px 3px 5px;
|
||||||
border: 1px solid #ddd;
|
border: 1px solid #ddd;
|
||||||
background-image: none;
|
background: #f4f4f4 none;
|
||||||
background-color: #f4f4f4;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice .select2-search-choice-close { display: none;
|
.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice .select2-search-choice-close { display: none;
|
||||||
|
@ -598,8 +596,7 @@ html[dir="rtl"] .select2-search-choice-close {
|
||||||
.select2-search-choice-close,
|
.select2-search-choice-close,
|
||||||
.select2-container .select2-choice abbr,
|
.select2-container .select2-choice abbr,
|
||||||
.select2-container .select2-choice .select2-arrow b {
|
.select2-container .select2-choice .select2-arrow b {
|
||||||
background-image: asset-url('select2x2.png') !important;
|
background: asset-url('select2x2.png') no-repeat !important;
|
||||||
background-repeat: no-repeat !important;
|
|
||||||
background-size: 60px 40px !important;
|
background-size: 60px 40px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,13 +12,12 @@ class SearchController < ApplicationController
|
||||||
search = Search.new(params[:q], type_filter: 'topic', guardian: guardian, include_blurbs: true, blurb_length: 300)
|
search = Search.new(params[:q], type_filter: 'topic', guardian: guardian, include_blurbs: true, blurb_length: 300)
|
||||||
result = search.execute
|
result = search.execute
|
||||||
|
|
||||||
serializer = serialize_data(result, GroupedSearchResultSerializer, :result => result)
|
serializer = serialize_data(result, GroupedSearchResultSerializer, result: result)
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html do
|
format.html do
|
||||||
store_preloaded("search", MultiJson.dump(serializer))
|
store_preloaded("search", MultiJson.dump(serializer))
|
||||||
end
|
end
|
||||||
|
|
||||||
format.json do
|
format.json do
|
||||||
render_json_dump(serializer)
|
render_json_dump(serializer)
|
||||||
end
|
end
|
||||||
|
@ -29,14 +28,14 @@ class SearchController < ApplicationController
|
||||||
def query
|
def query
|
||||||
params.require(:term)
|
params.require(:term)
|
||||||
|
|
||||||
search_args = {guardian: guardian}
|
search_args = { guardian: guardian }
|
||||||
|
|
||||||
search_args[:type_filter] = params[:type_filter] if params[:type_filter].present?
|
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" 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[:search_for_id] = true if params[:search_for_id].present?
|
||||||
|
|
||||||
search_context = params[:search_context]
|
search_context = params[:search_context]
|
||||||
|
|
||||||
if search_context.present?
|
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) unless SearchController.valid_context_types.include?(search_context[:type])
|
||||||
raise Discourse::InvalidParameters.new(:search_context) if search_context[:id].blank?
|
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)
|
search = Search.new(params[:term], search_args.symbolize_keys)
|
||||||
result = search.execute
|
result = search.execute
|
||||||
render_serialized(result, GroupedSearchResultSerializer, :result => result)
|
render_serialized(result, GroupedSearchResultSerializer, result: result)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -15,18 +15,19 @@ class CategoryUser < ActiveRecord::Base
|
||||||
TopicUser.notification_levels
|
TopicUser.notification_levels
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.auto_track_new_topic(topic)
|
%w{watch track}.each do |s|
|
||||||
apply_default_to_topic(topic,
|
define_singleton_method("auto_#{s}_new_topic") do |topic, new_category=nil|
|
||||||
TopicUser.notification_levels[:tracking],
|
category_id = topic.category_id
|
||||||
TopicUser.notification_reasons[:auto_track_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
|
end
|
||||||
|
|
||||||
def self.auto_watch_new_topic(topic)
|
apply_default_to_topic(topic.id, category_id, TopicUser.notification_levels[:"#{s}ing"], TopicUser.notification_reasons[:"auto_#{s}_category"])
|
||||||
apply_default_to_topic(topic,
|
end
|
||||||
TopicUser.notification_levels[:watching],
|
|
||||||
TopicUser.notification_reasons[:auto_watch_category]
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.batch_set(user, level, category_ids)
|
def self.batch_set(user, level, category_ids)
|
||||||
|
@ -56,7 +57,7 @@ class CategoryUser < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
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
|
# 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
|
sql = <<-SQL
|
||||||
INSERT INTO topic_users(user_id, topic_id, notification_level, notifications_reason_id)
|
INSERT INTO topic_users(user_id, topic_id, notification_level, notifications_reason_id)
|
||||||
|
@ -68,14 +69,30 @@ class CategoryUser < ActiveRecord::Base
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
exec_sql(sql,
|
exec_sql(sql,
|
||||||
topic_id: topic.id,
|
topic_id: topic_id,
|
||||||
category_id: topic.category_id,
|
category_id: category_id,
|
||||||
level: level,
|
level: level,
|
||||||
reason: reason
|
reason: reason
|
||||||
)
|
)
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
# == Schema Information
|
# == Schema Information
|
||||||
|
|
|
@ -80,7 +80,9 @@ class Site
|
||||||
return {
|
return {
|
||||||
periods: TopTopic.periods.map(&:to_s),
|
periods: TopTopic.periods.map(&:to_s),
|
||||||
filters: Discourse.filters.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
|
}.to_json
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -482,8 +482,8 @@ class Topic < ActiveRecord::Base
|
||||||
Category.where(id: new_category.id).update_all("topic_count = topic_count + 1")
|
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(old_category) unless @import_mode
|
||||||
CategoryFeaturedTopic.feature_topics_for(new_category) unless @import_mode || old_category.id == new_category.id
|
CategoryFeaturedTopic.feature_topics_for(new_category) unless @import_mode || old_category.id == new_category.id
|
||||||
CategoryUser.auto_watch_new_topic(self)
|
CategoryUser.auto_watch_new_topic(self, new_category)
|
||||||
CategoryUser.auto_track_new_topic(self)
|
CategoryUser.auto_track_new_topic(self, new_category)
|
||||||
end
|
end
|
||||||
|
|
||||||
true
|
true
|
||||||
|
|
|
@ -77,7 +77,7 @@ After completing your edits, press <kbd>Ctrl</kbd><kbd>O</kbd> then <kbd>Enter</
|
||||||
|
|
||||||
- Already have a mail server? Great. Use your existing mail server credentials.
|
- 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.
|
- 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.
|
||||||
|
|
||||||
|
|
|
@ -87,7 +87,11 @@ class DiscoursePluginRegistry
|
||||||
self.asset_globs.each do |g|
|
self.asset_globs.each do |g|
|
||||||
root, ext, options = *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|
|
Dir.glob("#{root}/**/*") do |f|
|
||||||
yield f, ext
|
yield f, ext
|
||||||
|
|
|
@ -52,8 +52,8 @@ describe CategoryUser do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "watches categories that have been changed" do
|
it "watches categories that have been changed" do
|
||||||
watched_category = Fabricate(:category)
|
|
||||||
user = Fabricate(:user)
|
user = Fabricate(:user)
|
||||||
|
watched_category = Fabricate(:category)
|
||||||
CategoryUser.create!(user: user, category: watched_category, notification_level: CategoryUser.notification_levels[:watching])
|
CategoryUser.create!(user: user, category: watched_category, notification_level: CategoryUser.notification_levels[:watching])
|
||||||
|
|
||||||
post = create_post
|
post = create_post
|
||||||
|
@ -65,6 +65,21 @@ describe CategoryUser do
|
||||||
expect(tu.notification_level).to eq TopicUser.notification_levels[:watching]
|
expect(tu.notification_level).to eq TopicUser.notification_levels[:watching]
|
||||||
end
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,18 @@
|
||||||
|
import Badge from 'discourse/models/badge';
|
||||||
|
|
||||||
moduleFor('controller:admin-user-badges', {
|
moduleFor('controller:admin-user-badges', {
|
||||||
needs: ['controller:adminUser']
|
needs: ['controller:adminUser']
|
||||||
});
|
});
|
||||||
|
|
||||||
test("grantableBadges", function() {
|
test("grantableBadges", function() {
|
||||||
var badge_first = Discourse.Badge.create({id: 3, name: "A Badge"});
|
const badgeFirst = Badge.create({id: 3, name: "A Badge"});
|
||||||
var badge_middle = Discourse.Badge.create({id: 1, name: "My Badge"});
|
const badgeMiddle = Badge.create({id: 1, name: "My Badge"});
|
||||||
var badge_last = Discourse.Badge.create({id: 2, name: "Zoo Badge"});
|
const badgeLast = Badge.create({id: 2, name: "Zoo Badge"});
|
||||||
var controller = this.subject({ badges: [badge_last, badge_first, badge_middle] });
|
const controller = this.subject({ badges: [badgeLast, badgeFirst, badgeMiddle] });
|
||||||
var sorted_names = [badge_first.name, badge_middle.name, badge_last.name];
|
const sortedNames = [badgeFirst.name, badgeMiddle.name, badgeLast.name];
|
||||||
var badge_names = controller.get('grantableBadges').map(function(badge) {
|
const badgeNames = controller.get('grantableBadges').map(function(badge) {
|
||||||
return badge.name;
|
return badge.name;
|
||||||
});
|
});
|
||||||
|
|
||||||
deepEqual(badge_names, sorted_names, "sorts badges by name");
|
deepEqual(badgeNames, sortedNames, "sorts badges by name");
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,43 +1,45 @@
|
||||||
module("Discourse.Badge");
|
import Badge from 'discourse/models/badge';
|
||||||
|
|
||||||
|
module("model:badge");
|
||||||
|
|
||||||
test('newBadge', function() {
|
test('newBadge', function() {
|
||||||
var badge1 = Discourse.Badge.create({name: "New Badge"}),
|
const badge1 = Badge.create({name: "New Badge"}),
|
||||||
badge2 = Discourse.Badge.create({id: 1, name: "Old Badge"});
|
badge2 = Badge.create({id: 1, name: "Old Badge"});
|
||||||
ok(badge1.get('newBadge'), "badges without ids are new");
|
ok(badge1.get('newBadge'), "badges without ids are new");
|
||||||
ok(!badge2.get('newBadge'), "badges with ids are not new");
|
ok(!badge2.get('newBadge'), "badges with ids are not new");
|
||||||
});
|
});
|
||||||
|
|
||||||
test('displayName', function() {
|
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");
|
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);
|
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");
|
equal(badge2.get('displayName'), "badges.badge.test_badge_2.name", "uses translation when available");
|
||||||
});
|
});
|
||||||
|
|
||||||
test('translatedDescription', function() {
|
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");
|
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");
|
sandbox.stub(I18n, "t").returns("description translation");
|
||||||
equal(badge2.get('translatedDescription'), "description translation", "users translated description");
|
equal(badge2.get('translatedDescription'), "description translation", "users translated description");
|
||||||
});
|
});
|
||||||
|
|
||||||
test('displayDescription', function() {
|
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");
|
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");
|
sandbox.stub(I18n, "t").returns("description translation");
|
||||||
equal(badge2.get('displayDescription'), "description translation", "users translated description");
|
equal(badge2.get('displayDescription'), "description translation", "users translated description");
|
||||||
});
|
});
|
||||||
|
|
||||||
test('createFromJson array', function() {
|
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");
|
ok(Array.isArray(badges), "returns an array");
|
||||||
equal(badges[0].get('name'), "Badge 1", "badge details are set");
|
equal(badges[0].get('name'), "Badge 1", "badge details are set");
|
||||||
|
@ -45,16 +47,16 @@ test('createFromJson array', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('createFromJson single', 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");
|
ok(!Array.isArray(badge), "does not returns an array");
|
||||||
});
|
});
|
||||||
|
|
||||||
test('updateFromJson', function() {
|
test('updateFromJson', 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.create({name: "Badge 1"});
|
const badge = Badge.create({name: "Badge 1"});
|
||||||
badge.updateFromJson(badgeJson);
|
badge.updateFromJson(badgeJson);
|
||||||
equal(badge.get('id'), 1126, "id is set");
|
equal(badge.get('id'), 1126, "id is set");
|
||||||
equal(badge.get('badge_type.name'), "Silver 1", "badge_type reference 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() {
|
test('save', function() {
|
||||||
sandbox.stub(Discourse, 'ajax').returns(Ember.RSVP.resolve({}));
|
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
|
// TODO: clean API
|
||||||
badge.save(["name", "description", "badge_type_id"]);
|
badge.save(["name", "description", "badge_type_id"]);
|
||||||
ok(Discourse.ajax.calledOnce, "saved badge");
|
ok(Discourse.ajax.calledOnce, "saved badge");
|
||||||
|
@ -70,7 +72,7 @@ test('save', function() {
|
||||||
|
|
||||||
test('destroy', function() {
|
test('destroy', function() {
|
||||||
sandbox.stub(Discourse, 'ajax');
|
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();
|
badge.destroy();
|
||||||
ok(!Discourse.ajax.calledOnce, "no AJAX call for a new badge");
|
ok(!Discourse.ajax.calledOnce, "no AJAX call for a new badge");
|
||||||
badge.set('id', 3);
|
badge.set('id', 3);
|
||||||
|
|
|
@ -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}]};
|
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() {
|
test('createFromJson single', function() {
|
||||||
var userBadge = Discourse.UserBadge.createFromJson(singleBadgeJson);
|
const userBadge = UserBadge.createFromJson(singleBadgeJson);
|
||||||
ok(!Array.isArray(userBadge), "does not return an array");
|
ok(!Array.isArray(userBadge), "does not return an array");
|
||||||
equal(userBadge.get('badge.name'), "Badge 2", "badge reference is set");
|
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");
|
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() {
|
test('createFromJson array', function() {
|
||||||
var userBadges = Discourse.UserBadge.createFromJson(multipleBadgesJson);
|
const userBadges = UserBadge.createFromJson(multipleBadgesJson);
|
||||||
ok(Array.isArray(userBadges), "returns an array");
|
ok(Array.isArray(userBadges), "returns an array");
|
||||||
equal(userBadges[0].get('granted_by'), null, "granted_by reference is not set when null");
|
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() {
|
asyncTestDiscourse('findByUsername', function() {
|
||||||
expect(2);
|
expect(2);
|
||||||
sandbox.stub(Discourse, 'ajax').returns(Ember.RSVP.resolve(multipleBadgesJson));
|
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");
|
ok(Array.isArray(badges), "returns an array");
|
||||||
start();
|
start();
|
||||||
});
|
});
|
||||||
|
@ -30,7 +32,7 @@ asyncTestDiscourse('findByUsername', function() {
|
||||||
asyncTestDiscourse('findByBadgeId', function() {
|
asyncTestDiscourse('findByBadgeId', function() {
|
||||||
expect(2);
|
expect(2);
|
||||||
sandbox.stub(Discourse, 'ajax').returns(Ember.RSVP.resolve(multipleBadgesJson));
|
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");
|
ok(Array.isArray(badges), "returns an array");
|
||||||
start();
|
start();
|
||||||
});
|
});
|
||||||
|
@ -40,7 +42,7 @@ asyncTestDiscourse('findByBadgeId', function() {
|
||||||
asyncTestDiscourse('grant', function() {
|
asyncTestDiscourse('grant', function() {
|
||||||
expect(2);
|
expect(2);
|
||||||
sandbox.stub(Discourse, 'ajax').returns(Ember.RSVP.resolve(singleBadgeJson));
|
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");
|
ok(!Array.isArray(userBadge), "does not return an array");
|
||||||
start();
|
start();
|
||||||
});
|
});
|
||||||
|
@ -49,7 +51,7 @@ asyncTestDiscourse('grant', function() {
|
||||||
|
|
||||||
test('revoke', function() {
|
test('revoke', function() {
|
||||||
sandbox.stub(Discourse, 'ajax');
|
sandbox.stub(Discourse, 'ajax');
|
||||||
var userBadge = Discourse.UserBadge.create({id: 1});
|
const userBadge = UserBadge.create({id: 1});
|
||||||
userBadge.revoke();
|
userBadge.revoke();
|
||||||
ok(Discourse.ajax.calledOnce, "makes an AJAX call");
|
ok(Discourse.ajax.calledOnce, "makes an AJAX call");
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue