decidim-cdtb 0.1.3 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -1
- data/CHANGELOG.md +13 -0
- data/Gemfile.lock +2 -2
- data/README.md +52 -11
- data/decidim-cdtb.gemspec +1 -1
- data/lib/decidim/cdtb/engine.rb +4 -0
- data/lib/decidim/cdtb/spam/user_spam_detector.rb +89 -0
- data/lib/decidim/cdtb/tasks.rb +2 -0
- data/lib/decidim/cdtb/users/remover.rb +127 -0
- data/lib/decidim/cdtb/version.rb +1 -1
- data/lib/decidim/cdtb.rb +7 -0
- data/lib/tasks/spam.rake +13 -0
- data/lib/tasks/users.rake +23 -0
- metadata +10 -7
- data/lib/tasks/cdtb.rake +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 241f6ab9b3dcf2a9c90d13e458459776a5cedf5d0beff5ffbe9114b0c0f687ba
|
4
|
+
data.tar.gz: 2d9a4ef96c973b47296869acf12083a4f5862fdab7e7135e6ad36e6674115edc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 37c3ba1e514d44cecf61948a84f89b16c01dd67f7993b972fda6f6b006997b0afaabf3dcacf312ff85a9ef142d6f1576dd822824c1d5e492bbc200a4bff3cc20
|
7
|
+
data.tar.gz: 1517ff9df9d457abab99ed385d3fb0c7d1e4a6bdf080a8c29ba5fe3a766d8024ae294f37bc8fe4c90142155914bba4bfd20cdf51108a590b33ba168d977cd27e
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,18 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.1.6] - 2024-04-11 (Malifetes ben fetes)
|
4
|
+
|
5
|
+
- Add remover users task
|
6
|
+
|
7
|
+
## [0.1.5] - 2024-02-08 (Pastissos voladors de colors)
|
8
|
+
|
9
|
+
- Fix homepage in rubygems.org
|
10
|
+
- Add organization id and name in spam csv
|
11
|
+
|
12
|
+
## [0.1.4] - 2024-01-30 (Peus grans com gegants)
|
13
|
+
|
14
|
+
- Add users spam detector task
|
15
|
+
|
3
16
|
## [0.1.3] - 2023-06-23 (Tan iguals com especials)
|
4
17
|
|
5
18
|
- Validate migrations task
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -36,17 +36,6 @@ bin/rake cdtb:org_by_host_like[vila]
|
|
36
36
|
host: localhost, time_zone: Madrid, locales: ca + [ca, es, oc], available authorizations: [postal_letter, members_picker_authorization_handler]
|
37
37
|
```
|
38
38
|
|
39
|
-
### Fix nicknames
|
40
|
-
|
41
|
-
In a previous version than Decidim v0.25 a validation to the `Decidim::User.nickname` was added with a migration to fix existing nicknames. But the migration was only taking into acocunt managed (impersonated) users.
|
42
|
-
|
43
|
-
This task iterates (with `find_each`) over all non managed users and nicknamizes the nickname.
|
44
|
-
|
45
|
-
To execute the task run:
|
46
|
-
|
47
|
-
```
|
48
|
-
bin/rake cdtb:fix_nicknames
|
49
|
-
```
|
50
39
|
|
51
40
|
### Anonymize production dump
|
52
41
|
|
@@ -76,6 +65,58 @@ To migrate from S3 to local storage, the identified steps will be:
|
|
76
65
|
`bin/rake cache:clear`
|
77
66
|
5. Restart the Rails server
|
78
67
|
|
68
|
+
### Detect spam
|
69
|
+
|
70
|
+
To detect spam in Decidim.
|
71
|
+
|
72
|
+
#### Detect spam users
|
73
|
+
Detects users susceptible of being spammers. It can run on all organizations or be scoped to a single organization by passing the organization ID as the rake task parameter.
|
74
|
+
|
75
|
+
This rake task export a .csv with a list of all the searched users. A column indicates if each user is suspicious of being a spammer or not.
|
76
|
+
The columns in the CSV are: "ID, "Is suspicious?", "Name", "Email", "Nickname", "Personal URL", "About"
|
77
|
+
|
78
|
+
Examples:
|
79
|
+
`bin/rake cdtb:spam:users[org_id]` --> find users in organization with an id.
|
80
|
+
`bin/rake cdtb:spam:users` --> find all users in all organizations.
|
81
|
+
|
82
|
+
To set custom words in the rake, you can override it with an initalizer:
|
83
|
+
|
84
|
+
```
|
85
|
+
Decidim::Cdtb.configure do |config|
|
86
|
+
config.spam_words = ENV["CDTB_SPAM_WORDS"]&.split(",")
|
87
|
+
end
|
88
|
+
```
|
89
|
+
|
90
|
+
### Users
|
91
|
+
|
92
|
+
Tasks related with users.
|
93
|
+
|
94
|
+
### Fix nicknames
|
95
|
+
|
96
|
+
In a previous version than Decidim v0.25 a validation to the `Decidim::User.nickname` was added with a migration to fix existing nicknames. But the migration was only taking into account managed (impersonated) users.
|
97
|
+
|
98
|
+
This task iterates (with `find_each`) over all non managed users and nicknamizes the nickname.
|
99
|
+
|
100
|
+
To execute the task run:
|
101
|
+
|
102
|
+
```
|
103
|
+
bin/rake cdtb:users:fix_nicknames
|
104
|
+
```
|
105
|
+
|
106
|
+
#### Remove users
|
107
|
+
|
108
|
+
You can delete users through a CSV with the user ID. The purpose is to be able to eliminate potentially spammy users.
|
109
|
+
|
110
|
+
This task reports and hide the user's comments, blocks the user, and finally deletes the user.
|
111
|
+
|
112
|
+
The CSV will have a header and one column with the user ID.
|
113
|
+
|
114
|
+
To execute the task run:
|
115
|
+
|
116
|
+
```
|
117
|
+
bundle exec rake cdtb:users:remove[spam_users.csv]
|
118
|
+
```
|
119
|
+
|
79
120
|
### Upgrades:
|
80
121
|
|
81
122
|
#### Upgrade modules
|
data/decidim-cdtb.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
10
10
|
|
11
11
|
spec.summary = "CodiTramuntana's Decidim Toolbelt (cdtb)."
|
12
12
|
spec.description = "A gem to help managing Decidim applications."
|
13
|
-
spec.homepage = "
|
13
|
+
spec.homepage = "https://github.com/CodiTramuntana/decidim-module-cdtb"
|
14
14
|
spec.license = "MIT"
|
15
15
|
spec.required_ruby_version = ">= 2.7.5"
|
16
16
|
|
data/lib/decidim/cdtb/engine.rb
CHANGED
@@ -10,6 +10,10 @@ module Decidim
|
|
10
10
|
# Workaround for https://stackoverflow.com/questions/72970170/upgrading-to-rails-6-1-6-1-causes-psychdisallowedclass-tried-to-load-unspecif
|
11
11
|
Rails.application.config.active_record.use_yaml_unsafe_load = true
|
12
12
|
end
|
13
|
+
|
14
|
+
config.after_initialize do
|
15
|
+
Decidim::Cdtb.config.spam_regexp = Regexp.union(Decidim::Cdtb.config.spam_words)
|
16
|
+
end
|
13
17
|
end
|
14
18
|
end
|
15
19
|
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "csv"
|
4
|
+
|
5
|
+
module Decidim
|
6
|
+
module Cdtb
|
7
|
+
module Spam
|
8
|
+
# Detect spam behavior in users
|
9
|
+
#
|
10
|
+
class UserSpamDetector < ::Decidim::Cdtb::Task
|
11
|
+
# rubocop:disable Style/RedundantRegexpEscape
|
12
|
+
URL_REGEX = %r{(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|
|
13
|
+
www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|
|
14
|
+
(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.[a-zA-Z0-9]+\.[^\s]{2,})}.freeze
|
15
|
+
# rubocop:enable Style/RedundantRegexpEscape
|
16
|
+
|
17
|
+
def initialize(organization = nil)
|
18
|
+
@organization = organization
|
19
|
+
progress_bar = { title: "Decidim::User" }
|
20
|
+
super("SPAM DETECTOR", progress_bar: progress_bar)
|
21
|
+
end
|
22
|
+
|
23
|
+
def prepare_execution(_ctx)
|
24
|
+
@users = if @organization.present?
|
25
|
+
Decidim::User.where(organization: @organization)
|
26
|
+
else
|
27
|
+
Decidim::User.all
|
28
|
+
end
|
29
|
+
|
30
|
+
@num_users = @users.count
|
31
|
+
log_task_info("Checking #{@num_users} users...")
|
32
|
+
end
|
33
|
+
|
34
|
+
def total_items
|
35
|
+
@num_users
|
36
|
+
end
|
37
|
+
|
38
|
+
# rubocop:disable Metrics/AbcSize
|
39
|
+
def do_execution(context)
|
40
|
+
progress_bar = context[:progress_bar]
|
41
|
+
CSV.open("spam_users.csv", "w") do |csv|
|
42
|
+
csv_headers = ["ID", "Is suspicious?", "Name", "Email", "Nickname", "Personal URL", "About",
|
43
|
+
"Organization ID", "Organization Name"]
|
44
|
+
csv << csv_headers
|
45
|
+
|
46
|
+
@users.find_each do |user|
|
47
|
+
suspicious = "NO"
|
48
|
+
|
49
|
+
if spam_user?(user)
|
50
|
+
suspicious = "YES"
|
51
|
+
@num_applied+= 1
|
52
|
+
end
|
53
|
+
|
54
|
+
csv << [user.id, suspicious, user.name, user.email, user.nickname, user.personal_url, user.about,
|
55
|
+
user.organization.id, user.organization.name]
|
56
|
+
|
57
|
+
progress_bar.increment
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
# rubocop:enable Metrics/AbcSize
|
62
|
+
|
63
|
+
def end_execution(_ctx)
|
64
|
+
if @num_applied.positive?
|
65
|
+
log_task_step("#{@num_applied} suspicious users")
|
66
|
+
log_task_step("Suspicious users list exported to spam_users.csv")
|
67
|
+
else
|
68
|
+
log_task_step("There are not suspicious users!!")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def spam_user?(user)
|
73
|
+
has_spam_word?(user) || has_spam_url?(user)
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def has_spam_word?(user)
|
79
|
+
[user.name, user.about, user.nickname,
|
80
|
+
user.personal_url, user.about].compact.join("||").match?(Decidim::Cdtb.config.spam_regexp)
|
81
|
+
end
|
82
|
+
|
83
|
+
def has_spam_url?(user)
|
84
|
+
!!(user&.about =~ URL_REGEX || user.name =~ URL_REGEX)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
data/lib/decidim/cdtb/tasks.rb
CHANGED
@@ -3,7 +3,9 @@
|
|
3
3
|
require "decidim/cdtb/tasks_utils"
|
4
4
|
require "decidim/cdtb/task"
|
5
5
|
require "decidim/cdtb/fixes/nickname_fixer"
|
6
|
+
require "decidim/cdtb/users/remover"
|
6
7
|
require "decidim/cdtb/multitenants/org_by_host_like"
|
8
|
+
require "decidim/cdtb/spam/user_spam_detector"
|
7
9
|
require "decidim/cdtb/storage/local_sharding"
|
8
10
|
require "decidim/cdtb/storage/set_local_on_blobs"
|
9
11
|
require "decidim/cdtb/upgrades/validate_migrations_task"
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Cdtb
|
5
|
+
module Users
|
6
|
+
# Remove Decidim::User's
|
7
|
+
#
|
8
|
+
class Remover < ::Decidim::Cdtb::Task
|
9
|
+
def initialize(csv_path)
|
10
|
+
@csv_path = csv_path
|
11
|
+
progress_bar = { title: "Decidim::User" }
|
12
|
+
super("USER REMOVER", progress_bar: progress_bar)
|
13
|
+
end
|
14
|
+
|
15
|
+
def prepare_execution(_ctx); end
|
16
|
+
|
17
|
+
def total_items
|
18
|
+
File.open(@csv_path).readlines.size - 1
|
19
|
+
end
|
20
|
+
|
21
|
+
def do_execution(context)
|
22
|
+
progress_bar = context[:progress_bar]
|
23
|
+
|
24
|
+
CSV.foreach(@csv_path, headers: true, col_sep: ",") do |row|
|
25
|
+
user = Decidim::User.find_by(id: row[0])
|
26
|
+
next unless user.present?
|
27
|
+
|
28
|
+
reporter_user = Decidim::User.find_by(email: "support@coditramuntana.com",
|
29
|
+
organization: user.organization)
|
30
|
+
comments = Decidim::Comments::Comment.where(decidim_author_id: user.id)
|
31
|
+
manage_comments(comments, reporter_user) unless comments.empty?
|
32
|
+
destroy_user(user) if block_user(user, reporter_user)
|
33
|
+
progress_bar.increment
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def end_execution(_ctx)
|
38
|
+
log_task_step("#{@num_applied} users removed")
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def manage_comments(comments, reporter_user)
|
44
|
+
comments.find_each do |comment|
|
45
|
+
report_comment(comment, reporter_user)
|
46
|
+
hide_comment(comment, reporter_user)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def block_user(user, reporter_user)
|
51
|
+
params = {
|
52
|
+
user_id: user.id,
|
53
|
+
justification: "Confirmed spam suspicious"
|
54
|
+
}
|
55
|
+
|
56
|
+
form = Decidim::Admin::BlockUserForm.from_params(params).with_context(
|
57
|
+
{
|
58
|
+
current_organization: user.organization,
|
59
|
+
current_user: reporter_user
|
60
|
+
}
|
61
|
+
)
|
62
|
+
|
63
|
+
Decidim::Admin::BlockUser.call(form) do
|
64
|
+
on(:ok) do
|
65
|
+
puts "OK: User #{user.id} blocked"
|
66
|
+
return true
|
67
|
+
end
|
68
|
+
|
69
|
+
on(:invalid) do
|
70
|
+
puts "ERROR: User #{user.id} not blocked"
|
71
|
+
return false
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def destroy_user(user)
|
77
|
+
params = {
|
78
|
+
delete_reason: "Confirmed spam suspicious"
|
79
|
+
}
|
80
|
+
|
81
|
+
form = Decidim::DeleteAccountForm.from_params(params)
|
82
|
+
|
83
|
+
Decidim::DestroyAccount.call(user, form) do
|
84
|
+
on(:ok) do
|
85
|
+
puts "OK: User #{user.id} removed"
|
86
|
+
end
|
87
|
+
|
88
|
+
on(:invalid) do
|
89
|
+
puts "ERROR: User #{user.id} not removed"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def report_comment(comment, reporter_user)
|
95
|
+
params = {
|
96
|
+
reason: "spam",
|
97
|
+
details: "Spam message"
|
98
|
+
}
|
99
|
+
|
100
|
+
form = Decidim::ReportForm.from_params(params)
|
101
|
+
|
102
|
+
CreateReport.call(form, comment, reporter_user) do
|
103
|
+
on(:ok) do
|
104
|
+
puts "OK: Comment #{comment.id} of User #{user.id} reported"
|
105
|
+
end
|
106
|
+
|
107
|
+
on(:invalid) do
|
108
|
+
puts "ERROR: Comment #{comment.id} of User #{user.id} not reported"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def hide_comment(comment, reporter_user)
|
114
|
+
Admin::HideResource.call(comment, reporter_user) do
|
115
|
+
on(:ok) do
|
116
|
+
puts "OK: Comment #{comment.id} of User #{user.id} hided"
|
117
|
+
end
|
118
|
+
|
119
|
+
on(:invalid) do
|
120
|
+
puts "ERROR: Comment #{comment.id} of User #{user.id} not hided"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
data/lib/decidim/cdtb/version.rb
CHANGED
data/lib/decidim/cdtb.rb
CHANGED
@@ -5,7 +5,14 @@ require_relative "cdtb/engine"
|
|
5
5
|
require_relative "cdtb/tasks"
|
6
6
|
|
7
7
|
module Decidim
|
8
|
+
# Cdtb configuration
|
8
9
|
module Cdtb
|
10
|
+
include ActiveSupport::Configurable
|
11
|
+
|
9
12
|
class Error < StandardError; end
|
13
|
+
|
14
|
+
config_accessor :spam_words do
|
15
|
+
%w[viagra sex game free crypto crack xxx luck girls vip download]
|
16
|
+
end
|
10
17
|
end
|
11
18
|
end
|
data/lib/tasks/spam.rake
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
namespace :cdtb do
|
4
|
+
namespace :spam do
|
5
|
+
desc "Show a list with users suspected of spam"
|
6
|
+
task :users, %i[org_id] => :environment do |_task, args|
|
7
|
+
organization = args.org_id.present? ? Decidim::Organization.find(args.org_id) : nil
|
8
|
+
|
9
|
+
detector = ::Decidim::Cdtb::Spam::UserSpamDetector.new(organization)
|
10
|
+
detector.execute!
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "decidim/cdtb/tasks"
|
4
|
+
|
5
|
+
namespace :cdtb do
|
6
|
+
namespace :users do
|
7
|
+
desc <<~EODESC
|
8
|
+
Fix Decidim::User's nicknames.
|
9
|
+
EODESC
|
10
|
+
task fix_nicknames: [:environment] do
|
11
|
+
fixer= ::Decidim::Cdtb::Fixes::NicknameFixer.new
|
12
|
+
fixer.execute!
|
13
|
+
end
|
14
|
+
|
15
|
+
desc <<~EODESC
|
16
|
+
Remove Decidim::User's by IDs in a CSV.
|
17
|
+
EODESC
|
18
|
+
task :remove, %i[csv_file] => [:environment] do |_taks, args|
|
19
|
+
service = ::Decidim::Cdtb::Users::Remover.new(args.csv_file)
|
20
|
+
service.execute!
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: decidim-cdtb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Oliver Valls
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-04-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: decidim
|
@@ -105,6 +105,7 @@ files:
|
|
105
105
|
- lib/decidim/cdtb/engine.rb
|
106
106
|
- lib/decidim/cdtb/fixes/nickname_fixer.rb
|
107
107
|
- lib/decidim/cdtb/multitenants/org_by_host_like.rb
|
108
|
+
- lib/decidim/cdtb/spam/user_spam_detector.rb
|
108
109
|
- lib/decidim/cdtb/storage/local_sharding.rb
|
109
110
|
- lib/decidim/cdtb/storage/set_local_on_blobs.rb
|
110
111
|
- lib/decidim/cdtb/task.rb
|
@@ -113,23 +114,25 @@ files:
|
|
113
114
|
- lib/decidim/cdtb/upgrades/install_gem_migrations_step.rb
|
114
115
|
- lib/decidim/cdtb/upgrades/upgrade_modules_task.rb
|
115
116
|
- lib/decidim/cdtb/upgrades/validate_migrations_task.rb
|
117
|
+
- lib/decidim/cdtb/users/remover.rb
|
116
118
|
- lib/decidim/cdtb/version.rb
|
117
119
|
- lib/generators/cdtb/USAGE
|
118
120
|
- lib/generators/cdtb/templates/validate_migrations.yml
|
119
121
|
- lib/generators/cdtb/validate_migrations_ci_generator.rb
|
120
122
|
- lib/tasks/anonymize.rake
|
121
|
-
- lib/tasks/cdtb.rake
|
122
123
|
- lib/tasks/multitenants.rake
|
124
|
+
- lib/tasks/spam.rake
|
123
125
|
- lib/tasks/storage.rake
|
124
126
|
- lib/tasks/upgrade.rake
|
127
|
+
- lib/tasks/users.rake
|
125
128
|
- sig/decidim/cdtb.rbs
|
126
|
-
homepage:
|
129
|
+
homepage: https://github.com/CodiTramuntana/decidim-module-cdtb
|
127
130
|
licenses:
|
128
131
|
- MIT
|
129
132
|
metadata:
|
130
|
-
homepage_uri:
|
131
|
-
source_code_uri:
|
132
|
-
changelog_uri:
|
133
|
+
homepage_uri: https://github.com/CodiTramuntana/decidim-module-cdtb
|
134
|
+
source_code_uri: https://github.com/CodiTramuntana/decidim-module-cdtb
|
135
|
+
changelog_uri: https://github.com/CodiTramuntana/decidim-module-cdtb/CHANGELOG.md
|
133
136
|
post_install_message:
|
134
137
|
rdoc_options: []
|
135
138
|
require_paths:
|
data/lib/tasks/cdtb.rake
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "decidim/cdtb/tasks"
|
4
|
-
|
5
|
-
namespace :cdtb do
|
6
|
-
desc <<~EODESC
|
7
|
-
Fix Decidim::User's nicknames.
|
8
|
-
EODESC
|
9
|
-
task fix_nicknames: [:environment] do
|
10
|
-
fixer= ::Decidim::Cdtb::Fixes::NicknameFixer.new
|
11
|
-
fixer.execute!
|
12
|
-
end
|
13
|
-
end
|