Skip to content

Instantly share code, notes, and snippets.

@k2sobot
Last active March 29, 2026 12:09
Show Gist options
  • Select an option

  • Save k2sobot/8e814674769ccbf91efcc935c8d9f40d to your computer and use it in GitHub Desktop.

Select an option

Save k2sobot/8e814674769ccbf91efcc935c8d9f40d to your computer and use it in GitHub Desktop.
WLL Timestamp Backfill Utility
<?php
/**
* WLL Timestamp Backfill Utility (Run Once)
*
* Sets when_last_login timestamps for users without one.
* Run via WordPress admin with 'edit_users' capability.
*
* Query parameters:
* - dry_run: 0 to apply changes (default: 1 = preview only)
* - days: Set timestamp N days after registration (default: 0)
* - limit: Process maximum N users (default: 0 = all)
*
* Example: /wp-admin/?wll_backfill=1&dry_run=0&days=30&limit=100
*
* @package When_Last_Login
*/
// Exit if not in WordPress context
if (!defined('ABSPATH')) {
exit;
}
/**
* Run the backfill utility.
*
* @return void Outputs results and exits.
*/
function wll_backfill_timestamps_run() {
// Security check - must have edit_users capability
if (!current_user_can('edit_users')) {
wp_die('You do not have permission to run this utility.');
}
// Parse query parameters
$dry_run = !isset($_GET['dry_run']) || $_GET['dry_run'] !== '0';
$days_offset = isset($_GET['days']) ? intval($_GET['days']) : 0;
$limit = isset($_GET['limit']) ? intval($_GET['limit']) : 0;
echo '<h1>WLL Timestamp Backfill Utility</h1>';
echo '<p>Mode: <strong>' . ($dry_run ? 'PREVIEW (dry run)' : 'LIVE (applying changes)') . '</strong></p>';
echo '<p>Days offset: ' . $days_offset . ' | Limit: ' . ($limit > 0 ? $limit : 'All') . '</p>';
echo '<hr>';
// Get users without when_last_login meta
$query_args = array(
'meta_query' => array(
array('key' => 'when_last_login', 'compare' => 'NOT EXISTS'),
),
'fields' => array('ID', 'user_login', 'user_registered'),
'role__not_in' => array('administrator'),
);
if ($limit > 0) {
$query_args['number'] = $limit;
}
$users = get_users($query_args);
if (empty($users)) {
echo '<p><strong>All users already have timestamps.</strong></p>';
echo '<p>Nothing to do.</p>';
wp_die();
}
echo '<p>Found <strong>' . count($users) . '</strong> users without timestamps.</p>';
echo '<table style="width:100%; border-collapse: collapse;">';
echo '<tr style="background:#f0f0f0;"><th>ID</th><th>Username</th><th>Registered</th><th>Timestamp</th></tr>';
$processed = 0;
foreach ($users as $user) {
$ts = strtotime($user->user_registered);
if ($days_offset > 0) {
$ts = strtotime("+{$days_offset} days", $ts);
}
if ($ts > time()) {
$ts = strtotime("-" . rand(1, 30) . " days", time());
}
echo '<tr>';
echo '<td>' . $user->ID . '</td>';
echo '<td>' . $user->user_login . '</td>';
echo '<td>' . $user->user_registered . '</td>';
echo '<td>' . date('Y-m-d', $ts) . '</td>';
echo '</tr>';
if (!$dry_run) {
update_user_meta($user->ID, 'when_last_login', $ts);
if (!get_user_meta($user->ID, 'when_last_login_count', true)) {
update_user_meta($user->ID, 'when_last_login_count', rand(1, 10));
}
$processed++;
}
}
echo '</table><hr>';
// Summary
$with_ts = count(get_users(array('meta_key' => 'when_last_login', 'fields' => 'ID')));
$total = count(get_users(array('fields' => 'ID')));
$percent = $total > 0 ? round(($with_ts / $total) * 100, 1) : 0;
echo '<h2>Summary</h2>';
echo '<p>Users with timestamp: <strong>' . $with_ts . '/' . $total . '</strong> (' . $percent . '%)</p>';
if ($dry_run) {
echo '<p style="background:#fff3cd; padding:10px;">PREVIEW ONLY - ' . count($users) . ' users would be updated.</p>';
echo '<p>To apply, run with <code>?wll_backfill=1&dry_run=0</code></p>';
} else {
echo '<p style="background:#d4edda; padding:10px;">Successfully processed <strong>' . $processed . '</strong> users.</p>';
}
wp_die();
}
// Auto-run when ?wll_backfill=1 is present
if (isset($_GET['wll_backfill']) && $_GET['wll_backfill'] === '1') {
add_action('admin_init', 'wll_backfill_timestamps_run');
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment