From 96ae3cdaccf127f68ee1a4f4f911f3725aaf5a3a Mon Sep 17 00:00:00 2001 From: Manoj Date: Thu, 24 Oct 2013 13:29:58 +0530 Subject: [PATCH 01/33] Utilize already existing method 'find_by_username_or_email' check presence of email using include, dont use =~ --- app/controllers/session_controller.rb | 15 ++++++--------- app/models/user.rb | 20 +++++++++++++------- spec/models/user_spec.rb | 4 ++-- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/app/controllers/session_controller.rb b/app/controllers/session_controller.rb index 908eba5013c..1badedee94c 100644 --- a/app/controllers/session_controller.rb +++ b/app/controllers/session_controller.rb @@ -10,14 +10,11 @@ class SessionController < ApplicationController params.require(:login) params.require(:password) - login = params[:login].strip - login = login[1..-1] if login[0] == "@" + login = params[:login].strip + password = params[:password] + login = login[1..-1] if login[0] == "@" - if login =~ /@/ - @user = User.where(email: Email.downcase(login)).first - else - @user = User.where(username_lower: login.downcase).first - end + @user = User.find_by_username_or_email(login) if @user.present? @@ -28,7 +25,7 @@ class SessionController < ApplicationController end # If their password is correct - if @user.confirm_password?(params[:password]) + if @user.confirm_password?(password) if @user.is_banned? render json: { error: I18n.t("login.banned", {date: I18n.l(@user.banned_till, format: :date_only)}) } @@ -57,7 +54,7 @@ class SessionController < ApplicationController def forgot_password params.require(:login) - user = User.where('username_lower = :username or email = :email', username: params[:login].downcase, email: Email.downcase(params[:login])).first + user = User.find_by_username_or_email(params[:login]) if user.present? email_token = user.email_tokens.create(email: user.email) Jobs.enqueue(:user_email, type: :forgot_password, user_id: user.id, email_token: email_token.token) diff --git a/app/models/user.rb b/app/models/user.rb index 66e58ea6887..7276677dfcb 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -124,13 +124,11 @@ class User < ActiveRecord::Base end def self.find_by_username_or_email(username_or_email) - conditions = if username_or_email.include?('@') - { email: Email.downcase(username_or_email) } - else - { username_lower: username_or_email.downcase } - end - - users = User.where(conditions).to_a + users = if username_or_email.include?('@') + find_by_email(username_or_email) + else + find_by_username(username_or_email) + end if users.size > 1 raise Discourse::TooManyMatches @@ -139,6 +137,14 @@ class User < ActiveRecord::Base end end + def self.find_by_email(email) + where(email: Email.downcase(email)) + end + + def self.find_by_username(username) + where(username_lower: username.downcase) + end + def enqueue_welcome_message(message_type) return unless SiteSetting.send_welcome_message? Jobs.enqueue(:send_system_message, user_id: id, message_type: message_type) diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index dbeebb1b49e..aaf155332d2 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -829,8 +829,8 @@ describe User do context 'when multiple users are found' do it 'raises an exception' do - user_query = stub(to_a: [stub, stub]) - User.stubs(:where).with(username_lower: 'bob').returns(user_query) + user_query = [stub, stub] + User.stubs(:find_by_username).with('bob').returns(user_query) expect { User.find_by_username_or_email('bob') }.to raise_error(Discourse::TooManyMatches) end From dfd2d360898770e01b7339b19e4500ec2ba91c0b Mon Sep 17 00:00:00 2001 From: Philip Kallberg Date: Thu, 24 Oct 2013 16:30:44 +0100 Subject: [PATCH 02/33] cloud66 easydeploy --- README.md | 2 +- config/cloud/cloud66/deploy_hooks.yml | 75 +++++++++++++++------------ 2 files changed, 44 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 3d346dcef03..6362a8ffe53 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Before you get started, ensure you have the following minimum versions: [Ruby 1. ## Setting up a Discourse Forum -If you're not here to hack on the open source code, but rather looking to set up a Discourse forum in a production environment, see our [**Discourse Install Guide**](/docs/INSTALL-ubuntu.md). Be warned, however, that Discourse is still very much beta, and you will probably need a background as a developer or system administrator to set it up successfully. +If you're not here to hack on the open source code, but rather looking to set up a Discourse forum in a production environment, see our [**Discourse Install Guide**](/docs/INSTALL-ubuntu.md). Be warned, however, that Discourse is still very much beta, and you will probably need a background as a developer or system administrator to set it up successfully. Alternatively, you can deploy in a matter of minutes through [**Cloud 66**](/docs/INSTALL-cloud66.md). ## Vision diff --git a/config/cloud/cloud66/deploy_hooks.yml b/config/cloud/cloud66/deploy_hooks.yml index 9279d0506ac..dee74d8d67c 100644 --- a/config/cloud/cloud66/deploy_hooks.yml +++ b/config/cloud/cloud66/deploy_hooks.yml @@ -1,7 +1,7 @@ production: first_thing: # 1. Permissions on postgres box - - source: /.cloud66/scripts/permissions.sh + - source: /config/cloud/cloud66/scripts/permissions.sh destination: /tmp/scripts/permissions.sh target: postgresql apply_during: build_only @@ -16,25 +16,29 @@ production: owner: postgres after_checkout: # 3. Copy Procfile - - source: /.cloud66/files/Procfile + - source: /config/cloud/cloud66/files/Procfile destination: <%= ENV['RAILS_STACK_PATH'] %>/Procfile target: rails + run_on: all_servers # 4. Copy redis settings - - source: /.cloud66/files/redis.yml + - source: /config/cloud/cloud66/files/redis.yml destination: <%= ENV['RAILS_STACK_PATH'] %>/config/redis.yml target: rails parse: false + run_on: all_servers # 5. Copy production.rb file - - source: /.cloud66/files/production.rb + - source: /config/cloud/cloud66/files/production.rb destination: <%= ENV['RAILS_STACK_PATH'] %>/config/environments/production.rb target: rails + run_on: all_servers # 6. Move thin config to server - - source: /.cloud66/files/thin.yml + - source: /config/cloud/cloud66/files/thin.yml destination: <%= ENV['RAILS_STACK_PATH'] %>/config/thin.yml target: rails + run_on: all_servers after_rails: # 7. Set environment variables and allow PSQL user to access them - - source: /.cloud66/scripts/env_vars.sh + - source: /config/cloud/cloud66/scripts/env_vars.sh destination: /tmp/scripts/env_vars.sh target: rails apply_during: build_only @@ -42,21 +46,21 @@ production: sudo: true last_thing: # 8. KILL DB - - source: /.cloud66/scripts/kill_db.sh + - source: /config/cloud/cloud66/scripts/kill_db.sh destination: /tmp/scripts/kill_db.sh target: postgresql apply_during: build_only execute: true sudo: true # 9. DB:DROP & DB:CREATE - - source: /.cloud66/scripts/drop_create.sh + - source: /config/cloud/cloud66/scripts/drop_create.sh destination: /tmp/scripts/drop_create.sh target: rails apply_during: build_only execute: true sudo: true # 10. Import database image - - source: /.cloud66/scripts/import_prod.sh + - source: /config/cloud/cloud66/scripts/import_prod.sh destination: /tmp/scripts/import_prod.sh target: postgresql apply_during: build_only @@ -64,14 +68,14 @@ production: owner: postgres run_as: postgres # 11. Migrate database - - source: /.cloud66/scripts/migrate.sh + - source: /config/cloud/cloud66/scripts/migrate.sh destination: /tmp/migrate.sh target: rails apply_during: build_only execute: true sudo: true # 12. Curl script - - source: /.cloud66/scripts/curl.sh + - source: /config/cloud/cloud66/scripts/curl.sh destination: /tmp/curl.sh target: rails apply_during: build_only @@ -80,7 +84,7 @@ production: staging: first_thing: # 1. Permissions on postgres box - - source: /.cloud66/scripts/permissions.sh + - source: /config/cloud/cloud66/scripts/permissions.sh destination: /tmp/scripts/permissions.sh target: postgresql apply_during: build_only @@ -95,25 +99,29 @@ staging: owner: postgres after_checkout: # 3. Copy Procfile - - source: /.cloud66/files/Procfile + - source: /config/cloud/cloud66/files/Procfile destination: <%= ENV['RAILS_STACK_PATH'] %>/Procfile target: rails + run_on: all_servers # 4. Rename redis.yml.sample file - - source: /.cloud66/files/redis.yml + - source: /config/cloud/cloud66/files/redis.yml destination: <%= ENV['RAILS_STACK_PATH'] %>/config/redis.yml target: rails parse: false + run_on: all_servers # 5. Rename production.rb.sample file - - source: /.cloud66/files/production.rb + - source: /config/cloud/cloud66/files/production.rb destination: <%= ENV['RAILS_STACK_PATH'] %>/config/environments/production.rb target: rails + run_on: all_servers # 6. Move thin config to server - - source: /.cloud66/files/thin.yml + - source: /config/cloud/cloud66/files/thin.yml destination: <%= ENV['RAILS_STACK_PATH'] %>/config/thin.yml target: rails + run_on: all_servers after_rails: # 7. Set environment variables and allow PSQL user to access them - - source: /.cloud66/scripts/env_vars.sh + - source: /config/cloud/cloud66/scripts/env_vars.sh destination: /tmp/scripts/env_vars.sh target: rails apply_during: build_only @@ -121,21 +129,21 @@ staging: sudo: true last_thing: # 8. KILL DB - - source: /.cloud66/scripts/kill_db.sh + - source: /config/cloud/cloud66/scripts/kill_db.sh destination: /tmp/scripts/kill_db.sh target: postgresql apply_during: build_only execute: true sudo: true # 9. DB:DROP & DB:CREATE - - source: /.cloud66/scripts/drop_create.sh + - source: /config/cloud/cloud66/scripts/drop_create.sh destination: /tmp/scripts/drop_create.sh target: rails apply_during: build_only execute: true sudo: true # 10. Import database image - - source: /.cloud66/scripts/import_prod.sh + - source: /config/cloud/cloud66/scripts/import_prod.sh destination: /tmp/scripts/import_prod.sh target: postgresql apply_during: build_only @@ -143,14 +151,14 @@ staging: owner: postgres run_as: postgres # 11. Migrate database - - source: /.cloud66/scripts/migrate.sh + - source: /config/cloud/cloud66/scripts/migrate.sh destination: /tmp/migrate.sh target: rails apply_during: build_only execute: true sudo: true # 12. Curl script - - source: /.cloud66/scripts/curl.sh + - source: /config/cloud/cloud66/scripts/curl.sh destination: /tmp/curl.sh target: rails apply_during: build_only @@ -159,7 +167,7 @@ staging: development: first_thing: # 1. Permissions on postgres box - - source: /.cloud66/scripts/permissions.sh + - source: /config/cloud/cloud66/scripts/permissions.sh destination: /tmp/scripts/permissions.sh target: postgresql apply_during: build_only @@ -174,21 +182,24 @@ development: owner: postgres after_checkout: # 3. Copy Procfile - - source: /.cloud66/files/Procfile + - source: /config/cloud/cloud66/files/Procfile destination: <%= ENV['RAILS_STACK_PATH'] %>/Procfile target: rails + run_on: all_servers # 4. Rename redis.yml.sample file - - source: /.cloud66/files/redis.yml + - source: /config/cloud/cloud66/files/redis.yml destination: <%= ENV['RAILS_STACK_PATH'] %>/config/redis.yml target: rails parse: false + run_on: all_servers # 5. Move thin config to server - - source: /.cloud66/files/thin.yml + - source: /config/cloud/cloud66/files/thin.yml destination: <%= ENV['RAILS_STACK_PATH'] %>/config/thin.yml target: rails + run_on: all_servers after_rails: # 6. Set environment variables and allow PSQL user to access them - - source: /.cloud66/scripts/env_vars.sh + - source: /config/cloud/cloud66/scripts/env_vars.sh destination: /tmp/scripts/env_vars.sh target: rails apply_during: build_only @@ -196,21 +207,21 @@ development: sudo: true last_thing: # 7. KILL DB - - source: /.cloud66/scripts/kill_db.sh + - source: /config/cloud/cloud66/scripts/kill_db.sh destination: /tmp/scripts/kill_db.sh target: postgresql apply_during: build_only execute: true sudo: true # 8. DB:DROP & DB:CREATE - - source: /.cloud66/scripts/drop_create.sh + - source: /config/cloud/cloud66/scripts/drop_create.sh destination: /tmp/scripts/drop_create.sh target: rails apply_during: build_only execute: true sudo: true # 9. Import database image - - source: /.cloud66/scripts/import_dev.sh + - source: /config/cloud/cloud66/scripts/import_dev.sh destination: /tmp/scripts/import_dev.sh target: postgresql apply_during: build_only @@ -218,14 +229,14 @@ development: owner: postgres run_as: postgres # 10. Migrate database - - source: /.cloud66/scripts/migrate.sh + - source: /config/cloud/cloud66/scripts/migrate.sh destination: /tmp/migrate.sh target: rails apply_during: build_only execute: true sudo: true # 11. Curl script - - source: /.cloud66/scripts/curl.sh + - source: /config/cloud/cloud66/scripts/curl.sh destination: /tmp/curl.sh target: rails apply_during: build_only From 9adcd1579db5609b8c17c9ef611fb0edeaf16a67 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Thu, 24 Oct 2013 12:36:46 -0400 Subject: [PATCH 03/33] Renamed `components` to `lib` in the JS project, as Ember has components and they mean something different. --- app/assets/javascripts/application.js.erb | 2 +- .../discourse/{components => lib}/autocomplete.js | 0 .../discourse/{components => lib}/caret_position.js | 0 .../discourse/{components => lib}/click_track.js | 0 .../discourse/{components => lib}/computed.js | 0 .../discourse/{components => lib}/debounce.js | 0 .../discourse/{components => lib}/development.js | 0 .../discourse/{components => lib}/div_resizer.js | 0 .../javascripts/discourse/{components => lib}/eyeline.js | 0 .../discourse/{components => lib}/formatter.js | 0 .../discourse/{components => lib}/key_value_store.js | 0 .../discourse/{components => lib}/lightbox.js | 0 .../discourse/{components => lib}/markdown.js | 0 .../javascripts/discourse/{components => lib}/mention.js | 0 .../discourse/{components => lib}/message_bus.js | 0 .../javascripts/discourse/{components => lib}/mobile.js | 0 .../javascripts/discourse/{components => lib}/onebox.js | 0 .../javascripts/discourse/{components => lib}/probes.js | 0 .../javascripts/discourse/{components => lib}/quote.js | 0 .../discourse/{components => lib}/screen_track.js | 0 .../javascripts/discourse/{components => lib}/search.js | 0 .../discourse/{components => lib}/syntax_highlighting.js | 0 .../discourse/{components => lib}/transition_helper.js | 0 .../javascripts/discourse/{components => lib}/url.js | 0 .../discourse/{components => lib}/user_search.js | 0 .../discourse/{components => lib}/utilities.js | 0 app/assets/javascripts/main_include.js | 9 +++++---- config/projections.json | 3 +++ lib/pretty_text.rb | 4 ++-- test/javascripts/{components => lib}/bbcode_test.js | 0 test/javascripts/{components => lib}/click_track_test.js | 0 test/javascripts/{components => lib}/computed_test.js | 0 .../{components => lib}/debounce_promise_test.js | 0 test/javascripts/{components => lib}/debounce_test.js | 0 test/javascripts/{components => lib}/formatter_test.js | 0 .../{components => lib}/key_value_store_test.js | 0 test/javascripts/{components => lib}/markdown_test.js | 0 test/javascripts/{components => lib}/onebox_test.js | 0 .../{components => lib}/preload_store_test.js | 0 test/javascripts/{components => lib}/utilities_test.js | 4 ++-- test/javascripts/test_helper.js | 3 ++- 41 files changed, 15 insertions(+), 10 deletions(-) rename app/assets/javascripts/discourse/{components => lib}/autocomplete.js (100%) rename app/assets/javascripts/discourse/{components => lib}/caret_position.js (100%) rename app/assets/javascripts/discourse/{components => lib}/click_track.js (100%) rename app/assets/javascripts/discourse/{components => lib}/computed.js (100%) rename app/assets/javascripts/discourse/{components => lib}/debounce.js (100%) rename app/assets/javascripts/discourse/{components => lib}/development.js (100%) rename app/assets/javascripts/discourse/{components => lib}/div_resizer.js (100%) rename app/assets/javascripts/discourse/{components => lib}/eyeline.js (100%) rename app/assets/javascripts/discourse/{components => lib}/formatter.js (100%) rename app/assets/javascripts/discourse/{components => lib}/key_value_store.js (100%) rename app/assets/javascripts/discourse/{components => lib}/lightbox.js (100%) rename app/assets/javascripts/discourse/{components => lib}/markdown.js (100%) rename app/assets/javascripts/discourse/{components => lib}/mention.js (100%) rename app/assets/javascripts/discourse/{components => lib}/message_bus.js (100%) rename app/assets/javascripts/discourse/{components => lib}/mobile.js (100%) rename app/assets/javascripts/discourse/{components => lib}/onebox.js (100%) rename app/assets/javascripts/discourse/{components => lib}/probes.js (100%) rename app/assets/javascripts/discourse/{components => lib}/quote.js (100%) rename app/assets/javascripts/discourse/{components => lib}/screen_track.js (100%) rename app/assets/javascripts/discourse/{components => lib}/search.js (100%) rename app/assets/javascripts/discourse/{components => lib}/syntax_highlighting.js (100%) rename app/assets/javascripts/discourse/{components => lib}/transition_helper.js (100%) rename app/assets/javascripts/discourse/{components => lib}/url.js (100%) rename app/assets/javascripts/discourse/{components => lib}/user_search.js (100%) rename app/assets/javascripts/discourse/{components => lib}/utilities.js (100%) rename test/javascripts/{components => lib}/bbcode_test.js (100%) rename test/javascripts/{components => lib}/click_track_test.js (100%) rename test/javascripts/{components => lib}/computed_test.js (100%) rename test/javascripts/{components => lib}/debounce_promise_test.js (100%) rename test/javascripts/{components => lib}/debounce_test.js (100%) rename test/javascripts/{components => lib}/formatter_test.js (100%) rename test/javascripts/{components => lib}/key_value_store_test.js (100%) rename test/javascripts/{components => lib}/markdown_test.js (100%) rename test/javascripts/{components => lib}/onebox_test.js (100%) rename test/javascripts/{components => lib}/preload_store_test.js (100%) rename test/javascripts/{components => lib}/utilities_test.js (97%) diff --git a/app/assets/javascripts/application.js.erb b/app/assets/javascripts/application.js.erb index d81c2f245c8..eabbd1ea23a 100644 --- a/app/assets/javascripts/application.js.erb +++ b/app/assets/javascripts/application.js.erb @@ -7,7 +7,7 @@ //= require ./env // probe framework first -//= require ./discourse/components/probes.js +//= require ./discourse/lib/probes.js // Externals we need to load first diff --git a/app/assets/javascripts/discourse/components/autocomplete.js b/app/assets/javascripts/discourse/lib/autocomplete.js similarity index 100% rename from app/assets/javascripts/discourse/components/autocomplete.js rename to app/assets/javascripts/discourse/lib/autocomplete.js diff --git a/app/assets/javascripts/discourse/components/caret_position.js b/app/assets/javascripts/discourse/lib/caret_position.js similarity index 100% rename from app/assets/javascripts/discourse/components/caret_position.js rename to app/assets/javascripts/discourse/lib/caret_position.js diff --git a/app/assets/javascripts/discourse/components/click_track.js b/app/assets/javascripts/discourse/lib/click_track.js similarity index 100% rename from app/assets/javascripts/discourse/components/click_track.js rename to app/assets/javascripts/discourse/lib/click_track.js diff --git a/app/assets/javascripts/discourse/components/computed.js b/app/assets/javascripts/discourse/lib/computed.js similarity index 100% rename from app/assets/javascripts/discourse/components/computed.js rename to app/assets/javascripts/discourse/lib/computed.js diff --git a/app/assets/javascripts/discourse/components/debounce.js b/app/assets/javascripts/discourse/lib/debounce.js similarity index 100% rename from app/assets/javascripts/discourse/components/debounce.js rename to app/assets/javascripts/discourse/lib/debounce.js diff --git a/app/assets/javascripts/discourse/components/development.js b/app/assets/javascripts/discourse/lib/development.js similarity index 100% rename from app/assets/javascripts/discourse/components/development.js rename to app/assets/javascripts/discourse/lib/development.js diff --git a/app/assets/javascripts/discourse/components/div_resizer.js b/app/assets/javascripts/discourse/lib/div_resizer.js similarity index 100% rename from app/assets/javascripts/discourse/components/div_resizer.js rename to app/assets/javascripts/discourse/lib/div_resizer.js diff --git a/app/assets/javascripts/discourse/components/eyeline.js b/app/assets/javascripts/discourse/lib/eyeline.js similarity index 100% rename from app/assets/javascripts/discourse/components/eyeline.js rename to app/assets/javascripts/discourse/lib/eyeline.js diff --git a/app/assets/javascripts/discourse/components/formatter.js b/app/assets/javascripts/discourse/lib/formatter.js similarity index 100% rename from app/assets/javascripts/discourse/components/formatter.js rename to app/assets/javascripts/discourse/lib/formatter.js diff --git a/app/assets/javascripts/discourse/components/key_value_store.js b/app/assets/javascripts/discourse/lib/key_value_store.js similarity index 100% rename from app/assets/javascripts/discourse/components/key_value_store.js rename to app/assets/javascripts/discourse/lib/key_value_store.js diff --git a/app/assets/javascripts/discourse/components/lightbox.js b/app/assets/javascripts/discourse/lib/lightbox.js similarity index 100% rename from app/assets/javascripts/discourse/components/lightbox.js rename to app/assets/javascripts/discourse/lib/lightbox.js diff --git a/app/assets/javascripts/discourse/components/markdown.js b/app/assets/javascripts/discourse/lib/markdown.js similarity index 100% rename from app/assets/javascripts/discourse/components/markdown.js rename to app/assets/javascripts/discourse/lib/markdown.js diff --git a/app/assets/javascripts/discourse/components/mention.js b/app/assets/javascripts/discourse/lib/mention.js similarity index 100% rename from app/assets/javascripts/discourse/components/mention.js rename to app/assets/javascripts/discourse/lib/mention.js diff --git a/app/assets/javascripts/discourse/components/message_bus.js b/app/assets/javascripts/discourse/lib/message_bus.js similarity index 100% rename from app/assets/javascripts/discourse/components/message_bus.js rename to app/assets/javascripts/discourse/lib/message_bus.js diff --git a/app/assets/javascripts/discourse/components/mobile.js b/app/assets/javascripts/discourse/lib/mobile.js similarity index 100% rename from app/assets/javascripts/discourse/components/mobile.js rename to app/assets/javascripts/discourse/lib/mobile.js diff --git a/app/assets/javascripts/discourse/components/onebox.js b/app/assets/javascripts/discourse/lib/onebox.js similarity index 100% rename from app/assets/javascripts/discourse/components/onebox.js rename to app/assets/javascripts/discourse/lib/onebox.js diff --git a/app/assets/javascripts/discourse/components/probes.js b/app/assets/javascripts/discourse/lib/probes.js similarity index 100% rename from app/assets/javascripts/discourse/components/probes.js rename to app/assets/javascripts/discourse/lib/probes.js diff --git a/app/assets/javascripts/discourse/components/quote.js b/app/assets/javascripts/discourse/lib/quote.js similarity index 100% rename from app/assets/javascripts/discourse/components/quote.js rename to app/assets/javascripts/discourse/lib/quote.js diff --git a/app/assets/javascripts/discourse/components/screen_track.js b/app/assets/javascripts/discourse/lib/screen_track.js similarity index 100% rename from app/assets/javascripts/discourse/components/screen_track.js rename to app/assets/javascripts/discourse/lib/screen_track.js diff --git a/app/assets/javascripts/discourse/components/search.js b/app/assets/javascripts/discourse/lib/search.js similarity index 100% rename from app/assets/javascripts/discourse/components/search.js rename to app/assets/javascripts/discourse/lib/search.js diff --git a/app/assets/javascripts/discourse/components/syntax_highlighting.js b/app/assets/javascripts/discourse/lib/syntax_highlighting.js similarity index 100% rename from app/assets/javascripts/discourse/components/syntax_highlighting.js rename to app/assets/javascripts/discourse/lib/syntax_highlighting.js diff --git a/app/assets/javascripts/discourse/components/transition_helper.js b/app/assets/javascripts/discourse/lib/transition_helper.js similarity index 100% rename from app/assets/javascripts/discourse/components/transition_helper.js rename to app/assets/javascripts/discourse/lib/transition_helper.js diff --git a/app/assets/javascripts/discourse/components/url.js b/app/assets/javascripts/discourse/lib/url.js similarity index 100% rename from app/assets/javascripts/discourse/components/url.js rename to app/assets/javascripts/discourse/lib/url.js diff --git a/app/assets/javascripts/discourse/components/user_search.js b/app/assets/javascripts/discourse/lib/user_search.js similarity index 100% rename from app/assets/javascripts/discourse/components/user_search.js rename to app/assets/javascripts/discourse/lib/user_search.js diff --git a/app/assets/javascripts/discourse/components/utilities.js b/app/assets/javascripts/discourse/lib/utilities.js similarity index 100% rename from app/assets/javascripts/discourse/components/utilities.js rename to app/assets/javascripts/discourse/lib/utilities.js diff --git a/app/assets/javascripts/main_include.js b/app/assets/javascripts/main_include.js index 6b93ef4e6c3..5d02310384a 100644 --- a/app/assets/javascripts/main_include.js +++ b/app/assets/javascripts/main_include.js @@ -43,11 +43,11 @@ // Stuff we need to load first //= require ./discourse/mixins/scrolling //= require_tree ./discourse/mixins -//= require ./discourse/components/markdown -//= require ./discourse/components/computed +//= require ./discourse/lib/markdown +//= require ./discourse/lib/computed //= require ./discourse/views/view //= require ./discourse/views/container_view -//= require ./discourse/components/debounce +//= require ./discourse/lib/debounce //= require ./discourse/models/model //= require ./discourse/models/user_action //= require ./discourse/models/composer @@ -63,9 +63,10 @@ //= require ./discourse/dialects/dialect //= require_tree ./discourse/dialects //= require_tree ./discourse/controllers -//= require_tree ./discourse/components +//= require_tree ./discourse/lib //= require_tree ./discourse/models //= require_tree ./discourse/views +//= require_tree ./discourse/components //= require_tree ./discourse/helpers //= require_tree ./discourse/templates //= require_tree ./discourse/routes diff --git a/config/projections.json b/config/projections.json index 0952b3e19b2..c6426bf8a33 100644 --- a/config/projections.json +++ b/config/projections.json @@ -2,6 +2,9 @@ "app/assets/javascripts/discourse/components/*.js": { "command": "dcomponent" }, + "app/assets/javascripts/discourse/lib/*.js": { + "command": "dlib" + }, "app/assets/javascripts/discourse/routes/*.js": { "command": "droute" }, diff --git a/lib/pretty_text.rb b/lib/pretty_text.rb index 7cfe7c93a67..7ccdeae13e6 100644 --- a/lib/pretty_text.rb +++ b/lib/pretty_text.rb @@ -70,8 +70,8 @@ module PrettyText "vendor/assets/javascripts/better_markdown.js", "app/assets/javascripts/defer/html-sanitizer-bundle.js", "app/assets/javascripts/discourse/dialects/dialect.js", - "app/assets/javascripts/discourse/components/utilities.js", - "app/assets/javascripts/discourse/components/markdown.js") + "app/assets/javascripts/discourse/lib/utilities.js", + "app/assets/javascripts/discourse/lib/markdown.js") Dir["#{Rails.root}/app/assets/javascripts/discourse/dialects/**.js"].each do |dialect| unless dialect =~ /\/dialect\.js$/ diff --git a/test/javascripts/components/bbcode_test.js b/test/javascripts/lib/bbcode_test.js similarity index 100% rename from test/javascripts/components/bbcode_test.js rename to test/javascripts/lib/bbcode_test.js diff --git a/test/javascripts/components/click_track_test.js b/test/javascripts/lib/click_track_test.js similarity index 100% rename from test/javascripts/components/click_track_test.js rename to test/javascripts/lib/click_track_test.js diff --git a/test/javascripts/components/computed_test.js b/test/javascripts/lib/computed_test.js similarity index 100% rename from test/javascripts/components/computed_test.js rename to test/javascripts/lib/computed_test.js diff --git a/test/javascripts/components/debounce_promise_test.js b/test/javascripts/lib/debounce_promise_test.js similarity index 100% rename from test/javascripts/components/debounce_promise_test.js rename to test/javascripts/lib/debounce_promise_test.js diff --git a/test/javascripts/components/debounce_test.js b/test/javascripts/lib/debounce_test.js similarity index 100% rename from test/javascripts/components/debounce_test.js rename to test/javascripts/lib/debounce_test.js diff --git a/test/javascripts/components/formatter_test.js b/test/javascripts/lib/formatter_test.js similarity index 100% rename from test/javascripts/components/formatter_test.js rename to test/javascripts/lib/formatter_test.js diff --git a/test/javascripts/components/key_value_store_test.js b/test/javascripts/lib/key_value_store_test.js similarity index 100% rename from test/javascripts/components/key_value_store_test.js rename to test/javascripts/lib/key_value_store_test.js diff --git a/test/javascripts/components/markdown_test.js b/test/javascripts/lib/markdown_test.js similarity index 100% rename from test/javascripts/components/markdown_test.js rename to test/javascripts/lib/markdown_test.js diff --git a/test/javascripts/components/onebox_test.js b/test/javascripts/lib/onebox_test.js similarity index 100% rename from test/javascripts/components/onebox_test.js rename to test/javascripts/lib/onebox_test.js diff --git a/test/javascripts/components/preload_store_test.js b/test/javascripts/lib/preload_store_test.js similarity index 100% rename from test/javascripts/components/preload_store_test.js rename to test/javascripts/lib/preload_store_test.js diff --git a/test/javascripts/components/utilities_test.js b/test/javascripts/lib/utilities_test.js similarity index 97% rename from test/javascripts/components/utilities_test.js rename to test/javascripts/lib/utilities_test.js index a2fc7037e30..1c9f804fca5 100644 --- a/test/javascripts/components/utilities_test.js +++ b/test/javascripts/lib/utilities_test.js @@ -26,8 +26,8 @@ test("new user cannot upload images", function() { Discourse.SiteSettings.newuser_max_images = 0; this.stub(bootbox, "alert"); - ok(!validUpload([{name: "image.png"}])); - ok(bootbox.alert.calledWith(I18n.t('post.errors.image_upload_not_allowed_for_new_user'))); + ok(!validUpload([{name: "image.png"}]), 'the upload is not valid'); + ok(bootbox.alert.calledWith(I18n.t('post.errors.image_upload_not_allowed_for_new_user')), 'the alert is called'); }); test("new user cannot upload attachments", function() { diff --git a/test/javascripts/test_helper.js b/test/javascripts/test_helper.js index 37cf2bb550b..40855d3fd16 100644 --- a/test/javascripts/test_helper.js +++ b/test/javascripts/test_helper.js @@ -6,7 +6,7 @@ //= require ../../app/assets/javascripts/preload_store.js // probe framework first -//= require ../../app/assets/javascripts/discourse/components/probes.js +//= require ../../app/assets/javascripts/discourse/lib/probes.js // Externals we need to load first //= require development/jquery-2.0.3.js @@ -65,6 +65,7 @@ //= require helpers/assertions //= require_tree ./fixtures +//= require_tree ./lib //= require_tree . //= require_self //= require jshint_all From ee2dd9d24c629b026be99dea8eeebb7479b854f3 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Thu, 24 Oct 2013 15:19:15 -0400 Subject: [PATCH 04/33] Working drop downs for category changing --- .../components/breadcrumbs_component.js | 16 ++++- .../components/categorydrop_component.js | 44 ++++++++++++ .../discourse-breadcrumbs.js.handlebars | 17 ++--- .../discourse-categorydrop.js.handlebars | 15 +++- app/assets/stylesheets/desktop/topic.scss | 69 ++++++++++++++++--- 5 files changed, 136 insertions(+), 25 deletions(-) create mode 100644 app/assets/javascripts/discourse/components/categorydrop_component.js diff --git a/app/assets/javascripts/discourse/components/breadcrumbs_component.js b/app/assets/javascripts/discourse/components/breadcrumbs_component.js index b60c811f372..05ae9585b67 100644 --- a/app/assets/javascripts/discourse/components/breadcrumbs_component.js +++ b/app/assets/javascripts/discourse/components/breadcrumbs_component.js @@ -1,5 +1,19 @@ Discourse.DiscourseBreadcrumbsComponent = Ember.Component.extend({ classNames: ['category-breadcrumb'], tagName: 'ol', - parentCategory: Em.computed.alias('category.parentCategory') + parentCategory: Em.computed.alias('category.parentCategory'), + + parentCategories: Em.computed.filter('categories', function(c) { + return !c.get('parentCategory'); + }), + + targetCategory: function() { + // Note we can't use Em.computed.or here because it returns a boolean not the object + return this.get('parentCategory') || this.get('category'); + }.property('parentCategory', 'category'), + + childCategories: Em.computed.filter('categories', function(c) { + return c.get('parentCategory') === this.get('targetCategory'); + }) + }); diff --git a/app/assets/javascripts/discourse/components/categorydrop_component.js b/app/assets/javascripts/discourse/components/categorydrop_component.js new file mode 100644 index 00000000000..e1cec611aa5 --- /dev/null +++ b/app/assets/javascripts/discourse/components/categorydrop_component.js @@ -0,0 +1,44 @@ +Discourse.DiscourseCategorydropComponent = Ember.Component.extend({ + + classNameBindings: ['category::no-category', 'categories:has-drop'], + tagName: 'li', + + iconClass: function() { + if (this.get('expanded')) { return "icon icon-caret-down"; } + return "icon icon-caret-right"; + }.property('expanded'), + + actions: { + expand: function() { + if (this.get('expanded')) { + this.close(); + return; + } + + if (this.get('categories')) { + this.set('expanded', true); + } + + var self = this, + $dropdown = this.$()[0]; + + $('html').on('click.category-drop', function(e) { + var closest = $(e.target).closest($dropdown); + return (closest.length && closest[0] === $dropdown) ? true : self.close(); + }); + } + }, + + close: function() { + $('html').off('click.category-drop'); + this.set('expanded', false); + }, + + didInsertElement: function() { + }, + + willDestroyElement: function() { + $('html').off('click.category-drop'); + } + +}); diff --git a/app/assets/javascripts/discourse/templates/components/discourse-breadcrumbs.js.handlebars b/app/assets/javascripts/discourse/templates/components/discourse-breadcrumbs.js.handlebars index e076c749ffb..c20dfc4a542 100644 --- a/app/assets/javascripts/discourse/templates/components/discourse-breadcrumbs.js.handlebars +++ b/app/assets/javascripts/discourse/templates/components/discourse-breadcrumbs.js.handlebars @@ -1,18 +1,9 @@ -
  • - {{title}} - -
  • +{{discourse-categorydrop title=title categories=parentCategories}} + +{{discourse-categorydrop category=targetCategory categories=childCategories}} {{#if parentCategory}} -
  • - {{discourse-categorydrop category=parentCategory categories=categories}} -
  • -{{/if}} - -{{#if category}} -
  • - {{discourse-categorydrop category=category}} -
  • + {{discourse-categorydrop category=category}} {{/if}}
    \ No newline at end of file diff --git a/app/assets/javascripts/discourse/templates/components/discourse-categorydrop.js.handlebars b/app/assets/javascripts/discourse/templates/components/discourse-categorydrop.js.handlebars index 35ba8bcf4a8..5be553f5c43 100644 --- a/app/assets/javascripts/discourse/templates/components/discourse-categorydrop.js.handlebars +++ b/app/assets/javascripts/discourse/templates/components/discourse-categorydrop.js.handlebars @@ -1,4 +1,15 @@ -{{categoryLink category}} +{{#if category}} + {{categoryLink category}} +{{else}} + {{title}} +{{/if}} + {{#if categories}} - + + +
    + {{#each categories}} +
    {{categoryLink this}}
    + {{/each}} +
    {{/if}} \ No newline at end of file diff --git a/app/assets/stylesheets/desktop/topic.scss b/app/assets/stylesheets/desktop/topic.scss index 3f156d29161..70451b2b8a9 100644 --- a/app/assets/stylesheets/desktop/topic.scss +++ b/app/assets/stylesheets/desktop/topic.scss @@ -203,25 +203,62 @@ ol.category-breadcrumb { margin: 0 0 10px 0; padding: 0; - .first-caret { - margin: 0 3px 0 3px; - color: #666; - font-size: 13px; - } - li { float: left; margin-right: 5px; border: 1px solid transparent; - height: 21px; - padding-top: 2px; + + &.no-category { + font-size: 13px; + + a { + margin-right: 3px; + margin-left: 4px; + + &:hover { + color: black; + } + } + + button { + padding-left: 6px; + &:hover { + background-color: #eee; + } + } + + &.has-drop:hover { + background-color: #f9f9f9; + button { + border-left: 1px solid #bbb; + } + } + + .category-menu { + margin-left: -1px; + padding: 8px 5px 0 3px; + } + } + + .badge-category { + margin: 0; + display: inline-block; + padding: 1px 6px 2px 6px; + } button { background: none; border: 1px solid transparent; - padding: 0px 4px; + padding: 0 4px 0 2px; margin: 0; + height: 22px; color: #666; + line-height: 13px; + text-align: center; + + i.icon-caret-down { + margin-left: -2px; + } } &.has-drop:hover { @@ -238,5 +275,19 @@ ol.category-breadcrumb { clear: both; } + .category-menu { + overflow-x: hidden; + overflow-y: scroll; + position: absolute; + border: 1px solid #ccc; + background-color: white; + width: 200px; + height: 200px; + padding: 8px 5px 0 7px; + z-index: 100; + div { + margin-bottom: 10px; + } + } } From 61468f6f27eebf4f302e0045e44fffed4d7be6af Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Thu, 24 Oct 2013 17:03:28 -0400 Subject: [PATCH 05/33] Can assign a parent category to a category. --- .../controllers/edit_category_controller.js | 42 ++++++++++++++----- .../javascripts/discourse/models/category.js | 3 +- .../modal/edit_category.js.handlebars | 5 +++ .../discourse/views/category_chooser_view.js | 14 ++++--- .../discourse/views/modal/modal_body_view.js | 6 --- app/assets/stylesheets/desktop/modal.scss | 13 ++++++ app/controllers/categories_controller.rb | 2 +- app/models/category.rb | 4 +- config/locales/client.en.yml | 1 + config/locales/server.en.yml | 4 +- 10 files changed, 67 insertions(+), 27 deletions(-) diff --git a/app/assets/javascripts/discourse/controllers/edit_category_controller.js b/app/assets/javascripts/discourse/controllers/edit_category_controller.js index 0686254af13..6be805503a9 100644 --- a/app/assets/javascripts/discourse/controllers/edit_category_controller.js +++ b/app/assets/javascripts/discourse/controllers/edit_category_controller.js @@ -13,6 +13,12 @@ Discourse.EditCategoryController = Discourse.ObjectController.extend(Discourse.M settingsSelected: Ember.computed.equal('selectedTab', 'settings'), foregroundColors: ['FFFFFF', '000000'], + parentCategories: function() { + return Discourse.Category.list().filter(function (c) { + return !c.get('parentCategory'); + }); + }.property(), + onShow: function() { this.changeSize(); this.titleChanged(); @@ -122,17 +128,27 @@ Discourse.EditCategoryController = Discourse.ObjectController.extend(Discourse.M }, saveCategory: function() { - var categoryController = this; + var self = this, + model = this.get('model'), + parentCategory = Discourse.Category.list().findBy('id', parseInt(model.get('parent_category_id'), 10)); + this.set('saving', true); + model.set('parentCategory', parentCategory); + var newSlug = Discourse.Category.slugFor(this.get('model')); + this.get('model').save().then(function(result) { // success - categoryController.send('closeModal'); - Discourse.URL.redirectTo("/category/" + Discourse.Category.slugFor(result.category)); - }, function(errors) { - // errors - if(errors.length === 0) errors.push(I18n.t("category.creation_error")); - categoryController.displayErrors(errors); - categoryController.set('saving', false); + self.send('closeModal'); + Discourse.URL.redirectTo("/category/" + newSlug); + }, function(error) { + + if (error && error.responseText) { + self.flash($.parseJSON(error.responseText).errors[0]); + } else { + self.flash(I18n.t('generic_error')); + } + + self.set('saving', false); }); }, @@ -147,8 +163,14 @@ Discourse.EditCategoryController = Discourse.ObjectController.extend(Discourse.M // success self.send('closeModal'); Discourse.URL.redirectTo("/categories"); - }, function(jqXHR){ - // error + }, function(error){ + + if (error && error.responseText) { + self.flash($.parseJSON(error.responseText).errors[0]); + } else { + self.flash(I18n.t('generic_error')); + } + self.send('showModal'); self.displayErrors([I18n.t("category.delete_error")]); self.set('deleting', false); diff --git a/app/assets/javascripts/discourse/models/category.js b/app/assets/javascripts/discourse/models/category.js index cf32f9d93ff..3a2f3be31e7 100644 --- a/app/assets/javascripts/discourse/models/category.js +++ b/app/assets/javascripts/discourse/models/category.js @@ -58,7 +58,8 @@ Discourse.Category = Discourse.Model.extend({ secure: this.get('secure'), permissions: this.get('permissionsForUpdate'), auto_close_days: this.get('auto_close_days'), - position: this.get('position') + position: this.get('position'), + parent_category_id: this.get('parent_category_id') }, type: this.get('id') ? 'PUT' : 'POST' }); diff --git a/app/assets/javascripts/discourse/templates/modal/edit_category.js.handlebars b/app/assets/javascripts/discourse/templates/modal/edit_category.js.handlebars index e8bb4fd1522..55e43efad11 100644 --- a/app/assets/javascripts/discourse/templates/modal/edit_category.js.handlebars +++ b/app/assets/javascripts/discourse/templates/modal/edit_category.js.handlebars @@ -21,6 +21,11 @@ {{textField value=name placeholderKey="category.name_placeholder" maxlength="50"}} +
    + + {{categoryChooser valueAttribute="id" value=parent_category_id categories=parentCategories}} +
    + {{#unless isUncategorized}}
    diff --git a/app/assets/javascripts/discourse/views/category_chooser_view.js b/app/assets/javascripts/discourse/views/category_chooser_view.js index c7448842620..005ff0c642a 100644 --- a/app/assets/javascripts/discourse/views/category_chooser_view.js +++ b/app/assets/javascripts/discourse/views/category_chooser_view.js @@ -12,14 +12,16 @@ Discourse.CategoryChooserView = Discourse.ComboboxView.extend({ dataAttributes: ['name', 'color', 'text_color', 'description_text', 'topic_count'], valueBinding: Ember.Binding.oneWay('source'), + content: Em.computed.filter('categories', function(c) { + var uncategorized_id = Discourse.Site.currentProp("uncategorized_category_id"); + return c.get('permission') === Discourse.PermissionType.FULL && c.get('id') !== uncategorized_id; + }), + init: function() { this._super(); - // TODO perhaps allow passing a param in to select if we need full or not - - var uncategorized_id = Discourse.Site.currentProp("uncategorized_category_id"); - this.set('content', _.filter(Discourse.Category.list(), function(c){ - return c.permission === Discourse.PermissionType.FULL && c.id !== uncategorized_id; - })); + if (!this.get('categories')) { + this.set('categories', Discourse.Category.list()); + } }, none: function() { diff --git a/app/assets/javascripts/discourse/views/modal/modal_body_view.js b/app/assets/javascripts/discourse/views/modal/modal_body_view.js index 77b8b6dfa6c..bfaceb5c586 100644 --- a/app/assets/javascripts/discourse/views/modal/modal_body_view.js +++ b/app/assets/javascripts/discourse/views/modal/modal_body_view.js @@ -27,12 +27,6 @@ Discourse.ModalBodyView = Discourse.View.extend({ } }, - // Pass the errors to our errors view - displayErrors: function(errors, callback) { - this.set('parentView.parentView.modalErrorsView.errors', errors); - if (typeof callback === "function") callback(); - }, - flashMessageChanged: function() { var flashMessage = this.get('controller.flashMessage'); if (flashMessage) { diff --git a/app/assets/stylesheets/desktop/modal.scss b/app/assets/stylesheets/desktop/modal.scss index a4999ad6034..03faf9ca6f8 100644 --- a/app/assets/stylesheets/desktop/modal.scss +++ b/app/assets/stylesheets/desktop/modal.scss @@ -164,6 +164,19 @@ border-bottom: 1px solid #bbb; } + + .category-combobox { + width: 430px; + + .chzn-drop { + left: -9000px; + width: 428px; + } + .chzn-search input { + width: 378px; + } + } + &.hidden { display: none; } diff --git a/app/controllers/categories_controller.rb b/app/controllers/categories_controller.rb index 5110dc6d57b..6b0853a09b1 100644 --- a/app/controllers/categories_controller.rb +++ b/app/controllers/categories_controller.rb @@ -90,7 +90,7 @@ class CategoriesController < ApplicationController end end - params.permit(*required_param_keys, :position, :hotness, :auto_close_days, :permissions => [*p.try(:keys)]) + params.permit(*required_param_keys, :position, :hotness, :parent_category_id, :auto_close_days, :permissions => [*p.try(:keys)]) end end diff --git a/app/models/category.rb b/app/models/category.rb index 77d56f9fc2f..4669168f51c 100644 --- a/app/models/category.rb +++ b/app/models/category.rb @@ -187,10 +187,10 @@ SQL def parent_category_validator if parent_category_id - errors.add(:parent_category_id, "You can't link a category to itself") if parent_category_id == id + errors.add(:parent_category_id, I18n.t("category.errors.self_parent")) if parent_category_id == id grandfather_id = Category.where(id: parent_category_id).pluck(:parent_category_id).first - errors.add(:parent_category_id, "You can't have more than one level of subcategory") if grandfather_id + errors.add(:base, I18n.t("category.errors.depth")) if grandfather_id end end diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 74c9266bc24..8cb26e22965 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -975,6 +975,7 @@ en: add_permission: "Add Permission" this_year: "this year" position: "position" + parent: "Parent Category" flagging: title: 'Why are you flagging this post?' diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index e928e5daff6..6a6d576552c 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -159,7 +159,9 @@ en: topic_prefix: "Category definition for %{category}" replace_paragraph: "[Replace this first paragraph with a short description of your new category. This guidance will appear in the category selection area, so try to keep it below 200 characters.]" post_template: "%{replace_paragraph}\n\nUse the following paragraphs for a longer description, as well as to establish any category guidelines or rules.\n\nSome things to consider in any discussion replies below:\n\n- What is this category for? Why should people select this category for their topic?\n\n- How is this different than the other categories we already have?\n\n- Do we need this category?\n\n- Should we merge this with another category, or split it into more categories?\n" - + errors: + self_parent: "A subcategory's parent cannot be itself." + depth: "You can't nest a subcategory under another." trust_levels: newuser: title: "new user" From 017efdece5c96b71bb43d07833259322bd0a76c8 Mon Sep 17 00:00:00 2001 From: Neil Lalonde Date: Thu, 24 Oct 2013 17:18:10 -0400 Subject: [PATCH 06/33] A form to add ip addresses to be blocked or whitelisted --- .../screened_ip_address_form_component.js | 64 +++++++++++++++++++ ...n_logs_screened_ip_addresses_controller.js | 10 ++- .../admin/models/screened_ip_address.js | 18 +++--- .../logs/screened_ip_addresses.js.handlebars | 3 + .../screened-ip-address-form.handlebars | 4 ++ .../stylesheets/common/admin/admin_base.scss | 8 +++ .../admin/screened_ip_addresses_controller.rb | 9 +++ .../screened_ip_address_serializer.rb | 4 +- config/locales/client.en.yml | 6 +- config/routes.rb | 2 +- 10 files changed, 113 insertions(+), 15 deletions(-) create mode 100644 app/assets/javascripts/admin/components/screened_ip_address_form_component.js create mode 100644 app/assets/javascripts/discourse/templates/components/screened-ip-address-form.handlebars diff --git a/app/assets/javascripts/admin/components/screened_ip_address_form_component.js b/app/assets/javascripts/admin/components/screened_ip_address_form_component.js new file mode 100644 index 00000000000..dbae5a9aac9 --- /dev/null +++ b/app/assets/javascripts/admin/components/screened_ip_address_form_component.js @@ -0,0 +1,64 @@ +/** + A form to create an IP address that will be blocked or whitelisted. + Example usage: + + {{screened-ip-address-form action="recordAdded"}} + + where action is a callback on the controller or route that will get called after + the new record is successfully saved. It is called with the new ScreenedIpAddress record + as an argument. + + @class ScreenedIpAddressFormComponent + @extends Ember.Component + @namespace Discourse + @module Discourse +**/ +Discourse.ScreenedIpAddressFormComponent = Ember.Component.extend({ + classNames: ['screened-ip-address-form'], + formSubmitted: false, + actionName: 'block', + + actionNames: function() { + return [ + {id: 'block', name: I18n.t('admin.logs.screened_ips.actions.block')}, + {id: 'do_nothing', name: I18n.t('admin.logs.screened_ips.actions.do_nothing')} + ]; + }.property(), + + actions: { + submit: function() { + if (!this.get('formSubmitted')) { + var self = this; + this.set('formSubmitted', true); + var screenedIpAddress = Discourse.ScreenedIpAddress.create({ip_address: this.get('ip_address'), action_name: this.get('actionName')}); + screenedIpAddress.save().then(function(result) { + self.set('ip_address', ''); + self.set('formSubmitted', false); + self.sendAction('action', Discourse.ScreenedIpAddress.create(result.screened_ip_address)); + Em.run.schedule('afterRender', function() { self.$('.ip-address-input').focus(); }); + }, function(e) { + self.set('formSubmitted', false); + var msg; + if (e.responseJSON && e.responseJSON.errors) { + msg = I18n.t("generic_error_with_reason", {error: e.responseJSON.errors.join('. ')}); + } else { + msg = I18n.t("generic_error"); + } + bootbox.alert(msg, function() { self.$('.ip-address-input').focus(); }); + }); + } + } + }, + + didInsertElement: function(e) { + var self = this; + this._super(); + Em.run.schedule('afterRender', function() { + self.$('.ip-address-input').keydown(function(e) { + if (e.keyCode === 13) { // enter key + self.send('submit'); + } + }); + }); + } +}); diff --git a/app/assets/javascripts/admin/controllers/admin_logs_screened_ip_addresses_controller.js b/app/assets/javascripts/admin/controllers/admin_logs_screened_ip_addresses_controller.js index 303b7fb3434..bbf06d69921 100644 --- a/app/assets/javascripts/admin/controllers/admin_logs_screened_ip_addresses_controller.js +++ b/app/assets/javascripts/admin/controllers/admin_logs_screened_ip_addresses_controller.js @@ -18,6 +18,12 @@ Discourse.AdminLogsScreenedIpAddressesController = Ember.ArrayController.extend( self.set('content', result); self.set('loading', false); }); + }, + + actions: { + recordAdded: function(arg) { + this.get("content").unshiftObject(arg); + } } }); @@ -27,12 +33,12 @@ Discourse.AdminLogsScreenedIpAddressController = Ember.ObjectController.extend({ actions: { allow: function(record) { - record.set('action', 'do_nothing'); + record.set('action_name', 'do_nothing'); this.send('save', record); }, block: function(record) { - record.set('action', 'block'); + record.set('action_name', 'block'); this.send('save', record); }, diff --git a/app/assets/javascripts/admin/models/screened_ip_address.js b/app/assets/javascripts/admin/models/screened_ip_address.js index 5a635214d1e..b209d7f6205 100644 --- a/app/assets/javascripts/admin/models/screened_ip_address.js +++ b/app/assets/javascripts/admin/models/screened_ip_address.js @@ -9,20 +9,20 @@ **/ Discourse.ScreenedIpAddress = Discourse.Model.extend({ actionName: function() { - return I18n.t("admin.logs.screened_ips.actions." + this.get('action')); - }.property('action'), + return I18n.t("admin.logs.screened_ips.actions." + this.get('action_name')); + }.property('action_name'), isBlocked: function() { - return (this.get('action') === 'block'); - }.property('action'), + return (this.get('action_name') === 'block'); + }.property('action_name'), actionIcon: function() { - if (this.get('action') === 'block') { + if (this.get('action_name') === 'block') { return this.get('blockIcon'); } else { return this.get('doNothingIcon'); } - }.property('action'), + }.property('action_name'), blockIcon: function() { return 'icon-ban-circle'; @@ -33,9 +33,9 @@ Discourse.ScreenedIpAddress = Discourse.Model.extend({ }.property(), save: function() { - return Discourse.ajax("/admin/logs/screened_ip_addresses/" + this.get('id') + ".json", { - type: 'PUT', - data: {ip_address: this.get('ip_address'), action_name: this.get('action')} + return Discourse.ajax("/admin/logs/screened_ip_addresses" + (this.id ? '/' + this.id : '') + ".json", { + type: this.id ? 'PUT' : 'POST', + data: {ip_address: this.get('ip_address'), action_name: this.get('action_name')} }); }, diff --git a/app/assets/javascripts/admin/templates/logs/screened_ip_addresses.js.handlebars b/app/assets/javascripts/admin/templates/logs/screened_ip_addresses.js.handlebars index b929daf340c..920306ae7e6 100644 --- a/app/assets/javascripts/admin/templates/logs/screened_ip_addresses.js.handlebars +++ b/app/assets/javascripts/admin/templates/logs/screened_ip_addresses.js.handlebars @@ -1,5 +1,8 @@

    {{i18n admin.logs.screened_ips.description}}

    +{{screened-ip-address-form action="recordAdded"}} +
    + {{#if loading}}
    {{i18n loading}}
    {{else}} diff --git a/app/assets/javascripts/discourse/templates/components/screened-ip-address-form.handlebars b/app/assets/javascripts/discourse/templates/components/screened-ip-address-form.handlebars new file mode 100644 index 00000000000..04ee5701372 --- /dev/null +++ b/app/assets/javascripts/discourse/templates/components/screened-ip-address-form.handlebars @@ -0,0 +1,4 @@ +{{i18n admin.logs.screened_ips.form.label}} +{{textField value=ip_address disabled=formSubmitted class="ip-address-input" placeholderKey="admin.logs.screened_ips.form.ip_address" autocorrect="off" autocapitalize="off"}} +{{combobox content=actionNames value=actionName}} + diff --git a/app/assets/stylesheets/common/admin/admin_base.scss b/app/assets/stylesheets/common/admin/admin_base.scss index 01e1c933da9..19c86a13e14 100644 --- a/app/assets/stylesheets/common/admin/admin_base.scss +++ b/app/assets/stylesheets/common/admin/admin_base.scss @@ -752,6 +752,14 @@ table.api-keys { } } +.screened-ip-address-form { + margin-left: 6px; + .combobox { + width: 130px; + top: 10px; + } +} + .screened-emails, .screened-urls { .ip_address { width: 110px; diff --git a/app/controllers/admin/screened_ip_addresses_controller.rb b/app/controllers/admin/screened_ip_addresses_controller.rb index 974cf4c727d..00b5acb27ff 100644 --- a/app/controllers/admin/screened_ip_addresses_controller.rb +++ b/app/controllers/admin/screened_ip_addresses_controller.rb @@ -7,6 +7,15 @@ class Admin::ScreenedIpAddressesController < Admin::AdminController render_serialized(screened_ip_addresses, ScreenedIpAddressSerializer) end + def create + screened_ip_address = ScreenedIpAddress.new(allowed_params) + if screened_ip_address.save + render_serialized(screened_ip_address, ScreenedIpAddressSerializer) + else + render_json_error(screened_ip_address) + end + end + def update if @screened_ip_address.update_attributes(allowed_params) render json: success_json diff --git a/app/serializers/screened_ip_address_serializer.rb b/app/serializers/screened_ip_address_serializer.rb index d9d9048f59f..2f206792589 100644 --- a/app/serializers/screened_ip_address_serializer.rb +++ b/app/serializers/screened_ip_address_serializer.rb @@ -1,12 +1,12 @@ class ScreenedIpAddressSerializer < ApplicationSerializer attributes :id, :ip_address, - :action, + :action_name, :match_count, :last_match_at, :created_at - def action + def action_name ScreenedIpAddress.actions.key(object.action_type).to_s end diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 8cb26e22965..944550edb29 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -1268,11 +1268,15 @@ en: url: "URL" screened_ips: title: "Screened IPs" - description: "IP addresses that are being watched." + description: 'IP addresses that are being watched. Use "Allow" to whitelist IP addresses.' delete_confirm: "Are you sure you want to remove the rule for %{ip_address}?" actions: block: "Block" do_nothing: "Allow" + form: + label: "New:" + ip_address: "IP address" + add: "Add" impersonate: title: "Impersonate User" diff --git a/config/routes.rb b/config/routes.rb index 20dfb54c6a4..2513694d1ad 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -69,7 +69,7 @@ Discourse::Application.routes.draw do scope '/logs' do resources :staff_action_logs, only: [:index] resources :screened_emails, only: [:index] - resources :screened_ip_addresses, only: [:index, :update, :destroy] + resources :screened_ip_addresses, only: [:index, :create, :update, :destroy] resources :screened_urls, only: [:index] end From d149066ac62e5d5f638aa727c118f51181aecc11 Mon Sep 17 00:00:00 2001 From: Neil Lalonde Date: Thu, 24 Oct 2013 18:22:47 -0400 Subject: [PATCH 07/33] Show a warning on admin dashboard if running on an older patch level of ruby 2.0 --- app/models/admin_dashboard_data.rb | 5 +++++ config/locales/server.en.yml | 1 + 2 files changed, 6 insertions(+) diff --git a/app/models/admin_dashboard_data.rb b/app/models/admin_dashboard_data.rb index 30b5f3e555f..2a0ac42d9dd 100644 --- a/app/models/admin_dashboard_data.rb +++ b/app/models/admin_dashboard_data.rb @@ -22,6 +22,7 @@ class AdminDashboardData def problems [ rails_env_check, + ruby_version_check, host_names_check, gc_checks, sidekiq_check || queue_size_check, @@ -161,6 +162,10 @@ class AdminDashboardData I18n.t('dashboard.notification_email_warning') if SiteSetting.notification_email.blank? end + def ruby_version_check + I18n.t('dashboard.ruby_version_warning') if RUBY_VERSION == '2.0.0' and RUBY_PATCHLEVEL < 247 + end + # TODO: generalize this method of putting i18n keys with expiry in redis # that should be reported on the admin dashboard: diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 6a6d576552c..a41a342245d 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -418,6 +418,7 @@ en: dashboard: rails_env_warning: "Your server is running in %{env} mode." + ruby_version_warning: "You are running a version of Ruby 2.0.0 that is known to have problems. Upgrade to patch level 247 or later." host_names_warning: "Your config/database.yml file is using the default localhost hostname. Update it to use your site's hostname." gc_warning: 'Your server is using default ruby garbage collection parameters, which will not give you the best performance. Read this topic on performance tuning: Tuning Ruby and Rails for Discourse.' sidekiq_warning: 'Sidekiq is not running. Many tasks, like sending emails, are executed asynchronously by sidekiq. Please ensure at least one sidekiq process is running. Learn about Sidekiq here.' From 4929c5db71a1b9623c874949c57cf3b443fb8725 Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 25 Oct 2013 09:33:18 +1100 Subject: [PATCH 08/33] FIX: when allow_uncategorized_topics was false, PMs can not be created --- app/models/topic.rb | 7 ++++--- spec/models/topic_spec.rb | 5 +++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/models/topic.rb b/app/models/topic.rb index 619afa123f0..e3f5b745c5e 100644 --- a/app/models/topic.rb +++ b/app/models/topic.rb @@ -59,7 +59,8 @@ class Topic < ActiveRecord::Base 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 + !SiteSetting.allow_uncategorized_topics && + (t.archetype.nil? || t.archetype == Archetype.default) } @@ -106,7 +107,7 @@ class Topic < ActiveRecord::Base # Return private message topics scope :private_messages, lambda { - where(archetype: Archetype::private_message) + where(archetype: Archetype.private_message) } scope :listable_topics, lambda { where('topics.archetype <> ?', [Archetype.private_message]) } @@ -169,7 +170,7 @@ class Topic < ActiveRecord::Base Jobs.cancel_scheduled_job(:close_topic, {topic_id: id}) true end - if category_id.nil? && (archetype.nil? || archetype == "regular") + if category_id.nil? && (archetype.nil? || archetype == Archetype.default) self.category_id = SiteSetting.uncategorized_category_id end end diff --git a/spec/models/topic_spec.rb b/spec/models/topic_spec.rb index 92fb51d5be7..acdbfdb0529 100644 --- a/spec/models/topic_spec.rb +++ b/spec/models/topic_spec.rb @@ -185,6 +185,11 @@ describe Topic do topic.errors[:category_id].should be_present end + it "allows PMs" do + topic = Fabricate.build(:topic, category: nil, archetype: Archetype.private_message) + topic.should be_valid + end + it 'passes for topics with a category' do Fabricate.build(:topic, category: Fabricate(:category)).should be_valid end From 270fde7dbdcb68599ee977368825acc5db3986b8 Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 25 Oct 2013 10:31:33 +1100 Subject: [PATCH 09/33] correct rake test:prepare so it seeds the db correctly move category creation into seeds as well, so db can be seeded from structure.sql --- db/fixtures/categories.rb | 20 +++++++++++++++++++ ...131022045114_add_uncategorized_category.rb | 4 ++-- lib/tasks/db.rake | 4 ++++ 3 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 db/fixtures/categories.rb diff --git a/db/fixtures/categories.rb b/db/fixtures/categories.rb new file mode 100644 index 00000000000..1c1558cdb56 --- /dev/null +++ b/db/fixtures/categories.rb @@ -0,0 +1,20 @@ +SiteSetting.refresh! +if SiteSetting.uncategorized_category_id == -1 + puts "Seeding uncategorized category!" + + result = Category.exec_sql "SELECT 1 FROM categories WHERE name = 'uncategorized'" + name = 'uncategorized' + if result.count > 0 + name << SecureRandom.hex + end + + result = Category.exec_sql "INSERT INTO categories + (name,color,slug,description,text_color, user_id, created_at, updated_at, position) + VALUES ('#{name}', 'AB9364', 'uncategorized', '', 'FFFFFF', -1, now(), now(), 1 ) + RETURNING id + " + category_id = result[0]["id"].to_i + + Category.exec_sql "INSERT INTO site_settings(name, data_type, value, created_at, updated_at) + VALUES ('uncategorized_category_id', 3, #{category_id}, now(), now())" +end diff --git a/db/migrate/20131022045114_add_uncategorized_category.rb b/db/migrate/20131022045114_add_uncategorized_category.rb index d8416d91677..f44e851efc4 100644 --- a/db/migrate/20131022045114_add_uncategorized_category.rb +++ b/db/migrate/20131022045114_add_uncategorized_category.rb @@ -2,14 +2,14 @@ class AddUncategorizedCategory < ActiveRecord::Migration def up result = execute "SELECT 1 FROM categories WHERE name = 'uncategorized'" + 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 ) + VALUES ('#{name}', 'AB9364', 'uncategorized', '', 'FFFFFF', -1, now(), now(), 1 ) RETURNING id " category_id = result[0]["id"].to_i diff --git a/lib/tasks/db.rake b/lib/tasks/db.rake index f0dcb29a53a..ce531412f8f 100644 --- a/lib/tasks/db.rake +++ b/lib/tasks/db.rake @@ -2,3 +2,7 @@ task 'db:migrate' => 'environment' do SeedFu.seed end + +task 'test:prepare' => 'environment' do + SeedFu.seed +end From 572f036e741fbb3532722a19031f0faf38965b8d Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 25 Oct 2013 11:15:20 +1100 Subject: [PATCH 10/33] filter down the top level navs --- .../discourse/controllers/list_controller.js | 18 ++++++++++-------- .../javascripts/discourse/models/nav_item.js | 5 +++-- .../discourse/models/topic_tracking_state.js | 13 +++++++------ 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/app/assets/javascripts/discourse/controllers/list_controller.js b/app/assets/javascripts/discourse/controllers/list_controller.js index edf9765fdeb..072918c9b32 100644 --- a/app/assets/javascripts/discourse/controllers/list_controller.js +++ b/app/assets/javascripts/discourse/controllers/list_controller.js @@ -7,32 +7,34 @@ @module Discourse **/ Discourse.ListController = Discourse.Controller.extend({ - categoryBinding: 'topicList.category', + categoryBinding: "topicList.category", canCreateCategory: false, canCreateTopic: false, - needs: ['composer', 'modal', 'listTopics'], + needs: ["composer", "modal", "listTopics"], availableNavItems: function() { var loggedOn = !!Discourse.User.current(); + var category = this.get("category"); return Discourse.SiteSettings.top_menu.split("|").map(function(i) { return Discourse.NavItem.fromText(i, { - loggedOn: loggedOn + loggedOn: loggedOn, + category: category }); }).filter(function(i) { - return i !== null; + return i !== null && !(category && i.get("name") === "categories"); }); - }.property(), + }.property("category"), createTopicText: function() { - if (this.get('category.name')) { + if (this.get("category.name")) { return I18n.t("topic.create_in", { - categoryName: this.get('category.name') + categoryName: this.get("category.name") }); } else { return I18n.t("topic.create"); } - }.property('category.name'), + }.property("category.name"), /** Refresh our current topic list diff --git a/app/assets/javascripts/discourse/models/nav_item.js b/app/assets/javascripts/discourse/models/nav_item.js index c4df6c42dc9..b5a4de47c35 100644 --- a/app/assets/javascripts/discourse/models/nav_item.js +++ b/app/assets/javascripts/discourse/models/nav_item.js @@ -42,7 +42,7 @@ Discourse.NavItem = Discourse.Model.extend({ count: function() { var state = this.get('topicTrackingState'); if (state) { - return state.lookupCount(this.get('name')); + return state.lookupCount(this.get('name'), this.get('category')); } }.property('topicTrackingState.messageCount'), @@ -71,7 +71,8 @@ Discourse.NavItem.reopenClass({ opts = { name: name, hasIcon: name === "unread" || name === "favorited", - filters: split.splice(1) + filters: split.splice(1), + category: opts.category }; return Discourse.NavItem.create(opts); diff --git a/app/assets/javascripts/discourse/models/topic_tracking_state.js b/app/assets/javascripts/discourse/models/topic_tracking_state.js index 882d0dd6af3..90584c7839a 100644 --- a/app/assets/javascripts/discourse/models/topic_tracking_state.js +++ b/app/assets/javascripts/discourse/models/topic_tracking_state.js @@ -159,15 +159,16 @@ Discourse.TopicTrackingState = Discourse.Model.extend({ return count; }, - lookupCount: function(name){ + lookupCount: function(name, category){ + var categoryName = Em.get(category, "name"); if(name==="new") { - return this.countNew(); + return this.countNew(categoryName); } else if(name==="unread") { - return this.countUnread(); + return this.countUnread(categoryName); } else { - var category = name.split("/")[1]; - if(category) { - return this.countCategory(category); + categoryName = name.split("/")[1]; + if(categoryName) { + return this.countCategory(categoryName); } } }, From a65407b2cbc5e9621c24f92e775b1247985e43ae Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 25 Oct 2013 14:18:34 +1100 Subject: [PATCH 11/33] implemented UI for filtered down by category view --- .../discourse/controllers/list_controller.js | 8 +- .../javascripts/discourse/models/category.js | 7 ++ .../javascripts/discourse/models/nav_item.js | 14 ++- .../discourse/models/topic_list.js | 1 - .../discourse/routes/list_category_route.js | 9 +- .../discourse-categorydrop.js.handlebars | 4 +- .../discourse/templates/list.js.handlebars | 7 +- .../list/topic_list_item.js.handlebars | 2 + .../templates/list/topics.js.handlebars | 6 +- .../list/wide_categories.js.handlebars | 4 +- .../discourse/views/nav_item_view.js | 9 +- .../stylesheets/desktop/topic-list.scss | 78 +++++++------- app/assets/stylesheets/desktop/topic.scss | 17 ++- app/assets/stylesheets/mobile/topic-list.scss | 100 +++++++++++------- 14 files changed, 157 insertions(+), 109 deletions(-) diff --git a/app/assets/javascripts/discourse/controllers/list_controller.js b/app/assets/javascripts/discourse/controllers/list_controller.js index 072918c9b32..45f02dc7115 100644 --- a/app/assets/javascripts/discourse/controllers/list_controller.js +++ b/app/assets/javascripts/discourse/controllers/list_controller.js @@ -22,7 +22,7 @@ Discourse.ListController = Discourse.Controller.extend({ category: category }); }).filter(function(i) { - return i !== null && !(category && i.get("name") === "categories"); + return i !== null && !(category && i.get("name").indexOf("categor") === 0); }); }.property("category"), @@ -134,7 +134,11 @@ Discourse.ListController = Discourse.Controller.extend({ } else { return false; } - }.property('category') + }.property('category'), + + categories: function() { + return Discourse.Category.list(); + }.property() }); diff --git a/app/assets/javascripts/discourse/models/category.js b/app/assets/javascripts/discourse/models/category.js index 3a2f3be31e7..dc75876c55c 100644 --- a/app/assets/javascripts/discourse/models/category.js +++ b/app/assets/javascripts/discourse/models/category.js @@ -34,6 +34,13 @@ Discourse.Category = Discourse.Model.extend({ return Discourse.getURL("/category/") + (this.get('slug')); }.property('name'), + unreadUrl: function() { + return this.get('url') + '/unread'; + }.property('url'), + + newUrl: function() { + return this.get('url') + '/new'; + }.property('url'), style: function() { return "background-color: #" + (this.get('category.color')) + "; color: #" + (this.get('category.text_color')) + ";"; diff --git a/app/assets/javascripts/discourse/models/nav_item.js b/app/assets/javascripts/discourse/models/nav_item.js index b5a4de47c35..5bb6ef44a94 100644 --- a/app/assets/javascripts/discourse/models/nav_item.js +++ b/app/assets/javascripts/discourse/models/nav_item.js @@ -31,11 +31,21 @@ Discourse.NavItem = Discourse.Model.extend({ // href from this item href: function() { + return Discourse.getURL("/") + this.get('filterMode'); + }.property('filterMode'), + + // href from this item + filterMode: function() { var name = this.get('name'); if( name.split('/')[0] === 'category' ) { - return Discourse.getURL("/") + 'category/' + this.get('categorySlug'); + return 'category/' + this.get('categorySlug'); } else { - return Discourse.getURL("/") + name.replace(' ', '-'); + var mode = ""; + var category = this.get("category"); + if(category){ + mode += "category/" + category.get("slug") + "/"; + } + return mode + name.replace(' ', '-'); } }.property('name'), diff --git a/app/assets/javascripts/discourse/models/topic_list.js b/app/assets/javascripts/discourse/models/topic_list.js index 97b6ea4ab73..f27400cddf6 100644 --- a/app/assets/javascripts/discourse/models/topic_list.js +++ b/app/assets/javascripts/discourse/models/topic_list.js @@ -146,7 +146,6 @@ Discourse.TopicList.reopenClass({ return Ember.RSVP.resolve(list); } session.setProperties({topicList: null, topicListScrollPos: null}); - return Discourse.TopicList.find(filter, menuItem.get('excludeCategory')); } }); diff --git a/app/assets/javascripts/discourse/routes/list_category_route.js b/app/assets/javascripts/discourse/routes/list_category_route.js index cef425dff8e..e85c8ccf372 100644 --- a/app/assets/javascripts/discourse/routes/list_category_route.js +++ b/app/assets/javascripts/discourse/routes/list_category_route.js @@ -22,11 +22,14 @@ Discourse.ListCategoryRoute = Discourse.FilteredListRoute.extend({ } var listController = this.controllerFor('list'), - urlId = Discourse.Category.slugFor(category), + categorySlug = Discourse.Category.slugFor(category), self = this; - listController.set('filterMode', "category/" + urlId); - listController.load("category/" + urlId).then(function(topicList) { + var filter = this.filter || "latest"; + var url = "category/" + categorySlug + "/" + filter; + + listController.set('filterMode', url); + listController.load(url).then(function(topicList) { listController.setProperties({ canCreateTopic: topicList.get('can_create_topic'), category: category diff --git a/app/assets/javascripts/discourse/templates/components/discourse-categorydrop.js.handlebars b/app/assets/javascripts/discourse/templates/components/discourse-categorydrop.js.handlebars index 5be553f5c43..f2148db82c2 100644 --- a/app/assets/javascripts/discourse/templates/components/discourse-categorydrop.js.handlebars +++ b/app/assets/javascripts/discourse/templates/components/discourse-categorydrop.js.handlebars @@ -1,7 +1,7 @@ {{#if category}} {{categoryLink category}} {{else}} - {{title}} + {{/if}} {{#if categories}} @@ -12,4 +12,4 @@
    {{categoryLink this}}
    {{/each}}
    -{{/if}} \ No newline at end of file +{{/if}} diff --git a/app/assets/javascripts/discourse/templates/list.js.handlebars b/app/assets/javascripts/discourse/templates/list.js.handlebars index 2f5a9f32d2b..ab0c79b242f 100644 --- a/app/assets/javascripts/discourse/templates/list.js.handlebars +++ b/app/assets/javascripts/discourse/templates/list.js.handlebars @@ -1,5 +1,10 @@ -
    +
    + + {{#if category}} + {{discourse-breadcrumbs category=category categories=categories}} + {{/if}} + diff --git a/app/assets/javascripts/discourse/templates/list/topic_list_item.js.handlebars b/app/assets/javascripts/discourse/templates/list/topic_list_item.js.handlebars index 81d25ea5e7a..a1f65f4cfe8 100644 --- a/app/assets/javascripts/discourse/templates/list/topic_list_item.js.handlebars +++ b/app/assets/javascripts/discourse/templates/list/topic_list_item.js.handlebars @@ -41,9 +41,11 @@ {{/if}} +{{#unless controller.category}} {{categoryLink category}} +{{/unless}} {{#each posters}} diff --git a/app/assets/javascripts/discourse/templates/list/topics.js.handlebars b/app/assets/javascripts/discourse/templates/list/topics.js.handlebars index c5d8ad3faf8..9753fda8580 100644 --- a/app/assets/javascripts/discourse/templates/list/topics.js.handlebars +++ b/app/assets/javascripts/discourse/templates/list/topics.js.handlebars @@ -10,10 +10,6 @@ {{/if}} - {{#if category}} - {{discourse-breadcrumbs title=Discourse.SiteSettings.title category=category categories=categories}} - {{/if}} - @@ -23,7 +19,9 @@ + {{#unless category}} + {{/unless}} 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 444d63133ec..d76e8549591 100644 --- a/app/assets/javascripts/discourse/templates/list/wide_categories.js.handlebars +++ b/app/assets/javascripts/discourse/templates/list/wide_categories.js.handlebars @@ -22,10 +22,10 @@ {{/if}} {{categoryLink this allowUncategorized=true}} {{#if unreadTopics}} - {{unbound unreadTopics}} + {{unbound unreadTopics}} {{/if}} {{#if newTopics}} - {{unbound newTopics}} + {{unbound newTopics}} {{/if}}
    {{i18n topic.title}} {{i18n category_title}}{{i18n top_contributors}} {{i18n posts}}