Displaying the Age of a Resource in MODX

You might sometime have a need to display the age of a resource. The MODX Forums, for example, show how long ago a post was entered in Years, months, weeks, days, hours, and seconds. The display shows the age in the largest and second-largest of these that contain a non-zero value. For example, you might see posts with these messages:

1 year, 9 months ago
2 weeks, 3 days ago
5 minutes, 30 seconds ago

MODX logo

I didn’t have access to the Discuss code that does this (it’s not in the version of Discuss released at this writing), so I had to reverse engineer it. I didn’t look very hard for the code because I thought creating it would be fun.

I started from the excellent time_duration function created by Aidan Lister. I modified it to work as a snippet in MODX, added some MODX-specific sanity checks and features, and modified it to (optionally) produce the two-value display you see in the Forums.

By default (i.e., with no properties in the tag) it creates the two-value string you see in the Forums based on the createdon field of the current resource. Using the properties, you can choose to have it display the full value, omit certain of the terms, and/or display zero values. You can also control which resources it reports on, which field it uses, and what it returns in the case of non-date fields, empty fields, or unpublished resources (if using the publishedon field.

The Code

/* ResourceAge snippet */
function time_duration($seconds, $full = false, $use = null, $zeros = false) {
    /* Define time periods */
    $periods = array(
        'years'   => 31556926,
        'Months'  => 2629743,
        'weeks'   => 604800,
        'days'    => 86400,
        'hours'   => 3600,
        'minutes' => 60,
        'seconds' => 1
    );

    /* Break into periods */
    $seconds = (float) $seconds;
    $segments = array();
    $haveOne = false;
    foreach ($periods as $period => $value) {
        if ($use && strpos($use, $period[0]) === false) {
            continue;
        }

        /* round the current number down */
        $count = floor($seconds / $value);

        if ($count == 0 && !$zeros) {
            continue;
        }
        $segments[strtolower($period)] = $count;
        $seconds = $seconds % $value;
        if (!$full && $seconds)  {
            if ($haveOne) {
                break;
            } else {
                $haveOne = true;
            }

        }
    }

    /* Build the string */
    $string = array();
    foreach ($segments as $key => $value) {
        $segment_name = substr($key, 0, -1);
        $segment = $value . ' ' . $segment_name;
        if ($value != 1) {
            $segment .= 's';
        }
        $string[] = $segment;
    }

    return implode(', ', $string);
}
/** @var $scriptProperties array */
/* Save some typing */
$sp = $scriptProperties;

/* Get the property values sent in the tag */
$docId = $modx->getOption('docId', $sp, null);
$field = $modx->getOption('field', $sp, 'createdon');
$full = $modx->getOption('full', $sp, false);
$use = $modx->getOption('use', $sp, null);
$zeros = $modx->getOption('zeros', $sp, false);
$unpublishedMsg = $modx->getOption('unpublishedMsg', $sp, 'Not Published');
$errorMsg = $modx->getOption('errorMsg', $sp, 'Field is empty or not a date field');

/* Return a message if the field is publishedon and the
   doc is not published. */
if ($field == 'publishedon' && (!$doc->get('published'))) {
    return $unpublishedMsg;
}

/* Use the current resource if $docId is not set */
$doc = $docId? $modx->getObject('modResource', $docId) : $modx->resource;

/* get the timestamp of the selected field */
$co = strtotime($doc->get($field));

/* return an error message for empty or non-date fields */
if ($co === false) {
    return $errorMsg;
}
/* get the elapsed time in seconds */
$sec = time() - $co;
return time_duration($sec, $full, $use, $zeros);

The snippet uses $modx->getOption() to retrieve the properties sent in the snippet tag and provide a default value for each of them. In the next article, we’ll look at some of the finer points of using $modx->getOption(). For now, back using our snippet.

Usage

In its simplest form, the snippet returns the age as a two-value string based on the createdon field of the current resource:

This page was created [[!ResourceAge]] ago.

This page was created 2 months and 2 days ago.

If you want the value of another resource, you can send its ID in the &docId property.

If you want to use a field other than createdon (e.g., editedon> or publishedon, send the name of the field in the &field property. Using the pub_date or unpub_date fields is discouraged because they are typically emptied when the resources is published or unpublished. Be sure the field you use is a date field.

If you want the full display, use &full=`1`. That will give you a result like this:

This page was published 1 year, 7 months, 2 weeks, 1 day, 4 hours, 3 minutes, 20 seconds ago.

You can select the units used and ignored by sending the &use property. It defaults to yMwdhms (years, months, weeks, days, hours, minutes, seconds). You can leave out any of the letters to omit that unit. Note that M (uppercase) is for month and m (lowercase) is for minute.

By default, zero values are not displayed, but if you include &zeros=`1`, they’ll be included.

The &unpublishedMsg property sets the message returned when you’ve used publishedon for the field and the resource is unpublished. The default is “Not Published”. If you don’t want a message returned, use &unpublishedMsg=``.

The &errorMsg property sets the message returned when the field is empty or not a date field. The default is “Field is empty or not a date field”. If you don’t want a message returned, use &errorMsg=``.

Use in Tpl Chunks

I have not tested this, but depending on the parsing order, the snippet should work in Tpl chunks used by getResources or another aggregator if you use this form:

[[ResourceAge? &docId=`[[+id]]` ]]

Notes

The values displayed are a little imprecise, since they don’t take account of specific month lengths or leap years (months are assumed to be about 30.44 days). Having the snippet take into account the specific months involved in the interval and deal with leap years and leap seconds would lengthen the code and slow it down considerably. I didn’t consider it worth the effort, but feel free to add the appropriate modifications.

The second value is also rounded down, so a message posted 6 days, 23 hours, 59 minutes, and 59 seconds ago would be displayed as 6 days, 23 hours ago, even though it is one second shy of a week ago. You can always use the “full” value if you need more accuracy.

If I can find the time, I’ll probably release this snippet as an extra.


For more information on how to use MODX to create a web site, see my web site Bob’s Guides, or better yet, buy my book: MODX: The Official Guide.

Looking for quality MODX Web Hosting? Look no further than Arvixe Web Hosting!

Tags: , , , , , , , | Posted under MODX, MODX | RSS 2.0

Author Spotlight

Bob Ray

Bob Ray

I am the author of MODX: The Official Guide and over 30 MODX add-on components. I host Bob's Guides, a source of valuable information for MODX users, and I've been very active in the MODX Forums with over 14,000 posts.

Leave a Reply

Your email address will not be published. Required fields are marked *


8 + 1 =

You may use these HTML tags and attributes: <a href="" title="" rel=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>