├── .gitignore ├── LICENSE.md ├── README.md ├── user_importer.rake └── users.csv.example /.gitignore: -------------------------------------------------------------------------------- 1 | users.csv 2 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Vincent Oord 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Discourse User Importer 2 | This script will create users for a Discourse installation given a CSV file with 3 | users. 4 | 5 | 6 | 7 | > 🚨 **Deprecation Warning** 🚨 8 | > 9 | > Please be aware that this script is quite old and Discourse has evolved since then. If you want to add users to your Discourse installation automatically, please check out the following options: 10 | > 11 | > There are a few different programs that help automate the process using the API. For example: 12 | > * https://github.com/pfaffman/discourse-user-creator 13 | > * https://github.com/safisher/discourse_create_user 14 | > 15 | > As well as various helpful threads on the Discourse forum: 16 | > * https://meta.discourse.org/t/creating-user-via-api/36133 17 | > * https://meta.discourse.org/t/creating-active-users-via-the-api-gem/33133/36 18 | > * https://meta.discourse.org/t/creating-a-user-through-restful-users-api/9359 19 | > 20 | > Thanks to [@safisher](https://github.com/safisher) for these tips 21 | 22 | Some features include: 23 | 24 | * Password is automatically generated, but your users need to reset it 25 | * Imported users are automatically approved by the system user (`id: -1`) 26 | * Imported users are automatically granted `trust_level: 1` permissions, just like invited users 27 | * Users can be automatically assigned to one or more predefined groups 28 | * If a user already exists, but has new groups in the CSV-file, these groups will be automatically added to the existing user 29 | * Special task to check if usernames already exist 30 | 31 | Caveats: 32 | 33 | * Error handling may be a little rough. Please check beforehand if all data in 34 | your CSV-file is correct, e.g. that the groups you want to use already exist. 35 | 36 | ## Import file format 37 | 38 | email;name;username;title;groups 39 | 40 | Field | Required | Description 41 | -----------|----------|------------ 42 | `email` | **Yes** | The email address of the user 43 | `name` | **Yes** | The user's full name 44 | `username` | No | The user's user name. If none is supplied, this will be generated from the email address. 45 | `title` | No | The user's title, e.g. _Vice President of Engineering_ 46 | `groups` | No | A comma separated list of groups a user should be added to. When left empty, the user will not be added to any specific groups. 47 | 48 | ## On passwords 49 | For each user a random password is generated using `SecureRandom.hex`. Users 50 | are able to set their own password after importing by using the password reset 51 | function in Discourse. 52 | 53 | ## Running it 54 | Add this file to your `lib/tasks` and run one of the following tasks: 55 | 56 | To check if one or more usernames already exists, without importing: 57 | 58 | $ rake user_importer:check[/path/to/users.csv] 59 | 60 | To import all the users in the file: 61 | 62 | $ rake user_importer:import[/path/to/users.csv] 63 | 64 | ## After running this script 65 | Since users are automatically approved, they can log in right away after running 66 | this script. Because the user's password is an automatically generated random 67 | string, a newly imported user can only log in by generating a new password 68 | through the password reset function in Discourse. They will then receive a link 69 | to set a new password in their email. 70 | -------------------------------------------------------------------------------- /user_importer.rake: -------------------------------------------------------------------------------- 1 | require 'csv' 2 | 3 | namespace :user_importer do 4 | desc "Import users from a CSV file" 5 | task :import, [:csv_file] => [:environment] do |_, args| 6 | abort "Please specify the CSV file to import" if args[:csv_file].blank? 7 | 8 | CSV.foreach(args[:csv_file], col_sep: ';', headers: true) do |new_user| 9 | user = User.where(email: new_user['email']).first 10 | if user 11 | new_groups = new_user_groups(new_user['groups']) - user.groups.map(&:name) 12 | user.groups << parse_user_groups(new_groups) 13 | 14 | puts "User #{new_user['email']} already exists and is not imported." 15 | puts ">> #{new_user['email']} was added to #{new_groups.join(',')}" unless new_groups.empty? 16 | else 17 | u = User.new({ 18 | username: new_user['username'] || UserNameSuggester.suggest(new_user['email']), 19 | email: new_user['email'], 20 | password: SecureRandom.hex, 21 | name: new_user['name'], 22 | title: new_user['title'], 23 | approved: true, 24 | approved_by_id: -1, 25 | trust_level: 1 26 | }) 27 | u.import_mode = true 28 | u.groups = parse_user_groups new_user['groups'] 29 | u.activate 30 | 31 | if u.save 32 | puts "Imported #{u.name} (#{u.email}) as #{u.username} to #{u.groups.map(&:name).join(',')}" 33 | else 34 | puts "Could not import #{u.name} (#{u.email}) due to #{u.errors.messages}" 35 | end 36 | end 37 | 38 | end 39 | end 40 | 41 | desc "Check usernames of users to be imported" 42 | task :check, [:csv_file] => [:environment] do |_, args| 43 | abort "Please specify the CSV file to import" if args[:csv_file].blank? 44 | 45 | CSV.foreach(args[:csv_file], col_sep: ';', headers: true) do |new_user| 46 | 47 | if new_user['username'] 48 | user = User.where(username: new_user['username']).first 49 | 50 | if user 51 | puts "Username #{new_user['username']} (#{new_user['email']}) already exists for user: #{user.name} (#{user.email})" 52 | else 53 | puts "Username #{new_user['username']} is free to use!" 54 | end 55 | 56 | else 57 | puts "No username supplied for #{new_user['email']}, ignoring..." 58 | end 59 | 60 | end 61 | end 62 | end 63 | 64 | def new_user_groups(groups) 65 | groups.split(',') 66 | end 67 | 68 | def parse_user_groups(groups) 69 | return [] if groups.blank? 70 | new_user_groups(groups).map do |group| 71 | Group.where(name: group).first_or_create 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /users.csv.example: -------------------------------------------------------------------------------- 1 | email;username;name;title;groups 2 | optimus.prime@example.com;optimus_prime;Optimus Prime;Autobots Supreme Commander;action_masters,autorollers 3 | megatron@example.com;megatron;Megatron;Decepticon Super Commander;action_masters,basic_beasts 4 | --------------------------------------------------------------------------------