<?php
/*
Plugin Name: FOAF Output
Plugin URI: http://www.wasab.dk/morten/blog/archives/2004/07/05/wordpress-plugin-foaf-output
Description: Personal profile page with friends (XFN) and interests, FOAF RDF/XML profile, and extended RSS (1.0) output.
Version: 1.17
Author: Morten Frederiksen
Author URI: http://purl.org/net/morten/

Changes since 1.16 (2005-08-31):
* Fixed errant line breaks in links.
* Added generator comment to RSS output.

Changes since 1.15 (2005-03-27):
* Added oneline bio on profile page, not HTML-escaped.
* Added link to RSS channel from author list.
* Tweaked prefix usage for namespaces.

Changes since 1.14 (2005-01-01):
* Added check for array being returned from get_the_category().
* Added check for get_Lat and get_Long for > 1.2 compatibility.

Changes since 1.13 (2004-12-13):
* Fixed category/interest when no categories were found.
* Changed skos:externalID to dc:identifier.
* Updated SKOS generation with SKOS extensions vocabulary.
* Fixed generation of author list URI.

Changes since 1.12 (2004-09-27):
* Changed a wrong foaf:made to foaf:page, caught by Ian Davis.
* Fixed erroneous output of homepage URI on profile page.
* Fixed a problem with statements being added to Atom feeds, thanks Danny/Sam.

Changes since 1.11 (2004-09-23):
* Added blog-wide FOAF output (blogroll) with seeAlso's to authors' individual files.

Changes since 1.10:
* Added SKOS output and enhanced RSS output.
* Added document level RDF/XML API hook, foaf_output_profile_rdf_document, to allow for additional properties by add-ons, e.g. generator information.
* Fixed possible missing namespace declarations for dcterms in RSS/Atom.
* Tweaked initialisation code to increase reusability.

Changes since 1.9:
* Fixed limited interest generation for HTML profile page.
* Multiple URIs per interest is now handled correctly (if separated by whitespace).
* Only categories with posts by author are deemed "interesting".
* Added bio:olb per B.K. DeLong's suggestion.
* Added trust ratings for friends.

Changes since 1.8:
* Renamed foaf_output_the_date to get_foaf_output_profile_page to better reflect the functionality.

Changes since 1.7:
* Improved identification of "active" author / user.
* Now really only shows profile on first archive page, even in paged mode.

*/

function get_foaf_output_profile_rdf($author_id '') {
    global 
$authordata;
    
$authordata get_foaf_output_author($author_id);
    if (!
$authordata)
        return 
'';
    
# Define mappings from XFN to relationship vocabulary, where possible.
    
$xfn2rel = array(
        
'acquaintance' => 'acquaintanceOf',
        
'friend' => 'friendOf',
        
'met' => 'hasMet',
        
'co-worker' => 'worksWith',
        
'colleague' => 'colleagueOf',
        
'co-resident' => 'livesWith',
        
'neighbor' => 'neighborOf',
        
'child' => 'parentOf',
        
'parent' => 'childOf',
        
'sibling' => 'siblingof',
        
'spouse' => 'spouseof',
        
'muse' => '',
        
'crush' => '',
        
'date' => '',
        
'sweetheart' => '');
    
# Create and return FOAF profile.
    
$rdf '<?xml version="1.0" encoding="' get_settings('blog_charset') . '"?>
<rdf:RDF
    '
;
    
$rdf .= apply_filters('foaf_output_profile_rdf_namespaces',
            
'xmlns="http://xmlns.com/foaf/0.1/"
    xmlns:foaf="http://xmlns.com/foaf/0.1/"
    xmlns:rss="http://purl.org/rss/1.0/"
    xmlns:rel="http://purl.org/vocab/relationship/"
    xmlns:xfn="http://gmpg.org/xfn/1#"
    xmlns:bio="http://purl.org/vocab/bio/0.1/"
    xmlns:trust="http://trust.mindswap.org/ont/trust.owl#"
    xmlns:admin="http://webns.net/mvcb/"
    xmlns:skos="http://www.w3.org/2004/02/skos/core#"
    xmlns:dc="http://purl.org/dc/elements/1.1/"
    xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"'
);
    
$rdf .= '>
<PersonalProfileDocument rdf:about="">' 
"\n\t";
    
$rdf .= apply_filters('foaf_output_profile_rdf_document',
            
'<dc:title>FOAF Document for ' htmlspecialchars(the_author(''false)) . '</dc:title>
    <primaryTopic rdf:nodeID="a' 
$authordata->ID '"/>
    <maker rdf:nodeID="a' 
$authordata->ID '"/>
    <admin:generatorAgent rdf:resource="http://www.wasab.dk/morten/blog/archives/2004/07/05/wordpress-plugin-foaf-output?version=1.17"/>'
);
    
$rdf .= '
</PersonalProfileDocument>
<Person rdf:nodeID="a' 
$authordata->ID '">
    <weblog>
        <Document rdf:about="' 
htmlspecialchars(get_author_link(0$authordata->ID$authordata->user_nicename)) . '">
            <dc:title>' 
htmlspecialchars(get_bloginfo('name')) . '</dc:title>
            <rdfs:seeAlso>
                <rss:channel rdf:about="' 
htmlspecialchars(get_bloginfo('rdf_url')) . '"/>
            </rdfs:seeAlso>
        </Document>
    </weblog>'
;
    if (
$authordata->user_firstname)
        
$rdf .= "\n\t" '<firstName>' htmlspecialchars($authordata->user_firstname) . '</firstName>';
    if (
$authordata->user_lastname)
        
$rdf .= "\n\t" '<surname>' htmlspecialchars($authordata->user_lastname) . '</surname>';
    if (
$authordata->user_nickname)
        
$rdf .= "\n\t" '<nick>' htmlspecialchars($authordata->user_nickname) . '</nick>';
    if (
$authordata->user_email)
        
$rdf .= "\n\t" get_foaf_output_email_property($authordata->user_email);
    if (
$authordata->user_aim)
        
$rdf .= "\n\t" '<aimChatID>' htmlspecialchars($authordata->user_aim) . '</aimChatID>';
    if (
$authordata->user_yim)
        
$rdf .= "\n\t" '<yahooChatID>' htmlspecialchars($authordata->user_yim) . '</yahooChatID>';
    if (
$authordata->user_msn)
        
$rdf .= "\n\t" '<msnChatID>' htmlspecialchars($authordata->user_msn) . '</msnChatID>';
    if (
$authordata->user_icq)
        
$rdf .= "\n\t" '<icqChatID>' htmlspecialchars($authordata->user_icq) . '</icqChatID>';
    if (
$authordata->user_url)
        
$rdf .= "\n\t" '<homepage rdf:resource="' htmlspecialchars($authordata->user_url) . '"/>';
    if (
$authordata->user_description)
        
$rdf .= "\n\t" '<bio:olb>' htmlspecialchars(strip_tags($authordata->user_description)) . '</bio:olb>';
    
# Friends.
    
foreach (get_linkobjects() as $link) {
        if (
$link->link_rel && $authordata->ID == $link->link_owner) {
            
$rdf .= "\n\t" '<knows>
        <Person rdf:nodeID="n' 
$link->link_id '">
            <name>' 
htmlspecialchars($link->link_description) . '</name>
            <weblog>
                <Document rdf:about="' 
htmlspecialchars($link->link_url) . '">
                    <dc:title>' 
htmlspecialchars($link->link_name) . '</dc:title>';
                if (
$link->link_rss)
                    
$rdf .= '
                    <rdfs:seeAlso>
                        <rss:channel rdf:about="' 
htmlspecialchars($link->link_rss) . '"/>
                    </rdfs:seeAlso>'
;
                
$rdf .= '
                </Document>
            </weblog>
        </Person>
    </knows>'
;
            foreach (
explode(' '$link->link_rel) as $xfn) {
                
$rdf .= "\n\t" '<xfn:' $xfn ' rdf:nodeID="n' $link->link_id '"/>';
                if (
array_key_exists($xfn$xfn2rel) && '' != $xfn2rel[$xfn])
                    
$rdf .= "\n\t" '<rel:' $xfn2rel[$xfn] . ' rdf:nodeID="n' $link->link_id '"/>';
            }
            if (
$link->link_rating && preg_match('|^\d$|'$link->link_rating))
                
$rdf .= "\n\t" '<trust:trust' $link->link_rating ' rdf:nodeID="n' $link->link_id '"/>';
        }
    }
    
# Interests.
    
foreach (get_foaf_output_interests() as $interest) {
        
$lines preg_split('|(\r?\n)+|'$interest->category_description);
        foreach (
$lines as $line) {
            if (
preg_match('|^(http:\S+)(\s)?(\S.+)?$|'$line$M)) {
                if (
array_key_exists(3$M))
                    
$title $M[3];
                else
                    
$title $interest->cat_name;
                
$rdf .= "\n\t" '<interest>
        <Document rdf:about="' 
htmlspecialchars($M[1]) . '">
            <dc:title>' 
htmlspecialchars($title) . '</dc:title>
        </Document>
    </interest>'
;
            }
        }
    }
    
$rdf .= apply_filters('foaf_output_profile_rdf_person''');
    
$authors preg_replace('|/feed/?.*?$|''/feed/foaf'get_bloginfo('rss_url'));
    
$authors preg_replace('|/wp-rss.php$|''/wp-feed.php?feed=foaf'$authors);
    
$rdf .= '
    <rdfs:seeAlso rdf:resource=""/>
    <rdfs:seeAlso>
        <Document rdf:about="' 
get_skos_scheme_document_uri() . '">
            <dc:title>' 
htmlspecialchars(the_author(''false)) . '\'s Categories</dc:title>
            <dc:subject>
                <skos:ConceptScheme rdf:about="' 
get_skos_scheme_document_uri() . '#scheme"/>
            </dc:subject>
        </Document>
    </rdfs:seeAlso>
    <rdfs:seeAlso>
        <Document rdf:about="' 
$authors '">
            <dc:title>Author list for ' 
htmlspecialchars(get_bloginfo('name')) . '</dc:title>
        </Document>
    </rdfs:seeAlso>
</Person>'
;
    
$rdf .= apply_filters('foaf_output_profile_rdf''');
    
$rdf .= '
</rdf:RDF>'
;
    return 
$rdf;
}

function 
get_foaf_output_profile_html($author_id ''$title '') {
    global 
$authordata;
    
$authordata get_foaf_output_author($author_id);
    if (!
$authordata)
        return 
'';
    
# Create and return HTML profile.
    
if (!$title)
        
$title 'Profile and latest posts for ';
    
$author_fields=array(
        
'Name'     => htmlspecialchars($authordata->user_firstname.' ' $authordata->user_lastname),
        
'Nickname' => htmlspecialchars($authordata->user_nickname),
        
'Homepage' => ('' != $authordata->user_url)
                ? 
'<a href="' htmlspecialchars($authordata->user_url) . '">' htmlspecialchars($authordata->user_url) . '</a>'
                
'',
        
'Oneline Bio' => nl2br($authordata->user_description),
        
'AIM'      => htmlspecialchars($authordata->aim),
        
'Yahoo'    => htmlspecialchars($authordata->yim),
        
'ICQ'      => htmlspecialchars($authordata->icq),
        
'MSN'      => htmlspecialchars($authordata->msn));
    
$profile '<h2 class="profile"><a href="' get_foaf_output_profile_uri()
            . 
'" title="FOAF">' __($title) . ' ' the_author(''false)
            . 
'</a></h2>
    <div class="profile">
        <dl>
'
;
    
# Basic personal information.
    
while (list($label$value) = each($author_fields)) {
        if (
$value)
            
$profile .= "\t\t\t" '<dt class="author">' $label ':</dt><dd class="author">' $value '</dd>'."\n";
    }
    
# Interests.
    
$interests '';
    foreach (
get_foaf_output_interests() as $interest) {
        
$lines preg_split('|(\r?\n)+|'$interest->category_description);
        foreach (
$lines as $line) {
            if (
preg_match('|^(http:\S+)(\s)?(\S.+)?$|'$line$M)) {
                if (
array_key_exists(3$M))
                    
$title $M[3];
                else
                    
$title $interest->cat_name;
                
$interests .= '<li><a title="' htmlspecialchars($interest->cat_name)
                        . 
'" href="' htmlspecialchars($M[1]) . '">'
                        
htmlspecialchars($title) . '</a> (<a href="'
                        
get_category_link(false$interest->cat_ID$interest->category_nicename) . '">'
                        
$interest->post_count ' ' . (1!=$interest->post_count?__('posts'):__('post')) . '</a>)</li>';
            }
        }
    }
    if (
'' != $interests)
        
$profile .= "\t\t\t" '<dt class="interests">Interests/Concepts:</dt><dd class="interests"><ul>' $interests "</ul></dd>\n";
    
# Friends.
    
$friends='';
    foreach (
get_linkobjects() as $link) {
        if (
$link->link_rel && $authordata->ID == $link->link_owner)
            
$friends .= '<li><a title="' .htmlspecialchars($link->link_name)
                    . 
'" href="' htmlspecialchars($link->link_url) . '">'
                    
htmlspecialchars($link->link_description) . '</a> ('
                    
join(', 'explode(' '$link->link_rel)) . ')</li>';
    }
    if (
'' != $friends)
        
$profile .= "\t\t\t" '<dt class="friends">Friends:</dt><dd class="friends"><ul>' $friends "</ul></dd>\n";
    
$profile .= apply_filters('foaf_output_profile_html_person''');
    
$profile .= "\n\t\t</dl>";
    
$profile .= apply_filters('foaf_output_profile_html''');
    
$profile .= "\n\t</div>";
    return 
$profile;
}

function 
foaf_output_the_category_rss($text) {
    global 
$feed$authordata$post$_SERVER;

    
# Don't do anything if not RSS 1.0.
    
if ('rdf' != $feed && !preg_match('|<dc:subject>|'$text)
            || !
preg_match('|rdf|'$_SERVER['REQUEST_URI']))
        return 
$text;

    
# Add comment with version information.
    
$text .= "\n\t" '<!-- Generator Agent: http://www.wasab.dk/morten/blog/archives/2004/07/05/wordpress-plugin-foaf-output?version=1.17"-->';

    
# Add statements about foaf:maker.
    
$maker '<foaf:maker xmlns:foaf="http://xmlns.com/foaf/0.1/">
        <foaf:Person>'
;
    if (
'' != $authordata->user_nickname)
        
$maker .= "\n\t\t\t" '<foaf:nick>' htmlspecialchars($authordata->user_nickname) . '</foaf:nick>';
    
$maker .= "\n\t\t\t" get_foaf_output_email_property($authordata->user_email);
    if (
'' != $authordata->user_url)
        
$maker .= "\n\t\t\t" '<foaf:homepage rdf:resource="' htmlspecialchars($authordata->user_url) . '"/>';
    
$text $maker '
            <rdfs:seeAlso xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" rdf:resource="' 
get_foaf_output_profile_uri($authordata->ID) . '"/>
        </foaf:Person>
    </foaf:maker>' 
$text;

    
# Add foaf:topic statements and concept pointers to item.
    
$categories get_the_category();
    if (
is_array($categories)) {
        foreach (
$categories as $category) {
            
$text .= '
    <dc:subject rdf:resource="' 
get_skos_scheme_document_uri() . '#c' $category->category_id '"/>';
            
$lines preg_split('|(\r?\n)+|'stripslashes(convert_chars($category->category_description)));
            foreach (
$lines as $line) {
                if (
preg_match('|^(http:\S+)(\s)?(\S.+)?$|'$line$M)) {
                    if (
array_key_exists(3$M))
                        
$title $M[3];
                    else
                        
$title stripslashes(convert_chars($category->cat_name));
                    
$text .= '
    <foaf:topic xmlns:foaf="http://xmlns.com/foaf/0.1/" rdf:parseType="Resource">
        <dc:title>' 
htmlspecialchars($title) . '</dc:title>
        <foaf:page rdf:resource="' 
htmlspecialchars($M[1]) . '"/>
    </foaf:topic>'
;
                }
            }
        }
    }

    
# Add location, if possible.
    
if (function_exists('get_Lat()') && function_exists('get_Lon()')) {
        if (
round(get_settings('default_geourl_lat'), 3) != round(get_Lat(),3)
                && 
round(get_settings('default_geourl_lon'), 3) != round(get_Lon(), 3))
            
$text .= '
        <dcterms:spatial xmlns:dcterms="http://purl.org/dc/terms/" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" rdf:parseType="Resource">
            <geo:lat>' 
htmlspecialchars(get_Lat()) . '</geo:lat>
            <geo:long>' 
htmlspecialchars(get_Lon()) . '</geo:long>
        </dcterms:spatial>'
;
    }

    
# Add Dublin Core references for links.
    
preg_match_all('|<a[^>]+href=[\'"]([^\'"]+?)[\'"][^>]*>([^<]+)</a>|'apply_filters('the_content'$post->post_content), $linksPREG_SET_ORDER);
    foreach (
$links as $link) {
        
$link[1] = preg_replace('|\s+|','',$link[1]);
        
$link[1] = preg_replace('|<br/>|','',$link[1]);
        
$text .= '
    <dcterms:references xmlns:dcterms="http://purl.org/dc/terms/" rdf:resource="' 
$link[1] . '" dc:title="' htmlspecialchars($link[2]) . '"/>';
    }

    
# Done.
    
return $text "\n";
}

function 
get_skos_scheme_rdf($author_id '') {
    global 
$authordata;
    
$authordata get_foaf_output_author($author_id);
    if (!
$authordata)
        return 
'';
    
# Create and return SKOS scheme.
    
$rdf '<?xml version="1.0" encoding="' get_settings('blog_charset') . '"?>
<rdf:RDF
    '
;
    
$rdf .= apply_filters('skos_scheme_rdf_namespaces',
            
'xmlns="http://www.w3.org/2004/02/skos/core#"
    xmlns:se="http://www.w3.org/2004/02/skos/extensions#"
    xmlns:foaf="http://xmlns.com/foaf/0.1/"
    xmlns:rss="http://purl.org/rss/1.0/"
    xmlns:admin="http://webns.net/mvcb/"
    xmlns:dc="http://purl.org/dc/elements/1.1/"
    xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"'
);
    
$rdf .= '>
<foaf:Document rdf:about="">'
;
    
$rdf .= apply_filters('skos_scheme_rdf_document''
    <dc:title>' 
htmlspecialchars(the_author(''false)) . '\'s Categories</dc:title>
    <dc:subject rdf:resource="#scheme"/>
    <foaf:maker rdf:nodeID="a' 
$authordata->ID '"/>
    <admin:generatorAgent rdf:resource="http://www.wasab.dk/morten/blog/archives/2004/07/05/wordpress-plugin-foaf-output?version=1.17"/>'
);
    
$rdf .= '
</foaf:Document>
<ConceptScheme rdf:about="#scheme">'
;
    
$rdf .= apply_filters('skos_scheme_rdf_scheme''
    <dc:title>' 
htmlspecialchars(the_author(''false)) . '\'s Categories</dc:title>
    <dc:description>Concepts from the weblog "' 
htmlspecialchars(get_bloginfo('name')) . '" based on category usage by ' htmlspecialchars(the_author(''false)) . '</dc:description>
    <dc:creator>' 
the_author(''false) . '</dc:creator>');
    
$rdf .= '
</ConceptScheme>
<foaf:Person rdf:nodeID="a' 
$authordata->ID '">';
    if (
$authordata->user_firstname)
        
$rdf .= "\n\t" '<foaf:firstName>' htmlspecialchars($authordata->user_firstname) . '</foaf:firstName>';
    if (
$authordata->user_lastname)
        
$rdf .= "\n\t" '<foaf:surname>' htmlspecialchars($authordata->user_lastname) . '</foaf:surname>';
    if (
$authordata->user_nickname)
        
$rdf .= "\n\t" '<foaf:nick>' htmlspecialchars($authordata->user_nickname) . '</foaf:nick>';
    if (
$authordata->user_email)
        
$rdf .= "\n\t" get_foaf_output_email_property($authordata->user_email);
    
$rdf .= "\n\t" '<foaf:weblog rdf:resource="' htmlspecialchars(get_author_link(0$authordata->ID$authordata->user_nicename)) . '"/>
    <rdfs:seeAlso>
        <foaf:PersonalProfileDocument rdf:about="' 
htmlspecialchars(get_foaf_output_profile_uri()) . '"/>
    </rdfs:seeAlso>
</foaf:Person>'
;
    
    
# Concepts (all categories with posts from author)
    
$concepts get_foaf_output_interests(true);
    foreach (
$concepts as $concept) {
        
$lines preg_split('|(\r?\n)+|'$concept->category_description);
        
$rdf .= '
<Concept rdf:about="' 
'#c' $concept->cat_ID '">
    <dc:identifier>' 
$concept->cat_ID '</dc:identifier>
    <prefLabel>' 
htmlspecialchars($concept->cat_name) . '</prefLabel>
    <rdfs:label>' 
htmlspecialchars($concept->cat_name) . '</rdfs:label>
    <inScheme rdf:resource="#scheme"/>'
;
        foreach (
$lines as $line) {
            if (
preg_match('/^skos:(related|broader|narrower)\s+(http:\S+)(\s+)?(\S.+)?$/'$line$M)) {
                if (
array_key_exists(4$M))
                    
$title "\n\t\t\t" '<altLabel>' $M[4] . '</altLabel>';
                else
                    
$title '';
                
$rdf .= "\n\t" '<' $M[1] . '>
        <Concept rdf:about="' 
htmlspecialchars($M[2]) . '">' $title '
        </Concept>
    </' 
$M[1] . '>';
            } elseif (
preg_match('/^(skos|se|skosext):(broaderPartitive|narrowerPartitive|relatedPartOf|relatedHasPart)\s+(http:\S+)(\s+)?(\S.+)?$/'$line$M)) {
                if (
array_key_exists(5$M))
                    
$title "\n\t\t\t" '<altLabel>' $M[5] . '</altLabel>';
                else
                    
$title '';
                
$rdf .= "\n\t" '<se:' $M[2] . '>
        <Concept rdf:about="' 
htmlspecialchars($M[3]) . '">' $title '
        </Concept>
    </se:' 
$M[2] . '>';
            } elseif (!
preg_match('|^http:\S+(\s\S.+)?$|'$line)
                    && 
preg_match('|\S+|'$line)) {
                
$rdf .= "\n\t" '<scopeNote>' htmlspecialchars(trim($line)) . '</scopeNote>';
            }
        }
        if (
$concept->category_parent && array_key_exists($concept->category_parent$concepts))
            
$rdf .= "\n\t" '<broader rdf:resource="#c' $concept->category_parent '"/>';
        
$channel preg_replace('|feed/?$|''rdf'get_category_rss_link(false$concept->cat_ID''));
        
$channel preg_replace('|wp-rss2|''wp-rdf'$channel);
        
$rdf .= '
</Concept>
<foaf:Document rdf:about="' 
get_category_link(false$concept->cat_ID'') . '">
    <dc:title>' 
htmlspecialchars(get_bloginfo('name')) . ': ' htmlspecialchars($concept->cat_name) . '</dc:title>
    <dc:subject rdf:resource="' 
'#c' $concept->cat_ID '"/>
    <rdfs:seeAlso>
        <rss:channel rdf:about="' 
htmlspecialchars($channel) . '"/>
    </rdfs:seeAlso>
</foaf:Document>'
;
        foreach (
$lines as $line) {
            if (
preg_match('|^(http://\S+)(\s)?(\S.+)?$|'$line$M)) {
                if (
array_key_exists(3$M))
                    
$title "\n\t" '<dc:title>' $M[3] . '</dc:title>';
                else
                    
$title '';
                
$rdf .= '
<foaf:Document rdf:about="' 
htmlspecialchars($M[1]) . '">' $title '
    <dc:subject rdf:resource="' 
'#c' $concept->cat_ID '"/>
</foaf:Document>'
;
            }
        }
    }
    
$rdf .= apply_filters('skos_scheme_rdf''');
    
$rdf .= '
</rdf:RDF>'
;
    return 
$rdf;
}

function 
get_foaf_output_authors_rdf() {
    
# Generate list of authors.
    
global $wpdb$tableusers$tableposts;
    
$authors $wpdb->get_results('SELECT u.ID as ID, count(post_status) AS pc FROM ' $tableusers ' AS u LEFT JOIN ' $tableposts ' AS p ON u.ID=p.post_author GROUP BY u.ID HAVING pc>0 or u.ID>1');
    
# Create and return FOAF blogroll.
    
$rdf '<?xml version="1.0" encoding="' get_settings('blog_charset') . '"?>
<rdf:RDF
    '
;
    
$rdf .= apply_filters('foaf_output_authors_rdf_namespaces',
            
'xmlns="http://xmlns.com/foaf/0.1/"
    xmlns:foaf="http://xmlns.com/foaf/0.1/"
    xmlns:rss="http://purl.org/rss/1.0/"
    xmlns:admin="http://webns.net/mvcb/"
    xmlns:dc="http://purl.org/dc/elements/1.1/"
    xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"'
);
    
$rdf .= '>
<Document rdf:about="">' 
"\n\t";
    
$rdf .= apply_filters('foaf_output_authors_rdf_document',
            
'<dc:title>Author list for ' htmlspecialchars(get_bloginfo('name')) . '</dc:title>
    <primaryTopic rdf:nodeID="authors"/>
    <admin:generatorAgent rdf:resource="http://www.wasab.dk/morten/blog/archives/2004/07/05/wordpress-plugin-foaf-output?version=1.17"/>'
);
    
$rdf .= '
</Document>
<Group rdf:nodeID="authors">
    <name>Authors at ' 
htmlspecialchars(get_bloginfo('name')) . '</name>
    <made>
        <Document rdf:about="' 
htmlspecialchars(get_bloginfo('siteurl')) . '/">
            <dc:title>' 
htmlspecialchars(get_bloginfo('name')) . '</dc:title>
            <rdfs:seeAlso>
                <rss:channel rdf:about="' 
htmlspecialchars(get_bloginfo('rdf_url')) . '"/>
            </rdfs:seeAlso>
        </Document>
    </made>'
;
    foreach (
$authors as $author) {
        global 
$foaf_output_author;
        
$foaf_output_author 0;
        
$authordata get_foaf_output_author($author->ID);
        if (
$authordata) {
            
$rdf .= '
    <member>
        <Person>'
;
            if (
$authordata->user_firstname)
                
$rdf .= "\n\t\t\t" '<firstName>' htmlspecialchars($authordata->user_firstname) . '</firstName>';
            if (
$authordata->user_lastname)
                
$rdf .= "\n\t\t\t" '<surname>' htmlspecialchars($authordata->user_lastname) . '</surname>';
            if (
$authordata->user_nickname)
                
$rdf .= "\n\t\t\t" '<nick>' htmlspecialchars($authordata->user_nickname) . '</nick>';
            if (
$authordata->user_email)
                
$rdf .= "\n\t\t\t" get_foaf_output_email_property($authordata->user_email);
            
$rdf .= '
            <weblog rdf:resource="' 
htmlspecialchars(get_author_link(0$authordata->ID$authordata->user_nicename)) . '"/>
            <rdfs:seeAlso>
                <PersonalProfileDocument rdf:about="' 
htmlspecialchars(get_foaf_output_profile_uri()) . '"/>
            </rdfs:seeAlso>'
;
            
$rdf .= apply_filters('foaf_output_authors_rdf_person''');
            
$rdf .= '
        </Person>
    </member>'
;
        }
    }
    
$rdf .= '
    <rdfs:seeAlso rdf:resource=""/>
</Group>'
;
    
$rdf .= apply_filters('foaf_output_authors_rdf''');
    
$rdf .= '
</rdf:RDF>'
;
    return 
$rdf;
}

function 
get_foaf_output_email_property($email$imadethis false) {
    
$sha1 '';
    
# Try to calculate SHA1 hash of email URI.
    
if (function_exists('sha1'))
        
$sha1 sha1('mailto:' $email);
    else if (
function_exists('mhash'))
        
$sha1 bin2hex(mhash(MHASH_SHA1'mailto:' $email));
    
# If no hash, return foaf:mbox instead of foaf:mbox_sha1sum.
    
if ('' == $sha1) {
        if (
$imadethis)
            return 
'foaf:mbox mailto:' $email;
        else
            return 
'<foaf:mbox rdf:resource="mailto:' $email '"/>';
    } else {
        if (
$imadethis)
            return 
"foaf:mbox_sha1sum '$sha1'";
        else
            return 
'<foaf:mbox_sha1sum>' $sha1 '</foaf:mbox_sha1sum>';
    }
}

function 
get_foaf_output_interests($all=false) {
    
# Return array of categories deemed interesting.
    
global $wpdb$authordata$tablecategories$tablepost2cat$tableposts$cache_categories;
    
$interests = array();
    
$cats $wpdb->get_results("SELECT DISTINCT
            cat_ID, cat_name, category_nicename, category_description, category_parent, count(ID) as post_count
            FROM 
$tablecategories$tablepost2cat$tableposts
            WHERE category_id = cat_ID AND post_id = ID AND post_author = "
            
$authordata->ID ' AND post_status =  "publish"
            GROUP BY category_id ORDER BY cat_name'
);
    if (
is_array($cats) && sizeof($cats)) {
        foreach (
$cats as $cat) {
            
$lines preg_split('|(\r?\n)+|'$cat->category_description);
            foreach (
$lines as $line) {
                if (
preg_match('|^http:\S+(\s\S.+)?$|'$line) || $all) {
                    
$interests[$cat->cat_ID] = $cat;
                    break;
                }
            }
        }
    }
    
$cats $wpdb->get_results("SELECT * FROM $tablecategories");
    if (
is_array($cats) && sizeof($cats)) {
        foreach (
$cats as $cat)
            
$cache_categories[$cat->cat_ID] = $cat;
    }
    return 
$interests;
}

function 
get_foaf_output_profile_page($text '') {
    global 
$paged$author$author_name$posts$post;
    
# If this is the first post on (first) author page, prepend HTML profile.
    
if ((!$paged || == $paged) && ($author || $author_name) && $posts && sizeof($posts) && $posts[0]->ID == $post->ID)
        
$text get_foaf_output_profile_html() . $text;
    return 
$text;
}

function 
foaf_output_head() {
    global 
$foaf_output_author;
    
# Add link to author list "blogroll".
    
$channel preg_replace('|/feed/?.*?$|''/feed/foaf'get_bloginfo('rss_url'));
    echo 
'<link rel="meta" type="application/rdf+xml" title="FOAF" href="' $channel '" />
'
;
    
# Add link to FOAF profile, if relevant.
    
$uri get_foaf_output_profile_uri();
    if (
'' != $uri)
        echo 
'
    <meta name="foaf:maker" content="' 
get_foaf_output_email_property($foaf_output_author->user_emailtrue) . '"/>
    <link rel="meta" type="application/rdf+xml" title="SKOS" href="' 
get_skos_scheme_document_uri($author_id) . '" />
    <link rel="meta" type="application/rdf+xml" title="FOAF" href="' 
$uri '" />
    <link rel="author" href="' 
get_foaf_output_profile_uri(''false) . '" />
'
;
}

function 
get_foaf_output_profile_link($author_id ''$listitem true) {
    
# Return link to FOAF as linked item, if relevant.
    
$uri get_foaf_output_profile_uri($author_id);
    if (
'' != $uri) {
        
$link '<a href="' $uri '" title="FOAF">' __('<abbr title="Friend-of-a-Friend">FOAF</abbr> Profile') . '</a>';
        if (
$listitem)
            return 
'<li>' $link '</li>';
        else
            return 
$link;
    }
    return 
'';
}

function 
get_skos_scheme_link($author_id ''$listitem true) {
    
# Return link to SKOS scheme as linked item, if relevant.
    
$uri get_skos_scheme_document_uri($author_id);
    if (
'' != $uri) {
        
$link '<a href="' $uri '" title="SKOS">' __('<abbr title="Simple Knowledge Organisation Systems">SKOS</abbr> Concept Scheme') . '</a>';
        if (
$listitem)
            return 
'<li>' $link '</li>';
        else
            return 
$link;
    }
    return 
'';
}

function 
get_foaf_output_profile_uri($author_id ''$rdf true) {
    global 
$foaf_output_author;
    
$foaf_output_author get_foaf_output_author($author_id);
    
# Return URI for author's FOAF profile, if relevant.
    
if (!$foaf_output_author)
        return 
'';
    
$link get_author_link(false$foaf_output_author->ID$foaf_output_author->user_nicename);
    if (
$rdf) {
        if (!
preg_match('|/$|'$link))
            
$link .= '/';
        
$link .= 'foaf.rdf';
    }
    return 
$link;
}

function 
get_skos_scheme_document_uri($author_id ''$rdf true) {
    global 
$foaf_output_author;
    
$foaf_output_author get_foaf_output_author($author_id);
    
# Return URI for author's SKOS scheme, if relevant.
    
if (!$foaf_output_author)
        return 
'';
    
$link get_author_link(false$foaf_output_author->ID$foaf_output_author->user_nicename);
    if (
$rdf) {
        if (!
preg_match('|/$|'$link))
            
$link .= '/';
        
$link .= 'skos.rdf';
    }
    return 
$link;
}

function 
get_foaf_output_author($author_id '') {
    global 
$foaf_output_author;
    global 
$single$post$posts;
    global 
$author$author_name;

    
# Is author already set?
    
if ($foaf_output_author && '' == $author_id)
        return 
$foaf_output_author;
    
# Determine which author is "active", who "owns" this page?
    
if (preg_match('|^\d+$|'$author_id)) {
        
$foaf_output_author get_userdata($author_id);
        return 
$foaf_output_author;
    }
    if ((
$single || == sizeof($posts)) && $post) {
        
$foaf_output_author get_userdata($post->post_author);
        return 
$foaf_output_author;
    }
    if (
$posts) {
        
$users=array();
        foreach (
$posts as $p)
            
$users[$p->post_author] = 1;
        if (
== sizeof($users)) {
            
$foaf_output_author get_userdata(key($users));
            return 
$foaf_output_author;
        }
    }
    if (
preg_match('|^\d+$|'$author)) {
        
$foaf_output_author get_userdata($author);
        return 
$foaf_output_author;
    }
    if (
'' != $author_name) {
        
$foaf_output_author get_userdatabynicename($author_name);
        return 
$foaf_output_author;
    }
    return 
0;
}

function 
get_userdatabynicename($user_nicename) {
    global 
$tableusers$cache_userdata$wpdb;
    
$user $wpdb->get_row("SELECT ID FROM $tableusers WHERE user_nicename = '$user_nicename'");
    return 
get_userdata($user->ID);
}

function 
load_plugins() {
    global 
$current_plugins;
    
# Load remaining plugins.
    
$loaded true;
    foreach (
$current_plugins as $plugin) {
        if (!
$loaded && file_exists(ABSPATH 'wp-content/plugins/' $plugin))
            include_once(
ABSPATH 'wp-content/plugins/' $plugin);
        if (
$loaded && __FILE__ == ABSPATH 'wp-content/plugins/' $plugin)
            
$loaded false;
    }
}

function 
conditional_get() {
    
# HTTP section copied from wp-blog-header.php.
    // We're showing a feed, so WP is indeed the only thing that last changed
    
$wp_last_modified mysql2date('D, d M Y H:i:s'get_lastpostmodified('GMT'), 0).' GMT';
    
$wp_etag '"' md5($wp_last_modified) . '"';
    @
header('Last-Modified: ' $wp_last_modified);
    @
header('ETag: ' $wp_etag);

    
// Support for Conditional GET
    
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']))
        
$client_last_modified $_SERVER['HTTP_IF_MODIFIED_SINCE'];
    else
        
$client_last_modified false;
    if (isset(
$_SERVER['HTTP_IF_NONE_MATCH']))
        
$client_etag stripslashes($_SERVER['HTTP_IF_NONE_MATCH']);
    else
        
$client_etag false;
    if ( (
$client_last_modified && $client_etag) ?
            ((
$client_last_modified == $wp_last_modified) && ($client_etag == $wp_etag)) :
            ((
$client_last_modified == $wp_last_modified) || ($client_etag == $wp_etag)) ) {
        if ( 
preg_match('/cgi/',php_sapi_name()) ) {
            
header('HTTP/1.1 304 Not Modified');
            echo 
"\r\n\r\n";
        } else {
            if (
version_compare(phpversion(),'4.3.0','>='))
                
header('Not Modified'TRUE304);
            else
                
header('HTTP/1.1 304 Not Modified');
        }
        exit;
    }
}

# Is FOAF profile wanted?
$foaf_output_author 0;
if (
preg_match('#^([^/]+)/(foaf|skos)\.rdf$#', @$_GET['author_name'], $M))
    
$foaf_output_author get_userdatabynicename($M[1]);
else if (
preg_match('#^(\d+)/(foaf|skos)\.rdf$#', @$_GET['author'], $M))
    
$foaf_output_author get_userdata($M[1]);
else if (
preg_match('#^foaf$#', @$_GET['feed']))
    
$foaf_output_author = -1;
if (
$foaf_output_author) {
    
# Load remaining plugins.
    
load_plugins();

    
# Set content type.
    
header('Content-Type: application/rdf+xml');

    
# Handle conditional GETs.
    
conditional_get();

    
# Output RDF/XML...
    
if ('skos' == $M[2])
        print 
get_skos_scheme_rdf($foaf_output_author->ID);
    elseif (-
1==$foaf_output_author)
        print 
get_foaf_output_authors_rdf();
    else
        print 
get_foaf_output_profile_rdf($foaf_output_author->ID);
    exit;
}

# Add filter to RSS category, for foaf:topic statements etc.
add_filter('the_category_rss''foaf_output_the_category_rss');

# Add auto discovery link to FOAF in HEAD.
add_action('wp_head''foaf_output_head');

# Ugly API hack to output HTML profile.
add_filter('the_date''get_foaf_output_profile_page');

?>