There are several resource fields that are not shown on the Create/Edit Resource panel. At this writing, the Manager does not show the id, context_key, editedon, createdon, createdby, editedby, or publishedby fields on the Create/Edit Resource panel. The id and context_key fields can be seen in the Resource tree, but if you have a lot of resources, it can be a pain to find them. The other fields are only visible in the database. You might want to see some of these fields when editing a resource. In this article, we'll look at how to display them.
This article shows that it's relatively easy to display any of the hidden fields on the Create/Edit Resource panel using a plugin. Unfortunately, letting the user update those fields and storing the results in the database is much more complicated. Doing so is beyond the scope of this article.
The Method
It's possible to use JavaScript to create, format, and insert the fields anywhere on the Create/Edit Resource panel, but it's extremely difficult and error-prone. The Create/Edit Resource panel is mostly created on the fly by ExtJS/modExt and many of the fields have IDs that are generated automatically and can change over time. I spent some time trying to make this work and got nowhere with it, though I think it's probably doable. Luckily, there's an easier way.
If you create a plugin attached to the OnDocFormRender event, put some HTML in a string, and send it to the modx->event->output() function, MODX will place the HTML at the bottom of the top section on the main tab of the Create/Edit Resource panel (just above the Content section). In the code below, you can see that we've done that at the end of the code, just above the return statement.
If you have code that also uses this technique (e.g., the plugins in the ClassExtender package), the code below will place the elements above or below the ones inserted by that extra, depending on the Priority of the two plugins, set on the System Events tab next to any attached events. Plugins with lower Priority numbers will execute first.
This code will display all the fields you are likely to want to see. If there are some fields you don't want displayed, just comment out or delete the line in the $fields array near the top of the code. Be sure each line in the array ends in a comma. The Tpl used to display each field sets the input section for each field to readonly, because editing these fields directly is a recipe for disaster. That's why MODX doesn't show them. In the code, the attribute is readonly="readonly". If you use just readonly, as is often done, it will work, but the code will not be valid XHTML. To make the fields editable, you need to remove the entire attribute, though it's a terrible idea, and I'm not sure MODX would save them correctly.
The Fields
Here's a quick rundown of the most useful hidden resource fields and what is stored in each one.
id— ID of the resourcecontext_key— Name of the resource's context (e.g., web)editedon— The date and time of the last update for the resourcecreatedon— The date and time the resource was createdcreatedby— ID of the user who created the resourceeditedby— ID of the last user to edit the resourcepublishedby— ID of the user who published the resource
There are actually a number of other fields. It's easy to add them to the display, just add a new line for each one to the array near the top of the plugin code below.
The Code
Because the user fields hold user IDs rather than names, we have to get the information from the user with that ID. Our code will have an option to use the username field for this or the fullname field. We'll also make it possible to format the display of the date fields.
Here's the code of the plugin. I called it ExtraDocFields, but you can use any name you like (note that ClassExtender uses ExtraResourceFields, so don't use that). Just paste the code into a new plugin. On the System Events tab, put a check next to OnDocFormRender, and click on the "Save" button.
$output = '';
$dateFormat = 'M d, Y';
$fullName = true; /* If false, username will be displayed */
/* Make it run in either MODX 2 or MODX 3 */
$prefix = $modx->getVersionData()['version'] >= 3
? 'MODX\Revolution\\'
: '';
/* Note that the Published On (publishedon), Publish Date (pub_date), and Unpublish Date (unpub_date) fields are omitted here because they are shown in the Manager. You can use a similar technique if you want to show them in the front end. */
$fields = array(
'Resource ID,id,integer',
'Context,context_key,default',
'Last Edited On,editedon,date',
'Created On,createdon,date',
'Created By,createdby,user',
'Edited By,editedby,user',
'Published By,publishedby,user',
);
/* Don't execute for new documents.
The fields would be bogus except for context_key */
if ($mode == modSystemEvent::MODE_NEW) {
return '';
}
foreach($fields as $field) {
$parts = explode(',', $field);
if (count($parts) != 3) {
$caption = 'Error: ';
$value = 'Malformed array member';
$output .= '<div class="x-form-item x-tab-item"><label class="x-form-item-label" style="color:red">' . $caption . ' <span>' . $value . '</span></label></div>';
} else {
$caption = $parts[0];
$fieldName = $parts[1];
$type = $parts[2];
$value = $resource->get($fieldName);
switch ($type) {
case 'date':
$value = date($dateFormat, strtotime($value));
break;
case 'user':
if ($fullName) {
$profile = $modx->getObject($prefix . 'modUserProfile', array('internalKey' => $value));
$value = $profile->get('fullname');
} else {
$userObject = $modx->getObject($prefix . 'modUser', $value);
$value = $userObject->get('username');
}
break;
case 'integer':
default:
/* Value used as is */
break;
}
$fieldTpl = <<< TPL
<div class="x-form-item x-tab-item">
<label for="modx-resource-{$fieldName}" class="x-form-item-label">{$caption}</label>
<div class="x-form-element" id = "x-form-el-modx-resource-'{$fieldName}" style="padding-left:0;">
<input type = "text" size="30" readonly="readonly" autocomplete = on" msgtarget="under" id="modx-resource-{$fieldName} name="{$fieldName}"
class="x-form-text x-form-field" title="" style = "width: 973px;" value="{$value}">
<div class="x-form-clear-left"></div>
</div >
</div>
TPL;
$output .= $fieldTpl;
}
}
// $modx->log(modX::LOG_LEVEL_ERROR, 'Output: ' . $output);
$modx->event->output($output);
return '';
The code starts by initializing the $output variable to an empty string, and setting the $dateFormat and $fullName options. The $dateFormat string will be sent to the PHP date() function. You can see the format string options in the table on the PHP date() manual page. Both the date and the time can be displayed in a wide variety of formats. If the $fullName variable is set to false, the username will be used.
Next, we create an array with the fields to show. Each field has three elements, separated by commas. The first one is the caption to be shown for the field, the second one is the field name in the database, the third is the type of field it is. Valid types are date, user, integer, and default. Default fields will be shown exactly as they are retrieved from the DB.
Once we have the $fields array, we loop through it, adding the appropriate stuff to the $output variable as we go. The explode() function returns a PHP array containing the three members of the array element. If any element doesn't have exactly three parts, we add an error message to the output. If the array element is valid, we use the three member of the $parts array to set the $caption, $fieldName, and $type variables. Then, we get the value of the field from the $resource object which is set automatically by MODX when the plugin is called. The value goes in the $value variable, but it may be modified in the switch statement below.
Each section of the switch statement handles a different type of field.
For date fields, MODX returns a human-readable date/time, which is probably not in the format you want, so we convert it to a timestamp with strtotime(), and then call date() using our own date format to make it display in our preferred form.
The user fields (createdby, editedby, and publishedby) are a little trickier because the username is in the modUser object and the full name is in the modUserProfile object. Depending on the value of the $fullName variable, we get the appropriate object. Notice that the user object is retrieved directly with the user ID. The user profile object, though, is retrieved by looking for that value in the internalKey field. If you get a profile using the user's ID, it will usually work, but not always. In the profile table, the user's ID is held in the internalKey field and the id field is arbitrary. Since users and their profiles are almost always created at the same time, the two are often the same, but over time, they can diverge.
Integer and default fields are not modified. They're used just as they come from the DB. The 'integer' and 'default' cases in the switch statement are not actually necessary, but they make the code easier to understand.
Finally, we use our Tpl to create the output for the field being processed and add it to the $output variable. Notice that this happens inside the loop, but after the switch statement when the $value is in the form we want.
The Tpl is in $modx->getChunk() and having MODX parse the chunk and replace all the placeholders each time through the loop.
Finally, we call $modx->event->output() with our string of HTML ($output) as the argument.
Other Formats
You may not like the formatted output produced by the date() function. In that case, you can simply change the $dateFormat variable to modify the output. For example, you might want to include the time the Resource was created, edited, or published. To do that, you can add g:i a to the end of the formatting string to see something like this:
Dec 23, 2025 4:07:10 pm
For more information on the formatting codes, see this table in the PHP manual.
Styling the Display
The current code mimics the format of the standard fields of the Create/Edit Resource panel, but it pushes the content section down (depending on how many of the fields you show). On some displays, it could be off the screen. It's certainly possible to reformat the display. The values could be shown on the same line as the captions. In fact, the captions and values could all be shown on the same line. The easiest and safest way to reformat the extra field displays would be to add or modify inline styles for the elements to the Tpl code without changing the existing MODX classes. You could, for example, add something like style="display:inline; margin:5px 15px;" in the Tpl code to make everything appear on the same line. Any inline styles you add this way will override the MODX CSS.
Moving the Hidden Field Display
In the Tpl chunk used to display the fields, I've used the MODX standard IDs for each field. I haven't tested it, but I think it might be possible to move the fields around with Form Customization. If that works, the extra fields could go on the Settings tab, or on another existing tab. In theory, you could create another tab and put them there. Doing this is beyond the scope of this article, but feel free to attempt it.
Coming Up
One drawback of our method above is that it's very difficult to internationalize it so people see things like month and day names in languages other than English. This used to be done with PHP's strftime() function, which honored the current locale setting. Now that strftime() is deprecated, the recommended way to do it is with the IntlDateFormatter class. We'll look at how to do that in my next article.
About Bob Ray
Bob Ray is the author of the MODX: The Official Guide and dozens of MODX Extras including QuickEmail, NewsPublisher, SiteCheck, GoRevo, Personalize, EZfaq, MyComponent and many more. His website is Bob’s Guides. It not only includes a plethora of MODX tutorials but there are some really great bread recipes there, as well.
Learn more about Bob Ray.