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