FEATURE: rebuild user preferences page to use tabs
This commit is contained in:
parent
f5f4c36795
commit
2503241ce5
|
@ -1,230 +1,3 @@
|
|||
import { setting } from 'discourse/lib/computed';
|
||||
import CanCheckEmails from 'discourse/mixins/can-check-emails';
|
||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||
import { default as computed, observes } from "ember-addons/ember-computed-decorators";
|
||||
import { cook } from 'discourse/lib/text';
|
||||
import { NotificationLevels } from 'discourse/lib/notification-levels';
|
||||
import { listThemes, selectDefaultTheme, previewTheme } from 'discourse/lib/theme-selector';
|
||||
|
||||
export default Ember.Controller.extend(CanCheckEmails, {
|
||||
|
||||
userSelectableThemes: function(){
|
||||
return listThemes(this.site);
|
||||
}.property(),
|
||||
|
||||
@observes("selectedTheme")
|
||||
themeKeyChanged() {
|
||||
let key = this.get("selectedTheme");
|
||||
previewTheme(key);
|
||||
},
|
||||
|
||||
@computed("model.watchedCategories", "model.trackedCategories", "model.mutedCategories")
|
||||
selectedCategories(watched, tracked, muted) {
|
||||
return [].concat(watched, tracked, muted);
|
||||
},
|
||||
|
||||
// By default we haven't saved anything
|
||||
saved: false,
|
||||
|
||||
newNameInput: null,
|
||||
|
||||
@computed("model.user_fields.@each.value")
|
||||
userFields() {
|
||||
let siteUserFields = this.site.get('user_fields');
|
||||
if (!Ember.isEmpty(siteUserFields)) {
|
||||
const userFields = this.get('model.user_fields');
|
||||
|
||||
// Staff can edit fields that are not `editable`
|
||||
if (!this.get('currentUser.staff')) {
|
||||
siteUserFields = siteUserFields.filterBy('editable', true);
|
||||
}
|
||||
return siteUserFields.sortBy('position').map(function(field) {
|
||||
const value = userFields ? userFields[field.get('id').toString()] : null;
|
||||
return Ember.Object.create({ value, field });
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
cannotDeleteAccount: Em.computed.not('currentUser.can_delete_account'),
|
||||
deleteDisabled: Em.computed.or('model.isSaving', 'deleting', 'cannotDeleteAccount'),
|
||||
|
||||
canEditName: setting('enable_names'),
|
||||
|
||||
@computed()
|
||||
nameInstructions() {
|
||||
return I18n.t(this.siteSettings.full_name_required ? 'user.name.instructions_required' : 'user.name.instructions');
|
||||
},
|
||||
|
||||
@computed("model.has_title_badges")
|
||||
canSelectTitle(hasTitleBadges) {
|
||||
return this.siteSettings.enable_badges && hasTitleBadges;
|
||||
},
|
||||
|
||||
@computed("model.can_change_bio")
|
||||
canChangeBio(canChangeBio)
|
||||
{
|
||||
return canChangeBio;
|
||||
},
|
||||
|
||||
@computed()
|
||||
canChangePassword() {
|
||||
return !this.siteSettings.enable_sso && this.siteSettings.enable_local_logins;
|
||||
},
|
||||
|
||||
@computed()
|
||||
availableLocales() {
|
||||
return this.siteSettings.available_locales.split('|').map(s => ({ name: s, value: s }));
|
||||
},
|
||||
|
||||
@computed()
|
||||
frequencyEstimate() {
|
||||
var estimate = this.get('model.mailing_list_posts_per_day');
|
||||
if (!estimate || estimate < 2) {
|
||||
return I18n.t('user.mailing_list_mode.few_per_day');
|
||||
} else {
|
||||
return I18n.t('user.mailing_list_mode.many_per_day', { dailyEmailEstimate: estimate });
|
||||
}
|
||||
},
|
||||
|
||||
@computed()
|
||||
mailingListModeOptions() {
|
||||
return [
|
||||
{name: I18n.t('user.mailing_list_mode.daily'), value: 0},
|
||||
{name: this.get('frequencyEstimate'), value: 1},
|
||||
{name: I18n.t('user.mailing_list_mode.individual_no_echo'), value: 2}
|
||||
];
|
||||
},
|
||||
|
||||
previousRepliesOptions: [
|
||||
{name: I18n.t('user.email_previous_replies.always'), value: 0},
|
||||
{name: I18n.t('user.email_previous_replies.unless_emailed'), value: 1},
|
||||
{name: I18n.t('user.email_previous_replies.never'), value: 2}
|
||||
],
|
||||
|
||||
digestFrequencies: [{ name: I18n.t('user.email_digests.every_30_minutes'), value: 30 },
|
||||
{ name: I18n.t('user.email_digests.every_hour'), value: 60 },
|
||||
{ name: I18n.t('user.email_digests.daily'), value: 1440 },
|
||||
{ name: I18n.t('user.email_digests.every_three_days'), value: 4320 },
|
||||
{ name: I18n.t('user.email_digests.weekly'), value: 10080 },
|
||||
{ name: I18n.t('user.email_digests.every_two_weeks'), value: 20160 }],
|
||||
|
||||
likeNotificationFrequencies: [{ name: I18n.t('user.like_notification_frequency.always'), value: 0 },
|
||||
{ name: I18n.t('user.like_notification_frequency.first_time_and_daily'), value: 1 },
|
||||
{ name: I18n.t('user.like_notification_frequency.first_time'), value: 2 },
|
||||
{ name: I18n.t('user.like_notification_frequency.never'), value: 3 }],
|
||||
|
||||
autoTrackDurations: [{ name: I18n.t('user.auto_track_options.never'), value: -1 },
|
||||
{ name: I18n.t('user.auto_track_options.immediately'), value: 0 },
|
||||
{ name: I18n.t('user.auto_track_options.after_30_seconds'), value: 30000 },
|
||||
{ name: I18n.t('user.auto_track_options.after_1_minute'), value: 60000 },
|
||||
{ name: I18n.t('user.auto_track_options.after_2_minutes'), value: 120000 },
|
||||
{ name: I18n.t('user.auto_track_options.after_3_minutes'), value: 180000 },
|
||||
{ name: I18n.t('user.auto_track_options.after_4_minutes'), value: 240000 },
|
||||
{ name: I18n.t('user.auto_track_options.after_5_minutes'), value: 300000 },
|
||||
{ name: I18n.t('user.auto_track_options.after_10_minutes'), value: 600000 }],
|
||||
|
||||
notificationLevelsForReplying: [{ name: I18n.t('topic.notifications.watching.title'), value: NotificationLevels.WATCHING },
|
||||
{ name: I18n.t('topic.notifications.tracking.title'), value: NotificationLevels.TRACKING },
|
||||
{ name: I18n.t('topic.notifications.regular.title'), value: NotificationLevels.REGULAR }],
|
||||
|
||||
|
||||
considerNewTopicOptions: [{ name: I18n.t('user.new_topic_duration.not_viewed'), value: -1 },
|
||||
{ name: I18n.t('user.new_topic_duration.after_1_day'), value: 60 * 24 },
|
||||
{ name: I18n.t('user.new_topic_duration.after_2_days'), value: 60 * 48 },
|
||||
{ name: I18n.t('user.new_topic_duration.after_1_week'), value: 7 * 60 * 24 },
|
||||
{ name: I18n.t('user.new_topic_duration.after_2_weeks'), value: 2 * 7 * 60 * 24 },
|
||||
{ name: I18n.t('user.new_topic_duration.last_here'), value: -2 }],
|
||||
|
||||
@computed("model.isSaving")
|
||||
saveButtonText(isSaving) {
|
||||
return isSaving ? I18n.t('saving') : I18n.t('save');
|
||||
},
|
||||
|
||||
reset() {
|
||||
this.setProperties({
|
||||
passwordProgress: null
|
||||
});
|
||||
},
|
||||
|
||||
passwordProgress: null,
|
||||
|
||||
actions: {
|
||||
|
||||
save() {
|
||||
this.set('saved', false);
|
||||
|
||||
const model = this.get('model');
|
||||
|
||||
const userFields = this.get('userFields');
|
||||
|
||||
// Update the user fields
|
||||
if (!Ember.isEmpty(userFields)) {
|
||||
const modelFields = model.get('user_fields');
|
||||
if (!Ember.isEmpty(modelFields)) {
|
||||
userFields.forEach(function(uf) {
|
||||
modelFields[uf.get('field.id').toString()] = uf.get('value');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Cook the bio for preview
|
||||
model.set('name', this.get('newNameInput'));
|
||||
return model.save().then(() => {
|
||||
if (Discourse.User.currentProp('id') === model.get('id')) {
|
||||
Discourse.User.currentProp('name', model.get('name'));
|
||||
}
|
||||
model.set('bio_cooked', cook(model.get('bio_raw')));
|
||||
selectDefaultTheme(this.get('selectedTheme'));
|
||||
this.set('saved', true);
|
||||
}).catch(popupAjaxError);
|
||||
},
|
||||
|
||||
changePassword() {
|
||||
if (!this.get('passwordProgress')) {
|
||||
this.set('passwordProgress', I18n.t("user.change_password.in_progress"));
|
||||
return this.get('model').changePassword().then(() => {
|
||||
// password changed
|
||||
this.setProperties({
|
||||
changePasswordProgress: false,
|
||||
passwordProgress: I18n.t("user.change_password.success")
|
||||
});
|
||||
}).catch(() => {
|
||||
// password failed to change
|
||||
this.setProperties({
|
||||
changePasswordProgress: false,
|
||||
passwordProgress: I18n.t("user.change_password.error")
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
delete() {
|
||||
this.set('deleting', true);
|
||||
const self = this,
|
||||
message = I18n.t('user.delete_account_confirm'),
|
||||
model = this.get('model'),
|
||||
buttons = [
|
||||
{ label: I18n.t("cancel"),
|
||||
class: "cancel-inline",
|
||||
link: true,
|
||||
callback: () => { this.set('deleting', false); }
|
||||
},
|
||||
{ label: '<i class="fa fa-exclamation-triangle"></i> ' + I18n.t("user.delete_account"),
|
||||
class: "btn btn-danger",
|
||||
callback() {
|
||||
model.delete().then(function() {
|
||||
bootbox.alert(I18n.t('user.deleted_yourself'), function() {
|
||||
window.location.pathname = Discourse.getURL('/');
|
||||
});
|
||||
}, function() {
|
||||
bootbox.alert(I18n.t('user.delete_yourself_not_allowed'));
|
||||
self.set('deleting', false);
|
||||
});
|
||||
}
|
||||
}
|
||||
];
|
||||
bootbox.dialog(message, buttons, {"classes": "delete-account"});
|
||||
}
|
||||
}
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
application: Ember.inject.controller()
|
||||
});
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
import CanCheckEmails from 'discourse/mixins/can-check-emails';
|
||||
import { default as computed } from "ember-addons/ember-computed-decorators";
|
||||
import PreferencesTabController from "discourse/mixins/preferences-tab-controller";
|
||||
|
||||
export default Ember.Controller.extend(CanCheckEmails, PreferencesTabController, {
|
||||
|
||||
passwordProgress: null,
|
||||
|
||||
cannotDeleteAccount: Em.computed.not('currentUser.can_delete_account'),
|
||||
deleteDisabled: Em.computed.or('model.isSaving', 'deleting', 'cannotDeleteAccount'),
|
||||
|
||||
reset() {
|
||||
this.setProperties({
|
||||
passwordProgress: null
|
||||
});
|
||||
},
|
||||
|
||||
@computed()
|
||||
canChangePassword() {
|
||||
return !this.siteSettings.enable_sso && this.siteSettings.enable_local_logins;
|
||||
},
|
||||
|
||||
actions: {
|
||||
changePassword() {
|
||||
if (!this.get('passwordProgress')) {
|
||||
this.set('passwordProgress', I18n.t("user.change_password.in_progress"));
|
||||
return this.get('model').changePassword().then(() => {
|
||||
// password changed
|
||||
this.setProperties({
|
||||
changePasswordProgress: false,
|
||||
passwordProgress: I18n.t("user.change_password.success")
|
||||
});
|
||||
}).catch(() => {
|
||||
// password failed to change
|
||||
this.setProperties({
|
||||
changePasswordProgress: false,
|
||||
passwordProgress: I18n.t("user.change_password.error")
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
delete() {
|
||||
this.set('deleting', true);
|
||||
const self = this,
|
||||
message = I18n.t('user.delete_account_confirm'),
|
||||
model = this.get('model'),
|
||||
buttons = [
|
||||
{ label: I18n.t("cancel"),
|
||||
class: "cancel-inline",
|
||||
link: true,
|
||||
callback: () => { this.set('deleting', false); }
|
||||
},
|
||||
{ label: '<i class="fa fa-exclamation-triangle"></i> ' + I18n.t("user.delete_account"),
|
||||
class: "btn btn-danger",
|
||||
callback() {
|
||||
model.delete().then(function() {
|
||||
bootbox.alert(I18n.t('user.deleted_yourself'), function() {
|
||||
window.location.pathname = Discourse.getURL('/');
|
||||
});
|
||||
}, function() {
|
||||
bootbox.alert(I18n.t('user.delete_yourself_not_allowed'));
|
||||
self.set('deleting', false);
|
||||
});
|
||||
}
|
||||
}
|
||||
];
|
||||
bootbox.dialog(message, buttons, {"classes": "delete-account"});
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,20 @@
|
|||
import PreferencesTabController from "discourse/mixins/preferences-tab-controller";
|
||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||
|
||||
export default Ember.Controller.extend(PreferencesTabController, {
|
||||
saveAttrNames: [
|
||||
'muted_category_ids',
|
||||
'watched_category_ids',
|
||||
'tracked_category_ids',
|
||||
'watched_first_post_category_ids'
|
||||
],
|
||||
|
||||
actions: {
|
||||
save() {
|
||||
this.set('saved', false);
|
||||
return this.get('model').save(this.get('saveAttrNames')).then(() => {
|
||||
this.set('saved', true);
|
||||
}).catch(popupAjaxError);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,62 @@
|
|||
import PreferencesTabController from "discourse/mixins/preferences-tab-controller";
|
||||
import { default as computed } from "ember-addons/ember-computed-decorators";
|
||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||
|
||||
export default Ember.Controller.extend(PreferencesTabController, {
|
||||
|
||||
saveAttrNames: [
|
||||
'email_always',
|
||||
'mailing_list_mode',
|
||||
'mailing_list_mode_frequency',
|
||||
'email_digests',
|
||||
'email_direct',
|
||||
'email_in_reply_to',
|
||||
'email_private_messages',
|
||||
'email_previous_replies',
|
||||
'digest_after_minutes',
|
||||
'include_tl0_in_digests'
|
||||
],
|
||||
|
||||
@computed()
|
||||
frequencyEstimate() {
|
||||
var estimate = this.get('model.mailing_list_posts_per_day');
|
||||
if (!estimate || estimate < 2) {
|
||||
return I18n.t('user.mailing_list_mode.few_per_day');
|
||||
} else {
|
||||
return I18n.t('user.mailing_list_mode.many_per_day', { dailyEmailEstimate: estimate });
|
||||
}
|
||||
},
|
||||
|
||||
@computed()
|
||||
mailingListModeOptions() {
|
||||
return [
|
||||
{name: I18n.t('user.mailing_list_mode.daily'), value: 0},
|
||||
{name: this.get('frequencyEstimate'), value: 1},
|
||||
{name: I18n.t('user.mailing_list_mode.individual_no_echo'), value: 2}
|
||||
];
|
||||
},
|
||||
|
||||
previousRepliesOptions: [
|
||||
{name: I18n.t('user.email_previous_replies.always'), value: 0},
|
||||
{name: I18n.t('user.email_previous_replies.unless_emailed'), value: 1},
|
||||
{name: I18n.t('user.email_previous_replies.never'), value: 2}
|
||||
],
|
||||
|
||||
digestFrequencies: [
|
||||
{ name: I18n.t('user.email_digests.every_30_minutes'), value: 30 },
|
||||
{ name: I18n.t('user.email_digests.every_hour'), value: 60 },
|
||||
{ name: I18n.t('user.email_digests.daily'), value: 1440 },
|
||||
{ name: I18n.t('user.email_digests.every_three_days'), value: 4320 },
|
||||
{ name: I18n.t('user.email_digests.weekly'), value: 10080 },
|
||||
{ name: I18n.t('user.email_digests.every_two_weeks'), value: 20160 }
|
||||
],
|
||||
|
||||
actions: {
|
||||
save() {
|
||||
this.set('saved', false);
|
||||
return this.get('model').save(this.get('saveAttrNames')).then(() => {
|
||||
this.set('saved', true);
|
||||
}).catch(popupAjaxError);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,45 @@
|
|||
import PreferencesTabController from "discourse/mixins/preferences-tab-controller";
|
||||
import { default as computed, observes } from "ember-addons/ember-computed-decorators";
|
||||
import { listThemes, previewTheme } from 'discourse/lib/theme-selector';
|
||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||
import { selectDefaultTheme } from 'discourse/lib/theme-selector';
|
||||
|
||||
export default Ember.Controller.extend(PreferencesTabController, {
|
||||
|
||||
saveAttrNames: [
|
||||
'locale',
|
||||
'external_links_in_new_tab',
|
||||
'dynamic_favicon',
|
||||
'enable_quoting',
|
||||
'disable_jump_reply',
|
||||
'automatically_unpin_topics'
|
||||
],
|
||||
|
||||
preferencesController: Ember.inject.controller('preferences'),
|
||||
|
||||
@computed()
|
||||
availableLocales() {
|
||||
return this.siteSettings.available_locales.split('|').map(s => ({ name: s, value: s }));
|
||||
},
|
||||
|
||||
userSelectableThemes: function(){
|
||||
return listThemes(this.site);
|
||||
}.property(),
|
||||
|
||||
@observes("selectedTheme")
|
||||
themeKeyChanged() {
|
||||
let key = this.get("selectedTheme");
|
||||
this.get('preferencesController').set('selectedTheme', key);
|
||||
previewTheme(key);
|
||||
},
|
||||
|
||||
actions: {
|
||||
save() {
|
||||
this.set('saved', false);
|
||||
return this.get('model').save(this.get('saveAttrNames')).then(() => {
|
||||
this.set('saved', true);
|
||||
selectDefaultTheme(this.get('selectedTheme'));
|
||||
}).catch(popupAjaxError);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,56 @@
|
|||
import PreferencesTabController from "discourse/mixins/preferences-tab-controller";
|
||||
import { default as computed } from "ember-addons/ember-computed-decorators";
|
||||
import { NotificationLevels } from 'discourse/lib/notification-levels';
|
||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||
|
||||
export default Ember.Controller.extend(PreferencesTabController, {
|
||||
|
||||
saveAttrNames:[
|
||||
'muted_usernames',
|
||||
'new_topic_duration_minutes',
|
||||
'auto_track_topics_after_msecs',
|
||||
'notification_level_when_replying',
|
||||
'like_notification_frequency'
|
||||
],
|
||||
|
||||
@computed("model.watchedCategories", "model.trackedCategories", "model.mutedCategories")
|
||||
selectedCategories(watched, tracked, muted) {
|
||||
return [].concat(watched, tracked, muted);
|
||||
},
|
||||
|
||||
likeNotificationFrequencies: [{ name: I18n.t('user.like_notification_frequency.always'), value: 0 },
|
||||
{ name: I18n.t('user.like_notification_frequency.first_time_and_daily'), value: 1 },
|
||||
{ name: I18n.t('user.like_notification_frequency.first_time'), value: 2 },
|
||||
{ name: I18n.t('user.like_notification_frequency.never'), value: 3 }],
|
||||
|
||||
autoTrackDurations: [{ name: I18n.t('user.auto_track_options.never'), value: -1 },
|
||||
{ name: I18n.t('user.auto_track_options.immediately'), value: 0 },
|
||||
{ name: I18n.t('user.auto_track_options.after_30_seconds'), value: 30000 },
|
||||
{ name: I18n.t('user.auto_track_options.after_1_minute'), value: 60000 },
|
||||
{ name: I18n.t('user.auto_track_options.after_2_minutes'), value: 120000 },
|
||||
{ name: I18n.t('user.auto_track_options.after_3_minutes'), value: 180000 },
|
||||
{ name: I18n.t('user.auto_track_options.after_4_minutes'), value: 240000 },
|
||||
{ name: I18n.t('user.auto_track_options.after_5_minutes'), value: 300000 },
|
||||
{ name: I18n.t('user.auto_track_options.after_10_minutes'), value: 600000 }],
|
||||
|
||||
notificationLevelsForReplying: [{ name: I18n.t('topic.notifications.watching.title'), value: NotificationLevels.WATCHING },
|
||||
{ name: I18n.t('topic.notifications.tracking.title'), value: NotificationLevels.TRACKING },
|
||||
{ name: I18n.t('topic.notifications.regular.title'), value: NotificationLevels.REGULAR }],
|
||||
|
||||
considerNewTopicOptions: [{ name: I18n.t('user.new_topic_duration.not_viewed'), value: -1 },
|
||||
{ name: I18n.t('user.new_topic_duration.after_1_day'), value: 60 * 24 },
|
||||
{ name: I18n.t('user.new_topic_duration.after_2_days'), value: 60 * 48 },
|
||||
{ name: I18n.t('user.new_topic_duration.after_1_week'), value: 7 * 60 * 24 },
|
||||
{ name: I18n.t('user.new_topic_duration.after_2_weeks'), value: 2 * 7 * 60 * 24 },
|
||||
{ name: I18n.t('user.new_topic_duration.last_here'), value: -2 }],
|
||||
|
||||
actions: {
|
||||
save() {
|
||||
this.set('saved', false);
|
||||
return this.get('model').save(this.get('saveAttrNames')).then(() => {
|
||||
this.set('saved', true);
|
||||
}).catch(popupAjaxError);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
|
@ -0,0 +1,84 @@
|
|||
import { default as computed } from "ember-addons/ember-computed-decorators";
|
||||
import PreferencesTabController from "discourse/mixins/preferences-tab-controller";
|
||||
import { setting } from 'discourse/lib/computed';
|
||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||
import { cook } from 'discourse/lib/text';
|
||||
|
||||
export default Ember.Controller.extend(PreferencesTabController, {
|
||||
|
||||
saveAttrNames: [
|
||||
'name',
|
||||
'bio_raw',
|
||||
'website',
|
||||
'location',
|
||||
'custom_fields',
|
||||
'user_fields',
|
||||
'profile_background',
|
||||
'card_background',
|
||||
'date_of_birth'
|
||||
],
|
||||
|
||||
canEditName: setting('enable_names'),
|
||||
|
||||
newNameInput: null,
|
||||
|
||||
@computed()
|
||||
nameInstructions() {
|
||||
return I18n.t(this.siteSettings.full_name_required ? 'user.name.instructions_required' : 'user.name.instructions');
|
||||
},
|
||||
|
||||
@computed("model.has_title_badges")
|
||||
canSelectTitle(hasTitleBadges) {
|
||||
return this.siteSettings.enable_badges && hasTitleBadges;
|
||||
},
|
||||
|
||||
@computed("model.user_fields.@each.value")
|
||||
userFields() {
|
||||
let siteUserFields = this.site.get('user_fields');
|
||||
if (!Ember.isEmpty(siteUserFields)) {
|
||||
const userFields = this.get('model.user_fields');
|
||||
|
||||
// Staff can edit fields that are not `editable`
|
||||
if (!this.get('currentUser.staff')) {
|
||||
siteUserFields = siteUserFields.filterBy('editable', true);
|
||||
}
|
||||
return siteUserFields.sortBy('position').map(function(field) {
|
||||
const value = userFields ? userFields[field.get('id').toString()] : null;
|
||||
return Ember.Object.create({ value, field });
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@computed("model.can_change_bio")
|
||||
canChangeBio(canChangeBio)
|
||||
{
|
||||
return canChangeBio;
|
||||
},
|
||||
|
||||
actions: {
|
||||
|
||||
save() {
|
||||
this.set('saved', false);
|
||||
|
||||
const model = this.get('model'),
|
||||
userFields = this.get('userFields');
|
||||
|
||||
model.set('name', this.get('newNameInput'));
|
||||
|
||||
// Update the user fields
|
||||
if (!Ember.isEmpty(userFields)) {
|
||||
const modelFields = model.get('user_fields');
|
||||
if (!Ember.isEmpty(modelFields)) {
|
||||
userFields.forEach(function(uf) {
|
||||
modelFields[uf.get('field.id').toString()] = uf.get('value');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return model.save(this.get('saveAttrNames')).then(() => {
|
||||
model.set('bio_cooked', cook(model.get('bio_raw')));
|
||||
this.set('saved', true);
|
||||
}).catch(popupAjaxError);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,21 @@
|
|||
import PreferencesTabController from "discourse/mixins/preferences-tab-controller";
|
||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||
|
||||
export default Ember.Controller.extend(PreferencesTabController, {
|
||||
|
||||
saveAttrNames: [
|
||||
'muted_tags',
|
||||
'tracked_tags',
|
||||
'watched_tags',
|
||||
'watching_first_post_tags'
|
||||
],
|
||||
|
||||
actions: {
|
||||
save() {
|
||||
this.set('saved', false);
|
||||
return this.get('model').save(this.get('saveAttrNames')).then(() => {
|
||||
this.set('saved', true);
|
||||
}).catch(popupAjaxError);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,10 @@
|
|||
import { default as computed } from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default Ember.Mixin.create({
|
||||
saved: false,
|
||||
|
||||
@computed("model.isSaving")
|
||||
saveButtonText(isSaving) {
|
||||
return isSaving ? I18n.t('saving') : I18n.t('save');
|
||||
}
|
||||
});
|
|
@ -201,8 +201,9 @@ const User = RestModel.extend({
|
|||
return Discourse.User.create(this.getProperties(Object.keys(this)));
|
||||
},
|
||||
|
||||
save() {
|
||||
const data = this.getProperties(
|
||||
save(fields) {
|
||||
|
||||
let userFields = [
|
||||
'bio_raw',
|
||||
'website',
|
||||
'location',
|
||||
|
@ -217,43 +218,55 @@ const User = RestModel.extend({
|
|||
'tracked_tags',
|
||||
'watched_tags',
|
||||
'watching_first_post_tags',
|
||||
'date_of_birth');
|
||||
'date_of_birth'
|
||||
];
|
||||
|
||||
['email_always',
|
||||
'mailing_list_mode',
|
||||
'mailing_list_mode_frequency',
|
||||
'external_links_in_new_tab',
|
||||
'email_digests',
|
||||
'email_direct',
|
||||
'email_in_reply_to',
|
||||
'email_private_messages',
|
||||
'email_previous_replies',
|
||||
'dynamic_favicon',
|
||||
'enable_quoting',
|
||||
'disable_jump_reply',
|
||||
'automatically_unpin_topics',
|
||||
'digest_after_minutes',
|
||||
'new_topic_duration_minutes',
|
||||
'auto_track_topics_after_msecs',
|
||||
'notification_level_when_replying',
|
||||
'like_notification_frequency',
|
||||
'include_tl0_in_digests'
|
||||
].forEach(s => {
|
||||
const data = this.getProperties(fields ? _.intersection(userFields, fields) : userFields);
|
||||
|
||||
let userOptionFields = [
|
||||
'email_always',
|
||||
'mailing_list_mode',
|
||||
'mailing_list_mode_frequency',
|
||||
'external_links_in_new_tab',
|
||||
'email_digests',
|
||||
'email_direct',
|
||||
'email_in_reply_to',
|
||||
'email_private_messages',
|
||||
'email_previous_replies',
|
||||
'dynamic_favicon',
|
||||
'enable_quoting',
|
||||
'disable_jump_reply',
|
||||
'automatically_unpin_topics',
|
||||
'digest_after_minutes',
|
||||
'new_topic_duration_minutes',
|
||||
'auto_track_topics_after_msecs',
|
||||
'notification_level_when_replying',
|
||||
'like_notification_frequency',
|
||||
'include_tl0_in_digests'
|
||||
];
|
||||
|
||||
if (fields) {
|
||||
userOptionFields = _.intersection(userOptionFields, fields);
|
||||
}
|
||||
|
||||
userOptionFields.forEach(s => {
|
||||
data[s] = this.get(`user_option.${s}`);
|
||||
});
|
||||
|
||||
var updatedState = {};
|
||||
|
||||
['muted','watched','tracked','watched_first_post'].forEach(s => {
|
||||
let prop = s === "watched_first_post" ? "watchedFirstPostCategories" : s + "Categories";
|
||||
let cats = this.get(prop);
|
||||
if (cats) {
|
||||
let cat_ids = cats.map(c => c.get('id'));
|
||||
updatedState[s + '_category_ids'] = cat_ids;
|
||||
if (fields === undefined || fields.includes(s + '_category_ids')) {
|
||||
let prop = s === "watched_first_post" ? "watchedFirstPostCategories" : s + "Categories";
|
||||
let cats = this.get(prop);
|
||||
if (cats) {
|
||||
let cat_ids = cats.map(c => c.get('id'));
|
||||
updatedState[s + '_category_ids'] = cat_ids;
|
||||
|
||||
// HACK: denote lack of categories
|
||||
if (cats.length === 0) { cat_ids = [-1]; }
|
||||
data[s + '_category_ids'] = cat_ids;
|
||||
// HACK: denote lack of categories
|
||||
if (cats.length === 0) { cat_ids = [-1]; }
|
||||
data[s + '_category_ids'] = cat_ids;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -93,6 +93,15 @@ export default function() {
|
|||
});
|
||||
|
||||
this.route('preferences', { resetNamespace: true }, function() {
|
||||
this.route('account');
|
||||
this.route('profile');
|
||||
this.route('emails');
|
||||
this.route('notifications');
|
||||
this.route('categories');
|
||||
this.route('tags');
|
||||
this.route('interface');
|
||||
this.route('apps');
|
||||
|
||||
this.route('username');
|
||||
this.route('email');
|
||||
this.route('about', { path: '/about-me' });
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
import RestrictedUserRoute from "discourse/routes/restricted-user";
|
||||
|
||||
export default RestrictedUserRoute.extend({
|
||||
setupController(controller, user) {
|
||||
controller.reset();
|
||||
controller.setProperties({ model: user });
|
||||
}
|
||||
});
|
|
@ -1,7 +1,7 @@
|
|||
import RestrictedUserRoute from "discourse/routes/restricted-user";
|
||||
|
||||
export default RestrictedUserRoute.extend({
|
||||
renderTemplate: function() {
|
||||
this.render('preferences', { into: 'user', controller: 'preferences' });
|
||||
redirect() {
|
||||
this.transitionTo('preferences.account');
|
||||
}
|
||||
});
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
import RestrictedUserRoute from "discourse/routes/restricted-user";
|
||||
import { currentThemeKey } from 'discourse/lib/theme-selector';
|
||||
|
||||
export default RestrictedUserRoute.extend({
|
||||
setupController(controller, user) {
|
||||
controller.setProperties({
|
||||
model: user,
|
||||
selectedTheme: $.cookie('theme_key') || currentThemeKey()
|
||||
});
|
||||
}
|
||||
});
|
|
@ -0,0 +1,10 @@
|
|||
import RestrictedUserRoute from "discourse/routes/restricted-user";
|
||||
|
||||
export default RestrictedUserRoute.extend({
|
||||
setupController(controller, user) {
|
||||
controller.setProperties({
|
||||
model: user,
|
||||
newNameInput: user.get('name')
|
||||
});
|
||||
}
|
||||
});
|
|
@ -1,7 +1,6 @@
|
|||
import RestrictedUserRoute from "discourse/routes/restricted-user";
|
||||
import showModal from 'discourse/lib/show-modal';
|
||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||
import { currentThemeKey } from 'discourse/lib/theme-selector';
|
||||
|
||||
export default RestrictedUserRoute.extend({
|
||||
model() {
|
||||
|
@ -9,11 +8,8 @@ export default RestrictedUserRoute.extend({
|
|||
},
|
||||
|
||||
setupController(controller, user) {
|
||||
controller.reset();
|
||||
controller.setProperties({
|
||||
model: user,
|
||||
newNameInput: user.get('name'),
|
||||
selectedTheme: $.cookie('theme_key') || currentThemeKey()
|
||||
model: user
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<label>{{{field.name}}}</label>
|
||||
<label class="control-label">{{{field.name}}}</label>
|
||||
<div class='controls'>
|
||||
{{input value=value maxlength=site.user_field_max_length}}
|
||||
{{#if field.required}}<span class='required'>*</span>{{/if}}
|
||||
<p>{{{field.description}}}</p>
|
||||
<div class="instructions">{{{field.description}}}</div>
|
||||
</div>
|
||||
|
|
|
@ -1,383 +1,24 @@
|
|||
{{#d-section pageClass="user-preferences" class="user-content user-preferences"}}
|
||||
{{#d-section pageClass="user-preferences" class="user-navigation"}}
|
||||
{{#mobile-nav class='preferences-nav' desktopClass='preferences-list action-list nav-stacked' currentPath=application.currentPath}}
|
||||
<li class='no-glyph nav-account'>{{#link-to 'preferences.account'}}{{i18n 'user.preferences_nav.account'}}{{/link-to}}</li>
|
||||
<li class='no-glyph nav-profile'>{{#link-to 'preferences.profile'}}{{i18n 'user.preferences_nav.profile'}}{{/link-to}}</li>
|
||||
<li class='no-glyph nav-emails'>{{#link-to 'preferences.emails'}}{{i18n 'user.preferences_nav.emails'}}{{/link-to}}</li>
|
||||
<li class='no-glyph nav-notifications'>{{#link-to 'preferences.notifications'}}{{i18n 'user.preferences_nav.notifications'}}{{/link-to}}</li>
|
||||
<li class='no-glyph indent nav-categories'>{{#link-to 'preferences.categories'}}{{i18n 'user.preferences_nav.categories'}}{{/link-to}}</li>
|
||||
{{#if siteSettings.tagging_enabled}}
|
||||
<li class='no-glyph indent nav-tags'>{{#link-to 'preferences.tags'}}{{i18n 'user.preferences_nav.tags'}}{{/link-to}}</li>
|
||||
{{/if}}
|
||||
<li class='no-glyph nav-interface'>{{#link-to 'preferences.interface'}}{{i18n 'user.preferences_nav.interface'}}{{/link-to}}</li>
|
||||
{{#if model.userApiKeys}}
|
||||
<li class='no-glyph nav-apps'>{{#link-to 'preferences.apps'}}{{i18n 'user.preferences_nav.apps'}}{{/link-to}}</li>
|
||||
{{/if}}
|
||||
{{/mobile-nav}}
|
||||
{{/d-section}}
|
||||
|
||||
<section class='user-right user-preferences'>
|
||||
{{plugin-outlet name="above-user-preferences"}}
|
||||
|
||||
<form class="form-horizontal">
|
||||
|
||||
<div class="control-group save-button" id='save-button-top'>
|
||||
<div class="controls">
|
||||
{{partial 'user/preferences/save-button'}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group pref-username">
|
||||
<label class="control-label">{{i18n 'user.username.title'}}</label>
|
||||
<div class="controls">
|
||||
<span class='static'>{{model.username}}</span>
|
||||
{{#if model.can_edit_username}}
|
||||
{{#link-to "preferences.username" class="btn btn-small pad-left no-text"}}<i class="fa fa-pencil"></i>{{/link-to}}
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class='instructions'>
|
||||
{{{i18n 'user.username.short_instructions' username=model.username}}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if canEditName}}
|
||||
<div class="control-group pref-name">
|
||||
<label class="control-label">{{i18n 'user.name.title'}}</label>
|
||||
<div class="controls">
|
||||
{{#if model.can_edit_name}}
|
||||
{{text-field value=newNameInput classNames="input-xxlarge"}}
|
||||
{{else}}
|
||||
<span class='static'>{{model.name}}</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class='instructions'>
|
||||
{{nameInstructions}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if canSelectTitle}}
|
||||
<div class="control-group pref-title">
|
||||
<label class="control-label">{{i18n 'user.title.title'}}</label>
|
||||
<div class="controls">
|
||||
<span class="static">{{model.title}}</span>
|
||||
{{#link-to "preferences.badgeTitle" class="btn btn-small pad-left no-text"}}{{fa-icon "pencil"}}{{/link-to}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if canCheckEmails}}
|
||||
<div class="control-group pref-email">
|
||||
<label class="control-label">{{i18n 'user.email.title'}}</label>
|
||||
{{#if model.email}}
|
||||
<div class="controls">
|
||||
<span class='static'>{{model.email}}</span>
|
||||
{{#if model.can_edit_email}}
|
||||
{{#link-to "preferences.email" class="btn btn-small pad-left no-text"}}{{fa-icon "pencil"}}{{/link-to}}
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class='instructions'>
|
||||
{{i18n 'user.email.instructions'}}
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="controls">
|
||||
{{d-button action="checkEmail" actionParam=model title="admin.users.check_email.title" icon="envelope-o" label="admin.users.check_email.text"}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if canChangePassword}}
|
||||
<div class="control-group pref-password">
|
||||
<label class="control-label">{{i18n 'user.password.title'}}</label>
|
||||
<div class="controls">
|
||||
<a href {{action "changePassword"}} class='btn'>
|
||||
{{fa-icon "envelope"}}
|
||||
{{#if model.no_password}}
|
||||
{{i18n 'user.change_password.set_password'}}
|
||||
{{else}}
|
||||
{{i18n 'user.change_password.action'}}
|
||||
{{/if}}
|
||||
</a>
|
||||
{{passwordProgress}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="control-group pref-avatar">
|
||||
<label class="control-label">{{i18n 'user.avatar.title'}}</label>
|
||||
<div class="controls">
|
||||
{{! we want the "huge" version even though we're downsizing it to "large" in CSS }}
|
||||
{{bound-avatar model "huge"}}
|
||||
{{#unless siteSettings.sso_overrides_avatar}}
|
||||
{{d-button action="showAvatarSelector" class="pad-left" icon="pencil"}}
|
||||
{{/unless}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if siteSettings.allow_profile_backgrounds}}
|
||||
<div class="control-group pref-profile-bg">
|
||||
<label class="control-label">{{i18n 'user.change_profile_background.title'}}</label>
|
||||
<div class="controls">
|
||||
{{image-uploader imageUrl=model.profile_background type="profile_background"}}
|
||||
</div>
|
||||
<div class='instructions'>
|
||||
{{i18n 'user.change_profile_background.instructions'}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group pref-profile-bg">
|
||||
<label class="control-label">{{i18n 'user.change_card_background.title'}}</label>
|
||||
<div class="controls">
|
||||
{{image-uploader imageUrl=model.card_background type="card_background"}}
|
||||
</div>
|
||||
<div class='instructions'>
|
||||
{{i18n 'user.change_card_background.instructions'}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if siteSettings.allow_user_locale}}
|
||||
<div class="control-group pref-locale">
|
||||
<label class="control-label">{{i18n 'user.locale.title'}}</label>
|
||||
<div class="controls">
|
||||
{{combo-box valueAttribute="value" content=availableLocales value=model.locale none="user.locale.default"}}
|
||||
</div>
|
||||
<div class='instructions'>
|
||||
{{i18n 'user.locale.instructions'}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if canChangeBio}}
|
||||
<div class="control-group pref-bio">
|
||||
<label class="control-label">{{i18n 'user.bio'}}</label>
|
||||
<div class="controls bio-composer">
|
||||
{{d-editor value=model.bio_raw}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#each userFields as |uf|}}
|
||||
{{user-field field=uf.field value=uf.value}}
|
||||
{{/each}}
|
||||
<div class='clearfix'></div>
|
||||
|
||||
<div class="control-group pref-location">
|
||||
<label class="control-label">{{i18n 'user.location'}}</label>
|
||||
<div class="controls">
|
||||
{{input type="text" value=model.location class="input-xxlarge" id='edit-location'}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group pref-website">
|
||||
<label class="control-label">{{i18n 'user.website'}}</label>
|
||||
<div class="controls">
|
||||
{{input type="text" value=model.website class="input-xxlarge"}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group pref-card-badge">
|
||||
<label class="control-label">{{i18n 'user.card_badge.title'}}</label>
|
||||
<div class="controls">
|
||||
{{#if model.card_image_badge}}
|
||||
{{icon-or-image model.card_image_badge}}
|
||||
{{/if}}
|
||||
{{#link-to "preferences.card-badge" class="btn btn-small pad-left no-text"}}{{fa-icon "pencil"}}{{/link-to}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group pref-email-settings">
|
||||
<label class="control-label">{{i18n 'user.email_settings'}}</label>
|
||||
<div class='controls controls-dropdown'>
|
||||
<label>{{i18n 'user.email_previous_replies.title'}}</label>
|
||||
{{combo-box valueAttribute="value" content=previousRepliesOptions value=model.user_option.email_previous_replies}}
|
||||
</div>
|
||||
{{preference-checkbox labelKey="user.email_in_reply_to" checked=model.user_option.email_in_reply_to}}
|
||||
{{preference-checkbox labelKey="user.email_private_messages" checked=model.user_option.email_private_messages}}
|
||||
{{preference-checkbox labelKey="user.email_direct" checked=model.user_option.email_direct}}
|
||||
{{preference-checkbox labelKey="user.email_always" checked=model.user_option.email_always}}
|
||||
{{#unless model.user_option.email_always}}
|
||||
<div class='instructions'>
|
||||
{{#if siteSettings.email_time_window_mins}}
|
||||
{{i18n 'user.email.frequency' count=siteSettings.email_time_window_mins}}
|
||||
{{else}}
|
||||
{{i18n 'user.email.frequency_immediately'}}
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/unless}}
|
||||
</div>
|
||||
|
||||
{{#unless siteSettings.disable_digest_emails}}
|
||||
<div class='control-group pref-activity-summary'>
|
||||
<label class="control-label">{{i18n 'user.email_activity_summary'}}</label>
|
||||
{{preference-checkbox labelKey="user.email_digests.title" disabled=model.user_option.mailing_list_mode checked=model.user_option.email_digests}}
|
||||
{{#if model.user_option.email_digests}}
|
||||
<div class='controls controls-dropdown'>
|
||||
{{combo-box valueAttribute="value" content=digestFrequencies value=model.user_option.digest_after_minutes}}
|
||||
</div>
|
||||
{{preference-checkbox labelKey="user.include_tl0_in_digests" disabled=model.user_option.mailing_list_mode checked=model.user_option.include_tl0_in_digests}}
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/unless}}
|
||||
|
||||
{{#unless siteSettings.disable_mailing_list_mode}}
|
||||
<div class='control-group pref-mailing-list-mode'>
|
||||
<label class="control-label">{{i18n 'user.mailing_list_mode.label'}}</label>
|
||||
{{preference-checkbox labelKey="user.mailing_list_mode.enabled" checked=model.user_option.mailing_list_mode}}
|
||||
<div class='instructions'>{{{i18n 'user.mailing_list_mode.instructions'}}}</div>
|
||||
{{#if model.user_option.mailing_list_mode}}
|
||||
<div class='controls controls-dropdown'>
|
||||
{{combo-box valueAttribute="value" content=mailingListModeOptions value=model.user_option.mailing_list_mode_frequency}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/unless}}
|
||||
|
||||
<div class="control-group notifications">
|
||||
<label class="control-label">{{i18n 'user.desktop_notifications.label'}}</label>
|
||||
{{desktop-notification-config}}
|
||||
<div class="instructions">{{i18n 'user.desktop_notifications.each_browser_note'}}</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group other">
|
||||
<label class="control-label">{{i18n 'user.other_settings'}}</label>
|
||||
|
||||
<div class="controls controls-dropdown">
|
||||
<label>{{i18n 'user.new_topic_duration.label'}}</label>
|
||||
{{combo-box valueAttribute="value" content=considerNewTopicOptions value=model.user_option.new_topic_duration_minutes}}
|
||||
</div>
|
||||
|
||||
<div class="controls controls-dropdown">
|
||||
<label>{{i18n 'user.auto_track_topics'}}</label>
|
||||
{{combo-box valueAttribute="value" content=autoTrackDurations value=model.user_option.auto_track_topics_after_msecs}}
|
||||
</div>
|
||||
|
||||
<div class="controls controls-dropdown">
|
||||
<label>{{i18n 'user.notification_level_when_replying'}}</label>
|
||||
{{combo-box valueAttribute="value" content=notificationLevelsForReplying value=model.user_option.notification_level_when_replying}}
|
||||
</div>
|
||||
|
||||
<div class="controls controls-dropdown">
|
||||
<label>{{i18n 'user.like_notification_frequency.title'}}</label>
|
||||
{{combo-box valueAttribute="value" content=likeNotificationFrequencies value=model.user_option.like_notification_frequency}}
|
||||
</div>
|
||||
|
||||
{{preference-checkbox labelKey="user.external_links_in_new_tab" checked=model.user_option.external_links_in_new_tab}}
|
||||
{{preference-checkbox labelKey="user.enable_quoting" checked=model.user_option.enable_quoting}}
|
||||
{{preference-checkbox labelKey="user.dynamic_favicon" checked=model.user_option.dynamic_favicon}}
|
||||
{{preference-checkbox labelKey="user.disable_jump_reply" checked=model.user_option.disable_jump_reply}}
|
||||
{{plugin-outlet name="user-custom-preferences" args=(hash model=model)}}
|
||||
</div>
|
||||
|
||||
<div class="control-group category">
|
||||
<label class="control-label">{{i18n 'user.categories_settings'}}</label>
|
||||
<div class="controls category-controls">
|
||||
<label><span class="icon fa fa-exclamation-circle watching"></span> {{i18n 'user.watched_categories'}}</label>
|
||||
{{category-selector categories=model.watchedCategories blacklist=selectedCategories}}
|
||||
</div>
|
||||
<div class="instructions">{{i18n 'user.watched_categories_instructions'}}</div>
|
||||
<div class="controls category-controls">
|
||||
<a href="{{unbound model.watchingTopicsPath}}">{{i18n 'user.watched_topics_link'}}</a>
|
||||
</div>
|
||||
<div class="controls category-controls">
|
||||
<label><span class="icon fa fa-circle tracking"></span> {{i18n 'user.tracked_categories'}}</label>
|
||||
{{category-selector categories=model.trackedCategories blacklist=selectedCategories}}
|
||||
</div>
|
||||
<div class="instructions">{{i18n 'user.tracked_categories_instructions'}}</div>
|
||||
<div class="controls category-controls">
|
||||
<a href="{{unbound model.trackingTopicsPath}}">{{i18n 'user.tracked_topics_link'}}</a>
|
||||
</div>
|
||||
<div class="controls category-controls">
|
||||
<label><span class="icon fa fa-dot-circle-o watching-first-post"></span> {{i18n 'user.watched_first_post_categories'}}</label>
|
||||
{{category-selector categories=model.watchedFirstPostCategories}}
|
||||
</div>
|
||||
<div class="instructions">{{i18n 'user.watched_first_post_categories_instructions'}}</div>
|
||||
<div class="controls category-controls">
|
||||
<label><span class="icon fa fa-times-circle muted"></span> {{i18n 'user.muted_categories'}}</label>
|
||||
{{category-selector categories=model.mutedCategories blacklist=selectedCategories}}
|
||||
</div>
|
||||
<div class="instructions">{{i18n 'user.muted_categories_instructions'}}</div>
|
||||
<div class="controls category-controls">
|
||||
<a href="{{unbound model.mutedTopicsPath}}">{{i18n 'user.muted_topics_link'}}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if siteSettings.tagging_enabled}}
|
||||
<div class="control-group tags">
|
||||
<label class="control-label">{{i18n 'user.tag_settings'}}</label>
|
||||
<div class="controls tag-controls">
|
||||
<label><span class="icon fa fa-exclamation-circle watching"></span> {{i18n 'user.watched_tags'}}</label>
|
||||
{{tag-chooser tags=model.watched_tags blacklist=selectedTags allowCreate=false placeholder="" everyTag="true" unlimitedTagCount="true"}}
|
||||
</div>
|
||||
<div class="instructions">{{i18n 'user.watched_tags_instructions'}}</div>
|
||||
<div class="controls tag-controls">
|
||||
<label><span class="icon fa fa-circle tracking"></span> {{i18n 'user.tracked_tags'}}</label>
|
||||
{{tag-chooser tags=model.tracked_tags blacklist=selectedTags allowCreate=false placeholder="" everyTag="true" unlimitedTagCount="true"}}
|
||||
</div>
|
||||
<div class="instructions">{{i18n 'user.tracked_tags_instructions'}}</div>
|
||||
<div class="controls tag-controls">
|
||||
<label><span class="icon fa fa-dot-circle-o watching-first-post"></span> {{i18n 'user.watched_first_post_tags'}}</label>
|
||||
{{tag-chooser tags=model.watching_first_post_tags blacklist=selectedTags allowCreate=false placeholder="" everyTag="true" unlimitedTagCount="true"}}
|
||||
</div>
|
||||
<div class="instructions">{{i18n 'user.watched_first_post_tags_instructions'}}</div>
|
||||
<div class="controls tag-controls">
|
||||
<label><span class="icon fa fa-times-circle muted"></span> {{i18n 'user.muted_tags'}}</label>
|
||||
{{tag-chooser tags=model.muted_tags blacklist=selectedTags allowCreate=false placeholder="" everyTag="true" unlimitedTagCount="true"}}
|
||||
</div>
|
||||
<div class="instructions">{{i18n 'user.muted_tags_instructions'}}</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="control-group muting">
|
||||
<label class="control-label">{{i18n 'user.users'}}</label>
|
||||
<div class="controls category-controls">
|
||||
<label><span class="icon fa fa-times-circle muted"></span> {{i18n 'user.muted_users'}}</label>
|
||||
{{user-selector excludeCurrentUser=true usernames=model.muted_usernames class="user-selector"}}
|
||||
</div>
|
||||
<div class="instructions">{{i18n 'user.muted_users_instructions'}}</div>
|
||||
</div>
|
||||
|
||||
{{#if siteSettings.automatically_unpin_topics}}
|
||||
<div class="control-group topics">
|
||||
<label class="control-label">{{i18n 'categories.topics'}}</label>
|
||||
{{preference-checkbox labelKey="user.automatically_unpin_topics" checked=model.user_option.automatically_unpin_topics}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if model.userApiKeys}}
|
||||
<div class="control-group apps">
|
||||
<label class="control-label">{{i18n 'user.apps'}}</label>
|
||||
<div class="controls">
|
||||
{{#each model.userApiKeys as |key|}}
|
||||
<div>
|
||||
<span>{{key.application_name}}</span>
|
||||
{{#if key.revoked}}
|
||||
{{d-button action="undoRevokeApiKey" actionParam=key class="btn" label="user.undo_revoke_access"}}
|
||||
{{else}}
|
||||
{{d-button action="revokeApiKey" actionParam=key class="btn" label="user.revoke_access"}}
|
||||
{{/if}}
|
||||
<p>
|
||||
<ul>
|
||||
{{#each key.scopes as |scope|}}
|
||||
<li>{{scope}}</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</p>
|
||||
<p><span>{{i18n "user.api_approved"}}</span> {{bound-date key.created_at}}</p>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if userSelectableThemes}}
|
||||
<div class="control-group theme">
|
||||
<label class="control-label">{{i18n 'user.theme'}}</label>
|
||||
<div class="controls">
|
||||
{{combo-box content=userSelectableThemes value=selectedTheme}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{plugin-outlet name="user-custom-controls" args=(hash model=model)}}
|
||||
|
||||
<div class="control-group save-button">
|
||||
<div class="controls">
|
||||
{{partial 'user/preferences/save-button'}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if model.canDeleteAccount}}
|
||||
<div class="control-group delete-account">
|
||||
<hr/>
|
||||
<div class="controls">
|
||||
{{d-button action="delete" disabled=deleteDisabled class="btn-danger" icon="trash-o" label="user.delete_account"}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
<form class="form-vertical">
|
||||
{{outlet}}
|
||||
</form>
|
||||
{{/d-section}}
|
||||
</section>
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
<div class="control-group pref-username">
|
||||
<label class="control-label">{{i18n 'user.username.title'}}</label>
|
||||
<div class="controls">
|
||||
<span class='static'>{{model.username}}</span>
|
||||
{{#if model.can_edit_username}}
|
||||
{{#link-to "preferences.username" class="btn btn-small pad-left no-text"}}<i class="fa fa-pencil"></i>{{/link-to}}
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class='instructions'>
|
||||
{{{i18n 'user.username.short_instructions' username=model.username}}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if canCheckEmails}}
|
||||
<div class="control-group pref-email">
|
||||
<label class="control-label">{{i18n 'user.email.title'}}</label>
|
||||
{{#if model.email}}
|
||||
<div class="controls">
|
||||
<span class='static'>{{model.email}}</span>
|
||||
{{#if model.can_edit_email}}
|
||||
{{#link-to "preferences.email" class="btn btn-small pad-left no-text"}}{{fa-icon "pencil"}}{{/link-to}}
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class='instructions'>
|
||||
{{i18n 'user.email.instructions'}}
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="controls">
|
||||
{{d-button action="checkEmail" actionParam=model title="admin.users.check_email.title" icon="envelope-o" label="admin.users.check_email.text"}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if canChangePassword}}
|
||||
<div class="control-group pref-password">
|
||||
<label class="control-label">{{i18n 'user.password.title'}}</label>
|
||||
<div class="controls">
|
||||
<a href {{action "changePassword"}} class='btn'>
|
||||
{{fa-icon "envelope"}}
|
||||
{{#if model.no_password}}
|
||||
{{i18n 'user.change_password.set_password'}}
|
||||
{{else}}
|
||||
{{i18n 'user.change_password.action'}}
|
||||
{{/if}}
|
||||
</a>
|
||||
{{passwordProgress}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<br/>
|
||||
|
||||
{{#if model.canDeleteAccount}}
|
||||
<div class="control-group delete-account">
|
||||
<br/>
|
||||
<div class="controls">
|
||||
{{d-button action="delete" disabled=deleteDisabled class="btn-danger" icon="trash-o" label="user.delete_account"}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
|
@ -0,0 +1,25 @@
|
|||
{{#if model.userApiKeys}}
|
||||
<div class="control-group apps">
|
||||
<label class="control-label">{{i18n 'user.apps'}}</label>
|
||||
<div class="controls">
|
||||
{{#each model.userApiKeys as |key|}}
|
||||
<div>
|
||||
<span>{{key.application_name}}</span>
|
||||
{{#if key.revoked}}
|
||||
{{d-button action="undoRevokeApiKey" actionParam=key class="btn" label="user.undo_revoke_access"}}
|
||||
{{else}}
|
||||
{{d-button action="revokeApiKey" actionParam=key class="btn" label="user.revoke_access"}}
|
||||
{{/if}}
|
||||
<p>
|
||||
<ul>
|
||||
{{#each key.scopes as |scope|}}
|
||||
<li>{{scope}}</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</p>
|
||||
<p><span>{{i18n "user.api_approved"}}</span> {{bound-date key.created_at}}</p>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
|
@ -0,0 +1,48 @@
|
|||
<div class="control-group category-notifications">
|
||||
|
||||
<label class="control-label">{{i18n 'user.categories_settings'}}</label>
|
||||
|
||||
<div class="controls category-controls">
|
||||
<label><span class="icon fa fa-exclamation-circle watching"></span> {{i18n 'user.watched_categories'}}</label>
|
||||
{{category-selector categories=model.watchedCategories blacklist=selectedCategories}}
|
||||
</div>
|
||||
<div class="instructions">{{i18n 'user.watched_categories_instructions'}}</div>
|
||||
<div class="controls">
|
||||
<a href="{{unbound model.watchingTopicsPath}}">{{i18n 'user.watched_topics_link'}}</a>
|
||||
</div>
|
||||
|
||||
<div class="controls category-controls">
|
||||
<label><span class="icon fa fa-circle tracking"></span> {{i18n 'user.tracked_categories'}}</label>
|
||||
{{category-selector categories=model.trackedCategories blacklist=selectedCategories}}
|
||||
</div>
|
||||
<div class="instructions">{{i18n 'user.tracked_categories_instructions'}}</div>
|
||||
<div class="controls">
|
||||
<a href="{{unbound model.trackingTopicsPath}}">{{i18n 'user.tracked_topics_link'}}</a>
|
||||
</div>
|
||||
|
||||
<div class="controls category-controls">
|
||||
<label><span class="icon fa fa-dot-circle-o watching-first-post"></span> {{i18n 'user.watched_first_post_categories'}}</label>
|
||||
{{category-selector categories=model.watchedFirstPostCategories}}
|
||||
</div>
|
||||
<div class="instructions">{{i18n 'user.watched_first_post_categories_instructions'}}</div>
|
||||
|
||||
<div class="controls category-controls">
|
||||
<label><span class="icon fa fa-times-circle muted"></span> {{i18n 'user.muted_categories'}}</label>
|
||||
{{category-selector categories=model.mutedCategories blacklist=selectedCategories}}
|
||||
</div>
|
||||
<div class="instructions">{{i18n 'user.muted_categories_instructions'}}</div>
|
||||
<div class="controls">
|
||||
<a href="{{unbound model.mutedTopicsPath}}">{{i18n 'user.muted_topics_link'}}</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
|
||||
{{plugin-outlet name="user-custom-controls" args=(hash model=model)}}
|
||||
|
||||
<div class="control-group save-button">
|
||||
<div class="controls">
|
||||
{{partial 'user/preferences/save-button'}}
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,56 @@
|
|||
<div class="control-group pref-email-settings">
|
||||
<label class="control-label">{{i18n 'user.email_settings'}}</label>
|
||||
<div class='controls controls-dropdown'>
|
||||
<label>{{i18n 'user.email_previous_replies.title'}}</label>
|
||||
{{combo-box valueAttribute="value" content=previousRepliesOptions value=model.user_option.email_previous_replies}}
|
||||
</div>
|
||||
{{preference-checkbox labelKey="user.email_in_reply_to" checked=model.user_option.email_in_reply_to}}
|
||||
{{preference-checkbox labelKey="user.email_private_messages" checked=model.user_option.email_private_messages}}
|
||||
{{preference-checkbox labelKey="user.email_direct" checked=model.user_option.email_direct}}
|
||||
{{preference-checkbox labelKey="user.email_always" checked=model.user_option.email_always}}
|
||||
{{#unless model.user_option.email_always}}
|
||||
<div class='instructions'>
|
||||
{{#if siteSettings.email_time_window_mins}}
|
||||
{{i18n 'user.email.frequency' count=siteSettings.email_time_window_mins}}
|
||||
{{else}}
|
||||
{{i18n 'user.email.frequency_immediately'}}
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/unless}}
|
||||
</div>
|
||||
|
||||
{{#unless siteSettings.disable_digest_emails}}
|
||||
<div class='control-group pref-activity-summary'>
|
||||
<label class="control-label">{{i18n 'user.email_activity_summary'}}</label>
|
||||
{{preference-checkbox labelKey="user.email_digests.title" disabled=model.user_option.mailing_list_mode checked=model.user_option.email_digests}}
|
||||
{{#if model.user_option.email_digests}}
|
||||
<div class='controls controls-dropdown'>
|
||||
{{combo-box valueAttribute="value" content=digestFrequencies value=model.user_option.digest_after_minutes}}
|
||||
</div>
|
||||
{{preference-checkbox labelKey="user.include_tl0_in_digests" disabled=model.user_option.mailing_list_mode checked=model.user_option.include_tl0_in_digests}}
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/unless}}
|
||||
|
||||
{{#unless siteSettings.disable_mailing_list_mode}}
|
||||
<div class='control-group pref-mailing-list-mode'>
|
||||
<label class="control-label">{{i18n 'user.mailing_list_mode.label'}}</label>
|
||||
{{preference-checkbox labelKey="user.mailing_list_mode.enabled" checked=model.user_option.mailing_list_mode}}
|
||||
<div class='instructions'>{{{i18n 'user.mailing_list_mode.instructions'}}}</div>
|
||||
{{#if model.user_option.mailing_list_mode}}
|
||||
<div class='controls controls-dropdown'>
|
||||
{{combo-box valueAttribute="value" content=mailingListModeOptions value=model.user_option.mailing_list_mode_frequency}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/unless}}
|
||||
|
||||
<br/>
|
||||
|
||||
{{plugin-outlet name="user-custom-controls" args=(hash model=model)}}
|
||||
|
||||
<div class="control-group save-button">
|
||||
<div class="controls">
|
||||
{{partial 'user/preferences/save-button'}}
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,47 @@
|
|||
{{#if userSelectableThemes}}
|
||||
<div class="control-group theme">
|
||||
<label class="control-label">{{i18n 'user.theme'}}</label>
|
||||
<div class="controls">
|
||||
{{combo-box content=userSelectableThemes value=selectedTheme}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if siteSettings.allow_user_locale}}
|
||||
<div class="control-group pref-locale">
|
||||
<label class="control-label">{{i18n 'user.locale.title'}}</label>
|
||||
<div class="controls">
|
||||
{{combo-box valueAttribute="value" content=availableLocales value=model.locale none="user.locale.default"}}
|
||||
</div>
|
||||
<div class='instructions'>
|
||||
{{i18n 'user.locale.instructions'}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
|
||||
<div class="control-group other">
|
||||
<label class="control-label">{{i18n 'user.other_settings'}}</label>
|
||||
|
||||
{{preference-checkbox labelKey="user.external_links_in_new_tab" checked=model.user_option.external_links_in_new_tab}}
|
||||
{{preference-checkbox labelKey="user.enable_quoting" checked=model.user_option.enable_quoting}}
|
||||
{{preference-checkbox labelKey="user.dynamic_favicon" checked=model.user_option.dynamic_favicon}}
|
||||
{{preference-checkbox labelKey="user.disable_jump_reply" checked=model.user_option.disable_jump_reply}}
|
||||
</div>
|
||||
|
||||
{{#if siteSettings.automatically_unpin_topics}}
|
||||
<div class="control-group topics">
|
||||
<label class="control-label">{{i18n 'categories.topics'}}</label>
|
||||
{{preference-checkbox labelKey="user.automatically_unpin_topics" checked=model.user_option.automatically_unpin_topics}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<br/>
|
||||
|
||||
{{plugin-outlet name="user-custom-controls" args=(hash model=model)}}
|
||||
|
||||
<div class="control-group save-button">
|
||||
<div class="controls">
|
||||
{{partial 'user/preferences/save-button'}}
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,46 @@
|
|||
<div class="control-group notifications">
|
||||
<div class="controls controls-dropdown">
|
||||
<label>{{i18n 'user.new_topic_duration.label'}}</label>
|
||||
{{combo-box valueAttribute="value" content=considerNewTopicOptions value=model.user_option.new_topic_duration_minutes}}
|
||||
</div>
|
||||
|
||||
<div class="controls controls-dropdown">
|
||||
<label>{{i18n 'user.auto_track_topics'}}</label>
|
||||
{{combo-box valueAttribute="value" content=autoTrackDurations value=model.user_option.auto_track_topics_after_msecs}}
|
||||
</div>
|
||||
|
||||
<div class="controls controls-dropdown">
|
||||
<label>{{i18n 'user.notification_level_when_replying'}}</label>
|
||||
{{combo-box valueAttribute="value" content=notificationLevelsForReplying value=model.user_option.notification_level_when_replying}}
|
||||
</div>
|
||||
|
||||
<div class="controls controls-dropdown">
|
||||
<label>{{i18n 'user.like_notification_frequency.title'}}</label>
|
||||
{{combo-box valueAttribute="value" content=likeNotificationFrequencies value=model.user_option.like_notification_frequency}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group desktop-notifications">
|
||||
<label class="control-label">{{i18n 'user.desktop_notifications.label'}}</label>
|
||||
{{desktop-notification-config}}
|
||||
<div class="instructions">{{i18n 'user.desktop_notifications.each_browser_note'}}</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group muting">
|
||||
<label class="control-label">{{i18n 'user.users'}}</label>
|
||||
<div class="controls category-controls">
|
||||
<label><span class="icon fa fa-times-circle muted"></span> {{i18n 'user.muted_users'}}</label>
|
||||
{{user-selector excludeCurrentUser=true usernames=model.muted_usernames class="user-selector"}}
|
||||
</div>
|
||||
<div class="instructions">{{i18n 'user.muted_users_instructions'}}</div>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
|
||||
{{plugin-outlet name="user-custom-controls" args=(hash model=model)}}
|
||||
|
||||
<div class="control-group save-button">
|
||||
<div class="controls">
|
||||
{{partial 'user/preferences/save-button'}}
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,110 @@
|
|||
{{#if canEditName}}
|
||||
<div class="control-group pref-name">
|
||||
<label class="control-label">{{i18n 'user.name.title'}}</label>
|
||||
<div class="controls">
|
||||
{{#if model.can_edit_name}}
|
||||
{{text-field value=newNameInput classNames="input-xxlarge"}}
|
||||
{{else}}
|
||||
<span class='static'>{{model.name}}</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class='instructions'>
|
||||
{{nameInstructions}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if canSelectTitle}}
|
||||
<div class="control-group pref-title">
|
||||
<label class="control-label">{{i18n 'user.title.title'}}</label>
|
||||
<div class="controls">
|
||||
<span class="static">{{model.title}}</span>
|
||||
{{#link-to "preferences.badgeTitle" class="btn btn-small pad-left no-text"}}{{fa-icon "pencil"}}{{/link-to}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="control-group pref-avatar">
|
||||
<label class="control-label">{{i18n 'user.avatar.title'}}</label>
|
||||
<div class="controls">
|
||||
{{! we want the "huge" version even though we're downsizing it to "large" in CSS }}
|
||||
{{bound-avatar model "huge"}}
|
||||
{{#unless siteSettings.sso_overrides_avatar}}
|
||||
{{d-button action="showAvatarSelector" class="pad-left" icon="pencil"}}
|
||||
{{/unless}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if siteSettings.allow_profile_backgrounds}}
|
||||
<div class="control-group pref-profile-bg">
|
||||
<label class="control-label">{{i18n 'user.change_profile_background.title'}}</label>
|
||||
<div class="controls">
|
||||
{{image-uploader imageUrl=model.profile_background type="profile_background"}}
|
||||
</div>
|
||||
<div class='instructions'>
|
||||
{{i18n 'user.change_profile_background.instructions'}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group pref-profile-bg">
|
||||
<label class="control-label">{{i18n 'user.change_card_background.title'}}</label>
|
||||
<div class="controls">
|
||||
{{image-uploader imageUrl=model.card_background type="card_background"}}
|
||||
</div>
|
||||
<div class='instructions'>
|
||||
{{i18n 'user.change_card_background.instructions'}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if canChangeBio}}
|
||||
<div class="control-group pref-bio">
|
||||
<label class="control-label">{{i18n 'user.bio'}}</label>
|
||||
<div class="controls bio-composer">
|
||||
{{d-editor value=model.bio_raw}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="control-group pref-location">
|
||||
<label class="control-label">{{i18n 'user.location'}}</label>
|
||||
<div class="controls">
|
||||
{{input type="text" value=model.location class="input-xxlarge" id='edit-location'}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group pref-website">
|
||||
<label class="control-label">{{i18n 'user.website'}}</label>
|
||||
<div class="controls">
|
||||
{{input type="text" value=model.website class="input-xxlarge"}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#each userFields as |uf|}}
|
||||
<div class="control-group">
|
||||
{{user-field field=uf.field value=uf.value}}
|
||||
</div>
|
||||
{{/each}}
|
||||
<div class='clearfix'></div>
|
||||
|
||||
<div class="control-group pref-card-badge">
|
||||
<label class="control-label">{{i18n 'user.card_badge.title'}}</label>
|
||||
<div class="controls">
|
||||
{{#if model.card_image_badge}}
|
||||
{{icon-or-image model.card_image_badge}}
|
||||
{{/if}}
|
||||
{{#link-to "preferences.card-badge" class="btn btn-small pad-left no-text"}}{{fa-icon "pencil"}}{{/link-to}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{plugin-outlet name="user-custom-preferences" args=(hash model=model)}}
|
||||
|
||||
<br/>
|
||||
|
||||
{{plugin-outlet name="user-custom-controls" args=(hash model=model)}}
|
||||
|
||||
<div class="control-group save-button">
|
||||
<div class="controls">
|
||||
{{partial 'user/preferences/save-button'}}
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,41 @@
|
|||
{{#if siteSettings.tagging_enabled}}
|
||||
<div class="control-group tag-notifications">
|
||||
|
||||
<label class="control-label">{{i18n 'user.tag_settings'}}</label>
|
||||
|
||||
<div class="controls tag-controls">
|
||||
<label><span class="icon fa fa-exclamation-circle watching"></span> {{i18n 'user.watched_tags'}}</label>
|
||||
{{tag-chooser tags=model.watched_tags blacklist=selectedTags allowCreate=false placeholder="" everyTag="true" unlimitedTagCount="true"}}
|
||||
</div>
|
||||
<div class="instructions">{{i18n 'user.watched_tags_instructions'}}</div>
|
||||
|
||||
<div class="controls tag-controls">
|
||||
<label><span class="icon fa fa-circle tracking"></span> {{i18n 'user.tracked_tags'}}</label>
|
||||
{{tag-chooser tags=model.tracked_tags blacklist=selectedTags allowCreate=false placeholder="" everyTag="true" unlimitedTagCount="true"}}
|
||||
</div>
|
||||
<div class="instructions">{{i18n 'user.tracked_tags_instructions'}}</div>
|
||||
|
||||
<div class="controls tag-controls">
|
||||
<label><span class="icon fa fa-dot-circle-o watching-first-post"></span> {{i18n 'user.watched_first_post_tags'}}</label>
|
||||
{{tag-chooser tags=model.watching_first_post_tags blacklist=selectedTags allowCreate=false placeholder="" everyTag="true" unlimitedTagCount="true"}}
|
||||
</div>
|
||||
<div class="instructions">{{i18n 'user.watched_first_post_tags_instructions'}}</div>
|
||||
|
||||
<div class="controls tag-controls">
|
||||
<label><span class="icon fa fa-times-circle muted"></span> {{i18n 'user.muted_tags'}}</label>
|
||||
{{tag-chooser tags=model.muted_tags blacklist=selectedTags allowCreate=false placeholder="" everyTag="true" unlimitedTagCount="true"}}
|
||||
</div>
|
||||
<div class="instructions">{{i18n 'user.muted_tags_instructions'}}</div>
|
||||
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
|
||||
{{plugin-outlet name="user-custom-controls" args=(hash model=model)}}
|
||||
|
||||
<div class="control-group save-button">
|
||||
<div class="controls">
|
||||
{{partial 'user/preferences/save-button'}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
|
@ -55,9 +55,12 @@ $input-width: 220px;
|
|||
font-weight: normal;
|
||||
float: auto;
|
||||
}
|
||||
p {
|
||||
.instructions {
|
||||
color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%));
|
||||
margin: 0;
|
||||
font-size: 0.929em;
|
||||
font-weight: normal;
|
||||
line-height: 18px;
|
||||
}
|
||||
}
|
||||
clear: both;
|
||||
|
|
|
@ -65,6 +65,9 @@
|
|||
color: $primary;
|
||||
}
|
||||
}
|
||||
> li.indent {
|
||||
padding-left: 15px;
|
||||
}
|
||||
.active > a, & li > a.active
|
||||
{
|
||||
color: $secondary;
|
||||
|
|
|
@ -101,6 +101,36 @@ body {
|
|||
}
|
||||
}
|
||||
|
||||
body {
|
||||
.form-vertical input, .form-vertical textarea, .form-vertical select, .form-vertical .input-prepend, .form-vertical .input-append {
|
||||
display: inline-block;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.form-vertical {
|
||||
.control-group {
|
||||
margin-bottom: 24px;
|
||||
&:before {
|
||||
display: table;
|
||||
content: "";
|
||||
}
|
||||
&:after {
|
||||
display: table;
|
||||
content: "";
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
.control-label {
|
||||
font-weight: bold;
|
||||
font-size: 1.2em;
|
||||
line-height: 2;
|
||||
}
|
||||
.controls {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* bootstrap carryover */
|
||||
|
||||
|
||||
|
|
|
@ -20,7 +20,9 @@
|
|||
}
|
||||
|
||||
.user-preferences {
|
||||
input.category-selector, input.user-selector {
|
||||
padding-top: 10px;
|
||||
|
||||
input.category-selector, input.user-selector, input.tag-chooser {
|
||||
width: 530px;
|
||||
}
|
||||
|
||||
|
@ -40,9 +42,9 @@
|
|||
display: inline-block;
|
||||
}
|
||||
.instructions {
|
||||
display: inline-block;
|
||||
color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%));
|
||||
margin-left: 160px;
|
||||
margin-top: 5px;
|
||||
margin-top: 0;
|
||||
margin-bottom: 10px;
|
||||
font-size: 80%;
|
||||
line-height: 1.4em;
|
||||
|
@ -66,15 +68,19 @@
|
|||
}
|
||||
}
|
||||
|
||||
.other .controls {
|
||||
.category-notifications .controls, .tag-notifications .controls {
|
||||
select {
|
||||
width: 280px;
|
||||
}
|
||||
}
|
||||
|
||||
.category-notifications .category-controls, .tag-notifications .tag-controls {
|
||||
margin-top: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.form-horizontal .control-group.other {
|
||||
margin-bottom: 0;
|
||||
.user-main .user-preferences .user-field.text {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.form-horizontal .control-group.category {
|
||||
|
@ -93,11 +99,11 @@
|
|||
// position: static;
|
||||
}
|
||||
|
||||
.user-navigation {
|
||||
.user-navigation, .user-preferences {
|
||||
display: table-cell;
|
||||
vertical-align: top;
|
||||
width: 170px;
|
||||
padding-right: 30px;
|
||||
padding-left: 30px;
|
||||
|
||||
h3 {
|
||||
color: $primary;
|
||||
|
@ -131,7 +137,6 @@
|
|||
|
||||
.user-right {
|
||||
width: 900px;
|
||||
margin-top: 20px;
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
|
@ -578,12 +583,6 @@
|
|||
}
|
||||
|
||||
.user-field {
|
||||
label {
|
||||
width: 140px;
|
||||
float: left;
|
||||
text-align: right;
|
||||
font-weight: bold;
|
||||
}
|
||||
input[type=text] {
|
||||
width: 530px;
|
||||
}
|
||||
|
@ -594,7 +593,8 @@
|
|||
font-weight: normal;
|
||||
float: auto;
|
||||
}
|
||||
p {
|
||||
.instructions {
|
||||
display: block;
|
||||
color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%));
|
||||
margin-top: 5px;
|
||||
margin-bottom: 10px;
|
||||
|
|
|
@ -102,7 +102,7 @@ h2#site-text-logo
|
|||
}
|
||||
|
||||
.mobile-view .mobile-nav {
|
||||
&.messages-nav, &.notifications-nav, &.activity-nav {
|
||||
&.messages-nav, &.notifications-nav, &.activity-nav, &.preferences-nav {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
top: -55px;
|
||||
|
@ -188,3 +188,33 @@ h2.recent-topics-title {
|
|||
.page-not-found-search h2 {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.form-vertical {
|
||||
input, textarea, select, .input-prepend, .input-append {
|
||||
display: inline-block;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.control-group {
|
||||
margin-bottom: 12px;
|
||||
&:before {
|
||||
display: table;
|
||||
content: "";
|
||||
}
|
||||
&:after {
|
||||
display: table;
|
||||
content: "";
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
.control-label {
|
||||
font-weight: bold;
|
||||
font-size: 1.2em;
|
||||
line-height: 2;
|
||||
}
|
||||
.controls {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,12 +2,8 @@
|
|||
|
||||
.user-preferences {
|
||||
.control-group {
|
||||
// border-bottom: 1px solid #e5e5e5;
|
||||
padding: 8px 36px 8px 8px;
|
||||
}
|
||||
.control-label {
|
||||
font-weight: bold;
|
||||
}
|
||||
textarea {
|
||||
width: 530px;
|
||||
height: 100px;
|
||||
|
@ -39,8 +35,9 @@
|
|||
width: 520px;
|
||||
}
|
||||
|
||||
.other .controls-dropdown {
|
||||
.controls-dropdown {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 15px;
|
||||
padding-left: 5px;
|
||||
select {
|
||||
width: 280px;
|
||||
|
@ -68,6 +65,16 @@
|
|||
}
|
||||
|
||||
textarea {width: 100%;}
|
||||
|
||||
.desktop-notifications button {
|
||||
float: none;
|
||||
}
|
||||
.apps .controls button {
|
||||
float: right;
|
||||
}
|
||||
.category-notifications .category-controls, .tag-notifications .tag-controls {
|
||||
margin-top: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.profile-image {
|
||||
|
@ -123,10 +130,6 @@
|
|||
|
||||
}
|
||||
|
||||
.form-horizontal .control-group.other {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.form-horizontal .control-group.category {
|
||||
margin-top: 18px;
|
||||
}
|
||||
|
@ -211,9 +214,10 @@
|
|||
overflow: hidden;
|
||||
color: $secondary;
|
||||
|
||||
.secondary {
|
||||
background: dark-light-diff($primary, $secondary, 90%, -65%);
|
||||
font-size: 0.929em;
|
||||
.secondary {
|
||||
background: dark-light-diff($primary, $secondary, 90%, -65%);
|
||||
font-size: 0.929em;
|
||||
|
||||
.btn { padding: 3px 12px; }
|
||||
|
||||
dl dd {
|
||||
|
@ -549,14 +553,10 @@
|
|||
|
||||
.user-field {
|
||||
label {
|
||||
width: 140px;
|
||||
float: left;
|
||||
text-align: right;
|
||||
width: auto;
|
||||
text-align: left;
|
||||
font-weight: bold;
|
||||
}
|
||||
input[type=text] {
|
||||
width: 530px;
|
||||
}
|
||||
.controls {
|
||||
label {
|
||||
width: auto;
|
||||
|
@ -564,7 +564,7 @@
|
|||
font-weight: normal;
|
||||
float: auto;
|
||||
}
|
||||
p {
|
||||
.instructions {
|
||||
color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%));
|
||||
margin-top: 5px;
|
||||
margin-bottom: 10px;
|
||||
|
@ -581,9 +581,9 @@
|
|||
margin-top: 20px;
|
||||
|
||||
.user-content {
|
||||
width: 100%;
|
||||
margin-top: 0;
|
||||
}
|
||||
width: 100%;
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.user-archive {
|
||||
|
|
|
@ -660,6 +660,16 @@ en:
|
|||
failed_to_move: "Failed to move selected messages (perhaps your network is down)"
|
||||
select_all: "Select All"
|
||||
|
||||
preferences_nav:
|
||||
account: "Account"
|
||||
profile: "Profile"
|
||||
emails: "Emails"
|
||||
notifications: "Notifications"
|
||||
categories: "Categories"
|
||||
tags: "Tags"
|
||||
interface: "Interface"
|
||||
apps: "Apps"
|
||||
|
||||
change_password:
|
||||
success: "(email sent)"
|
||||
in_progress: "(sending email)"
|
||||
|
|
|
@ -347,6 +347,14 @@ Discourse::Application.routes.draw do
|
|||
get "#{root_path}/:username/emails" => "users#check_emails", constraints: {username: USERNAME_ROUTE_FORMAT}
|
||||
get({ "#{root_path}/:username/preferences" => "users#preferences", constraints: { username: USERNAME_ROUTE_FORMAT } }.merge(index == 1 ? { as: :email_preferences } : {}))
|
||||
get "#{root_path}/:username/preferences/email" => "users_email#index", constraints: {username: USERNAME_ROUTE_FORMAT}
|
||||
get "#{root_path}/:username/preferences/account" => "users#preferences", constraints: {username: USERNAME_ROUTE_FORMAT}
|
||||
get "#{root_path}/:username/preferences/profile" => "users#preferences", constraints: {username: USERNAME_ROUTE_FORMAT}
|
||||
get "#{root_path}/:username/preferences/emails" => "users#preferences", constraints: {username: USERNAME_ROUTE_FORMAT}
|
||||
get "#{root_path}/:username/preferences/notifications" => "users#preferences", constraints: {username: USERNAME_ROUTE_FORMAT}
|
||||
get "#{root_path}/:username/preferences/categories" => "users#preferences", constraints: {username: USERNAME_ROUTE_FORMAT}
|
||||
get "#{root_path}/:username/preferences/tags" => "users#preferences", constraints: {username: USERNAME_ROUTE_FORMAT}
|
||||
get "#{root_path}/:username/preferences/interface" => "users#preferences", constraints: {username: USERNAME_ROUTE_FORMAT}
|
||||
get "#{root_path}/:username/preferences/apps" => "users#preferences", constraints: {username: USERNAME_ROUTE_FORMAT}
|
||||
put "#{root_path}/:username/preferences/email" => "users_email#update", constraints: {username: USERNAME_ROUTE_FORMAT}
|
||||
get "#{root_path}/:username/preferences/about-me" => "users#preferences", constraints: {username: USERNAME_ROUTE_FORMAT}
|
||||
get "#{root_path}/:username/preferences/badge_title" => "users#preferences", constraints: {username: USERNAME_ROUTE_FORMAT}
|
||||
|
|
|
@ -6,10 +6,12 @@ test("update some fields", () => {
|
|||
|
||||
andThen(() => {
|
||||
ok($('body.user-preferences-page').length, "has the body class");
|
||||
equal(currentURL(), '/u/eviltrout/preferences', "it doesn't redirect");
|
||||
equal(currentURL(), '/u/eviltrout/preferences/account', "defaults to account tab");
|
||||
ok(exists('.user-preferences'), 'it shows the preferences');
|
||||
});
|
||||
|
||||
click(".preferences-nav .nav-profile a");
|
||||
|
||||
fillIn("#edit-location", "Westeros");
|
||||
|
||||
click('.save-user');
|
||||
|
|
Loading…
Reference in New Issue