<?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), $links, PREG_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 || 1 == $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_email, true) . '"/>
    <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 || 1 == 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 (
1 == 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', TRUE, 304);
            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');

?>