Getting joined data for widget scope

Continuing the discussion from A tour of how the Widget (Virtual DOM) code in Discourse works:

If I want to access something outside the scope of a widget that only has access to the User object, how would I do? I am specifically trying to show a custom_field beside the user’s name in a post.

I was thinking of doing something like this in poster-name widget:

contents.push(h('span.post-user-location', attrs.custom_fields.location));

Any advice?

Furthering this though: how can you tell what attrs a widget has access to?

@eviltrout is there a mechanism for plugins to drive extra data into the widget models?

The poster-name widget has access to the entire post as attrs. If you want to attach a new widget within it, you can just pass along attrs and get access to everything.

Custom fields in particular are quite commonly needed in plugin widgets, so you can just access them like `attrs.userCustomFields.

Remember to make sure you whitelist the custom field if you want it accessible in your plugin:

whitelist_staff_user_custom_field('your_field_name')

If you want to see how we use it, the staff notes plugin uses user custom fields and extends widgets.

5 Likes

In using the staff notes plugin for reference, I have tried to make a custom field available to the post-name widget by doing the following:

In plugin.rb:

after_initialize do
  require_dependency 'user'
  whitelist_staff_user_custom_field("myCustomField")
end

In initializer:

import { withPluginApi } from 'discourse/lib/plugin-api';

export default {
  name: "alt-topic-initializer",

  initialize: function(container) {

    withPluginApi('0.2', api => {

      api.decorateWidget('poster-name:after', dec => {
        console.log(dec.attrs.userCustomFields);
      });

    });
 });

The result of my console.log is undefined.

I have tried whitelisting the custom field in Discourse site settings as well.

That should definitely work. Just to confirm, you were viewing the user as a staff user right? That API only makes the custom field visible for staff users.

I wanted the custom field to show for everyone. So when a user posts, the user custom field is shown by their name.

Okay but the api you’re using is for adding them for staff users. Have you tried added them to the public_user_custom_fields site setting?

2 Likes

Ya, I have it in the public_user_custom_fields site setting as well. Perhaps I am misunderstanding userCustomFields. The field in question is created under customization > user fields and the user can fill it in under their preferences.

In the rails console, UserCustomField gives me:

#<UserCustomField:0x007fb6a2bed830
  id: 10,
  user_id: -1,
  name: "user_field_2",
  value: "Cold Lake",
  created_at: Thu, 28 Apr 2016 18:03:11 UTC +00:00,
  updated_at: Thu, 28 Apr 2016 18:03:11 UTC +00:00>

So maybe I need to add “user_field_2” to the site settings rather than the name of the field?

Bingo! :confetti_ball: That was the issue! So the question is, why is the name of the custom user field like this?

Custom fields are a database structure originally built for plugins to store meta data associated with various things, including users. Originally the only way to create/update them was via a plugin.

Later we added that custom field admin section to make it easier for discourse admins to add forms and capture certain values. It uses the same back end as the plugin system, but you need to use the database name to access them.

It might be a good idea to accept either name, now that you mention it. I doubt there would be a conflict if we searched by name first and if not then value.

So in the admin section, the Field Name isn’t in the database, only the value?

The UserField table corresponds those form fields to UserCustomFields.

Ok, thanks. So for now the best way to access the UserCustomField in the widget is to just whitelist the user_field_X name in site settings.

Otherwise, in the plugin.rb I would need to do something along the lines of, find UserField with name “myCustomField”, get the id and then whitelist user_field_<id> somehow without site settings. Obviously possible but maybe just easier to look up the user field number and do it in site settings.

I’d like to follow this topic… In mentioned poster-name widget you have explained how to access User custom_fields.

From the same widget I’d like to access columns in user_stats table instead.

I know that any of user_stats variables needs to be passed to widget as attrs so I’d like to extend underlying functions.

I believe that the data source is post_serializer.rb and then also transform-post.js.es6 creates attrs in transformBasicPost(post).

This doesn’t work, I must be missing something… Thx in advance.

That is correct. You need to add the code to the serializer, then the transformer.

If it’s not working we’ll need a little more information. Is the json returned from the server including what you want?

1 Like

Thanks for confirming my direction… :slight_smile:

I had to re-check everything and found a bug on my part… This code works for me… Example is getting last_seen_at from user object. Would be fine for highlighting online users…

Thanks for this great project!

post_serializer.rb

  attributes :post_number,
             :user_last_seen_at,
             
  def user_last_seen_at
    object.try(:user).try(:last_seen_at)
  end

transform-post.js.es6

 export function transformBasicPost(post) {
   ...
   const postAtts = {
     ... 
     user_last_seen_at: post.user_last_seen_at,
2 Likes