From 666264879c59c553f7898c41457b64e89eac2afe Mon Sep 17 00:00:00 2001 From: Sam <sam.saffron@gmail.com> Date: Thu, 24 Oct 2013 10:05:51 +1100 Subject: [PATCH] change it so all topics MUST include a category, we store a special uncategorized category to compensate this cleans up a bunch of internals and removes some settings --- .../discourse/components/utilities.js | 3 +- .../controllers/edit_category_controller.js | 44 ++++----------- .../discourse/helpers/application_helpers.js | 3 +- .../javascripts/discourse/models/category.js | 13 ----- .../discourse/models/category_list.js | 15 +---- .../templates/featured_topics.js.handlebars | 2 +- .../list/wide_categories.js.handlebars | 2 +- .../discourse/views/category_chooser_view.js | 4 +- app/controllers/list_controller.rb | 25 ++------- app/models/category.rb | 11 ++-- app/models/category_list.rb | 56 +------------------ app/models/post.rb | 2 +- app/models/site_setting.rb | 8 +-- app/models/topic.rb | 45 +++++++++------ .../category_detailed_serializer.rb | 2 +- app/serializers/site_serializer.rb | 8 +-- config/locales/client.cs.yml | 1 - config/locales/client.de.yml | 1 - config/locales/client.en.yml | 1 - config/locales/client.fr.yml | 1 - config/locales/client.it.yml | 1 - config/locales/client.ko.yml | 1 - config/locales/client.nb_NO.yml | 1 - config/locales/client.nl.yml | 1 - config/locales/client.pseudo.yml | 1 - config/locales/client.pt_BR.yml | 1 - config/locales/client.ru.yml | 1 - config/locales/client.zh_CN.yml | 1 - config/locales/client.zh_TW.yml | 1 - config/locales/server.cs.yml | 4 -- config/locales/server.da.yml | 1 - config/locales/server.de.yml | 4 -- config/locales/server.en.yml | 4 -- config/locales/server.es.yml | 1 - config/locales/server.fr.yml | 3 - config/locales/server.id.yml | 1 - config/locales/server.it.yml | 4 -- config/locales/server.ko.yml | 4 -- config/locales/server.nl.yml | 4 -- config/locales/server.pseudo.yml | 6 -- config/locales/server.pt.yml | 1 - config/locales/server.pt_BR.yml | 4 -- config/locales/server.ru.yml | 3 - config/locales/server.sv.yml | 2 - config/locales/server.zh_CN.yml | 2 - config/locales/server.zh_TW.yml | 2 - ...131022045114_add_uncategorized_category.rb | 35 ++++++++++++ lib/post_creator.rb | 1 - lib/site_setting_extension.rb | 29 ++++++---- lib/site_settings/local_process_provider.rb | 10 +++- lib/topic_query.rb | 16 +----- spec/components/category_list_spec.rb | 36 ++---------- spec/components/guardian_spec.rb | 7 ++- spec/components/topic_query_spec.rb | 6 +- spec/components/trashable_spec.rb | 8 +-- .../controllers/categories_controller_spec.rb | 2 +- spec/controllers/list_controller_spec.rb | 34 +---------- spec/fabricators/topic_fabricator.rb | 1 + spec/models/category_spec.rb | 31 +++++----- spec/models/post_mover_spec.rb | 2 +- spec/models/site_spec.rb | 10 +++- spec/models/topic_spec.rb | 13 ++--- spec/spec_helper.rb | 5 ++ 63 files changed, 183 insertions(+), 369 deletions(-) create mode 100644 db/migrate/20131022045114_add_uncategorized_category.rb diff --git a/app/assets/javascripts/discourse/components/utilities.js b/app/assets/javascripts/discourse/components/utilities.js index 3b930ac7b..eef2ed1e2 100644 --- a/app/assets/javascripts/discourse/components/utilities.js +++ b/app/assets/javascripts/discourse/components/utilities.js @@ -40,8 +40,9 @@ Discourse.Utilities = { @param {Discourse.Category} category the category whose link we want @returns {String} the html category badge **/ - categoryLink: function(category) { + categoryLink: function(category, allowUncategorized) { if (!category) return ""; + if (!allowUncategorized && Em.get(category, 'id') === Discourse.Site.currentProp("uncategorized_category_id")) return ""; var color = Em.get(category, 'color'), textColor = Em.get(category, 'text_color'), diff --git a/app/assets/javascripts/discourse/controllers/edit_category_controller.js b/app/assets/javascripts/discourse/controllers/edit_category_controller.js index 838ac0226..0686254af 100644 --- a/app/assets/javascripts/discourse/controllers/edit_category_controller.js +++ b/app/assets/javascripts/discourse/controllers/edit_category_controller.js @@ -30,9 +30,6 @@ Discourse.EditCategoryController = Discourse.ObjectController.extend(Discourse.M if (this.get('id')) { return I18n.t("category.edit_long") + " : " + this.get('model.name'); } - if (this.get('isUncategorized')){ - return I18n.t("category.edit_uncategorized"); - } return I18n.t("category.create") + (this.get('model.name') ? (" : " + this.get('model.name')) : ''); }.property('id', 'model.name'), @@ -127,37 +124,16 @@ Discourse.EditCategoryController = Discourse.ObjectController.extend(Discourse.M 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 - categoryController.send('closeModal'); - // 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(I18n.t("category.save_error")); - categoryController.displayErrors(errors); - categoryController.set('saving', false); - }); - } else { - 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); - }); - } + 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); + }); }, deleteCategory: function() { diff --git a/app/assets/javascripts/discourse/helpers/application_helpers.js b/app/assets/javascripts/discourse/helpers/application_helpers.js index 89a58dc90..e1c58b29d 100644 --- a/app/assets/javascripts/discourse/helpers/application_helpers.js +++ b/app/assets/javascripts/discourse/helpers/application_helpers.js @@ -42,8 +42,9 @@ Handlebars.registerHelper('topicLink', function(property, options) { @for Handlebars **/ Handlebars.registerHelper('categoryLink', function(property, options) { + var allowUncategorized = options.hash && options.hash.allowUncategorized; var category = Ember.Handlebars.get(this, property, options); - return new Handlebars.SafeString(Discourse.Utilities.categoryLink(category)); + return new Handlebars.SafeString(Discourse.Utilities.categoryLink(category, allowUncategorized)); }); diff --git a/app/assets/javascripts/discourse/models/category.js b/app/assets/javascripts/discourse/models/category.js index 793404853..499df75d3 100644 --- a/app/assets/javascripts/discourse/models/category.js +++ b/app/assets/javascripts/discourse/models/category.js @@ -117,19 +117,6 @@ Discourse.Category = Discourse.Model.extend({ Discourse.Category.reopenClass({ - uncategorizedInstance: function() { - if (this.uncategorized) return this.uncategorized; - - this.uncategorized = this.create({ - slug: 'uncategorized', - name: Discourse.SiteSettings.uncategorized_name, - isUncategorized: true, - color: Discourse.SiteSettings.uncategorized_color, - text_color: Discourse.SiteSettings.uncategorized_text_color - }); - return this.uncategorized; - }, - slugFor: function(category) { if (!category) return ""; diff --git a/app/assets/javascripts/discourse/models/category_list.js b/app/assets/javascripts/discourse/models/category_list.js index 24761a1c5..1f4b36af4 100644 --- a/app/assets/javascripts/discourse/models/category_list.js +++ b/app/assets/javascripts/discourse/models/category_list.js @@ -45,19 +45,8 @@ Discourse.CategoryList.reopenClass({ }); } - if (c.is_uncategorized) { - var uncategorized = Discourse.Category.uncategorizedInstance(); - uncategorized.setProperties({ - topics: c.topics, - featured_users: c.featured_users, - topics_week: c.topics_week, - topics_month: c.topics_month, - topics_year: c.topics_year - }); - categories.pushObject(uncategorized); - } else { - categories.pushObject(Discourse.Category.create(c)); - } + categories.pushObject(Discourse.Category.create(c)); + }); return categories; }, diff --git a/app/assets/javascripts/discourse/templates/featured_topics.js.handlebars b/app/assets/javascripts/discourse/templates/featured_topics.js.handlebars index 38c078350..4963365d3 100644 --- a/app/assets/javascripts/discourse/templates/featured_topics.js.handlebars +++ b/app/assets/javascripts/discourse/templates/featured_topics.js.handlebars @@ -1,7 +1,7 @@ <table id='topic-list'> <tr> <th class="main-link"> - {{categoryLink this}} + {{categoryLink this allowUncategorized=true}} <div class='posters'> {{#each featured_users}} diff --git a/app/assets/javascripts/discourse/templates/list/wide_categories.js.handlebars b/app/assets/javascripts/discourse/templates/list/wide_categories.js.handlebars index f1e499e2f..444d63133 100644 --- a/app/assets/javascripts/discourse/templates/list/wide_categories.js.handlebars +++ b/app/assets/javascripts/discourse/templates/list/wide_categories.js.handlebars @@ -20,7 +20,7 @@ {{#if controller.ordering}} <i class="icon-reorder"></i> {{/if}} - {{categoryLink this}} + {{categoryLink this allowUncategorized=true}} {{#if unreadTopics}} <a href={{unbound url}} class='badge new-posts badge-notification' title='{{i18n topic.unread_topics count="unreadTopics"}}'>{{unbound unreadTopics}}</a> {{/if}} diff --git a/app/assets/javascripts/discourse/views/category_chooser_view.js b/app/assets/javascripts/discourse/views/category_chooser_view.js index aba5c5afa..c74488426 100644 --- a/app/assets/javascripts/discourse/views/category_chooser_view.js +++ b/app/assets/javascripts/discourse/views/category_chooser_view.js @@ -15,8 +15,10 @@ Discourse.CategoryChooserView = Discourse.ComboboxView.extend({ 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; + return c.permission === Discourse.PermissionType.FULL && c.id !== uncategorized_id; })); }, diff --git a/app/controllers/list_controller.rb b/app/controllers/list_controller.rb index d9146bf59..07b15a5b7 100644 --- a/app/controllers/list_controller.rb +++ b/app/controllers/list_controller.rb @@ -49,18 +49,13 @@ class ListController < ApplicationController def category query = TopicQuery.new(current_user, page: params[:page]) - # If they choose uncategorized, return topics NOT in a category - if request_is_for_uncategorized? - list = query.list_uncategorized - else - if !@category - raise Discourse::NotFound - return - end - guardian.ensure_can_see!(@category) - list = query.list_category(@category) - @description = @category.description + if !@category + raise Discourse::NotFound + return end + guardian.ensure_can_see!(@category) + list = query.list_category(@category) + @description = @category.description if params[:parent_category].present? list.more_topics_url = url_for(category_list_parent_path(params[:parent_category], params[:category], page: next_page, format: "json")) @@ -72,8 +67,6 @@ class ListController < ApplicationController end def category_feed - raise Discourse::InvalidParameters.new('Category RSS of "uncategorized"') if request_is_for_uncategorized? - guardian.ensure_can_see!(@category) discourse_expires_in 1.minute @@ -137,12 +130,6 @@ class ListController < ApplicationController Category.where(id: slug.to_i, parent_category_id: parent_category_id).includes(:featured_users).first end - def request_is_for_uncategorized? - params[:category] == Slug.for(SiteSetting.uncategorized_name) || - params[:category] == SiteSetting.uncategorized_name || - params[:category] == 'uncategorized' - end - def build_topic_list_options # html format means we need to parse exclude category (aka filter) from the site options top menu menu_items = SiteSetting.top_menu_items diff --git a/app/models/category.rb b/app/models/category.rb index b0f322ca6..dd45d05d8 100644 --- a/app/models/category.rb +++ b/app/models/category.rb @@ -117,7 +117,7 @@ class Category < ActiveRecord::Base topics_with_post_count = Topic .select("topics.category_id, COUNT(*) topic_count, SUM(topics.posts_count) post_count") - .where("topics.id NOT IN (select cc.topic_id from categories cc)") + .where("topics.id NOT IN (select cc.topic_id from categories cc WHERE topic_id IS NOT NULL)") .group("topics.category_id") .visible.to_sql @@ -150,10 +150,11 @@ SQL end def create_category_definition - create_topic!(title: I18n.t("category.topic_prefix", category: name), user: user, pinned_at: Time.now) - update_column(:topic_id, topic.id) - topic.update_column(:category_id, id) - topic.posts.create(raw: post_template, user: user) + t = Topic.new(title: I18n.t("category.topic_prefix", category: name), user: user, pinned_at: Time.now, category_id: id) + t.skip_callbacks = true + t.save! + update_column(:topic_id, t.id) + t.posts.create(raw: post_template, user: user) end def topic_url diff --git a/app/models/category_list.rb b/app/models/category_list.rb index 04013cd45..9d9185e79 100644 --- a/app/models/category_list.rb +++ b/app/models/category_list.rb @@ -16,7 +16,6 @@ class CategoryList find_categories prune_empty - add_uncategorized find_user_data end @@ -91,63 +90,14 @@ class CategoryList end end - # Add the uncategorized "magic" category - # TODO: remove this entire hack, not needed - def add_uncategorized - # Support for uncategorized topics - uncategorized_topics = Topic - .listable_topics - .visible - .where(category_id: nil) - .topic_list_order - .limit(SiteSetting.category_featured_topics) - if uncategorized_topics.present? - - totals = Topic.exec_sql("SELECT SUM(CASE WHEN created_at >= (CURRENT_TIMESTAMP - INTERVAL '1 WEEK') THEN 1 ELSE 0 END) as topics_week, - SUM(CASE WHEN created_at >= (CURRENT_TIMESTAMP - INTERVAL '1 MONTH') THEN 1 ELSE 0 END) as topics_month, - SUM(CASE WHEN created_at >= (CURRENT_TIMESTAMP - INTERVAL '1 YEAR') THEN 1 ELSE 0 END) as topics_year, - COUNT(*) AS topic_count - FROM topics - WHERE topics.visible - AND topics.deleted_at IS NULL - AND topics.category_id IS NULL - AND topics.archetype <> '#{Archetype.private_message}'").first - - - uncategorized = Category.new({name: SiteSetting.uncategorized_name, - slug: Slug.for(SiteSetting.uncategorized_name), - color: SiteSetting.uncategorized_color, - text_color: SiteSetting.uncategorized_text_color, - featured_topics: uncategorized_topics}.merge(totals)) - - # Find the appropriate place to insert it: - insert_at = nil - - unless latest_post_only? - @categories.each_with_index do |c, idx| - if (uncategorized.topics_week || 0) > (c.topics_week || 0) - insert_at = idx - break - end - end - end - - @categories.insert(insert_at || @categories.size, uncategorized) - end - - if uncategorized.present? - @all_topics ||= [] - uncategorized.displayable_topics = uncategorized_topics - @all_topics << uncategorized_topics - @all_topics.flatten! - end - end # Remove any empty topics unless we can create them (so we can see the controls) def prune_empty unless @guardian.can_create?(Category) # Remove categories with no featured topics unless we have the ability to edit one - @categories.delete_if { |c| c.displayable_topics.blank? && c.description.nil? } + @categories.delete_if { |c| + c.displayable_topics.blank? && c.description.blank? + } end end diff --git a/app/models/post.rb b/app/models/post.rb index d1bfd3e6f..8e3f4d1c8 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -84,7 +84,7 @@ class Post < ActiveRecord::Base super update_flagged_posts_count TopicLink.extract_from(self) - if topic && topic.category_id + if topic && topic.category_id && topic.category topic.category.update_latest end end diff --git a/app/models/site_setting.rb b/app/models/site_setting.rb index 800126207..fa6e8a0c0 100644 --- a/app/models/site_setting.rb +++ b/app/models/site_setting.rb @@ -103,10 +103,6 @@ class SiteSetting < ActiveRecord::Base setting(:max_mentions_per_post, 10) setting(:newuser_max_mentions_per_post, 2) - client_setting(:uncategorized_name, 'uncategorized') - client_setting(:uncategorized_color, 'AB9364'); - client_setting(:uncategorized_text_color, 'FFFFFF'); - setting(:unique_posts_mins, Rails.env.test? ? 0 : 5) # Rate Limits @@ -267,11 +263,13 @@ class SiteSetting < ActiveRecord::Base setting(:max_daily_gravatar_crawls, 500) setting(:sequential_replies_threshold, 2) - client_setting(:enable_mobile_theme, true) setting(:dominating_topic_minimum_percent, 20) + # hidden setting only used by system + hidden_setting(:uncategorized_category_id, -1, hidden: true) + def self.call_discourse_hub? self.enforce_global_nicknames? && self.discourse_org_access_key.present? end diff --git a/app/models/topic.rb b/app/models/topic.rb index 0df39d989..619afa123 100644 --- a/app/models/topic.rb +++ b/app/models/topic.rb @@ -56,11 +56,12 @@ class Topic < ActiveRecord::Base :case_sensitive => false, :collection => Proc.new{ Topic.listable_topics } } - # The allow_uncategorized_topics site setting can be changed at any time, so there may be - # existing topics with nil category. We'll allow that, but when someone tries to make a new - # topic or change a topic's category, perform validation. - attr_accessor :do_category_validation - validates :category_id, :presence => { :if => Proc.new { @do_category_validation && !SiteSetting.allow_uncategorized_topics } } + validates :category_id, :presence => true ,:exclusion => {:in => [SiteSetting.uncategorized_category_id]}, + :if => Proc.new { |t| + (t.new_record? || t.category_id_changed?) && + !SiteSetting.allow_uncategorized_topics + } + before_validation do self.sanitize_title @@ -142,13 +143,16 @@ class Topic < ActiveRecord::Base before_create do self.bumped_at ||= Time.now self.last_post_user_id ||= user_id - self.do_category_validation = true if !@ignore_category_auto_close and self.category and self.category.auto_close_days and self.auto_close_at.nil? set_auto_close(self.category.auto_close_days) end end + attr_accessor :skip_callbacks + after_create do + return if skip_callbacks + changed_to_category(category) if archetype == Archetype.private_message DraftSequence.next!(user, Draft::NEW_PRIVATE_MESSAGE) @@ -158,14 +162,21 @@ class Topic < ActiveRecord::Base end before_save do + return if skip_callbacks + if (auto_close_at_changed? and !auto_close_at_was.nil?) or (auto_close_user_id_changed? and auto_close_at) self.auto_close_started_at ||= Time.zone.now if auto_close_at Jobs.cancel_scheduled_job(:close_topic, {topic_id: id}) true end + if category_id.nil? && (archetype.nil? || archetype == "regular") + self.category_id = SiteSetting.uncategorized_category_id + end end after_save do + return if skip_callbacks + if auto_close_at and (auto_close_at_changed? or auto_close_user_id_changed?) Jobs.enqueue_at(auto_close_at, :close_topic, {topic_id: id, user_id: auto_close_user_id || user_id}) end @@ -333,8 +344,13 @@ class Topic < ActiveRecord::Base Category.where(['id = ?', category_id]).update_all 'topic_count = topic_count - 1' end - self.category_id = cat.id - if save + success = true + if self.category_id != cat.id + self.category_id = cat.id + success = save + end + + if success CategoryFeaturedTopic.feature_topics_for(old_category) Category.where(id: cat.id).update_all 'topic_count = topic_count + 1' CategoryFeaturedTopic.feature_topics_for(cat) unless old_category.try(:id) == cat.try(:id) @@ -372,20 +388,15 @@ class Topic < ActiveRecord::Base # Changes the category to a new name def change_category(name) - self.do_category_validation = true - # If the category name is blank, reset the attribute if name.blank? - if category_id.present? - CategoryFeaturedTopic.feature_topics_for(category) - Category.where(id: category_id).update_all 'topic_count = topic_count - 1' - end - self.category_id = nil - return save + cat = Category.where(id: SiteSetting.uncategorized_category_id).first + else + cat = Category.where(name: name).first end - cat = Category.where(name: name).first return true if cat == category + return false unless cat changed_to_category(cat) end diff --git a/app/serializers/category_detailed_serializer.rb b/app/serializers/category_detailed_serializer.rb index b4b31b217..af695d350 100644 --- a/app/serializers/category_detailed_serializer.rb +++ b/app/serializers/category_detailed_serializer.rb @@ -23,7 +23,7 @@ class CategoryDetailedSerializer < BasicCategorySerializer end def is_uncategorized - name == SiteSetting.uncategorized_name + object.id == SiteSetting.uncategorized_category_id end def include_is_uncategorized? diff --git a/app/serializers/site_serializer.rb b/app/serializers/site_serializer.rb index d67f65c4e..2fe85fef2 100644 --- a/app/serializers/site_serializer.rb +++ b/app/serializers/site_serializer.rb @@ -3,8 +3,8 @@ class SiteSerializer < ApplicationSerializer attributes :default_archetype, :notification_types, :post_types, - :uncategorized_slug, - :group_names + :group_names, + :uncategorized_category_id # this is hidden so putting it here has_many :categories, serializer: BasicCategorySerializer, embed: :objects @@ -21,8 +21,8 @@ class SiteSerializer < ApplicationSerializer Post.types end - def uncategorized_slug - Slug.for(SiteSetting.uncategorized_name) + def uncategorized_category_id + SiteSetting.uncategorized_category_id end end diff --git a/config/locales/client.cs.yml b/config/locales/client.cs.yml index ac23b2ce0..7a9df917e 100644 --- a/config/locales/client.cs.yml +++ b/config/locales/client.cs.yml @@ -935,7 +935,6 @@ cs: none: '(bez kategorie)' edit: 'upravit' edit_long: "Upravit kategorii" - edit_uncategorized: "Upravit nekategorizované" view: 'Zobrazit témata v kategorii' general: 'Obecné' settings: 'Nastavení' diff --git a/config/locales/client.de.yml b/config/locales/client.de.yml index f901e7d48..4e25f34e5 100644 --- a/config/locales/client.de.yml +++ b/config/locales/client.de.yml @@ -925,7 +925,6 @@ de: none: '(keine Kategorie)' edit: 'Bearbeiten' edit_long: "Kategorie bearbeiten" - edit_uncategorized: "Unkategorisierte bearbeiten" view: 'Zeige Themen dieser Kategorie' general: 'Generell' settings: 'Einstellungen' diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 6ebb86129..74c9266bc 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -945,7 +945,6 @@ en: choose: 'Select a category…' edit: 'edit' edit_long: "Edit Category" - edit_uncategorized: "Edit Uncategorized" view: 'View Topics in Category' general: 'General' settings: 'Settings' diff --git a/config/locales/client.fr.yml b/config/locales/client.fr.yml index c0bcfb1a0..a373c82f4 100644 --- a/config/locales/client.fr.yml +++ b/config/locales/client.fr.yml @@ -831,7 +831,6 @@ fr: choose: 'Sélectionner une catégorie…' edit: 'éditer' edit_long: "Editer la catégorie" - edit_uncategorized: "Editer les sans catégorie" view: 'Voir les discussions dans cette catégorie' general: 'Général' settings: 'Paramètres' diff --git a/config/locales/client.it.yml b/config/locales/client.it.yml index 4c2016e33..0210be809 100644 --- a/config/locales/client.it.yml +++ b/config/locales/client.it.yml @@ -885,7 +885,6 @@ it: none: '(nessuna categoria)' edit: 'modifica' edit_long: "Modifica Categoria" - edit_uncategorized: "Modifica Non categorizzata" view: 'Mostra Topic nella Categoria' general: 'Generale' settings: 'Impostazioni' diff --git a/config/locales/client.ko.yml b/config/locales/client.ko.yml index df6e36b56..d3179c216 100644 --- a/config/locales/client.ko.yml +++ b/config/locales/client.ko.yml @@ -733,7 +733,6 @@ ko: none: '(카테고리 없음)' edit: '편집' edit_long: "카테고리 편집" - edit_uncategorized: "분류되지 않은 편집" view: '카테고리 항목보기' delete: '카테고리 지우기' create: '카테고리 만들기' diff --git a/config/locales/client.nb_NO.yml b/config/locales/client.nb_NO.yml index 94db1dfe1..ea9aa4f78 100644 --- a/config/locales/client.nb_NO.yml +++ b/config/locales/client.nb_NO.yml @@ -797,7 +797,6 @@ nb_NO: none: '(no category)' edit: 'rediger' edit_long: "Rediger Kategori" - edit_uncategorized: "Rediger Ukategorisert" view: 'Se Emner i Kategori' general: 'Generellt' settings: 'Innstillinger' diff --git a/config/locales/client.nl.yml b/config/locales/client.nl.yml index 3723aaa97..5ec41bb05 100644 --- a/config/locales/client.nl.yml +++ b/config/locales/client.nl.yml @@ -890,7 +890,6 @@ nl: none: (geen categorie) edit: bewerk edit_long: Bewerk categorie - edit_uncategorized: "Wijzig ongecategoriseerd" view: Bekijk topics in categorie general: Algemeen settings: Instellingen diff --git a/config/locales/client.pseudo.yml b/config/locales/client.pseudo.yml index 027e4a95f..62d1143e5 100644 --- a/config/locales/client.pseudo.yml +++ b/config/locales/client.pseudo.yml @@ -851,7 +851,6 @@ pseudo: none: '[[ (ɳó čáťéǧóřý) ]]' edit: '[[ éďíť ]]' edit_long: '[[ Éďíť Čáťéǧóřý ]]' - edit_uncategorized: '[[ Éďíť Ůɳčáťéǧóřížéď ]]' view: '[[ Ѷíéŵ Ťóƿíčš íɳ Čáťéǧóřý ]]' general: '[[ Ǧéɳéřáł ]]' settings: '[[ Šéťťíɳǧš ]]' diff --git a/config/locales/client.pt_BR.yml b/config/locales/client.pt_BR.yml index bb96ab792..6f1054292 100644 --- a/config/locales/client.pt_BR.yml +++ b/config/locales/client.pt_BR.yml @@ -944,7 +944,6 @@ pt_BR: choose: 'Selecione uma categoria…' edit: 'editar' edit_long: "Editar Categoria" - edit_uncategorized: "Editar Sem Categoria" view: 'Visualizar Tópicos na Categoria' general: 'Geral' settings: 'Configurações' diff --git a/config/locales/client.ru.yml b/config/locales/client.ru.yml index dbdf521af..3be78a6d7 100644 --- a/config/locales/client.ru.yml +++ b/config/locales/client.ru.yml @@ -942,7 +942,6 @@ ru: choose: 'Select a category…' edit: изменить edit_long: 'Изменить категорию' - edit_uncategorized: 'Изменить "Без категории"' view: 'Просмотр тем по категориям' general: Общие settings: Настройки diff --git a/config/locales/client.zh_CN.yml b/config/locales/client.zh_CN.yml index d00c75270..c9d67090e 100644 --- a/config/locales/client.zh_CN.yml +++ b/config/locales/client.zh_CN.yml @@ -945,7 +945,6 @@ zh_CN: choose: '选择分类……' edit: '编辑' edit_long: "编辑分类" - edit_uncategorized: "编辑未分类的" view: '浏览分类下的主题' general: '通常' settings: '设置' diff --git a/config/locales/client.zh_TW.yml b/config/locales/client.zh_TW.yml index a938eea40..560f1fe53 100644 --- a/config/locales/client.zh_TW.yml +++ b/config/locales/client.zh_TW.yml @@ -844,7 +844,6 @@ zh_TW: none: '(未分類)' edit: '編輯' edit_long: "編輯分類" - edit_uncategorized: "編輯未分類的" view: '浏覽分類下的主題' general: '通常' settings: '設置' diff --git a/config/locales/server.cs.yml b/config/locales/server.cs.yml index 3919deb65..b8a6648db 100644 --- a/config/locales/server.cs.yml +++ b/config/locales/server.cs.yml @@ -596,10 +596,6 @@ cs: active_user_rate_limit_secs: "Jak často aktualizujeme informaci o poslední návštěvě uživatelů, v sekundách" previous_visit_timeout_hours: "Kolik času musí uplynout v hodinách, než je návštěva uživatele považována za uplynulou" - uncategorized_name: "Výchozí kategorie pro témata, která nemají nastavenou kategorii" - uncategorized_color: "Barva pozadí štítku kategorie pro témata, která nemají nastavenou kategorii" - uncategorized_text_color: "Barva textu na štítku kategorie pro témata, která nemají nastavenou kategorii" - rate_limit_create_topic: "Počet sekund, které je nutné počkat od vytvoření tématu, než smí uživatel vytvořit další" rate_limit_create_post: "Počet sekund, které je nutné počkat od zaslání příspěvku, než smí uživatel zaslat další" diff --git a/config/locales/server.da.yml b/config/locales/server.da.yml index 5dfbecd0e..7dbced667 100644 --- a/config/locales/server.da.yml +++ b/config/locales/server.da.yml @@ -385,7 +385,6 @@ da: active_user_rate_limit_secs: "How frequently we update the 'last_seen_at' field, in seconds" previous_visit_timeout_hours: "How long a visit lasts before we consider it the 'previous' visit, in hours" - uncategorized_name: "The default category for topics that have no category in the /categories page" max_mentions_per_post: "Maximum number of @name notifications you can use in a single post" rate_limit_create_topic: "How many seconds, after creating a topic, before you can create another topic" diff --git a/config/locales/server.de.yml b/config/locales/server.de.yml index 5c6a088b6..888cf2bb8 100644 --- a/config/locales/server.de.yml +++ b/config/locales/server.de.yml @@ -570,10 +570,6 @@ de: active_user_rate_limit_secs: "Sekunden, nach denen das 'last_seen_at'-Feld aktualisiert wird." previous_visit_timeout_hours: "Stunden, die ein Besuch dauert bevor er als 'früherer' Besuch gezählt wird." - uncategorized_name: "Name der Standardkategorie für Themen, die keiner Kategorie zugeordnet wurden in der Kategorieübersicht /categories." - uncategorized_color: "Die Hintergrundfarbe der Plakette für die Kategorie der Themen welche keine Kategorie haben" - uncategorized_text_color: "Die Textfarbe der Plakette für die Kategorie der Themen welche keine Kategorie haben" - rate_limit_create_topic: "Sekunden Wartezeit nach Erstellung eines Themas, bevor man ein neues Thema erstellen kann." rate_limit_create_post: "Sekunden Wartezeit nach Erstellung eines Beitrags, bevor man einen neuen Beitrag erstellen kann." diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 9f6cec0fd..e928e5daf 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -607,10 +607,6 @@ en: active_user_rate_limit_secs: "How frequently we update the 'last_seen_at' field, in seconds" previous_visit_timeout_hours: "How long a visit lasts before we consider it the 'previous' visit, in hours" - uncategorized_name: "The default category for topics that have no category in the /categories page" - uncategorized_color: "The background color of the uncategorized topics category" - uncategorized_text_color: "The text color of the uncategorized topics category" - rate_limit_create_topic: "After creating a topic, users must wait this many seconds before they can create another topic" rate_limit_create_post: "After posting, users must wait this many seconds before they can create another post" diff --git a/config/locales/server.es.yml b/config/locales/server.es.yml index 1f0b97a5a..11dbcddd2 100644 --- a/config/locales/server.es.yml +++ b/config/locales/server.es.yml @@ -373,7 +373,6 @@ es: active_user_rate_limit_secs: "Como de frecuentemente actualizaremos el campo 'last_seen_at', en segundos" previous_visit_timeout_hours: "Cuanto tiempo debe pasar antes de que una visita sea considerada la 'visita previa', en horas" - uncategorized_name: "La categoría por defecto para los topics que no tienen categoría en la página /categories" max_mentions_per_post: "Maximo número de notificaciones @name que se pueden usar en un post" rate_limit_create_topic: "Cuantos segundos, después de crear un topic, deben pasar antes de poder crear otro topic" diff --git a/config/locales/server.fr.yml b/config/locales/server.fr.yml index c8501fc25..223e75f06 100644 --- a/config/locales/server.fr.yml +++ b/config/locales/server.fr.yml @@ -547,9 +547,6 @@ fr: allow_import: "Autoriser l'importation qui remplacera TOUTES les données du site. Laisser non coché, sauf si vous prévoyez d'importer des données." active_user_rate_limit_secs: "A quelle fréquence mettre à jour le champ 'dernier_vu_a', en secondes." previous_visit_timeout_hours: "Combien de temps dure une visite avant de la considérer comme la visite 'précédente', en heures." - uncategorized_name: "Catégorie par défaut pour les discussions sans catégorie sur la pages /categories" - uncategorized_color: "La couleur de fond de la catégorie des discussions sans catégorie" - uncategorized_text_color: "La couleur du texte de la catégorie des discussions sans catégorie" rate_limit_create_topic: "Après la création d'une discussion, les utilisateurs doivent attendre ce nombre de secondes avant de pouvoir créer une nouvelle discussion" rate_limit_create_post: "Après avoir posté un message, les utilisateurs doivent attendre ce nombre de secondes avant de pouvoir en poster un nouveau" max_likes_per_day: "Quantité maximale de J'aime qu'un utilisateur peut effectuer en un jour" diff --git a/config/locales/server.id.yml b/config/locales/server.id.yml index 2b6dfd00c..0ef901e28 100644 --- a/config/locales/server.id.yml +++ b/config/locales/server.id.yml @@ -384,7 +384,6 @@ id: active_user_rate_limit_secs: "How frequently we update the 'last_seen_at' field, in seconds" previous_visit_timeout_hours: "How long a visit lasts before we consider it the 'previous' visit, in hours" - uncategorized_name: "The default category for topics that have no category in the /categories page" max_mentions_per_post: "Maximum number of @name notifications you can use in a single post" rate_limit_create_topic: "How many seconds, after creating a topic, before you can create another topic" diff --git a/config/locales/server.it.yml b/config/locales/server.it.yml index ef70f655e..cb150a790 100644 --- a/config/locales/server.it.yml +++ b/config/locales/server.it.yml @@ -506,10 +506,6 @@ it: active_user_rate_limit_secs: "Quanto frequentemente viene aggiornato il campo 'last_seen_at' field, in secondi" previous_visit_timeout_hours: "Durata di una visita prima che venga considerata la visita 'precedente', in ore" - uncategorized_name: "La categoria di default per i topic non categorizzati nella pagina /categories" - uncategorized_color: "Il colore di sfondo del badge per la categoria con i topic privi di categoria" - uncategorized_text_color: "Il colore del testo del badge per la categoria con i topic privi di categoria" - rate_limit_create_topic: "Quanti secondi, dopo aver creato un topic, per poter creare un nuovo topic" rate_limit_create_post: "Quanti secondi, dopo aver creato un post, per poter creare un nuovo post" diff --git a/config/locales/server.ko.yml b/config/locales/server.ko.yml index 1fdd99e83..4f8ab54a2 100644 --- a/config/locales/server.ko.yml +++ b/config/locales/server.ko.yml @@ -506,10 +506,6 @@ ko: active_user_rate_limit_secs: "How frequently we update the 'last_seen_at' field, in seconds" previous_visit_timeout_hours: "How long a visit lasts before we consider it the 'previous' visit, in hours" - uncategorized_name: "The default category for topics that have no category in the /categories page" - uncategorized_color: "The background color of the badge for the category with topics that have no category" - uncategorized_text_color: "The text color of the badge for the category with topics that have no category" - rate_limit_create_topic: "How many seconds, after creating a topic, before you can create another topic" rate_limit_create_post: "How many seconds, after creating a post, before you can create another post" diff --git a/config/locales/server.nl.yml b/config/locales/server.nl.yml index e4fdbac8f..edac72a3f 100644 --- a/config/locales/server.nl.yml +++ b/config/locales/server.nl.yml @@ -570,10 +570,6 @@ nl: active_user_rate_limit_secs: "Hoe vaak we het 'last_seen_at'-veld updaten, in seconden." previous_visit_timeout_hours: "Hoe lang een bezoek duurt voordat we het als het 'vorige' bezoek beschouwen, in uren." - uncategorized_name: De naam voor ongecategoriseerde topics in de categorielijst - uncategorized_color: De achtergrondkleur van de badge voor de categorie met topics zonder categorie - uncategorized_text_color: De tekstkleur van de badge voor de categorie met topics zonder categorie - rate_limit_create_topic: Hoeveel seconden voordat je een ander topic kan aanmaken rate_limit_create_post: Hoeveel seconden voordat je een ander bericht kan aanmaken diff --git a/config/locales/server.pseudo.yml b/config/locales/server.pseudo.yml index a24e1ec58..521b3e10c 100644 --- a/config/locales/server.pseudo.yml +++ b/config/locales/server.pseudo.yml @@ -685,12 +685,6 @@ pseudo: ƒíéłď, íɳ šéčóɳďš ]]' previous_visit_timeout_hours: '[[ Ĥóŵ łóɳǧ á νíšíť łášťš ƀéƒóřé ŵé čóɳšíďéř íť ťĥé ''ƿřéνíóůš'' νíšíť, íɳ ĥóůřš ]]' - uncategorized_name: '[[ Ťĥé ďéƒáůłť čáťéǧóřý ƒóř ťóƿíčš ťĥáť ĥáνé ɳó čáťéǧóřý - íɳ ťĥé /čáťéǧóříéš ƿáǧé ]]' - uncategorized_color: '[[ Ťĥé ƀáčǩǧřóůɳď čółóř óƒ ťĥé ƀáďǧé ƒóř ťĥé čáťéǧóřý ŵíťĥ - ťóƿíčš ťĥáť ĥáνé ɳó čáťéǧóřý ]]' - uncategorized_text_color: '[[ Ťĥé ťéхť čółóř óƒ ťĥé ƀáďǧé ƒóř ťĥé čáťéǧóřý ŵíťĥ - ťóƿíčš ťĥáť ĥáνé ɳó čáťéǧóřý ]]' rate_limit_create_topic: '[[ Ĥóŵ ɱáɳý šéčóɳďš, áƒťéř čřéáťíɳǧ á ťóƿíč, ƀéƒóřé ýóů čáɳ čřéáťé áɳóťĥéř ťóƿíč ]]' rate_limit_create_post: '[[ Ĥóŵ ɱáɳý šéčóɳďš, áƒťéř čřéáťíɳǧ á ƿóšť, ƀéƒóřé ýóů diff --git a/config/locales/server.pt.yml b/config/locales/server.pt.yml index 9267a851c..405673266 100644 --- a/config/locales/server.pt.yml +++ b/config/locales/server.pt.yml @@ -313,7 +313,6 @@ pt: active_user_rate_limit_secs: "How frequently we update the 'last_seen_at' field, in seconds." previous_visit_timeout_hours: "How long a visit lasts before we consider it the 'previous' visit, in hours." - uncategorized_name: "The name for the uncategorized topics on the category list" max_mentions_per_post: "The maximum amount of @notifications you can add to a post" rate_limit_create_topic: "How many seconds before you can create another topic" diff --git a/config/locales/server.pt_BR.yml b/config/locales/server.pt_BR.yml index 51f27661d..28f026a18 100644 --- a/config/locales/server.pt_BR.yml +++ b/config/locales/server.pt_BR.yml @@ -601,10 +601,6 @@ pt_BR: active_user_rate_limit_secs: "Qual a frequencia de atualização do campo 'última vez visto em', em segundos." previous_visit_timeout_hours: "Quanto tempo uma visita dura antes de considerarmos como 'última visita', em horas." - uncategorized_name: "Nome para tópicos sem categoria na lista de categorias" - uncategorized_color: "Cor de fundo da etiqueta da categoria que tem tópicos sem nenhuma categoria" - uncategorized_text_color: "Cor do texto da etiqueta da categoria que tem tópicos sem nenhuma categoria" - rate_limit_create_topic: "Quantos segundos você precisa aguardar antes de poder criar um novo tópico" rate_limit_create_post: "Quantos segundos você precisa aguardar antes de poder fazer uma nova postagem" diff --git a/config/locales/server.ru.yml b/config/locales/server.ru.yml index ddfc0b385..557046da7 100644 --- a/config/locales/server.ru.yml +++ b/config/locales/server.ru.yml @@ -568,9 +568,6 @@ ru: allow_import: 'Позволить импорт, который может заменить ВСЕ данные сайта. Оставьте false, если не планируете импортировать данные' active_user_rate_limit_secs: 'Как часто мы обновляем поле ''last_seen_at'', в секундах' previous_visit_timeout_hours: 'Как долго должно длиться посещение сайта, чтобы мы посчитали его «предыдущим посещением», в часах' - uncategorized_name: 'Категория по умолчанию для тем, которые не отнесены ни к одной категории на странице /categories' - uncategorized_color: 'Фоновый цвет категории с темами без установленной категории' - uncategorized_text_color: 'Цвет текста категории с темами без установленной категории' rate_limit_create_topic: 'После создания темы пользователи должны выждать указанное количество секунд перед созданием новой темы' rate_limit_create_post: 'После создания сообщения пользователи должны выждать указанное количество секунд перед созданием нового сообщения' max_likes_per_day: 'Максимальное количество симпатий, выраженных одним пользователем в день' diff --git a/config/locales/server.sv.yml b/config/locales/server.sv.yml index ed9bf4c30..4794ce069 100644 --- a/config/locales/server.sv.yml +++ b/config/locales/server.sv.yml @@ -428,8 +428,6 @@ sv: active_user_rate_limit_secs: "How frequently we update the 'last_seen_at' field, in seconds" previous_visit_timeout_hours: "How long a visit lasts before we consider it the 'previous' visit, in hours" - uncategorized_name: "The default category for topics that have no category in the /categories page" - rate_limit_create_topic: "How many seconds, after creating a topic, before you can create another topic" rate_limit_create_post: "How many seconds, after creating a post, before you can create another post" diff --git a/config/locales/server.zh_CN.yml b/config/locales/server.zh_CN.yml index 3d1ab05b2..5b6ac26ac 100644 --- a/config/locales/server.zh_CN.yml +++ b/config/locales/server.zh_CN.yml @@ -525,8 +525,6 @@ zh_CN: active_user_rate_limit_secs: "更新“最后一次见到”数据的频率,单位为秒" previous_visit_timeout_hours: "系统判断一次访问之后多少小时后为“上一次”访问" - uncategorized_name: "在分类 /categories 页面,没有分类的主题的缺省分类" - rate_limit_create_topic: "在创建一个主题之后间隔多少秒你才能创建另一个主题" rate_limit_create_post: "在创建一个帖子之后间隔多少秒你才能创建另一个帖子" diff --git a/config/locales/server.zh_TW.yml b/config/locales/server.zh_TW.yml index 485a6f4de..2e3c53b6b 100644 --- a/config/locales/server.zh_TW.yml +++ b/config/locales/server.zh_TW.yml @@ -506,8 +506,6 @@ zh_TW: active_user_rate_limit_secs: "更新“最後一次見到”數據的頻率,單位爲秒" previous_visit_timeout_hours: "系統判斷一次訪問之後多少小時後爲“上一次”訪問" - uncategorized_name: "在分類 /categories 頁面,沒有分類的主題的缺省分類" - rate_limit_create_topic: "在創建一個主題之後間隔多少秒你才能創建另一個主題" rate_limit_create_post: "在創建一個帖子之後間隔多少秒你才能創建另一個帖子" diff --git a/db/migrate/20131022045114_add_uncategorized_category.rb b/db/migrate/20131022045114_add_uncategorized_category.rb new file mode 100644 index 000000000..d8416d916 --- /dev/null +++ b/db/migrate/20131022045114_add_uncategorized_category.rb @@ -0,0 +1,35 @@ +class AddUncategorizedCategory < ActiveRecord::Migration + def up + + result = execute "SELECT 1 FROM categories WHERE name = 'uncategorized'" + if result.count > 0 + name << SecureRandom.hex + end + + + result = execute "INSERT INTO categories + (name,color,slug,description,text_color, user_id, created_at, updated_at, position) + VALUES ('uncategorized', 'AB9364', 'uncategorized', '', 'FFFFFF', -1, now(), now(), 1 ) + RETURNING id + " + category_id = result[0]["id"].to_i + + execute "INSERT INTO site_settings(name, data_type, value, created_at, updated_at) + VALUES ('uncategorized_category_id', 3, #{category_id}, now(), now())" + + + execute "DELETE from site_settings where name in ('uncategorized_name', 'uncategorized_text_color', 'uncategorized_color')" + + execute "UPDATE topics SET category_id = #{category_id} WHERE archetype = 'regular' AND category_id IS NULL" + + execute "ALTER table topics ADD CONSTRAINT has_category_id CHECK (category_id IS NOT NULL OR archetype <> 'regular')" + + end + + def down + execute "ALTER TABLE topics DROP CONSTRAINT has_category_id" + execute "DELETE from categories WHERE id in (select value::int from site_settings where name = 'uncategorized_category_id')" + execute "DELETE from site_settings where name = 'uncategorized_category_id'" + execute "UPDATE topics SET category_id = null WHERE category_id NOT IN (SELECT id from categories)" + end +end diff --git a/lib/post_creator.rb b/lib/post_creator.rb index 1279048d8..559c3e8d9 100644 --- a/lib/post_creator.rb +++ b/lib/post_creator.rb @@ -140,7 +140,6 @@ class PostCreator end def after_topic_create - # Don't publish invisible topics return unless @topic.visible? diff --git a/lib/site_setting_extension.rb b/lib/site_setting_extension.rb index 1b45dd5d8..2c33d2350 100644 --- a/lib/site_setting_extension.rb +++ b/lib/site_setting_extension.rb @@ -34,6 +34,10 @@ module SiteSettingExtension @enums ||= {} end + def hidden_settings + @hidden_settings ||= [] + end + def setting(name, default = nil, opts = {}) mutex.synchronize do self.defaults[name] = default @@ -42,6 +46,9 @@ module SiteSettingExtension enum = opts[:enum] enums[name] = enum.is_a?(String) ? enum.constantize : enum end + if opts[:hidden] == true + hidden_settings << name + end setup_methods(name, current_value) end end @@ -76,16 +83,18 @@ module SiteSettingExtension end # Retrieve all settings - def all_settings - @defaults.map do |s, v| - value = send(s) - type = types[get_data_type(s, value)] - {setting: s, - description: description(s), - default: v, - type: type.to_s, - value: value.to_s}.merge( type == :enum ? {valid_values: enum_class(s).values, translate_names: enum_class(s).translate_names?} : {}) - end + def all_settings(include_hidden=false) + @defaults + .reject{|s, v| hidden_settings.include?(s) || include_hidden} + .map do |s, v| + value = send(s) + type = types[get_data_type(s, value)] + {setting: s, + description: description(s), + default: v, + type: type.to_s, + value: value.to_s}.merge( type == :enum ? {valid_values: enum_class(s).values, translate_names: enum_class(s).translate_names?} : {}) + end end def description(setting) diff --git a/lib/site_settings/local_process_provider.rb b/lib/site_settings/local_process_provider.rb index 10532a6f4..1ab5e42c1 100644 --- a/lib/site_settings/local_process_provider.rb +++ b/lib/site_settings/local_process_provider.rb @@ -4,16 +4,20 @@ class SiteSettings::LocalProcessProvider Setting = Struct.new(:name, :value, :data_type) unless defined? SiteSettings::LocalProcessProvider::Setting - def initialize + def initialize(defaults = {}) @settings = {} + @defaults = {} + defaults.each do |name,(value,data_type)| + @defaults[name] = Setting.new(name,value,data_type) + end end def all - @settings.values + (@defaults.merge @settings).values end def find(name) - @settings[name] + @settings[name] || @defaults[name] end def save(name, value, data_type) diff --git a/lib/topic_query.rb b/lib/topic_query.rb index c31dc24eb..b6b442218 100644 --- a/lib/topic_query.rb +++ b/lib/topic_query.rb @@ -50,7 +50,7 @@ class TopicQuery # If you've clearned the pin, use bumped_at, otherwise put it at the top def order_nocategory_with_pinned_sql "CASE - WHEN topics.category_id IS NULL and (COALESCE(topics.pinned_at, '#{lowest_date}') > COALESCE(tu.cleared_pinned_at, '#{lowest_date}')) + WHEN topics.category_id = #{SiteSetting.uncategorized_category_id.to_i} and (COALESCE(topics.pinned_at, '#{lowest_date}') > COALESCE(tu.cleared_pinned_at, '#{lowest_date}')) THEN '#{highest_date}' ELSE topics.bumped_at END DESC" @@ -58,7 +58,7 @@ class TopicQuery # For anonymous users def order_nocategory_basic_bumped - "CASE WHEN topics.category_id IS NULL and (topics.pinned_at IS NOT NULL) THEN 0 ELSE 1 END, topics.bumped_at DESC" + "CASE WHEN topics.category_id = #{SiteSetting.uncategorized_category_id.to_i} and (topics.pinned_at IS NOT NULL) THEN 0 ELSE 1 END, topics.bumped_at DESC" end def order_basic_bumped @@ -152,18 +152,6 @@ class TopicQuery TopicList.new(:private_messages, user, list) end - def list_uncategorized - create_list(:uncategorized, unordered: true) do |list| - list = list.where(category_id: nil) - - if @user - list.order(TopicQuery.order_with_pinned_sql) - else - list.order(TopicQuery.order_nocategory_basic_bumped) - end - end - end - def list_category(category) create_list(:category, unordered: true) do |list| list = list.where(category_id: category.id) diff --git a/spec/components/category_list_spec.rb b/spec/components/category_list_spec.rb index ef27f65e1..97f8f8d17 100644 --- a/spec/components/category_list_spec.rb +++ b/spec/components/category_list_spec.rb @@ -6,35 +6,6 @@ describe CategoryList do let(:user) { Fabricate(:user) } let(:category_list) { CategoryList.new(Guardian.new user) } - context "with no categories" do - - it "has no categories" do - category_list.categories.should be_blank - end - - context "with an uncategorized topic" do - let!(:topic) { Fabricate(:topic)} - let(:category) { category_list.categories.first } - - it "has the right category" do - category.should be_present - category.name.should == SiteSetting.uncategorized_name - category.slug.should == SiteSetting.uncategorized_name - category.topics_week.should == 1 - category.featured_topics.should == [topic] - category.displayable_topics.should == [topic] # CategoryDetailedSerializer needs this attribute - end - - it 'does not return an invisible topic' do - invisible_topic = Fabricate(:topic) - invisible_topic.update_status('visible', false, Fabricate(:admin)) - expect(category.featured_topics).to_not include(invisible_topic) - end - - end - - end - context "security" do it "properly hide secure categories" do admin = Fabricate(:admin) @@ -45,7 +16,9 @@ describe CategoryList do cat.set_permissions(:admins => :full) cat.save - CategoryList.new(Guardian.new admin).categories.count.should == 1 + # uncategorized + this + CategoryList.new(Guardian.new admin).categories.count.should == 2 + CategoryList.new(Guardian.new user).categories.count.should == 0 CategoryList.new(Guardian.new nil).categories.count.should == 0 end @@ -75,13 +48,12 @@ describe CategoryList do it 'returns the empty category and a non-empty category for those who can create them' do category_with_topics = Fabricate(:topic, category: Fabricate(:category)) Guardian.any_instance.expects(:can_create?).with(Category).returns(true) - category_list.categories.should have(2).categories + category_list.categories.should have(3).categories category_list.categories.should include(topic_category) end end - context "with a topic in a category" do let!(:topic) { Fabricate(:topic, category: topic_category)} let(:category) { category_list.categories.first } diff --git a/spec/components/guardian_spec.rb b/spec/components/guardian_spec.rb index 065035569..e9e60c173 100644 --- a/spec/components/guardian_spec.rb +++ b/spec/components/guardian_spec.rb @@ -687,9 +687,6 @@ describe Guardian do end - - - context 'can_delete?' do it 'returns false with a nil object' do @@ -697,6 +694,10 @@ describe Guardian do end context 'a Topic' do + before do + # pretend we have a real topic + topic.id = 9999999 + end it 'returns false when not logged in' do Guardian.new.can_delete?(topic).should be_false diff --git a/spec/components/topic_query_spec.rb b/spec/components/topic_query_spec.rb index 1b44d360d..3cd82bef2 100644 --- a/spec/components/topic_query_spec.rb +++ b/spec/components/topic_query_spec.rb @@ -49,7 +49,7 @@ describe TopicQuery do context 'list_latest' do it "returns the topics in the correct order" do - topics.should == [pinned_topic, closed_topic, archived_topic, regular_topic] + topics.map(&:title).should == [pinned_topic, closed_topic, archived_topic, regular_topic].map(&:title) end it "includes the invisible topic if you're a moderator" do @@ -80,10 +80,6 @@ describe TopicQuery do let!(:topic_no_cat) { Fabricate(:topic) } let!(:topic_in_cat) { Fabricate(:topic, category: category) } - it "returns the topic without a category when filtering uncategorized" do - topic_query.list_uncategorized.topics.should == [topic_no_cat] - end - it "returns the topic with a category when filtering by category" do topic_query.list_category(category).topics.should == [topic_category, topic_in_cat] end diff --git a/spec/components/trashable_spec.rb b/spec/components/trashable_spec.rb index 7a0cca945..779816f76 100644 --- a/spec/components/trashable_spec.rb +++ b/spec/components/trashable_spec.rb @@ -7,12 +7,8 @@ describe Trashable do p1 = Fabricate(:post) p2 = Fabricate(:post) - Post.count.should == 2 - p1.trash! - - Post.count.should == 1 - - Post.with_deleted.count.should == 2 + expect { p1.trash! }.to change{Post.count}.by(-1) + Post.with_deleted.count.should == Post.count + 1 end end diff --git a/spec/controllers/categories_controller_spec.rb b/spec/controllers/categories_controller_spec.rb index 7ca122bef..fead81681 100644 --- a/spec/controllers/categories_controller_spec.rb +++ b/spec/controllers/categories_controller_spec.rb @@ -58,7 +58,7 @@ describe CategoriesController do } response.status.should == 200 - category = Category.first + category = Category.where(name: "hello").first category.category_groups.map{|g| [g.group_id, g.permission_type]}.sort.should == [ [Group[:everyone].id, readonly],[Group[:staff].id,create_post] ] diff --git a/spec/controllers/list_controller_spec.rb b/spec/controllers/list_controller_spec.rb index 5254ba508..5994e0db8 100644 --- a/spec/controllers/list_controller_spec.rb +++ b/spec/controllers/list_controller_spec.rb @@ -27,7 +27,8 @@ describe ListController do end it 'allows users to filter on a set of topic ids' do - p = Fabricate(:post) + p = create_post + xhr :get, :latest, format: :json, topic_ids: "#{p.topic_id}" response.should be_success parsed = JSON.parse(response.body) @@ -128,38 +129,7 @@ describe ListController do response.content_type.should == 'application/rss+xml' end end - end - - context 'uncategorized' do - - it "doesn't check access to see the category, since we didn't provide one" do - Guardian.any_instance.expects(:can_see?).never - xhr :get, :category, category: SiteSetting.uncategorized_name - end - - it "responds with success" do - xhr :get, :category, category: SiteSetting.uncategorized_name - response.should be_success - end - - context 'SiteSetting.uncategorized_name is non standard' do - before do - SiteSetting.stubs(:uncategorized_name).returns('testing') - end - - it "responds with success given SiteSetting.uncategorized_name" do - xhr :get, :category, category: SiteSetting.uncategorized_name - response.should be_success - end - - it 'responds with success given "uncategorized"' do - xhr :get, :category, category: 'uncategorized' - response.should be_success - end - end - end - end describe "topics_by" do diff --git a/spec/fabricators/topic_fabricator.rb b/spec/fabricators/topic_fabricator.rb index 0a2e16ba1..c05fe83f0 100644 --- a/spec/fabricators/topic_fabricator.rb +++ b/spec/fabricators/topic_fabricator.rb @@ -1,6 +1,7 @@ Fabricator(:topic) do user title { sequence(:title) { |i| "This is a test topic #{i}" } } + category_id { SiteSetting.uncategorized_category_id } end Fabricator(:deleted_topic, from: :topic) do diff --git a/spec/models/category_spec.rb b/spec/models/category_spec.rb index a58eb8357..10867eab7 100644 --- a/spec/models/category_spec.rb +++ b/spec/models/category_spec.rb @@ -31,6 +31,9 @@ describe Category do describe "topic_create_allowed and post_create_allowed" do it "works" do + + # NOTE we also have the uncategorized category ... hence the increased count + default_category = Fabricate(:category) full_category = Fabricate(:category) can_post_category = Fabricate(:category) @@ -54,20 +57,20 @@ describe Category do can_read_category.save guardian = Guardian.new(admin) - Category.topic_create_allowed(guardian).count.should == 4 - Category.post_create_allowed(guardian).count.should == 4 - Category.secured(guardian).count.should == 4 + Category.topic_create_allowed(guardian).count.should == 5 + Category.post_create_allowed(guardian).count.should == 5 + Category.secured(guardian).count.should == 5 guardian = Guardian.new(user) - Category.secured(guardian).count.should == 4 - Category.post_create_allowed(guardian).count.should == 3 - Category.topic_create_allowed(guardian).count.should == 2 # explicitly allowed once, default allowed once + Category.secured(guardian).count.should == 5 + Category.post_create_allowed(guardian).count.should == 4 + Category.topic_create_allowed(guardian).count.should == 3 # explicitly allowed once, default allowed once # everyone has special semantics, test it as well can_post_category.set_permissions(:everyone => :create_post) can_post_category.save - Category.post_create_allowed(guardian).count.should == 3 + Category.post_create_allowed(guardian).count.should == 4 # anonymous has permission to create no topics guardian = Guardian.new(nil) @@ -105,22 +108,16 @@ describe Category do end it "lists all secured categories correctly" do + uncategorized = Category.first + group.add(user) category.set_permissions(group.id => :full) category.save category_2.set_permissions(group.id => :full) category_2.save - Category.secured.should =~ [] - Category.secured(Guardian.new(user)).should =~ [category, category_2] - end - end - - describe "uncategorized name" do - let(:category) { Fabricate.build(:category, name: SiteSetting.uncategorized_name) } - - it "is invalid to create a category with the reserved name" do - category.should_not be_valid + Category.secured.should =~ [uncategorized] + Category.secured(Guardian.new(user)).should =~ [uncategorized,category, category_2] end end diff --git a/spec/models/post_mover_spec.rb b/spec/models/post_mover_spec.rb index 605e2b24a..5ae21949d 100644 --- a/spec/models/post_mover_spec.rb +++ b/spec/models/post_mover_spec.rb @@ -113,7 +113,7 @@ describe PostMover do moved_to.highest_post_number.should == 3 moved_to.featured_user1_id.should == another_user.id moved_to.like_count.should == 1 - moved_to.category.should be_blank + moved_to.category_id.should == SiteSetting.uncategorized_category_id # Posts should be re-ordered p2.reload diff --git a/spec/models/site_spec.rb b/spec/models/site_spec.rb index 771400e74..63ed9a7d8 100644 --- a/spec/models/site_spec.rb +++ b/spec/models/site_spec.rb @@ -6,12 +6,16 @@ describe Site do category = Fabricate(:category) user = Fabricate(:user) - Site.new(Guardian.new(user)).categories.count.should == 1 + Site.new(Guardian.new(user)).categories.count.should == 2 category.set_permissions(:everyone => :create_post) category.save - # TODO clean up querying so we can make sure we have the correct permission set - Site.new(Guardian.new(user)).categories[0].permission.should_not == CategoryGroup.permission_types[:full] + Site.new(Guardian.new(user)) + .categories + .keep_if{|c| c.name == category.name} + .first + .permission + .should_not == CategoryGroup.permission_types[:full] end end diff --git a/spec/models/topic_spec.rb b/spec/models/topic_spec.rb index c2f6864a1..92fb51d5b 100644 --- a/spec/models/topic_spec.rb +++ b/spec/models/topic_spec.rb @@ -171,8 +171,6 @@ describe Topic do topic.fancy_title.should == "“this topic” – has “fancy stuff”" end end - - end context 'category validation' do @@ -182,7 +180,7 @@ describe Topic do end it "does not allow nil category" do - topic = Fabricate(:topic, category: nil) + topic = Fabricate.build(:topic, category: nil) topic.should_not be_valid topic.errors[:category_id].should be_present end @@ -796,7 +794,7 @@ describe Topic do describe 'without a previous category' do it 'should not change the topic_count when not changed' do - lambda { @topic.change_category(nil); @category.reload }.should_not change(@category, :topic_count) + lambda { @topic.change_category(@topic.category.name); @category.reload }.should_not change(@category, :topic_count) end describe 'changed category' do @@ -812,10 +810,9 @@ describe Topic do end - it "doesn't change the category when it can't be found" do @topic.change_category('made up') - @topic.category.should be_blank + @topic.category_id.should == SiteSetting.uncategorized_category_id end end @@ -876,7 +873,7 @@ describe Topic do end it "resets the category" do - @topic.category_id.should be_blank + @topic.category_id.should == SiteSetting.uncategorized_category_id @category.topic_count.should == 0 end end @@ -955,7 +952,7 @@ describe Topic do it "ignores the category's default auto-close" do Timecop.freeze(Time.zone.now) do Jobs.expects(:enqueue_at).with(7.days.from_now, :close_topic, all_of( has_key(:topic_id), has_key(:user_id) )) - Fabricate(:topic, auto_close_days: 7, user: Fabricate(:admin), category: Fabricate(:category, auto_close_days: 2)) + Fabricate(:topic, auto_close_days: 7, user: Fabricate(:admin), category_id: Fabricate(:category, auto_close_days: 2).id) end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index b658128f3..5d9db3851 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -77,8 +77,13 @@ Spork.prefork do config.before(:all) do DiscoursePluginRegistry.clear + uncat_id = SiteSetting.uncategorized_category_id + Discourse.current_user_provider = TestCurrentUserProvider + # a bit odd, but this setting is actually preloaded + SiteSetting.defaults[:uncategorized_category_id] = SiteSetting.uncategorized_category_id + require_dependency 'site_settings/local_process_provider' SiteSetting.provider = SiteSettings::LocalProcessProvider.new end