File tree Expand file tree Collapse file tree 13 files changed +279
-9
lines changed
app/controllers/discourse_anon_usernames Expand file tree Collapse file tree 13 files changed +279
-9
lines changed Original file line number Diff line number Diff line change 22
33** Plugin Summary**
44
5- For more information, please see: ** url to meta topic**
5+ > [ !WARNING]
6+ > Currently only works with ` invite only ` setups.
7+
8+ This plugin provides functionality to generate anonymous usernames for users during sign-up.
9+
10+ - [x] Generates random usernames based on a predefined list of words(` random words list ` + random number).
11+ - [x] Option to restrict users to only use generated usernames (` only generated usernames ` setting).
12+
13+ > [ !IMPORTANT]
14+ > Ensure to have ` full_name_requirement ` setting as ` "required_at_signup" ` .
Original file line number Diff line number Diff line change 33module ::DiscourseAnonUsernames
44 class ExamplesController < ::ApplicationController
55 requires_plugin PLUGIN_NAME
6-
7- def index
8- render json : { hello : "world" }
9- end
106 end
117end
Original file line number Diff line number Diff line change 1+ import Component from " @glimmer/component" ;
2+ import { action } from " @ember/object" ;
3+ import { service } from " @ember/service" ;
4+ import DButton from " discourse/components/d-button" ;
5+
6+ const getFirstName = (fullName ) => fullName .split (" " )[0 ];
7+
8+ export default class RandomizerButton extends Component {
9+ @service siteSettings;
10+
11+ get randomWordsList () {
12+ return this .siteSettings .random_words_list .split (" |" ) || [];
13+ }
14+
15+ fetchRandomWord () {
16+ if (this .randomWordsList .length === 0 ) {
17+ return " " ;
18+ }
19+ return this .randomWordsList [
20+ Math .floor (Math .random () * this .randomWordsList .length )
21+ ];
22+ }
23+
24+ @action
25+ async generate () {
26+ const randomizeFrom = getFirstName (this .args .randomizeFrom );
27+ const randomWord = this .fetchRandomWord ();
28+
29+ this .args .onGenerate ({
30+ target: {
31+ value: randomizeFrom + randomWord + Math .floor (Math .random () * 1000 ),
32+ },
33+ });
34+ }
35+
36+ get canGenerate () {
37+ return (
38+ this .args .randomizeFrom &&
39+ this .args .randomizeFrom .length !== 0 &&
40+ getFirstName (this .args .randomizeFrom ).trim () !==
41+ this .args .randomizeFrom .trim ()
42+ );
43+ }
44+
45+ <template >
46+ <DButton
47+ @ icon =" rotate"
48+ class =" btn-primary randomizer-btn"
49+ @ action ={{this .generate }}
50+ disabled ={{if this . canGenerate false true }}
51+ />
52+ </template >
53+ }
Original file line number Diff line number Diff line change 1+ import { on } from " @ember/modifier" ;
2+ import InputTip from " discourse/components/input-tip" ;
3+ import valueEntered from " discourse/helpers/value-entered" ;
4+ import { apiInitializer } from " discourse/lib/api" ;
5+ import { i18n } from " discourse-i18n" ;
6+ import RandomizerButton from " ../components/randomizer-button" ;
7+
8+ export default apiInitializer ((api ) => {
9+ const siteSettings = api .container .lookup (" service:site-settings" );
10+
11+ api .renderInOutlet (
12+ " invite-username-input" ,
13+ <template >
14+ <div class =" input-with-randomizer-btn" >
15+ <input
16+ {{on " focusin" @ scrollInputIntoView}}
17+ {{on " input" @ setAccountUsername}}
18+ type =" text"
19+ value ={{@ accountUsername }}
20+ class ={{valueEntered @ accountUsername}}
21+ id =" new-account-username"
22+ name =" username"
23+ disabled ={{siteSettings.only_generated_usernames }}
24+ maxlength ={{@ maxUsernameLength }}
25+ autocomplete =" off"
26+ />
27+
28+ <RandomizerButton
29+ @ randomizeFrom ={{@ accountName }}
30+ @ onGenerate ={{@ setAccountUsername }}
31+ />
32+
33+ </div >
34+ {{#unless @ accountUsername }}
35+ <label class =" alt-placeholder" for =" new-account-username" >
36+ {{i18n " user.username.title" }}
37+ </label >
38+ {{/unless }}
39+ <InputTip @ validation ={{@ usernameValidation }} id =" username-validation" />
40+ </template >
41+ );
42+ });
Original file line number Diff line number Diff line change 1+ @use " lib/viewport" ;
2+
3+ // change ordering of username input in invite sign up
4+ .invites-show {
5+ .invite-form form {
6+ .input-group {
7+ & .email-input {
8+ order : 1 ;
9+ }
10+
11+ & .name-input.name-required {
12+ order : 2 ;
13+ }
14+
15+ & .username-input {
16+ order : 3 ;
17+ }
18+
19+ & .password-input {
20+ order : 4 ;
21+ }
22+ }
23+
24+ .invitation-cta {
25+ order : 5 ;
26+ }
27+ }
28+ }
29+
30+ .input-with-randomizer-btn {
31+ display : flex ;
32+
33+ .btn-primary {
34+ margin-left : 0.5rem ;
35+ height : 2.5rem ;
36+ }
37+ }
Original file line number Diff line number Diff line change 66 discourse_anon_usernames : " Discourse Anon Usernames"
77 js :
88 discourse_anon_usernames :
9- placeholder : placeholder
9+ errors :
10+ full_name_required :
11+ title : " Full name required"
12+ message : " Please enter your full name (first and last) to generate an anonymous username."
Original file line number Diff line number Diff line change 11en :
2+ discourse_anon_usernames :
3+ errors :
4+ username_invalid : " The chosen username appears to contain your last name. Please choose a different username."
Original file line number Diff line number Diff line change 11# frozen_string_literal: true
22
33DiscourseAnonUsernames ::Engine . routes . draw do
4- get "/examples" => "examples#index"
54 # define routes here
65end
76
Original file line number Diff line number Diff line change @@ -2,3 +2,10 @@ discourse_anon_usernames:
22 discourse_anon_usernames_enabled :
33 default : false
44 client : true
5+ only_generated_usernames :
6+ default : false
7+ client : true
8+ random_words_list :
9+ type : list
10+ default : " "
11+ client : true
Original file line number Diff line number Diff line change 11# frozen_string_literal: true
22
33# name: discourse-anon-usernames
4- # about: TODO
4+ # about: Generate anonymous usernames for users and blocks them from using their real names in usernames
55# meta_topic_id: TODO
66# version: 0.0.1
77# authors: Discourse
@@ -16,6 +16,17 @@ module ::DiscourseAnonUsernames
1616
1717require_relative "lib/discourse_anon_usernames/engine"
1818
19+ register_asset "stylesheets/anon-usernames.scss"
20+
1921after_initialize do
20- # Code which should run after Rails has finished booting
22+ register_modifier ( :username_validation ) do |errors , context |
23+ next if context . object . nil? || context . object . name . blank?
24+
25+ username = context . username
26+ last_name = context . object . name . split ( " " ) . last
27+
28+ is_leaking_last_name = username . downcase . include? ( last_name . downcase )
29+
30+ errors << I18n . t ( "discourse_anon_usernames.errors.username_invalid" ) if is_leaking_last_name
31+ end
2132end
You can’t perform that action at this time.
0 commit comments