mirror of
https://github.com/discourse/discourse.git
synced 2025-05-24 22:41:10 +08:00
Refactor: Back all modals by controllers
This commit is contained in:
@ -155,9 +155,9 @@ Discourse = Ember.Application.createWithMixins({
|
|||||||
},
|
},
|
||||||
|
|
||||||
authenticationComplete: function(options) {
|
authenticationComplete: function(options) {
|
||||||
// TODO, how to dispatch this to the view without the container?
|
// TODO, how to dispatch this to the controller without the container?
|
||||||
var loginView = Discourse.__container__.lookup('controller:modal').get('currentView');
|
var loginController = Discourse.__container__.lookup('controller:login');
|
||||||
return loginView.authenticationComplete(options);
|
return loginController.authenticationComplete(options);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -9,14 +9,6 @@
|
|||||||
@module Discourse
|
@module Discourse
|
||||||
**/
|
**/
|
||||||
Discourse.ApplicationController = Discourse.Controller.extend({
|
Discourse.ApplicationController = Discourse.Controller.extend({
|
||||||
needs: ['modal'],
|
|
||||||
|
|
||||||
showLogin: function() {
|
|
||||||
var modalController = this.get('controllers.modal');
|
|
||||||
if (modalController) {
|
|
||||||
modalController.show(Discourse.LoginView.create())
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
routeChanged: function(){
|
routeChanged: function(){
|
||||||
if (window._gaq === undefined) { return; }
|
if (window._gaq === undefined) { return; }
|
||||||
|
@ -0,0 +1,276 @@
|
|||||||
|
/**
|
||||||
|
The modal for creating accounts
|
||||||
|
|
||||||
|
@class CreateAccountController
|
||||||
|
@extends Discourse.Controller
|
||||||
|
@namespace Discourse
|
||||||
|
@uses Discourse.ModalFunctionality
|
||||||
|
@module Discourse
|
||||||
|
**/
|
||||||
|
Discourse.CreateAccountController = Discourse.Controller.extend(Discourse.ModalFunctionality, {
|
||||||
|
uniqueUsernameValidation: null,
|
||||||
|
globalNicknameExists: false,
|
||||||
|
complete: false,
|
||||||
|
accountPasswordConfirm: 0,
|
||||||
|
accountChallenge: 0,
|
||||||
|
formSubmitted: false,
|
||||||
|
|
||||||
|
submitDisabled: function() {
|
||||||
|
if (this.get('formSubmitted')) return true;
|
||||||
|
if (this.get('nameValidation.failed')) return true;
|
||||||
|
if (this.get('emailValidation.failed')) return true;
|
||||||
|
if (this.get('usernameValidation.failed')) return true;
|
||||||
|
if (this.get('passwordValidation.failed')) return true;
|
||||||
|
return false;
|
||||||
|
}.property('nameValidation.failed', 'emailValidation.failed', 'usernameValidation.failed', 'passwordValidation.failed', 'formSubmitted'),
|
||||||
|
|
||||||
|
passwordRequired: function() {
|
||||||
|
return this.blank('authOptions.auth_provider');
|
||||||
|
}.property('authOptions.auth_provider'),
|
||||||
|
|
||||||
|
// Validate the name
|
||||||
|
nameValidation: function() {
|
||||||
|
|
||||||
|
// If blank, fail without a reason
|
||||||
|
if (this.blank('accountName')) return Discourse.InputValidation.create({ failed: true });
|
||||||
|
|
||||||
|
if (this.get('accountPasswordConfirm') === 0) {
|
||||||
|
this.fetchConfirmationValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If too short
|
||||||
|
if (this.get('accountName').length < 3) {
|
||||||
|
return Discourse.InputValidation.create({
|
||||||
|
failed: true,
|
||||||
|
reason: Em.String.i18n('user.name.too_short')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Looks good!
|
||||||
|
return Discourse.InputValidation.create({
|
||||||
|
ok: true,
|
||||||
|
reason: Em.String.i18n('user.name.ok')
|
||||||
|
});
|
||||||
|
}.property('accountName'),
|
||||||
|
|
||||||
|
// Check the email address
|
||||||
|
emailValidation: function() {
|
||||||
|
// If blank, fail without a reason
|
||||||
|
var email;
|
||||||
|
if (this.blank('accountEmail')) {
|
||||||
|
return Discourse.InputValidation.create({
|
||||||
|
failed: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
email = this.get("accountEmail");
|
||||||
|
if ((this.get('authOptions.email') === email) && this.get('authOptions.email_valid')) {
|
||||||
|
return Discourse.InputValidation.create({
|
||||||
|
ok: true,
|
||||||
|
reason: Em.String.i18n('user.email.authenticated', {
|
||||||
|
provider: this.get('authOptions.auth_provider')
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Discourse.Utilities.emailValid(email)) {
|
||||||
|
return Discourse.InputValidation.create({
|
||||||
|
ok: true,
|
||||||
|
reason: Em.String.i18n('user.email.ok')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Discourse.InputValidation.create({
|
||||||
|
failed: true,
|
||||||
|
reason: Em.String.i18n('user.email.invalid')
|
||||||
|
});
|
||||||
|
}.property('accountEmail'),
|
||||||
|
|
||||||
|
usernameMatch: function() {
|
||||||
|
if (this.usernameNeedsToBeValidatedWithEmail()) {
|
||||||
|
if (this.get('emailValidation.failed')) {
|
||||||
|
if (this.shouldCheckUsernameMatch()) {
|
||||||
|
return this.set('uniqueUsernameValidation', Discourse.InputValidation.create({
|
||||||
|
failed: true,
|
||||||
|
reason: Em.String.i18n('user.username.enter_email')
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
return this.set('uniqueUsernameValidation', Discourse.InputValidation.create({ failed: true }));
|
||||||
|
}
|
||||||
|
} else if (this.shouldCheckUsernameMatch()) {
|
||||||
|
this.set('uniqueUsernameValidation', Discourse.InputValidation.create({
|
||||||
|
failed: true,
|
||||||
|
reason: Em.String.i18n('user.username.checking')
|
||||||
|
}));
|
||||||
|
return this.checkUsernameAvailability();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.observes('accountEmail'),
|
||||||
|
|
||||||
|
basicUsernameValidation: function() {
|
||||||
|
this.set('uniqueUsernameValidation', null);
|
||||||
|
|
||||||
|
// If blank, fail without a reason
|
||||||
|
if (this.blank('accountUsername')) {
|
||||||
|
return Discourse.InputValidation.create({
|
||||||
|
failed: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// If too short
|
||||||
|
if (this.get('accountUsername').length < 3) {
|
||||||
|
return Discourse.InputValidation.create({
|
||||||
|
failed: true,
|
||||||
|
reason: Em.String.i18n('user.username.too_short')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// If too long
|
||||||
|
if (this.get('accountUsername').length > 15) {
|
||||||
|
return Discourse.InputValidation.create({
|
||||||
|
failed: true,
|
||||||
|
reason: Em.String.i18n('user.username.too_long')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.checkUsernameAvailability();
|
||||||
|
// Let's check it out asynchronously
|
||||||
|
return Discourse.InputValidation.create({
|
||||||
|
failed: true,
|
||||||
|
reason: Em.String.i18n('user.username.checking')
|
||||||
|
});
|
||||||
|
}.property('accountUsername'),
|
||||||
|
|
||||||
|
shouldCheckUsernameMatch: function() {
|
||||||
|
return !this.blank('accountUsername') && this.get('accountUsername').length > 2;
|
||||||
|
},
|
||||||
|
|
||||||
|
checkUsernameAvailability: Discourse.debounce(function() {
|
||||||
|
var _this = this;
|
||||||
|
if (this.shouldCheckUsernameMatch()) {
|
||||||
|
return Discourse.User.checkUsername(this.get('accountUsername'), this.get('accountEmail')).then(function(result) {
|
||||||
|
_this.set('globalNicknameExists', false);
|
||||||
|
if (result.available) {
|
||||||
|
if (result.global_match) {
|
||||||
|
_this.set('globalNicknameExists', true);
|
||||||
|
return _this.set('uniqueUsernameValidation', Discourse.InputValidation.create({
|
||||||
|
ok: true,
|
||||||
|
reason: Em.String.i18n('user.username.global_match')
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
return _this.set('uniqueUsernameValidation', Discourse.InputValidation.create({
|
||||||
|
ok: true,
|
||||||
|
reason: Em.String.i18n('user.username.available')
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (result.suggestion) {
|
||||||
|
if (result.global_match !== void 0 && result.global_match === false) {
|
||||||
|
_this.set('globalNicknameExists', true);
|
||||||
|
return _this.set('uniqueUsernameValidation', Discourse.InputValidation.create({
|
||||||
|
failed: true,
|
||||||
|
reason: Em.String.i18n('user.username.global_mismatch', result)
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
return _this.set('uniqueUsernameValidation', Discourse.InputValidation.create({
|
||||||
|
failed: true,
|
||||||
|
reason: Em.String.i18n('user.username.not_available', result)
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
} else if (result.errors) {
|
||||||
|
return _this.set('uniqueUsernameValidation', Discourse.InputValidation.create({
|
||||||
|
failed: true,
|
||||||
|
reason: result.errors.join(' ')
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
_this.set('globalNicknameExists', true);
|
||||||
|
return _this.set('uniqueUsernameValidation', Discourse.InputValidation.create({
|
||||||
|
failed: true,
|
||||||
|
reason: Em.String.i18n('user.username.enter_email')
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 500),
|
||||||
|
|
||||||
|
// Actually wait for the async name check before we're 100% sure we're good to go
|
||||||
|
usernameValidation: function() {
|
||||||
|
var basicValidation, uniqueUsername;
|
||||||
|
basicValidation = this.get('basicUsernameValidation');
|
||||||
|
uniqueUsername = this.get('uniqueUsernameValidation');
|
||||||
|
if (uniqueUsername) {
|
||||||
|
return uniqueUsername;
|
||||||
|
}
|
||||||
|
return basicValidation;
|
||||||
|
}.property('uniqueUsernameValidation', 'basicUsernameValidation'),
|
||||||
|
|
||||||
|
usernameNeedsToBeValidatedWithEmail: function() {
|
||||||
|
return( this.get('globalNicknameExists') || false );
|
||||||
|
},
|
||||||
|
|
||||||
|
// Validate the password
|
||||||
|
passwordValidation: function() {
|
||||||
|
var password;
|
||||||
|
if (!this.get('passwordRequired')) {
|
||||||
|
return Discourse.InputValidation.create({
|
||||||
|
ok: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// If blank, fail without a reason
|
||||||
|
password = this.get("accountPassword");
|
||||||
|
if (this.blank('accountPassword')) {
|
||||||
|
return Discourse.InputValidation.create({ failed: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// If too short
|
||||||
|
if (password.length < 6) {
|
||||||
|
return Discourse.InputValidation.create({
|
||||||
|
failed: true,
|
||||||
|
reason: Em.String.i18n('user.password.too_short')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Looks good!
|
||||||
|
return Discourse.InputValidation.create({
|
||||||
|
ok: true,
|
||||||
|
reason: Em.String.i18n('user.password.ok')
|
||||||
|
});
|
||||||
|
}.property('accountPassword'),
|
||||||
|
|
||||||
|
fetchConfirmationValue: function() {
|
||||||
|
var createAccountController = this;
|
||||||
|
return Discourse.ajax('/users/hp.json').then(function (json) {
|
||||||
|
createAccountController.set('accountPasswordConfirm', json.value);
|
||||||
|
createAccountController.set('accountChallenge', json.challenge.split("").reverse().join(""));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
createAccount: function() {
|
||||||
|
var createAccountController = this;
|
||||||
|
this.set('formSubmitted', true);
|
||||||
|
var name = this.get('accountName');
|
||||||
|
var email = this.get('accountEmail');
|
||||||
|
var password = this.get('accountPassword');
|
||||||
|
var username = this.get('accountUsername');
|
||||||
|
var passwordConfirm = this.get('accountPasswordConfirm');
|
||||||
|
var challenge = this.get('accountChallenge');
|
||||||
|
return Discourse.User.createAccount(name, email, password, username, passwordConfirm, challenge).then(function(result) {
|
||||||
|
if (result.success) {
|
||||||
|
createAccountController.flash(result.message);
|
||||||
|
createAccountController.set('complete', true);
|
||||||
|
} else {
|
||||||
|
createAccountController.flash(result.message || Em.String.i18n('create_account.failed'), 'error');
|
||||||
|
createAccountController.set('formSubmitted', false);
|
||||||
|
}
|
||||||
|
if (result.active) {
|
||||||
|
return window.location.reload();
|
||||||
|
}
|
||||||
|
}, function() {
|
||||||
|
createAccountController.set('formSubmitted', false);
|
||||||
|
return createAccountController.flash(Em.String.i18n('create_account.failed'), 'error');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
@ -0,0 +1,194 @@
|
|||||||
|
/**
|
||||||
|
Modal for editing / creating a category
|
||||||
|
|
||||||
|
@class EditCategoryController
|
||||||
|
@extends Discourse.ObjectController
|
||||||
|
@namespace Discourse
|
||||||
|
@uses Discourse.ModalFunctionality
|
||||||
|
@module Discourse
|
||||||
|
**/
|
||||||
|
Discourse.EditCategoryController = Discourse.ObjectController.extend(Discourse.ModalFunctionality, {
|
||||||
|
generalSelected: Ember.computed.equal('selectedTab', 'general'),
|
||||||
|
securitySelected: Ember.computed.equal('selectedTab', 'security'),
|
||||||
|
settingsSelected: Ember.computed.equal('selectedTab', 'settings'),
|
||||||
|
foregroundColors: ['FFFFFF', '000000'],
|
||||||
|
|
||||||
|
descriptionChanged: function() {
|
||||||
|
if (this.present('description')) {
|
||||||
|
this.set('controllers.modal.modalClass', 'edit-category-modal full');
|
||||||
|
} else {
|
||||||
|
this.set('controllers.modal.modalClass', 'edit-category-modal small');
|
||||||
|
}
|
||||||
|
}.observes('description'),
|
||||||
|
|
||||||
|
title: function() {
|
||||||
|
if (this.get('id')) return Em.String.i18n("category.edit_long");
|
||||||
|
if (this.get('isUncategorized')) return Em.String.i18n("category.edit_uncategorized");
|
||||||
|
return Em.String.i18n("category.create");
|
||||||
|
}.property('id'),
|
||||||
|
|
||||||
|
titleChanged: function() {
|
||||||
|
this.set('controllers.modal.title', this.get('title'));
|
||||||
|
}.observes('title'),
|
||||||
|
|
||||||
|
selectGeneral: function() {
|
||||||
|
this.set('selectedTab', 'general');
|
||||||
|
},
|
||||||
|
|
||||||
|
selectSecurity: function() {
|
||||||
|
this.set('selectedTab', 'security');
|
||||||
|
},
|
||||||
|
|
||||||
|
selectSettings: function() {
|
||||||
|
this.set('selectedTab', 'settings');
|
||||||
|
},
|
||||||
|
|
||||||
|
disabled: function() {
|
||||||
|
if (this.get('saving') || this.get('deleting')) return true;
|
||||||
|
if (!this.get('name')) return true;
|
||||||
|
if (!this.get('color')) return true;
|
||||||
|
return false;
|
||||||
|
}.property('name', 'color', 'deleting'),
|
||||||
|
|
||||||
|
deleteVisible: function() {
|
||||||
|
return (this.get('id') && this.get('topic_count') === 0);
|
||||||
|
}.property('id', 'topic_count'),
|
||||||
|
|
||||||
|
deleteDisabled: function() {
|
||||||
|
return (this.get('deleting') || this.get('saving') || false);
|
||||||
|
}.property('disabled', 'saving', 'deleting'),
|
||||||
|
|
||||||
|
colorStyle: function() {
|
||||||
|
return "background-color: #" + (this.get('color')) + "; color: #" + (this.get('text_color')) + ";";
|
||||||
|
}.property('color', 'text_color'),
|
||||||
|
|
||||||
|
// background colors are available as a pipe-separated string
|
||||||
|
backgroundColors: function() {
|
||||||
|
var categories = Discourse.Category.list();
|
||||||
|
return Discourse.SiteSettings.category_colors.split("|").map(function(i) { return i.toUpperCase(); }).concat(
|
||||||
|
categories.map(function(c) { return c.color.toUpperCase(); }) ).uniq();
|
||||||
|
}.property('Discourse.SiteSettings.category_colors'),
|
||||||
|
|
||||||
|
usedBackgroundColors: function() {
|
||||||
|
var categories = Discourse.Category.list();
|
||||||
|
|
||||||
|
var currentCat = this.get('model');
|
||||||
|
|
||||||
|
return categories.map(function(c) {
|
||||||
|
// If editing a category, don't include its color:
|
||||||
|
return (currentCat.get('id') && currentCat.get('color').toUpperCase() === c.color.toUpperCase()) ? null : c.color.toUpperCase();
|
||||||
|
}, this).compact();
|
||||||
|
}.property('id', 'color'),
|
||||||
|
|
||||||
|
categoryName: function() {
|
||||||
|
var name = this.get('name') || "";
|
||||||
|
return name.trim().length > 0 ? name : Em.String.i18n("preview");
|
||||||
|
}.property('name'),
|
||||||
|
|
||||||
|
buttonTitle: function() {
|
||||||
|
if (this.get('saving')) return Em.String.i18n("saving");
|
||||||
|
if (this.get('isUncategorized')) return Em.String.i18n("save");
|
||||||
|
return (this.get('id') ? Em.String.i18n("category.save") : Em.String.i18n("category.create"));
|
||||||
|
}.property('saving', 'id'),
|
||||||
|
|
||||||
|
deleteButtonTitle: function() {
|
||||||
|
return Em.String.i18n('category.delete');
|
||||||
|
}.property(),
|
||||||
|
|
||||||
|
didInsertElement: function() {
|
||||||
|
this._super();
|
||||||
|
|
||||||
|
if (this.get('id')) {
|
||||||
|
this.set('loading', true);
|
||||||
|
var categoryController = this;
|
||||||
|
|
||||||
|
// We need the topic_count to be correct, so get the most up-to-date info about this category from the server.
|
||||||
|
Discourse.Category.findBySlugOrId( this.get('slug') || this.get('id') ).then( function(cat) {
|
||||||
|
categoryController.set('category', cat);
|
||||||
|
Discourse.Site.instance().updateCategory(cat);
|
||||||
|
categoryController.set('id', categoryController.get('slug'));
|
||||||
|
categoryController.set('loading', false);
|
||||||
|
});
|
||||||
|
} else if( this.get('isUncategorized') ) {
|
||||||
|
this.set('category', Discourse.Category.uncategorizedInstance());
|
||||||
|
} else {
|
||||||
|
this.set('category', Discourse.Category.create({ color: 'AB9364', text_color: 'FFFFFF', hotness: 5 }));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
showCategoryTopic: function() {
|
||||||
|
$('#discourse-modal').modal('hide');
|
||||||
|
Discourse.URL.routeTo(this.get('topic_url'));
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
addGroup: function(){
|
||||||
|
this.get('model').addGroup(this.get("selectedGroup"));
|
||||||
|
},
|
||||||
|
|
||||||
|
removeGroup: function(group){
|
||||||
|
// OBVIOUS, Ember treats this as Ember.String, we need a real string here
|
||||||
|
group = group + "";
|
||||||
|
this.get('model').removeGroup(group);
|
||||||
|
},
|
||||||
|
|
||||||
|
saveCategory: function() {
|
||||||
|
var categoryController = this;
|
||||||
|
this.set('saving', true);
|
||||||
|
|
||||||
|
|
||||||
|
if( this.get('isUncategorized') ) {
|
||||||
|
$.when(
|
||||||
|
Discourse.SiteSetting.update('uncategorized_color', this.get('color')),
|
||||||
|
Discourse.SiteSetting.update('uncategorized_text_color', this.get('text_color')),
|
||||||
|
Discourse.SiteSetting.update('uncategorized_name', this.get('name'))
|
||||||
|
).then(function(result) {
|
||||||
|
// success
|
||||||
|
$('#discourse-modal').modal('hide');
|
||||||
|
// We can't redirect to the uncategorized category on save because the slug
|
||||||
|
// might have changed.
|
||||||
|
Discourse.URL.redirectTo("/categories");
|
||||||
|
}, function(errors) {
|
||||||
|
// errors
|
||||||
|
if(errors.length === 0) errors.push(Em.String.i18n("category.save_error"));
|
||||||
|
categoryController.displayErrors(errors);
|
||||||
|
categoryController.set('saving', false);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.get('model').save().then(function(result) {
|
||||||
|
// success
|
||||||
|
$('#discourse-modal').modal('hide');
|
||||||
|
Discourse.URL.redirectTo("/category/" + Discourse.Category.slugFor(result.category));
|
||||||
|
}, function(errors) {
|
||||||
|
// errors
|
||||||
|
if(errors.length === 0) errors.push(Em.String.i18n("category.creation_error"));
|
||||||
|
categoryController.displayErrors(errors);
|
||||||
|
categoryController.set('saving', false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteCategory: function() {
|
||||||
|
var categoryController = this;
|
||||||
|
this.set('deleting', true);
|
||||||
|
$('#discourse-modal').modal('hide');
|
||||||
|
bootbox.confirm(Em.String.i18n("category.delete_confirm"), Em.String.i18n("no_value"), Em.String.i18n("yes_value"), function(result) {
|
||||||
|
if (result) {
|
||||||
|
categoryController.get('category').destroy().then(function(){
|
||||||
|
// success
|
||||||
|
Discourse.URL.redirectTo("/categories");
|
||||||
|
}, function(jqXHR){
|
||||||
|
// error
|
||||||
|
$('#discourse-modal').modal('show');
|
||||||
|
categoryController.displayErrors([Em.String.i18n("category.delete_error")]);
|
||||||
|
categoryController.set('deleting', false);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$('#discourse-modal').modal('show');
|
||||||
|
categoryController.set('deleting', false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
});
|
@ -0,0 +1,45 @@
|
|||||||
|
/**
|
||||||
|
Modal related to auto closing of topics
|
||||||
|
|
||||||
|
@class EditTopicAutoCloseController
|
||||||
|
@extends Discourse.ObjectController
|
||||||
|
@namespace Discourse
|
||||||
|
@uses Discourse.ModalFunctionality
|
||||||
|
@module Discourse
|
||||||
|
**/
|
||||||
|
Discourse.EditTopicAutoCloseController = Discourse.ObjectController.extend(Discourse.ModalFunctionality, {
|
||||||
|
|
||||||
|
setDays: function() {
|
||||||
|
if( this.get('auto_close_at') ) {
|
||||||
|
var closeTime = Date.create( this.get('auto_close_at') );
|
||||||
|
if (closeTime.isFuture()) {
|
||||||
|
this.set('auto_close_days', closeTime.daysSince());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.set('auto_close_days', "");
|
||||||
|
}
|
||||||
|
}.observes('auto_close_at'),
|
||||||
|
|
||||||
|
saveAutoClose: function() {
|
||||||
|
this.setAutoClose( parseFloat(this.get('auto_close_days')) );
|
||||||
|
},
|
||||||
|
|
||||||
|
removeAutoClose: function() {
|
||||||
|
this.setAutoClose(null);
|
||||||
|
},
|
||||||
|
|
||||||
|
setAutoClose: function(days) {
|
||||||
|
var editTopicAutoCloseController = this;
|
||||||
|
Discourse.ajax({
|
||||||
|
url: "/t/" + this.get('id') + "/autoclose",
|
||||||
|
type: 'PUT',
|
||||||
|
dataType: 'json',
|
||||||
|
data: { auto_close_days: days > 0 ? days : null }
|
||||||
|
}).then(function(){
|
||||||
|
editTopicAutoCloseController.set('auto_close_at', Date.create(days + ' days from now').toJSON());
|
||||||
|
}, function (error) {
|
||||||
|
bootbox.alert(Em.String.i18n('generic_error'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
@ -0,0 +1,78 @@
|
|||||||
|
/**
|
||||||
|
This controller supports actions related to flagging
|
||||||
|
|
||||||
|
@class FlagController
|
||||||
|
@extends Discourse.ObjectController
|
||||||
|
@namespace Discourse
|
||||||
|
@uses Discourse.ModalFunctionality
|
||||||
|
@module Discourse
|
||||||
|
**/
|
||||||
|
Discourse.FlagController = Discourse.ObjectController.extend(Discourse.ModalFunctionality, {
|
||||||
|
|
||||||
|
// trick to bind user / post to flag
|
||||||
|
boundFlags: function() {
|
||||||
|
var _this = this;
|
||||||
|
var original = this.get('flagsAvailable');
|
||||||
|
if(original){
|
||||||
|
return $.map(original, function(v){
|
||||||
|
var b = Discourse.BoundPostActionType.create(v);
|
||||||
|
b.set('post', _this.get('model'));
|
||||||
|
return b;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}.property('flagsAvailable.@each'),
|
||||||
|
|
||||||
|
changePostActionType: function(action) {
|
||||||
|
if (this.get('postActionTypeId') === action.id) return false;
|
||||||
|
|
||||||
|
this.get('boundFlags').setEach('selected', false);
|
||||||
|
action.set('selected', true);
|
||||||
|
|
||||||
|
this.set('postActionTypeId', action.id);
|
||||||
|
this.set('isCustomFlag', action.is_custom_flag);
|
||||||
|
this.set('selected', action);
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
showSubmit: function() {
|
||||||
|
if (this.get('postActionTypeId')) {
|
||||||
|
if (this.get('isCustomFlag')) {
|
||||||
|
var m = this.get('selected.message');
|
||||||
|
return m && m.length >= 10 && m.length <= 500;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}.property('isCustomFlag', 'selected.customMessageLength', 'postActionTypeId'),
|
||||||
|
|
||||||
|
submitText: function(){
|
||||||
|
var action = this.get('selected');
|
||||||
|
if (this.get('selected.is_custom_flag')) {
|
||||||
|
return Em.String.i18n("flagging.notify_action");
|
||||||
|
} else {
|
||||||
|
return Em.String.i18n("flagging.action");
|
||||||
|
}
|
||||||
|
}.property('selected'),
|
||||||
|
|
||||||
|
createFlag: function() {
|
||||||
|
var _this = this;
|
||||||
|
|
||||||
|
var action = this.get('selected');
|
||||||
|
var postAction = this.get('actionByName.' + (action.get('name_key')));
|
||||||
|
|
||||||
|
var actionType = Discourse.Site.instance().postActionTypeById(this.get('postActionTypeId'));
|
||||||
|
if (postAction) {
|
||||||
|
postAction.act({
|
||||||
|
message: action.get('message')
|
||||||
|
}).then(function() {
|
||||||
|
return $('#discourse-modal').modal('hide');
|
||||||
|
}, function(errors) {
|
||||||
|
return _this.displayErrors(errors);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
The modal for when the user has forgotten their password
|
||||||
|
|
||||||
|
@class ForgotPasswordController
|
||||||
|
@extends Discourse.Controller
|
||||||
|
@namespace Discourse
|
||||||
|
@uses Discourse.ModalFunctionality
|
||||||
|
@module Discourse
|
||||||
|
**/
|
||||||
|
Discourse.ForgotPasswordController = Discourse.Controller.extend(Discourse.ModalFunctionality, {
|
||||||
|
|
||||||
|
// You need a value in the field to submit it.
|
||||||
|
submitDisabled: function() {
|
||||||
|
return this.blank('accountEmailOrUsername');
|
||||||
|
}.property('accountEmailOrUsername'),
|
||||||
|
|
||||||
|
submit: function() {
|
||||||
|
|
||||||
|
Discourse.ajax("/session/forgot_password", {
|
||||||
|
data: { login: this.get('accountEmailOrUsername') },
|
||||||
|
type: 'POST'
|
||||||
|
});
|
||||||
|
|
||||||
|
// don't tell people what happened, this keeps it more secure (ensure same on server)
|
||||||
|
this.flash(Em.String.i18n('forgot_password.complete'));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
@ -0,0 +1,85 @@
|
|||||||
|
/*jshint newcap:false*/
|
||||||
|
/*global diff_match_patch:true assetPath:true*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
This controller handles displaying of history
|
||||||
|
|
||||||
|
@class HistoryController
|
||||||
|
@extends Discourse.ObjectController
|
||||||
|
@namespace Discourse
|
||||||
|
@uses Discourse.ModalFunctionality
|
||||||
|
@module Discourse
|
||||||
|
**/
|
||||||
|
Discourse.HistoryController = Discourse.ObjectController.extend(Discourse.ModalFunctionality, {
|
||||||
|
diffLibraryLoaded: false,
|
||||||
|
diff: null,
|
||||||
|
|
||||||
|
init: function(){
|
||||||
|
this._super();
|
||||||
|
var historyController = this;
|
||||||
|
$LAB.script(assetPath('defer/google_diff_match_patch')).wait(function(){
|
||||||
|
historyController.set('diffLibraryLoaded', true);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
loadSide: function(side) {
|
||||||
|
if (this.get("version" + side)) {
|
||||||
|
var orig = this.get('model');
|
||||||
|
var version = this.get("version" + side + ".number");
|
||||||
|
if (version === orig.get('version')) {
|
||||||
|
this.set("post" + side, orig);
|
||||||
|
} else {
|
||||||
|
var historyController = this;
|
||||||
|
Discourse.Post.loadVersion(orig.get('id'), version).then(function(post) {
|
||||||
|
historyController.set("post" + side, post);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
changedLeftVersion: function() {
|
||||||
|
this.loadSide("Left");
|
||||||
|
}.observes('versionLeft'),
|
||||||
|
|
||||||
|
changedRightVersion: function() {
|
||||||
|
this.loadSide("Right");
|
||||||
|
}.observes('versionRight'),
|
||||||
|
|
||||||
|
loadedPosts: function() {
|
||||||
|
if (this.get('diffLibraryLoaded') && this.get('postLeft') && this.get('postRight')) {
|
||||||
|
var dmp = new diff_match_patch(),
|
||||||
|
before = this.get("postLeft.cooked"),
|
||||||
|
after = this.get("postRight.cooked"),
|
||||||
|
diff = dmp.diff_main(before, after);
|
||||||
|
dmp.diff_cleanupSemantic(diff);
|
||||||
|
this.set('diff', dmp.diff_prettyHtml(diff));
|
||||||
|
}
|
||||||
|
}.observes('diffLibraryLoaded', 'postLeft', 'postRight'),
|
||||||
|
|
||||||
|
refresh: function() {
|
||||||
|
this.setProperties({
|
||||||
|
loading: true,
|
||||||
|
postLeft: null,
|
||||||
|
postRight: null
|
||||||
|
});
|
||||||
|
|
||||||
|
var historyController = this;
|
||||||
|
this.get('model').loadVersions().then(function(result) {
|
||||||
|
result.each(function(item) {
|
||||||
|
item.description = "v" + item.number + " - " + Date.create(item.created_at).relative() + " - " +
|
||||||
|
Em.String.i18n("changed_by", { author: item.display_username });
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('wat');
|
||||||
|
historyController.setProperties({
|
||||||
|
loading: false,
|
||||||
|
versionLeft: result.first(),
|
||||||
|
versionRight: result.last(),
|
||||||
|
versions: result
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
The modal for inviting a user to a topic
|
||||||
|
|
||||||
|
@class ImageSelectorController
|
||||||
|
@extends Discourse.Controller
|
||||||
|
@namespace Discourse
|
||||||
|
@uses Discourse.ModalFunctionality
|
||||||
|
@module Discourse
|
||||||
|
**/
|
||||||
|
Discourse.ImageSelectorController = Discourse.Controller.extend(Discourse.ModalFunctionality, {
|
||||||
|
|
||||||
|
selectLocal: function() {
|
||||||
|
this.set('localSelected', true);
|
||||||
|
},
|
||||||
|
|
||||||
|
selectRemote: function() {
|
||||||
|
this.set('localSelected', false);
|
||||||
|
},
|
||||||
|
|
||||||
|
remoteSelected: Em.computed.not('localSelected')
|
||||||
|
|
||||||
|
});
|
@ -0,0 +1,44 @@
|
|||||||
|
/**
|
||||||
|
The modal for inviting a user to a topic
|
||||||
|
|
||||||
|
@class InviteController
|
||||||
|
@extends Discourse.Controller
|
||||||
|
@namespace Discourse
|
||||||
|
@uses Discourse.ModalFunctionality
|
||||||
|
@module Discourse
|
||||||
|
**/
|
||||||
|
Discourse.InviteController = Discourse.ObjectController.extend(Discourse.ModalFunctionality, {
|
||||||
|
|
||||||
|
disabled: function() {
|
||||||
|
if (this.get('saving')) return true;
|
||||||
|
if (this.blank('email')) return true;
|
||||||
|
if (!Discourse.Utilities.emailValid(this.get('email'))) return true;
|
||||||
|
return false;
|
||||||
|
}.property('email', 'saving'),
|
||||||
|
|
||||||
|
buttonTitle: function() {
|
||||||
|
if (this.get('saving')) return Em.String.i18n('topic.inviting');
|
||||||
|
return Em.String.i18n('topic.invite_reply.action');
|
||||||
|
}.property('saving'),
|
||||||
|
|
||||||
|
successMessage: function() {
|
||||||
|
return Em.String.i18n('topic.invite_reply.success', { email: this.get('email') });
|
||||||
|
}.property('email'),
|
||||||
|
|
||||||
|
createInvite: function() {
|
||||||
|
var inviteController = this;
|
||||||
|
this.set('saving', true);
|
||||||
|
this.set('error', false);
|
||||||
|
this.get('model').inviteUser(this.get('email')).then(function() {
|
||||||
|
// Success
|
||||||
|
inviteController.set('saving', false);
|
||||||
|
return inviteController.set('finished', true);
|
||||||
|
}, function() {
|
||||||
|
// Failure
|
||||||
|
inviteController.set('error', true);
|
||||||
|
return inviteController.set('saving', false);
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
The modal for inviting a user to a private topic
|
||||||
|
|
||||||
|
@class InvitePrivateController
|
||||||
|
@extends Discourse.Controller
|
||||||
|
@namespace Discourse
|
||||||
|
@uses Discourse.ModalFunctionality
|
||||||
|
@module Discourse
|
||||||
|
**/
|
||||||
|
Discourse.InvitePrivateController = Discourse.ObjectController.extend(Discourse.ModalFunctionality, {
|
||||||
|
|
||||||
|
disabled: function() {
|
||||||
|
if (this.get('saving')) return true;
|
||||||
|
return this.blank('emailOrUsername');
|
||||||
|
}.property('emailOrUsername', 'saving'),
|
||||||
|
|
||||||
|
buttonTitle: function() {
|
||||||
|
if (this.get('saving')) return Em.String.i18n('topic.inviting');
|
||||||
|
return Em.String.i18n('topic.invite_private.action');
|
||||||
|
}.property('saving'),
|
||||||
|
|
||||||
|
invite: function() {
|
||||||
|
var invitePrivateController = this;
|
||||||
|
this.set('saving', true);
|
||||||
|
this.set('error', false);
|
||||||
|
// Invite the user to the private message
|
||||||
|
this.get('content').inviteUser(this.get('emailOrUsername')).then(function() {
|
||||||
|
// Success
|
||||||
|
invitePrivateController.set('saving', false);
|
||||||
|
invitePrivateController.set('finished', true);
|
||||||
|
}, function() {
|
||||||
|
// Failure
|
||||||
|
invitePrivateController.set('error', true);
|
||||||
|
invitePrivateController.set('saving', false);
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
@ -24,11 +24,6 @@ Discourse.ListCategoriesController = Discourse.ObjectController.extend({
|
|||||||
});
|
});
|
||||||
}.property('categories.@each'),
|
}.property('categories.@each'),
|
||||||
|
|
||||||
editCategory: function(category) {
|
|
||||||
this.get('controllers.modal').show(Discourse.EditCategoryView.create({ category: category }));
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
canEdit: function() {
|
canEdit: function() {
|
||||||
var u = Discourse.User.current();
|
var u = Discourse.User.current();
|
||||||
return u && u.admin;
|
return u && u.admin;
|
||||||
|
@ -101,11 +101,6 @@ Discourse.ListController = Discourse.Controller.extend({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
createCategory: function() {
|
|
||||||
var _ref;
|
|
||||||
return (_ref = this.get('controllers.modal')) ? _ref.show(Discourse.EditCategoryView.create()) : void 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
canEditCategory: function() {
|
canEditCategory: function() {
|
||||||
if( this.present('category') ) {
|
if( this.present('category') ) {
|
||||||
var u = Discourse.User.current();
|
var u = Discourse.User.current();
|
||||||
@ -113,12 +108,7 @@ Discourse.ListController = Discourse.Controller.extend({
|
|||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}.property('category'),
|
}.property('category')
|
||||||
|
|
||||||
editCategory: function() {
|
|
||||||
this.get('controllers.modal').show(Discourse.EditCategoryView.create({ category: this.get('category') }));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -41,14 +41,6 @@ Discourse.ListTopicsController = Discourse.ObjectController.extend({
|
|||||||
this.toggleProperty('rankDetailsVisible');
|
this.toggleProperty('rankDetailsVisible');
|
||||||
},
|
},
|
||||||
|
|
||||||
// Show rank details
|
|
||||||
showRankDetails: function(topic) {
|
|
||||||
var modalController = this.get('controllers.modal');
|
|
||||||
if (modalController) {
|
|
||||||
modalController.show(Discourse.TopicRankDetailsView.create({ topic: topic }));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
createTopic: function() {
|
createTopic: function() {
|
||||||
this.get('controllers.list').createTopic();
|
this.get('controllers.list').createTopic();
|
||||||
},
|
},
|
||||||
|
156
app/assets/javascripts/discourse/controllers/login_controller.js
Normal file
156
app/assets/javascripts/discourse/controllers/login_controller.js
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
/**
|
||||||
|
This controller supports actions related to flagging
|
||||||
|
|
||||||
|
@class LoginController
|
||||||
|
@extends Discourse.Controller
|
||||||
|
@namespace Discourse
|
||||||
|
@uses Discourse.ModalFunctionality
|
||||||
|
@module Discourse
|
||||||
|
**/
|
||||||
|
Discourse.LoginController = Discourse.Controller.extend(Discourse.ModalFunctionality, {
|
||||||
|
needs: ['modal', 'createAccount'],
|
||||||
|
authenticate: null,
|
||||||
|
loggingIn: false,
|
||||||
|
|
||||||
|
site: function() {
|
||||||
|
return Discourse.Site.instance();
|
||||||
|
}.property(),
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Determines whether at least one login button is enabled
|
||||||
|
**/
|
||||||
|
hasAtLeastOneLoginButton: function() {
|
||||||
|
return Discourse.SiteSettings.enable_google_logins ||
|
||||||
|
Discourse.SiteSettings.enable_facebook_logins ||
|
||||||
|
Discourse.SiteSettings.enable_cas_logins ||
|
||||||
|
Discourse.SiteSettings.enable_twitter_logins ||
|
||||||
|
Discourse.SiteSettings.enable_yahoo_logins ||
|
||||||
|
Discourse.SiteSettings.enable_github_logins ||
|
||||||
|
Discourse.SiteSettings.enable_persona_logins;
|
||||||
|
}.property(),
|
||||||
|
|
||||||
|
loginButtonText: function() {
|
||||||
|
return this.get('loggingIn') ? Em.String.i18n('login.logging_in') : Em.String.i18n('login.title');
|
||||||
|
}.property('loggingIn'),
|
||||||
|
|
||||||
|
loginDisabled: function() {
|
||||||
|
return this.get('loggingIn') || this.blank('loginName') || this.blank('loginPassword');
|
||||||
|
}.property('loginName', 'loginPassword', 'loggingIn'),
|
||||||
|
|
||||||
|
login: function() {
|
||||||
|
this.set('loggingIn', true);
|
||||||
|
|
||||||
|
var loginController = this;
|
||||||
|
Discourse.ajax("/session", {
|
||||||
|
data: { login: this.get('loginName'), password: this.get('loginPassword') },
|
||||||
|
type: 'POST'
|
||||||
|
}).then(function (result) {
|
||||||
|
// Successful login
|
||||||
|
if (result.error) {
|
||||||
|
loginController.set('loggingIn', false);
|
||||||
|
if( result.reason === 'not_activated' ) {
|
||||||
|
loginController.send('showNotActivated', {
|
||||||
|
username: loginController.get('loginName'),
|
||||||
|
sentTo: result.sent_to_email,
|
||||||
|
currentEmail: result.current_email
|
||||||
|
});
|
||||||
|
}
|
||||||
|
loginController.flash(result.error, 'error');
|
||||||
|
} else {
|
||||||
|
// Trigger the browser's password manager using the hidden static login form:
|
||||||
|
var $hidden_login_form = $('#hidden-login-form');
|
||||||
|
$hidden_login_form.find('input[name=username]').val(loginController.get('loginName'));
|
||||||
|
$hidden_login_form.find('input[name=password]').val(loginController.get('loginPassword'));
|
||||||
|
$hidden_login_form.find('input[name=redirect]').val(window.location.href);
|
||||||
|
$hidden_login_form.find('input[name=authenticity_token]').val($('meta[name=csrf-token]').attr('content'));
|
||||||
|
$hidden_login_form.submit();
|
||||||
|
}
|
||||||
|
|
||||||
|
}, function(result) {
|
||||||
|
// Failed to login
|
||||||
|
loginController.flash(Em.String.i18n('login.error'), 'error');
|
||||||
|
loginController.set('loggingIn', false);
|
||||||
|
})
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
authMessage: (function() {
|
||||||
|
if (this.blank('authenticate')) return "";
|
||||||
|
return Em.String.i18n("login." + (this.get('authenticate')) + ".message");
|
||||||
|
}).property('authenticate'),
|
||||||
|
|
||||||
|
twitterLogin: function() {
|
||||||
|
this.set('authenticate', 'twitter');
|
||||||
|
var left = this.get('lastX') - 400;
|
||||||
|
var top = this.get('lastY') - 200;
|
||||||
|
return window.open(Discourse.getURL("/auth/twitter"), "_blank", "menubar=no,status=no,height=400,width=800,left=" + left + ",top=" + top);
|
||||||
|
},
|
||||||
|
|
||||||
|
facebookLogin: function() {
|
||||||
|
this.set('authenticate', 'facebook');
|
||||||
|
var left = this.get('lastX') - 400;
|
||||||
|
var top = this.get('lastY') - 200;
|
||||||
|
return window.open(Discourse.getURL("/auth/facebook"), "_blank", "menubar=no,status=no,height=400,width=800,left=" + left + ",top=" + top);
|
||||||
|
},
|
||||||
|
|
||||||
|
casLogin: function() {
|
||||||
|
var left, top;
|
||||||
|
this.set('authenticate', 'cas');
|
||||||
|
left = this.get('lastX') - 400;
|
||||||
|
top = this.get('lastY') - 200;
|
||||||
|
return window.open("/auth/cas", "_blank", "menubar=no,status=no,height=400,width=800,left=" + left + ",top=" + top);
|
||||||
|
},
|
||||||
|
|
||||||
|
openidLogin: function(provider) {
|
||||||
|
var left = this.get('lastX') - 400;
|
||||||
|
var top = this.get('lastY') - 200;
|
||||||
|
if (provider === "yahoo") {
|
||||||
|
this.set("authenticate", 'yahoo');
|
||||||
|
return window.open(Discourse.getURL("/auth/yahoo"), "_blank", "menubar=no,status=no,height=400,width=800,left=" + left + ",top=" + top);
|
||||||
|
} else {
|
||||||
|
window.open(Discourse.getURL("/auth/google"), "_blank", "menubar=no,status=no,height=500,width=850,left=" + left + ",top=" + top);
|
||||||
|
return this.set("authenticate", 'google');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
githubLogin: function() {
|
||||||
|
this.set('authenticate', 'github');
|
||||||
|
var left = this.get('lastX') - 400;
|
||||||
|
var top = this.get('lastY') - 200;
|
||||||
|
return window.open(Discourse.getURL("/auth/github"), "_blank", "menubar=no,status=no,height=400,width=800,left=" + left + ",top=" + top);
|
||||||
|
},
|
||||||
|
|
||||||
|
personaLogin: function() {
|
||||||
|
navigator.id.request();
|
||||||
|
},
|
||||||
|
|
||||||
|
authenticationComplete: function(options) {
|
||||||
|
if (options.awaiting_approval) {
|
||||||
|
this.flash(Em.String.i18n('login.awaiting_approval'), 'success');
|
||||||
|
this.set('authenticate', null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (options.awaiting_activation) {
|
||||||
|
this.flash(Em.String.i18n('login.awaiting_confirmation'), 'success');
|
||||||
|
this.set('authenticate', null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Reload the page if we're authenticated
|
||||||
|
if (options.authenticated) {
|
||||||
|
window.location.reload();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var createAccountController = this.get('controllers.createAccount');
|
||||||
|
createAccountController.setProperties({
|
||||||
|
accountEmail: options.email,
|
||||||
|
accountUsername: options.username,
|
||||||
|
accountName: options.name,
|
||||||
|
authOptions: Em.Object.create(options)
|
||||||
|
})
|
||||||
|
this.send('showCreateAccount');
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
@ -0,0 +1,56 @@
|
|||||||
|
/**
|
||||||
|
Modal related to auto closing of topics
|
||||||
|
|
||||||
|
@class MergeTopicController
|
||||||
|
@extends Discourse.ObjectController
|
||||||
|
@namespace Discourse
|
||||||
|
@uses Discourse.ModalFunctionality
|
||||||
|
@module Discourse
|
||||||
|
**/
|
||||||
|
Discourse.MergeTopicController = Discourse.ObjectController.extend(Discourse.SelectedPostsCount, Discourse.ModalFunctionality, {
|
||||||
|
needs: ['topic'],
|
||||||
|
|
||||||
|
topicController: Em.computed.alias('controllers.topic'),
|
||||||
|
selectedPosts: Em.computed.alias('topicController.selectedPosts'),
|
||||||
|
allPostsSelected: Em.computed.alias('topicController.allPostsSelected'),
|
||||||
|
|
||||||
|
buttonDisabled: function() {
|
||||||
|
if (this.get('saving')) return true;
|
||||||
|
return this.blank('selectedTopicId');
|
||||||
|
}.property('selectedTopicId', 'saving'),
|
||||||
|
|
||||||
|
buttonTitle: function() {
|
||||||
|
if (this.get('saving')) return Em.String.i18n('saving');
|
||||||
|
return Em.String.i18n('topic.merge_topic.title');
|
||||||
|
}.property('saving'),
|
||||||
|
|
||||||
|
movePostsToExistingTopic: function() {
|
||||||
|
this.set('saving', true);
|
||||||
|
|
||||||
|
var moveSelectedView = this;
|
||||||
|
|
||||||
|
var promise = null;
|
||||||
|
if (this.get('allPostsSelected')) {
|
||||||
|
promise = Discourse.Topic.mergeTopic(this.get('id'), this.get('selectedTopicId'));
|
||||||
|
} else {
|
||||||
|
var postIds = this.get('selectedPosts').map(function(p) { return p.get('id'); });
|
||||||
|
promise = Discourse.Topic.movePosts(this.get('id'), {
|
||||||
|
destination_topic_id: this.get('selectedTopicId'),
|
||||||
|
post_ids: postIds
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
promise.then(function(result) {
|
||||||
|
// Posts moved
|
||||||
|
$('#discourse-modal').modal('hide');
|
||||||
|
moveSelectedView.get('topicController').toggleMultiSelect();
|
||||||
|
Em.run.next(function() { Discourse.URL.routeTo(result.url); });
|
||||||
|
}, function() {
|
||||||
|
// Error moving posts
|
||||||
|
moveSelectedView.flash(Em.String.i18n('topic.merge_topic.error'));
|
||||||
|
moveSelectedView.set('saving', false);
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
@ -7,9 +7,7 @@
|
|||||||
@module Discourse
|
@module Discourse
|
||||||
**/
|
**/
|
||||||
Discourse.ModalController = Discourse.Controller.extend({
|
Discourse.ModalController = Discourse.Controller.extend({
|
||||||
show: function(view) {
|
|
||||||
this.set('currentView', view);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
Modal displayed to a user when they are not active yet.
|
||||||
|
|
||||||
|
@class NotActivatedController
|
||||||
|
@extends Discourse.Controller
|
||||||
|
@namespace Discourse
|
||||||
|
@uses Discourse.ModalFunctionality
|
||||||
|
@module Discourse
|
||||||
|
**/
|
||||||
|
Discourse.NotActivatedController = Discourse.Controller.extend(Discourse.ModalFunctionality, {
|
||||||
|
emailSent: false,
|
||||||
|
|
||||||
|
sendActivationEmail: function() {
|
||||||
|
Discourse.ajax('/users/' + this.get('username') + '/send_activation_email');
|
||||||
|
this.set('emailSent', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
@ -0,0 +1,49 @@
|
|||||||
|
/**
|
||||||
|
Modal related to auto closing of topics
|
||||||
|
|
||||||
|
@class SplitTopicController
|
||||||
|
@extends Discourse.ObjectController
|
||||||
|
@namespace Discourse
|
||||||
|
@uses Discourse.ModalFunctionality
|
||||||
|
@module Discourse
|
||||||
|
**/
|
||||||
|
Discourse.SplitTopicController = Discourse.ObjectController.extend(Discourse.SelectedPostsCount, Discourse.ModalFunctionality, {
|
||||||
|
needs: ['topic'],
|
||||||
|
|
||||||
|
topicController: Em.computed.alias('controllers.topic'),
|
||||||
|
selectedPosts: Em.computed.alias('topicController.selectedPosts'),
|
||||||
|
saving: false,
|
||||||
|
|
||||||
|
buttonDisabled: function() {
|
||||||
|
if (this.get('saving')) return true;
|
||||||
|
return this.blank('topicName');
|
||||||
|
}.property('saving', 'topicName'),
|
||||||
|
|
||||||
|
buttonTitle: function() {
|
||||||
|
if (this.get('saving')) return Em.String.i18n('saving');
|
||||||
|
return Em.String.i18n('topic.split_topic.action');
|
||||||
|
}.property('saving'),
|
||||||
|
|
||||||
|
movePostsToNewTopic: function() {
|
||||||
|
this.set('saving', true);
|
||||||
|
|
||||||
|
var postIds = this.get('selectedPosts').map(function(p) { return p.get('id'); });
|
||||||
|
var moveSelectedView = this;
|
||||||
|
|
||||||
|
Discourse.Topic.movePosts(this.get('id'), {
|
||||||
|
title: this.get('topicName'),
|
||||||
|
post_ids: postIds
|
||||||
|
}).then(function(result) {
|
||||||
|
// Posts moved
|
||||||
|
$('#discourse-modal').modal('hide');
|
||||||
|
moveSelectedView.get('topicController').toggleMultiSelect();
|
||||||
|
Em.run.next(function() { Discourse.URL.routeTo(result.url); });
|
||||||
|
}, function() {
|
||||||
|
// Error moving posts
|
||||||
|
moveSelectedView.flash(Em.String.i18n('topic.split_topic.error'));
|
||||||
|
moveSelectedView.set('saving', false);
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
@ -16,15 +16,6 @@ Discourse.TopicAdminMenuController = Discourse.ObjectController.extend({
|
|||||||
|
|
||||||
hide: function() {
|
hide: function() {
|
||||||
this.set('visible', false);
|
this.set('visible', false);
|
||||||
},
|
|
||||||
|
|
||||||
autoClose: function() {
|
|
||||||
var modalController = this.get('controllers.modal');
|
|
||||||
if (modalController) {
|
|
||||||
var v = Discourse.EditTopicAutoCloseView.create();
|
|
||||||
v.set('topic', this.get('content'));
|
|
||||||
modalController.show(v);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -107,29 +107,6 @@ Discourse.TopicController = Discourse.ObjectController.extend(Discourse.Selected
|
|||||||
this.toggleProperty('summaryCollapsed');
|
this.toggleProperty('summaryCollapsed');
|
||||||
},
|
},
|
||||||
|
|
||||||
splitTopic: function() {
|
|
||||||
var modalController = this.get('controllers.modal');
|
|
||||||
if (!modalController) return;
|
|
||||||
|
|
||||||
modalController.show(Discourse.SplitTopicView.create({
|
|
||||||
topicController: this,
|
|
||||||
topic: this.get('content'),
|
|
||||||
selectedPosts: this.get('selectedPosts')
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
|
|
||||||
mergeTopic: function() {
|
|
||||||
var modalController = this.get('controllers.modal');
|
|
||||||
if (!modalController) return;
|
|
||||||
|
|
||||||
modalController.show(Discourse.MergeTopicView.create({
|
|
||||||
topicController: this,
|
|
||||||
topic: this.get('content'),
|
|
||||||
allPostsSelected: this.get('allPostsSelected'),
|
|
||||||
selectedPosts: this.get('selectedPosts')
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
|
|
||||||
deleteSelected: function() {
|
deleteSelected: function() {
|
||||||
var topicController = this;
|
var topicController = this;
|
||||||
bootbox.confirm(Em.String.i18n("post.delete.confirm", { count: this.get('selectedPostsCount')}), function(result) {
|
bootbox.confirm(Em.String.i18n("post.delete.confirm", { count: this.get('selectedPostsCount')}), function(result) {
|
||||||
@ -432,47 +409,6 @@ Discourse.TopicController = Discourse.ObjectController.extend(Discourse.Selected
|
|||||||
actionType.loadUsers();
|
actionType.loadUsers();
|
||||||
},
|
},
|
||||||
|
|
||||||
showPrivateInviteModal: function() {
|
|
||||||
var modal = Discourse.InvitePrivateModalView.create({
|
|
||||||
topic: this.get('content')
|
|
||||||
});
|
|
||||||
|
|
||||||
var modalController = this.get('controllers.modal');
|
|
||||||
if (modalController) {
|
|
||||||
modalController.show(modal);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
showInviteModal: function() {
|
|
||||||
var modalController = this.get('controllers.modal');
|
|
||||||
if (modalController) {
|
|
||||||
modalController.show(Discourse.InviteModalView.create({
|
|
||||||
topic: this.get('content')
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Clicked the flag button
|
|
||||||
showFlags: function(post) {
|
|
||||||
var modalController = this.get('controllers.modal');
|
|
||||||
if (modalController) {
|
|
||||||
modalController.show(Discourse.FlagView.create({
|
|
||||||
post: post,
|
|
||||||
controller: this
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
showHistory: function(post) {
|
|
||||||
var modalController = this.get('controllers.modal');
|
|
||||||
|
|
||||||
if (modalController) {
|
|
||||||
modalController.show(Discourse.HistoryView.create({
|
|
||||||
originalPost: post
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
recoverPost: function(post) {
|
recoverPost: function(post) {
|
||||||
post.set('deleted_at', null);
|
post.set('deleted_at', null);
|
||||||
post.recover();
|
post.recover();
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
This mixin provides functionality to modal controllers
|
||||||
|
|
||||||
|
@class Discourse.ModalFunctionality
|
||||||
|
@extends Ember.Mixin
|
||||||
|
@namespace Discourse
|
||||||
|
@module Discourse
|
||||||
|
**/
|
||||||
|
Discourse.ModalFunctionality = Em.Mixin.create({
|
||||||
|
needs: ['modal'],
|
||||||
|
|
||||||
|
/**
|
||||||
|
Flash a message at the top of the modal
|
||||||
|
|
||||||
|
@method blank
|
||||||
|
@param {String} name the name of the property we want to check
|
||||||
|
@return {Boolean}
|
||||||
|
**/
|
||||||
|
flash: function(message, messageClass) {
|
||||||
|
this.set('flashMessage', Em.Object.create({
|
||||||
|
message: message,
|
||||||
|
messageClass: messageClass
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
@ -8,10 +8,10 @@
|
|||||||
**/
|
**/
|
||||||
Discourse.Archetype = Discourse.Model.extend({
|
Discourse.Archetype = Discourse.Model.extend({
|
||||||
|
|
||||||
hasOptions: (function() {
|
hasOptions: function() {
|
||||||
if (!this.get('options')) return false;
|
if (!this.get('options')) return false;
|
||||||
return this.get('options').length > 0;
|
return this.get('options').length > 0;
|
||||||
}).property('options.@each'),
|
}.property('options.@each'),
|
||||||
|
|
||||||
isDefault: function() {
|
isDefault: function() {
|
||||||
return this.get('id') === Discourse.Site.instance().get('default_archetype');
|
return this.get('id') === Discourse.Site.instance().get('default_archetype');
|
||||||
|
47
app/assets/javascripts/discourse/routes/application_route.js
Normal file
47
app/assets/javascripts/discourse/routes/application_route.js
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
Application route for Discourse
|
||||||
|
|
||||||
|
@class ApplicationRoute
|
||||||
|
@extends Ember.Route
|
||||||
|
@namespace Discourse
|
||||||
|
@module Discourse
|
||||||
|
**/
|
||||||
|
Discourse.ApplicationRoute = Em.Route.extend({
|
||||||
|
|
||||||
|
events: {
|
||||||
|
showLogin: function() {
|
||||||
|
Discourse.Route.showModal(this, 'login');
|
||||||
|
},
|
||||||
|
|
||||||
|
showCreateAccount: function() {
|
||||||
|
Discourse.Route.showModal(this, 'createAccount');
|
||||||
|
},
|
||||||
|
|
||||||
|
showForgotPassword: function() {
|
||||||
|
Discourse.Route.showModal(this, 'forgotPassword');
|
||||||
|
},
|
||||||
|
|
||||||
|
showNotActivated: function(props) {
|
||||||
|
Discourse.Route.showModal(this, 'notActivated');
|
||||||
|
this.controllerFor('notActivated').setProperties(props);
|
||||||
|
},
|
||||||
|
|
||||||
|
showImageSelector: function(composerView) {
|
||||||
|
Discourse.Route.showModal(this, 'imageSelector');
|
||||||
|
this.controllerFor('imageSelector').setProperties({
|
||||||
|
localSelected: true,
|
||||||
|
composerView: composerView
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
editCategory: function(category) {
|
||||||
|
var router = this;
|
||||||
|
Discourse.Category.findBySlugOrId(category.get('slug')).then(function (c) {
|
||||||
|
Discourse.Route.showModal(router, 'editCategory', c);
|
||||||
|
router.controllerFor('editCategory').set('selectedTab', 'general');
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
@ -27,6 +27,7 @@ Discourse.Route = Em.Route.extend({
|
|||||||
var hideDropDownFunction = $('html').data('hide-dropdown');
|
var hideDropDownFunction = $('html').data('hide-dropdown');
|
||||||
if (hideDropDownFunction) return hideDropDownFunction();
|
if (hideDropDownFunction) return hideDropDownFunction();
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -38,6 +39,23 @@ Discourse.Route.reopenClass({
|
|||||||
if (oldBuilder) oldBuilder.call(this);
|
if (oldBuilder) oldBuilder.call(this);
|
||||||
return builder.call(this);
|
return builder.call(this);
|
||||||
};
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
Shows a modal
|
||||||
|
|
||||||
|
@method showModal
|
||||||
|
**/
|
||||||
|
showModal: function(router, name, model) {
|
||||||
|
router.controllerFor('modal').set('modalClass', null);
|
||||||
|
router.render(name, {into: 'modal', outlet: 'modalBody'});
|
||||||
|
var controller = router.controllerFor(name);
|
||||||
|
if (controller) {
|
||||||
|
if (model) {
|
||||||
|
controller.set('model', model);
|
||||||
|
}
|
||||||
|
controller.set('flashMessage', null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -8,6 +8,15 @@
|
|||||||
**/
|
**/
|
||||||
Discourse.ListCategoriesRoute = Discourse.Route.extend({
|
Discourse.ListCategoriesRoute = Discourse.Route.extend({
|
||||||
|
|
||||||
|
events: {
|
||||||
|
|
||||||
|
createCategory: function() {
|
||||||
|
Discourse.Route.showModal(this, 'editCategory', Discourse.Category.create());
|
||||||
|
this.controllerFor('editCategory').set('selectedTab', 'general');
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
model: function() {
|
model: function() {
|
||||||
var listTopicsController = this.controllerFor('listTopics');
|
var listTopicsController = this.controllerFor('listTopics');
|
||||||
if (listTopicsController) listTopicsController.set('content', null);
|
if (listTopicsController) listTopicsController.set('content', null);
|
||||||
|
@ -8,6 +8,57 @@
|
|||||||
**/
|
**/
|
||||||
Discourse.TopicRoute = Discourse.Route.extend({
|
Discourse.TopicRoute = Discourse.Route.extend({
|
||||||
|
|
||||||
|
events: {
|
||||||
|
// Modals that can pop up within a topic
|
||||||
|
|
||||||
|
showFlags: function(post) {
|
||||||
|
Discourse.Route.showModal(this, 'flag', post);
|
||||||
|
this.controllerFor('flag').setProperties({
|
||||||
|
postActionTypeId: null
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
showAutoClose: function() {
|
||||||
|
Discourse.Route.showModal(this, 'editTopicAutoClose', this.modelFor('topic'));
|
||||||
|
this.controllerFor('modal').set('modalClass', 'edit-auto-close-modal');
|
||||||
|
},
|
||||||
|
|
||||||
|
showInvite: function() {
|
||||||
|
Discourse.Route.showModal(this, 'invite', this.modelFor('topic'));
|
||||||
|
this.controllerFor('invite').setProperties({
|
||||||
|
email: null,
|
||||||
|
error: false,
|
||||||
|
saving: false,
|
||||||
|
finished: false
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
showPrivateInvite: function() {
|
||||||
|
Discourse.Route.showModal(this, 'invitePrivate', this.modelFor('topic'))
|
||||||
|
this.controllerFor('invitePrivate').setProperties({
|
||||||
|
email: null,
|
||||||
|
error: false,
|
||||||
|
saving: false,
|
||||||
|
finished: false
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
showHistory: function(post) {
|
||||||
|
Discourse.Route.showModal(this, 'history', post);
|
||||||
|
this.controllerFor('history').refresh();
|
||||||
|
this.controllerFor('modal').set('modalClass', 'history-modal')
|
||||||
|
},
|
||||||
|
|
||||||
|
mergeTopic: function() {
|
||||||
|
Discourse.Route.showModal(this, 'mergeTopic', this.modelFor('topic'));
|
||||||
|
},
|
||||||
|
|
||||||
|
splitTopic: function() {
|
||||||
|
Discourse.Route.showModal(this, 'splitTopic', this.modelFor('topic'));
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
model: function(params) {
|
model: function(params) {
|
||||||
var currentModel, _ref;
|
var currentModel, _ref;
|
||||||
if (currentModel = (_ref = this.controllerFor('topic')) ? _ref.get('content') : void 0) {
|
if (currentModel = (_ref = this.controllerFor('topic')) ? _ref.get('content') : void 0) {
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
<a href='#' {{action deleteCategory target="view"}} class='btn btn-small'>{{i18n category.delete}}</a>
|
<a href='#' {{action deleteCategory target="view"}} class='btn btn-small'>{{i18n category.delete}}</a>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if view.can_edit}}
|
{{#if view.can_edit}}
|
||||||
<a href='#' {{action editCategory target="view"}} class='btn btn-small'>{{i18n category.edit}}</a>
|
<a href='#' {{action editCategory view}} class='btn btn-small'>{{i18n category.edit}}</a>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<a href="/category/{{unbound view.slug}}" class='btn btn-small'>{{i18n category.view}}</a>
|
<a href="/category/{{unbound view.slug}}" class='btn btn-small'>{{i18n category.view}}</a>
|
||||||
</footer>
|
</footer>
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
<div class="modal-body flag-modal">
|
|
||||||
{{#if view.post.flagsAvailable}}
|
|
||||||
<form>
|
|
||||||
{{#each view.boundFlags}}
|
|
||||||
<div class='controls'>
|
|
||||||
<label class='radio'>
|
|
||||||
<input type='radio' id="radio_{{unbound name_key}}" {{action changePostActionType this target="view"}} name='post_action_type_index'> <strong>{{formattedName}}</strong>
|
|
||||||
{{#if is_custom_flag}}
|
|
||||||
{{#unless selected}}
|
|
||||||
<div class='description'>{{{description}}}</div>
|
|
||||||
{{/unless}}
|
|
||||||
{{else}}
|
|
||||||
{{#if description}}
|
|
||||||
<div class='description'>{{{description}}}</div>
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
|
||||||
</label>
|
|
||||||
{{#if is_custom_flag}}
|
|
||||||
{{#if selected}}
|
|
||||||
{{textarea name="message" class="flag-message" placeholder=customPlaceholder value=message}}
|
|
||||||
<div {{bindAttr class="customMessageLengthClasses"}}>{{customMessageLength}}</div>
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
{{/each}}
|
|
||||||
</form>
|
|
||||||
{{else}}
|
|
||||||
{{i18n flagging.cant}}
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{#if view.showSubmit}}
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button class='btn btn-primary' {{action createFlag target="view"}}>{{view.submitText}}</button>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
@ -1,13 +1,13 @@
|
|||||||
<ul class="nav nav-pills image-options">
|
<ul class="nav nav-pills image-options">
|
||||||
<li title="{{i18n image_selector.local_title}}" {{bindAttr class="view.localSelected:active"}}>
|
<li title="{{i18n image_selector.local_title}}" {{bindAttr class="localSelected:active"}}>
|
||||||
<a href="#" {{action selectLocal target="view"}}>{{i18n image_selector.from_my_computer}}</a>
|
<a href="#" {{action selectLocal}}>{{i18n image_selector.from_my_computer}}</a>
|
||||||
</li>
|
</li>
|
||||||
<li title="{{i18n image_selector.remote_title}}" {{bindAttr class="view.remoteSelected:active"}}>
|
<li title="{{i18n image_selector.remote_title}}" {{bindAttr class="remoteSelected:active"}}>
|
||||||
<a href="#" {{action selectRemote target="view"}}>{{i18n image_selector.from_the_web}}</a>
|
<a href="#" {{action selectRemote}}>{{i18n image_selector.from_the_web}}</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
{{#if view.localSelected}}
|
{{#if localSelected}}
|
||||||
<div class='modal-body'>
|
<div class='modal-body'>
|
||||||
<form>
|
<form>
|
||||||
<input type="file" name="file" id="filename-input" value="browse" accept="image/*"><br>
|
<input type="file" name="file" id="filename-input" value="browse" accept="image/*"><br>
|
||||||
@ -15,7 +15,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class='modal-footer'>
|
<div class='modal-footer'>
|
||||||
<button class='btn btn-large btn-primary' {{action "upload" target="view"}}>
|
<button class='btn btn-large btn-primary' {{action upload target="view"}}>
|
||||||
<span class='add-picture'><i class='icon-picture'></i><i class='icon-plus'></i></span>
|
<span class='add-picture'><i class='icon-picture'></i><i class='icon-plus'></i></span>
|
||||||
{{i18n image_selector.upload}}
|
{{i18n image_selector.upload}}
|
||||||
</button>
|
</button>
|
||||||
@ -28,7 +28,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class='modal-footer'>
|
<div class='modal-footer'>
|
||||||
<button class='btn btn-large btn-primary' {{action "add" target="view"}}>
|
<button class='btn btn-large btn-primary' {{action add target="view"}}>
|
||||||
<span class='add-picture'><i class='icon-picture'></i><i class='icon-plus'></i></span>
|
<span class='add-picture'><i class='icon-picture'></i><i class='icon-plus'></i></span>
|
||||||
{{i18n image_selector.add_image}}
|
{{i18n image_selector.add_image}}
|
||||||
</button>
|
</button>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if canEditCategory}}
|
{{#if canEditCategory}}
|
||||||
<button class='btn btn-default' {{action editCategory}}>{{i18n category.edit_long}}</button>
|
<button class='btn btn-default' {{action editCategory category}}>{{i18n category.edit_long}}</button>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if canCreateCategory}}
|
{{#if canCreateCategory}}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form>
|
<form>
|
||||||
{{autoCloseForm autoCloseDays=view.auto_close_days}}
|
{{autoCloseForm autoCloseDays=auto_close_days}}
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button class='btn btn-primary' {{action saveAutoClose target="view"}} data-dismiss="modal">{{i18n topic.auto_close_save}}</button>
|
<button class='btn btn-primary' {{action saveAutoClose}} data-dismiss="modal">{{i18n topic.auto_close_save}}</button>
|
||||||
<button class='btn' data-dismiss="modal">{{i18n topic.auto_close_cancel}}</button>
|
<button class='btn' data-dismiss="modal">{{i18n topic.auto_close_cancel}}</button>
|
||||||
<button class='btn pull-right' {{action removeAutoClose target="view"}} data-dismiss="modal">{{i18n topic.auto_close_remove}}</button>
|
<button class='btn pull-right' {{action removeAutoClose}} data-dismiss="modal">{{i18n topic.auto_close_remove}}</button>
|
||||||
</div>
|
</div>
|
@ -1,4 +1,4 @@
|
|||||||
{{#unless view.complete}}
|
{{#unless complete}}
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div>
|
<div>
|
||||||
<form>
|
<form>
|
||||||
@ -6,7 +6,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td style="width:80px"><label for='new-account-name'>{{i18n user.name.title}}</label></td>
|
<td style="width:80px"><label for='new-account-name'>{{i18n user.name.title}}</label></td>
|
||||||
<td style="width:496px">
|
<td style="width:496px">
|
||||||
{{textField value=view.accountName id="new-account-name" autofocus="autofocus"}}
|
{{textField value=accountName id="new-account-name" autofocus="autofocus"}}
|
||||||
{{inputTip validation=nameValidation}}
|
{{inputTip validation=nameValidation}}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -18,8 +18,8 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td><label for='new-account-email'>{{i18n user.email.title}}</label></td>
|
<td><label for='new-account-email'>{{i18n user.email.title}}</label></td>
|
||||||
<td>
|
<td>
|
||||||
{{input value=view.accountEmail id="new-account-email"}}
|
{{input value=accountEmail id="new-account-email"}}
|
||||||
{{inputTip validation=view.emailValidation}}
|
{{inputTip validation=emailValidation}}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@ -30,8 +30,8 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td><label for='new-account-username'>{{i18n user.username.title}}</label></td>
|
<td><label for='new-account-username'>{{i18n user.username.title}}</label></td>
|
||||||
<td>
|
<td>
|
||||||
{{input value=view.accountUsername id="new-account-username" maxlength="15"}}
|
{{input value=accountUsername id="new-account-username" maxlength="15"}}
|
||||||
{{inputTip validation=view.usernameValidation}}
|
{{inputTip validation=usernameValidation}}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@ -39,12 +39,12 @@
|
|||||||
<td><label>{{i18n user.username.instructions}}</label></td>
|
<td><label>{{i18n user.username.instructions}}</label></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
{{#if view.passwordRequired}}
|
{{#if passwordRequired}}
|
||||||
<tr>
|
<tr>
|
||||||
<td><label for='new-account-password'>{{i18n user.password.title}}</label></td>
|
<td><label for='new-account-password'>{{i18n user.password.title}}</label></td>
|
||||||
<td>
|
<td>
|
||||||
{{input type="password" value=view.accountPassword id="new-account-password"}}
|
{{input type="password" value=accountPassword id="new-account-password"}}
|
||||||
{{inputTip validation=view.passwordValidation}}
|
{{inputTip validation=passwordValidation}}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
@ -52,8 +52,8 @@
|
|||||||
<tr class="password-confirmation">
|
<tr class="password-confirmation">
|
||||||
<td><label for='new-account-password-confirmation'>{{i18n user.password_confirmation.title}}</label></td>
|
<td><label for='new-account-password-confirmation'>{{i18n user.password_confirmation.title}}</label></td>
|
||||||
<td>
|
<td>
|
||||||
{{input type="password" value=view.accountPasswordConfirm id="new-account-confirmation"}}
|
{{input type="password" value=accountPasswordConfirm id="new-account-confirmation"}}
|
||||||
{{input value=view.accountChallenge id="new-account-challenge"}}
|
{{input value=accountChallenge id="new-account-challenge"}}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
@ -63,6 +63,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button class='btn btn-large btn-primary' {{bindAttr disabled="view.submitDisabled"}} {{action createAccount target="view"}}>{{i18n create_account.title}}</button>
|
<button class='btn btn-large btn-primary' {{bindAttr disabled="submitDisabled"}} {{action createAccount}}>{{i18n create_account.title}}</button>
|
||||||
</div>
|
</div>
|
||||||
{{/unless}}
|
{{/unless}}
|
@ -1,22 +1,20 @@
|
|||||||
{{#with view.category}}
|
<div {{bindAttr class="loading:invisible"}}>
|
||||||
|
|
||||||
<div {{bindAttr class="view.loading:invisible"}}>
|
|
||||||
<ul class="nav nav-pills">
|
<ul class="nav nav-pills">
|
||||||
<li {{bindAttr class="view.generalSelected:active"}}>
|
<li {{bindAttr class="generalSelected:active"}}>
|
||||||
<a href="#" {{action selectGeneral target="view"}}>{{i18n category.general}}</a>
|
<a href="#" {{action selectGeneral}}>{{i18n category.general}}</a>
|
||||||
</li>
|
</li>
|
||||||
{{#unless isUncategorized}}
|
{{#unless isUncategorized}}
|
||||||
<li {{bindAttr class="view.securitySelected:active"}}>
|
<li {{bindAttr class="securitySelected:active"}}>
|
||||||
<a href="#" {{action selectSecurity target="view"}}>{{i18n category.security}}</a>
|
<a href="#" {{action selectSecurity}}>{{i18n category.security}}</a>
|
||||||
</li>
|
</li>
|
||||||
<li {{bindAttr class="view.settingsSelected:active"}}>
|
<li {{bindAttr class="settingsSelected:active"}}>
|
||||||
<a href="#" {{action selectSettings target="view"}}>{{i18n category.settings}}</a>
|
<a href="#" {{action selectSettings}}>{{i18n category.settings}}</a>
|
||||||
</li>
|
</li>
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div {{bindAttr class=":modal-tab :general-tab view.generalSelected::invisible"}}>
|
<div {{bindAttr class=":modal-tab :general-tab generalSelected::invisible"}}>
|
||||||
<form>
|
<form>
|
||||||
<section class='field'>
|
<section class='field'>
|
||||||
<label>{{i18n category.name}}</label>
|
<label>{{i18n category.name}}</label>
|
||||||
@ -34,7 +32,7 @@
|
|||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if topic_url}}
|
{{#if topic_url}}
|
||||||
<br/>
|
<br/>
|
||||||
<button class="btn btn-small" {{action showCategoryTopic target="view"}}>{{i18n category.change_in_category_topic}}</button>
|
<button class="btn btn-small" {{action showCategoryTopic}}>{{i18n category.change_in_category_topic}}</button>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</section>
|
</section>
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
@ -42,25 +40,25 @@
|
|||||||
<section class='field'>
|
<section class='field'>
|
||||||
<label>{{i18n category.badge_colors}}</label>
|
<label>{{i18n category.badge_colors}}</label>
|
||||||
<div class="category-color-editor">
|
<div class="category-color-editor">
|
||||||
<span class='badge-category' {{bindAttr style="view.colorStyle"}}>{{view.categoryName}}</span>
|
<span class='badge-category' {{bindAttr style="colorStyle"}}>{{categoryName}}</span>
|
||||||
|
|
||||||
<div class='input-prepend input-append' style="margin-top: 10px;">
|
<div class='input-prepend input-append' style="margin-top: 10px;">
|
||||||
<span class='color-title'>{{i18n category.background_color}}:</span>
|
<span class='color-title'>{{i18n category.background_color}}:</span>
|
||||||
<span class='add-on'>#</span>{{textField value=color placeholderKey="category.color_placeholder" maxlength="6"}}
|
<span class='add-on'>#</span>{{textField value=color placeholderKey="category.color_placeholder" maxlength="6"}}
|
||||||
{{colorPicker colors=view.backgroundColors usedColors=view.usedBackgroundColors value=color}}
|
{{colorPicker colors=backgroundColors usedColors=usedBackgroundColors value=color}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class='input-prepend input-append'>
|
<div class='input-prepend input-append'>
|
||||||
<span class='color-title'>{{i18n category.foreground_color}}:</span>
|
<span class='color-title'>{{i18n category.foreground_color}}:</span>
|
||||||
<span class='add-on'>#</span>{{textField value=text_color placeholderKey="category.color_placeholder" maxlength="6"}}
|
<span class='add-on'>#</span>{{textField value=text_color placeholderKey="category.color_placeholder" maxlength="6"}}
|
||||||
{{colorPicker colors=view.foregroundColors value=text_color}}
|
{{colorPicker colors=foregroundColors value=text_color}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{{#unless isUncategorized}}
|
{{#unless isUncategorized}}
|
||||||
<div {{bindAttr class=":modal-tab :options-tab view.securitySelected::invisible"}}>
|
<div {{bindAttr class=":modal-tab :options-tab securitySelected::invisible"}}>
|
||||||
<section class='field'>
|
<section class='field'>
|
||||||
<label>
|
<label>
|
||||||
{{input type="checkbox" checked=secure}}
|
{{input type="checkbox" checked=secure}}
|
||||||
@ -73,17 +71,17 @@
|
|||||||
{{#each groups}}
|
{{#each groups}}
|
||||||
<li class="badge-group">
|
<li class="badge-group">
|
||||||
{{this}}
|
{{this}}
|
||||||
<a {{action removeGroup this target="view"}}><i class="icon icon-remove-sign"></i></a>
|
<a {{action removeGroup this}}><i class="icon icon-remove-sign"></i></a>
|
||||||
</li>
|
</li>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</ul>
|
</ul>
|
||||||
{{view Ember.Select contentBinding="availableGroups" valueBinding="view.selectedGroup"}}
|
{{view Ember.Select contentBinding="availableGroups" valueBinding="selectedGroup"}}
|
||||||
<button {{action addGroup target="view"}} class="btn btn-small">{{i18n category.add_group}}</button>
|
<button {{action addGroup}} class="btn btn-small">{{i18n category.add_group}}</button>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<div {{bindAttr class=":modal-tab :options-tab view.settingsSelected::invisible"}}>
|
<div {{bindAttr class=":modal-tab :options-tab settingsSelected::invisible"}}>
|
||||||
<section class='field'>
|
<section class='field'>
|
||||||
{{autoCloseForm autoCloseDays=auto_close_days labelKey="category.auto_close_label"}}
|
{{autoCloseForm autoCloseDays=auto_close_days labelKey="category.auto_close_label"}}
|
||||||
</section>
|
</section>
|
||||||
@ -96,11 +94,9 @@
|
|||||||
{{/unless}}
|
{{/unless}}
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button class='btn btn-primary' {{bindAttr disabled="view.disabled"}} {{action saveCategory target="view"}}>{{view.buttonTitle}}</button>
|
<button class='btn btn-primary' {{bindAttr disabled="disabled"}} {{action saveCategory}}>{{buttonTitle}}</button>
|
||||||
{{#if view.deleteVisible}}
|
{{#if deleteVisible}}
|
||||||
<button class='btn btn-danger pull-right' {{bindAttr disabled="view.deleteDisabled"}} {{action deleteCategory target="view"}}><i class="icon icon-trash"></i>{{view.deleteButtonTitle}}</button>
|
<button class='btn btn-danger pull-right' {{bindAttr disabled="deleteDisabled"}} {{action deleteCategory}}><i class="icon icon-trash"></i>{{deleteButtonTitle}}</button>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{/with}}
|
|
||||||
|
@ -0,0 +1,36 @@
|
|||||||
|
<div class="modal-body flag-modal">
|
||||||
|
{{#if flagsAvailable}}
|
||||||
|
<form>
|
||||||
|
{{#each boundFlags}}
|
||||||
|
<div class='controls'>
|
||||||
|
<label class='radio'>
|
||||||
|
<input type='radio' id="radio_{{unbound name_key}}" {{action changePostActionType this}} name='post_action_type_index'> <strong>{{formattedName}}</strong>
|
||||||
|
{{#if is_custom_flag}}
|
||||||
|
{{#unless selected}}
|
||||||
|
<div class='description'>{{{description}}}</div>
|
||||||
|
{{/unless}}
|
||||||
|
{{else}}
|
||||||
|
{{#if description}}
|
||||||
|
<div class='description'>{{{description}}}</div>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
</label>
|
||||||
|
{{#if is_custom_flag}}
|
||||||
|
{{#if selected}}
|
||||||
|
{{textarea name="message" class="flag-message" placeholder=customPlaceholder value=message}}
|
||||||
|
<div {{bindAttr class="customMessageLengthClasses"}}>{{customMessageLength}}</div>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
</form>
|
||||||
|
{{else}}
|
||||||
|
{{i18n flagging.cant}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#if showSubmit}}
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button class='btn btn-primary' {{action createFlag}}>{{submitText}}</button>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
@ -1,9 +1,9 @@
|
|||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form>
|
<form>
|
||||||
<label for='username-or-email'>{{i18n forgot_password.invite}}</label>
|
<label for='username-or-email'>{{i18n forgot_password.invite}}</label>
|
||||||
{{textField value=view.accountEmailOrUsername placeholderKey="login.email_placeholder" id="username-or-email" autocorrect="off" autocapitalize="off"}}
|
{{textField value=accountEmailOrUsername placeholderKey="login.email_placeholder" id="username-or-email" autocorrect="off" autocapitalize="off"}}
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button class='btn btn-large btn-primary' {{bindAttr disabled="view.submitDisabled"}} {{action submit target="view"}}>{{i18n forgot_password.reset}}</button>
|
<button class='btn btn-large btn-primary' {{bindAttr disabled="submitDisabled"}} {{action submit}}>{{i18n forgot_password.reset}}</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
|
|
||||||
{{#if view.loading}}
|
{{#if loading}}
|
||||||
{{i18n loading}}
|
{{i18n loading}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{#if view.versions}}
|
{{#if versions}}
|
||||||
<div class='span8'>
|
<div class='span8'>
|
||||||
|
|
||||||
{{view Ember.Select
|
{{view Ember.Select
|
||||||
contentBinding="view.versions"
|
contentBinding="versions"
|
||||||
optionLabelPath="content.description"
|
optionLabelPath="content.description"
|
||||||
optionValuePath="content.number"
|
optionValuePath="content.number"
|
||||||
selectionBinding="view.versionLeft"}}
|
selectionBinding="versionLeft"}}
|
||||||
|
|
||||||
<div class='contents'>
|
<div class='contents'>
|
||||||
{{#if view.postLeft}}
|
{{#if postLeft}}
|
||||||
{{{view.postLeft.cooked}}}
|
{{{postLeft.cooked}}}
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class='history-loading'>{{i18n loading}}</div>
|
<div class='history-loading'>{{i18n loading}}</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
@ -24,14 +24,14 @@
|
|||||||
|
|
||||||
<div class='span8 offset1'>
|
<div class='span8 offset1'>
|
||||||
{{view Ember.Select
|
{{view Ember.Select
|
||||||
contentBinding="view.versions"
|
contentBinding="versions"
|
||||||
optionLabelPath="content.description"
|
optionLabelPath="content.description"
|
||||||
optionValuePath="content.number"
|
optionValuePath="content.number"
|
||||||
selectionBinding="view.versionRight"}}
|
selectionBinding="versionRight"}}
|
||||||
|
|
||||||
<div class='contents'>
|
<div class='contents'>
|
||||||
{{#if view.diff}}
|
{{#if diff}}
|
||||||
{{{view.diff}}}
|
{{{diff}}}
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class='history-loading'>{{i18n loading}}</div>
|
<div class='history-loading'>{{i18n loading}}</div>
|
||||||
{{/if}}
|
{{/if}}
|
@ -0,0 +1,36 @@
|
|||||||
|
<ul class="nav nav-pills image-options">
|
||||||
|
<li title="{{i18n image_selector.local_title}}" {{bindAttr class="localSelected:active"}}>
|
||||||
|
<a href="#" {{action selectLocal}}>{{i18n image_selector.from_my_computer}}</a>
|
||||||
|
</li>
|
||||||
|
<li title="{{i18n image_selector.remote_title}}" {{bindAttr class="remoteSelected:active"}}>
|
||||||
|
<a href="#" {{action selectRemote}}>{{i18n image_selector.from_the_web}}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{{#if localSelected}}
|
||||||
|
<div class='modal-body'>
|
||||||
|
<form>
|
||||||
|
<input type="file" name="file" id="filename-input" value="browse" accept="image/*"><br>
|
||||||
|
<span class='description'>{{i18n image_selector.local_tip}}</span> <br>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class='modal-footer'>
|
||||||
|
<button class='btn btn-large btn-primary' {{action upload target="view"}}>
|
||||||
|
<span class='add-picture'><i class='icon-picture'></i><i class='icon-plus'></i></span>
|
||||||
|
{{i18n image_selector.upload}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{{else}}
|
||||||
|
<div class='modal-body'>
|
||||||
|
<form>
|
||||||
|
<input type="text" name="text" id="fileurl-input" autofocus><br>
|
||||||
|
<span class='description'>{{i18n image_selector.remote_tip}}</span> <br>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class='modal-footer'>
|
||||||
|
<button class='btn btn-large btn-primary' {{action add target="view"}}>
|
||||||
|
<span class='add-picture'><i class='icon-picture'></i><i class='icon-plus'></i></span>
|
||||||
|
{{i18n image_selector.add_image}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
@ -1,25 +1,25 @@
|
|||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
{{#if view.error}}
|
{{#if error}}
|
||||||
<div class="alert alert-error">
|
<div class="alert alert-error">
|
||||||
<button class="close" data-dismiss="alert">×</button>
|
<button class="close" data-dismiss="alert">×</button>
|
||||||
{{i18n topic.invite_reply.error}}
|
{{i18n topic.invite_reply.error}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if view.finished}}
|
{{#if finished}}
|
||||||
{{{view.successMessage}}}
|
{{{successMessage}}}
|
||||||
{{else}}
|
{{else}}
|
||||||
<form>
|
<form>
|
||||||
<label>{{i18n topic.invite_reply.email}}</label>
|
<label>{{i18n topic.invite_reply.email}}</label>
|
||||||
{{textField value=view.email placeholderKey="topic.invite_reply.email_placeholder"}}
|
{{textField value=email placeholderKey="topic.invite_reply.email_placeholder"}}
|
||||||
</form>
|
</form>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
{{#if view.finished}}
|
{{#if finished}}
|
||||||
<button class='btn btn-primary' data-dismiss="modal">{{i18n close}}</button>
|
<button class='btn btn-primary' data-dismiss="modal">{{i18n close}}</button>
|
||||||
{{else}}
|
{{else}}
|
||||||
<button class='btn btn-primary' {{bindAttr disabled="view.disabled"}} {{action createInvite target="view"}}>{{view.buttonTitle}}</button>
|
<button class='btn btn-primary' {{bindAttr disabled="disabled"}} {{action createInvite}}>{{buttonTitle}}</button>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
</div>
|
</div>
|
@ -1,25 +1,25 @@
|
|||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
{{#if view.error}}
|
{{#if error}}
|
||||||
<div class="alert alert-error">
|
<div class="alert alert-error">
|
||||||
<button class="close" data-dismiss="alert">×</button>
|
<button class="close" data-dismiss="alert">×</button>
|
||||||
{{i18n topic.invite_private.error}}
|
{{i18n topic.invite_private.error}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if view.finished}}
|
{{#if finished}}
|
||||||
{{i18n topic.invite_private.success}}
|
{{i18n topic.invite_private.success}}
|
||||||
{{else}}
|
{{else}}
|
||||||
<form>
|
<form>
|
||||||
<label>{{i18n topic.invite_private.email_or_username}}</label>
|
<label>{{i18n topic.invite_private.email_or_username}}</label>
|
||||||
{{textField value=view.emailOrUsername placeholderKey="topic.invite_private.email_or_username_placeholder"}}
|
{{textField value=emailOrUsername placeholderKey="topic.invite_private.email_or_username_placeholder"}}
|
||||||
</form>
|
</form>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
{{#if view.finished}}
|
{{#if finished}}
|
||||||
<button class='btn btn-primary' data-dismiss="modal">{{i18n close}}</button>
|
<button class='btn btn-primary' data-dismiss="modal">{{i18n close}}</button>
|
||||||
{{else}}
|
{{else}}
|
||||||
<button class='btn btn-primary' {{bindAttr disabled="view.disabled"}} {{action invite target="view"}}>{{view.buttonTitle}}</button>
|
<button class='btn btn-primary' {{bindAttr disabled="disabled"}} {{action invite}}>{{buttonTitle}}</button>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
</div>
|
</div>
|
@ -1,31 +1,31 @@
|
|||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
{{#if view.hasAtLeastOneLoginButton}}
|
{{#if hasAtLeastOneLoginButton}}
|
||||||
<div id="login-buttons">
|
<div id="login-buttons">
|
||||||
{{#if Discourse.SiteSettings.enable_google_logins}}
|
{{#if Discourse.SiteSettings.enable_google_logins}}
|
||||||
<button class="btn btn-social google" title="{{i18n login.google.title}}" {{action openidLogin "google" target="view"}}>{{i18n login.google.title}}</button>
|
<button class="btn btn-social google" title="{{i18n login.google.title}}" {{action openidLogin "google"}}>{{i18n login.google.title}}</button>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if Discourse.SiteSettings.enable_facebook_logins}}
|
{{#if Discourse.SiteSettings.enable_facebook_logins}}
|
||||||
<button class="btn btn-social facebook" title="{{i18n login.facebook.title}}" {{action "facebookLogin" target="view"}}>{{i18n login.facebook.title}}</button>
|
<button class="btn btn-social facebook" title="{{i18n login.facebook.title}}" {{action "facebookLogin"}}>{{i18n login.facebook.title}}</button>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if Discourse.SiteSettings.enable_cas_logins}}
|
{{#if Discourse.SiteSettings.enable_cas_logins}}
|
||||||
<button class="btn btn-social cas" title="{{i18n login.cas.title}}" {{action "casLogin" target="view"}}>{{i18n login.cas.title}}</button>
|
<button class="btn btn-social cas" title="{{i18n login.cas.title}}" {{action "casLogin"}}>{{i18n login.cas.title}}</button>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if Discourse.SiteSettings.enable_twitter_logins}}
|
{{#if Discourse.SiteSettings.enable_twitter_logins}}
|
||||||
<button class="btn btn-social twitter" title="{{i18n login.twitter.title}}" {{action "twitterLogin" target="view"}}>{{i18n login.twitter.title}}</button>
|
<button class="btn btn-social twitter" title="{{i18n login.twitter.title}}" {{action "twitterLogin"}}>{{i18n login.twitter.title}}</button>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if Discourse.SiteSettings.enable_yahoo_logins}}
|
{{#if Discourse.SiteSettings.enable_yahoo_logins}}
|
||||||
<button class="btn btn-social yahoo" title="{{i18n login.yahoo.title}}" {{action openidLogin "yahoo" target="view"}}>{{i18n login.yahoo.title}}</button>
|
<button class="btn btn-social yahoo" title="{{i18n login.yahoo.title}}" {{action openidLogin "yahoo"}}>{{i18n login.yahoo.title}}</button>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if Discourse.SiteSettings.enable_github_logins}}
|
{{#if Discourse.SiteSettings.enable_github_logins}}
|
||||||
<button class="btn btn-social github" title="{{i18n login.github.title}}" {{action "githubLogin" target="view"}}>{{i18n login.github.title}}</button>
|
<button class="btn btn-social github" title="{{i18n login.github.title}}" {{action "githubLogin"}}>{{i18n login.github.title}}</button>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if Discourse.SiteSettings.enable_persona_logins}}
|
{{#if Discourse.SiteSettings.enable_persona_logins}}
|
||||||
<button class="btn btn-social persona" title="{{i18n login.persona.title}}" {{action "personaLogin" target="view"}}>{{i18n login.persona.title}}</button>
|
<button class="btn btn-social persona" title="{{i18n login.persona.title}}" {{action "personaLogin"}}>{{i18n login.persona.title}}</button>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if Discourse.SiteSettings.enable_local_logins}}
|
{{#if Discourse.SiteSettings.enable_local_logins}}
|
||||||
{{#if view.hasAtLeastOneLoginButton}}
|
{{#if hasAtLeastOneLoginButton}}
|
||||||
<h3 style="text-align:center; margin-bottom:10px;">{{i18n login.or}}</h3>
|
<h3 style="text-align:center; margin-bottom:10px;">{{i18n login.or}}</h3>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<form id='login-form'>
|
<form id='login-form'>
|
||||||
@ -36,31 +36,31 @@
|
|||||||
<label for='login-account-name'>{{i18n login.username}} </label>
|
<label for='login-account-name'>{{i18n login.username}} </label>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{textField value=view.loginName placeholderKey="login.email_placeholder" id="login-account-name" autocorrect="off" autocapitalize="off" autofocus="autofocus"}}
|
{{textField value=loginName placeholderKey="login.email_placeholder" id="login-account-name" autocorrect="off" autocapitalize="off" autofocus="autofocus"}}
|
||||||
</td>
|
</td>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<label for='login-account-password'>{{i18n login.password}} </label>
|
<label for='login-account-password'>{{i18n login.password}} </label>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{textField value=view.loginPassword type="password" id="login-account-password"}}
|
{{textField value=loginPassword type="password" id="login-account-password"}}
|
||||||
<a id="forgot-password-link" {{action forgotPassword target="view"}}>{{i18n forgot_password.action}}</a>
|
<a id="forgot-password-link" {{action showForgotPassword}}>{{i18n forgot_password.action}}</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{view.authMessage}}
|
{{authMessage}}
|
||||||
<div id='login-alert' {{bindAttr class="view.alertClass"}}>{{view.alert}}</div>
|
<div id='login-alert' {{bindAttr class="alertClass"}}>{{alert}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
{{#if view.authenticate}}
|
{{#if authenticate}}
|
||||||
{{i18n login.authenticating}}
|
{{i18n login.authenticating}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if Discourse.SiteSettings.enable_local_logins}}
|
{{#if Discourse.SiteSettings.enable_local_logins}}
|
||||||
<button class='btn btn-large btn-primary' {{bindAttr disabled="view.loginDisabled"}} {{action login target="view"}}><i class="icon-unlock"></i> {{view.loginButtonText}}</button>
|
<button class='btn btn-large btn-primary' {{bindAttr disabled="loginDisabled"}} {{action login}}><i class="icon-unlock"></i> {{loginButtonText}}</button>
|
||||||
|
|
||||||
{{i18n create_account.invite}} <a id="new-account-link" {{action newAccount target="view"}}>{{i18n create_account.action}}</a>
|
{{i18n create_account.invite}} <a id="new-account-link" {{action showCreateAccount}}>{{i18n create_account.action}}</a>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
<div id='move-selected' class="modal-body">
|
<div id='move-selected' class="modal-body">
|
||||||
{{#if view.error}}
|
{{#if error}}
|
||||||
<div class="alert alert-error">
|
<div class="alert alert-error">
|
||||||
<button class="close" data-dismiss="alert">×</button>
|
<button class="close" data-dismiss="alert">×</button>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<p>{{{i18n topic.merge_topic.instructions count="view.selectedPostsCount"}}}</p>
|
<p>{{{i18n topic.merge_topic.instructions count="selectedPostsCount"}}}</p>
|
||||||
|
|
||||||
{{view Discourse.ChooseTopicView selectedTopicIdBinding="view.selectedTopicId"}}
|
{{chooseTopic selectedTopicId=selectedTopicId}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button class='btn btn-primary' {{bindAttr disabled="view.buttonDisabled"}} {{action movePostsToExistingTopic target="view"}}>{{view.buttonTitle}}</button>
|
<button class='btn btn-primary' {{bindAttr disabled="buttonDisabled"}} {{action movePostsToExistingTopic}}>{{buttonTitle}}</button>
|
||||||
</div>
|
</div>
|
@ -0,0 +1,16 @@
|
|||||||
|
<div class="modal-header">
|
||||||
|
<a class="close" data-dismiss="modal"><i class='icon-remove icon'></i></a>
|
||||||
|
<h3>{{title}}</h3>
|
||||||
|
</div>
|
||||||
|
<div id='modal-alert'></div>
|
||||||
|
|
||||||
|
{{outlet modalBody}}
|
||||||
|
|
||||||
|
{{#each errors}}
|
||||||
|
<div class="alert alert-error">
|
||||||
|
<button class="close" data-dismiss="alert">×</button>
|
||||||
|
{{this}}
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
|||||||
{{#if view.errors}}
|
|
||||||
{{#each view.errors}}
|
|
||||||
<div class="alert alert-error">
|
|
||||||
<button class="close" data-dismiss="alert">×</button>
|
|
||||||
{{this}}
|
|
||||||
</div>
|
|
||||||
{{/each}}
|
|
||||||
{{/if}}
|
|
@ -1,5 +0,0 @@
|
|||||||
<div class="modal-header">
|
|
||||||
<a class="close" data-dismiss="modal"><i class='icon-remove icon'></i></a>
|
|
||||||
<h3>{{view.title}}</h3>
|
|
||||||
</div>
|
|
||||||
<div id='modal-alert'></div>
|
|
@ -1,9 +1,9 @@
|
|||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
{{#if view.emailSent}}
|
{{#if emailSent}}
|
||||||
{{{i18n login.sent_activation_email_again currentEmail="view.currentEmail"}}}
|
{{{i18n login.sent_activation_email_again currentEmail="currentEmail"}}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{{i18n login.not_activated sentTo="view.sentTo"}}}
|
{{{i18n login.not_activated sentTo="sentTo"}}}
|
||||||
<a href="#" {{action "sendActivationEmail" target="view"}}>{{i18n login.resend_activation_email}}</a>
|
<a href="#" {{action "sendActivationEmail"}}>{{i18n login.resend_activation_email}}</a>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
<div id='move-selected' class="modal-body">
|
<div id='move-selected' class="modal-body">
|
||||||
{{#if view.error}}
|
{{#if error}}
|
||||||
<div class="alert alert-error">
|
<div class="alert alert-error">
|
||||||
<button class="close" data-dismiss="alert">×</button>
|
<button class="close" data-dismiss="alert">×</button>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{{i18n topic.split_topic.instructions count="view.selectedPostsCount"}}}
|
{{{i18n topic.split_topic.instructions count="selectedPostsCount"}}}
|
||||||
|
|
||||||
<form>
|
<form>
|
||||||
<label>{{i18n topic.split_topic.topic_name}}</label>
|
<label>{{i18n topic.split_topic.topic_name}}</label>
|
||||||
{{textField value=view.topicName placeholderKey="composer.title_placeholder"}}
|
{{textField value=topicName placeholderKey="composer.title_placeholder"}}
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button class='btn btn-primary' {{bindAttr disabled="view.buttonDisabled"}} {{action movePostsToNewTopic target="view"}}>{{view.buttonTitle}}</button>
|
<button class='btn btn-primary' {{bindAttr disabled="buttonDisabled"}} {{action movePostsToNewTopic}}>{{buttonTitle}}</button>
|
||||||
</div>
|
</div>
|
@ -1,46 +0,0 @@
|
|||||||
{{#with view.topic.rank_details}}
|
|
||||||
<div class="modal-body">
|
|
||||||
|
|
||||||
<!-- Note this isn't translated because it's a debugging tool for a feature
|
|
||||||
that is not complete yet. We will probably rip this out altogether -->
|
|
||||||
|
|
||||||
<table class='table'>
|
|
||||||
<tr>
|
|
||||||
<td>hot topic type</td>
|
|
||||||
<td>
|
|
||||||
{{hot_topic_type}}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>random bias</td>
|
|
||||||
<td>{{float random_bias}}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>random multiplier</td>
|
|
||||||
<td>{{float random_multiplier}}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>days ago bias</td>
|
|
||||||
<td>{{float days_ago_bias}}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>days ago multiplier</td>
|
|
||||||
<td>{{float days_ago_multiplier}}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>ranking formula</td>
|
|
||||||
<td>
|
|
||||||
<p>= (random_bias * random_multiplier) +<br/>
|
|
||||||
(days_ago_bias * days_ago_multiplier)</p>
|
|
||||||
<p>= ({{float random_bias}} * {{float random_multiplier}}) + ({{float days_ago_bias}} * {{float days_ago_multiplier}})</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>ranking score</td>
|
|
||||||
<td><b>{{float ranking_score}}</b></td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
</table>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
{{/with}}
|
|
@ -18,7 +18,7 @@
|
|||||||
<button {{action toggleClosed}} class='btn btn-admin'><i class='icon-unlock'></i> {{i18n topic.actions.open}}</button>
|
<button {{action toggleClosed}} class='btn btn-admin'><i class='icon-unlock'></i> {{i18n topic.actions.open}}</button>
|
||||||
{{else}}
|
{{else}}
|
||||||
<button {{action toggleClosed}} class='btn btn-admin'><i class='icon-lock'></i> {{i18n topic.actions.close}}</button>
|
<button {{action toggleClosed}} class='btn btn-admin'><i class='icon-lock'></i> {{i18n topic.actions.close}}</button>
|
||||||
<button {{action autoClose}} class='btn btn-admin'><i class='icon-time'></i> {{i18n topic.actions.auto_close}}</button>
|
<button {{action showAutoClose}} class='btn btn-admin'><i class='icon-time'></i> {{i18n topic.actions.auto_close}}</button>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
@ -18,6 +18,6 @@
|
|||||||
</div>
|
</div>
|
||||||
{{#if can_invite_to}}
|
{{#if can_invite_to}}
|
||||||
<div class='controls'>
|
<div class='controls'>
|
||||||
<button class='btn' {{action showPrivateInviteModal}}>{{i18n private_message_info.invite}}</button>
|
<button class='btn' {{action showPrivateInvite}}>{{i18n private_message_info.invite}}</button>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
@ -40,7 +40,7 @@ Discourse.ChooseTopicView = Discourse.View.extend({
|
|||||||
var topicId = Em.get(topic, 'id');
|
var topicId = Em.get(topic, 'id');
|
||||||
this.set('selectedTopicId', topicId);
|
this.set('selectedTopicId', topicId);
|
||||||
|
|
||||||
Em.run.schedule('afterRender', function () {
|
Em.run.next(function () {
|
||||||
$('#choose-topic-' + topicId).prop('checked', 'true');
|
$('#choose-topic-' + topicId).prop('checked', 'true');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -50,3 +50,4 @@ Discourse.ChooseTopicView = Discourse.View.extend({
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Discourse.View.registerHelper('chooseTopic', Discourse.ChooseTopicView);
|
@ -197,10 +197,7 @@ Discourse.ComposerView = Discourse.View.extend({
|
|||||||
$uploadTarget = $('#reply-control');
|
$uploadTarget = $('#reply-control');
|
||||||
this.editor.hooks.insertImageDialog = function(callback) {
|
this.editor.hooks.insertImageDialog = function(callback) {
|
||||||
callback(null);
|
callback(null);
|
||||||
_this.get('controller.controllers.modal').show(Discourse.ImageSelectorView.create({
|
_this.get('controller').send('showImageSelector', _this);
|
||||||
composer: _this,
|
|
||||||
uploadTarget: $uploadTarget
|
|
||||||
}));
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -342,7 +339,6 @@ Discourse.ComposerView = Discourse.View.extend({
|
|||||||
Em.run.schedule('afterRender', function() {
|
Em.run.schedule('afterRender', function() {
|
||||||
Discourse.Utilities.setCaretPosition(ctrl, caretPosition + text.length);
|
Discourse.Utilities.setCaretPosition(ctrl, caretPosition + text.length);
|
||||||
});
|
});
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Uses javascript to get the image sizes from the preview, if present
|
// Uses javascript to get the image sizes from the preview, if present
|
||||||
|
@ -19,6 +19,8 @@ Discourse.ColorPickerView = Ember.ContainerView.extend({
|
|||||||
var _this = this;
|
var _this = this;
|
||||||
var isUsed, usedColors = this.get('usedColors') || [];
|
var isUsed, usedColors = this.get('usedColors') || [];
|
||||||
|
|
||||||
|
if (!colors) return;
|
||||||
|
|
||||||
colors.each(function(color) {
|
colors.each(function(color) {
|
||||||
isUsed = usedColors.indexOf(color.toUpperCase()) >= 0;
|
isUsed = usedColors.indexOf(color.toUpperCase()) >= 0;
|
||||||
_this.addObject(Discourse.View.create({
|
_this.addObject(Discourse.View.create({
|
||||||
|
@ -9,280 +9,18 @@
|
|||||||
Discourse.CreateAccountView = Discourse.ModalBodyView.extend({
|
Discourse.CreateAccountView = Discourse.ModalBodyView.extend({
|
||||||
templateName: 'modal/create_account',
|
templateName: 'modal/create_account',
|
||||||
title: Em.String.i18n('create_account.title'),
|
title: Em.String.i18n('create_account.title'),
|
||||||
uniqueUsernameValidation: null,
|
|
||||||
globalNicknameExists: false,
|
|
||||||
complete: false,
|
|
||||||
accountPasswordConfirm: 0,
|
|
||||||
accountChallenge: 0,
|
|
||||||
formSubmitted: false,
|
|
||||||
|
|
||||||
submitDisabled: (function() {
|
|
||||||
if (this.get('formSubmitted')) return true;
|
|
||||||
if (this.get('nameValidation.failed')) return true;
|
|
||||||
if (this.get('emailValidation.failed')) return true;
|
|
||||||
if (this.get('usernameValidation.failed')) return true;
|
|
||||||
if (this.get('passwordValidation.failed')) return true;
|
|
||||||
return false;
|
|
||||||
}).property('nameValidation.failed', 'emailValidation.failed', 'usernameValidation.failed', 'passwordValidation.failed', 'formSubmitted'),
|
|
||||||
|
|
||||||
passwordRequired: (function() {
|
|
||||||
return this.blank('authOptions.auth_provider');
|
|
||||||
}).property('authOptions.auth_provider'),
|
|
||||||
|
|
||||||
// Validate the name
|
|
||||||
nameValidation: (function() {
|
|
||||||
|
|
||||||
// If blank, fail without a reason
|
|
||||||
if (this.blank('accountName')) return Discourse.InputValidation.create({ failed: true });
|
|
||||||
|
|
||||||
if (this.get('accountPasswordConfirm') === 0) {
|
|
||||||
this.fetchConfirmationValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If too short
|
|
||||||
if (this.get('accountName').length < 3) {
|
|
||||||
return Discourse.InputValidation.create({
|
|
||||||
failed: true,
|
|
||||||
reason: Em.String.i18n('user.name.too_short')
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Looks good!
|
|
||||||
return Discourse.InputValidation.create({
|
|
||||||
ok: true,
|
|
||||||
reason: Em.String.i18n('user.name.ok')
|
|
||||||
});
|
|
||||||
}).property('accountName'),
|
|
||||||
|
|
||||||
// Check the email address
|
|
||||||
emailValidation: (function() {
|
|
||||||
// If blank, fail without a reason
|
|
||||||
var email;
|
|
||||||
if (this.blank('accountEmail')) {
|
|
||||||
return Discourse.InputValidation.create({
|
|
||||||
failed: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
email = this.get("accountEmail");
|
|
||||||
if ((this.get('authOptions.email') === email) && this.get('authOptions.email_valid')) {
|
|
||||||
return Discourse.InputValidation.create({
|
|
||||||
ok: true,
|
|
||||||
reason: Em.String.i18n('user.email.authenticated', {
|
|
||||||
provider: this.get('authOptions.auth_provider')
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Discourse.Utilities.emailValid(email)) {
|
|
||||||
return Discourse.InputValidation.create({
|
|
||||||
ok: true,
|
|
||||||
reason: Em.String.i18n('user.email.ok')
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return Discourse.InputValidation.create({
|
|
||||||
failed: true,
|
|
||||||
reason: Em.String.i18n('user.email.invalid')
|
|
||||||
});
|
|
||||||
}).property('accountEmail'),
|
|
||||||
|
|
||||||
usernameMatch: (function() {
|
|
||||||
if (this.usernameNeedsToBeValidatedWithEmail()) {
|
|
||||||
if (this.get('emailValidation.failed')) {
|
|
||||||
if (this.shouldCheckUsernameMatch()) {
|
|
||||||
return this.set('uniqueUsernameValidation', Discourse.InputValidation.create({
|
|
||||||
failed: true,
|
|
||||||
reason: Em.String.i18n('user.username.enter_email')
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
return this.set('uniqueUsernameValidation', Discourse.InputValidation.create({ failed: true }));
|
|
||||||
}
|
|
||||||
} else if (this.shouldCheckUsernameMatch()) {
|
|
||||||
this.set('uniqueUsernameValidation', Discourse.InputValidation.create({
|
|
||||||
failed: true,
|
|
||||||
reason: Em.String.i18n('user.username.checking')
|
|
||||||
}));
|
|
||||||
return this.checkUsernameAvailability();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).observes('accountEmail'),
|
|
||||||
|
|
||||||
basicUsernameValidation: (function() {
|
|
||||||
this.set('uniqueUsernameValidation', null);
|
|
||||||
|
|
||||||
// If blank, fail without a reason
|
|
||||||
if (this.blank('accountUsername')) {
|
|
||||||
return Discourse.InputValidation.create({
|
|
||||||
failed: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// If too short
|
|
||||||
if (this.get('accountUsername').length < 3) {
|
|
||||||
return Discourse.InputValidation.create({
|
|
||||||
failed: true,
|
|
||||||
reason: Em.String.i18n('user.username.too_short')
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// If too long
|
|
||||||
if (this.get('accountUsername').length > 15) {
|
|
||||||
return Discourse.InputValidation.create({
|
|
||||||
failed: true,
|
|
||||||
reason: Em.String.i18n('user.username.too_long')
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.checkUsernameAvailability();
|
|
||||||
// Let's check it out asynchronously
|
|
||||||
return Discourse.InputValidation.create({
|
|
||||||
failed: true,
|
|
||||||
reason: Em.String.i18n('user.username.checking')
|
|
||||||
});
|
|
||||||
}).property('accountUsername'),
|
|
||||||
|
|
||||||
shouldCheckUsernameMatch: function() {
|
|
||||||
return !this.blank('accountUsername') && this.get('accountUsername').length > 2;
|
|
||||||
},
|
|
||||||
|
|
||||||
checkUsernameAvailability: Discourse.debounce(function() {
|
|
||||||
var _this = this;
|
|
||||||
if (this.shouldCheckUsernameMatch()) {
|
|
||||||
return Discourse.User.checkUsername(this.get('accountUsername'), this.get('accountEmail')).then(function(result) {
|
|
||||||
_this.set('globalNicknameExists', false);
|
|
||||||
if (result.available) {
|
|
||||||
if (result.global_match) {
|
|
||||||
_this.set('globalNicknameExists', true);
|
|
||||||
return _this.set('uniqueUsernameValidation', Discourse.InputValidation.create({
|
|
||||||
ok: true,
|
|
||||||
reason: Em.String.i18n('user.username.global_match')
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
return _this.set('uniqueUsernameValidation', Discourse.InputValidation.create({
|
|
||||||
ok: true,
|
|
||||||
reason: Em.String.i18n('user.username.available')
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (result.suggestion) {
|
|
||||||
if (result.global_match !== void 0 && result.global_match === false) {
|
|
||||||
_this.set('globalNicknameExists', true);
|
|
||||||
return _this.set('uniqueUsernameValidation', Discourse.InputValidation.create({
|
|
||||||
failed: true,
|
|
||||||
reason: Em.String.i18n('user.username.global_mismatch', result)
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
return _this.set('uniqueUsernameValidation', Discourse.InputValidation.create({
|
|
||||||
failed: true,
|
|
||||||
reason: Em.String.i18n('user.username.not_available', result)
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
} else if (result.errors) {
|
|
||||||
return _this.set('uniqueUsernameValidation', Discourse.InputValidation.create({
|
|
||||||
failed: true,
|
|
||||||
reason: result.errors.join(' ')
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
_this.set('globalNicknameExists', true);
|
|
||||||
return _this.set('uniqueUsernameValidation', Discourse.InputValidation.create({
|
|
||||||
failed: true,
|
|
||||||
reason: Em.String.i18n('user.username.enter_email')
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, 500),
|
|
||||||
|
|
||||||
// Actually wait for the async name check before we're 100% sure we're good to go
|
|
||||||
usernameValidation: (function() {
|
|
||||||
var basicValidation, uniqueUsername;
|
|
||||||
basicValidation = this.get('basicUsernameValidation');
|
|
||||||
uniqueUsername = this.get('uniqueUsernameValidation');
|
|
||||||
if (uniqueUsername) {
|
|
||||||
return uniqueUsername;
|
|
||||||
}
|
|
||||||
return basicValidation;
|
|
||||||
}).property('uniqueUsernameValidation', 'basicUsernameValidation'),
|
|
||||||
|
|
||||||
usernameNeedsToBeValidatedWithEmail: function() {
|
|
||||||
return( this.get('globalNicknameExists') || false );
|
|
||||||
},
|
|
||||||
|
|
||||||
// Validate the password
|
|
||||||
passwordValidation: (function() {
|
|
||||||
var password;
|
|
||||||
if (!this.get('passwordRequired')) {
|
|
||||||
return Discourse.InputValidation.create({
|
|
||||||
ok: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// If blank, fail without a reason
|
|
||||||
password = this.get("accountPassword");
|
|
||||||
if (this.blank('accountPassword')) {
|
|
||||||
return Discourse.InputValidation.create({ failed: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
// If too short
|
|
||||||
if (password.length < 6) {
|
|
||||||
return Discourse.InputValidation.create({
|
|
||||||
failed: true,
|
|
||||||
reason: Em.String.i18n('user.password.too_short')
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Looks good!
|
|
||||||
return Discourse.InputValidation.create({
|
|
||||||
ok: true,
|
|
||||||
reason: Em.String.i18n('user.password.ok')
|
|
||||||
});
|
|
||||||
}).property('accountPassword'),
|
|
||||||
|
|
||||||
fetchConfirmationValue: function() {
|
|
||||||
var createAccountView = this;
|
|
||||||
return Discourse.ajax('/users/hp.json').then(function (json) {
|
|
||||||
createAccountView.set('accountPasswordConfirm', json.value);
|
|
||||||
createAccountView.set('accountChallenge', json.challenge.split("").reverse().join(""));
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
createAccount: function() {
|
|
||||||
var challenge, email, name, password, passwordConfirm, username,
|
|
||||||
_this = this;
|
|
||||||
this.set('formSubmitted', true);
|
|
||||||
name = this.get('accountName');
|
|
||||||
email = this.get('accountEmail');
|
|
||||||
password = this.get('accountPassword');
|
|
||||||
username = this.get('accountUsername');
|
|
||||||
passwordConfirm = this.get('accountPasswordConfirm');
|
|
||||||
challenge = this.get('accountChallenge');
|
|
||||||
return Discourse.User.createAccount(name, email, password, username, passwordConfirm, challenge).then(function(result) {
|
|
||||||
if (result.success) {
|
|
||||||
_this.flash(result.message);
|
|
||||||
_this.set('complete', true);
|
|
||||||
} else {
|
|
||||||
_this.flash(result.message || Em.String.i18n('create_account.failed'), 'error');
|
|
||||||
_this.set('formSubmitted', false);
|
|
||||||
}
|
|
||||||
if (result.active) {
|
|
||||||
return window.location.reload();
|
|
||||||
}
|
|
||||||
}, function() {
|
|
||||||
_this.set('formSubmitted', false);
|
|
||||||
return _this.flash(Em.String.i18n('create_account.failed'), 'error');
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
didInsertElement: function(e) {
|
didInsertElement: function(e) {
|
||||||
|
|
||||||
|
this._super();
|
||||||
|
|
||||||
// allows the submission the form when pressing 'ENTER' on *any* text input field
|
// allows the submission the form when pressing 'ENTER' on *any* text input field
|
||||||
// but only when the submit button is enabled
|
// but only when the submit button is enabled
|
||||||
var createAccountView = this;
|
var createAccountController = this.get('controller');
|
||||||
Em.run.schedule('afterRender', function() {
|
Em.run.schedule('afterRender', function() {
|
||||||
$("input[type='text'], input[type='password']").keydown(function(e) {
|
$("input[type='text'], input[type='password']").keydown(function(e) {
|
||||||
if (createAccountView.get('submitDisabled') === false && e.keyCode === 13) {
|
if (createAccountController.get('submitDisabled') === false && e.keyCode === 13) {
|
||||||
createAccountView.createAccount();
|
createAccountController.createAccount();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -7,181 +7,5 @@
|
|||||||
@module Discourse
|
@module Discourse
|
||||||
**/
|
**/
|
||||||
Discourse.EditCategoryView = Discourse.ModalBodyView.extend({
|
Discourse.EditCategoryView = Discourse.ModalBodyView.extend({
|
||||||
templateName: 'modal/edit_category',
|
templateName: 'modal/edit_category'
|
||||||
generalSelected: Ember.computed.equal('selectedTab', 'general'),
|
|
||||||
securitySelected: Ember.computed.equal('selectedTab', 'security'),
|
|
||||||
settingsSelected: Ember.computed.equal('selectedTab', 'settings'),
|
|
||||||
foregroundColors: ['FFFFFF', '000000'],
|
|
||||||
|
|
||||||
init: function() {
|
|
||||||
this._super();
|
|
||||||
this.set('selectedTab', 'general');
|
|
||||||
},
|
|
||||||
|
|
||||||
modalClass: function() {
|
|
||||||
return "edit-category-modal " + (this.present('category.description') ? 'full' : 'small');
|
|
||||||
}.property('category.description'),
|
|
||||||
|
|
||||||
selectGeneral: function() {
|
|
||||||
this.set('selectedTab', 'general');
|
|
||||||
},
|
|
||||||
|
|
||||||
selectSecurity: function() {
|
|
||||||
this.set('selectedTab', 'security');
|
|
||||||
},
|
|
||||||
|
|
||||||
selectSettings: function() {
|
|
||||||
this.set('selectedTab', 'settings');
|
|
||||||
},
|
|
||||||
|
|
||||||
disabled: function() {
|
|
||||||
if (this.get('saving') || this.get('deleting')) return true;
|
|
||||||
if (!this.get('category.name')) return true;
|
|
||||||
if (!this.get('category.color')) return true;
|
|
||||||
return false;
|
|
||||||
}.property('category.name', 'category.color', 'deleting'),
|
|
||||||
|
|
||||||
deleteVisible: function() {
|
|
||||||
return (this.get('category.id') && this.get('category.topic_count') === 0);
|
|
||||||
}.property('category.id', 'category.topic_count'),
|
|
||||||
|
|
||||||
deleteDisabled: function() {
|
|
||||||
return (this.get('deleting') || this.get('saving') || false);
|
|
||||||
}.property('disabled', 'saving', 'deleting'),
|
|
||||||
|
|
||||||
colorStyle: function() {
|
|
||||||
return "background-color: #" + (this.get('category.color')) + "; color: #" + (this.get('category.text_color')) + ";";
|
|
||||||
}.property('category.color', 'category.text_color'),
|
|
||||||
|
|
||||||
// background colors are available as a pipe-separated string
|
|
||||||
backgroundColors: function() {
|
|
||||||
var categories = Discourse.Category.list();
|
|
||||||
return Discourse.SiteSettings.category_colors.split("|").map(function(i) { return i.toUpperCase(); }).concat(
|
|
||||||
categories.map(function(c) { return c.color.toUpperCase(); }) ).uniq();
|
|
||||||
}.property('Discourse.SiteSettings.category_colors'),
|
|
||||||
|
|
||||||
usedBackgroundColors: function() {
|
|
||||||
var categories = Discourse.Category.list();
|
|
||||||
return categories.map(function(c) {
|
|
||||||
// If editing a category, don't include its color:
|
|
||||||
return (this.get('category.id') && this.get('category.color').toUpperCase() === c.color.toUpperCase()) ? null : c.color.toUpperCase();
|
|
||||||
}, this).compact();
|
|
||||||
}.property('category.id', 'category.color'),
|
|
||||||
|
|
||||||
title: function() {
|
|
||||||
if (this.get('category.id')) return Em.String.i18n("category.edit_long");
|
|
||||||
if (this.get('category.isUncategorized')) return Em.String.i18n("category.edit_uncategorized");
|
|
||||||
return Em.String.i18n("category.create");
|
|
||||||
}.property('category.id'),
|
|
||||||
|
|
||||||
categoryName: function() {
|
|
||||||
var name = this.get('category.name') || "";
|
|
||||||
return name.trim().length > 0 ? name : Em.String.i18n("preview");
|
|
||||||
}.property('category.name'),
|
|
||||||
|
|
||||||
buttonTitle: function() {
|
|
||||||
if (this.get('saving')) return Em.String.i18n("saving");
|
|
||||||
if (this.get('category.isUncategorized')) return Em.String.i18n("save");
|
|
||||||
return (this.get('category.id') ? Em.String.i18n("category.save") : Em.String.i18n("category.create"));
|
|
||||||
}.property('saving', 'category.id'),
|
|
||||||
|
|
||||||
deleteButtonTitle: function() {
|
|
||||||
return Em.String.i18n('category.delete');
|
|
||||||
}.property(),
|
|
||||||
|
|
||||||
didInsertElement: function() {
|
|
||||||
this._super();
|
|
||||||
|
|
||||||
if (this.get('category.id')) {
|
|
||||||
this.set('loading', true);
|
|
||||||
var categoryView = this;
|
|
||||||
|
|
||||||
// We need the topic_count to be correct, so get the most up-to-date info about this category from the server.
|
|
||||||
Discourse.Category.findBySlugOrId( this.get('category.slug') || this.get('category.id') ).then( function(cat) {
|
|
||||||
categoryView.set('category', cat);
|
|
||||||
Discourse.Site.instance().updateCategory(cat);
|
|
||||||
categoryView.set('id', categoryView.get('category.slug'));
|
|
||||||
categoryView.set('loading', false);
|
|
||||||
});
|
|
||||||
} else if( this.get('category.isUncategorized') ) {
|
|
||||||
this.set('category', Discourse.Category.uncategorizedInstance());
|
|
||||||
} else {
|
|
||||||
this.set('category', Discourse.Category.create({ color: 'AB9364', text_color: 'FFFFFF', hotness: 5 }));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
showCategoryTopic: function() {
|
|
||||||
$('#discourse-modal').modal('hide');
|
|
||||||
Discourse.URL.routeTo(this.get('category.topic_url'));
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
addGroup: function(){
|
|
||||||
this.get("category").addGroup(this.get("selectedGroup"));
|
|
||||||
},
|
|
||||||
|
|
||||||
removeGroup: function(group){
|
|
||||||
// OBVIOUS, Ember treats this as Ember.String, we need a real string here
|
|
||||||
group = group + "";
|
|
||||||
this.get("category").removeGroup(group);
|
|
||||||
},
|
|
||||||
|
|
||||||
saveCategory: function() {
|
|
||||||
var categoryView = this;
|
|
||||||
this.set('saving', true);
|
|
||||||
|
|
||||||
|
|
||||||
if( this.get('category.isUncategorized') ) {
|
|
||||||
$.when(
|
|
||||||
Discourse.SiteSetting.update('uncategorized_color', this.get('category.color')),
|
|
||||||
Discourse.SiteSetting.update('uncategorized_text_color', this.get('category.text_color')),
|
|
||||||
Discourse.SiteSetting.update('uncategorized_name', this.get('category.name'))
|
|
||||||
).then(function(result) {
|
|
||||||
// success
|
|
||||||
$('#discourse-modal').modal('hide');
|
|
||||||
// We can't redirect to the uncategorized category on save because the slug
|
|
||||||
// might have changed.
|
|
||||||
Discourse.URL.redirectTo("/categories");
|
|
||||||
}, function(errors) {
|
|
||||||
// errors
|
|
||||||
if(errors.length === 0) errors.push(Em.String.i18n("category.save_error"));
|
|
||||||
categoryView.displayErrors(errors);
|
|
||||||
categoryView.set('saving', false);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.get('category').save().then(function(result) {
|
|
||||||
// success
|
|
||||||
$('#discourse-modal').modal('hide');
|
|
||||||
Discourse.URL.redirectTo("/category/" + Discourse.Category.slugFor(result.category));
|
|
||||||
}, function(errors) {
|
|
||||||
// errors
|
|
||||||
if(errors.length === 0) errors.push(Em.String.i18n("category.creation_error"));
|
|
||||||
categoryView.displayErrors(errors);
|
|
||||||
categoryView.set('saving', false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
deleteCategory: function() {
|
|
||||||
var categoryView = this;
|
|
||||||
this.set('deleting', true);
|
|
||||||
$('#discourse-modal').modal('hide');
|
|
||||||
bootbox.confirm(Em.String.i18n("category.delete_confirm"), Em.String.i18n("no_value"), Em.String.i18n("yes_value"), function(result) {
|
|
||||||
if (result) {
|
|
||||||
categoryView.get('category').destroy().then(function(){
|
|
||||||
// success
|
|
||||||
Discourse.URL.redirectTo("/categories");
|
|
||||||
}, function(jqXHR){
|
|
||||||
// error
|
|
||||||
$('#discourse-modal').modal('show');
|
|
||||||
categoryView.displayErrors([Em.String.i18n("category.delete_error")]);
|
|
||||||
categoryView.set('deleting', false);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
$('#discourse-modal').modal('show');
|
|
||||||
categoryView.set('deleting', false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -8,38 +8,5 @@
|
|||||||
**/
|
**/
|
||||||
Discourse.EditTopicAutoCloseView = Discourse.ModalBodyView.extend({
|
Discourse.EditTopicAutoCloseView = Discourse.ModalBodyView.extend({
|
||||||
templateName: 'modal/auto_close',
|
templateName: 'modal/auto_close',
|
||||||
title: Em.String.i18n('topic.auto_close_title'),
|
title: Em.String.i18n('topic.auto_close_title')
|
||||||
modalClass: 'edit-auto-close-modal',
|
|
||||||
|
|
||||||
setDays: function() {
|
|
||||||
if( this.get('topic.auto_close_at') ) {
|
|
||||||
var closeTime = Date.create( this.get('topic.auto_close_at') );
|
|
||||||
if (closeTime.isFuture()) {
|
|
||||||
this.set('auto_close_days', closeTime.daysSince());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.observes('topic'),
|
|
||||||
|
|
||||||
saveAutoClose: function() {
|
|
||||||
this.setAutoClose( parseFloat(this.get('auto_close_days')) );
|
|
||||||
},
|
|
||||||
|
|
||||||
removeAutoClose: function() {
|
|
||||||
this.setAutoClose(null);
|
|
||||||
},
|
|
||||||
|
|
||||||
setAutoClose: function(days) {
|
|
||||||
var view = this;
|
|
||||||
Discourse.ajax({
|
|
||||||
url: "/t/" + this.get('topic.id') + "/autoclose",
|
|
||||||
type: 'PUT',
|
|
||||||
dataType: 'json',
|
|
||||||
data: { auto_close_days: days > 0 ? days : null }
|
|
||||||
}).then(function(){
|
|
||||||
view.get('topic').set('auto_close_at', Date.create(days + ' days from now').toJSON());
|
|
||||||
}, function (error) {
|
|
||||||
bootbox.alert(Em.String.i18n('generic_error'));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
});
|
@ -7,83 +7,19 @@
|
|||||||
@module Discourse
|
@module Discourse
|
||||||
**/
|
**/
|
||||||
Discourse.FlagView = Discourse.ModalBodyView.extend({
|
Discourse.FlagView = Discourse.ModalBodyView.extend({
|
||||||
templateName: 'flag',
|
templateName: 'modal/flag',
|
||||||
title: Em.String.i18n('flagging.title'),
|
title: Em.String.i18n('flagging.title'),
|
||||||
|
|
||||||
// trick to bind user / post to flag
|
selectedChanged: function() {
|
||||||
boundFlags: function(){
|
var nameKey = this.get('controller.selected.name_key');
|
||||||
var _this = this;
|
if (!nameKey) return;
|
||||||
var original = this.get('post.flagsAvailable');
|
|
||||||
if(original){
|
|
||||||
return $.map(original, function(v){
|
|
||||||
var b = Discourse.BoundPostActionType.create(v);
|
|
||||||
b.set('post', _this.get('post'));
|
|
||||||
return b;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}.property('post.flagsAvailable'),
|
|
||||||
|
|
||||||
changePostActionType: function(action) {
|
|
||||||
|
|
||||||
if (this.get('postActionTypeId') === action.id) return false;
|
|
||||||
|
|
||||||
this.get('boundFlags').each(function(f){
|
|
||||||
f.set('selected', false);
|
|
||||||
});
|
|
||||||
action.set('selected', true);
|
|
||||||
|
|
||||||
this.set('postActionTypeId', action.id);
|
|
||||||
this.set('isCustomFlag', action.is_custom_flag);
|
|
||||||
this.set('selected', action);
|
|
||||||
Em.run.next(function() {
|
Em.run.next(function() {
|
||||||
$('#radio_' + action.name_key).prop('checked', 'true');
|
$('#radio_' + nameKey).prop('checked', 'true');
|
||||||
});
|
});
|
||||||
return false;
|
}.observes('controller.selected.name_key'),
|
||||||
},
|
|
||||||
|
|
||||||
createFlag: function() {
|
|
||||||
var _this = this;
|
|
||||||
|
|
||||||
var action = this.get('selected');
|
|
||||||
var postAction = this.get('post.actionByName.' + (action.get('name_key')));
|
|
||||||
|
|
||||||
var actionType = Discourse.Site.instance().postActionTypeById(this.get('postActionTypeId'));
|
|
||||||
if (postAction) {
|
|
||||||
postAction.act({
|
|
||||||
message: action.get('message')
|
|
||||||
}).then(function() {
|
|
||||||
return $('#discourse-modal').modal('hide');
|
|
||||||
}, function(errors) {
|
|
||||||
return _this.displayErrors(errors);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
showSubmit: (function() {
|
|
||||||
var m;
|
|
||||||
if (this.get('postActionTypeId')) {
|
|
||||||
if (this.get('isCustomFlag')) {
|
|
||||||
m = this.get('selected.message');
|
|
||||||
return m && m.length >= 10 && m.length <= 500;
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}).property('isCustomFlag', 'selected.customMessageLength', 'postActionTypeId'),
|
|
||||||
|
|
||||||
submitText: function(){
|
|
||||||
var action = this.get('selected');
|
|
||||||
if (this.get('selected.is_custom_flag')) {
|
|
||||||
return Em.String.i18n("flagging.notify_action");
|
|
||||||
} else {
|
|
||||||
return Em.String.i18n("flagging.action");
|
|
||||||
}
|
|
||||||
}.property('selected'),
|
|
||||||
|
|
||||||
didInsertElement: function() {
|
didInsertElement: function() {
|
||||||
this.set('postActionTypeId', null);
|
this._super();
|
||||||
|
|
||||||
// Would be nice if there were an EmberJs radio button to do this for us. Oh well, one should be coming
|
// Would be nice if there were an EmberJs radio button to do this for us. Oh well, one should be coming
|
||||||
// in an upcoming release.
|
// in an upcoming release.
|
||||||
|
@ -8,25 +8,7 @@
|
|||||||
**/
|
**/
|
||||||
Discourse.ForgotPasswordView = Discourse.ModalBodyView.extend({
|
Discourse.ForgotPasswordView = Discourse.ModalBodyView.extend({
|
||||||
templateName: 'modal/forgot_password',
|
templateName: 'modal/forgot_password',
|
||||||
title: Em.String.i18n('forgot_password.title'),
|
title: Em.String.i18n('forgot_password.title')
|
||||||
|
|
||||||
// You need a value in the field to submit it.
|
|
||||||
submitDisabled: (function() {
|
|
||||||
return this.blank('accountEmailOrUsername');
|
|
||||||
}).property('accountEmailOrUsername'),
|
|
||||||
|
|
||||||
submit: function() {
|
|
||||||
|
|
||||||
Discourse.ajax("/session/forgot_password", {
|
|
||||||
data: { login: this.get('accountEmailOrUsername') },
|
|
||||||
type: 'POST'
|
|
||||||
});
|
|
||||||
|
|
||||||
// don't tell people what happened, this keeps it more secure (ensure same on server)
|
|
||||||
this.flash(Em.String.i18n('forgot_password.complete'));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,6 +1,3 @@
|
|||||||
/*jshint newcap:false*/
|
|
||||||
/*global diff_match_patch:true assetPath:true*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
This view handles rendering of the history of a post
|
This view handles rendering of the history of a post
|
||||||
|
|
||||||
@ -9,75 +6,7 @@
|
|||||||
@namespace Discourse
|
@namespace Discourse
|
||||||
@module Discourse
|
@module Discourse
|
||||||
**/
|
**/
|
||||||
Discourse.HistoryView = Discourse.View.extend({
|
Discourse.HistoryView = Discourse.ModalBodyView.extend({
|
||||||
templateName: 'history',
|
templateName: 'modal/history',
|
||||||
title: Em.String.i18n('history'),
|
title: Em.String.i18n('history')
|
||||||
modalClass: 'history-modal',
|
|
||||||
diffLibraryLoaded: false,
|
|
||||||
diff: null,
|
|
||||||
|
|
||||||
init: function(){
|
|
||||||
this._super();
|
|
||||||
var historyView = this;
|
|
||||||
$LAB.script(assetPath('defer/google_diff_match_patch')).wait(function(){
|
|
||||||
historyView.set('diffLibraryLoaded', true);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
loadSide: function(side) {
|
|
||||||
if (this.get("version" + side)) {
|
|
||||||
var orig = this.get('originalPost');
|
|
||||||
var version = this.get("version" + side + ".number");
|
|
||||||
if (version === orig.get('version')) {
|
|
||||||
this.set("post" + side, orig);
|
|
||||||
} else {
|
|
||||||
var historyView = this;
|
|
||||||
Discourse.Post.loadVersion(orig.get('id'), version).then(function(post) {
|
|
||||||
historyView.set("post" + side, post);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
changedLeftVersion: function() {
|
|
||||||
this.loadSide("Left");
|
|
||||||
}.observes('versionLeft'),
|
|
||||||
|
|
||||||
changedRightVersion: function() {
|
|
||||||
this.loadSide("Right");
|
|
||||||
}.observes('versionRight'),
|
|
||||||
|
|
||||||
loadedPosts: function() {
|
|
||||||
if (this.get('diffLibraryLoaded') && this.get('postLeft') && this.get('postRight')) {
|
|
||||||
var dmp = new diff_match_patch(),
|
|
||||||
before = this.get("postLeft.cooked"),
|
|
||||||
after = this.get("postRight.cooked"),
|
|
||||||
diff = dmp.diff_main(before, after);
|
|
||||||
dmp.diff_cleanupSemantic(diff);
|
|
||||||
this.set('diff', dmp.diff_prettyHtml(diff));
|
|
||||||
}
|
|
||||||
}.observes('diffLibraryLoaded', 'postLeft', 'postRight'),
|
|
||||||
|
|
||||||
didInsertElement: function() {
|
|
||||||
this.setProperties({
|
|
||||||
loading: true,
|
|
||||||
postLeft: null,
|
|
||||||
postRight: null
|
|
||||||
});
|
|
||||||
|
|
||||||
var historyView = this;
|
|
||||||
this.get('originalPost').loadVersions().then(function(result) {
|
|
||||||
result.each(function(item) {
|
|
||||||
item.description = "v" + item.number + " - " + Date.create(item.created_at).relative() + " - " +
|
|
||||||
Em.String.i18n("changed_by", { author: item.display_username });
|
|
||||||
});
|
|
||||||
|
|
||||||
historyView.setProperties({
|
|
||||||
loading: false,
|
|
||||||
versionLeft: result.first(),
|
|
||||||
versionRight: result.last(),
|
|
||||||
versions: result
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
@ -2,38 +2,22 @@
|
|||||||
This view handles the image upload interface
|
This view handles the image upload interface
|
||||||
|
|
||||||
@class ImageSelectorView
|
@class ImageSelectorView
|
||||||
@extends Discourse.View
|
@extends Discourse.ModalBodyView
|
||||||
@namespace Discourse
|
@namespace Discourse
|
||||||
@module Discourse
|
@module Discourse
|
||||||
**/
|
**/
|
||||||
Discourse.ImageSelectorView = Discourse.View.extend({
|
Discourse.ImageSelectorView = Discourse.ModalBodyView.extend({
|
||||||
templateName: 'image_selector',
|
templateName: 'modal/image_selector',
|
||||||
classNames: ['image-selector'],
|
classNames: ['image-selector'],
|
||||||
title: Em.String.i18n('image_selector.title'),
|
title: Em.String.i18n('image_selector.title'),
|
||||||
|
|
||||||
init: function() {
|
|
||||||
this._super();
|
|
||||||
this.set('localSelected', true);
|
|
||||||
},
|
|
||||||
|
|
||||||
selectLocal: function() {
|
|
||||||
this.set('localSelected', true);
|
|
||||||
},
|
|
||||||
|
|
||||||
selectRemote: function() {
|
|
||||||
this.set('localSelected', false);
|
|
||||||
},
|
|
||||||
|
|
||||||
remoteSelected: function() {
|
|
||||||
return !this.get('localSelected');
|
|
||||||
}.property('localSelected'),
|
|
||||||
|
|
||||||
upload: function() {
|
upload: function() {
|
||||||
this.get('uploadTarget').fileupload('add', { fileInput: $('#filename-input') });
|
$('#reply-control').fileupload('add', { fileInput: $('#filename-input') });
|
||||||
},
|
},
|
||||||
|
|
||||||
add: function() {
|
add: function() {
|
||||||
this.get('composer').addMarkdown(".val() + ")");
|
this.get('controller.composerView').addMarkdown(".val() + ")");
|
||||||
$('#discourse-modal').modal('hide');
|
$('#discourse-modal').modal('hide');
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -1,59 +0,0 @@
|
|||||||
/**
|
|
||||||
A modal view for inviting a user to Discourse
|
|
||||||
|
|
||||||
@class InviteModalView
|
|
||||||
@extends Discourse.ModalBodyView
|
|
||||||
@namespace Discourse
|
|
||||||
@module Discourse
|
|
||||||
**/
|
|
||||||
Discourse.InviteModalView = Discourse.ModalBodyView.extend({
|
|
||||||
templateName: 'modal/invite',
|
|
||||||
title: Em.String.i18n('topic.invite_reply.title'),
|
|
||||||
email: null,
|
|
||||||
error: false,
|
|
||||||
saving: false,
|
|
||||||
finished: false,
|
|
||||||
|
|
||||||
disabled: (function() {
|
|
||||||
if (this.get('saving')) return true;
|
|
||||||
if (this.blank('email')) return true;
|
|
||||||
if (!Discourse.Utilities.emailValid(this.get('email'))) return true;
|
|
||||||
return false;
|
|
||||||
}).property('email', 'saving'),
|
|
||||||
|
|
||||||
buttonTitle: (function() {
|
|
||||||
if (this.get('saving')) return Em.String.i18n('topic.inviting');
|
|
||||||
return Em.String.i18n('topic.invite_reply.action');
|
|
||||||
}).property('saving'),
|
|
||||||
|
|
||||||
successMessage: (function() {
|
|
||||||
return Em.String.i18n('topic.invite_reply.success', {
|
|
||||||
email: this.get('email')
|
|
||||||
});
|
|
||||||
}).property('email'),
|
|
||||||
|
|
||||||
didInsertElement: function() {
|
|
||||||
var inviteModalView = this;
|
|
||||||
Em.run.schedule('afterRender', function() {
|
|
||||||
inviteModalView.$('input').focus();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
createInvite: function() {
|
|
||||||
var _this = this;
|
|
||||||
this.set('saving', true);
|
|
||||||
this.set('error', false);
|
|
||||||
this.get('topic').inviteUser(this.get('email')).then(function() {
|
|
||||||
// Success
|
|
||||||
_this.set('saving', false);
|
|
||||||
return _this.set('finished', true);
|
|
||||||
}, function() {
|
|
||||||
// Failure
|
|
||||||
_this.set('error', true);
|
|
||||||
return _this.set('saving', false);
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
|||||||
/**
|
|
||||||
A modal view for inviting a user to private message
|
|
||||||
|
|
||||||
@class InvitePrivateModalView
|
|
||||||
@extends Discourse.ModalBodyView
|
|
||||||
@namespace Discourse
|
|
||||||
@module Discourse
|
|
||||||
**/
|
|
||||||
Discourse.InvitePrivateModalView = Discourse.ModalBodyView.extend({
|
|
||||||
templateName: 'modal/invite_private',
|
|
||||||
title: Em.String.i18n('topic.invite_private.title'),
|
|
||||||
email: null,
|
|
||||||
error: false,
|
|
||||||
saving: false,
|
|
||||||
finished: false,
|
|
||||||
|
|
||||||
disabled: (function() {
|
|
||||||
if (this.get('saving')) return true;
|
|
||||||
return this.blank('emailOrUsername');
|
|
||||||
}).property('emailOrUsername', 'saving'),
|
|
||||||
|
|
||||||
buttonTitle: (function() {
|
|
||||||
if (this.get('saving')) return Em.String.i18n('topic.inviting');
|
|
||||||
return Em.String.i18n('topic.invite_private.action');
|
|
||||||
}).property('saving'),
|
|
||||||
|
|
||||||
didInsertElement: function() {
|
|
||||||
var invitePrivateModalView = this;
|
|
||||||
Em.run.schedule('afterRender', function() {
|
|
||||||
invitePrivateModalView.$('input').focus();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
invite: function() {
|
|
||||||
var _this = this;
|
|
||||||
this.set('saving', true);
|
|
||||||
this.set('error', false);
|
|
||||||
// Invite the user to the private message
|
|
||||||
this.get('topic').inviteUser(this.get('emailOrUsername')).then(function() {
|
|
||||||
// Success
|
|
||||||
_this.set('saving', false);
|
|
||||||
_this.set('finished', true);
|
|
||||||
}, function() {
|
|
||||||
// Failure
|
|
||||||
_this.set('error', true);
|
|
||||||
_this.set('saving', false);
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
A modal view for inviting a user to private message
|
||||||
|
|
||||||
|
@class InvitePrivateView
|
||||||
|
@extends Discourse.ModalBodyView
|
||||||
|
@namespace Discourse
|
||||||
|
@module Discourse
|
||||||
|
**/
|
||||||
|
Discourse.InvitePrivateView = Discourse.ModalBodyView.extend({
|
||||||
|
templateName: 'modal/invite_private',
|
||||||
|
title: Em.String.i18n('topic.invite_private.title'),
|
||||||
|
|
||||||
|
didInsertElement: function() {
|
||||||
|
this._super();
|
||||||
|
var invitePrivateModalView = this;
|
||||||
|
Em.run.schedule('afterRender', function() {
|
||||||
|
invitePrivateModalView.$('input').focus();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
24
app/assets/javascripts/discourse/views/modal/invite_view.js
Normal file
24
app/assets/javascripts/discourse/views/modal/invite_view.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
A modal view for inviting a user to Discourse
|
||||||
|
|
||||||
|
@class InviteView
|
||||||
|
@extends Discourse.ModalBodyView
|
||||||
|
@namespace Discourse
|
||||||
|
@module Discourse
|
||||||
|
**/
|
||||||
|
Discourse.InviteView = Discourse.ModalBodyView.extend({
|
||||||
|
templateName: 'modal/invite',
|
||||||
|
title: Em.String.i18n('topic.invite_reply.title'),
|
||||||
|
|
||||||
|
didInsertElement: function() {
|
||||||
|
this._super();
|
||||||
|
|
||||||
|
var inviteModalView = this;
|
||||||
|
Em.run.schedule('afterRender', function() {
|
||||||
|
inviteModalView.$('input').focus();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
@ -9,175 +9,29 @@
|
|||||||
Discourse.LoginView = Discourse.ModalBodyView.extend({
|
Discourse.LoginView = Discourse.ModalBodyView.extend({
|
||||||
templateName: 'modal/login',
|
templateName: 'modal/login',
|
||||||
title: Em.String.i18n('login.title'),
|
title: Em.String.i18n('login.title'),
|
||||||
authenticate: null,
|
|
||||||
loggingIn: false,
|
|
||||||
|
|
||||||
|
|
||||||
site: function() {
|
|
||||||
return Discourse.Site.instance();
|
|
||||||
}.property(),
|
|
||||||
|
|
||||||
showView: function(view) {
|
|
||||||
return this.get('controller').show(view);
|
|
||||||
},
|
|
||||||
|
|
||||||
newAccount: function() {
|
|
||||||
return this.showView(Discourse.CreateAccountView.create());
|
|
||||||
},
|
|
||||||
|
|
||||||
forgotPassword: function() {
|
|
||||||
return this.showView(Discourse.ForgotPasswordView.create());
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
Determines whether at least one login button is enabled
|
|
||||||
**/
|
|
||||||
hasAtLeastOneLoginButton: function() {
|
|
||||||
return Discourse.SiteSettings.enable_google_logins ||
|
|
||||||
Discourse.SiteSettings.enable_facebook_logins ||
|
|
||||||
Discourse.SiteSettings.enable_cas_logins ||
|
|
||||||
Discourse.SiteSettings.enable_twitter_logins ||
|
|
||||||
Discourse.SiteSettings.enable_yahoo_logins ||
|
|
||||||
Discourse.SiteSettings.enable_github_logins ||
|
|
||||||
Discourse.SiteSettings.enable_persona_logins;
|
|
||||||
}.property(),
|
|
||||||
|
|
||||||
loginButtonText: function() {
|
|
||||||
return this.get('loggingIn') ? Em.String.i18n('login.logging_in') : Em.String.i18n('login.title');
|
|
||||||
}.property('loggingIn'),
|
|
||||||
|
|
||||||
loginDisabled: function() {
|
|
||||||
return this.get('loggingIn') || this.blank('loginName') || this.blank('loginPassword');
|
|
||||||
}.property('loginName', 'loginPassword', 'loggingIn'),
|
|
||||||
|
|
||||||
login: function() {
|
|
||||||
this.set('loggingIn', true);
|
|
||||||
|
|
||||||
var loginView = this;
|
|
||||||
Discourse.ajax("/session", {
|
|
||||||
data: { login: this.get('loginName'), password: this.get('loginPassword') },
|
|
||||||
type: 'POST'
|
|
||||||
}).then(function (result) {
|
|
||||||
// Successful login
|
|
||||||
if (result.error) {
|
|
||||||
loginView.set('loggingIn', false);
|
|
||||||
if( result.reason === 'not_activated' ) {
|
|
||||||
return loginView.showView(Discourse.NotActivatedView.create({
|
|
||||||
username: loginView.get('loginName'),
|
|
||||||
sentTo: result.sent_to_email,
|
|
||||||
currentEmail: result.current_email
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
loginView.flash(result.error, 'error');
|
|
||||||
} else {
|
|
||||||
// Trigger the browser's password manager using the hidden static login form:
|
|
||||||
var $hidden_login_form = $('#hidden-login-form');
|
|
||||||
$hidden_login_form.find('input[name=username]').val(loginView.get('loginName'));
|
|
||||||
$hidden_login_form.find('input[name=password]').val(loginView.get('loginPassword'));
|
|
||||||
$hidden_login_form.find('input[name=redirect]').val(window.location.href);
|
|
||||||
$hidden_login_form.find('input[name=authenticity_token]').val($('meta[name=csrf-token]').attr('content'));
|
|
||||||
$hidden_login_form.submit();
|
|
||||||
}
|
|
||||||
|
|
||||||
}, function(result) {
|
|
||||||
// Failed to login
|
|
||||||
loginView.flash(Em.String.i18n('login.error'), 'error');
|
|
||||||
loginView.set('loggingIn', false);
|
|
||||||
})
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
authMessage: (function() {
|
|
||||||
if (this.blank('authenticate')) return "";
|
|
||||||
return Em.String.i18n("login." + (this.get('authenticate')) + ".message");
|
|
||||||
}).property('authenticate'),
|
|
||||||
|
|
||||||
twitterLogin: function() {
|
|
||||||
this.set('authenticate', 'twitter');
|
|
||||||
var left = this.get('lastX') - 400;
|
|
||||||
var top = this.get('lastY') - 200;
|
|
||||||
return window.open(Discourse.getURL("/auth/twitter"), "_blank", "menubar=no,status=no,height=400,width=800,left=" + left + ",top=" + top);
|
|
||||||
},
|
|
||||||
|
|
||||||
facebookLogin: function() {
|
|
||||||
this.set('authenticate', 'facebook');
|
|
||||||
var left = this.get('lastX') - 400;
|
|
||||||
var top = this.get('lastY') - 200;
|
|
||||||
return window.open(Discourse.getURL("/auth/facebook"), "_blank", "menubar=no,status=no,height=400,width=800,left=" + left + ",top=" + top);
|
|
||||||
},
|
|
||||||
|
|
||||||
casLogin: function() {
|
|
||||||
var left, top;
|
|
||||||
this.set('authenticate', 'cas');
|
|
||||||
left = this.get('lastX') - 400;
|
|
||||||
top = this.get('lastY') - 200;
|
|
||||||
return window.open("/auth/cas", "_blank", "menubar=no,status=no,height=400,width=800,left=" + left + ",top=" + top);
|
|
||||||
},
|
|
||||||
|
|
||||||
openidLogin: function(provider) {
|
|
||||||
var left = this.get('lastX') - 400;
|
|
||||||
var top = this.get('lastY') - 200;
|
|
||||||
if (provider === "yahoo") {
|
|
||||||
this.set("authenticate", 'yahoo');
|
|
||||||
return window.open(Discourse.getURL("/auth/yahoo"), "_blank", "menubar=no,status=no,height=400,width=800,left=" + left + ",top=" + top);
|
|
||||||
} else {
|
|
||||||
window.open(Discourse.getURL("/auth/google"), "_blank", "menubar=no,status=no,height=500,width=850,left=" + left + ",top=" + top);
|
|
||||||
return this.set("authenticate", 'google');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
githubLogin: function() {
|
|
||||||
this.set('authenticate', 'github');
|
|
||||||
var left = this.get('lastX') - 400;
|
|
||||||
var top = this.get('lastY') - 200;
|
|
||||||
return window.open(Discourse.getURL("/auth/github"), "_blank", "menubar=no,status=no,height=400,width=800,left=" + left + ",top=" + top);
|
|
||||||
},
|
|
||||||
|
|
||||||
personaLogin: function() {
|
|
||||||
navigator.id.request();
|
|
||||||
},
|
|
||||||
|
|
||||||
authenticationComplete: function(options) {
|
|
||||||
if (options.awaiting_approval) {
|
|
||||||
this.flash(Em.String.i18n('login.awaiting_approval'), 'success');
|
|
||||||
this.set('authenticate', null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (options.awaiting_activation) {
|
|
||||||
this.flash(Em.String.i18n('login.awaiting_confirmation'), 'success');
|
|
||||||
this.set('authenticate', null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Reload the page if we're authenticated
|
|
||||||
if (options.authenticated) {
|
|
||||||
window.location.reload();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return this.showView(Discourse.CreateAccountView.create({
|
|
||||||
accountEmail: options.email,
|
|
||||||
accountUsername: options.username,
|
|
||||||
accountName: options.name,
|
|
||||||
authOptions: Em.Object.create(options)
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
|
|
||||||
mouseMove: function(e) {
|
mouseMove: function(e) {
|
||||||
this.set('lastX', e.screenX);
|
this.set('controller.lastX', e.screenX);
|
||||||
return this.set('lastY', e.screenY);
|
this.set('controller.lastY', e.screenY);
|
||||||
},
|
},
|
||||||
|
|
||||||
didInsertElement: function(e) {
|
didInsertElement: function(e) {
|
||||||
|
|
||||||
|
this._super();
|
||||||
|
|
||||||
|
var loginController = this.get('controller');
|
||||||
|
|
||||||
// Get username and password from the browser's password manager,
|
// Get username and password from the browser's password manager,
|
||||||
// if it filled the hidden static login form:
|
// if it filled the hidden static login form:
|
||||||
this.set('loginName', $('#hidden-login-form input[name=username]').val());
|
loginController.set('loginName', $('#hidden-login-form input[name=username]').val());
|
||||||
this.set('loginPassword', $('#hidden-login-form input[name=password]').val());
|
loginController.set('loginPassword', $('#hidden-login-form input[name=password]').val());
|
||||||
|
|
||||||
|
|
||||||
var loginView = this;
|
|
||||||
Em.run.schedule('afterRender', function() {
|
Em.run.schedule('afterRender', function() {
|
||||||
$('#login-account-password').keydown(function(e) {
|
$('#login-account-password').keydown(function(e) {
|
||||||
if (e.keyCode === 13) {
|
if (e.keyCode === 13) {
|
||||||
loginView.login();
|
loginController.login();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -6,49 +6,9 @@
|
|||||||
@namespace Discourse
|
@namespace Discourse
|
||||||
@module Discourse
|
@module Discourse
|
||||||
**/
|
**/
|
||||||
Discourse.MergeTopicView = Discourse.ModalBodyView.extend(Discourse.SelectedPostsCount, {
|
Discourse.MergeTopicView = Discourse.ModalBodyView.extend({
|
||||||
templateName: 'modal/merge_topic',
|
templateName: 'modal/merge_topic',
|
||||||
title: Em.String.i18n('topic.merge_topic.title'),
|
title: Em.String.i18n('topic.merge_topic.title')
|
||||||
|
|
||||||
buttonDisabled: function() {
|
|
||||||
if (this.get('saving')) return true;
|
|
||||||
return this.blank('selectedTopicId');
|
|
||||||
}.property('selectedTopicId', 'saving'),
|
|
||||||
|
|
||||||
buttonTitle: function() {
|
|
||||||
if (this.get('saving')) return Em.String.i18n('saving');
|
|
||||||
return Em.String.i18n('topic.merge_topic.title');
|
|
||||||
}.property('saving'),
|
|
||||||
|
|
||||||
movePostsToExistingTopic: function() {
|
|
||||||
this.set('saving', true);
|
|
||||||
|
|
||||||
var moveSelectedView = this;
|
|
||||||
|
|
||||||
var promise = null;
|
|
||||||
if (this.get('allPostsSelected')) {
|
|
||||||
promise = Discourse.Topic.mergeTopic(this.get('topic.id'), this.get('selectedTopicId'));
|
|
||||||
} else {
|
|
||||||
var postIds = this.get('selectedPosts').map(function(p) { return p.get('id'); });
|
|
||||||
promise = Discourse.Topic.movePosts(this.get('topic.id'), {
|
|
||||||
destination_topic_id: this.get('selectedTopicId'),
|
|
||||||
post_ids: postIds
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
promise.then(function(result) {
|
|
||||||
// Posts moved
|
|
||||||
$('#discourse-modal').modal('hide');
|
|
||||||
moveSelectedView.get('topicController').toggleMultiSelect();
|
|
||||||
Em.run.next(function() { Discourse.URL.routeTo(result.url); });
|
|
||||||
}, function() {
|
|
||||||
// Error moving posts
|
|
||||||
moveSelectedView.flash(Em.String.i18n('topic.merge_topic.error'));
|
|
||||||
moveSelectedView.set('saving', false);
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -10,10 +10,18 @@ Discourse.ModalBodyView = Discourse.View.extend({
|
|||||||
|
|
||||||
// Focus on first element
|
// Focus on first element
|
||||||
didInsertElement: function() {
|
didInsertElement: function() {
|
||||||
|
$('#discourse-modal').modal('show');
|
||||||
|
$('#modal-alert').hide();
|
||||||
|
|
||||||
var modalBodyView = this;
|
var modalBodyView = this;
|
||||||
Em.run.schedule('afterRender', function() {
|
Em.run.schedule('afterRender', function() {
|
||||||
modalBodyView.$('form input:first').focus();
|
modalBodyView.$('form input:first').focus();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var title = this.get('title');
|
||||||
|
if (title) {
|
||||||
|
this.set('controller.controllers.modal.title', title);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Pass the errors to our errors view
|
// Pass the errors to our errors view
|
||||||
@ -22,14 +30,16 @@ Discourse.ModalBodyView = Discourse.View.extend({
|
|||||||
if (typeof callback === "function") callback();
|
if (typeof callback === "function") callback();
|
||||||
},
|
},
|
||||||
|
|
||||||
// Just use jQuery to show an alert. We don't need anythign fancier for now
|
flashMessageChanged: function() {
|
||||||
// like an actual ember view
|
var flashMessage = this.get('controller.flashMessage');
|
||||||
flash: function(msg, flashClass) {
|
if (flashMessage) {
|
||||||
if (!flashClass) flashClass = "success";
|
var messageClass = flashMessage.get('messageClass') || 'success';
|
||||||
var $alert = $('#modal-alert').hide().removeClass('alert-error', 'alert-success');
|
var $alert = $('#modal-alert').hide().removeClass('alert-error', 'alert-success');
|
||||||
$alert.addClass("alert alert-" + flashClass).html(msg);
|
$alert.addClass("alert alert-" + messageClass).html(flashMessage.get('message'));
|
||||||
$alert.fadeIn();
|
$alert.fadeIn();
|
||||||
}
|
}
|
||||||
|
}.observes('controller.flashMessage')
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -6,30 +6,10 @@
|
|||||||
@namespace Discourse
|
@namespace Discourse
|
||||||
@module Discourse
|
@module Discourse
|
||||||
**/
|
**/
|
||||||
Discourse.ModalView = Ember.ContainerView.extend({
|
Discourse.ModalView = Discourse.View.extend({
|
||||||
childViews: ['modalHeaderView', 'modalBodyView', 'modalErrorsView'],
|
|
||||||
classNames: ['modal', 'hidden'],
|
|
||||||
classNameBindings: ['controller.currentView.modalClass'],
|
|
||||||
elementId: 'discourse-modal',
|
elementId: 'discourse-modal',
|
||||||
|
templateName: 'modal/modal',
|
||||||
modalHeaderView: Ember.View.create({
|
classNameBindings: [':modal', ':hidden', 'controller.modalClass']
|
||||||
templateName: 'modal/modal_header',
|
|
||||||
titleBinding: 'controller.currentView.title'
|
|
||||||
}),
|
|
||||||
modalBodyView: Ember.ContainerView.create({ currentViewBinding: 'controller.currentView' }),
|
|
||||||
modalErrorsView: Ember.View.create({ templateName: 'modal/modal_errors' }),
|
|
||||||
|
|
||||||
viewChanged: function() {
|
|
||||||
this.set('modalErrorsView.errors', null);
|
|
||||||
|
|
||||||
var view = this.get('controller.currentView');
|
|
||||||
var modalView = this;
|
|
||||||
if (view) {
|
|
||||||
$('#modal-alert').hide();
|
|
||||||
Em.run.schedule('afterRender', function() { modalView.$().modal('show'); });
|
|
||||||
}
|
|
||||||
}.observes('controller.currentView')
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,11 +8,5 @@
|
|||||||
**/
|
**/
|
||||||
Discourse.NotActivatedView = Discourse.ModalBodyView.extend({
|
Discourse.NotActivatedView = Discourse.ModalBodyView.extend({
|
||||||
templateName: 'modal/not_activated',
|
templateName: 'modal/not_activated',
|
||||||
title: Em.String.i18n('log_in'),
|
title: Em.String.i18n('log_in')
|
||||||
emailSent: false,
|
|
||||||
|
|
||||||
sendActivationEmail: function() {
|
|
||||||
Discourse.ajax('/users/' + this.get('username') + '/send_activation_email');
|
|
||||||
this.set('emailSent', true);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
@ -8,40 +8,7 @@
|
|||||||
**/
|
**/
|
||||||
Discourse.SplitTopicView = Discourse.ModalBodyView.extend(Discourse.SelectedPostsCount, {
|
Discourse.SplitTopicView = Discourse.ModalBodyView.extend(Discourse.SelectedPostsCount, {
|
||||||
templateName: 'modal/split_topic',
|
templateName: 'modal/split_topic',
|
||||||
title: Em.String.i18n('topic.split_topic.title'),
|
title: Em.String.i18n('topic.split_topic.title')
|
||||||
saving: false,
|
|
||||||
|
|
||||||
buttonDisabled: function() {
|
|
||||||
if (this.get('saving')) return true;
|
|
||||||
return this.blank('topicName');
|
|
||||||
}.property('saving', 'topicName'),
|
|
||||||
|
|
||||||
buttonTitle: function() {
|
|
||||||
if (this.get('saving')) return Em.String.i18n('saving');
|
|
||||||
return Em.String.i18n('topic.split_topic.action');
|
|
||||||
}.property('saving'),
|
|
||||||
|
|
||||||
movePostsToNewTopic: function() {
|
|
||||||
this.set('saving', true);
|
|
||||||
|
|
||||||
var postIds = this.get('selectedPosts').map(function(p) { return p.get('id'); });
|
|
||||||
var moveSelectedView = this;
|
|
||||||
|
|
||||||
Discourse.Topic.movePosts(this.get('topic.id'), {
|
|
||||||
title: this.get('topicName'),
|
|
||||||
post_ids: postIds
|
|
||||||
}).then(function(result) {
|
|
||||||
// Posts moved
|
|
||||||
$('#discourse-modal').modal('hide');
|
|
||||||
moveSelectedView.get('topicController').toggleMultiSelect();
|
|
||||||
Em.run.next(function() { Discourse.URL.routeTo(result.url); });
|
|
||||||
}, function() {
|
|
||||||
// Error moving posts
|
|
||||||
moveSelectedView.flash(Em.String.i18n('topic.split_topic.error'));
|
|
||||||
moveSelectedView.set('saving', false);
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
/**
|
|
||||||
A modal view for displaying the ranking details of a topic
|
|
||||||
|
|
||||||
@class TopicRankDetailsView
|
|
||||||
@extends Discourse.ModalBodyView
|
|
||||||
@namespace Discourse
|
|
||||||
@module Discourse
|
|
||||||
**/
|
|
||||||
Discourse.TopicRankDetailsView = Discourse.ModalBodyView.extend({
|
|
||||||
templateName: 'modal/topic_rank_details',
|
|
||||||
title: Em.String.i18n('rank_details.title')
|
|
||||||
|
|
||||||
});
|
|
@ -113,7 +113,7 @@ Discourse.PostMenuView = Discourse.View.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
clickFlag: function() {
|
clickFlag: function() {
|
||||||
this.get('controller').showFlags(this.get('post'));
|
this.get('controller').send('showFlags', this.get('post'));
|
||||||
},
|
},
|
||||||
|
|
||||||
// Edit button
|
// Edit button
|
||||||
|
@ -38,7 +38,7 @@ Discourse.TopicFooterButtonsView = Ember.ContainerView.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
click: function() {
|
click: function() {
|
||||||
return this.get('controller').showInviteModal();
|
return this.get('controller').send('showInvite');
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@ -170,8 +170,7 @@ Discourse.TopicFooterButtonsView = Ember.ContainerView.extend({
|
|||||||
textKey: 'topic.login_reply',
|
textKey: 'topic.login_reply',
|
||||||
classNames: ['btn', 'btn-primary', 'create'],
|
classNames: ['btn', 'btn-primary', 'create'],
|
||||||
click: function() {
|
click: function() {
|
||||||
var _ref;
|
this.get('controller').send('showLogin');
|
||||||
return (_ref = this.get('controller.controllers.modal')) ? _ref.show(Discourse.LoginView.create()) : void 0;
|
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ class TopicList
|
|||||||
def has_rank_details?
|
def has_rank_details?
|
||||||
|
|
||||||
# Only moderators can see rank details
|
# Only moderators can see rank details
|
||||||
return false unless @current_user.try(:moderator?)
|
return false unless @current_user.try(:staff?)
|
||||||
|
|
||||||
# Only show them on 'Hot'
|
# Only show them on 'Hot'
|
||||||
return @filter == :hot
|
return @filter == :hot
|
||||||
|
Reference in New Issue
Block a user