Rake task for merging users

How about adding a staff note along the lines of “Username with email was merged to this account, date

This is what I did when I was merging accounts manually. Probably more work than a staff action log entry, but might be useful information at some point later.

4 Likes

@gerhard we should make sure the task is logged in the staff logs if possible, per above ↑ ↑

2 Likes

I’m currently writing my todo list for this week and it’s already on it. :slight_smile:

5 Likes

Is there an easy way to change which email address is primary?

Here’s my use case:

imported user with email user@defunctdomain.com emails from user@fancynewemail.com which creates a staged user. After a few PMs with the staged user, I merge the accounts, but the primary address is still the old one. And from the web interface I can’t change the email in his profile because it’s already taken. By him.

Perhaps this is a bug in the multiple email address model and not a complaint for here.

The simple solution, which took me much to long to come up with, is to delete the old address.

2 Likes

Merging users always preserves the existing data of the target_user, so the email address of the source_user is only added as secondary email address.

Yeah, that’s the solution.

2 Likes

Is there something easier that I should do?
I need to keep the old username with the new email. The staged user is the real problem. Perhaps I should just delete the staged user, but then I don’t have an easy way to reply to them to tell them what happened.

It’s pretty cumbersome to merge[‘newname_newemail’,'oldname_bogusemail"] and then have to do something like

u=User.find_by_username['oldname_bogusemail']
ue=UserEmail.where(user_id: u.id)
ue[0].destroy
ue[1].active=true
ue[1].save

is there some easier way that I’m missing?

1 Like

I updated the rake task so that it logs a “merge user” action instead of “delete user” in the staff logs.

6 Likes

I’m little bit confused. I don’t have users:merge task in my docker container. Does it already in v2.1.0.beta2 +16 version?

Yes, it’s part of Discourse since 2.0.0.beta4

2 Likes

Hm. Here what I have when I run rake --tasks:

rake about                                                             # List versions of all Rails frameworks and the environment
rake add_topic_to_quotes                                               # Add the topic to quotes
rake admin:create                                                      # Creates a forum administrator
rake admin:invite[email]                                               # invite an admin to this discourse instance
rake api_key:get                                                       # generate api key if missing, return existing if already there
rake app:template                                                      # Applies the template supplied by LOCATION=(/path/to/template) or URL
rake app:update                                                        # Update configs and some other initially generated files (or use just update:configs or update:bin)
rake assets:clean[keep]                                                # Remove old compiled assets
rake assets:clobber                                                    # Remove compiled assets
rake assets:environment                                                # Load asset compile environment
rake assets:precompile                                                 # Compile all the assets named in config.assets.precompile
rake assets:prestage                                                   # pre-stage assets on cdn
rake autospec                                                          # Run all specs automatically as needed
rake avatars:clean                                                     # Clean up all avatar thumbnails (use this when the thumbnail algorithm changes)
rake avatars:refresh                                                   # Refresh all avatars (download missing gravatars, refresh system)
rake build:stamp                                                       # stamp the current build with the git hash placed in version.rb
rake build_test_topic                                                  # create pushstate/replacestate test topic
rake cache_digests:dependencies                                        # Lookup first-level dependencies for TEMPLATE (like messages/show or comments/_comment.html)
rake cache_digests:nested_dependencies                                 # Lookup nested dependencies for TEMPLATE (like messages/show or comments/_comment.html)
rake db:create[multisite]                                              # Creates the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:create:all to create all databases in the config). Without RAILS_ENV or when RAILS_ENV is development, it defaults to creating the development and test databases
rake db:drop[multisite]                                                # Drops the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:drop:all to drop all databases in the config). Without RAILS_ENV or when RAILS_ENV is development, it defaults to dropping the development and test databases
rake db:environment:set[multisite]                                     # Set the environment value for the database
rake db:fixtures:load                                                  # Loads fixtures into the current environment's database
rake db:migrate[multisite]                                             # Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog)
rake db:migrate:status                                                 # Display status of migrations
rake db:rebuild_indexes                                                # Rebuild indexes
rake db:rollback                                                       # Rolls the schema back to the previous version (specify steps w/ STEP=n)
rake db:schema:cache:clear                                             # Clears a db/schema_cache.yml file
rake db:schema:cache:dump                                              # Creates a db/schema_cache.yml file
rake db:schema:dump                                                    # Creates a db/schema.rb file that is portable against any DB supported by Active Record
rake db:schema:load                                                    # Loads a schema.rb file into the database
rake db:seed                                                           # Loads the seed data from db/seeds.rb
rake db:seed_fu                                                        # Loads seed data for the current environment
rake db:setup                                                          # Creates the database, loads the schema, and initializes with the seed data (use db:reset to also drop the database first)
rake db:stats                                                          # Statistics about database
rake db:structure:load                                                 # Recreates the databases from the structure.sql file
rake db:version                                                        # Retrieves the current schema version number
rake destroy:groups                                                    # Destroy all groups
rake destroy:private_messages                                          # Remove all private messages
rake destroy:stats                                                     # Destroy site stats
rake destroy:topics[category]                                          # Remove all topics in a category
rake destroy:topics_all_categories                                     # Remove all topics in all categories
rake destroy:users                                                     # Destroy all non-admin users
rake dev:cache                                                         # Toggle development mode caching on/off
rake docker:test                                                       # Run all tests (JS and code in a standalone environment)
rake emails:import                                                     # use this task to import a mailbox into Disourse
rake emails:test[email]                                                # Send email test message
rake emoji:test                                                        # test the emoji generation script
rake emoji:update                                                      # update emoji images
rake enqueue_digest_emails                                             # This task is called by the Heroku scheduler add-on
rake export:categories[category_ids]                                   # Export all the categories
rake export:category_structure[include_group_users,file_name]          # Export only the structure of all categories
rake highlightjs:update                                                # download latest version of highlight and prepare it
rake i18n:stats                                                        # show the current translation status
rake import:file[file_name]                                            # Import existing exported file
rake initializers                                                      # Print out all defined initializers in the order they are invoked by Rails
rake integration:create_fixtures                                       # Creates the integration fixtures
rake log:clear                                                         # Truncates all/specified *.log files in log/ to zero bytes (specify which logs with LOGS=test,development)
rake middleware                                                        # Prints out your Rack middleware stack
rake multisite:generate:config                                         # generate multisite config file (if missing)
rake multisite:migrate                                                 # migrate all sites in tier
rake multisite:rollback                                                # rollback migrations for all sites in tier
rake notes                                                             # Enumerate all annotations (use notes:optimize, :fixme, :todo for focus)
rake notes:custom                                                      # Enumerate a custom annotation, specify with ANNOTATION=CUSTOM
rake plugin:install[repo]                                              # install plugin
rake plugin:install_all_official                                       # install all official plugins (use GIT_WRITE=1 to pull with write access)
rake plugin:qunit[plugin,timeout]                                      # run plugin qunit tests
rake plugin:spec[plugin]                                               # run plugin specs
rake plugin:update[plugin]                                             # update a plugin
rake plugin:update_all                                                 # update all plugins
rake poll:migrate_old_polls                                            # Migrate old polls to new syntax
rake posts:defer_all_flags                                             # Defer all flags
rake posts:delete_all_likes                                            # Delete all likes
rake posts:delete_word[find,type]                                      # Delete occurrence of a word/string
rake posts:fix_letter_avatars                                          # Rebake all posts with a quote using a letter_avatar
rake posts:normalize_code                                              # normalize all markdown so <pre><code> is not used and instead backticks
rake posts:rebake                                                      # Update each post with latest markdown
rake posts:rebake_match[pattern,type,delay]                            # Rebake all posts matching string/regex and optionally delay the loop
rake posts:refresh_emails[topic_id]                                    # Refreshes each post that was received via email
rake posts:refresh_oneboxes                                            # Update each post with latest markdown and refresh oneboxes
rake posts:remap[find,replace,type]                                    # Remap all posts matching specific string
rake posts:reorder_posts[topic_id]                                     # Reorders all posts based on their creation_date
rake qunit:test[timeout,qunit_path]                                    # Runs the qunit test suite
rake release_note:generate[from,to]                                    # generate a release note from the important commits
rake restart                                                           # Restart app by touching tmp/restart.txt
rake routes                                                            # Print out all defined routes in match order, with names
rake scheduler:run_all                                                 # run every task the scheduler knows about in that order, use only for debugging
rake secret                                                            # Generate a cryptographically secure secret key (this is typically used to generate a secret for cookie sessions)
rake site_settings:export                                              # Exports site settings
rake site_settings:import                                              # Imports site settings
rake smoke:test                                                        # run chrome headless smoke tests on current build
rake stats                                                             # Report code statistics (KLOCs, etc) from the application or engine
rake test                                                              # Runs all tests in test folder except system ones
rake test:db                                                           # Run tests quickly, but also reset db
rake test:system                                                       # Run system tests only
rake time:zones[country_or_offset]                                     # List all time zones, list by two-letter country code (`rails time:zones[US]`), or list by UTC offset (`rails time:zones[-8]`)
rake tmp:clear                                                         # Clear cache, socket and screenshot files from tmp/ (narrow w/ tmp:cache:clear, tmp:sockets:clear, tmp:screenshots:clear)
rake tmp:create                                                        # Creates tmp directories for cache, sockets, and pids
rake user_actions:rebuild                                              # rebuild the user_actions table
rake users:change_post_ownership[old_username,new_username,archetype]  # Change topic/post ownership of all the topics/posts by a specific user (without creating new revision)
rake users:update_posts[old_username,current_username]                 # Updates username in quotes and mentions
rake yarn:install                                                      # Install all JavaScript dependencies as specified via Yarn

And I rebuild discourse container in a last week.

1 Like

Oh, I see. The task doesn’t show up in that list because it doesn’t have a description. I’m going to add one.

But you should be able to execute the task as shown in the first post:

4 Likes

Here is what I have:

root@host:/var/www/discourse# rake users:merge['source','target']
rake aborted!
PG::UndefinedFunction: ОШИБКА:  функция jsonb_build_object(unknown, text, unknown, text, unknown, text, unknown, text) не существует
LINE 4:                 jsonb_build_object(
                        ^
HINT:  Функция с данными именем и типами аргументов не найдена. Возможно, вам следует добавить явные приведения типов.
/var/www/discourse/vendor/bundle/ruby/2.5.0/gems/rack-mini-profiler-1.0.0/lib/patches/db/pg.rb:92:in `async_exec'
/var/www/discourse/vendor/bundle/ruby/2.5.0/gems/rack-mini-profiler-1.0.0/lib/patches/db/pg.rb:92:in `async_exec'
/var/www/discourse/vendor/bundle/ruby/2.5.0/gems/mini_sql-0.1.9/lib/mini_sql/connection.rb:104:in `run'
/var/www/discourse/vendor/bundle/ruby/2.5.0/gems/mini_sql-0.1.9/lib/mini_sql/connection.rb:76:in `exec'
/var/www/discourse/app/jobs/regular/update_username.rb:63:in `update_notifications'
/var/www/discourse/app/jobs/regular/update_username.rb:22:in `execute'
/var/www/discourse/app/services/username_changer.rb:46:in `update_username'
/var/www/discourse/app/services/user_merger.rb:30:in `update_username'
/var/www/discourse/app/services/user_merger.rb:10:in `merge!'
/var/www/discourse/lib/tasks/users.rake:47:in `block in <top (required)>'
/usr/local/bin/bundle:23:in `load'
/usr/local/bin/bundle:23:in `<main>'
Tasks: TOP => users:merge
(See full trace by running task with --trace)

It wrote: ERROR: function jsonb_build_object(unknown, text, unknown, text, unknown, text, unknown, text) doesn't exist

You need to upgrade PostgreSQL to version 9.5 or greater. The current docker container uses Postgres 10 by default.

4 Likes

Ou, I see now. Thank you very much!

@gerhard Any chance that merging users on a site with the Discourse Voting plugin will prevent one of those users from logging in with the following error and log message?

The software powering this discussion forum encountered an unexpected problem. We apologize for the inconvenience.

Disabling the plugin solves the problem, but I like the plugin, so ideas on how I could fix it are very welcome!

The line in question is at https://github.com/discourse/discourse-voting/blob/master/plugin.rb#L112 and I think I’ve fixed it at https://github.com/discourse/discourse-voting/pull/33.

5 Likes

Maybe. Plugins can subscribe to the :merging_users event. It’s possible that the Voting plugin needs to do that.

5 Likes

Just found out about this and immediately tried it out. Works like a charm. Thank you for this nice feature!

3 Likes

Is there any way to empty the second emails field? I merged two users and then later realized I should not have done so. Now I can’t create the user again because the email address is in the second emails field.

Obviously some sort of UI fix would be lovely long term for emptying/changing these addresses, but I know that’s likely a ways off. In the meantime I’d be grateful for command line steps. Thanks in advance! :seedling:

Sure, this can be done in the rails console within the container.

rails c

# restrict to secondary emails, just in case you try to delete the wrong email
UserEmail.where(email: "foo@example.com", primary: false).destroy_all

# probably not needed, but better safe than sorry
EmailToken.where(email: "foo@example.com").destroy_all
8 Likes

This is helpful. This seems like something that people will need with some frequency; it’d probably be good if this were a rake task.

2 Likes