MODX 3 modDashboardWidgetPlacement questions

UpgradeMODX connects its snippet-based widget to the default dashboard in a resolver. This usually works fine, but occasionally, the widget fails to show up on the dashboard.

It happened today and though I could see the widget listed in the dashboard’s grid, nothing I did would make it visible.

I looked at the modDashboardWidgetPlacement DB table and found that the user field for UpgradeMODX was set to 0. I set it to 1 (my user ID) and the widget showed up. Set it back to 0, and the widget disappeared again.

Looking at the DB, the user field clearly contains user IDs.
There are no usergroups or ACL entries related to the widgets or dashboard as far as I can tell.

So many questions:

  • What is the user field for and how does it work?

  • Is there a way to control the user field value in the Manager?

  • Should the user field be set when creating a
    modDashboardWidgetPlacement record in code?

  • How did all of the users get modDashboardWidgetPlacement records set with their IDs for some of the widgets? I certainly didn’t do it.

  • How did I get user fields set to 0? Should that be prevented, or should 0 mean visible to all users?

I’m probably misunderstanding the whole process, but couldn’t find any core code that relates to these issues.

Any help appreciated.

There seems to be a new feature in MODX3 that makes Dashboard widgets configurable on the dashboard itself.

If the option Customizable is checked for the dashboard, then the user can delete, add, reorder (drag and drop) or resize widgets directly on the dashboard. Every user can personalize his own dashboard.

I believe the entries with user set to 0 are the default settings for the dashboard.

If a new user logs into the manager, it creates a copy of these entries with user set to the user’s id.
If the user makes changes to his personal dashboard, then these changes are immediately saved to the database with his user id.

Thanks, that helps. I was thrown by the mismatch between the DB and the visible widgets. That turned out to be a cache issue (doh).

Here’s what the DB looks like now (widget 10 is UpgradeMODX):


When I attach a widget to a dashboard in the Manager, it gets assigned to all three users in this table (plus the fictional user 0).

I haven’t been able to find the code that does this, so I don’t know if it’s all users, or only qualified users.

The UGM widget originally had a record only for user 0, because it was created in code, rather than in the Manager, and the user field wasn’t set.

I still don’t know what user 0 is for, since if those were the only records, no one would see the widget.

There are now 2 places in the manager where a widget can be added.
Either in the menu “Dashboards” (/manager/?a=system/dashboards/update&id=1) or on the starting page (/manager) itself.

If you add a widget in the menu “Dashboards”, it creates an entry with user = 0 (and entries for other users of this dashboard if “Customizable” is checked.)

If you add the widget on the manager starting page, then it only gets added for the currently logged in user.

For a dashboard with “Customizable” unchecked, there are only entries with user = 0, because the dashboard is the same for every user.

For a customizable dashboard, the rows with user = 0 are what you set in the menu “Dashboards” (which is then used as the initial configuration of the dashboard for new users).

Thanks again. That makes sense, though it’s difficult to know what to do when installing a new widget.

Attaching it to the default dashboard, and assuming that the default dashboard is configureable (as it is on install) seems logical, but It seems crazy to create modDashboardWidgetPlacement records for potentially hundreds or even thousands of users, most of whom probably don’t have access to the Manager.

I wish I could find the code that creates those records when you attach a widget to a dashboard in the Manager to see if it somehow checks the users for Manager access, or just blindly creates the records for all users on the site.

It just queries the same table for users with the correct dashboard id.

Good catch. I don’t know how I missed that. So it creates records only for users that already have a record. That’s kind of brilliant.

It seems like I should be able to just call that processor with

$options = array(
   'widgets' => array($widgetId),
   'dashboard' => $dashboardObject,
    'id' => $dashboardObject->get('id'),

But that removes all of the widgets from the dashboard and sets the widget value to 0 for the rest. I’ve stepped through the code in debug and it looks like everything is working, but obviously it’s not.

Where do you get this $options array from?

When this processor is called from the manager, then there is more data and the widgets field contains an array of all the widgets of the dashboard with all the detailed information of the widgets (id, rank, name, etc.)

I create the $options array based on default dashboard and the widget I’m installing. The idea of sending all the widgets came to me in the bathtub (where I get all my best ideas). I haven’t tried it yet, but it seems to me that if that were the only problem, my widget would still have been placed on the dashboard, which it wasn’t.

I think I’ve been overthinking this. It should be enough just to check to see if my widget is already attached, and if not, get the users listed in the placement table, and create a record for my widget for each of them, plus the user 0 record.

Actually, my first thought was to do something even simpler, like this:

/* Get objects here */

$widgets = array($widgetObject);

$dashboardObject->addMany($widgets, 'Widgets');

But it appears there’s no alias for that.

There is an alias “Placements”.

That looks promising. It’s used in the dashboard duplicate processor. I already have code that works, but addMany() would simplify it a log. I’ll give it a try.

It works. You have to create a placement for each user, and you have to save the dashboard object, which addMany() doesn’t usually require.

It ended up being very close to the code I already had, which I think is a bit faster. This code is slightly more reliable, though.

Thanks again for your help. You saved me a lot of time and trouble.

Here’s the final code, for anyone who might need it in the future:

$widget = $modx->getObject('MODX\Revolution\modDashboardWidget', array('name' => 'Upgrade MODX'));
$widgetId = $widget->get('id');
$dashboardObject = $modx->getObject('MODX\Revolution\modDashboard', 1);
$placements = $dashboardObject->getMany('Placements');

$users = array();

/* Create array of user Ids from placements. */

foreach ($placements as $placement) {
    $users[] = $placement->get('user');

/* Remove duplicates */
$users = array_unique($users);

$fields = array(
    'widget' => $widgetId,
    'dashboard' => 1,
    'rank' => 0,
    'size' => 'half',

/* Create array of placement objects */
foreach ($users as $user) {
    $dbp = $modx->newObject('MODX\Revolution\modDashboardWidgetPlacement');
    if ($dbp) {
        $fields['user'] = $user;
        $dbp->fromArray($fields, '', true);
        $placements[] = $dbp;