diff --git a/app/assets/javascripts/discourse/controllers/edit_category_controller.js b/app/assets/javascripts/discourse/controllers/edit_category_controller.js
index 0686254af13..6be805503a9 100644
--- a/app/assets/javascripts/discourse/controllers/edit_category_controller.js
+++ b/app/assets/javascripts/discourse/controllers/edit_category_controller.js
@@ -13,6 +13,12 @@ Discourse.EditCategoryController = Discourse.ObjectController.extend(Discourse.M
settingsSelected: Ember.computed.equal('selectedTab', 'settings'),
foregroundColors: ['FFFFFF', '000000'],
+ parentCategories: function() {
+ return Discourse.Category.list().filter(function (c) {
+ return !c.get('parentCategory');
+ });
+ }.property(),
+
onShow: function() {
this.changeSize();
this.titleChanged();
@@ -122,17 +128,27 @@ Discourse.EditCategoryController = Discourse.ObjectController.extend(Discourse.M
},
saveCategory: function() {
- var categoryController = this;
+ var self = this,
+ model = this.get('model'),
+ parentCategory = Discourse.Category.list().findBy('id', parseInt(model.get('parent_category_id'), 10));
+
this.set('saving', true);
+ model.set('parentCategory', parentCategory);
+ var newSlug = Discourse.Category.slugFor(this.get('model'));
+
this.get('model').save().then(function(result) {
// success
- categoryController.send('closeModal');
- Discourse.URL.redirectTo("/category/" + Discourse.Category.slugFor(result.category));
- }, function(errors) {
- // errors
- if(errors.length === 0) errors.push(I18n.t("category.creation_error"));
- categoryController.displayErrors(errors);
- categoryController.set('saving', false);
+ self.send('closeModal');
+ Discourse.URL.redirectTo("/category/" + newSlug);
+ }, function(error) {
+
+ if (error && error.responseText) {
+ self.flash($.parseJSON(error.responseText).errors[0]);
+ } else {
+ self.flash(I18n.t('generic_error'));
+ }
+
+ self.set('saving', false);
});
},
@@ -147,8 +163,14 @@ Discourse.EditCategoryController = Discourse.ObjectController.extend(Discourse.M
// success
self.send('closeModal');
Discourse.URL.redirectTo("/categories");
- }, function(jqXHR){
- // error
+ }, function(error){
+
+ if (error && error.responseText) {
+ self.flash($.parseJSON(error.responseText).errors[0]);
+ } else {
+ self.flash(I18n.t('generic_error'));
+ }
+
self.send('showModal');
self.displayErrors([I18n.t("category.delete_error")]);
self.set('deleting', false);
diff --git a/app/assets/javascripts/discourse/models/category.js b/app/assets/javascripts/discourse/models/category.js
index cf32f9d93ff..3a2f3be31e7 100644
--- a/app/assets/javascripts/discourse/models/category.js
+++ b/app/assets/javascripts/discourse/models/category.js
@@ -58,7 +58,8 @@ Discourse.Category = Discourse.Model.extend({
secure: this.get('secure'),
permissions: this.get('permissionsForUpdate'),
auto_close_days: this.get('auto_close_days'),
- position: this.get('position')
+ position: this.get('position'),
+ parent_category_id: this.get('parent_category_id')
},
type: this.get('id') ? 'PUT' : 'POST'
});
diff --git a/app/assets/javascripts/discourse/templates/modal/edit_category.js.handlebars b/app/assets/javascripts/discourse/templates/modal/edit_category.js.handlebars
index e8bb4fd1522..55e43efad11 100644
--- a/app/assets/javascripts/discourse/templates/modal/edit_category.js.handlebars
+++ b/app/assets/javascripts/discourse/templates/modal/edit_category.js.handlebars
@@ -21,6 +21,11 @@
{{textField value=name placeholderKey="category.name_placeholder" maxlength="50"}}
+
+
+ {{categoryChooser valueAttribute="id" value=parent_category_id categories=parentCategories}}
+
+
{{#unless isUncategorized}}
diff --git a/app/assets/javascripts/discourse/views/category_chooser_view.js b/app/assets/javascripts/discourse/views/category_chooser_view.js
index c7448842620..005ff0c642a 100644
--- a/app/assets/javascripts/discourse/views/category_chooser_view.js
+++ b/app/assets/javascripts/discourse/views/category_chooser_view.js
@@ -12,14 +12,16 @@ Discourse.CategoryChooserView = Discourse.ComboboxView.extend({
dataAttributes: ['name', 'color', 'text_color', 'description_text', 'topic_count'],
valueBinding: Ember.Binding.oneWay('source'),
+ content: Em.computed.filter('categories', function(c) {
+ var uncategorized_id = Discourse.Site.currentProp("uncategorized_category_id");
+ return c.get('permission') === Discourse.PermissionType.FULL && c.get('id') !== uncategorized_id;
+ }),
+
init: function() {
this._super();
- // TODO perhaps allow passing a param in to select if we need full or not
-
- var uncategorized_id = Discourse.Site.currentProp("uncategorized_category_id");
- this.set('content', _.filter(Discourse.Category.list(), function(c){
- return c.permission === Discourse.PermissionType.FULL && c.id !== uncategorized_id;
- }));
+ if (!this.get('categories')) {
+ this.set('categories', Discourse.Category.list());
+ }
},
none: function() {
diff --git a/app/assets/javascripts/discourse/views/modal/modal_body_view.js b/app/assets/javascripts/discourse/views/modal/modal_body_view.js
index 77b8b6dfa6c..bfaceb5c586 100644
--- a/app/assets/javascripts/discourse/views/modal/modal_body_view.js
+++ b/app/assets/javascripts/discourse/views/modal/modal_body_view.js
@@ -27,12 +27,6 @@ Discourse.ModalBodyView = Discourse.View.extend({
}
},
- // Pass the errors to our errors view
- displayErrors: function(errors, callback) {
- this.set('parentView.parentView.modalErrorsView.errors', errors);
- if (typeof callback === "function") callback();
- },
-
flashMessageChanged: function() {
var flashMessage = this.get('controller.flashMessage');
if (flashMessage) {
diff --git a/app/assets/stylesheets/desktop/modal.scss b/app/assets/stylesheets/desktop/modal.scss
index a4999ad6034..03faf9ca6f8 100644
--- a/app/assets/stylesheets/desktop/modal.scss
+++ b/app/assets/stylesheets/desktop/modal.scss
@@ -164,6 +164,19 @@
border-bottom: 1px solid #bbb;
}
+
+ .category-combobox {
+ width: 430px;
+
+ .chzn-drop {
+ left: -9000px;
+ width: 428px;
+ }
+ .chzn-search input {
+ width: 378px;
+ }
+ }
+
&.hidden {
display: none;
}
diff --git a/app/controllers/categories_controller.rb b/app/controllers/categories_controller.rb
index 5110dc6d57b..6b0853a09b1 100644
--- a/app/controllers/categories_controller.rb
+++ b/app/controllers/categories_controller.rb
@@ -90,7 +90,7 @@ class CategoriesController < ApplicationController
end
end
- params.permit(*required_param_keys, :position, :hotness, :auto_close_days, :permissions => [*p.try(:keys)])
+ params.permit(*required_param_keys, :position, :hotness, :parent_category_id, :auto_close_days, :permissions => [*p.try(:keys)])
end
end
diff --git a/app/models/category.rb b/app/models/category.rb
index 77d56f9fc2f..4669168f51c 100644
--- a/app/models/category.rb
+++ b/app/models/category.rb
@@ -187,10 +187,10 @@ SQL
def parent_category_validator
if parent_category_id
- errors.add(:parent_category_id, "You can't link a category to itself") if parent_category_id == id
+ errors.add(:parent_category_id, I18n.t("category.errors.self_parent")) if parent_category_id == id
grandfather_id = Category.where(id: parent_category_id).pluck(:parent_category_id).first
- errors.add(:parent_category_id, "You can't have more than one level of subcategory") if grandfather_id
+ errors.add(:base, I18n.t("category.errors.depth")) if grandfather_id
end
end
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 74c9266bc24..8cb26e22965 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -975,6 +975,7 @@ en:
add_permission: "Add Permission"
this_year: "this year"
position: "position"
+ parent: "Parent Category"
flagging:
title: 'Why are you flagging this post?'
diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml
index e928e5daff6..6a6d576552c 100644
--- a/config/locales/server.en.yml
+++ b/config/locales/server.en.yml
@@ -159,7 +159,9 @@ en:
topic_prefix: "Category definition for %{category}"
replace_paragraph: "[Replace this first paragraph with a short description of your new category. This guidance will appear in the category selection area, so try to keep it below 200 characters.]"
post_template: "%{replace_paragraph}\n\nUse the following paragraphs for a longer description, as well as to establish any category guidelines or rules.\n\nSome things to consider in any discussion replies below:\n\n- What is this category for? Why should people select this category for their topic?\n\n- How is this different than the other categories we already have?\n\n- Do we need this category?\n\n- Should we merge this with another category, or split it into more categories?\n"
-
+ errors:
+ self_parent: "A subcategory's parent cannot be itself."
+ depth: "You can't nest a subcategory under another."
trust_levels:
newuser:
title: "new user"