diff --git a/app/assets/javascripts/admin/models/site_setting.js b/app/assets/javascripts/admin/models/site_setting.js index 55f45e20e..5284c3043 100644 --- a/app/assets/javascripts/admin/models/site_setting.js +++ b/app/assets/javascripts/admin/models/site_setting.js @@ -103,21 +103,25 @@ Discourse.SiteSetting = Discourse.Model.extend({ Discourse.SiteSetting.reopenClass({ - /** - Retrieve all settings from the server - - @method findAll - **/ findAll: function() { - var result = Em.A(); - Discourse.ajax("/admin/site_settings").then(function (settings) { + return Discourse.ajax("/admin/site_settings").then(function (settings) { + // Group the results by category + var categoryNames = [], + categories = {}, + result = Em.A(); _.each(settings.site_settings,function(s) { s.originalValue = s.value; - result.pushObject(Discourse.SiteSetting.create(s)); + if (!categoryNames.contains(s.category)) { + categoryNames.pushObject(s.category); + categories[s.category] = Em.A(); + } + categories[s.category].pushObject(Discourse.SiteSetting.create(s)); }); - result.set('diags', settings.diags); + _.each(categoryNames, function(n) { + result.pushObject({nameKey: n, name: I18n.t('admin.site_settings.categories.' + n),siteSettings: categories[n]}); + }); + return result; }); - return result; }, update: function(key, value) { @@ -126,6 +130,7 @@ Discourse.SiteSetting.reopenClass({ data: { value: value } }); } + }); diff --git a/app/assets/javascripts/admin/routes/admin_routes.js b/app/assets/javascripts/admin/routes/admin_routes.js index 58e39cf81..11e2ece7a 100644 --- a/app/assets/javascripts/admin/routes/admin_routes.js +++ b/app/assets/javascripts/admin/routes/admin_routes.js @@ -7,7 +7,9 @@ Discourse.Route.buildRoutes(function() { this.resource('admin', { path: '/admin' }, function() { this.route('dashboard', { path: '/' }); - this.route('site_settings', { path: '/site_settings' }); + this.resource('adminSiteSettings', { path: '/site_settings' }, function() { + this.resource('adminSiteSettingsCategory', { path: 'category/:category_id'} ); + }); this.resource('adminSiteContents', { path: '/site_contents' }, function() { diff --git a/app/assets/javascripts/admin/routes/admin_site_setting_category_route.js b/app/assets/javascripts/admin/routes/admin_site_setting_category_route.js new file mode 100644 index 000000000..889be542d --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin_site_setting_category_route.js @@ -0,0 +1,18 @@ +/** + Handles routes related to viewing and editing site settings within one category. + + @class AdminSiteSettingCategoryRoute + @extends Discourse.Route + @namespace Discourse + @module Discourse +**/ +Discourse.AdminSiteSettingsCategoryRoute = Discourse.Route.extend({ + model: function(params) { + var category = this.modelFor('adminSiteSettings').find(function(siteSettingCategory) { + return siteSettingCategory.nameKey === params.category_id; + }); + if (category) { + return category.siteSettings; + } + } +}); diff --git a/app/assets/javascripts/admin/routes/admin_site_settings_route.js b/app/assets/javascripts/admin/routes/admin_site_settings_route.js index 164bd6361..3dbe5ad1f 100644 --- a/app/assets/javascripts/admin/routes/admin_site_settings_route.js +++ b/app/assets/javascripts/admin/routes/admin_site_settings_route.js @@ -11,3 +11,13 @@ Discourse.AdminSiteSettingsRoute = Discourse.Route.extend({ return Discourse.SiteSetting.findAll(); } }); + +/** + Handles when you click the Site Settings tab in admin, but haven't + chosen a category. It will redirect to the first category. +**/ +Discourse.AdminSiteSettingsIndexRoute = Discourse.Route.extend({ + model: function() { + this.transitionTo('adminSiteSettingsCategory', this.modelFor('adminSiteSettings')[0].nameKey); + } +}); \ No newline at end of file diff --git a/app/assets/javascripts/admin/templates/admin.js.handlebars b/app/assets/javascripts/admin/templates/admin.js.handlebars index 5a09dd52f..acb69cde5 100644 --- a/app/assets/javascripts/admin/templates/admin.js.handlebars +++ b/app/assets/javascripts/admin/templates/admin.js.handlebars @@ -5,7 +5,7 @@ <ul class="nav nav-pills"> <li>{{#link-to 'admin.dashboard'}}{{i18n admin.dashboard.title}}{{/link-to}}</li> {{#if currentUser.admin}} - <li>{{#link-to 'admin.site_settings'}}{{i18n admin.site_settings.title}}{{/link-to}}</li> + <li>{{#link-to 'adminSiteSettings'}}{{i18n admin.site_settings.title}}{{/link-to}}</li> <li>{{#link-to 'adminSiteContents'}}{{i18n admin.site_content.title}}{{/link-to}}</li> {{/if}} <li>{{#link-to 'adminUsersList'}}{{i18n admin.users.title}}{{/link-to}}</li> diff --git a/app/assets/javascripts/admin/templates/site_settings.js.handlebars b/app/assets/javascripts/admin/templates/site_settings.js.handlebars index ff6e21ab7..a5b948cc6 100644 --- a/app/assets/javascripts/admin/templates/site_settings.js.handlebars +++ b/app/assets/javascripts/admin/templates/site_settings.js.handlebars @@ -8,8 +8,14 @@ <div class='controls'> {{textField value=filter placeholderKey="type_to_filter"}} </div> - </div> -{{collection contentBinding="filteredContent" classNames="form-horizontal settings" itemViewClass="Discourse.SiteSettingView"}} +<ul class="nav nav-pills"> + {{#each category in controller}} + <li>{{#link-to 'adminSiteSettingsCategory' category.nameKey}}{{category.name}}{{/link-to}}</a></li> + {{/each}} +</ul> +<hr/> + +{{ outlet }} diff --git a/app/assets/javascripts/admin/templates/site_settings_category.js.handlebars b/app/assets/javascripts/admin/templates/site_settings_category.js.handlebars new file mode 100644 index 000000000..8c65f5226 --- /dev/null +++ b/app/assets/javascripts/admin/templates/site_settings_category.js.handlebars @@ -0,0 +1 @@ +{{collection contentBinding="content" classNames="form-horizontal settings" itemViewClass="Discourse.SiteSettingView"}} diff --git a/app/assets/javascripts/admin/views/admin_site_settings_category_view.js b/app/assets/javascripts/admin/views/admin_site_settings_category_view.js new file mode 100644 index 000000000..f6ce4ee82 --- /dev/null +++ b/app/assets/javascripts/admin/views/admin_site_settings_category_view.js @@ -0,0 +1,3 @@ +Discourse.AdminSiteSettingsCategoryView = Discourse.View.extend({ + templateName: 'admin/templates/site_settings_category' +}); diff --git a/app/models/site_setting.rb b/app/models/site_setting.rb index b3ec854e1..09d5a9f3a 100644 --- a/app/models/site_setting.rb +++ b/app/models/site_setting.rb @@ -8,11 +8,10 @@ class SiteSetting < ActiveRecord::Base validates_presence_of :data_type SiteSettings::YamlLoader.new("#{Rails.root}/config/site_settings.yml").load do |category, name, default, opts| - # TODO: category support if opts.delete(:client) - client_setting(name.to_sym, default) + client_setting(name, default, category) else - setting(name.to_sym, default, opts) + setting(name, default, category, opts) end end diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 433e93dcc..c500b71e8 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -1417,5 +1417,13 @@ en: title: 'Settings' reset: 'reset to default' none: 'none' - - + categories: + mandatory: 'Mandatory' + users: 'Users' + posting: 'Posting' + email: 'Email' + files: 'Files' + trust: 'Trust Levels' + spam: 'Spam' + rate_limits: 'Rate Limits' + uncategorized: 'Uncategorized' diff --git a/config/routes.rb b/config/routes.rb index 23709ece6..9c9b2f556 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -21,8 +21,11 @@ Discourse::Application.routes.draw do namespace :admin, constraints: StaffConstraint.new do get '' => 'admin#index' - - resources :site_settings, constraints: AdminConstraint.new + resources :site_settings, constraints: AdminConstraint.new do + collection do + get 'category/:id' => 'site_settings#index' + end + end get 'reports/:type' => 'reports#show' diff --git a/config/site_settings.yml b/config/site_settings.yml index c37c28d33..81dbb23d4 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -1,239 +1,41 @@ -all: +mandatory: title: client: true default: 'Discourse' site_description: '' + contact_email: '' + notification_email: 'info@discourse.org' + site_contact_username: '' logo_url: client: true default: '/assets/d-logo-sketch.png' logo_small_url: client: true default: '/assets/d-logo-sketch-small.png' - contact_email: '' - company_full_name: 'My Unconfigured Forum Ltd.' - company_short_name: 'Unconfigured Forum' - company_domain: 'www.example.com' - tos_url: - client: true - default: '' - faq_url: - client: true - default: '' - privacy_policy_url: - client: true - default: '' - traditional_markdown_linebreaks: - client: true - default: false - top_menu: - client: true - default: 'latest|new|unread|favorited|categories' - post_menu: - client: true - default: 'like|edit|flag|delete|share|bookmark|reply' - share_links: - client: true - default: 'twitter|facebook|google+|email' - track_external_right_clicks: - client: true - default: false - must_approve_users: - client: true - default: false - ga_tracking_code: - client: true - default: '' - ga_domain_name: - client: true - default: '' - enable_escaped_fragments: - client: true - default: false - enable_noscript_support: - client: true - default: true - enable_long_polling: - client: true - default: true - polling_interval: - client: true - default: 3000 - anon_polling_interval: - client: true - default: 30000 - min_post_length: - client: true - default: - test: 5 - default: 20 - min_private_message_post_length: - client: true - default: - test: 5 - default: 10 - max_post_length: - client: true - default: 32000 - min_topic_title_length: - client: true - default: 15 - max_topic_title_length: - client: true - default: 255 - min_private_message_title_length: - client: true - default: 2 - allow_uncategorized_topics: - client: true - default: true - min_search_term_length: - client: true - default: 3 - flush_timings_secs: - client: true - default: 5 - suppress_reply_directly_below: - client: true - default: true - suppress_reply_directly_above: - client: true - default: true - email_domains_blacklist: - client: true - default: 'mailinator.com' - email_domains_whitelist: - client: true - default: '' - version_checks: - client: true - default: true - new_version_emails: true - min_title_similar_length: - client: true - default: 10 - min_body_similar_length: - client: true - default: 15 - category_colors: - client: true - default: 'BF1E2E|F1592A|F7941D|9EB83B|3AB54A|12A89D|25AAE2|0E76BD|652D90|92278F|ED207B|8C6238|231F20|808281|B3B5B4|283890' - enable_wide_category_list: - client: true - default: false - title_prettify: true - max_image_size_kb: - client: true - default: 2048 - max_attachment_size_kb: - client: true - default: 1024 - authorized_extensions: - client: true - default: '.jpg|.jpeg|.png|.gif' - auto_track_topics_after: 240000 - new_topic_duration_minutes: 2880 - long_polling_interval: 15000 - flags_required_to_hide_post: 3 - cooldown_minutes_after_hiding_posts: 10 - max_topics_in_first_day: 5 - max_replies_in_first_day: 10 - num_flags_to_block_new_user: 3 - num_users_to_block_new_user: 3 - notify_mods_when_user_blocked: false - flag_sockpuppets: true - force_hostname: '' - port: - default: - development: 3000 - default: '' - enable_private_messages: true - use_ssl: false - queue_jobs: - default: - test: false - default: true - crawl_images: - default: - test: false - default: true - max_image_width: - client: true - default: 690 - max_image_height: - client: true - default: 500 - create_thumbnails: true - category_featured_topics: - client: true - default: 6 - topics_per_page: 30 - posts_per_page: - client: true - default: 20 - invite_expiry_days: 14 - active_user_rate_limit_secs: 60 - previous_visit_timeout_hours: 1 favicon_url: client: true default: '/assets/default-favicon.ico' apple_touch_icon_url: /assets/default-apple-touch-icon.png' - ninja_edit_window: 300 - edit_history_visible_to_public: - client: true - default: true - delete_removed_posts_after: - client: true - default: 24 - post_undo_action_window_mins: 10 - site_contact_username: '' - max_mentions_per_post: 10 - newuser_max_mentions_per_post: 2 - unique_posts_mins: - default: - test: 0 - default: 5 - rate_limit_create_topic: 5 - rate_limit_create_post: 5 - max_topics_per_day: 20 - max_private_messages_per_day: 20 - max_likes_per_day: 50 - max_bookmarks_per_day: 20 - max_flags_per_day: 20 - max_edits_per_day: 30 - max_favorites_per_day: 20 - email_time_window_mins: 10 - email_posts_context: 5 - default_digest_email_frequency: - default: 7 - enum: 'DigestEmailSiteSetting' - onebox_max_chars: 5000 - suggested_topics: 5 - allow_duplicate_topic_titles: false - staff_like_weight: 3 - add_rel_nofollow_to_user_content: true - exclude_rel_nofollow_domains: '' - post_excerpt_maxlength: 300 - post_onebox_maxlength: 500 - best_of_score_threshold: 15 - best_of_posts_required: 50 - best_of_likes_required: 1 - best_of_percent_filter: 20 - notification_email: 'info@discourse.org' - email_custom_headers: 'Auto-Submitted: auto-generated' - allow_index_in_robots_txt: true - send_welcome_message: true - invite_only: - client: true - default: false - login_required: - client: true - default: false + company_full_name: 'My Unconfigured Forum Ltd.' + company_short_name: 'Unconfigured Forum' + company_domain: 'www.example.com' + +users: enable_local_logins: client: true default: true enable_local_account_create: client: true default: true + invite_only: + client: true + default: false + login_required: + client: true + default: false + must_approve_users: + client: true + default: false enable_google_logins: client: true default: true @@ -265,6 +67,131 @@ all: default: false enforce_global_nicknames: true discourse_org_access_key: '' + invite_expiry_days: 14 + username_change_period: 3 + email_editable: true + enable_names: + client: true + default: true + invites_shown: + client: true + default: 30 + delete_user_max_age: + client: true + default: 14 + delete_all_posts_max: 15 + +posting: + min_post_length: + client: true + default: + test: 5 + default: 20 + min_private_message_post_length: + client: true + default: + test: 5 + default: 10 + max_post_length: + client: true + default: 32000 + min_topic_title_length: + client: true + default: 15 + max_topic_title_length: + client: true + default: 255 + title_prettify: true + title_fancy_entities: true + min_private_message_title_length: + client: true + default: 2 + allow_uncategorized_topics: + client: true + default: true + allow_duplicate_topic_titles: false + min_title_similar_length: + client: true + default: 10 + min_body_similar_length: + client: true + default: 15 + enable_private_messages: true + ninja_edit_window: 300 + edit_history_visible_to_public: + client: true + default: true + delete_removed_posts_after: + client: true + default: 24 + traditional_markdown_linebreaks: + client: true + default: false + suppress_reply_directly_below: + client: true + default: true + suppress_reply_directly_above: + client: true + default: true + post_undo_action_window_mins: 10 + max_mentions_per_post: 10 + newuser_max_mentions_per_post: 2 + onebox_max_chars: 5000 + title_min_entropy: 10 + body_min_entropy: 7 + max_word_length: 30 + newuser_max_links: 2 + newuser_max_images: + client: true + default: 0 + newuser_max_attachments: + client: true + default: 0 + uncategorized_category_id: + default: -1 + hidden: true + post_excerpt_maxlength: 300 + post_onebox_maxlength: 500 + display_name_on_posts: + client: true + default: false + +email: + email_time_window_mins: 10 + email_posts_context: 5 + default_digest_email_frequency: + default: 7 + enum: 'DigestEmailSiteSetting' + email_custom_headers: 'Auto-Submitted: auto-generated' + reply_by_email_enabled: false + reply_by_email_address: '' + pop3s_polling_enabled: false + pop3s_polling_host: '' + pop3s_polling_port: 995 + pop3s_polling_username: '' + pop3s_polling_password: '' + +files: + max_image_size_kb: + client: true + default: 2048 + max_attachment_size_kb: + client: true + default: 1024 + authorized_extensions: + client: true + default: '.jpg|.jpeg|.png|.gif' + crawl_images: + default: + test: false + default: true + max_image_width: + client: true + default: 690 + max_image_height: + client: true + default: 500 + create_thumbnails: true clean_up_uploads: false uploads_grace_period_in_hours: 1 enable_s3_uploads: false @@ -274,10 +201,18 @@ all: default: '' enum: 'S3RegionSiteSetting' s3_upload_bucket: '' - enable_flash_video_onebox: false + allow_uploaded_avatars: + client: true + default: true + allow_animated_avatars: + client: true + default: false + detect_custom_avatars: true + max_daily_gravatar_crawls: 500 + +trust: default_trust_level: 0 default_invitee_trust_level: 1 - allow_import: false basic_requires_topics_entered: 5 basic_requires_read_posts: 50 basic_requires_time_spent_mins: 15 @@ -291,25 +226,136 @@ all: min_trust_to_create_topic: default: 0 enum: 'MinTrustToCreateTopicSetting' - reply_by_email_enabled: false - reply_by_email_address: '' - pop3s_polling_enabled: false - pop3s_polling_host: '' - pop3s_polling_port: 995 - pop3s_polling_username: '' - pop3s_polling_password: '' - title_min_entropy: 10 - body_min_entropy: 7 - max_word_length: 30 - newuser_max_links: 2 - newuser_max_images: + +spam: + email_domains_blacklist: client: true - default: 0 - newuser_max_attachments: + default: 'mailinator.com' + email_domains_whitelist: client: true - default: 0 + default: '' + flags_required_to_hide_post: 3 + cooldown_minutes_after_hiding_posts: 10 + max_topics_in_first_day: 5 + max_replies_in_first_day: 10 + num_flags_to_block_new_user: 3 + num_users_to_block_new_user: 3 + notify_mods_when_user_blocked: false + flag_sockpuppets: true newuser_spam_host_threshold: 3 - title_fancy_entities: true + +rate_limits: + unique_posts_mins: + default: + test: 0 + default: 5 + rate_limit_create_topic: 5 + rate_limit_create_post: 5 + max_topics_per_day: 20 + max_private_messages_per_day: 20 + max_likes_per_day: 50 + max_bookmarks_per_day: 20 + max_flags_per_day: 20 + max_edits_per_day: 30 + max_favorites_per_day: 20 + + +uncategorized: + tos_url: + client: true + default: '' + faq_url: + client: true + default: '' + privacy_policy_url: + client: true + default: '' + top_menu: + client: true + default: 'latest|new|unread|favorited|categories' + post_menu: + client: true + default: 'like|edit|flag|delete|share|bookmark|reply' + share_links: + client: true + default: 'twitter|facebook|google+|email' + track_external_right_clicks: + client: true + default: false + ga_tracking_code: + client: true + default: '' + ga_domain_name: + client: true + default: '' + enable_escaped_fragments: + client: true + default: false + enable_noscript_support: + client: true + default: true + enable_long_polling: + client: true + default: true + polling_interval: + client: true + default: 3000 + anon_polling_interval: + client: true + default: 30000 + min_search_term_length: + client: true + default: 3 + flush_timings_secs: + client: true + default: 5 + version_checks: + client: true + default: true + new_version_emails: true + category_colors: + client: true + default: 'BF1E2E|F1592A|F7941D|9EB83B|3AB54A|12A89D|25AAE2|0E76BD|652D90|92278F|ED207B|8C6238|231F20|808281|B3B5B4|283890' + enable_wide_category_list: + client: true + default: false + + auto_track_topics_after: 240000 + new_topic_duration_minutes: 2880 + long_polling_interval: 15000 + force_hostname: '' + port: + default: + development: 3000 + default: '' + use_ssl: false + queue_jobs: + default: + test: false + default: true + + category_featured_topics: + client: true + default: 6 + topics_per_page: 30 + posts_per_page: + client: true + default: 20 + active_user_rate_limit_secs: 60 + previous_visit_timeout_hours: 1 + + suggested_topics: 5 + staff_like_weight: 3 + add_rel_nofollow_to_user_content: true + exclude_rel_nofollow_domains: '' + best_of_score_threshold: 15 + best_of_posts_required: 50 + best_of_likes_required: 1 + best_of_percent_filter: 20 + allow_index_in_robots_txt: true + send_welcome_message: true + enable_flash_video_onebox: false + allow_import: false default_locale: default: 'en' enum: 'LocaleSiteSetting' @@ -330,34 +376,10 @@ all: relative_date_duration: client: true default: 30 - delete_user_max_age: - client: true - default: 14 - delete_all_posts_max: 15 - username_change_period: 3 - email_editable: true - allow_uploaded_avatars: - client: true - default: true - allow_animated_avatars: - client: true - default: false - detect_custom_avatars: true - max_daily_gravatar_crawls: 500 sequential_replies_threshold: 2 enable_mobile_theme: client: true default: true dominating_topic_minimum_percent: 20 - uncategorized_category_id: - default: -1 - hidden: true - display_name_on_posts: - client: true - default: false - enable_names: - client: true - default: true - invites_shown: - client: true - default: 30 + + diff --git a/lib/site_setting_extension.rb b/lib/site_setting_extension.rb index 2c33d2350..923638170 100644 --- a/lib/site_setting_extension.rb +++ b/lib/site_setting_extension.rb @@ -30,6 +30,10 @@ module SiteSettingExtension @defaults ||= {} end + def categories + @categories ||= {} + end + def enums @enums ||= {} end @@ -38,9 +42,11 @@ module SiteSettingExtension @hidden_settings ||= [] end - def setting(name, default = nil, opts = {}) + def setting(name_arg, default = nil, category = nil, opts = {}) + name = name_arg.to_sym mutex.synchronize do self.defaults[name] = default + categories[name] = category.try(:to_sym) || :uncategorized current_value = current.has_key?(name) ? current[name] : default if opts[:enum] enum = opts[:enum] @@ -54,8 +60,8 @@ module SiteSettingExtension end # just like a setting, except that it is available in javascript via DiscourseSession - def client_setting(name, default = nil) - setting(name,default) + def client_setting(name, default = nil, category = nil) + setting(name, default, category) @@client_settings ||= [] @@client_settings << name end @@ -93,7 +99,8 @@ module SiteSettingExtension 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?} : {}) + value: value.to_s, + category: categories[s]}.merge( type == :enum ? {valid_values: enum_class(s).values, translate_names: enum_class(s).translate_names?} : {}) end end diff --git a/spec/components/site_setting_extension_spec.rb b/spec/components/site_setting_extension_spec.rb index bc7de0fa9..43cad61cf 100644 --- a/spec/components/site_setting_extension_spec.rb +++ b/spec/components/site_setting_extension_spec.rb @@ -161,4 +161,32 @@ describe SiteSettingExtension do # end # end # end + + describe 'a setting with a category' do + before do + settings.setting(:test_setting, 88, :tests) + settings.refresh! + end + + it "should return the category in all_settings" do + settings.all_settings.find {|s| s[:setting] == :test_setting }[:category].should == :tests + end + + context "when overidden" do + after :each do + settings.remove_override!(:test_setting) + end + + it "should have the correct override" do + settings.test_setting = 101 + settings.test_setting.should == 101 + end + + it "should still have the correct category" do + settings.test_setting = 102 + settings.all_settings.find {|s| s[:setting] == :test_setting }[:category].should == :tests + end + end + end + end