2016-01-16 13:24:13 +00:00
< ? php
register_module ([
" name " => " Recent Changes " ,
2019-02-10 23:18:34 +00:00
" version " => " 0.5 " ,
2016-01-16 13:24:13 +00:00
" author " => " Starbeamrainbowlabs " ,
" description " => " Adds recent changes. Access through the 'recent-changes' action. " ,
" id " => " feature-recent-changes " ,
" code " => function () {
global $settings , $env , $paths ;
2016-06-12 20:15:43 +00:00
2016-01-16 13:24:13 +00:00
// Add the recent changes json file to $paths for convenience.
$paths -> recentchanges = $env -> storage_prefix . " recent-changes.json " ;
// Create the recent changes json file if it doesn't exist
if ( ! file_exists ( $paths -> recentchanges ))
file_put_contents ( $paths -> recentchanges , " [] " );
2018-04-17 17:59:23 +00:00
/**
2019-01-24 18:25:10 +00:00
* @ api { get } ? action = recent - changes [ & offset = { number }][ & count = { number }][ & format = { code }] Get a list of recent changes
2018-04-17 17:59:23 +00:00
* @ apiName RecentChanges
* @ apiGroup Stats
* @ apiPermission Anonymous
*
2019-01-24 18:25:10 +00:00
* @ apiParam { number } offset If specified , start returning changes from this many changes in . 0 is the beginning .
* @ apiParam { number } count If specified , return at most this many changes . A value of 0 means no limit ( the default ) - apart from the limit on the number of changes stored by the server ( configurable in pepppermint . json ) .
2019-01-24 20:30:20 +00:00
* @ apiParam { string } format The format to return the recent changes in . Valid values : html , json , csv , atom . Default : html .
2018-04-17 17:59:23 +00:00
*/
2016-01-16 13:24:13 +00:00
/*
* ██████ ███████ ██████ ███████ ███ ██ ████████
* ██ ██ ██ ██ ██ ████ ██ ██
* ██████ █████ ██ █████ ██ ██ ██ ██
* ██ ██ ██ ██ ██ ██ ██ ██ ██
* ██ ██ ███████ ██████ ███████ ██ ████ ██
*
* ██████ ██ ██ █████ ███ ██ ██████ ███████ ███████
* ██ ██ ██ ██ ██ ████ ██ ██ ██ ██
* ██ ███████ ███████ ██ ██ ██ ██ ███ █████ ███████
* ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
* ██████ ██ ██ ██ ██ ██ ████ ██████ ███████ ███████
*/
add_action ( " recent-changes " , function () {
2016-03-21 16:21:09 +00:00
global $settings , $paths , $pageindex ;
2016-01-16 13:24:13 +00:00
2018-04-17 17:59:23 +00:00
$format = $_GET [ " format " ] ? ? " html " ;
2019-01-24 18:25:10 +00:00
$offset = intval ( $_GET [ " offset " ] ? ? 0 );
$count = intval ( $_GET [ " count " ] ? ? 0 );
2016-01-16 13:24:13 +00:00
2016-04-03 14:58:49 +00:00
$recent_changes = json_decode ( file_get_contents ( $paths -> recentchanges ));
2016-01-16 13:51:25 +00:00
2019-01-24 18:25:10 +00:00
// Limit the number of changes displayed if requested
if ( $count > 0 )
$recent_changes = array_slice ( $recent_changes , $offset , $count );
2018-04-17 17:59:23 +00:00
switch ( $format ) {
case " html " :
$content = " \t \t <h1>Recent Changes</h1> \n " ;
if ( count ( $recent_changes ) > 0 )
$content .= render_recent_changes ( $recent_changes );
else // No changes yet :(
$content .= " <p><em>None yet! Try making a few changes and then check back here.</em></p> \n " ;
2019-01-27 23:12:39 +00:00
page_renderer :: add_header_html ( " \t <link rel= \" alternate \" type= \" application/atom+xml \" href= \" ?action=recent-changes&format=atom \" />
2019-01-24 21:15:23 +00:00
< link rel = \ " alternate \" type= \" text/csv \" href= \" ?action=recent-changes&format=csv \" />
2019-01-27 23:12:39 +00:00
< link rel = \ " alternate \" type= \" application/json \" href= \" ?action=recent-changes&format=json \" /> " );
2019-01-24 21:15:23 +00:00
2019-01-27 23:12:39 +00:00
exit ( page_renderer :: render ( " Recent Changes - $settings->sitename " , $content ));
2018-04-17 17:59:23 +00:00
break ;
case " json " :
$result = json_encode ( $recent_changes );
header ( " content-type: application/json " );
2018-04-17 18:04:26 +00:00
header ( " content-length: " . strlen ( $result ));
2018-04-17 17:59:23 +00:00
exit ( $result );
break ;
2019-01-24 18:18:20 +00:00
case " csv " :
if ( empty ( $recent_changes )) {
http_response_code ( 404 );
header ( " content-type: text/plain " );
exit ( " No changes made been recorded yet. Make some changes and then come back later! " );
}
$result = fopen ( 'php://temp/maxmemory:' . ( 5 * 1024 * 1024 ), 'r+' );
fputcsv ( $result , array_keys ( get_object_vars ( $recent_changes [ 0 ])));
foreach ( $recent_changes as $recent_change )
fputcsv ( $result , array_values ( get_object_vars ( $recent_change )));
rewind ( $result );
header ( " content-type: text/csv " );
header ( " content-length: " . fstat ( $result )[ " size " ]);
2019-01-24 20:30:20 +00:00
exit ( stream_get_contents ( $result ));
break ;
case " atom " :
$result = render_recent_change_atom ( $recent_changes );
header ( " content-type: application/atom+xml " );
header ( " content-length: " . strlen ( $result ));
exit ( $result );
default :
http_response_code ( 406 );
header ( " content-type: text/plain " );
header ( " content-length: 42 " );
exit ( " Error: That format code wasnot recognised. " );
2016-01-16 13:24:13 +00:00
}
2018-04-17 17:59:23 +00:00
2016-01-16 13:24:13 +00:00
});
register_save_preprocessor ( function ( & $pageinfo , & $newsource , & $oldsource ) {
global $env , $settings , $paths ;
// Work out the old and new page lengths
$oldsize = strlen ( $oldsource );
$newsize = strlen ( $newsource );
// Calculate the page length difference
$size_diff = $newsize - $oldsize ;
2016-04-03 14:27:50 +00:00
$newchange = [
" type " => " edit " ,
2016-01-16 13:24:13 +00:00
" timestamp " => time (),
" page " => $env -> page ,
" user " => $env -> user ,
" newsize " => $newsize ,
" sizediff " => $size_diff
2016-04-03 14:27:50 +00:00
];
if ( $oldsize == 0 )
$newchange [ " newpage " ] = true ;
2016-01-16 13:24:13 +00:00
2016-04-03 14:31:50 +00:00
add_recent_change ( $newchange );
2016-01-16 13:24:13 +00:00
});
2016-03-21 16:21:09 +00:00
add_help_section ( " 800-raw-page-content " , " Recent Changes " , " <p>The <a href='?action=recent-changes'>recent changes</a> page displays a list of all the most recent changes that have happened around $settings->sitename , arranged in chronological order. It can be found in the \" More... \" menu in the top right by default.</p>
< p > Each entry displays the name of the page in question , who edited it , how long ago they did so , and the number of characters added or removed . Pages that < em > currently </ em > redirect to another page are shown in italics , and hovering over the time since the edit wil show the exact time that the edit was made .</ p > " );
2016-01-16 13:24:13 +00:00
}
]);
2016-04-03 14:31:50 +00:00
/**
* Adds a new recent change to the recent changes file .
2017-09-15 22:06:10 +00:00
* @ package feature - recent - changes
* @ param array $rchange The new change to add .
2016-04-03 14:31:50 +00:00
*/
function add_recent_change ( $rchange )
{
global $settings , $paths ;
2016-04-03 14:43:40 +00:00
2016-04-03 14:31:50 +00:00
$recentchanges = json_decode ( file_get_contents ( $paths -> recentchanges ), true );
array_unshift ( $recentchanges , $rchange );
// Limit the number of entries in the recent changes file if we've
// been asked to.
if ( isset ( $settings -> max_recent_changes ))
2016-08-28 12:41:18 +00:00
$recentchanges = array_slice ( $recentchanges , 0 , $settings -> max_recent_changes );
2016-04-03 14:31:50 +00:00
// Save the recent changes file back to disk
file_put_contents ( $paths -> recentchanges , json_encode ( $recentchanges , JSON_PRETTY_PRINT ));
}
2017-09-15 22:06:10 +00:00
/**
* Renders a list of recent changes to HTML .
* @ package feature - recent - changes
* @ param array $recent_changes The recent changes to render .
* @ return string The given recent changes as HTML .
*/
2016-04-03 19:36:01 +00:00
function render_recent_changes ( $recent_changes )
2016-04-03 14:58:49 +00:00
{
2016-04-04 12:55:43 +00:00
global $pageindex ;
2016-04-03 19:36:01 +00:00
// Cache the number of recent changes we are dealing with
$rchange_count = count ( $recent_changes );
// Group changes made on the same page and the same day together
for ( $i = 0 ; $i < $rchange_count ; $i ++ )
{
for ( $s = $i + 1 ; $s < $rchange_count ; $s ++ )
{
// Break out if we have reached the end of the day we are scanning
if ( date ( " dmY " , $recent_changes [ $i ] -> timestamp ) !== date ( " dmY " , $recent_changes [ $s ] -> timestamp ))
break ;
2016-10-01 10:37:42 +00:00
// If we have found a change that has been made on the same page and
// on the same day as the one that we are scanning for, move it up
// next to the change we are scanning for.
if ( $recent_changes [ $i ] -> page == $recent_changes [ $s ] -> page &&
date ( " j " , $recent_changes [ $i ] -> timestamp ) === date ( " j " , $recent_changes [ $s ] -> timestamp ))
2016-04-03 19:36:01 +00:00
{
// FUTURE: We may need to remove and insert instead of swapping changes around if this causes some changes to appear out of order.
$temp = $recent_changes [ $i + 1 ];
$recent_changes [ $i + 1 ] = $recent_changes [ $s ];
$recent_changes [ $s ] = $temp ;
$i ++ ;
}
}
}
2016-04-03 14:58:49 +00:00
$content = " <ul class='page-list'> \n " ;
2016-04-03 17:11:34 +00:00
$last_time = 0 ;
2016-04-03 19:36:01 +00:00
for ( $i = 0 ; $i < $rchange_count ; $i ++ )
2016-04-03 14:58:49 +00:00
{
2016-04-03 19:36:01 +00:00
$rchange = $recent_changes [ $i ];
2016-09-11 17:57:33 +00:00
2016-04-03 17:11:34 +00:00
if ( $last_time !== date ( " dmY " , $rchange -> timestamp ))
2016-04-04 12:55:43 +00:00
$content .= " <li class='header'><h2> " . date ( " jS F " , $rchange -> timestamp ) . " </h2></li> \n " ;
$rchange_results = [];
for ( $s = $i ; $s < $rchange_count ; $s ++ )
{
if ( $recent_changes [ $s ] -> page !== $rchange -> page )
break ;
$rchange_results [ $s ] = render_recent_change ( $recent_changes [ $s ]);
$i ++ ;
}
2016-09-11 17:57:33 +00:00
// Take one from i to account for when we tick over to the next
// iteration of the main loop
$i -= 1 ;
2016-04-04 12:55:43 +00:00
$next_entry = implode ( " \n " , $rchange_results );
2016-09-11 17:57:33 +00:00
// If the change count is greater than 1, then we should enclose it
// in a <details /> tag.
2016-04-04 12:55:43 +00:00
if ( count ( $rchange_results ) > 1 )
{
reset ( $rchange_results );
$rchange_first = $recent_changes [ key ( $rchange_results )];
end ( $rchange_results );
$rchange_last = $recent_changes [ key ( $rchange_results )];
2016-06-08 16:41:30 +00:00
$pageDisplayHtml = render_pagename ( $rchange_first );
$timeDisplayHtml = render_timestamp ( $rchange_first -> timestamp );
2016-04-04 12:55:43 +00:00
$users = [];
foreach ( $rchange_results as $key => $rchange_result )
{
if ( ! in_array ( $recent_changes [ $key ] -> user , $users ))
$users [] = $recent_changes [ $key ] -> user ;
}
2017-02-12 09:55:37 +00:00
foreach ( $users as & $user )
$user = page_renderer :: render_username ( $user );
2016-06-08 16:41:30 +00:00
$userDisplayHtml = render_editor ( implode ( " , " , $users ));
2016-04-04 12:55:43 +00:00
2016-11-02 17:51:00 +00:00
$next_entry = " <li><details><summary><a href='?page= " . rawurlencode ( $rchange_first -> page ) . " '> $pageDisplayHtml </a> $userDisplayHtml $timeDisplayHtml </summary><ul class='page-list'> $next_entry </ul></details></li> " ;
2016-04-04 12:55:43 +00:00
$content .= " $next_entry\n " ;
}
2016-04-06 14:24:49 +00:00
else
{
$content .= implode ( " \n " , $rchange_results );
}
2016-04-03 17:11:34 +00:00
$last_time = date ( " dmY " , $rchange -> timestamp );
2016-04-03 14:58:49 +00:00
}
$content .= " \t \t </ul> " ;
return $content ;
}
2019-01-24 20:30:20 +00:00
/**
* Given a page name and timestamp , returns the associated page revision number .
* @ param string $pagename The page name to obtain the revision number for .
* @ param int $timestamap The timestamp at which the revision was saved .
* @ return int The revision number of the given page at the given time .
*/
2019-01-24 20:51:23 +00:00
function find_revisionid_timestamp ( $pagename , $timestamp ) {
global $pageindex ;
2019-01-24 20:30:20 +00:00
if ( ! isset ( $pageindex -> $pagename ) || ! isset ( $pageindex -> $pagename -> history ))
return null ;
foreach ( $pageindex -> $pagename -> history as $historyEntry ){
if ( $historyEntry -> timestamp == $timestamp ) {
return $historyEntry -> rid ;
break ;
}
}
}
2017-09-15 22:06:10 +00:00
/**
* Renders a single recent change
* @ package feature - recent - changes
* @ param object $rchange The recent change to render .
* @ return string The recent change , rendered to HTML .
*/
2016-04-03 14:58:49 +00:00
function render_recent_change ( $rchange )
{
2016-11-02 17:51:00 +00:00
global $pageindex ;
2016-06-08 16:41:30 +00:00
$pageDisplayHtml = render_pagename ( $rchange );
2017-02-12 09:55:37 +00:00
$editorDisplayHtml = render_editor ( page_renderer :: render_username ( $rchange -> user ));
2016-06-08 16:41:30 +00:00
$timeDisplayHtml = render_timestamp ( $rchange -> timestamp );
2016-04-03 14:58:49 +00:00
2019-01-24 20:30:20 +00:00
$revisionId = find_revisionid_timestamp ( $rchange -> page , $rchange -> timestamp );
2016-11-02 17:51:00 +00:00
2016-04-03 14:58:49 +00:00
$result = " " ;
$resultClasses = [];
2018-07-03 11:28:01 +00:00
$rchange_type = isset ( $rchange -> type ) ? $rchange -> type : " edit " ;
switch ( $rchange_type )
2016-04-03 14:58:49 +00:00
{
2018-07-03 11:28:01 +00:00
case " revert " :
2016-04-03 14:58:49 +00:00
case " edit " :
// The number (and the sign) of the size difference to display
$size_display = ( $rchange -> sizediff > 0 ? " + " : " " ) . $rchange -> sizediff ;
$size_display_class = $rchange -> sizediff > 0 ? " larger " : ( $rchange -> sizediff < 0 ? " smaller " : " nochange " );
if ( $rchange -> sizediff > 500 or $rchange -> sizediff < - 500 )
$size_display_class .= " significant " ;
2016-04-04 12:55:43 +00:00
$size_title_display = human_filesize ( $rchange -> newsize - $rchange -> sizediff ) . " -> " . human_filesize ( $rchange -> newsize );
2016-04-03 14:58:49 +00:00
if ( ! empty ( $rchange -> newpage ))
$resultClasses [] = " newpage " ;
2018-07-03 11:28:01 +00:00
if ( $rchange_type === " revert " )
$resultClasses [] = " reversion " ;
2016-04-03 14:58:49 +00:00
2019-01-24 20:30:20 +00:00
$result .= " <a href='?page= " . rawurlencode ( $rchange -> page ) . ( ! empty ( $revisionId ) ? " &revision= $revisionId " : " " ) . " '> $pageDisplayHtml </a> $editorDisplayHtml $timeDisplayHtml <span class=' $size_display_class ' title=' $size_title_display '>( $size_display )</span> " ;
2016-04-03 14:58:49 +00:00
break ;
2018-03-18 21:38:58 +00:00
2016-04-03 14:58:49 +00:00
case " deletion " :
$resultClasses [] = " deletion " ;
2016-04-04 12:55:43 +00:00
$result .= " $pageDisplayHtml $editorDisplayHtml $timeDisplayHtml " ;
2016-04-03 15:10:37 +00:00
break ;
2018-03-18 21:38:58 +00:00
case " move " :
$resultClasses [] = " move " ;
$result .= " $rchange->oldpage ⭢ <a href='?page= " . rawurlencode ( $rchange -> page ) . " '> $pageDisplayHtml </a> $editorDisplayHtml $timeDisplayHtml " ;
break ;
2016-04-03 15:10:37 +00:00
case " upload " :
$resultClasses [] = " upload " ;
2016-04-06 15:20:53 +00:00
$result .= " <a href='?page= $rchange->page '> $pageDisplayHtml </a> $editorDisplayHtml $timeDisplayHtml ( " . human_filesize ( $rchange -> filesize ) . " ) " ;
2016-04-03 15:10:37 +00:00
break ;
2017-05-20 14:18:22 +00:00
case " comment " :
$resultClasses [] = " new-comment " ;
2017-05-20 14:40:59 +00:00
$result .= " <a href='?page= $rchange->page #comment- " . ( ! empty ( $rchange -> comment_id ) ? " $rchange->comment_id " : " unknown_comment_id " ) . " '> $pageDisplayHtml </a> $editorDisplayHtml " ;
2016-04-03 14:58:49 +00:00
}
$resultAttributes = " " . ( count ( $resultClasses ) > 0 ? " class=' " . implode ( " " , $resultClasses ) . " ' " : " " );
$result = " \t \t \t <li $resultAttributes > $result </li> \n " ;
return $result ;
}
2019-01-24 20:30:20 +00:00
/**
* Renders a list of recent changes as an Atom 1.0 feed .
* Requires the XMLWriter PHP class .
* @ param array $recent_changes The array of recent changes to render .
* @ return string The recent changes as an Atom 1.0 feed .
*/
function render_recent_change_atom ( $recent_changes ) {
global $version , $settings ;
2019-01-24 20:56:25 +00:00
// See http://www.atomenabled.org/developers/syndication/#sampleFeed for easy-to-read Atom 1.0 docs
2019-01-24 20:30:20 +00:00
$full_url_stem = full_url ();
$full_url_stem = substr ( $full_url_stem , 0 , strpos ( $full_url_stem , " ? " ));
$xml = new XMLWriter ();
$xml -> openMemory ();
2019-01-24 20:47:16 +00:00
$xml -> setIndent ( true ); $xml -> setIndentString ( " \t " );
2019-01-24 20:30:20 +00:00
$xml -> startDocument ( " 1.0 " , " utf-8 " );
$xml -> startElement ( " feed " );
$xml -> writeAttribute ( " xmlns " , " http://www.w3.org/2005/Atom " );
$xml -> startElement ( " generator " );
$xml -> writeAttribute ( " uri " , " https://github.com/sbrl/Pepperminty-Wiki/ " );
$xml -> writeAttribute ( " version " , $version );
$xml -> text ( " Pepperminty Wiki " );
$xml -> endElement ();
$xml -> startElement ( " link " );
$xml -> writeAttribute ( " rel " , " self " );
$xml -> writeAttribute ( " type " , " application/atom+xml " );
$xml -> writeAttribute ( " href " , full_url ());
$xml -> endElement ();
$xml -> startElement ( " link " );
$xml -> writeAttribute ( " rel " , " alternate " );
$xml -> writeAttribute ( " type " , " text/html " );
$xml -> writeAttribute ( " href " , " $full_url_stem ?action=recent-changes&format=html " );
$xml -> endElement ();
$xml -> startElement ( " link " );
$xml -> writeAttribute ( " rel " , " alternate " );
$xml -> writeAttribute ( " type " , " application/json " );
$xml -> writeAttribute ( " href " , " $full_url_stem ?action=recent-changes&format=json " );
$xml -> endElement ();
$xml -> startElement ( " link " );
$xml -> writeAttribute ( " rel " , " alternate " );
$xml -> writeAttribute ( " type " , " text/csv " );
$xml -> writeAttribute ( " href " , " $full_url_stem ?action=recent-changes&format=csv " );
$xml -> endElement ();
$xml -> writeElement ( " updated " , date ( DateTime :: ATOM ));
$xml -> writeElement ( " id " , full_url ());
$xml -> writeElement ( " icon " , $settings -> favicon );
$xml -> writeElement ( " title " , " $settings->sitename - Recent Changes " );
2019-01-24 20:47:16 +00:00
$xml -> writeElement ( " subtitle " , " Recent Changes on $settings->sitename " );
2019-01-24 20:30:20 +00:00
foreach ( $recent_changes as $recent_change ) {
2019-01-24 20:47:16 +00:00
if ( empty ( $recent_change -> type ))
$recent_change -> type = " edit " ;
2019-01-24 20:30:20 +00:00
$xml -> startElement ( " entry " );
// Change types: revert, edit, deletion, move, upload, comment
$type = $recent_change -> type ;
$url = " $full_url_stem ?page= " . rawurlencode ( $recent_change -> page );
2019-01-24 20:47:16 +00:00
$content = " <ul>
< li >< strong > Change type :</ strong > $recent_change -> type </ li >
< li >< strong > User :</ strong > $recent_change -> user </ li >
< li >< strong > Page name :</ strong > $recent_change -> page </ li >
2019-01-24 23:54:27 +00:00
< li >< strong > Timestamp :</ strong > " .date(DateTime::RFC1123, $recent_change->timestamp ). " </ li > \n " ;
2019-01-24 20:47:16 +00:00
2019-01-24 20:30:20 +00:00
switch ( $type ) {
case " revert " :
case " edit " :
$type = ( $type == " revert " ? " Reversion of " : " Edit to " );
$revision_id = find_revisionid_timestamp ( $recent_change -> page , $recent_change -> timestamp );
if ( ! empty ( $revision_id ))
$url .= " &revision= $revision_id " ;
2019-01-24 23:54:27 +00:00
$content .= " <li><strong>New page size:</strong> " . human_filesize ( $recent_change -> newsize ) . " </li>
< li >< strong > Page size difference :</ strong > " .( $recent_change->sizediff > 0 ? " + " : " " ). " $recent_change -> sizediff </ li > \n " ;
2019-01-24 20:30:20 +00:00
break ;
case " deletion " : $type = " Deletion of " ; break ;
case " move " : $type = " Movement of " ; break ;
2019-01-24 20:47:16 +00:00
case " upload " :
$type = " Upload of " ;
$content .= " \t <li><strong>File size:</strong> " . human_filesize ( $recent_change -> filesize ) . " </li> \n " ;
break ;
2019-01-24 20:30:20 +00:00
case " comment " :
$type = " Comment on " ;
2019-01-24 20:47:16 +00:00
$url .= " #comment- $recent_change->comment_id " ;
2019-01-24 20:30:20 +00:00
break ;
}
2019-01-24 20:47:16 +00:00
$content .= " </ul> " ;
$xml -> startElement ( " title " );
$xml -> writeAttribute ( " type " , " text " );
$xml -> text ( " $type $recent_change->page by $recent_change->user " );
$xml -> endElement ();
2019-01-24 20:30:20 +00:00
$xml -> writeElement ( " id " , $url );
$xml -> writeElement ( " updated " , date ( DateTime :: ATOM , $recent_change -> timestamp ));
2019-01-24 20:47:16 +00:00
2019-01-24 20:30:20 +00:00
$xml -> startElement ( " content " );
2019-01-24 20:47:16 +00:00
$xml -> writeAttribute ( " type " , " html " );
$xml -> text ( $content );
2019-01-24 20:30:20 +00:00
$xml -> endElement ();
$xml -> startElement ( " link " );
$xml -> writeAttribute ( " rel " , " alternate " );
$xml -> writeAttribute ( " type " , " text/html " );
$xml -> writeAttribute ( " href " , $url );
$xml -> endElement ();
$xml -> startElement ( " author " );
$xml -> writeElement ( " name " , $recent_change -> user );
2019-02-03 16:35:38 +00:00
$xml -> writeElement ( " uri " , " $ full_url_stem?page= " . rawurlencode ( " $settings->user_page_prefix / $recent_change->user " ));
2019-01-24 20:30:20 +00:00
$xml -> endElement ();
$xml -> endElement ();
}
$xml -> endElement ();
2019-01-24 20:47:16 +00:00
return $xml -> flush ();
2019-01-24 20:30:20 +00:00
}