Your account has been temporarily suspended (ends in <%= time_ago_in_words(current_user.community_user.suspension_end) %>). We look forward to your return and continued contributions to the site after this period. In the event of continued rule violations after this period, your account may be suspended for longer periods. If you have any questions about this suspension or would like to dispute it, contact us.
+ You can also sign in with your Single Sign-On provider.
+ <%= link_to "SSO Sign in", new_saml_session_path(resource_name), class: 'button is-primary is-filled' %>
+
+<% end %>
+
<%= form_for(resource, as: resource_name, url: session_path(resource_name), method: :post) do |f| %>
You can also sign in with your Single Sign-On provider.
- <%= link_to "SSO Sign in", new_saml_session_path(resource_name), class: 'button is-primary is-filled' %>
+ <%= link_to "SSO Sign in", new_saml_user_session_path(resource_name), class: 'button is-primary is-filled' %>
<%= f.label :email, class: "form-element" %>
diff --git a/app/views/devise/shared/_links.html.erb b/app/views/devise/shared/_links.html.erb
index d50e9269f..15b79838a 100644
--- a/app/views/devise/shared/_links.html.erb
+++ b/app/views/devise/shared/_links.html.erb
@@ -1,10 +1,9 @@
-<%- if controller_name != 'sessions' && controller_name != 'saml_sessions' %>
- <%- if devise_sign_in_enabled? %>
- <%= link_to "Sign in", new_session_path(resource_name) %>
- <% end %>
- <%- if sso_sign_in_enabled? %>
- <%= link_to "SSO Sign in", new_saml_session_path(resource_name) %>
- <% end %>
+<%- if controller_name != 'sessions' && devise_sign_in_enabled? %>
+ <%= link_to "Sign in", new_session_path(resource_name) %>
+<% end %>
+
+<%- if controller_name != 'saml_sessions' && sso_sign_in_enabled? %>
+ <%= link_to "SSO Sign in", new_saml_user_session_path(resource_name) %>
<% end %>
<%- if devise_mapping.registerable? && controller_name != 'registrations' && devise_sign_in_enabled? %>
From 0bdd4d4772694c1519ea58e5d7a4e5811ae2af52 Mon Sep 17 00:00:00 2001
From: Taico Aerts
Date: Mon, 29 Aug 2022 13:06:48 +0200
Subject: [PATCH 052/968] Add sign in with sso notice on registrations page
---
app/controllers/users/saml_sessions_controller.rb | 2 +-
app/views/devise/registrations/new.html.erb | 7 +++++++
2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/app/controllers/users/saml_sessions_controller.rb b/app/controllers/users/saml_sessions_controller.rb
index 24d36d8b1..afd2587d5 100644
--- a/app/controllers/users/saml_sessions_controller.rb
+++ b/app/controllers/users/saml_sessions_controller.rb
@@ -30,4 +30,4 @@ def create
end
end
end
-end
\ No newline at end of file
+end
diff --git a/app/views/devise/registrations/new.html.erb b/app/views/devise/registrations/new.html.erb
index 1679be35b..8fc1a62a7 100644
--- a/app/views/devise/registrations/new.html.erb
+++ b/app/views/devise/registrations/new.html.erb
@@ -1,5 +1,12 @@
Sign up
+<% if sso_sign_in_enabled? %>
+
+ You can also sign in with your Single Sign-On provider.
+ <%= link_to "SSO Sign in", new_saml_user_session_path(resource_name), class: 'button is-primary is-filled' %>
+
+<% end %>
+
If you have an account on another Codidact site,
don't create a new account here - <%= link_to 'sign in', new_user_session_path %> with your existing details instead.
From 0ee3045a647c039ad7878ce68c7675aed729eaf1 Mon Sep 17 00:00:00 2001
From: Taico Aerts
Date: Thu, 15 Sep 2022 20:23:57 +0200
Subject: [PATCH 053/968] Add initial scaffolding for tag synonyms
Adds tag synonyms into the search for tags
---
app/models/tag.rb | 10 +++++++---
app/models/tag_synonym.rb | 3 +++
db/migrate/20220915181608_create_tag_synonyms.rb | 9 +++++++++
db/schema.rb | 12 +++++++++++-
test/fixtures/tag_synonyms.yml | 11 +++++++++++
test/models/tag_synonym_test.rb | 7 +++++++
6 files changed, 48 insertions(+), 4 deletions(-)
create mode 100644 app/models/tag_synonym.rb
create mode 100644 db/migrate/20220915181608_create_tag_synonyms.rb
create mode 100644 test/fixtures/tag_synonyms.yml
create mode 100644 test/models/tag_synonym_test.rb
diff --git a/app/models/tag.rb b/app/models/tag.rb
index 1b78a95fe..e53e6f7b6 100644
--- a/app/models/tag.rb
+++ b/app/models/tag.rb
@@ -5,6 +5,7 @@ class Tag < ApplicationRecord
has_many :children, class_name: 'Tag', foreign_key: :parent_id
has_many :children_with_paths, class_name: 'TagWithPath', foreign_key: :parent_id
has_many :post_history_tags
+ has_many :tag_synonyms, dependent: :destroy
belongs_to :tag_set
belongs_to :parent, class_name: 'Tag', optional: true
@@ -16,9 +17,12 @@ class Tag < ApplicationRecord
validates :name, uniqueness: { scope: [:tag_set_id], case_sensitive: false }
def self.search(term)
- where('name LIKE ?', "%#{sanitize_sql_like(term)}%")
- .or(where('excerpt LIKE ?', "%#{sanitize_sql_like(term)}%"))
- .order(Arel.sql(sanitize_sql_array(['name LIKE ? DESC, name', "#{sanitize_sql_like(term)}%"])))
+ base = joins(:tag_synonyms)
+ base.where('name LIKE ?', "%#{sanitize_sql_like(term)}%")
+ .or(base.where('excerpt LIKE ?', "%#{sanitize_sql_like(term)}%"))
+ .or(base.where('tag_synonyms.name LIKE ?', "%#{sanitize_sql_like(term)}%"))
+ .distinct
+ .order(Arel.sql(sanitize_sql_array(['name LIKE ? DESC, tag_synonyms.name LIKE ? DESC, name', "#{sanitize_sql_like(term)}%"])))
end
def all_children
diff --git a/app/models/tag_synonym.rb b/app/models/tag_synonym.rb
new file mode 100644
index 000000000..d0b38117c
--- /dev/null
+++ b/app/models/tag_synonym.rb
@@ -0,0 +1,3 @@
+class TagSynonym < ApplicationRecord
+ belongs_to :tag
+end
diff --git a/db/migrate/20220915181608_create_tag_synonyms.rb b/db/migrate/20220915181608_create_tag_synonyms.rb
new file mode 100644
index 000000000..063f6e9a7
--- /dev/null
+++ b/db/migrate/20220915181608_create_tag_synonyms.rb
@@ -0,0 +1,9 @@
+class CreateTagSynonyms < ActiveRecord::Migration[7.0]
+ def change
+ create_table :tag_synonyms do |t|
+ t.belongs_to :tag, foreign_key: true
+ t.string :name, null: false, index: { unique: true }
+ t.timestamps
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 9cc021c6d..c236ac632 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[7.0].define(version: 2022_09_13_183826) do
+ActiveRecord::Schema[7.0].define(version: 2022_09_15_181608) do
create_table "abilities", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
t.bigint "community_id"
t.string "name"
@@ -594,6 +594,15 @@
t.index ["community_id"], name: "index_tag_sets_on_community_id"
end
+ create_table "tag_synonyms", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t|
+ t.bigint "tag_id"
+ t.string "name", null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.index ["name"], name: "index_tag_synonyms_on_name", unique: true
+ t.index ["tag_id"], name: "index_tag_synonyms_on_tag_id"
+ end
+
create_table "tags", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t|
t.string "name"
t.datetime "created_at", precision: nil, null: false
@@ -761,6 +770,7 @@
add_foreign_key "suggested_edits", "posts"
add_foreign_key "suggested_edits", "users"
add_foreign_key "suggested_edits", "users", column: "decided_by_id"
+ add_foreign_key "tag_synonyms", "tags"
add_foreign_key "tags", "communities"
add_foreign_key "tags", "tags", column: "parent_id"
add_foreign_key "thread_followers", "posts"
diff --git a/test/fixtures/tag_synonyms.yml b/test/fixtures/tag_synonyms.yml
new file mode 100644
index 000000000..d7a332924
--- /dev/null
+++ b/test/fixtures/tag_synonyms.yml
@@ -0,0 +1,11 @@
+# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+
+# This model initially had no columns defined. If you add columns to the
+# model remove the "{}" from the fixture names and add the columns immediately
+# below each fixture, per the syntax in the comments below
+#
+one: {}
+# column: value
+#
+two: {}
+# column: value
diff --git a/test/models/tag_synonym_test.rb b/test/models/tag_synonym_test.rb
new file mode 100644
index 000000000..ea074d1e6
--- /dev/null
+++ b/test/models/tag_synonym_test.rb
@@ -0,0 +1,7 @@
+require "test_helper"
+
+class TagSynonymTest < ActiveSupport::TestCase
+ # test "the truth" do
+ # assert true
+ # end
+end
From 25c264c4a29b033268b811f4738a095018bb13ae Mon Sep 17 00:00:00 2001
From: Taico Aerts
Date: Thu, 15 Sep 2022 21:03:40 +0200
Subject: [PATCH 054/968] Add display for tag synonyms
---
app/views/tags/show.html.erb | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/app/views/tags/show.html.erb b/app/views/tags/show.html.erb
index b2350de22..0aba8b951 100644
--- a/app/views/tags/show.html.erb
+++ b/app/views/tags/show.html.erb
@@ -17,6 +17,14 @@
<% end %>
<% end %>
+
+<% if @tag.tag_synonyms.any? %>
+ Synonyms:
+ <% @tag.tag_synonyms.each do |synonym| %>
+ <%= synonym.name %>
+ <% end %>
+<% end %>
+
From 5e8d782bf03924913b0b31511aede0a163792df6 Mon Sep 17 00:00:00 2001
From: Taico Aerts
Date: Thu, 22 Sep 2022 13:48:58 +0200
Subject: [PATCH 078/968] Fix rubocop issues
---
app/controllers/tags_controller.rb | 3 ++-
app/models/tag.rb | 10 +++++-----
test/models/tag_synonym_test.rb | 2 +-
3 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/app/controllers/tags_controller.rb b/app/controllers/tags_controller.rb
index 74e19f2d7..3661c38c7 100644
--- a/app/controllers/tags_controller.rb
+++ b/app/controllers/tags_controller.rb
@@ -194,7 +194,8 @@ def set_category
end
def tag_params
- params.require(:tag).permit(:excerpt, :wiki_markdown, :parent_id, :name, tag_synonyms_attributes: [:id, :name, :_destroy])
+ params.require(:tag).permit(:excerpt, :wiki_markdown, :parent_id, :name,
+ tag_synonyms_attributes: [:id, :name, :_destroy])
end
def exec(sql_array)
diff --git a/app/models/tag.rb b/app/models/tag.rb
index ecac74137..98e430147 100644
--- a/app/models/tag.rb
+++ b/app/models/tag.rb
@@ -21,15 +21,15 @@ class Tag < ApplicationRecord
def self.search(term)
# Query to search on tags, the name is used for sorting.
q1 = where('tags.name LIKE ?', "%#{sanitize_sql_like(term)}%")
- .or(where('tags.excerpt LIKE ?', "%#{sanitize_sql_like(term)}%"))
- .select(Arel.sql('name AS sortname, tags.*'))
+ .or(where('tags.excerpt LIKE ?', "%#{sanitize_sql_like(term)}%"))
+ .select(Arel.sql('name AS sortname, tags.*'))
# Query to search on synonyms, the synonym name is used for sorting.
# The order clause here actually applies to the union of q1 and q2 (so not just q2).
q2 = joins(:tag_synonyms)
- .where('tag_synonyms.name LIKE ?', "%#{sanitize_sql_like(term)}%")
- .select(Arel.sql('tag_synonyms.name AS sortname, tags.*'))
- .order(Arel.sql(sanitize_sql_array(['sortname LIKE ? DESC, sortname', "#{sanitize_sql_like(term)}%"])))
+ .where('tag_synonyms.name LIKE ?', "%#{sanitize_sql_like(term)}%")
+ .select(Arel.sql('tag_synonyms.name AS sortname, tags.*'))
+ .order(Arel.sql(sanitize_sql_array(['sortname LIKE ? DESC, sortname', "#{sanitize_sql_like(term)}%"])))
# Select from the union of the above queries, select only the tag columns such that we can distinct them
from(Arel.sql("(#{q1.to_sql} UNION #{q2.to_sql}) tags"))
diff --git a/test/models/tag_synonym_test.rb b/test/models/tag_synonym_test.rb
index ea074d1e6..a5ea423be 100644
--- a/test/models/tag_synonym_test.rb
+++ b/test/models/tag_synonym_test.rb
@@ -1,4 +1,4 @@
-require "test_helper"
+require 'test_helper'
class TagSynonymTest < ActiveSupport::TestCase
# test "the truth" do
From 01bfa86fdae2060af1bf04409908f84aa8d120e5 Mon Sep 17 00:00:00 2001
From: Taico Aerts
Date: Thu, 22 Sep 2022 13:54:32 +0200
Subject: [PATCH 079/968] Fix tests
---
test/fixtures/tag_synonyms.yml | 12 +++---------
test/fixtures/tags.yml | 5 +++++
2 files changed, 8 insertions(+), 9 deletions(-)
diff --git a/test/fixtures/tag_synonyms.yml b/test/fixtures/tag_synonyms.yml
index d7a332924..48965bc78 100644
--- a/test/fixtures/tag_synonyms.yml
+++ b/test/fixtures/tag_synonyms.yml
@@ -1,11 +1,5 @@
# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
-# This model initially had no columns defined. If you add columns to the
-# model remove the "{}" from the fixture names and add the columns immediately
-# below each fixture, per the syntax in the comments below
-#
-one: {}
-# column: value
-#
-two: {}
-# column: value
+base_synonym:
+ name: synonym
+ tag: base
diff --git a/test/fixtures/tags.yml b/test/fixtures/tags.yml
index de28397ed..0c5a1af59 100644
--- a/test/fixtures/tags.yml
+++ b/test/fixtures/tags.yml
@@ -38,3 +38,8 @@ child:
community: sample
tag_set: main
parent: topic
+
+base:
+ name: base
+ community: sample
+ tag_set: main
From 6a30bf6d8ff8d52e7d8b9b4262a287217890b3a4 Mon Sep 17 00:00:00 2001
From: Taico Aerts
Date: Thu, 22 Sep 2022 15:11:54 +0200
Subject: [PATCH 080/968] Add tests for synonyms
---
test/controllers/tags_controller_test.rb | 36 ++++++++++++++++++++++--
1 file changed, 34 insertions(+), 2 deletions(-)
diff --git a/test/controllers/tags_controller_test.rb b/test/controllers/tags_controller_test.rb
index 176beff95..4a9c99f4b 100644
--- a/test/controllers/tags_controller_test.rb
+++ b/test/controllers/tags_controller_test.rb
@@ -12,7 +12,7 @@ class TagsControllerTest < ActionController::TestCase
assert_not_nil assigns(:tags)
end
- test 'index with search params should return tags starting with search' do
+ test 'index with search params should return tags including search term' do
get :index, params: { format: 'json', term: 'dis' }
assert_response 200
assert_nothing_raised do
@@ -20,7 +20,19 @@ class TagsControllerTest < ActionController::TestCase
end
assert_not_nil assigns(:tags)
JSON.parse(response.body).each do |tag|
- assert_equal true, tag['name'].start_with?('dis')
+ assert_equal true, tag['name'].include?('dis') || tag['tag_synonyms'].any? { |ts| ts['name'].include?('syn') }
+ end
+ end
+
+ test 'index with search params should return tags whose synonyms include search term' do
+ get :index, params: { format: 'json', term: 'syn' }
+ assert_response 200
+ assert_nothing_raised do
+ JSON.parse(response.body)
+ end
+ assert_not_nil assigns(:tags)
+ JSON.parse(response.body).each do |tag|
+ assert_equal true, tag['name'].include?('syn') || tag['tag_synonyms'].any? { |ts| ts['name'].include?('syn') }
end
end
@@ -125,6 +137,26 @@ class TagsControllerTest < ActionController::TestCase
assert_equal 'things', assigns(:tag).excerpt
end
+ test 'should update tag with synonym addition' do
+ sign_in users(:deleter)
+ patch :update, params: { id: categories(:main).id, tag_id: tags(:topic).id,
+ tag: { tag_synonyms_attributes: { '1': { name: 'conversation' } } } }
+ assert_response 302
+ assert_redirected_to tag_path(id: categories(:main).id, tag_id: tags(:topic).id)
+ assert_not_nil assigns(:tag)
+ assert_equal 'conversation', assigns(:tag).tag_synonyms.first&.name
+ end
+
+ test 'should update tag with synonym removal' do
+ sign_in users(:deleter)
+ patch :update, params: { id: categories(:main).id, tag_id: tags(:base).id,
+ tag: { tag_synonyms_attributes: { '1': { id: tag_synonyms(:base_synonym).id, _destroy: 'true' } } } }
+ assert_response 302
+ assert_redirected_to tag_path(id: categories(:main).id, tag_id: tags(:base).id)
+ assert_not_nil assigns(:tag)
+ assert_equal true, assigns(:tag).tag_synonyms.none? { |ts| ts.name == 'synonym' }
+ end
+
test 'should prevent a tag being its own parent' do
sign_in users(:deleter)
patch :update, params: { id: categories(:main).id, tag_id: tags(:topic).id,
From b6fe1ac16e8b38ce20d016dcfa65c8548c3abaab Mon Sep 17 00:00:00 2001
From: Taico Aerts
Date: Thu, 22 Sep 2022 15:13:41 +0200
Subject: [PATCH 081/968] Fix rubocop error
---
test/controllers/tags_controller_test.rb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/controllers/tags_controller_test.rb b/test/controllers/tags_controller_test.rb
index 4a9c99f4b..cdb3983d9 100644
--- a/test/controllers/tags_controller_test.rb
+++ b/test/controllers/tags_controller_test.rb
@@ -154,7 +154,7 @@ class TagsControllerTest < ActionController::TestCase
assert_response 302
assert_redirected_to tag_path(id: categories(:main).id, tag_id: tags(:base).id)
assert_not_nil assigns(:tag)
- assert_equal true, assigns(:tag).tag_synonyms.none? { |ts| ts.name == 'synonym' }
+ assert_equal true, (assigns(:tag).tag_synonyms.none? { |ts| ts.name == 'synonym' })
end
test 'should prevent a tag being its own parent' do
From 3f3a5b54f04450f1c03f757f3de36ba8af2af2d3 Mon Sep 17 00:00:00 2001
From: MoshiKoi <54333972+MoshiKoi@users.noreply.github.com>
Date: Wed, 28 Sep 2022 15:23:07 -0700
Subject: [PATCH 082/968] Fix fetch path
---
app/assets/javascripts/qpixel_api.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/assets/javascripts/qpixel_api.js b/app/assets/javascripts/qpixel_api.js
index 2d7113dd8..fd277ae50 100644
--- a/app/assets/javascripts/qpixel_api.js
+++ b/app/assets/javascripts/qpixel_api.js
@@ -286,7 +286,7 @@ window.QPixel = {
// Assumes we're on a category page
setFilterAsDefault: async name => {
- const resp = await fetch(location.href + '/filters/default', {
+ const resp = await fetch(location.pathname + '/filters/default', {
method: 'POST',
credentials: 'include',
headers: {
From 981d28ddbb6305c4a119a96384b60711fd11889f Mon Sep 17 00:00:00 2001
From: MoshiKoi <54333972+MoshiKoi@users.noreply.github.com>
Date: Wed, 28 Sep 2022 15:32:03 -0700
Subject: [PATCH 083/968] Minor refactor of the save-as-default logic
---
app/assets/javascripts/filters.js | 3 ++-
app/assets/javascripts/qpixel_api.js | 5 ++---
app/views/search/_filters.html.erb | 5 ++++-
3 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/app/assets/javascripts/filters.js b/app/assets/javascripts/filters.js
index ef6e76588..d605c563f 100644
--- a/app/assets/javascripts/filters.js
+++ b/app/assets/javascripts/filters.js
@@ -120,7 +120,8 @@ $(() => {
$saveAsDefaultButton.on('click', async _ => {
await saveFilter();
- QPixel.setFilterAsDefault($select.val());
+ const id = $saveAsDefaultButton.data('categoryId');
+ QPixel.setFilterAsDefault(id, $select.val());
});
function clear() {
diff --git a/app/assets/javascripts/qpixel_api.js b/app/assets/javascripts/qpixel_api.js
index fd277ae50..59d8667e4 100644
--- a/app/assets/javascripts/qpixel_api.js
+++ b/app/assets/javascripts/qpixel_api.js
@@ -284,9 +284,8 @@ window.QPixel = {
return this._filters;
},
- // Assumes we're on a category page
- setFilterAsDefault: async name => {
- const resp = await fetch(location.pathname + '/filters/default', {
+ setFilterAsDefault: async (category_id, name) => {
+ const resp = await fetch(`/categories/${category_id}/filters/default`, {
method: 'POST',
credentials: 'include',
headers: {
diff --git a/app/views/search/_filters.html.erb b/app/views/search/_filters.html.erb
index 8ee4802ee..e9503d941 100644
--- a/app/views/search/_filters.html.erb
+++ b/app/views/search/_filters.html.erb
@@ -65,7 +65,10 @@
<% end %>
<% if user_signed_in? %>
-
+ <% if defined? @category %>
+
+ <% end%>
<% end %>
<% if allow_delete %>
From 13688122a1eff21bda52c07cbc44ce2affaf45b5 Mon Sep 17 00:00:00 2001
From: MoshiKoi <54333972+MoshiKoi@users.noreply.github.com>
Date: Wed, 28 Sep 2022 17:04:27 -0700
Subject: [PATCH 084/968] Move filters to top of category listing
---
app/views/categories/show.html.erb | 9 ++++++++-
app/views/layouts/_sidebar.html.erb | 12 ------------
2 files changed, 8 insertions(+), 13 deletions(-)
diff --git a/app/views/categories/show.html.erb b/app/views/categories/show.html.erb
index e1b56d759..9a6da82f7 100644
--- a/app/views/categories/show.html.erb
+++ b/app/views/categories/show.html.erb
@@ -50,6 +50,13 @@
+
+ Filters
+ <%= form_tag request.original_url, method: :get do %>
+ <%= render 'search/filters' %>
+ <% end %>
+
+
<% if @active_filter[:default] == true %>
You are currently filtering by <%= @active_filter[:name] %> because it is set as your default for this category
@@ -70,4 +77,4 @@
<%= link_to category_feed_path(@category, format: 'rss') do %>
Category RSS feed
<% end %>
-
<% vote_list.each do |post, vote_data| %>
From 13f27bab676c856953a48273b6ac3fbd588aef7a Mon Sep 17 00:00:00 2001
From: Taico Aerts
Date: Tue, 25 Oct 2022 13:27:46 +0200
Subject: [PATCH 092/968] Add back still required install instructions
The `rmagick` library requires imagemagick to also be installed (libvips is the new default used by image processing in rails).
---
INSTALLATION.md | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/INSTALLATION.md b/INSTALLATION.md
index 93e42a159..d460b444a 100644
--- a/INSTALLATION.md
+++ b/INSTALLATION.md
@@ -56,6 +56,16 @@ If you already have Node.JS installed, you can skip this step. If not,
If you haven't already got it, [download and install Redis](https://redis.io/download)
or for example `sudo apt install redis-server`.
+### Install Imagemagick
+
+If you haven't already installed Imagemagick, you'll need to
+[install it for your system](https://imagemagick.org/script/download.php).
+
+If you install Imagemagick from APT on a Debian-based system, you may need to
+also install the `libmagickwand-dev` package.
+
+`sudo apt install libmagick++-dev` should also work.
+
### Install Libvips
If you haven't already installed Libvips, you'll need to [install it for
From 913f6c8c6a1fe9846071d17e36b367b8b304d4c7 Mon Sep 17 00:00:00 2001
From: Taico Aerts
Date: Thu, 27 Oct 2022 10:53:18 +0200
Subject: [PATCH 093/968] Fix error in docker
---
config/environments/development.rb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/config/environments/development.rb b/config/environments/development.rb
index 3586b8d51..2c58a27a0 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -82,7 +82,7 @@
# Ensure docker ip added to allowed, given that we are in container
if File.file?('/.dockerenv') == true
host_ip = `/sbin/ip route|awk '/default/ { print $3 }'`.strip
- config.web_console.allowed_ips << host_ip
+ config.web_console.permissions = host_ip
# ==> Configuration for :confirmable
# A period that the user is allowed to access the website even without
From 9792bf84999fd20c83d14e4e704e7c4ca803c436 Mon Sep 17 00:00:00 2001
From: Taico Aerts
Date: Thu, 27 Oct 2022 10:53:26 +0200
Subject: [PATCH 094/968] Ensure docker works out of the box
---
docker/dummy.env | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docker/dummy.env b/docker/dummy.env
index 65c42be15..610597621 100644
--- a/docker/dummy.env
+++ b/docker/dummy.env
@@ -1,5 +1,5 @@
MYSQL_ROOT_PASSWORD=qpixel
-MYSQL_DATABASE=qpixel
+MYSQL_DATABASE=qpixel_dev
MYSQL_USER=qpixel
MYSQL_PASSWORD=qpixel
COMMUNITY_ADMIN_USERNAME=admin
From 20d51ccebdfbcb8261745002d25a94c9374f8f92 Mon Sep 17 00:00:00 2001
From: Taico Aerts
Date: Thu, 27 Oct 2022 11:08:04 +0200
Subject: [PATCH 095/968] Add some notes on docker compose and Mac
Also update display
---
docker/README.md | 32 ++++++++++++++++++++++----------
1 file changed, 22 insertions(+), 10 deletions(-)
diff --git a/docker/README.md b/docker/README.md
index f7e1a33ba..edfec7c60 100644
--- a/docker/README.md
+++ b/docker/README.md
@@ -2,12 +2,20 @@
A [docker-compose.yml](../docker-compose.yml) file is provided for deployment with Docker compose, if you choose.
-To use docker compose, you need to install the docker-compose-plugin. For a system like debian or ubuntu, you can use the following command.
+To use docker compose, you need to install the docker-compose-plugin. You can check whether it is already installed by running the following command.
+
+```bash
+sudo docker compose version
+```
+
+If your version is 2.x or higher, then you are good. Otherwise, you should install the docker compose plugin. For a system like debian or ubuntu, you can use the following command.
```bash
sudo apt-get install docker-compose-plugin
```
+For Mac OS, you can install docker desktop by downloading it from the docker website. After starting the application, the docker compose command becomes available in your terminal.
+
Depending on your setup, you may need to prefix every docker command with sudo.
## 1. Setup and Secrets
@@ -49,6 +57,8 @@ docker compose build db
docker compose build redis
```
+NOTE: If you get an error like "Cannot connect to the Docker daemon at ...", you need to ensure you start docker. Depending on your system, this can be done with `sudo service docker start` (Ubuntu) or by opening the Docker Desktop application and waiting for it to start (Mac OS).
+
## 4. Start Containers
Then start your containers!
@@ -64,15 +74,17 @@ The uwsgi container has a sleep command for 15 seconds to give the database a ch
so don't expect to see output right away. After about 20 seconds, check to make sure the server is running (and verify port 3000, note that you can change this mapping in the `.env` file)
```
-uwsgi_1 | => Booting Puma
-uwsgi_1 | => Rails 5.2.4.3 application starting in development
-uwsgi_1 | => Run `rails server -h` for more startup options
-uwsgi_1 | Puma starting in single mode...
-uwsgi_1 | * Version 3.12.6 (ruby 2.6.5-p114), codename: Llamas in Pajamas
-uwsgi_1 | * Min threads: 0, max threads: 16
-uwsgi_1 | * Environment: development
-uwsgi_1 | * Listening on tcp://localhost:3000
-uwsgi_1 | Use Ctrl-C to stop
+qpixel_uwsgi_1 | => Booting Puma
+qpixel_uwsgi_1 | => Rails 7.0.4 application starting in development
+qpixel_uwsgi_1 | => Run `rails server -h` for more startup options
+qpixel_uwsgi_1 | Puma starting in single mode...
+qpixel_uwsgi_1 | * Puma version: 5.6.5 (ruby 2.7.6-p219) ("Birdie's Version")
+qpixel_uwsgi_1 | * Min threads: 5
+qpixel_uwsgi_1 | * Max threads: 5
+qpixel_uwsgi_1 | * Environment: development
+qpixel_uwsgi_1 | * PID: 49
+qpixel_uwsgi_1 | * Listening on http://0.0.0.0:3000
+qpixel_uwsgi_1 | Use Ctrl-C to stop
```
You should then be able to open your browser to [http://localhost:3000](http://localhost:3000)
From 1b9ea3472c035f2ce1c62a6b50fe2454a05e029c Mon Sep 17 00:00:00 2001
From: Taico Aerts
Date: Thu, 27 Oct 2022 11:10:27 +0200
Subject: [PATCH 096/968] Also create storage config in docker
---
config/storage.docker.yml | 14 ++++++++++++++
docker/local-setup.sh | 1 +
2 files changed, 15 insertions(+)
create mode 100644 config/storage.docker.yml
diff --git a/config/storage.docker.yml b/config/storage.docker.yml
new file mode 100644
index 000000000..88649a3fb
--- /dev/null
+++ b/config/storage.docker.yml
@@ -0,0 +1,14 @@
+test:
+ service: Disk
+ root: <%= Rails.root.join('tmp/storage') %>
+
+local:
+ service: Disk
+ root: <%= Rails.root.join('storage') %>
+
+s3:
+ service: S3
+ access_key_id: ""
+ secret_access_key: ""
+ region: us-east-1
+ bucket: ""
diff --git a/docker/local-setup.sh b/docker/local-setup.sh
index 0b2674eeb..ebfbf6e2e 100755
--- a/docker/local-setup.sh
+++ b/docker/local-setup.sh
@@ -3,3 +3,4 @@
cp ./docker/dummy.env ./docker/env
cp ./docker/compose-env .env
cp config/database.docker.yml config/database.yml
+cp config/storage.docker.yml config/storage.yml
From 7778172c650d3a44ea761dfcb3eb1a54271d71d7 Mon Sep 17 00:00:00 2001
From: Taico Aerts
Date: Thu, 27 Oct 2022 15:00:06 +0200
Subject: [PATCH 097/968] Wait until mysql is up properly
---
docker-compose.yml | 18 ++++++++++++++----
docker/dummy.env | 2 +-
docker/entrypoint.sh | 3 ---
docker/mysql-init.sql | 2 --
4 files changed, 15 insertions(+), 10 deletions(-)
diff --git a/docker-compose.yml b/docker-compose.yml
index 201795be7..61c4c1e7e 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,4 +1,4 @@
-version: "3.8"
+version: "3.9"
services:
db:
build:
@@ -11,6 +11,13 @@ services:
command: mysqld --default-authentication-plugin=mysql_native_password --skip-mysqlx
cap_add:
- SYS_NICE
+ healthcheck:
+ test: mysqladmin ping -h 127.0.0.1 -u $$MYSQL_USER --password=$$MYSQL_PASSWORD
+ start_period: 5s
+ interval: 5s
+ timeout: 5s
+ retries: 12
+
uwsgi:
restart: always
@@ -18,7 +25,10 @@ services:
context: "."
dockerfile: docker/Dockerfile
depends_on:
- - db
+ db:
+ condition: service_healthy
+ redis:
+ condition: service_healthy
environment:
- COMMUNITY_NAME=${COMMUNITY_NAME}
- RAILS_ENV=${RAILS_ENV}
@@ -39,5 +49,5 @@ services:
redis:
restart: always
image: redis:latest
- depends_on:
- - db
+ healthcheck:
+ test: ["CMD", "redis-cli","ping"]
diff --git a/docker/dummy.env b/docker/dummy.env
index 610597621..65c42be15 100644
--- a/docker/dummy.env
+++ b/docker/dummy.env
@@ -1,5 +1,5 @@
MYSQL_ROOT_PASSWORD=qpixel
-MYSQL_DATABASE=qpixel_dev
+MYSQL_DATABASE=qpixel
MYSQL_USER=qpixel
MYSQL_PASSWORD=qpixel
COMMUNITY_ADMIN_USERNAME=admin
diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh
index 145b478c6..efbf2b14e 100644
--- a/docker/entrypoint.sh
+++ b/docker/entrypoint.sh
@@ -1,8 +1,5 @@
#!/bin/bash
-# Give database chance to finish creation
-sleep 15
-
# If not created yet
if [ ! -f "/db-created" ]; then
rails db:create
diff --git a/docker/mysql-init.sql b/docker/mysql-init.sql
index 2996693ec..510059bb9 100644
--- a/docker/mysql-init.sql
+++ b/docker/mysql-init.sql
@@ -1,8 +1,6 @@
/* database qpixel and user are already created *
if you change your environment file, you need to update database names here */
-CREATE DATABASE qpixel_dev;
-CREATE DATABASE qpixel_test;
GRANT ALL ON qpixel_dev.* TO qpixel;
GRANT ALL ON qpixel_test.* TO qpixel;
GRANT ALL ON qpixel.* TO qpixel;
From 27e9e62b2307d5014d7221783ad7ed4137bf2b19 Mon Sep 17 00:00:00 2001
From: Taico Aerts
Date: Mon, 31 Oct 2022 13:03:15 +0100
Subject: [PATCH 098/968] Remove outdated config option
---
.circleci/config.yml | 1 -
1 file changed, 1 deletion(-)
diff --git a/.circleci/config.yml b/.circleci/config.yml
index d83e7122e..56dfd2403 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -7,7 +7,6 @@ jobs:
command: [--default-authentication-plugin=mysql_native_password]
environment:
MYSQL_ROOT_HOST: '%'
- MYSQL_USER: 'root'
MYSQL_ROOT_PASSWORD: 'root'
MYSQL_DATABASE: 'qpixel_test'
- image: cimg/redis:7.0
From 3fcaf31e54dd5ff32dc6d876357436ac696cdede Mon Sep 17 00:00:00 2001
From: MoshiKoi <54333972+MoshiKoi@users.noreply.github.com>
Date: Wed, 2 Nov 2022 16:17:26 -0700
Subject: [PATCH 099/968] Display name of active filter
---
app/controllers/categories_controller.rb | 6 ++----
app/views/categories/show.html.erb | 13 ++++++-------
2 files changed, 8 insertions(+), 11 deletions(-)
diff --git a/app/controllers/categories_controller.rb b/app/controllers/categories_controller.rb
index 9069e9d15..dec072762 100644
--- a/app/controllers/categories_controller.rb
+++ b/app/controllers/categories_controller.rb
@@ -195,10 +195,8 @@ def set_list_posts
end
end
- unless filter_qualifiers.blank?
- @posts = helpers.qualifiers_to_sql(filter_qualifiers, @posts)
- end
-
+ @posts = helpers.qualifiers_to_sql(filter_qualifiers, @posts)
+ @filtered = filter_qualifiers.any?
@posts = @posts.paginate(page: params[:page], per_page: 50).order(sort_param)
end
diff --git a/app/views/categories/show.html.erb b/app/views/categories/show.html.erb
index 9a6da82f7..9fb6a52ec 100644
--- a/app/views/categories/show.html.erb
+++ b/app/views/categories/show.html.erb
@@ -51,18 +51,17 @@
- <%= f.submit "Update", class: 'button is-filled is-very-large' %>
+ <%= f.submit "Update", class: 'button is-filled is-very-large', disabled: sso %>
<% end %>
From bce153ecece252237519cfe93c65f0ee0f2f5aba Mon Sep 17 00:00:00 2001
From: Taico Aerts
Date: Sat, 26 Nov 2022 18:03:03 +0100
Subject: [PATCH 107/968] Enforce that SSO users sign-in with SSO
---
app/controllers/users/sessions_controller.rb | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/app/controllers/users/sessions_controller.rb b/app/controllers/users/sessions_controller.rb
index 6f199bfbe..e086adbd8 100644
--- a/app/controllers/users/sessions_controller.rb
+++ b/app/controllers/users/sessions_controller.rb
@@ -22,6 +22,14 @@ def create
return
end
+ if user.present? && user.sso_profile.present?
+ sign_out user
+ flash[:notice] = nil
+ flash[:danger] = 'Please sign in using the Single Sign On service of your institution.'
+ redirect_to new_saml_user_session_path
+ return
+ end
+
if user.present? && user.enabled_2fa
sign_out user
case user.two_factor_method
From 2f32d1ce88bc624151a3115c2cd145a91d18d7f7 Mon Sep 17 00:00:00 2001
From: Taico Aerts
Date: Sat, 26 Nov 2022 18:03:43 +0100
Subject: [PATCH 108/968] Allow disconnect account from SSO when mixed
---
app/controllers/users_controller.rb | 24 +++++++++++++++++++-
app/views/devise/registrations/edit.html.erb | 6 +++++
app/views/users/disconnect_sso.html.erb | 12 ++++++++++
config/routes.rb | 2 ++
db/seeds/site_settings.yml | 8 +++++++
5 files changed, 51 insertions(+), 1 deletion(-)
create mode 100644 app/views/users/disconnect_sso.html.erb
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 808d51427..9d2fe6de4 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -5,7 +5,8 @@ class UsersController < ApplicationController
include Devise::Controllers::Rememberable
before_action :authenticate_user!, only: [:edit_profile, :update_profile, :stack_redirect, :transfer_se_content,
- :qr_login_code, :me, :preferences, :set_preference, :my_vote_summary]
+ :qr_login_code, :me, :preferences, :set_preference, :my_vote_summary,
+ :disconnect_sso, :confirm_disconnect_sso]
before_action :verify_moderator, only: [:mod, :destroy, :soft_delete, :role_toggle, :full_log,
:annotate, :annotations, :mod_privileges, :mod_privilege_action]
before_action :set_user, only: [:show, :mod, :destroy, :soft_delete, :posts, :role_toggle, :full_log, :activity,
@@ -488,6 +489,27 @@ def specific_avatar
end
end
+ def disconnect_sso
+ render layout: 'without_sidebar'
+ end
+
+ def confirm_disconnect_sso
+ if current_user.sso_profile.blank? || !SiteSetting['AllowSsoDisconnect']
+ flash[:danger] = 'You cannot disable Single Sign On.'
+ redirect_to edit_user_registration_path
+ return
+ end
+
+ if current_user.sso_profile.destroy
+ current_user.send_reset_password_instructions
+ flash[:success] = 'Successfully disconnected from Single Sign On. Please see your email to set your password.'
+ redirect_to edit_user_registration_path
+ else
+ flash[:danger] = 'Failed to disconnect from Single Sign On.'
+ redirect_to user_disconnect_sso_path
+ end
+ end
+
private
def set_user
diff --git a/app/views/devise/registrations/edit.html.erb b/app/views/devise/registrations/edit.html.erb
index b638548ed..f2d06a4fe 100644
--- a/app/views/devise/registrations/edit.html.erb
+++ b/app/views/devise/registrations/edit.html.erb
@@ -8,6 +8,12 @@
<% sso = current_user.sso_profile.present? %>
<% if sso %>
+ <% if SiteSetting['AllowSsoDisconnect'] %>
+ <%= link_to user_disconnect_sso_path, class: 'button is-outlined is-danger' do %>
+ Disconnect Single Sign On »
+ <% end %>
+ <% end %>
+
You sign in through a Single Sign On provider.
Because of that, you cannot change your email address or password here.
diff --git a/app/views/users/disconnect_sso.html.erb b/app/views/users/disconnect_sso.html.erb
new file mode 100644
index 000000000..a77262d51
--- /dev/null
+++ b/app/views/users/disconnect_sso.html.erb
@@ -0,0 +1,12 @@
+
Disconnect from Single Sign On
+
+ You currently sign in using your institutions Single Sign On service.
+ It is possible to switch to signing in with a normal email address and password by disconnecting your account from
+ the Single Sign On service.
+ After disconnecting your account, you will be sent an email to set the password for your account.
+
+
+<%= link_to 'Disconnect my account from SSO',
+ user_confirm_disconnect_sso_path,
+ method: :post,
+ class: 'button is-filled is-danger' %>
\ No newline at end of file
diff --git a/config/routes.rb b/config/routes.rb
index a7c1997f0..a67a92757 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -184,6 +184,8 @@
patch '/edit/profile', to: 'users#update_profile', as: :update_user_profile
get '/me/vote-summary', to: 'users#my_vote_summary', as: :my_vote_summary
get '/avatar/:letter/:color/:size', to: 'users#specific_avatar', as: :specific_auto_avatar
+ get '/disconnect-sso', to: 'users#disconnect_sso', as: :user_disconnect_sso
+ post '/disconnect-sso', to: 'users#confirm_disconnect_sso', as: :user_confirm_disconnect_sso
get '/:id', to: 'users#show', as: :user
get '/:id/flags', to: 'flags#history', as: :flag_history
get '/:id/activity', to: 'users#activity', as: :user_activity
diff --git a/db/seeds/site_settings.yml b/db/seeds/site_settings.yml
index 33112b22e..c532835e6 100644
--- a/db/seeds/site_settings.yml
+++ b/db/seeds/site_settings.yml
@@ -542,3 +542,11 @@
description: >
This setting only has an effect when SSO Sign In is enabled. Enables 2FA options (and enforces 2FA for global mods and admins if configured) also for SSO users.
When the authentication is outsourced to a Single Sign On provider (which may already require 2FA), it often does not make sense to have an additional 2FA check on top of that.
+
+- name: AllowSsoDisconnect
+ value: false
+ value_type: boolean
+ community_id: ~
+ category: SignInAndSignUp
+ description: >
+ This setting only has an effect when SSO Sign In and Mixed Sign In are enabled. Allows users to disconnect their account from SSO and switch over to normal login.
From 5653c411025fac2c39abc5056bb158b29d6e5ff1 Mon Sep 17 00:00:00 2001
From: Taico Aerts
Date: Sat, 26 Nov 2022 18:21:30 +0100
Subject: [PATCH 109/968] Check for mixed sign in before allowing SSO
disconnect
---
app/controllers/users_controller.rb | 2 +-
app/views/devise/registrations/edit.html.erb | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 9d2fe6de4..c7e950a0a 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -494,7 +494,7 @@ def disconnect_sso
end
def confirm_disconnect_sso
- if current_user.sso_profile.blank? || !SiteSetting['AllowSsoDisconnect']
+ if current_user.sso_profile.blank? || !helpers.devise_sign_in_enabled? || !SiteSetting['AllowSsoDisconnect']
flash[:danger] = 'You cannot disable Single Sign On.'
redirect_to edit_user_registration_path
return
diff --git a/app/views/devise/registrations/edit.html.erb b/app/views/devise/registrations/edit.html.erb
index f2d06a4fe..9934309c6 100644
--- a/app/views/devise/registrations/edit.html.erb
+++ b/app/views/devise/registrations/edit.html.erb
@@ -8,7 +8,7 @@
<% sso = current_user.sso_profile.present? %>
<% if sso %>
- <% if SiteSetting['AllowSsoDisconnect'] %>
+ <% if devise_sign_in_enabled? && SiteSetting['AllowSsoDisconnect'] %>
<%= link_to user_disconnect_sso_path, class: 'button is-outlined is-danger' do %>
Disconnect Single Sign On »
<% end %>
From 709b6961a56b7bf799d361ef6648ceba3a9ca273 Mon Sep 17 00:00:00 2001
From: Taico Aerts
Date: Sat, 26 Nov 2022 20:41:00 +0100
Subject: [PATCH 110/968] Fix impersonation with SSO
---
app/controllers/admin_controller.rb | 7 ++++++-
app/views/admin/change_back.html.erb | 18 ++++++++++++------
2 files changed, 18 insertions(+), 7 deletions(-)
diff --git a/app/controllers/admin_controller.rb b/app/controllers/admin_controller.rb
index b7453e9ce..6bf41e85d 100644
--- a/app/controllers/admin_controller.rb
+++ b/app/controllers/admin_controller.rb
@@ -170,7 +170,12 @@ def verify_elevation
return not_found unless session[:impersonator_id].present?
@impersonator = User.find session[:impersonator_id]
- if @impersonator&.valid_password? params[:password]
+ if @impersonator&.sso_profile.present?
+ session.delete :impersonator_id
+ AuditLog.admin_audit(event_type: 'impersonation_end', related: current_user, user: @impersonator)
+ sign_out @impersonator
+ redirect_to new_saml_user_session_path
+ elsif @impersonator&.valid_password? params[:password]
session.delete :impersonator_id
AuditLog.admin_audit(event_type: 'impersonation_end', related: current_user, user: @impersonator)
sign_in @impersonator
diff --git a/app/views/admin/change_back.html.erb b/app/views/admin/change_back.html.erb
index 26e9e77c0..33cf3c7ea 100644
--- a/app/views/admin/change_back.html.erb
+++ b/app/views/admin/change_back.html.erb
@@ -1,14 +1,20 @@
Stop impersonating
- You (<%= @impersonator.username %>) are currently impersonating <%= current_user.username %>. To stop
- impersonating them, verify your password below
+ You (<%= @impersonator.username %>) are currently impersonating <%= current_user.username %>.
+ <% if @impersonator.sso_profile.present? %>
+ You can stop impersonating them with the button below, after which you will have to sign in again.
+ <% else %>
+ To stop impersonating them, verify your password below.
+ <% end %>
<%= form_tag stop_impersonating_path, class: 'form-horizontal' do %>
-
<%= submit_tag 'Verify & Stop Impersonating', class: 'button is-primary is-filled' %>
From 0f1aeae5d7268b03cbd8ade9f59e4be9b4135b2c Mon Sep 17 00:00:00 2001
From: Taico Aerts
Date: Sat, 26 Nov 2022 22:10:25 +0100
Subject: [PATCH 111/968] Consistently use Single Sign-On rather than Single
Sign On
---
app/controllers/users/sessions_controller.rb | 2 +-
app/controllers/users_controller.rb | 6 +++---
app/views/devise/registrations/edit.html.erb | 4 ++--
app/views/two_factor/tf_status.html.erb | 2 +-
app/views/users/disconnect_sso.html.erb | 6 +++---
db/seeds/site_settings.yml | 2 +-
6 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/app/controllers/users/sessions_controller.rb b/app/controllers/users/sessions_controller.rb
index e086adbd8..45899e5dd 100644
--- a/app/controllers/users/sessions_controller.rb
+++ b/app/controllers/users/sessions_controller.rb
@@ -25,7 +25,7 @@ def create
if user.present? && user.sso_profile.present?
sign_out user
flash[:notice] = nil
- flash[:danger] = 'Please sign in using the Single Sign On service of your institution.'
+ flash[:danger] = 'Please sign in using the Single Sign-On service of your institution.'
redirect_to new_saml_user_session_path
return
end
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index c7e950a0a..9820adb4d 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -495,17 +495,17 @@ def disconnect_sso
def confirm_disconnect_sso
if current_user.sso_profile.blank? || !helpers.devise_sign_in_enabled? || !SiteSetting['AllowSsoDisconnect']
- flash[:danger] = 'You cannot disable Single Sign On.'
+ flash[:danger] = 'You cannot disable Single Sign-On.'
redirect_to edit_user_registration_path
return
end
if current_user.sso_profile.destroy
current_user.send_reset_password_instructions
- flash[:success] = 'Successfully disconnected from Single Sign On. Please see your email to set your password.'
+ flash[:success] = 'Successfully disconnected from Single Sign-On. Please see your email to set your password.'
redirect_to edit_user_registration_path
else
- flash[:danger] = 'Failed to disconnect from Single Sign On.'
+ flash[:danger] = 'Failed to disconnect from Single Sign-On.'
redirect_to user_disconnect_sso_path
end
end
diff --git a/app/views/devise/registrations/edit.html.erb b/app/views/devise/registrations/edit.html.erb
index 9934309c6..6c4e20c8d 100644
--- a/app/views/devise/registrations/edit.html.erb
+++ b/app/views/devise/registrations/edit.html.erb
@@ -10,12 +10,12 @@
<% if sso %>
<% if devise_sign_in_enabled? && SiteSetting['AllowSsoDisconnect'] %>
<%= link_to user_disconnect_sso_path, class: 'button is-outlined is-danger' do %>
- Disconnect Single Sign On »
+ Disconnect Single Sign-On »
<% end %>
<% end %>
- You sign in through a Single Sign On provider.
+ You sign in through a Single Sign-On provider.
Because of that, you cannot change your email address or password here.
Please contact your system administrator if you would like to change these.
<% if current_user.sso_profile.present? && !SiteSetting['Enable2FAForSsoUsers'] %>
- Because you sign in with Single Sign On, you cannot enable two-factor authentication.
+ Because you sign in with Single Sign-On, you cannot enable two-factor authentication.
- You currently sign in using your institutions Single Sign On service.
+ You currently sign in using your institutions Single Sign-On service.
It is possible to switch to signing in with a normal email address and password by disconnecting your account from
- the Single Sign On service.
+ the Single Sign-On service.
After disconnecting your account, you will be sent an email to set the password for your account.
You'll find all your inbox messages here, as far back as your account goes.
diff --git a/app/views/users/_tabs.html.erb b/app/views/users/_tabs.html.erb
index 0c4713fef..9f093d3ed 100644
--- a/app/views/users/_tabs.html.erb
+++ b/app/views/users/_tabs.html.erb
@@ -18,5 +18,8 @@
<%= link_to user_preferences_path, class: "tabs--item #{current_page?(user_preferences_path) ? 'is-active' : ''}" do %>
Preferences
<% end %>
+ <%= link_to notifications_path, class: "tabs--item #{current_page?(notifications_path) ? 'is-active' : ''}" do %>
+ Notifications
+ <% end %>
<% end %>
\ No newline at end of file
From d996ff1e6df0de7dfe3634ff39f4f56dd1e26697 Mon Sep 17 00:00:00 2001
From: MoshiKoi <54333972+MoshiKoi@users.noreply.github.com>
Date: Tue, 3 Jan 2023 01:49:38 -0800
Subject: [PATCH 137/968] Add indicator if there are pending suggested edits
---
app/views/layouts/_header.html.erb | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/app/views/layouts/_header.html.erb b/app/views/layouts/_header.html.erb
index 32fa3b3f3..4ba7f8143 100644
--- a/app/views/layouts/_header.html.erb
+++ b/app/views/layouts/_header.html.erb
@@ -244,8 +244,13 @@
class: "category-header--nav-item #{active?(current_cat) && !['tags', 'suggested_edit'].include?(controller_name) ? 'is-active' : ''}" %>
<%= link_to 'Tags', category_tags_path(current_cat),
class: "category-header--nav-item #{active?(current_cat) && controller_name == 'tags' ? 'is-active' : ''}" %>
- <%= link_to 'Edits', suggested_edits_queue_path(current_cat),
- class: "category-header--nav-item #{active?(current_cat) && controller_name == 'suggested_edit' ? 'is-active' : ''}" %>
+ <%= link_to suggested_edits_queue_path(current_cat),
+ class: "category-header--nav-item #{active?(current_cat) && controller_name == 'suggested_edit' ? 'is-active' : ''}" do %>
+ Edits
+ <% if SuggestedEdit.where(post: Post.undeleted.where(category: current_cat), active: true).any? %>
+
+ <% end %>
+ <% end %>
<%= link_to category_post_types_path(current_cat.id),
class: 'category-header--nav-item is-button' do %>
From 82ef37fa27ad37d34ca5ae1a516b731f52f0d48e Mon Sep 17 00:00:00 2001
From: MoshiKoi <54333972+MoshiKoi@users.noreply.github.com>
Date: Tue, 3 Jan 2023 02:09:51 -0800
Subject: [PATCH 138/968] Cache whether there are pending suggestions
---
app/models/suggested_edit.rb | 6 ++++++
app/views/layouts/_header.html.erb | 5 ++++-
2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/app/models/suggested_edit.rb b/app/models/suggested_edit.rb
index 7730bbc61..278691e28 100644
--- a/app/models/suggested_edit.rb
+++ b/app/models/suggested_edit.rb
@@ -10,6 +10,12 @@ class SuggestedEdit < ApplicationRecord
has_and_belongs_to_many :tags
has_and_belongs_to_many :before_tags, class_name: 'Tag', join_table: 'suggested_edits_before_tags'
+ after_save :clear_pending_cache
+
+ def clear_pending_cache
+ Rails.cache.delete "pending_suggestions/#{post.category_id}"
+ end
+
def pending?
active
end
diff --git a/app/views/layouts/_header.html.erb b/app/views/layouts/_header.html.erb
index 4ba7f8143..7f439e333 100644
--- a/app/views/layouts/_header.html.erb
+++ b/app/views/layouts/_header.html.erb
@@ -247,7 +247,10 @@
<%= link_to suggested_edits_queue_path(current_cat),
class: "category-header--nav-item #{active?(current_cat) && controller_name == 'suggested_edit' ? 'is-active' : ''}" do %>
Edits
- <% if SuggestedEdit.where(post: Post.undeleted.where(category: current_cat), active: true).any? %>
+ <% has_pending = Rails.cache.fetch "pending_suggestions/#{current_cat.id}" do %>
+ <% SuggestedEdit.where(post: Post.undeleted.where(category: current_cat), active: true).any? %>
+ <% end %>
+ <% if has_pending %>
<% end %>
<% end %>
From 597c70a1dde87a1952d5e85c76f81e04bc69664b Mon Sep 17 00:00:00 2001
From: MoshiKoi <54333972+MoshiKoi@users.noreply.github.com>
Date: Tue, 3 Jan 2023 07:16:20 -0800
Subject: [PATCH 139/968] Move pending logic to helper method
---
app/helpers/categories_helper.rb | 6 ++++++
app/views/layouts/_header.html.erb | 5 +----
2 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/app/helpers/categories_helper.rb b/app/helpers/categories_helper.rb
index 68c77d120..7efbc1617 100644
--- a/app/helpers/categories_helper.rb
+++ b/app/helpers/categories_helper.rb
@@ -23,4 +23,10 @@ def current_category
@article.category
end
end
+
+ def pending_suggestions?
+ Rails.cache.fetch "pending_suggestions/#{current_category.id}" do
+ SuggestedEdit.where(post: Post.undeleted.where(category: current_category), active: true).any?
+ end
+ end
end
diff --git a/app/views/layouts/_header.html.erb b/app/views/layouts/_header.html.erb
index 7f439e333..26ea7774c 100644
--- a/app/views/layouts/_header.html.erb
+++ b/app/views/layouts/_header.html.erb
@@ -247,10 +247,7 @@
<%= link_to suggested_edits_queue_path(current_cat),
class: "category-header--nav-item #{active?(current_cat) && controller_name == 'suggested_edit' ? 'is-active' : ''}" do %>
Edits
- <% has_pending = Rails.cache.fetch "pending_suggestions/#{current_cat.id}" do %>
- <% SuggestedEdit.where(post: Post.undeleted.where(category: current_cat), active: true).any? %>
- <% end %>
- <% if has_pending %>
+ <% if pending_suggestions? %>
<% end %>
<% end %>
From 10c14745917a59d457d04137fa1235e665a40864 Mon Sep 17 00:00:00 2001
From: Taico Aerts
Date: Tue, 3 Jan 2023 18:46:34 +0100
Subject: [PATCH 140/968] Fix tour test for the 3rd time
Also add tests to ensure that the tagset is correctly created by the
seeds.
---
app/controllers/tour_controller.rb | 4 +++-
app/views/tour/question2.html.erb | 2 +-
db/seeds/tag_sets.yml | 4 +---
test/seeds/tour_test.rb | 16 ++++++++++++++++
4 files changed, 21 insertions(+), 5 deletions(-)
create mode 100644 test/seeds/tour_test.rb
diff --git a/app/controllers/tour_controller.rb b/app/controllers/tour_controller.rb
index b7101473d..13ea7f27e 100644
--- a/app/controllers/tour_controller.rb
+++ b/app/controllers/tour_controller.rb
@@ -5,7 +5,9 @@ def index; end
def question1; end
- def question2; end
+ def question2
+ @tagset_id = TagSet.find_by(name: 'Tour')&.id || -1
+ end
def question3; end
diff --git a/app/views/tour/question2.html.erb b/app/views/tour/question2.html.erb
index 008550ba3..f5d4cd6db 100644
--- a/app/views/tour/question2.html.erb
+++ b/app/views/tour/question2.html.erb
@@ -43,7 +43,7 @@
-
+
diff --git a/db/seeds/tag_sets.yml b/db/seeds/tag_sets.yml
index d675cb51a..9dfe81cf2 100644
--- a/db/seeds/tag_sets.yml
+++ b/db/seeds/tag_sets.yml
@@ -1,5 +1,3 @@
- name: Main
- name: Meta
-- name: Tour
- id: -1
- community_id: 1
\ No newline at end of file
+- name: Tour
\ No newline at end of file
diff --git a/test/seeds/tour_test.rb b/test/seeds/tour_test.rb
new file mode 100644
index 000000000..4ae732368
--- /dev/null
+++ b/test/seeds/tour_test.rb
@@ -0,0 +1,16 @@
+require 'test_helper'
+
+class TourTest < ActiveSupport::TestCase
+ test "Tour question tag set exists for all communities after seeding" do
+ # Ensure there are multiple communities
+ c1 = Community.create(name: 'Test 1', host: 'test.host.1')
+ c2 = Community.create(name: 'Test 2', host: 'test.host.2')
+
+ Rails.application.load_seed
+
+ # Every community should have the Tour tagset
+ Community.all.each do |c|
+ assert_not_nil TagSet.unscoped.where(community: c).find_by('Tour')
+ end
+ end
+end
\ No newline at end of file
From 9c91ff46d80ebb0a87e2e762ba87431641b6f8ef Mon Sep 17 00:00:00 2001
From: Taico Aerts
Date: Tue, 3 Jan 2023 21:07:15 +0100
Subject: [PATCH 141/968] Fix CI issues
- Fix rubocop errors accidentally merged
- Fix failing test
---
app/controllers/application_controller.rb | 3 +-
.../users/saml_sessions_controller.rb | 3 +-
app/controllers/users/sessions_controller.rb | 33 +++++++++++--------
app/models/application_record.rb | 8 ++---
test/helpers/comments_helper_test.rb | 2 +-
test/seeds/tour_test.rb | 10 +++---
6 files changed, 33 insertions(+), 26 deletions(-)
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index d6ce97914..f323a6caa 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -20,7 +20,8 @@ def upload
redirect_to helpers.upload_remote_url(params[:key]), status: 301, allow_other_host: true
else
blob = params[:key]
- redirect_to url_for(ActiveStorage::Blob.find_by(key: blob.is_a?(String) ? blob : blob.key)), allow_other_host: true
+ redirect_to url_for(ActiveStorage::Blob.find_by(key: blob.is_a?(String) ? blob : blob.key)),
+ allow_other_host: true
end
end
diff --git a/app/controllers/users/saml_sessions_controller.rb b/app/controllers/users/saml_sessions_controller.rb
index ec5ed11ad..ef22a4b13 100644
--- a/app/controllers/users/saml_sessions_controller.rb
+++ b/app/controllers/users/saml_sessions_controller.rb
@@ -4,7 +4,8 @@ class Users::SamlSessionsController < Devise::SamlSessionsController
def create
super do |user|
if user.deleted? || user.community_user&.deleted?
- # The IDP already confirmed the sign in, so we can't fool the user any more that their credentials were incorrect.
+ # The IDP already confirmed the sign in, so we can't fool the user any more that their credentials were
+ # incorrect.
sign_out user
flash[:notice] = nil
flash[:danger] = 'We could not sign you in because of an issue with your account.'
diff --git a/app/controllers/users/sessions_controller.rb b/app/controllers/users/sessions_controller.rb
index 45899e5dd..32d1d784d 100644
--- a/app/controllers/users/sessions_controller.rb
+++ b/app/controllers/users/sessions_controller.rb
@@ -31,20 +31,8 @@ def create
end
if user.present? && user.enabled_2fa
- sign_out user
- case user.two_factor_method
- when 'app'
- id = user.id
- @@first_factor << id
- redirect_to login_verify_2fa_path(uid: id)
- return
- when 'email'
- TwoFactorMailer.with(user: user, host: request.hostname).login_email.deliver_now
- flash[:notice] = nil
- flash[:info] = 'Please check your email inbox for a link to sign in.'
- redirect_to root_path
- return
- end
+ handle_2fa_login(user)
+ return
end
end
end
@@ -81,4 +69,21 @@ def verify_code
redirect_to login_verify_2fa_path(uid: params[:uid])
end
end
+
+ private
+
+ def handle_2fa_login(user)
+ sign_out user
+ case user.two_factor_method
+ when 'app'
+ id = user.id
+ @@first_factor << id
+ redirect_to login_verify_2fa_path(uid: id)
+ when 'email'
+ TwoFactorMailer.with(user: user, host: request.hostname).login_email.deliver_now
+ flash[:notice] = nil
+ flash[:info] = 'Please check your email inbox for a link to sign in.'
+ redirect_to root_path
+ end
+ end
end
diff --git a/app/models/application_record.rb b/app/models/application_record.rb
index 0e552701f..2f0d184b0 100644
--- a/app/models/application_record.rb
+++ b/app/models/application_record.rb
@@ -107,11 +107,11 @@ def user_sort(term_opts, **field_mappings)
end
end
-klasses = [::ActiveRecord::Relation]
-klasses << if defined? ::ActiveRecord::Associations::CollectionProxy
- ::ActiveRecord::Associations::CollectionProxy
+klasses = [ActiveRecord::Relation]
+klasses << if defined? ActiveRecord::Associations::CollectionProxy
+ ActiveRecord::Associations::CollectionProxy
else
- ::ActiveRecord::Associations::AssociationCollection
+ ActiveRecord::Associations::AssociationCollection
end
ActiveRecord::Base.extend UserSortable
diff --git a/test/helpers/comments_helper_test.rb b/test/helpers/comments_helper_test.rb
index fca0964bd..42c6bde74 100644
--- a/test/helpers/comments_helper_test.rb
+++ b/test/helpers/comments_helper_test.rb
@@ -28,7 +28,7 @@ class CommentsHelperTest < ActionView::TestCase
test '[flags?] substitution' do
expected = {
'[flag] me if you can' => "flag me if you can",
- '\'cause it\'s our [flags]hip product' => "\'cause it\'s our flagship product",
+ '\'cause it\'s our [flags]hip product' => "'cause it's our flagship product",
'yeah bad pun - [flagged] and downvoted' => 'yeah bad pun - [flagged] and downvoted'
}
expected.each do |input, expect|
diff --git a/test/seeds/tour_test.rb b/test/seeds/tour_test.rb
index 4ae732368..1d7124af6 100644
--- a/test/seeds/tour_test.rb
+++ b/test/seeds/tour_test.rb
@@ -1,16 +1,16 @@
require 'test_helper'
class TourTest < ActiveSupport::TestCase
- test "Tour question tag set exists for all communities after seeding" do
+ test 'Tour question tag set exists for all communities after seeding' do
# Ensure there are multiple communities
- c1 = Community.create(name: 'Test 1', host: 'test.host.1')
- c2 = Community.create(name: 'Test 2', host: 'test.host.2')
+ Community.create(name: 'Test 1', host: 'test.host.1')
+ Community.create(name: 'Test 2', host: 'test.host.2')
Rails.application.load_seed
# Every community should have the Tour tagset
Community.all.each do |c|
- assert_not_nil TagSet.unscoped.where(community: c).find_by('Tour')
+ assert_not_nil TagSet.unscoped.where(community: c).find_by(name: 'Tour')
end
end
-end
\ No newline at end of file
+end
From 0bdad197aa3c60633df56a6c50bf5a03d21b0ada Mon Sep 17 00:00:00 2001
From: Taico Aerts
Date: Tue, 3 Jan 2023 21:16:13 +0100
Subject: [PATCH 142/968] Apply consistently between sessions_controller and
saml version
---
.../users/saml_sessions_controller.rb | 33 +++++++++++--------
1 file changed, 19 insertions(+), 14 deletions(-)
diff --git a/app/controllers/users/saml_sessions_controller.rb b/app/controllers/users/saml_sessions_controller.rb
index ef22a4b13..bb6f2a8e0 100644
--- a/app/controllers/users/saml_sessions_controller.rb
+++ b/app/controllers/users/saml_sessions_controller.rb
@@ -15,21 +15,26 @@ def create
# Enforce 2fa if enabled for SSO users
if SiteSetting['Enable2FAForSsoUsers'] && user.present? && user.enabled_2fa
- sign_out user
- case user.two_factor_method
- when 'app'
- id = user.id
- Users::SessionsController.first_factor << id
- redirect_to login_verify_2fa_path(uid: id)
- return
- when 'email'
- TwoFactorMailer.with(user: user, host: request.hostname).login_email.deliver_now
- flash[:notice] = nil
- flash[:info] = 'Please check your email inbox for a link to sign in.'
- redirect_to root_path
- return
- end
+ handle_2fa_login(user)
+ return
end
end
end
+
+ private
+
+ def handle_2fa_login(user)
+ sign_out user
+ case user.two_factor_method
+ when 'app'
+ id = user.id
+ Users::SessionsController.first_factor << id
+ redirect_to login_verify_2fa_path(uid: id)
+ when 'email'
+ TwoFactorMailer.with(user: user, host: request.hostname).login_email.deliver_now
+ flash[:notice] = nil
+ flash[:info] = 'Please check your email inbox for a link to sign in.'
+ redirect_to root_path
+ end
+ end
end
From bb1c51e0c47efa8a85b59c71d4a8bfb514f76409 Mon Sep 17 00:00:00 2001
From: Taico Aerts
Date: Tue, 3 Jan 2023 23:38:45 +0100
Subject: [PATCH 143/968] Fix comparisons
---
app/assets/javascripts/tags.js | 6 +++---
db/seeds/tag_sets.yml | 1 -
2 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/app/assets/javascripts/tags.js b/app/assets/javascripts/tags.js
index a2d71685f..8169a5a6d 100644
--- a/app/assets/javascripts/tags.js
+++ b/app/assets/javascripts/tags.js
@@ -38,8 +38,8 @@ $(() => {
data: function (params) {
$this = $(this);
// (for the tour)
- if ($this.data('tag-set') === '-1') {
- return Object.assign(params, { tag_set: "1" });
+ if ($this.data('tag-set') === -1) {
+ return Object.assign(params, { tag_set: '1' });
}
return Object.assign(params, { tag_set: $this.data('tag-set') });
},
@@ -47,7 +47,7 @@ $(() => {
delay: 100,
processResults: data => {
// (for the tour)
- if ($this.data('tag-set') === '-1') {
+ if ($this.data('tag-set') === -1) {
return {
results: [
{ id: 1, text: 'hot-red-firebreather', desc: 'Very cute dragon' },
diff --git a/db/seeds/tag_sets.yml b/db/seeds/tag_sets.yml
index 9dfe81cf2..8320d8bf5 100644
--- a/db/seeds/tag_sets.yml
+++ b/db/seeds/tag_sets.yml
@@ -1,3 +1,2 @@
- name: Main
- name: Meta
-- name: Tour
\ No newline at end of file
From 10e98598e33ad6c5d12ee2a7e06eedc7689ae14e Mon Sep 17 00:00:00 2001
From: Taico Aerts
Date: Tue, 3 Jan 2023 23:39:22 +0100
Subject: [PATCH 144/968] Remove unnecessary test
---
test/seeds/tour_test.rb | 16 ----------------
1 file changed, 16 deletions(-)
delete mode 100644 test/seeds/tour_test.rb
diff --git a/test/seeds/tour_test.rb b/test/seeds/tour_test.rb
deleted file mode 100644
index 4ae732368..000000000
--- a/test/seeds/tour_test.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-require 'test_helper'
-
-class TourTest < ActiveSupport::TestCase
- test "Tour question tag set exists for all communities after seeding" do
- # Ensure there are multiple communities
- c1 = Community.create(name: 'Test 1', host: 'test.host.1')
- c2 = Community.create(name: 'Test 2', host: 'test.host.2')
-
- Rails.application.load_seed
-
- # Every community should have the Tour tagset
- Community.all.each do |c|
- assert_not_nil TagSet.unscoped.where(community: c).find_by('Tour')
- end
- end
-end
\ No newline at end of file
From b3bc0d428e6dcf6b940157f98296a0734693b57a Mon Sep 17 00:00:00 2001
From: trichoplax
Date: Mon, 9 Jan 2023 23:32:05 +0000
Subject: [PATCH 145/968] Convert 'else if' to 'if' because it depends on data
from the previous 'if' block
---
app/assets/javascripts/qpixel_api.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/assets/javascripts/qpixel_api.js b/app/assets/javascripts/qpixel_api.js
index df4236002..77317537c 100644
--- a/app/assets/javascripts/qpixel_api.js
+++ b/app/assets/javascripts/qpixel_api.js
@@ -186,7 +186,7 @@ window.QPixel = {
this._preferences = null;
}
}
- else if (this._preferences == null) {
+ if (this._preferences == null) {
// If they're still null (or undefined) after loading from localStorage, we're probably on a site we haven't
// loaded them for yet. Load from Redis via AJAX.
const resp = await fetch('/users/me/preferences', {
From 8be131c6cb77034ba0bcc628fcdfc0aa480fb92b Mon Sep 17 00:00:00 2001
From: trichoplax
Date: Mon, 9 Jan 2023 23:38:04 +0000
Subject: [PATCH 146/968] Remove check for old preferences schema from over 2
years ago
---
app/assets/javascripts/qpixel_api.js | 6 ------
1 file changed, 6 deletions(-)
diff --git a/app/assets/javascripts/qpixel_api.js b/app/assets/javascripts/qpixel_api.js
index 77317537c..651d62a35 100644
--- a/app/assets/javascripts/qpixel_api.js
+++ b/app/assets/javascripts/qpixel_api.js
@@ -179,12 +179,6 @@ window.QPixel = {
preferences: async () => {
if (this._preferences == null && !!localStorage['qpixel.user_preferences']) {
this._preferences = JSON.parse(localStorage['qpixel.user_preferences']);
-
- // If we don't have the global key, we're probably using an old preferences schema.
- if (!this._preferences.global) {
- delete localStorage['qpixel.user_preferences'];
- this._preferences = null;
- }
}
if (this._preferences == null) {
// If they're still null (or undefined) after loading from localStorage, we're probably on a site we haven't
From 4ba7d6a67003bbd65f448e7b7ee18ec5307e423c Mon Sep 17 00:00:00 2001
From: trichoplax
Date: Mon, 9 Jan 2023 23:44:50 +0000
Subject: [PATCH 147/968] Add early return for most frequent case to reduce
processing
---
app/assets/javascripts/qpixel_api.js | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/app/assets/javascripts/qpixel_api.js b/app/assets/javascripts/qpixel_api.js
index 651d62a35..c0c3d1fbb 100644
--- a/app/assets/javascripts/qpixel_api.js
+++ b/app/assets/javascripts/qpixel_api.js
@@ -177,6 +177,10 @@ window.QPixel = {
* @returns {Promise
No results for <%= params[:search] %>. Try using a different search term.
<% end %>
-
+
<% @posts.each do |post| %>
<% next if post.nil? %>
<%= render 'posts/type_agnostic', post: post, show_type_tag: true, show_category_tag: true %>
From 42b7833be117ee5f24cf8ba012600ff1e9b00078 Mon Sep 17 00:00:00 2001
From: trichoplax
Date: Fri, 20 Jan 2023 01:51:35 +0000
Subject: [PATCH 187/968] Prevent request for user data unless there is a
signed in user
---
app/assets/javascripts/qpixel_api.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/assets/javascripts/qpixel_api.js b/app/assets/javascripts/qpixel_api.js
index 8b6defbe3..a499ee98f 100644
--- a/app/assets/javascripts/qpixel_api.js
+++ b/app/assets/javascripts/qpixel_api.js
@@ -158,7 +158,7 @@ window.QPixel = {
* @returns {Promise} a JSON object containing user details
*/
user: async () => {
- if (QPixel._user != null) {
+ if (QPixel._user != null || document.body.dataset.signedIn === 'false') {
return QPixel._user;
}
const resp = await fetch('/users/me', {
From cc1cd9ed2842c4f8aa46eceea18c00e37625f6a2 Mon Sep 17 00:00:00 2001
From: trichoplax
Date: Fri, 20 Jan 2023 01:54:46 +0000
Subject: [PATCH 188/968] Use HTML data attribute userId instead of redundant
signedIn
---
app/assets/javascripts/qpixel_api.js | 4 ++--
app/views/layouts/application.html.erb | 1 -
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/app/assets/javascripts/qpixel_api.js b/app/assets/javascripts/qpixel_api.js
index a499ee98f..0bc6900ca 100644
--- a/app/assets/javascripts/qpixel_api.js
+++ b/app/assets/javascripts/qpixel_api.js
@@ -158,7 +158,7 @@ window.QPixel = {
* @returns {Promise} a JSON object containing user details
*/
user: async () => {
- if (QPixel._user != null || document.body.dataset.signedIn === 'false') {
+ if (QPixel._user != null || document.body.dataset.userId === 'none') {
return QPixel._user;
}
const resp = await fetch('/users/me', {
@@ -180,7 +180,7 @@ window.QPixel = {
*/
preferences: async () => {
// Do not attempt to access preferences if user is not signed in
- if (document.body.dataset.signedIn === 'false') {
+ if (document.body.dataset.userId === 'none') {
return null;
}
// Early return for the most frequent case (local variable already contains the preferences)
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
index 489588861..a4a8c1241 100644
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -4,7 +4,6 @@
<%= render 'layouts/head' %>
<%= render 'layouts/header' %>
From a3284e59446d37ffbf3f78f91515f81cedff6276 Mon Sep 17 00:00:00 2001
From: trichoplax
Date: Fri, 20 Jan 2023 02:12:44 +0000
Subject: [PATCH 189/968] Update JS doc comment because function is no longer
async
---
app/assets/javascripts/qpixel_api.js | 1 -
1 file changed, 1 deletion(-)
diff --git a/app/assets/javascripts/qpixel_api.js b/app/assets/javascripts/qpixel_api.js
index 0bc6900ca..d05787491 100644
--- a/app/assets/javascripts/qpixel_api.js
+++ b/app/assets/javascripts/qpixel_api.js
@@ -281,7 +281,6 @@ window.QPixel = {
/**
* Set local variable _preferences and localStorage to new preferences data
* @param data an object, containing the new preferences data
- * @returns {Promise}
*/
_updatePreferencesLocally: data => {
QPixel._preferences = data;
From fb39107e19467ad55dd04b0a5af9b7fb2beb299d Mon Sep 17 00:00:00 2001
From: trichoplax
Date: Fri, 20 Jan 2023 02:35:35 +0000
Subject: [PATCH 190/968] Simplify QPixel._preferencesLocalStorageKey function
---
app/assets/javascripts/qpixel_api.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/assets/javascripts/qpixel_api.js b/app/assets/javascripts/qpixel_api.js
index d05787491..f5137bbe8 100644
--- a/app/assets/javascripts/qpixel_api.js
+++ b/app/assets/javascripts/qpixel_api.js
@@ -260,7 +260,7 @@ window.QPixel = {
const id = document.body.dataset.userId;
const key = `qpixel.user_${id}_preferences`;
QPixel._preferencesLocalStorageKey = () => key;
- return QPixel._preferencesLocalStorageKey();
+ return key;
},
/**
From fee143ed1d337ff9ebaaa045d8ac4d283981a9bf Mon Sep 17 00:00:00 2001
From: Taico Aerts
Date: Sat, 21 Jan 2023 17:59:46 +0100
Subject: [PATCH 191/968] Merge migrations
---
.../20220914193044_diallow_filter_user_or_name_null.rb | 6 ------
db/migrate/20220916075849_add_tags_to_filters.rb | 6 ------
...create_filters.rb => 20220916075849_create_filters.rb} | 8 +++++---
3 files changed, 5 insertions(+), 15 deletions(-)
delete mode 100644 db/migrate/20220914193044_diallow_filter_user_or_name_null.rb
delete mode 100644 db/migrate/20220916075849_add_tags_to_filters.rb
rename db/migrate/{20220914044523_create_filters.rb => 20220916075849_create_filters.rb} (51%)
diff --git a/db/migrate/20220914193044_diallow_filter_user_or_name_null.rb b/db/migrate/20220914193044_diallow_filter_user_or_name_null.rb
deleted file mode 100644
index 888324316..000000000
--- a/db/migrate/20220914193044_diallow_filter_user_or_name_null.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-class DiallowFilterUserOrNameNull < ActiveRecord::Migration[7.0]
- def change
- change_column_null :filters, :user_id, false
- change_column_null :filters, :name, false
- end
-end
diff --git a/db/migrate/20220916075849_add_tags_to_filters.rb b/db/migrate/20220916075849_add_tags_to_filters.rb
deleted file mode 100644
index 08eb02fc3..000000000
--- a/db/migrate/20220916075849_add_tags_to_filters.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-class AddTagsToFilters < ActiveRecord::Migration[7.0]
- def change
- add_column :filters, :include_tags, :string
- add_column :filters, :exclude_tags, :string
- end
-end
diff --git a/db/migrate/20220914044523_create_filters.rb b/db/migrate/20220916075849_create_filters.rb
similarity index 51%
rename from db/migrate/20220914044523_create_filters.rb
rename to db/migrate/20220916075849_create_filters.rb
index 3659b63b9..a8bd9983c 100644
--- a/db/migrate/20220914044523_create_filters.rb
+++ b/db/migrate/20220916075849_create_filters.rb
@@ -1,13 +1,15 @@
-class CreateFilters < ActiveRecord::Migration[7.0]
+class AddTagsToFilters < ActiveRecord::Migration[7.0]
def change
create_table :filters do |t|
- t.references :user, foreign_key: true
- t.string :name
+ t.references :user, null: false, foreign_key: true
+ t.string :name, null: false
t.float :min_score
t.float :max_score
t.integer :min_answers
t.integer :max_answers
t.string :status
+ t.string :include_tags
+ t.string :exclude_tags
t.timestamps
end
From dc2c8c36dad09d59b84b69f43488e19f25979d51 Mon Sep 17 00:00:00 2001
From: Taico Aerts
Date: Sat, 21 Jan 2023 18:28:12 +0100
Subject: [PATCH 192/968] Small style cleanup
---
app/helpers/search_helper.rb | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb
index b70d7441a..f8dd1c927 100644
--- a/app/helpers/search_helper.rb
+++ b/app/helpers/search_helper.rb
@@ -39,7 +39,7 @@ def filter_to_qualifiers(filter)
qualifiers
end
- def active_filter()
+ def active_filter
{
default: false,
name: params[:predefined_filter],
@@ -206,7 +206,6 @@ def qualifiers_to_sql(qualifiers, query)
when :include_tag
query = query.where(posts: { id: PostsTag.where(tag_id: qualifier[:tag_id]).select(:post_id) })
when :include_tags
- # Efficiency is... questionable
qualifier[:tag_ids].each do |id|
query = query.where(id: PostsTag.where(tag_id: id).select(:post_id))
end
From 2e9c0e775ab21fa708119e0c4a6f4a07ba354055 Mon Sep 17 00:00:00 2001
From: Taico Aerts
Date: Sat, 21 Jan 2023 18:28:39 +0100
Subject: [PATCH 193/968] Fix error in case filter does not exist yet
---
app/helpers/users_helper.rb | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb
index b0fb61d96..c914ed235 100644
--- a/app/helpers/users_helper.rb
+++ b/app/helpers/users_helper.rb
@@ -36,7 +36,8 @@ def default_filter(user_id, category_id)
def set_filter_default(user_id, filter_id, category_id, keep)
if keep
- CategoryFilterDefault.find_or_create_by(user_id: user_id, category_id: category_id)
+ CategoryFilterDefault.create_with(filter_id: filter_id)
+ .find_or_create_by(user_id: user_id, category_id: category_id)
.update(filter_id: filter_id)
else
CategoryFilterDefault.where(user_id: user_id, category_id: category_id)
From b23433ed43c9db124f59fcec27e444204e513dfd Mon Sep 17 00:00:00 2001
From: Taico Aerts
Date: Sat, 21 Jan 2023 18:28:57 +0100
Subject: [PATCH 194/968] Fix accidental delete
---
app/views/layouts/_sidebar.html.erb | 1 +
1 file changed, 1 insertion(+)
diff --git a/app/views/layouts/_sidebar.html.erb b/app/views/layouts/_sidebar.html.erb
index d03117b6b..33a713a42 100644
--- a/app/views/layouts/_sidebar.html.erb
+++ b/app/views/layouts/_sidebar.html.erb
@@ -86,6 +86,7 @@
<% end %>
+
<% unless @community.is_fake %>
<% if user_signed_in? %>
From dbaee55ac94cf8e2d9b5f548b5190fb144181f3f Mon Sep 17 00:00:00 2001
From: Taico Aerts
Date: Sat, 21 Jan 2023 18:29:08 +0100
Subject: [PATCH 195/968] Set default to no value
---
config/config/preferences.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/config/config/preferences.yml b/config/config/preferences.yml
index 28fc48eca..5d72b1204 100644
--- a/config/config/preferences.yml
+++ b/config/config/preferences.yml
@@ -81,6 +81,6 @@ sticky_header:
global: true
default_filter_name:
- type: integer
+ type: ~
default: none
category: true
\ No newline at end of file
From 8ea0b02cfb1afae4353fc319e9c70cd187d38701 Mon Sep 17 00:00:00 2001
From: Taico Aerts
Date: Sat, 21 Jan 2023 18:29:22 +0100
Subject: [PATCH 196/968] Fix failing test
---
test/controllers/search_controller_test.rb | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/test/controllers/search_controller_test.rb b/test/controllers/search_controller_test.rb
index 00792a96e..9beba194c 100644
--- a/test/controllers/search_controller_test.rb
+++ b/test/controllers/search_controller_test.rb
@@ -3,10 +3,10 @@
class SearchControllerTest < ActionController::TestCase
include Devise::Test::ControllerHelpers
- test 'get without a search term should result in nil' do
+ test 'get without a search term should result in all posts' do
get :search
assert_response 200
- assert_nil assigns(:posts)
+ assert_not_nil assigns(:posts)
end
test 'get with a search term should have results' do
From ce204454af223643aa698ab75591ea552352b668 Mon Sep 17 00:00:00 2001
From: Taico Aerts
Date: Sat, 21 Jan 2023 18:30:19 +0100
Subject: [PATCH 197/968] Fix migration mistake
---
db/migrate/20220916075849_create_filters.rb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/db/migrate/20220916075849_create_filters.rb b/db/migrate/20220916075849_create_filters.rb
index a8bd9983c..ffb41db6a 100644
--- a/db/migrate/20220916075849_create_filters.rb
+++ b/db/migrate/20220916075849_create_filters.rb
@@ -1,4 +1,4 @@
-class AddTagsToFilters < ActiveRecord::Migration[7.0]
+class CreateFilters < ActiveRecord::Migration[7.0]
def change
create_table :filters do |t|
t.references :user, null: false, foreign_key: true
From 86fd841bd30cbbabe665f27aefee967c4d30e64a Mon Sep 17 00:00:00 2001
From: Taico Aerts
Date: Sat, 21 Jan 2023 18:50:18 +0100
Subject: [PATCH 198/968] Use strong parameters for filters
---
app/controllers/users_controller.rb | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 9082c578a..a2a6e2c0b 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -104,10 +104,7 @@ def set_filter
if user_signed_in? && params[:name]
filter = Filter.find_or_create_by(user: current_user, name: params[:name])
- filter.update(min_score: params[:min_score], max_score: params[:max_score],
- min_answers: params[:min_answers], max_answers: params[:max_answers],
- include_tags: params[:include_tags], exclude_tags: params[:exclude_tags],
- status: params[:status])
+ filter.update(filter_params)
unless params[:category].nil? || params[:is_default].nil?
helpers.set_filter_default(current_user.id, filter.id, params[:category].to_i, params[:is_default])
@@ -582,6 +579,11 @@ def specific_avatar
private
+ def filter_params
+ params.permit(:min_score, :max_score, :min_answers, :max_answers, :status, :include_tags, :exclude_tags,
+ include_tags: [], exclude_tags: [])
+ end
+
def set_user
@user = user_scope.find_by(id: params[:id])
not_found if @user.nil?
From 4cd0a6aae6ac3fbd53a4cd75a14e3f5ba2782dce Mon Sep 17 00:00:00 2001
From: Taico Aerts
Date: Sat, 21 Jan 2023 21:13:45 +0100
Subject: [PATCH 199/968] Update schema according to new migrations
---
db/schema.rb | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/db/schema.rb b/db/schema.rb
index 0effcfd25..e24ca8fc7 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -251,10 +251,10 @@
t.integer "min_answers"
t.integer "max_answers"
t.string "status"
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
t.string "include_tags"
t.string "exclude_tags"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
t.index ["user_id"], name: "index_filters_on_user_id"
end
From b770fe11b1b44ac3490b013234d712c9b7d2cd4b Mon Sep 17 00:00:00 2001
From: Taico Aerts
Date: Sat, 21 Jan 2023 21:39:01 +0100
Subject: [PATCH 200/968] Add missing dependent destroy
---
app/models/filter.rb | 1 +
1 file changed, 1 insertion(+)
diff --git a/app/models/filter.rb b/app/models/filter.rb
index 2fd752b07..fb11e1d47 100644
--- a/app/models/filter.rb
+++ b/app/models/filter.rb
@@ -1,5 +1,6 @@
class Filter < ApplicationRecord
belongs_to :user
+ has_many :category_filter_defaults, dependent: :destroy
validates :name, uniqueness: { scope: :user }
serialize :include_tags, Array
serialize :exclude_tags, Array
From 69cd37738af11394238b148a260e9aba009ee2a8 Mon Sep 17 00:00:00 2001
From: Taico Aerts
Date: Sat, 21 Jan 2023 21:50:26 +0100
Subject: [PATCH 201/968] Fix rubocop warnings
---
app/helpers/search_helper.rb | 14 +++++++-------
test/models/category_filter_default_test.rb | 2 +-
test/models/filter_test.rb | 2 +-
3 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb
index f8dd1c927..c6af23af2 100644
--- a/app/helpers/search_helper.rb
+++ b/app/helpers/search_helper.rb
@@ -131,12 +131,12 @@ def parse_qualifier_strings(qualifiers)
{ param: :created, operator: operator.presence || '=', timeframe: timeframe, value: val.to_i }
when 'user'
operator, val = if value.match?(valid_value[:numeric])
- numeric_value_sql value
- elsif value == 'me'
- ['=', current_user.id]
- else
- next
- end
+ numeric_value_sql value
+ elsif value == 'me'
+ ['=', current_user.id]
+ else
+ next
+ end
{ param: :user, operator: operator.presence || '=', user_id: val.to_i }
when 'upvotes'
@@ -178,7 +178,7 @@ def parse_qualifier_strings(qualifiers)
{ param: :status, value: value }
end
- end.reject(&:nil?)
+ end.compact
# Consider partitioning and telling the user which filters were invalid
end
diff --git a/test/models/category_filter_default_test.rb b/test/models/category_filter_default_test.rb
index 446d71ac3..72358b7c0 100644
--- a/test/models/category_filter_default_test.rb
+++ b/test/models/category_filter_default_test.rb
@@ -1,4 +1,4 @@
-require "test_helper"
+require 'test_helper'
class CategoryFilterDefaultTest < ActiveSupport::TestCase
# test "the truth" do
diff --git a/test/models/filter_test.rb b/test/models/filter_test.rb
index f9dee97f8..418de9e94 100644
--- a/test/models/filter_test.rb
+++ b/test/models/filter_test.rb
@@ -1,4 +1,4 @@
-require "test_helper"
+require 'test_helper'
class FilterTest < ActiveSupport::TestCase
# test "the truth" do
From e0e5e49b1118f5b303b7ea4de995d3a85618d7cd Mon Sep 17 00:00:00 2001
From: trichoplax
Date: Sun, 22 Jan 2023 00:01:46 +0000
Subject: [PATCH 202/968] Move check for signed in user from preferences
function to preference
Previously the QPixel.preferences function returned null when there
was no signed in user. This caused an error as the QPixel.preference
function tried to access properties of null. Now the check for a
signed in user happens in QPixel.preference and this function returns
null when there is no signed in user. The three places that currently
use this return value all behave correctly when there is no signed in
user - the null does not cause incorrect behaviour.
---
app/assets/javascripts/qpixel_api.js | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/app/assets/javascripts/qpixel_api.js b/app/assets/javascripts/qpixel_api.js
index f5137bbe8..4fc402f72 100644
--- a/app/assets/javascripts/qpixel_api.js
+++ b/app/assets/javascripts/qpixel_api.js
@@ -179,10 +179,6 @@ window.QPixel = {
* @returns {Promise} a JSON object containing user preferences
*/
preferences: async () => {
- // Do not attempt to access preferences if user is not signed in
- if (document.body.dataset.userId === 'none') {
- return null;
- }
// Early return for the most frequent case (local variable already contains the preferences)
if (QPixel._preferences != null) {
return QPixel._preferences;
@@ -209,6 +205,10 @@ window.QPixel = {
* @returns {Promise<*>} the value of the requested preference
*/
preference: async (name, community = false) => {
+ // Do not attempt to access preference if user is not signed in
+ if (document.body.dataset.userId === 'none') {
+ return null;
+ }
let prefs = await QPixel.preferences();
let value = community ? prefs.community[name] : prefs.global[name];
From 90ab889080c4ee882c8714046681dc5410517151 Mon Sep 17 00:00:00 2001
From: trichoplax
Date: Sun, 22 Jan 2023 00:16:05 +0000
Subject: [PATCH 203/968] Rename function to make clear it is internal only
---
app/assets/javascripts/qpixel_api.js | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/app/assets/javascripts/qpixel_api.js b/app/assets/javascripts/qpixel_api.js
index 4fc402f72..5dfb5e7e4 100644
--- a/app/assets/javascripts/qpixel_api.js
+++ b/app/assets/javascripts/qpixel_api.js
@@ -178,7 +178,7 @@ window.QPixel = {
* localStorage, or Redis via AJAX.
* @returns {Promise} a JSON object containing user preferences
*/
- preferences: async () => {
+ _getPreferences: async () => {
// Early return for the most frequent case (local variable already contains the preferences)
if (QPixel._preferences != null) {
return QPixel._preferences;
@@ -209,7 +209,7 @@ window.QPixel = {
if (document.body.dataset.userId === 'none') {
return null;
}
- let prefs = await QPixel.preferences();
+ let prefs = await QPixel._getPreferences();
let value = community ? prefs.community[name] : prefs.global[name];
// Note that null is a valid value for a preference, but undefined means we haven't fetched it.
@@ -219,7 +219,7 @@ window.QPixel = {
// If we haven't fetched a preference, that probably means it's new - run a full re-fetch.
await QPixel._fetchPreferences();
- prefs = await QPixel.preferences();
+ prefs = await QPixel._getPreferences();
value = community ? prefs.community[name] : prefs.global[name];
return value;
},
From 0ee5d266afc13abe37e8db2375dd6da7d8c81834 Mon Sep 17 00:00:00 2001
From: trichoplax
Date: Sun, 22 Jan 2023 01:47:49 +0000
Subject: [PATCH 204/968] Cache '_fetchPreferences' returned promise to avoid
redundant fetches
---
app/assets/javascripts/qpixel_api.js | 20 ++++++++++++++++++--
1 file changed, 18 insertions(+), 2 deletions(-)
diff --git a/app/assets/javascripts/qpixel_api.js b/app/assets/javascripts/qpixel_api.js
index 5dfb5e7e4..7c9ebf517 100644
--- a/app/assets/javascripts/qpixel_api.js
+++ b/app/assets/javascripts/qpixel_api.js
@@ -194,7 +194,7 @@ window.QPixel = {
}
// Preferences are still null (or undefined) after loading from localStorage, so we're probably on a site we
// haven't loaded them for yet. Load from Redis via AJAX.
- await QPixel._fetchPreferences();
+ await QPixel._cachedFetchPreferences();
return QPixel._preferences;
},
@@ -217,7 +217,7 @@ window.QPixel = {
return value;
}
// If we haven't fetched a preference, that probably means it's new - run a full re-fetch.
- await QPixel._fetchPreferences();
+ await QPixel._cachedFetchPreferences();
prefs = await QPixel._getPreferences();
value = community ? prefs.community[name] : prefs.global[name];
@@ -263,6 +263,22 @@ window.QPixel = {
return key;
},
+ /**
+ * Call _fetchPreferences but only the first time to prevent redundant HTTP requests
+ * @returns {Promise}
+ */
+ _cachedFetchPreferences: async () => {
+ // No 'await' because we want the promise not its value
+ const cachedPromise = QPixel._fetchPreferences();
+ // Redefine this function to await this same initial promise on every subsequent call
+ // This prevents multiple calls from triggering multiple redundant '_fetchPreferences' calls
+ QPixel._cachedFetchPreferences = async () => {
+ await cachedPromise;
+ }
+ // Remember to await the promise so the very first call does not return before '_fetchPreferences' returns
+ await cachedPromise;
+ },
+
/**
* Update local variable _preferences and localStorage with an AJAX call for the user preferences
* @returns {Promise}
From 91fd597be01ac33d070e5f97de1571437046f3d0 Mon Sep 17 00:00:00 2001
From: MoshiKoi <54333972+MoshiKoi@users.noreply.github.com>
Date: Sun, 22 Jan 2023 13:59:56 -0800
Subject: [PATCH 205/968] Add basic copy functionality
---
app/assets/javascripts/codeblocks.js | 11 +++++++++++
1 file changed, 11 insertions(+)
create mode 100644 app/assets/javascripts/codeblocks.js
diff --git a/app/assets/javascripts/codeblocks.js b/app/assets/javascripts/codeblocks.js
new file mode 100644
index 000000000..2f8ae5183
--- /dev/null
+++ b/app/assets/javascripts/codeblocks.js
@@ -0,0 +1,11 @@
+$(() => {
+ $(".post--content pre").each((_, block) => {
+ const copyButton = document.createElement('button');
+ copyButton.innerText = 'Copy';
+ copyButton.addEventListener('click', _ => {
+ navigator.clipboard.writeText(block.innerText);
+ });
+
+ block.insertAdjacentElement('afterend', copyButton);
+ });
+});
\ No newline at end of file
From 12519bdad40ce61c0527f60fc9804c5765e4b472 Mon Sep 17 00:00:00 2001
From: MoshiKoi <54333972+MoshiKoi@users.noreply.github.com>
Date: Sun, 22 Jan 2023 14:05:38 -0800
Subject: [PATCH 206/968] Add some styling
---
app/assets/javascripts/codeblocks.js | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/app/assets/javascripts/codeblocks.js b/app/assets/javascripts/codeblocks.js
index 2f8ae5183..416cbf7b3 100644
--- a/app/assets/javascripts/codeblocks.js
+++ b/app/assets/javascripts/codeblocks.js
@@ -1,11 +1,16 @@
$(() => {
$(".post--content pre").each((_, block) => {
const copyButton = document.createElement('button');
+ copyButton.classList.add('button', 'is-muted', 'is-outlined', 'has-float-right');
copyButton.innerText = 'Copy';
copyButton.addEventListener('click', _ => {
navigator.clipboard.writeText(block.innerText);
+ copyButton.innerText = 'Copied!';
+ setTimeout(() => {
+ copyButton.innerText = 'Copy';
+ }, 2000);
});
- block.insertAdjacentElement('afterend', copyButton);
+ block.insertAdjacentElement('afterbegin', copyButton);
});
});
\ No newline at end of file
From 9139f9435cefde23ac7b03b00eed4b328c7e3d07 Mon Sep 17 00:00:00 2001
From: MoshiKoi <54333972+MoshiKoi@users.noreply.github.com>
Date: Sun, 22 Jan 2023 14:12:03 -0800
Subject: [PATCH 207/968] Don't copy the button itself
---
app/assets/javascripts/codeblocks.js | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/app/assets/javascripts/codeblocks.js b/app/assets/javascripts/codeblocks.js
index 416cbf7b3..cf6262dbf 100644
--- a/app/assets/javascripts/codeblocks.js
+++ b/app/assets/javascripts/codeblocks.js
@@ -1,5 +1,6 @@
$(() => {
- $(".post--content pre").each((_, block) => {
+ $(".post--content pre > code").each((_, block) => {
+ const wrapper = block.parentElement;
const copyButton = document.createElement('button');
copyButton.classList.add('button', 'is-muted', 'is-outlined', 'has-float-right');
copyButton.innerText = 'Copy';
@@ -11,6 +12,6 @@ $(() => {
}, 2000);
});
- block.insertAdjacentElement('afterbegin', copyButton);
+ wrapper.insertAdjacentElement('afterbegin', copyButton);
});
});
\ No newline at end of file
From 8e573657e4b917e26fd3fbd01cb293df17c04f46 Mon Sep 17 00:00:00 2001
From: MoshiKoi <54333972+MoshiKoi@users.noreply.github.com>
Date: Sun, 22 Jan 2023 14:26:10 -0800
Subject: [PATCH 208/968] Update app/models/suggested_edit.rb
Co-authored-by: Taico Aerts
---
app/models/suggested_edit.rb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/models/suggested_edit.rb b/app/models/suggested_edit.rb
index 278691e28..69341c957 100644
--- a/app/models/suggested_edit.rb
+++ b/app/models/suggested_edit.rb
@@ -10,7 +10,7 @@ class SuggestedEdit < ApplicationRecord
has_and_belongs_to_many :tags
has_and_belongs_to_many :before_tags, class_name: 'Tag', join_table: 'suggested_edits_before_tags'
- after_save :clear_pending_cache
+ after_save :clear_pending_cache, if: Proc.new { saved_change_to_attribute?(:active) }
def clear_pending_cache
Rails.cache.delete "pending_suggestions/#{post.category_id}"
From 7b84fdd98cad844f82e0b23f1103f79ac4a1cb25 Mon Sep 17 00:00:00 2001
From: MoshiKoi <54333972+MoshiKoi@users.noreply.github.com>
Date: Sun, 22 Jan 2023 18:55:38 -0800
Subject: [PATCH 209/968] Fix positioning
---
app/assets/javascripts/codeblocks.js | 11 +++++++++--
app/assets/stylesheets/utilities.scss | 8 ++++++++
2 files changed, 17 insertions(+), 2 deletions(-)
diff --git a/app/assets/javascripts/codeblocks.js b/app/assets/javascripts/codeblocks.js
index cf6262dbf..d619c4847 100644
--- a/app/assets/javascripts/codeblocks.js
+++ b/app/assets/javascripts/codeblocks.js
@@ -1,8 +1,14 @@
$(() => {
$(".post--content pre > code").each((_, block) => {
const wrapper = block.parentElement;
+
+ const wrapperDiv = document.createElement('div');
+ wrapperDiv.style.position = 'relative';
+
const copyButton = document.createElement('button');
- copyButton.classList.add('button', 'is-muted', 'is-outlined', 'has-float-right');
+ copyButton.style.position = 'absolute';
+ copyButton.style.right = 0;
+ copyButton.classList.add('copy-button', 'button', 'is-muted', 'is-outlined', 'has-margin-2');
copyButton.innerText = 'Copy';
copyButton.addEventListener('click', _ => {
navigator.clipboard.writeText(block.innerText);
@@ -12,6 +18,7 @@ $(() => {
}, 2000);
});
- wrapper.insertAdjacentElement('afterbegin', copyButton);
+ wrapper.insertAdjacentElement('beforebegin', wrapperDiv);
+ wrapperDiv.append(copyButton, wrapper);
});
});
\ No newline at end of file
diff --git a/app/assets/stylesheets/utilities.scss b/app/assets/stylesheets/utilities.scss
index be684f5f9..3a7acd448 100644
--- a/app/assets/stylesheets/utilities.scss
+++ b/app/assets/stylesheets/utilities.scss
@@ -95,6 +95,14 @@ pre.pre-wrap {
white-space: pre-wrap !important;
}
+.copy-button {
+ display: none;
+}
+
+div:hover > .copy-button {
+ display: block;
+}
+
.stat-panel {
flex: 1;
border: 1px solid $muted-graphic;
From 654243a0b034904f755efb020de787d198624ac6 Mon Sep 17 00:00:00 2001
From: Taico Aerts
Date: Mon, 23 Jan 2023 15:46:04 +0100
Subject: [PATCH 210/968] Increase max module length
---
.rubocop.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.rubocop.yml b/.rubocop.yml
index 5e6705c20..bf168f19e 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -49,7 +49,7 @@ Metrics/CyclomaticComplexity:
Metrics/MethodLength:
Max: 60
Metrics/ModuleLength:
- Max: 200
+ Max: 250
Metrics/PerceivedComplexity:
Enabled: false
From 8814df9d2460ed3f8d412c928dc8ddb456c3c6eb Mon Sep 17 00:00:00 2001
From: Mithical
Date: Tue, 24 Jan 2023 18:46:00 +0200
Subject: [PATCH 211/968] site -> community
---
app/views/posts/_form.html.erb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/views/posts/_form.html.erb b/app/views/posts/_form.html.erb
index 97c7a2d9d..54146bd33 100644
--- a/app/views/posts/_form.html.erb
+++ b/app/views/posts/_form.html.erb
@@ -101,7 +101,7 @@
<% category_default = category.license %>
<% user_default = user_preference('default_license', community: true) %>
<% if site_default.present? %>
- site default:
+ community default:
<%= site_default.name %>
<% end %>
From 4adff31202db0d4bb52b51a54ea7defbafd6095a Mon Sep 17 00:00:00 2001
From: Mithical
Date: Tue, 24 Jan 2023 18:48:23 +0200
Subject: [PATCH 212/968] site -> community
---
app/views/licenses/_form.html.erb | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/views/licenses/_form.html.erb b/app/views/licenses/_form.html.erb
index 363306285..e0a182957 100644
--- a/app/views/licenses/_form.html.erb
+++ b/app/views/licenses/_form.html.erb
@@ -20,9 +20,9 @@
<%= f.check_box :default, class: 'form-checkbox-element' %>
<%= f.label :default, 'Default license?' %>
- Set this license as the site-default license. A site can only have one default license.
+ Set this license as the community-default license. A community can only have one default license.
<%= f.submit 'Save', class: 'button is-filled' %>
-<% end %>
\ No newline at end of file
+<% end %>
From 6ae38a8dd3d025c1acde842505602e0306268a24 Mon Sep 17 00:00:00 2001
From: Monica Cellio
Date: Tue, 24 Jan 2023 22:03:54 -0500
Subject: [PATCH 213/968] added info about using HTML details/summary
(CommonMark doesn't support)
---
db/seeds/posts/formatting.html | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/db/seeds/posts/formatting.html b/db/seeds/posts/formatting.html
index e9ad8a39d..4e4e966b4 100644
--- a/db/seeds/posts/formatting.html
+++ b/db/seeds/posts/formatting.html
@@ -64,3 +64,26 @@
Heading 6
Footnotes
To include a footnote in your post, you can use the syntax [^1]. In your main text, include Text[^1] and more text, and at the bottom (where you want to include your footnote), place a line resembling [^1]: footnote text.
+
+
Hidden Sections
+
CommonMark does not support collapsible sections (sometimes called "spoiler blocks"), but you can use the HTML details and summary tags, like this:
+
<details>
+ <summary>Spoiler! Click here to reveal</summary>
+ Secret details
+</details>
+
Which renders like this:
+
+Spoiler! Click here to reveal
+Secret details
+
+
If the details text uses any Markdown, you must add a blank line between the summary and the text:
+
<details>
+ <summary>Spoiler! Click here to reveal</summary>
+
+ *Secret* details
+</details>
+
Renders:
+
+Spoiler! Click here to reveal
+Secret details
+
From 4f6ab4f0697b48e577e488f1d6c177da056c17aa Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 25 Jan 2023 04:35:29 +0000
Subject: [PATCH 214/968] Bump commonmarker from 0.23.6 to 0.23.7
Bumps [commonmarker](https://github.com/gjtorikian/commonmarker) from 0.23.6 to 0.23.7.
- [Release notes](https://github.com/gjtorikian/commonmarker/releases)
- [Changelog](https://github.com/gjtorikian/commonmarker/blob/main/CHANGELOG.md)
- [Commits](https://github.com/gjtorikian/commonmarker/compare/v0.23.6...v0.23.7)
---
updated-dependencies:
- dependency-name: commonmarker
dependency-type: direct:production
...
Signed-off-by: dependabot[bot]
---
Gemfile.lock | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Gemfile.lock b/Gemfile.lock
index c5c1af741..afb4706ed 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -105,7 +105,7 @@ GEM
coffee-script-source
execjs
coffee-script-source (1.12.2)
- commonmarker (0.23.6)
+ commonmarker (0.23.7)
concurrent-ruby (1.1.10)
counter_culture (3.2.1)
activerecord (>= 4.2)
From 8056054b7fa8e40b094d9ca83c84fb1f5fc5890d Mon Sep 17 00:00:00 2001
From: ArtOfCode-
Date: Mon, 30 Jan 2023 17:12:01 +0000
Subject: [PATCH 215/968] Add Stripe setting
---
db/seeds/site_settings.yml | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/db/seeds/site_settings.yml b/db/seeds/site_settings.yml
index 0104be1ee..9ffaa63ae 100644
--- a/db/seeds/site_settings.yml
+++ b/db/seeds/site_settings.yml
@@ -550,3 +550,10 @@
category: SignInAndSignUp
description: >
This setting only has an effect when SSO Sign In and Mixed Sign In are enabled. Allows users to disconnect their account from SSO and switch over to normal login.
+
+- name: LoadStripeEverywhere
+ value: false
+ value_type: boolean
+ category: Integrations
+ description: >
+ Load Stripe JS API on all pages instead of just donation pages. May improve security and fraud detection.
From 45dc17f6fc32e81378095434f50486fa71cbfaea Mon Sep 17 00:00:00 2001
From: ArtOfCode-
Date: Mon, 30 Jan 2023 17:15:51 +0000
Subject: [PATCH 216/968] Only load Stripe on donation pages
---
app/views/layouts/_head.html.erb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/views/layouts/_head.html.erb b/app/views/layouts/_head.html.erb
index c9bf20abd..4015f1247 100644
--- a/app/views/layouts/_head.html.erb
+++ b/app/views/layouts/_head.html.erb
@@ -27,7 +27,7 @@
<%= javascript_include_tag "https://cdn.jsdelivr.net/npm/jquery@2.2.2/dist/jquery.min.js" %>
<%= javascript_include_tag "https://cdn.jsdelivr.net/npm/moment@2.13.0/min/moment.min.js" %>
<%= javascript_include_tag "https://cdn.jsdelivr.net/npm/select2@4.0.12/dist/js/select2.min.js" %>
-<% if SiteSetting['DonationsEnabled'] %>
+<% if SiteSetting['DonationsEnabled'] && (SiteSetting['LoadStripeEverywhere'] || controller_name == 'donations') %>
<%= javascript_include_tag "https://js.stripe.com/v3/" %>
<% end %>
<%= javascript_include_tag "https://cdn.jsdelivr.net/npm/dompurify@2.2.9/dist/purify.min.js" %>
From a1d938a996b648c187a6dbce8116f32f7c40353e Mon Sep 17 00:00:00 2001
From: ArtOfCode-
Date: Mon, 30 Jan 2023 19:39:19 +0000
Subject: [PATCH 217/968] Add all-users email tool
---
app/controllers/admin_controller.rb | 14 +++++++++++++-
app/mailers/admin_mailer.rb | 8 ++++++++
app/views/admin/all_email.html.erb | 17 +++++++++++++++++
app/views/admin/index.html.erb | 13 +++++++++++++
app/views/admin_mailer/to_all_users.html.erb | 2 ++
app/views/admin_mailer/to_all_users.text.erb | 1 +
config/locales/strings/en.admin.yml | 5 +++++
config/routes.rb | 2 ++
db/schema.rb | 6 +++---
9 files changed, 64 insertions(+), 4 deletions(-)
create mode 100644 app/views/admin/all_email.html.erb
create mode 100644 app/views/admin_mailer/to_all_users.html.erb
create mode 100644 app/views/admin_mailer/to_all_users.text.erb
diff --git a/app/controllers/admin_controller.rb b/app/controllers/admin_controller.rb
index 6bf41e85d..5d13f6d3b 100644
--- a/app/controllers/admin_controller.rb
+++ b/app/controllers/admin_controller.rb
@@ -3,7 +3,7 @@ class AdminController < ApplicationController
before_action :verify_admin, except: [:change_back, :verify_elevation]
before_action :verify_global_admin, only: [:admin_email, :send_admin_email, :new_site, :create_site, :setup,
:setup_save, :hellban]
- before_action :verify_developer, only: [:change_users, :impersonate]
+ before_action :verify_developer, only: [:change_users, :impersonate, :all_email, :send_all_email]
def index; end
@@ -52,6 +52,18 @@ def send_admin_email
redirect_to admin_path
end
+ def all_email; end
+
+ def send_all_email
+ Thread.new do
+ AdminMailer.with(body_markdown: params[:body_markdown], subject: params[:subject]).to_all_users.deliver_now
+ end
+ AuditLog.admin_audit(event_type: 'send_all_email', user: current_user,
+ comment: "Subject: #{params[:subject]}")
+ flash[:success] = t 'admin.email_being_sent'
+ redirect_to admin_path
+ end
+
def audit_log
@logs = if current_user.is_global_admin
AuditLog.unscoped.where.not(log_type: ['user_annotation', 'user_history'])
diff --git a/app/mailers/admin_mailer.rb b/app/mailers/admin_mailer.rb
index a68f9c55c..5dd0805e0 100644
--- a/app/mailers/admin_mailer.rb
+++ b/app/mailers/admin_mailer.rb
@@ -10,4 +10,12 @@ def to_moderators
emails = ActiveRecord::Base.connection.execute(query).to_a.flatten
mail subject: "Codidact Moderators: #{@subject}", to: 'moderators-noreply@codidact.org', bcc: emails
end
+
+ def to_all_users
+ @subject = params[:subject]
+ @body_markdown = params[:body_markdown]
+ @users = User.where('email NOT LIKE ?', "%localhost").select(:email).map(&:email)
+ mail subject: @subject, to: 'allusers-noreply@codidact.org', from: 'Codidact Team ',
+ reply_to: 'info@codidact.org', bcc: @users
+ end
end
diff --git a/app/views/admin/all_email.html.erb b/app/views/admin/all_email.html.erb
new file mode 100644
index 000000000..a57e4e172
--- /dev/null
+++ b/app/views/admin/all_email.html.erb
@@ -0,0 +1,17 @@
+<%= render 'posts/markdown_script' %>
+
+
<%= t 'admin.tools.email_all' %>
+
<%= t 'admin.email_all_blurb' %>
+
+<%= form_with url: send_all_email_path do |f| %>
+
From c1c717e2d07be1d3cd42b2579b11ea1419ea563f Mon Sep 17 00:00:00 2001
From: Monica Cellio
Date: Mon, 22 May 2023 10:19:11 -0400
Subject: [PATCH 233/968] added internal documentation
---
config/storage.sample.yml | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/config/storage.sample.yml b/config/storage.sample.yml
index 88649a3fb..8d0ce7438 100644
--- a/config/storage.sample.yml
+++ b/config/storage.sample.yml
@@ -1,3 +1,8 @@
+# To use external files (such as image upload), you need a storage.yml file.
+# If you are using only the local disk (such as in a development environment),
+# you can just copy this file. If you are using an S3 bucket, copy this
+# file and then edit the settings.
+
test:
service: Disk
root: <%= Rails.root.join('tmp/storage') %>
From 6a6d5f9f686fab2fdd0c916e15ba3446b2d20220 Mon Sep 17 00:00:00 2001
From: Monica Cellio
Date: Mon, 29 May 2023 20:18:06 -0400
Subject: [PATCH 234/968] I came to make the updates for operators described in
https://meta.codidact.com/posts/288179, and while I was there I noticed that
the section on tags/users/categories/types that was already deployed was
missing here, so I added that too.
---
db/seeds/posts/search.html | 25 ++++++++++++++++++++++---
1 file changed, 22 insertions(+), 3 deletions(-)
diff --git a/db/seeds/posts/search.html b/db/seeds/posts/search.html
index 8215a42c6..e21de8a20 100644
--- a/db/seeds/posts/search.html
+++ b/db/seeds/posts/search.html
@@ -17,20 +17,39 @@
Filtering by score and age
It's possible to filter your search to only include results that have been posted within a certain timeframe, or match certain score requirements.
+
You can use >, >=, <, and <= with these options to search for ranges. By default, a value without an operator looks for an exact match. For example, upvotes:4 searches for exactly 4; upvotes:>=4 searches for at least 4.
filtering by post score
-
Codidact uses Wilson scoring to help in sorting posts. (To learn more about how this works, see /help/scoring for a detailed explanation.) Every post has a score between 0.0 and 1.0. To use this in search, you can use score:0.5 to filter your search to only include posts with a score of at least 0.5.
+
Codidact uses Wilson scoring to help in sorting posts. (To learn more about how this works, see /help/scoring for a detailed explanation.) Every post has a score between 0.0 and 1.0. To use this in search, you can use score:>=0.5 to filter your search to only include posts with a score of at least 0.5.
filtering by votes
-
If you want to filter by the raw votes that a post has, you can use votes:5 to find posts where the net votes (upvotes minus downvotes) of a post equals 5 or higher.
+
If you want to filter by the raw votes that a post has, you can use votes:5 to find posts where the net votes (upvotes minus downvotes) of a post equals 5 or votes:>=5 for votes of 5 or more.
filtering by upvotes and downvotes
-
If you search for upvotes:4, Codidact will find posts that have received at least 4 upvotes, irrespective of how many downvotes the post has. Likewise, if you search for downvotes:4, Codidact will find posts that have received at least 4 downvotes without taking upvotes into consideration. You can also use a less than (<) symbol to filter for posts that have received no more than a certain number of votes (for instance, downvotes:<4 will find posts that have received less than four downvotes total).
+
If you search for upvotes:4, Codidact will find posts that have received exactly 4 upvotes, irrespective of how many downvotes the post has. Likewise, if you search for downvotes:4, Codidact will find posts that have received exactly 4 downvotes without taking upvotes into consideration. downvotes:<4 will find posts that have received fewer than four downvotes total).
+
+
filtering by number of answers
+
If you want to find posts with n answers, use answers:n. This is particularly helpful to find unanswered questions: answers:0. answers:<5 shows posts with fewer than five answers.
filtering by creation date
If you want to only find posts that have been written within a certain timeframe, you can use the created: search operator. created:<1w will find all posts created less than a week ago, where created:>1w will find only posts older than a week. You can use m for minute, h for hour, d for day, w for week, mo for month, and y for year.
+
Filtering by tag, user, category, or post type
+
+
filtering by tag
+
To filter for all posts with the tag snake, use the tag:snake operator. To exclude all posts with the tag oil, use -tag:oil.
+
+
filtering by user
+
If you want to search for posts written by a particular user you will need to know their unique user number for the community. This can be found by looking at their profile URL. You can then use user:xxxx where xxxx is the unique user number you are interested in.
+
+
filtering by category
+
To filter by category, you will need to know the unique numeric ID for that category. This can be found by looking at the URL shown when you click to view all posts in a particular category. Use the formatting category:xxxx to apply this filter.
+
+
filtering by post type
+
If you want to restrict your search to a particular post type, type post_type: into the search bar, and a dropdown menu should show the available post types to search. Note that not all communities may use all post types.
+
+
Advanced
wildcard
From f8b1e0dc4db92fd89d6c65b3778f5c955299a976 Mon Sep 17 00:00:00 2001
From: Monica Cellio
Date: Mon, 29 May 2023 20:37:19 -0400
Subject: [PATCH 235/968] on search results, collapse filter by default and
move it to right above the list of posts (for consistency)
---
app/views/search/_widget.html.erb | 2 --
app/views/search/search.html.erb | 5 +++++
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/app/views/search/_widget.html.erb b/app/views/search/_widget.html.erb
index 07ecb0582..6ec88433a 100644
--- a/app/views/search/_widget.html.erb
+++ b/app/views/search/_widget.html.erb
@@ -8,6 +8,4 @@
<%= submit_tag 'Search', class: 'button is-medium is-outlined', name: nil %>
<% end %>
@@ -29,4 +29,4 @@
<%= link_to 'Change your email settings or unsubscribe here',
- subscriptions_url(host: @subscription.community.host) %>
\ No newline at end of file
+ subscriptions_url(host: @subscription.community.host) %>
From a435de802eea78f0bbd1cd1a5698e5a3fe33aff9 Mon Sep 17 00:00:00 2001
From: trichoplax
Date: Sun, 4 Jun 2023 22:58:04 +0100
Subject: [PATCH 241/968] Make url_opts optional so user_link can be called
without it
---
app/helpers/users_helper.rb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb
index 0c0525962..de17c5602 100644
--- a/app/helpers/users_helper.rb
+++ b/app/helpers/users_helper.rb
@@ -59,7 +59,7 @@ def rtl_safe_username(user)
deleted_user?(user) ? 'deleted user' : user.rtl_safe_username
end
- def user_link(user, url_opts, **link_opts)
+ def user_link(user, url_opts=nil, **link_opts)
url_opts ||= {}
if deleted_user?(user)
link_to 'deleted user', '#', { dir: 'ltr' }.merge(link_opts)
From 88a16f538d66c42d60ff2bd79e08dc3c7e5fb752 Mon Sep 17 00:00:00 2001
From: Moshi <54333972+MoshiKoi@users.noreply.github.com>
Date: Mon, 5 Jun 2023 01:34:24 -0700
Subject: [PATCH 242/968] Make "follow new" have button formatting for
consistency
---
app/views/posts/_expanded.html.erb | 28 +++++++++++++++-------------
1 file changed, 15 insertions(+), 13 deletions(-)
diff --git a/app/views/posts/_expanded.html.erb b/app/views/posts/_expanded.html.erb
index 82733701f..b0567a5e3 100644
--- a/app/views/posts/_expanded.html.erb
+++ b/app/views/posts/_expanded.html.erb
@@ -456,19 +456,22 @@
<%= pluralize(public_count, 'comment thread') %>
<% if user_signed_in? %>
-
- <% if CommentThread.post_followed?(post, current_user) %>
- <%= link_to follow_post_comments_path(post_id: post.id), method: :post,
- title: 'Don\'t follow new comment threads on this post' do %>
- unfollow new
- <% end %>
- <% else %>
- <%= link_to follow_post_comments_path(post_id: post.id), method: :post,
- title: 'Follow all new comment threads on this post' do %>
- follow new
- <% end %>
+
+ Start new comment thread
+
+ <% if CommentThread.post_followed?(post, current_user) %>
+ <%= link_to follow_post_comments_path(post_id: post.id), method: :post,
+ class: "button is-muted is-outlined is-small",
+ title: 'Don\'t follow new comment threads on this post' do %>
+ Unfollow new
<% end %>
-
+ <% else %>
+ <%= link_to follow_post_comments_path(post_id: post.id), method: :post,
+ class: "button is-muted is-outlined is-small",
+ title: 'Follow all new comment threads on this post' do %>
+ Follow new
+ <% end %>
+ <% end %>
<% end %>
Comments have been disabled on this post, but as a moderator you are exempt from that block.
<% end %> - Start new comment thread <% end %> <%= render 'comments/new_thread_modal', post: post %> <% end %> From ea63f3b5c29df5ad4941ec07b975edc838474dc4 Mon Sep 17 00:00:00 2001 From: Moshi <54333972+MoshiKoi@users.noreply.github.com> Date: Mon, 5 Jun 2023 01:45:55 -0700 Subject: [PATCH 243/968] Fix error being thrown on invalid urls --- app/assets/javascripts/embed.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/embed.js b/app/assets/javascripts/embed.js index f8a6547e1..5797df56b 100644 --- a/app/assets/javascripts/embed.js +++ b/app/assets/javascripts/embed.js @@ -10,9 +10,11 @@ $(() => { // Only embed raw YT links, i.e. not [text](link), only [link](link) if ((href.startsWith('https://youtube.com') || href.startsWith('https://www.youtube.com')) && $tgt.text() === href) { const videoId = /v=([^$&]+)/.exec(href); - $tgt.after(``); - $tgt.remove(); + if (videoId) { + $tgt.after(``); + $tgt.remove(); + } } // Likewise, only raw Spotify links From dda6337a38700b0d9df32f4d48c60fab35d1c9dd Mon Sep 17 00:00:00 2001 From: Moshi <54333972+MoshiKoi@users.noreply.github.com> Date: Mon, 5 Jun 2023 20:16:00 -0700 Subject: [PATCH 244/968] Allow lists in comments --- app/helpers/comments_helper.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/helpers/comments_helper.rb b/app/helpers/comments_helper.rb index 636d0a95d..548a269c9 100644 --- a/app/helpers/comments_helper.rb +++ b/app/helpers/comments_helper.rb @@ -81,8 +81,8 @@ def get_pingable(thread) class CommentScrubber < Rails::Html::PermitScrubber def initialize super - self.tags = %w[a b i em strong s strike del pre code p blockquote span sup sub br] - self.attributes = %w[href title lang dir id class] + self.tags = %w[a b i em strong s strike del pre code p blockquote span sup sub br ul ol li] + self.attributes = %w[href title lang dir id class start] end def skip_node?(node) From 255257c03b26a3317f945c2510495e8a85c3f04a Mon Sep 17 00:00:00 2001 From: Moshi <54333972+MoshiKoi@users.noreply.github.com> Date: Tue, 6 Jun 2023 17:11:17 -0700 Subject: [PATCH 245/968] Show errors to user when adding a reaction fails --- app/controllers/reactions_controller.rb | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/app/controllers/reactions_controller.rb b/app/controllers/reactions_controller.rb index 8899d8fa4..39070bf42 100644 --- a/app/controllers/reactions_controller.rb +++ b/app/controllers/reactions_controller.rb @@ -33,13 +33,19 @@ def add reaction = Reaction.new(user: current_user, post: @post, reaction_type: reaction_type, comment: comment) - ActiveRecord::Base.transaction do - thread&.save! - comment&.save! - reaction.save! - end + begin + ActiveRecord::Base.transaction do + thread&.save! + comment&.save! + reaction.save! + end - render json: { status: 'success' } + render json: { status: 'success' } + rescue + render json: { status: 'failed', message: "Could not create comment thread: #{(thread&.errors&.full_messages \ + + comment&.errors&.full_messages \ + + reaction.errors.full_messages).join(', ')}" } + end end def retract From e48faab02cc5fa2923d150114cac85f8b4353996 Mon Sep 17 00:00:00 2001 From: Moshi <54333972+MoshiKoi@users.noreply.github.com> Date: Tue, 6 Jun 2023 17:14:55 -0700 Subject: [PATCH 246/968] Add placeholder to say the minimum character count --- app/views/reactions/_dialog.html.erb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/views/reactions/_dialog.html.erb b/app/views/reactions/_dialog.html.erb index d6e75d533..64bedbde1 100644 --- a/app/views/reactions/_dialog.html.erb +++ b/app/views/reactions/_dialog.html.erb @@ -37,6 +37,7 @@ <% end %>