Import avatars for user accounts programmatically - how?


(Joe Seyfried) #1

Hi all,

I’m currently stuck here: I try to import user avatars from my phpbb3 by modifying the import script. I tried to have a look at @elberet’s great work on the SMF2 importer, but I have the problem that the users all have the letter avatar instead of the uploaded one. Here’s what I’m doing:

I fetch the file name and path, do a create_upload, ask if it persisted and call

user.update(uploaded_avatar_id: upload.id).

All works fine, the upload directory gets populated, the avatars get copied there, but they don’t show up in the user profiles. I tried to reset user.import_mode to false, do a refresh_avatar, but I currently cannot figure out what else to do. Any hints?


(Jens Maier) #2

I think the process how avatars are stored in Discourse’s model has changed since I’ve written the SMF2 importer. Try this:

post_create_action: proc do |user|
  user.import_mode = false
  user.refresh_avatar
  user.import_mode = true
  if ... # determine if the user has a custom avatar
    upload = ... # create the upload
    user.user_avatar.update(custom_upload_id: upload.id)
    user.update(uploaded_avatar_id: upload.id)
  end
end

Alternatively, and perhaps preferrably, remove the import_mode and refresh_avatar bit and add user.create_user_avatar before the first update line.

Chances are that the SMF2 importer is now broken in this regard as well, but I’ll hold off patching that in until someone reports it as a problem.


(Joe Seyfried) #3

Thanks for your quick reply, Jens!

A couple of questions: why do you set import mode to false only for a quick refresh_avatar? Why refresh the avatar before the upload and updates? Is refresh_avatar a bit stored in a member variable or a function as you used in your code? Here’s what I have (and what doesn’t work - I also tried your approach - but I moved the import_mode further down so I only set it if there’s really an avatar to upload):

    post_create_action: proc do |user|
      if (u['user_avatar_type']==1 or u['user_avatar_type']==2) and 
                                         user.uploaded_avatar_id.blank?
          path = avatar_fullpath(u['user_avatar_type'], u['user_avatar']) and begin
          upload = create_upload(user.id, path, u[:user_avatar])
          if upload.persisted?
            user.import_mode = false
            user.create_user_avatar
            user.user_avatar.update(custom_upload_id: upload.id)
            user.update(uploaded_avatar_id: upload.id)
            user.refresh_avatar
          else
            puts "Upload failed!"
          end
          rescue SystemCallError => err
            puts "Could not import avatar: #{err.message}"
          end
```

Is there a sidekiq job that has to activate the avatars first? If I take a look at the imported user profiles, all show the setting "Gravatar" (default), and there are no uploaded avatars visible. :(

(Jens Maier) #4

import_mode (currently) has no influence on anything except the refresh_avatar method; it is also not saved with the model. If it is set to true, refresh_avatar does nothing and immediately returns. In that version, I’ve included a call to refresh_avatar to let it create the user_avatar model object for the user, but calling create_user_avatar can do that just as well.

However, I don’t really understand why your avatars aren’t showing up. A few experiments on my live Discourse show that simply changing a user’s uploaded_avatar_id should be sufficient to change their avatar.

The only thing I can think of right now is that the upload isn’t recognized as an image file. Just for kicks, try this:

post_create_action: proc do |user|
  if ([1,2].include?(u['user_avatar_type']) && user.uploaded_avatar_id.blank?)
    path = avatar_fullpath(u['user_avatar_type'], u['user_avatar']) and begin
      origname = make_image_name(u['user_avatar'], path)
      upload = create_upload(user.id, path, origname)
      if upload.persisted?
        user.create_user_avatar
        user.user_avatar.update(custom_upload_id: upload.id)
        user.update(uploaded_avatar_id: upload.id)
      else
        puts "Upload failed."
      end
    rescue => err
      puts "Could not import avatar: #{err.message}"
    end
  end
end

def make_image_name(filename, path)
  return filename if FileHelper.is_image?(filename)
  ext = MIME::Types.type_for(path).first.try(:extensions).try(:first)
  raise "not an image file" unless ext.present?
  return "#{filename}.#{ext}"
end

Actually, this may be overkill, since you have a slight bug in your code: u[:user_avatar] is probably always nil.


(Joe Seyfried) #5

*huge and mighty GROAN* Got me. The import is currently running (the attachments drastically reducing import speed, but that’s just a tiny side note). Thanks for all your help (I probably should learn to code Ruby someday, but in the meantime, I’ll continue hacking on…)

So now I have my phpbb3 importer spiced with user avatars.

Since this works, @neil, would you rather have my additions by PM or should I create a PR?


(Neil Lalonde) #6

Send a PR please. :slight_smile: Thanks for working on it!


(Joe Seyfried) #7

I will try to iron out a few crinkles, and then send the PR…


(Joe Seyfried) #8

Stupid question: is there an easy way to create an upload from an external URL? I was quite sure I saw something like this, but I can’t find it any more… :frowning:

If not, I will probably (to begin with something) ditch all type-2 avatars which are hotlinked images from other websites…


(Neil Lalonde) #9

We do that in pull_hotlinked_images: discourse/pull_hotlinked_images.rb at master · discourse/discourse · GitHub

Maybe FileHelper.download(src, @max_size, "discourse-hotlinked") is what you’re looking for.


(Joe Seyfried) #10

Thx - will try that!


(Joe Seyfried) #11

Here we go:
https://github.com/discourse/discourse/pull/2889
Dumb question, anyway: how long does it take discoursebot to recognise a signed CLA? Am I just impatient, doing something wrong, or is there something else going awry?

TIA!


(Jeff Atwood) #12

Forever, as Discoursebot only checks once at the time of PR submission.


(Neil Lalonde) #13

Great stuff! It has been merged.