From efe75e7949e6bd1fa15560be8ad012d55b50ec70 Mon Sep 17 00:00:00 2001
From: ArtOfCode-
Date: Tue, 3 Mar 2026 22:40:19 +0000
Subject: [PATCH 01/21] Add process complaints
---
app/views/complaints/report.html.erb | 11 +++++++++--
config/config/safety_center.yml | 18 ++++++++++++++++--
db/schema.rb | 2 +-
3 files changed, 26 insertions(+), 5 deletions(-)
diff --git a/app/views/complaints/report.html.erb b/app/views/complaints/report.html.erb
index 730ddedba..f608f10eb 100644
--- a/app/views/complaints/report.html.erb
+++ b/app/views/complaints/report.html.erb
@@ -6,7 +6,8 @@
Thank you for taking the time to make a report. If you've seen harmful, abusive, or illegal content on our
communities, you can report this to us here. You can also use this page if you've received a message saying we've
- classified your content as harmful, abusive, or illegal and you wish to contest it.
+ classified your content as harmful, abusive, or illegal and you wish to contest it, or if you have a complaint
+ about our processes or our compliance with our duties.
@@ -45,7 +46,8 @@
<%= label_tag :reported_url, 'Where is this content?' %>
Enter a URL or link to where we can find this content on our network. You can use the Copy Link button under
- posts to get a direct link to a post.
+ posts to get a direct link to a post. Enter N/A if you are not complaining about specific content; add more
+ details below.
<%= text_field_tag :reported_url, nil, class: 'form-element', required: true %>
@@ -95,6 +97,11 @@
Tell us any additional information you have about this report. Is there any additional content we removed that
you would like to include? Provide detailed reasoning explaining why you disagree with our classification.
+
+ Tell us the details of your complaint. Do you believe we have failed to comply with our statutory duties, or
+ that we have failed to follow our policies and procedures? In what way? Provide any relevant examples or
+ evidence.
+
<%= text_area_tag :content, nil, class: 'form-element', required: true, rows: 10 %>
diff --git a/config/config/safety_center.yml b/config/config/safety_center.yml
index dffcad187..10ad2b2e1 100644
--- a/config/config/safety_center.yml
+++ b/config/config/safety_center.yml
@@ -45,16 +45,24 @@ outcomes:
copyright: *illegal_upheld
appeal:
content: our community team agreed with your appeal and have reversed the action taken in your case.
+ process:
+ content: our community team have reviewed your complaint and found information to substantiate it.
+
actionable:
name: Actionable
- description: The content is actionable but the reported classification is not correct. NOT applicable to appeals.
+ description: The content is actionable but the reported classification is not correct. NOT applicable to appeals or
+ process complaints.
user_facing:
illegal: &illegal_actionable
content: our community team agreed that the content you reported was actionable and have taken appropriate
action, but have changed your classification of the content for reporting purposes.
abusive: *illegal_actionable
copyright: *illegal_actionable
- appeal: ~
+ appeal:
+ content: ~
+ process:
+ content: ~
+
disputed:
name: Disputed
description: The reporter's classification is disputed; the content does not appear to be actionable; in the case of
@@ -69,6 +77,8 @@ outcomes:
appeal:
content: our community team have reviewed your appeal and have decided that the action taken in your case was
appropriate.
+ process:
+ content: our community team have reviewed your complaint but found no information to substantiate it.
report_types:
illegal:
@@ -87,6 +97,10 @@ report_types:
enabled: true
name: Classification Appeal
description: an appeal regarding how we've handled your content
+ process:
+ enabled: true
+ name: Complaint about our process
+ description: a complaint about our processes or compliance with our duties
# This list is sourced from Ofcom's list of the 17 types of priority illegal content, which is in turn sourced from
# Schedules 5-7 of the Online Safety Act.
diff --git a/db/schema.rb b/db/schema.rb
index 8752d5420..2278fb412 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.2].define(version: 2025_12_26_185531) do
+ActiveRecord::Schema[7.2].define(version: 2026_02_08_223211) do
create_table "abilities", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
t.bigint "community_id"
t.string "name"
From c2f8fb6512ac12d09a620b61e3fcb73a3e5c7374 Mon Sep 17 00:00:00 2001
From: ArtOfCode-
Date: Thu, 5 Mar 2026 10:46:50 +0000
Subject: [PATCH 02/21] Add broad structure for training
---
app/assets/stylesheets/_variables.scss | 1 +
app/assets/stylesheets/complaints.scss | 41 ++++++++++++++
app/controllers/complaints_controller.rb | 17 ++++++
app/views/complaints/training/home.html.erb | 52 ++++++++++++++++++
app/views/layouts/osa_training.html.erb | 60 +++++++++++++++++++++
config/routes.rb | 1 +
6 files changed, 172 insertions(+)
create mode 100644 app/views/complaints/training/home.html.erb
create mode 100644 app/views/layouts/osa_training.html.erb
diff --git a/app/assets/stylesheets/_variables.scss b/app/assets/stylesheets/_variables.scss
index 51afa04b5..d412b3ec7 100644
--- a/app/assets/stylesheets/_variables.scss
+++ b/app/assets/stylesheets/_variables.scss
@@ -18,6 +18,7 @@ $danger: #EB5959;
$success: #2ECC71;
$info: #58A09A;
$brand: #4B68FF;
+$brand-comp: #F05137;
$data: (
diff --git a/app/assets/stylesheets/complaints.scss b/app/assets/stylesheets/complaints.scss
index fdec7e561..8c679c338 100644
--- a/app/assets/stylesheets/complaints.scss
+++ b/app/assets/stylesheets/complaints.scss
@@ -60,3 +60,44 @@
.is-lead + h1.complaint-title {
margin-top: -1rem;
}
+
+.is-brand-comp {
+ color: $brand-comp;
+}
+
+.with-subtitle {
+ margin-bottom: 0;
+}
+
+.subtitle {
+ margin-top: 0;
+ font-weight: normal;
+ font-size: 1.2rem;
+}
+
+.modules {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
+ gap: 0.5rem;
+}
+
+.module-widget {
+ border: 1px solid $muted-graphic;
+ border-radius: 0.2rem;
+ padding: 0.5rem;
+ color: $key !important;
+
+ > h4 {
+ text-decoration: underline;
+ }
+
+ > p {
+ text-decoration: none;
+ }
+
+ &:hover {
+ background: $primary;
+ color: white !important;
+ text-decoration: none !important;
+ }
+}
diff --git a/app/controllers/complaints_controller.rb b/app/controllers/complaints_controller.rb
index 3d8af4507..953cd9353 100644
--- a/app/controllers/complaints_controller.rb
+++ b/app/controllers/complaints_controller.rb
@@ -3,6 +3,7 @@ class ComplaintsController < ApplicationController
before_action :access_check, only: [:show, :comment]
before_action :write_access_check, only: [:self_assign, :update_status, :change_content_type]
before_action :verify_staff, only: [:reports, :reporting]
+ before_action :training_access, only: [:training]
def index
render layout: 'without_sidebar'
@@ -202,6 +203,16 @@ def reporting
render layout: 'without_sidebar'
end
+ def training
+ pages = Dir.glob(Rails.root.join('app', 'views', 'complaints', 'training', '*.html.erb'))
+ .map { |page| File.basename(page, '.html.erb') }
+ if pages.include?(params[:page])
+ render "complaints/training/#{params[:page]}", layout: 'osa_training'
+ else
+ not_found!
+ end
+ end
+
private
def access_check
@@ -235,4 +246,10 @@ def set_complaint
@complaint
end
+
+ def training_access
+ unless user_signed_in? && (current_user.staff? || current_user.at_least_moderator?)
+ not_found!
+ end
+ end
end
diff --git a/app/views/complaints/training/home.html.erb b/app/views/complaints/training/home.html.erb
new file mode 100644
index 000000000..83d3d3bbb
--- /dev/null
+++ b/app/views/complaints/training/home.html.erb
@@ -0,0 +1,52 @@
+Online Safety Act
+Moderator Training
+
+
+ As part of our responsibilities under the Online Safety Act, we're obligated to provide training to all staff and
+ volunteers undertaking moderation duties.
+
+
+ Take your training here. Completing all the modules will record that you have completed the training, but you can
+ revisit these pages at any time if you need guidance when making moderation decisions.
+
+
+
+ <%= link_to osa_training_path('overview'), class: 'module-widget' do %>
+
Overview
+
+ An overview of the Online Safety Act, our duties, and your responsibilities as a volunteer moderator.
+
+ <% end %>
+ <%= link_to osa_training_path('illegal-content'), class: 'module-widget' do %>
+
Priority & Non-Priority Illegal Content
+
+ An explanation of the difference between the 17 types of priority illegal content, and other applicable types of
+ non-priority illegal content.
+
+ <% end %>
+ <%= link_to osa_training_path('overview'), class: 'module-widget' do %>
+
Definitions
+
+ Definitions of all the types of illegal content which apply to us.
+
+ <% end %>
+ <%= link_to osa_training_path('overview'), class: 'module-widget' do %>
+
Handling Illegal Content
+
+ Your responsibilities and the steps you need to take in response to identifying potentially illegal content.
+
+ <% end %>
+ <%= link_to osa_training_path('overview'), class: 'module-widget' do %>
+
Higher-Risk Content
+
+ Some types of content are more likely to occur in our communities than others. More detail on those here.
+
+ <% end %>
+ <%= link_to osa_training_path('overview'), class: 'module-widget' do %>
+
Conclusion
+
+ Thank you for taking the time to complete this training. Mark it as complete here and come back here if you need
+ to refer back to it.
+
+ <% end %>
+
diff --git a/app/views/layouts/osa_training.html.erb b/app/views/layouts/osa_training.html.erb
new file mode 100644
index 000000000..54cf9cb66
--- /dev/null
+++ b/app/views/layouts/osa_training.html.erb
@@ -0,0 +1,60 @@
+
+
+
+ <%= render 'layouts/head' %>
+
+
+<%= render 'layouts/header' %>
+
+
+
+
+
+ <%= render 'shared/notices' %>
+
+ <% if @first_visit_notice %>
+ <%= render 'notices/first_visit' %>
+ <% end %>
+
+ <%= yield %>
+
+
+
+
+
+
+
+<%= render 'layouts/footer' %>
+
+<%= render 'layouts/matomo' %>
+
+<% if Rails.env.production? %>
+
+<% end %>
+
+
diff --git a/config/routes.rb b/config/routes.rb
index 05961caad..4719d354a 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -388,6 +388,7 @@
post 'report/:token/content_type', to: 'complaints#change_content_type', as: :update_complaint_content_type
get 'reports', to: 'complaints#reports', as: :complaints
get 'reporting', to: 'complaints#reporting', as: :complaints_reporting
+ get 'training/:page', to: 'complaints#training', as: :osa_training
end
get '403', to: 'errors#forbidden'
From adac66e6e516dc3d1ec2cef97825bc3f109693d8 Mon Sep 17 00:00:00 2001
From: ArtOfCode-
Date: Thu, 12 Mar 2026 11:26:18 +0000
Subject: [PATCH 03/21] Overview
---
.../complaints/training/overview.html.erb | 64 +++++++++++++++++++
1 file changed, 64 insertions(+)
create mode 100644 app/views/complaints/training/overview.html.erb
diff --git a/app/views/complaints/training/overview.html.erb b/app/views/complaints/training/overview.html.erb
new file mode 100644
index 000000000..59aee28dc
--- /dev/null
+++ b/app/views/complaints/training/overview.html.erb
@@ -0,0 +1,64 @@
+Last updated 12 March 2026
+
+Online Safety Act
+Overview
+
+
+ The Online Safety Act 2023 (available here) is a law
+ established in the UK in 2023 with the aim of improving online safety, particularly with regard to children, but with
+ wide-ranging effects for all online services. All services with UK users are required to comply with the Act. There
+ are ongoing cases which will define whether this is enforceable on non-UK entities in practice, but because Codidact
+ is a UK-based entity, we are clearly within scope and required to comply.
+
+
+ The Act is enforced by the UK's communications regulator, Ofcom, which also sets out the Register of Risks and Codes
+ of Practice on which our approach is based.
+
+
+Types of service
+
+ The Act defines two types of service: search services and user-to-user services. User-to-user services are those where
+ users may interact with one another; this is where we fall. There are different requirements imposed on each kind of
+ service, which for user-to-user services primarily focus on preventing and removing harmful content, and protecting
+ users from related harms.
+
+
+Our responsibilities
+
+ Responsibility for compliance with the requirements of the Act obviously falls on us (meaning the Codidact Foundation
+ as the organisation running the platform). The Foundation designates one of the Board of Directors as a named
+ individual with ultimate responsibility for compliance with the Act, which is currently
+ ArtOfCode.
+
+
+ One of our responsibilities is to ensure that our volunteer moderators (that's you) have an awareness of the Act and
+ are provided with appropriate training in order to equip them to handle any harmful content which may appear on the
+ platform.
+
+
+Your responsibilities
+
+ As a volunteer moderator, your job is to guide, curate, and set the tone for your community. Part of that job is
+ protecting the community from any unwanted content. The majority of the time, that might take the form of off-topic
+ posts, arguments between users, or handling flags for your attention. Unfortunately, it may also take the form of
+ harmful or illegal content covered by the Act, and one of your responsibilities is to ensure this is dealt with and
+ escalated appropriately.
+
+
+ To be clear: we're not expecting you to handle harmful or illegal content alone. Our ask of you is
+ simple: if you identify something that you think would be covered by the Act, please:
+
+
+ - Delete it to remove it from public view
+ -
+ Escalate it to the Community Team straight away: either ping us in Discord, or flag it yourself and escalate your
+ own flag for us to review.
+
+
+
+
+ <%= link_to osa_training_path('illegal-content') do %>
+ Next
+ Priority & Non-Priority Illegal Content »
+ <% end %>
+
From b3fc44af9f5f93226def698e24757d8d3d0505e0 Mon Sep 17 00:00:00 2001
From: ArtOfCode-
Date: Thu, 12 Mar 2026 20:29:45 +0000
Subject: [PATCH 04/21] Definitions
---
.../complaints/training/definitions.html.erb | 274 ++++++++++++++++++
app/views/complaints/training/home.html.erb | 8 +-
.../training/illegal-content.html.erb | 19 ++
.../complaints/training/overview.html.erb | 2 +-
4 files changed, 298 insertions(+), 5 deletions(-)
create mode 100644 app/views/complaints/training/definitions.html.erb
create mode 100644 app/views/complaints/training/illegal-content.html.erb
diff --git a/app/views/complaints/training/definitions.html.erb b/app/views/complaints/training/definitions.html.erb
new file mode 100644
index 000000000..af53f7170
--- /dev/null
+++ b/app/views/complaints/training/definitions.html.erb
@@ -0,0 +1,274 @@
+Last updated 12 March 2026
+
+Online Safety Act
+Definitions
+
+
+ It is important to understand what each type of content is exactly, so they are defined here. These are definitions
+ which we have written: the Act defines each type of content in terms of related criminal offences, which is more
+ complex than is necessary to deal with the content, so these descriptions are intended to provide a simple overview.
+
+
+Priority Illegal Content
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Non-Priority Illegal Content
+
+
+Service-specific illegal content risks
+
+ Our risk assessment has also identified some additional types of illegal content based on the platform's risk profile.
+
+
+
+
+
+ <%= link_to osa_training_path('handling') do %>
+ Next
+ Handling Illegal Content »
+ <% end %>
+
diff --git a/app/views/complaints/training/home.html.erb b/app/views/complaints/training/home.html.erb
index 83d3d3bbb..0ecadd1af 100644
--- a/app/views/complaints/training/home.html.erb
+++ b/app/views/complaints/training/home.html.erb
@@ -24,25 +24,25 @@
non-priority illegal content.
<% end %>
- <%= link_to osa_training_path('overview'), class: 'module-widget' do %>
+ <%= link_to osa_training_path('definitions'), class: 'module-widget' do %>
Definitions
Definitions of all the types of illegal content which apply to us.
<% end %>
- <%= link_to osa_training_path('overview'), class: 'module-widget' do %>
+ <%= link_to osa_training_path('handling'), class: 'module-widget' do %>
Handling Illegal Content
Your responsibilities and the steps you need to take in response to identifying potentially illegal content.
<% end %>
- <%= link_to osa_training_path('overview'), class: 'module-widget' do %>
+ <%= link_to osa_training_path('higher-risk'), class: 'module-widget' do %>
Higher-Risk Content
Some types of content are more likely to occur in our communities than others. More detail on those here.
<% end %>
- <%= link_to osa_training_path('overview'), class: 'module-widget' do %>
+ <%= link_to osa_training_path('conclusion'), class: 'module-widget' do %>
Conclusion
Thank you for taking the time to complete this training. Mark it as complete here and come back here if you need
diff --git a/app/views/complaints/training/illegal-content.html.erb b/app/views/complaints/training/illegal-content.html.erb
new file mode 100644
index 000000000..d55411938
--- /dev/null
+++ b/app/views/complaints/training/illegal-content.html.erb
@@ -0,0 +1,19 @@
+
Last updated 12 March 2026
+
+Online Safety Act
+Priority & Non-Priority Illegal Content
+
+
+ The Act sets out 17 types of priority illegal content, and a number of types of non-priority illegal content. We have
+ carried out a risk assessment for all types of priority illegal content, and applicable types of non-priority illegal
+ content, which details the likelihood and impact of each type of content on our platform specifically.
+
+
+
+
+
+ <%= link_to osa_training_path('definitions') do %>
+ Next
+ Definitions »
+ <% end %>
+
diff --git a/app/views/complaints/training/overview.html.erb b/app/views/complaints/training/overview.html.erb
index 59aee28dc..358c61976 100644
--- a/app/views/complaints/training/overview.html.erb
+++ b/app/views/complaints/training/overview.html.erb
@@ -1,4 +1,4 @@
-Last updated 12 March 2026
+Last updated 12 March 2026
Online Safety Act
Overview
From 829f72a9308a0f6e64cd0f4eea8c84c1780c18b8 Mon Sep 17 00:00:00 2001
From: ArtOfCode-
Date: Sun, 22 Mar 2026 15:25:54 +0000
Subject: [PATCH 05/21] Finalise training
---
app/controllers/complaints_controller.rb | 11 +-
app/views/complaints/index.html.erb | 14 +++
.../complaints/training/conclusion.html.erb | 17 +++
.../complaints/training/handling.html.erb | 52 +++++++++
.../complaints/training/higher-risk.html.erb | 100 ++++++++++++++++++
app/views/complaints/training/home.html.erb | 4 +-
.../training/illegal-content.html.erb | 61 ++++++++++-
config/routes.rb | 1 +
...0260322151439_add_osa_training_to_users.rb | 5 +
db/schema.rb | 4 +-
10 files changed, 264 insertions(+), 5 deletions(-)
create mode 100644 app/views/complaints/training/conclusion.html.erb
create mode 100644 app/views/complaints/training/handling.html.erb
create mode 100644 app/views/complaints/training/higher-risk.html.erb
create mode 100644 db/migrate/20260322151439_add_osa_training_to_users.rb
diff --git a/app/controllers/complaints_controller.rb b/app/controllers/complaints_controller.rb
index 953cd9353..700068d73 100644
--- a/app/controllers/complaints_controller.rb
+++ b/app/controllers/complaints_controller.rb
@@ -3,7 +3,7 @@ class ComplaintsController < ApplicationController
before_action :access_check, only: [:show, :comment]
before_action :write_access_check, only: [:self_assign, :update_status, :change_content_type]
before_action :verify_staff, only: [:reports, :reporting]
- before_action :training_access, only: [:training]
+ before_action :training_access, only: [:training, :training_complete]
def index
render layout: 'without_sidebar'
@@ -213,6 +213,15 @@ def training
end
end
+ def training_complete
+ if current_user.update(osa_training: DateTime.now)
+ flash[:success] = 'Thank you. Your training has been marked as complete.'
+ else
+ flash[:danger] = 'Something went wrong. Please tell a staff member about this.'
+ end
+ redirect_to safety_center_path
+ end
+
private
def access_check
diff --git a/app/views/complaints/index.html.erb b/app/views/complaints/index.html.erb
index 84243a825..b0476f101 100644
--- a/app/views/complaints/index.html.erb
+++ b/app/views/complaints/index.html.erb
@@ -25,6 +25,20 @@
+
<% if user_signed_in? && current_user.staff? %>
diff --git a/app/views/complaints/training/conclusion.html.erb b/app/views/complaints/training/conclusion.html.erb
new file mode 100644
index 000000000..b3ebbb9b4
--- /dev/null
+++ b/app/views/complaints/training/conclusion.html.erb
@@ -0,0 +1,17 @@
+Last updated 22 March 2026
+
+Online Safety Act
+Conclusion
+
+
+ Thank you for taking the time to go through this training. We appreciate that these are often difficult and sensitive
+ topics, but your understanding and assistance help us to create safe communities for all our users.
+
+
+ Please click the button below to mark your training complete. You can come back to these pages at any time from the
+ Safety Center if you need a reminder. If you need any support, please contact the Community Team.
+
+
+<%= form_tag osa_training_complete_path, method: :post do %>
+ <%= submit_tag 'Mark as complete', class: 'button is-filled is-primary' %>
+<% end %>
diff --git a/app/views/complaints/training/handling.html.erb b/app/views/complaints/training/handling.html.erb
new file mode 100644
index 000000000..423cc4d0e
--- /dev/null
+++ b/app/views/complaints/training/handling.html.erb
@@ -0,0 +1,52 @@
+Last updated 22 March 2026
+
+Online Safety Act
+Handling Illegal Content
+
+
+ It's important that illegal content is handled consistently in order to ensure that we are able to meet the statutory
+ requirements of us, to protect our users, and to enable us to be transparent with our communities. The way in which
+ illegal content is handled is set out in our Online Safety & Illegal Content Policy & Procedure, OP02 — you can
+ find a copy of this here.
+
+
+As a volunteer moderator
+
+ The two ways in which you are likely to come across illegal content as a volunteer moderator are either through
+ encountering it as you're browsing, or through flags from community members. Either way, our ask of you is the same.
+ If you believe the content may be illegal:
+
+
+ -
+ Delete it, using the "delete" button for the post or comment, to remove it from public view. Deletion doesn't
+ completely remove a post and leaves it visible for some community members, so in addition:
+
+ -
+ Escalate it to the Community Team. If a community member has flagged the post, you can escalate the flag to us; this
+ will send an email notifying us. If not, you can ping any of us in Discord, or flag the post yourself and escalate
+ your own flag to us.
+
+
+
+What the Community Team will do
+
+ Making a judgement on whether content constitutes illegal content is surprisingly difficult. Ofcom publishes guidance,
+ and we have a responsibility to make an initial decision and follow through on that; however, the final say can only
+ be given by the UK courts.
+
+If our decision is that the content is illegal, we will:
+
+ - Use a staff-only "delete for legal reasons" function to remove the post completely.
+ - File an internal report of the content for reporting purposes.
+ -
+ Notify the author that their content has been removed for this reason. This is a legal requirement. We will not
+ disclose who has reported or escalated the content report to us.
+
+
+
+
+ <%= link_to osa_training_path('higher-risk') do %>
+ Next
+ Higher-Risk Content »
+ <% end %>
+
\ No newline at end of file
diff --git a/app/views/complaints/training/higher-risk.html.erb b/app/views/complaints/training/higher-risk.html.erb
new file mode 100644
index 000000000..461e50c6d
--- /dev/null
+++ b/app/views/complaints/training/higher-risk.html.erb
@@ -0,0 +1,100 @@
+Last updated 22 March 2026
+
+Online Safety Act
+Higher-Risk Content
+
+
+ Every individual service has an individual risk profile based on its characteristics and demographics as different
+ functionalities and characteristics raise or lower the likelihood of different types of content. This means that we're
+ at higher risk of certain types of content than others; we identify and discuss these here.
+
+
+Priority illegal content
+Terrorism
+
+ While the on-platform impact of terrorism-related content is relatively low, we have a significant number of risk
+ factors that increase the likelihood of this type of content appearing on the platform. These include:
+
+
+ - Discussion forums (this is the category of service we fall into under the Act)
+ - Commenting on content
+ - Posting images or videos
+ - Searching user-generated content
+ - Fake or spam profiles
+ - Hyperlinking
+
+
+Hate
+
+ There is no shortage of examples of online platforms where user-to-user interactions have been plagued by hateful
+ content. There are many places where this has got better, but also plenty where it hasn't. Although we work hard to
+ foster a sense of community and establish respectful baselines, the risk of this is still present. We've assessed that
+ this is more likely to occur in comments or user profiles than in post content. As a volunteer moderator, your actions
+ can set the tone for your community, so you can make the difference in how likely this is to occur.
+
+
+Sexual exploitation of adults
+
+ While we haven't seen any content that we believe to be illegal content under this category, we have seen spam along
+ closely-related lines. Both here and on other platforms, it's not uncommon to see spam promoting "best escorts in X
+ city". There is a fine line between this being legal and illegal based on the consent of or impact on those involved,
+ but if there's any doubt it's safer to escalate for review. This can take the form of spam posts, but more commonly
+ occurs in spam user profiles, which can be longer-lived.
+
+
+Fraud & financial offences
+
+ This type of content likewise often occurs in spam, both as posts and as user profiles. The more obvious occurrences
+ may be common scams or offers for illegitimate financial gain, but this category also includes carrying out regulated
+ acts without being an authorized person or body. This could cover more seemingly innocuous actions, such as users
+ arranging loans or credit between themselves.
+
+
+Drugs & psychoactive substances
+
+ For this type of content—among others—it's important to remember that discussion of
+ drugs or psychoactive substances is not necessarily illegal. We also don't currently host any communities that would
+ be at higher risk of this type of content by the nature of their topic area. Relevant offences include
+ offering to supply drugs, psychoactive substances, or UK "controlled substances" (which may include medications which
+ are legal elsewhere in the world); the Act also includes attempting, encouraging, or assisting these offences, which
+ can be difficult to assess. As with everything, if in doubt please escalate it to us for review.
+
+
+Firearms, knives, and other weapons
+
+ The risks of this type of content are very similar to those of drugs and psychoactive substances, in that discussion
+ of the topic is not necessarily illegal, and its likelihood may be linked to communities covering related topics.
+ Our Outdoors community sometimes hosts content discussing tools such as knives which are weapons in other contexts;
+ these discussions are not necessarily illegal but additional care is required to assess when a line may have been
+ crossed. If in doubt, escalate!
+
+
+Non-priority illegal content
+Doxxing
+
+ Doxxing—the deliberate exposure of another user's private information—may fall under a variety of offences
+ depending on the exact circumstances. Unfortunately, this is something that most online social platforms have to deal
+ with at some point; we have had one or two such incidents in the past. Doxxing often occurs as part of a wider
+ campaign of offending against a specific individual known to the perpetrator, or as a result of a particularly heated
+ disagreement with another user. You can help to avoid the latter of those situations by actively moderating your
+ community, removing and redirecting disagreements before this becomes a risk, and setting a respectful tone for the
+ community.
+
+
+Copyright infringement
+
+ This is likewise an unfortunate reality of many online platforms. While professional research communities tend to be
+ excellent at citing sources and using material appropriately and legally, most of our communities are not at that
+ level. Users often fall into the trap of thinking "if it's on the Internet, it must be free for anyone to use". UK
+ "fair dealing" exceptions are stricter and narrower than US "fair use" doctrine, which presents another risk.
+ Generative AI is also another emerging risk: while we have a
+ fairly clear policy prohibiting sole AI use, this regularly gets
+ missed or ignored, and work into appropriately citing sources used in generative AI models is still very young.
+
+
+
+ <%= link_to osa_training_path('conclusion') do %>
+ Next
+ Conclusion »
+ <% end %>
+
\ No newline at end of file
diff --git a/app/views/complaints/training/home.html.erb b/app/views/complaints/training/home.html.erb
index 0ecadd1af..da90d0a71 100644
--- a/app/views/complaints/training/home.html.erb
+++ b/app/views/complaints/training/home.html.erb
@@ -6,8 +6,8 @@
volunteers undertaking moderation duties.
- Take your training here. Completing all the modules will record that you have completed the training, but you can
- revisit these pages at any time if you need guidance when making moderation decisions.
+ This training should take around an hour to read through all the content. Once you've finished all the modules you'll
+ be able to mark it as complete, but you'll be able to come back here any time if you need a reminder.
diff --git a/app/views/complaints/training/illegal-content.html.erb b/app/views/complaints/training/illegal-content.html.erb
index d55411938..d4951310f 100644
--- a/app/views/complaints/training/illegal-content.html.erb
+++ b/app/views/complaints/training/illegal-content.html.erb
@@ -1,4 +1,4 @@
-
Last updated 12 March 2026
+
Last updated 22 March 2026
Online Safety Act
Priority & Non-Priority Illegal Content
@@ -9,7 +9,66 @@
content, which details the likelihood and impact of each type of content on our platform specifically.
+
Priority Content
+
+ The Act sets out a number of types of priority illegal content. These are generally the most serious or prevalent
+ types of illegal content or activity, and the Act requires service providers to take proactive measures to protect
+ users. This includes risk-assessing, training, and transparency requirements, in addition to the obvious removal of
+ content.
+
+
+ Priority illegal content includes content or activity relating to[1]:
+
+
+ - child sexual abuse
+ - controlling or coercive behaviour
+ - extreme sexual violence
+ - extreme pornography
+ - fraud
+ - racially or religiously aggravated public order offences
+ - inciting violence
+ - illegal immigration and people smuggling
+ - promoting or facilitating suicide
+ - intimate image abuse
+ - selling illegal drugs or weapons
+ - sexual exploitation
+ - terrorism
+
+
+
Non-Priority Content
+
+ Also called "non-designated illegal content", this refers to illegal content which is not related to a priority
+ offence set out in the Act. This may cover any content which is illegal under UK law, and so this is a very
+ wide category. The Act specifically defines two new offences as non-priority offences so that they fall into this
+ category, but also imposes requirements on service providers to assess the risk of other types of non-designated
+ illegal content which are not specifically mentioned.
+
+
+ The two new non-priority offences are:
+
+
+ - epilepsy trolling
+ - cyberflashing
+
+
+ We have also identified risks relating to:
+
+
+ - copyright infringement
+ - doxxing
+
+
+ All of these types of content are defined more fully on the next page.
+
+
Footnotes
+
+ -
+
+ https://www.gov.uk/government/publications/online-safety-act-explainer/online-safety-act-explainer#types-of-content-the-act-tackles
+
+
+
<%= link_to osa_training_path('definitions') do %>
diff --git a/config/routes.rb b/config/routes.rb
index 4719d354a..2d8ad8ee2 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -389,6 +389,7 @@
get 'reports', to: 'complaints#reports', as: :complaints
get 'reporting', to: 'complaints#reporting', as: :complaints_reporting
get 'training/:page', to: 'complaints#training', as: :osa_training
+ post 'training/complete', to: 'complaints#training_complete', as: :osa_training_complete
end
get '403', to: 'errors#forbidden'
diff --git a/db/migrate/20260322151439_add_osa_training_to_users.rb b/db/migrate/20260322151439_add_osa_training_to_users.rb
new file mode 100644
index 000000000..ced7bfbd1
--- /dev/null
+++ b/db/migrate/20260322151439_add_osa_training_to_users.rb
@@ -0,0 +1,5 @@
+class AddOsaTrainingToUsers < ActiveRecord::Migration[7.2]
+ def change
+ add_column :users, :osa_training, :datetime
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index ac8ed1016..9aab26649 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.2].define(version: 2026_03_20_104406) do
+ActiveRecord::Schema[7.2].define(version: 2026_03_22_151439) do
create_table "abilities", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
t.bigint "community_id"
t.string "name"
@@ -115,6 +115,7 @@
t.index ["community_id"], name: "index_categories_on_community_id"
t.index ["default_filter_id"], name: "index_categories_on_default_filter_id"
t.index ["license_id"], name: "index_categories_on_license_id"
+ t.index ["min_view_trust_level"], name: "index_categories_on_min_view_trust_level"
t.index ["sequence"], name: "index_categories_on_sequence"
t.index ["tag_set_id"], name: "index_categories_on_tag_set_id"
end
@@ -802,6 +803,7 @@
t.datetime "deleted_at", precision: nil
t.bigint "deleted_by_id"
t.string "backup_2fa_code"
+ t.datetime "osa_training"
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
t.index ["deleted_by_id"], name: "index_users_on_deleted_by_id"
t.index ["email"], name: "index_users_on_email", unique: true
From f4d73cd520412758408f8365159399df9e301a5c Mon Sep 17 00:00:00 2001
From: ArtOfCode-
Date: Sun, 22 Mar 2026 15:29:08 +0000
Subject: [PATCH 06/21] Tests
---
.../controllers/complaints_controller_test.rb | 32 +++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/test/controllers/complaints_controller_test.rb b/test/controllers/complaints_controller_test.rb
index 249395b91..79ce44fca 100644
--- a/test/controllers/complaints_controller_test.rb
+++ b/test/controllers/complaints_controller_test.rb
@@ -346,6 +346,38 @@ class ComplaintsControllerTest < ActionDispatch::IntegrationTest
assert_response(:not_found)
end
+ test 'anon should not be able to access training' do
+ get osa_training_path('home')
+ assert_response(:not_found)
+ end
+
+ test 'basic user should not be able to access training' do
+ sign_in users(:basic_user)
+ get osa_training_path('home')
+ assert_response(:not_found)
+ end
+
+ test 'moderator should be able to access training' do
+ sign_in users(:moderator)
+ get osa_training_path('home')
+ assert_response(:success)
+ end
+
+ test 'staff should be able to access training' do
+ sign_in users(:staff)
+ get osa_training_path('home')
+ assert_response(:success)
+ end
+
+ test 'should be able to complete training' do
+ sign_in users(:staff)
+ post osa_training_complete_path
+ assert_response(:found)
+ assert_redirected_to safety_center_path
+ assert_equal 'Thank you. Your training has been marked as complete.', flash[:success]
+ assert_not_nil current_user.osa_training
+ end
+
private
def try_create_report(**params)
From 31fde7987fd9fe45c963869db08e12eee3449382 Mon Sep 17 00:00:00 2001
From: ArtOfCode-
Date: Sun, 22 Mar 2026 15:33:10 +0000
Subject: [PATCH 07/21] Doh
---
test/controllers/complaints_controller_test.rb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/controllers/complaints_controller_test.rb b/test/controllers/complaints_controller_test.rb
index 79ce44fca..6c663cc5d 100644
--- a/test/controllers/complaints_controller_test.rb
+++ b/test/controllers/complaints_controller_test.rb
@@ -375,7 +375,7 @@ class ComplaintsControllerTest < ActionDispatch::IntegrationTest
assert_response(:found)
assert_redirected_to safety_center_path
assert_equal 'Thank you. Your training has been marked as complete.', flash[:success]
- assert_not_nil current_user.osa_training
+ assert_not_nil users(:staff).osa_training
end
private
From e5155b71bd1d68194a450aab25b05b9ad6c2d26b Mon Sep 17 00:00:00 2001
From: ArtOfCode-
Date: Sat, 28 Mar 2026 17:03:02 +0000
Subject: [PATCH 08/21] Gate OSA training behind a site setting
---
app/controllers/complaints_controller.rb | 3 ++-
db/seeds/site_settings.yml | 10 ++++++++++
test/controllers/complaints_controller_test.rb | 7 +++++++
3 files changed, 19 insertions(+), 1 deletion(-)
diff --git a/app/controllers/complaints_controller.rb b/app/controllers/complaints_controller.rb
index 700068d73..395f38a08 100644
--- a/app/controllers/complaints_controller.rb
+++ b/app/controllers/complaints_controller.rb
@@ -257,7 +257,8 @@ def set_complaint
end
def training_access
- unless user_signed_in? && (current_user.staff? || current_user.at_least_moderator?)
+ osa_training_enabled = SiteSetting['OSATrainingEnabled']
+ unless user_signed_in? && (current_user.staff? || current_user.at_least_moderator?) && osa_training_enabled
not_found!
end
end
diff --git a/db/seeds/site_settings.yml b/db/seeds/site_settings.yml
index 3c98ad52f..b8d4343d3 100644
--- a/db/seeds/site_settings.yml
+++ b/db/seeds/site_settings.yml
@@ -862,3 +862,13 @@
description: >
Formatting types allowed in post titles.
By default, only code, keyboard, and italic are enabled.
+
+- name: OSATrainingEnabled
+ value: true
+ value_type: boolean
+ category: SafetyCenter
+ community_id: ~
+ description: >
+ Should Online Safety Act moderator training be available on this instance?
+
+
diff --git a/test/controllers/complaints_controller_test.rb b/test/controllers/complaints_controller_test.rb
index 6c663cc5d..d808f0d69 100644
--- a/test/controllers/complaints_controller_test.rb
+++ b/test/controllers/complaints_controller_test.rb
@@ -369,6 +369,13 @@ class ComplaintsControllerTest < ActionDispatch::IntegrationTest
assert_response(:success)
end
+ test 'training should not be accessible if disabled' do
+ SiteSetting['OSATrainingEnabled'] = false
+ sign_in users(:staff)
+ get osa_training_path('home')
+ assert_response(:not_found)
+ end
+
test 'should be able to complete training' do
sign_in users(:staff)
post osa_training_complete_path
From 983fff6bf7b1f981d3101e47565a8d7432f8f43c Mon Sep 17 00:00:00 2001
From: ArtOfCode-
Date: Sat, 28 Mar 2026 17:54:01 +0000
Subject: [PATCH 09/21] Make OSA director configurable
---
app/views/complaints/training/overview.html.erb | 2 +-
db/seeds/site_settings.yml | 8 +++++++-
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/app/views/complaints/training/overview.html.erb b/app/views/complaints/training/overview.html.erb
index 358c61976..17a6baa7d 100644
--- a/app/views/complaints/training/overview.html.erb
+++ b/app/views/complaints/training/overview.html.erb
@@ -28,7 +28,7 @@
Responsibility for compliance with the requirements of the Act obviously falls on us (meaning the Codidact Foundation
as the organisation running the platform). The Foundation designates one of the Board of Directors as a named
individual with ultimate responsibility for compliance with the Act, which is currently
- ArtOfCode.
+ <%= user_link(User.find(SiteSetting['OSADirector'])) %>.
One of our responsibilities is to ensure that our volunteer moderators (that's you) have an awareness of the Act and
diff --git a/db/seeds/site_settings.yml b/db/seeds/site_settings.yml
index b8d4343d3..0e989d74b 100644
--- a/db/seeds/site_settings.yml
+++ b/db/seeds/site_settings.yml
@@ -871,4 +871,10 @@
description: >
Should Online Safety Act moderator training be available on this instance?
-
+- name: OSADirector
+ value: 8045
+ value_type: integer
+ category: SafetyCenter
+ community_id: ~
+ description: >
+ The user ID of the Director with responsibility for OSA compliance.
From 382f69df66f210302904d2d6c7c157117ce7af2f Mon Sep 17 00:00:00 2001
From: ArtOfCode-
Date: Sat, 28 Mar 2026 17:56:24 +0000
Subject: [PATCH 10/21] Clarify county lines
---
app/views/complaints/training/definitions.html.erb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/views/complaints/training/definitions.html.erb b/app/views/complaints/training/definitions.html.erb
index af53f7170..4ac2f8dda 100644
--- a/app/views/complaints/training/definitions.html.erb
+++ b/app/views/complaints/training/definitions.html.erb
@@ -103,7 +103,7 @@
Human trafficking encompasses a wide range of harmful activities. It can involve modern slavery, and victims and
survivors include adults and children. It is estimated that there were 122,000 people living in modern slavery in
the UK in 2021. Notable forms of human trafficking where harm can manifest online include sexual exploitation and
- abuse, forced labour, and criminal exploitation such as county lines exportation of illegal drugs.
+ abuse, forced labour, and criminal exploitation such as "county lines" exportation of illegal drugs.
From 608cf65c69e2d45b9a1c0b4fe41a42827a1c77ef Mon Sep 17 00:00:00 2001
From: ArtOfCode-
Date: Sat, 28 Mar 2026 17:57:40 +0000
Subject: [PATCH 11/21] Clarify epilepsy trolling
---
app/views/complaints/training/definitions.html.erb | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/app/views/complaints/training/definitions.html.erb b/app/views/complaints/training/definitions.html.erb
index 4ac2f8dda..4ca657e34 100644
--- a/app/views/complaints/training/definitions.html.erb
+++ b/app/views/complaints/training/definitions.html.erb
@@ -114,7 +114,7 @@
‘unlawful immigration’ if they do an act which facilitates a breach or attempted breach of immigration law by an
individual who is not a national of the United Kingdom – and where they know or have reasonable cause for
believing this to be the case. It is usually encouraged by organised crime. Online aspects of unlawful immigration
- could include the sale of counterfeit travel documents such as passports, visas and identification papers, as well
+ could include the sale of counterfeit travel documents such as passports, visas, and identification papers, as well
as the sale of crossings.
@@ -192,7 +192,8 @@
Epilepsy trolling
Some individuals with epilepsy may have a physical reaction to online content; they may feel disorientated,
uncomfortable, or unwell after seeing certain images or patterns. The offence is sharing an image with the
- intention to cause harm to an individual with epilepsy.
+ intention to cause harm to an individual with epilepsy. This may include bright or flashing videos or images
+ intended to trigger a physical reaction.