Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

MediaWiki:Gadget-RecentChangesSidebar.js

MediaWiki interface page
Revision as of 17:42, 23 June 2026 by ScrapBlox (talk | contribs) (Listing recent wiki edits in sidebar)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
// From https://dev.miraheze.org/wiki/Recent_changes_sidebar
mw.loader.using([ 'mediawiki.api', 'mediawiki.util' ], function () {
    const scriptPath = mw.config.get('wgScriptPath');
    const skin = mw.config.get('skin');

    function articleUrl(title) {
        return mw.util.getUrl(title);
    }

    function contributionsUrl(user) {
        return mw.util.getUrl('Special:Contributions/' + user);
    }

    function recentChangesUrl() {
        return mw.util.getUrl('Special:RecentChanges');
    }

    function message(key, fallback) {
        const msg = mw.message(key);

        return msg.exists() ? msg.text() : fallback;
    }

    function formatAge(timestamp) {
        const timeDelta = Date.now() - Date.parse(timestamp);
        let timeString = Math.round(timeDelta / 60000) + 'm';

        if (timeDelta >= 3600000) {
            timeString = Math.round(timeDelta / 3600000) + 'h';
        }
        if (timeDelta >= 86400000) {
            timeString = Math.round(timeDelta / 86400000) + 'd';
        }

        return timeString;
    }

    function recentChangeItem(entry) {
        const user = entry.user;
        const userText = entry.anon === '' ? message('anonymous', 'anonymous') : user;
        const $timeLink = $('<a>', {
            href: scriptPath + `?diff=${entry.revid}`
        }).append(
            document.createTextNode(formatAge(entry.timestamp) + ' '),
            $('<span>', {
                class: 'rc-sidebar-ago',
                text: 'ago'
            })
        );

        return $('<li>', {
            class: 'mw-list-item rc-sidebar-item'
        }).append(
            $('<a>', {
                class: 'rc-sidebar-page',
                href: articleUrl(entry.title),
                text: entry.title
            }),
            $('<p>', {
                class: 'rc-sidebar-user'
            }).append(
                $timeLink,
                document.createTextNode(' - '),
                $('<a>', {
                    href: contributionsUrl(user),
                    text: userText
                })
            )
        );
    }

    function citizenShowMoreItem() {
        const $sidebar = $('.citizen-page-sidebar').first();

        if (!$sidebar.length) {
            return $();
        }

        const $showMore = $('<li>', {
            id: 'n-recentchanges',
            class: 'mw-list-item'
        }).append(
            $('<a>', {
                'data-mw': 'interface',
                href: recentChangesUrl(),
                rel: 'nofollow',
                title: 'A list of recent changes in the wiki'
            }).append(
                $('<span>', {
                    text: 'Show more...'
                })
            )
        );

        const $portlet = $('<nav>', {
            id: 'citizen-sidebar-recentchanges',
            class: 'citizen-menu rc-sidebar-menu',
            role: 'navigation',
            'aria-labelledby': 'citizen-sidebar-recentchanges-label'
        }).append(
            $('<div>', {
                id: 'citizen-sidebar-recentchanges-label',
                class: 'citizen-menu__heading',
                text: 'Recent changes'
            }),
            $('<div>', {
                class: 'citizen-menu__content'
            }).append(
                $('<ul>', {
                    class: 'citizen-menu__content-list'
                }).append($showMore)
            )
        );

        const $lastModified = $('#citizen-sidebar-lastmod').first();
        const $toc = $('#citizen-toc').first();

        if ($lastModified.length) {
            $lastModified.after($portlet);
        } else if ($toc.length) {
            $toc.before($portlet);
        } else {
            $sidebar.append($portlet);
        }

        return $showMore;
    }

    function showMoreItem() {
        if (skin === 'citizen') {
            return citizenShowMoreItem();
        }

        return $('#p-Recent_changes #n-recentchanges').first();
    }

    function populateRecentChanges($showMore, changes) {
        $showMore
            .children('a')
            .children('span')
            .last()
            .text('Show more...');

        $showMore.before(changes.map(function (entry) {
            return recentChangeItem(entry)[0];
        }));
    }

    const $showMore = showMoreItem();

    if (!$showMore.length) {
        return;
    }

    const api = new mw.Api();

    function handleError(msg) {
        console.error(msg);
    }

    api.get({
        action: 'query',
        list: 'recentchanges',
        rcprop: 'title|timestamp|user|ids',
        rcnamespace: '0',
        rclimit: '4',
        rctype: 'edit|new',
        rcshow: '!bot',
        rctoponly: '1',
        format: 'json',
        maxage: '120',
        smaxage: '120'
    }).done(function (data) {
        const changes = data.query && data.query.recentchanges ? data.query.recentchanges : [];

        if (changes.length === 0) {
            handleError("No recent change(s) found.");
            return;
        }

        populateRecentChanges($showMore, changes);
    }).fail(function (msg) {
        handleError(msg);
    });
});