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"