1<?php
2/**
3 * SIOC Exporter API.
4 *
5 * Allow people to easilly create their own SIOC Exporter for any PHP application
6 *
7 * @package sioc_inc
8 * @author  Alexandre Passant <alex@passant.org>
9 * @author  Uldis Bojars <captsolo@gmail.com> (adaptation to PHP4)
10 * @author  Thomas Schandl <tom.schandl@gmail.com> (addition of SIOCThread)
11 * @author  Fabrizio Orlandi <fabrizio.orlandi@deri.org> (addition of SIOCWIki SIOCWikiArticle SIOCCategory)
12 *
13 * @phpcs:disable PSR1.Classes.ClassDeclaration.MultipleClasses
14 */
15
16define('AUTHORS_NODE', 'authors');
17define('EXPORTER_URL', 'http://wiki.sioc-project.org/index.php/PHPExportAPI');
18define('EXPORTER_VERSION', '1.01');
19
20/**
21 * Main exporter class.
22 *
23 * Generates RDF/XML content of SIOC export.
24 * - Sets up parameters for generating RDF/XML
25 * - Sets up parameters for SIOC URL generation
26 */
27class SIOCExporter {
28
29    public $profile_url;
30    private $title;
31    private $blog_url;
32    private $sioc_url;
33    private $encoding;
34    private $generator;
35    private $urlseparator;
36    private $urlequal;
37    private $url4type; // e.g. type   or   sioc_type
38    private $url4id; // TS e. g.  id    or sioc_id
39    private $url4page;
40    // TS: if true: appends the "type" of a class to the url4id in order to compose the string for the "id part"
41    // of the siocURL. e. g. for a forum that could produce "forum_id=" or "forum_sioc_id="
42    private $url_usetype;
43    private $url_suffix; // TS:  custom parameter to be appended at the end of a siocURL
44    private $type_table;
45    private $ignore_suffix; // TS: for types in this table the url_suffix  won't be appended to their siocURL
46    private $export_email;
47    private $objects;
48
49    public function __construct() {
50        $this->urlseparator  = '&';
51        $this->urlequal      = '=';
52        $this->url4type      = 'type';
53        $this->url4id        = 'id';
54        $this->url4page      = 'page';
55        $this->url_usetype   = true;
56        $this->url_suffix    = '';
57        $this->type_table    = array();
58        $this->ignore_suffix = array();
59        $this->export_email  = false;
60        $this->encoding      = 'UTF-8';
61        $this->objects       = array();
62    }
63
64    public function setURLParameters(
65        $type = 'type',
66        $id = 'id',
67        $page = 'page',
68        $url_usetype = true,
69        $urlseparator = '&',
70        $urlequal = '=',
71        $suffix = ''
72    ) {
73        $this->urlseparator = $urlseparator;
74        $this->urlequal     = $urlequal;
75        $this->url4type     = $type;
76        $this->url4id       = $id;
77        $this->url4page     = $page;
78        $this->url_usetype  = $url_usetype;
79        $this->url_suffix   = $suffix;
80    }
81
82    public function setParameters($title, $url, $sioc_url, $encoding, $generator, $export_email = false) {
83        $this->title        = $title;
84        $this->blog_url     = $url;
85        $this->sioc_url     = $sioc_url;
86        $this->encoding     = $encoding;
87        $this->generator    = $generator;
88        $this->export_email = $export_email;
89    }
90
91    // Assigns some objects to the exporter
92    public function addObject(&$obj) {
93        $this->objects[] = &$obj;
94    }
95
96    // TS: Used to replace url4id in the siocURL for a given type (site, forum, etc.) with a
97    // parameter ($name) of your choice
98    // E.g. b2evo exporter uses "blog=" instead of "sioc_id=" in the siocURL of a forum
99    public function setURLTypeParm($type, $name) {
100        $this->type_table[$type] = $name;
101    }
102
103    public function setSuffixIgnore($type) {
104        $this->ignore_suffix[$type] = 1;
105    }
106
107    public function siocURL($type, $id, $page = "") {
108        $type_part = $this->url4type . $this->urlequal . $type;
109
110        if($id) {
111            if(isset($this->type_table[$type]))
112                $myID = $this->type_table[$type];
113            else
114                $myID = (($this->url_usetype) ? $type . '_' : '') . $this->url4id;
115
116            $id_part = $this->urlseparator . $myID . $this->urlequal . $id;
117        } else {
118            $id_part = '';
119        }
120
121        ($page) ? $page_part = $this->urlseparator . $this->url4page . $this->urlequal . $page : $page_part = '';
122
123        ($this->url_suffix && !isset($this->ignore_suffix[$type])) ? $suffix = $this->urlseparator
124            . $this->url_suffix : $suffix = '';
125
126        $siocURL = $this->sioc_url . $type_part . $id_part . $page_part . $suffix;
127        return clean($siocURL, true);
128    }
129
130    public function export($rdf_content = '') {
131        header('Content-Type: application/rdf+xml; charset=' . $this->encoding, true, 200);
132        echo $this->makeRDF($rdf_content);
133    }
134
135    public function makeRDF($rdf_content = '') {
136        $rdf = '<?xml version="1.0" encoding="' . $this->encoding . '" ?>' . "\n";
137        $rdf .= '
138<rdf:RDF
139    xmlns="http://xmlns.com/foaf/0.1/"
140    xmlns:foaf="http://xmlns.com/foaf/0.1/"
141    xmlns:admin="http://webns.net/mvcb/"
142    xmlns:content="http://purl.org/rss/1.0/modules/content/"
143    xmlns:dc="http://purl.org/dc/elements/1.1/"
144    xmlns:dcterms="http://purl.org/dc/terms/"
145    xmlns:dcmitype="http://purl.org/dc/dcmitype/"
146    xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
147    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
148    xmlns:sioc="http://rdfs.org/sioc/ns#"
149    xmlns:sioct="http://rdfs.org/sioc/types#"
150    xmlns:owl="http://www.w3.org/2002/07/owl">
151<foaf:Document rdf:about="' . clean($this->profile_url, true) . '">
152	<dc:title>"' . clean($this->title) . '" (SIOC profile)</dc:title>
153	<foaf:primaryTopic rdf:resource="' . clean($this->objects[0]->_url, true) . '"/>
154	<admin:generatorAgent rdf:resource="' . clean($this->generator, true) . '"/>
155	<admin:generatorAgent rdf:resource="' . clean(EXPORTER_URL, true) . '?version=' . EXPORTER_VERSION . '"/>
156</foaf:Document>' . "\n";
157        if($rdf_content) {
158            $rdf .= $rdf_content;
159        }
160        if(count($this->objects)) {
161            foreach($this->objects as $object) {
162                if($object) {
163                    $rdf .= $object->getContent($this);
164                }
165            }
166        }
167        $rdf .= "\n</rdf:RDF>";
168        return $rdf;
169    }
170}
171
172/**
173 * Generic SIOC Object
174 *
175 * All SIOC objects are derived from this.
176 */
177class SIOCObject {
178    protected $note = '';
179
180    public function addNote($note) {
181        $this->note = $note;
182    }
183
184    public function getContent(&$exp): string {
185        $rdf = "<sioc:Object>\n";
186        $rdf .= "    <rdfs:comment>Generic SIOC Object</rdfs:comment>\n";
187        $rdf .= "</sioc:Object>\n";
188        return $rdf;
189    }
190}
191
192/**
193 * SIOC::Site object
194 *
195 * Contains information about main SIOC page including:
196 *  - site description
197 *  - list of forums
198 *  - list of users
199 */
200class SIOCSite extends SIOCObject {
201
202    private $type = 'site';
203
204    private $url;
205    private $name;
206    private $description;
207    private $forums;
208    private $users;
209    private $page;
210    private $next_users;
211    private $next_forums;
212    private $usergroup_uri;
213
214    public function __construct($url, $name, $description, $page = '', $usergroup_uri = '') {
215        $this->url           = $url;
216        $this->name          = $name;
217        $this->description   = $description;
218        $this->forums        = array();
219        $this->users         = array();
220        $this->page          = $page;
221        $this->next_users    = false;
222        $this->next_forums   = false;
223        $this->usergroup_uri = $usergroup_uri;
224    }
225
226    public function addForum($id, $url) {
227        $this->forums[$id] = $url;
228    }
229
230    public function addUser($id, $url) {
231        $this->users[$id] = $url;
232    }
233
234    public function setNextPageUsers($next) {
235        $this->next_users = $next;
236    }
237
238    public function setNextPageForums($next) {
239        $this->next_forums = $next;
240    }
241
242    public function getContent(&$exp): string {
243        $rdf = "<sioc:Site rdf:about=\"" . clean($this->url) . "\">\n";
244        $rdf .= "    <dc:title>" . clean($this->name) . "</dc:title>\n";
245        $rdf .= "    <dc:description>" . clean($this->description) . "</dc:description>\n";
246        $rdf .= "    <sioc:link rdf:resource=\"" . clean($this->url) . "\"/>\n";
247        if($this->forums) {
248            foreach($this->forums as $id => $url) {
249                $rdf .= "    <sioc:host_of rdf:resource=\"" . clean($url) . "\"/>\n";
250            }
251        }
252        if($this->next_forums) {
253            $rdf .= "    <rdfs:seeAlso rdf:resource=\"" . $exp->siocURL('site', "", $this->page + 1) . "\"/>\n";
254        }
255        if($this->usergroup_uri) {
256            $rdf .= "    <sioc:has_Usergroup rdf:resource=\"" . $this->usergroup_uri . "\"/>\n";
257        } else {
258            $rdf .= "    <sioc:has_Usergroup rdf:nodeID=\"" . AUTHORS_NODE . "\"/>\n";
259        }
260        $rdf .= "</sioc:Site>\n";
261        // Forums
262        if($this->forums) {
263            $rdf .= "\n";
264            foreach($this->forums as $id => $url) {
265                $rdf .= '<sioc:Forum rdf:about="' . clean($url) . "\">\n";
266                $rdf .= "    <sioc:link rdf:resource=\"" . clean($url) . "\"/>\n";
267                $rdf .= "    <rdfs:seeAlso rdf:resource=\"" . $exp->siocURL('forum', $id) . "\"/>\n";
268                $rdf .= "</sioc:Forum>\n";
269            }
270        }
271        // Usergroup
272        if($this->users) {
273            $rdf .= "\n";
274            if($this->usergroup_uri) {
275                $rdf .= '<sioc:UserAccountgroup rdf:about="' . $this->usergroup_uri . "\">\n";
276            } else {
277                $rdf .= '<sioc:UserAccountgroup rdf:nodeID="' . AUTHORS_NODE . "\">\n";
278            }
279            $rdf .= "    <sioc:name>Authors for \"" . clean($this->name) . "\"</sioc:name>\n";
280            foreach($this->users as $id => $url) {
281                $rdf .= "    <sioc:has_member>\n";
282                $rdf .= "        <sioc:UserAccount rdf:about=\"" . clean($url) . "\">\n";
283                $rdf .= "            <rdfs:seeAlso rdf:resource=\"" . $exp->siocURL('user', $id) . "\"/>\n";
284                $rdf .= "        </sioc:UserAccount>\n";
285                $rdf .= "    </sioc:has_member>\n";
286            }
287            if($this->next_users) {
288                $rdf .= "    <rdfs:seeAlso rdf:resource=\"" . $exp->siocURL('site', "", $this->page + 1) . "\"/>\n";
289            }
290            $rdf .= "</sioc:UserAccountgroup>\n";
291        }
292
293        return $rdf;
294    }
295}
296
297// Export detaille d'un utilisateur
298
299/**
300 * SIOC::User object
301 *
302 * Contains user profile information
303 */
304class SIOCUser extends SIOCObject {
305
306    private $type = 'user';
307
308    private $id;
309    private $nick;
310    private $uri;
311    private $name;
312    private $email;
313    private $sha1;
314    private $homepage;
315    private $foaf_uri;
316    private $role;
317    private $sioc_url;
318    private $foaf_url;
319
320    public function __construct(
321        $id,
322        $uri,
323        $name,
324        $email,
325        $homepage = '',
326        $foaf_uri = '',
327        $role = false,
328        $nick = '',
329        $sioc_url = '',
330        $foaf_url = ''
331    ) {
332        $this->id   = $id;
333        $this->uri  = $uri;
334        $this->name = $name;
335
336        if(preg_match_all('/^.+@.+\..+$/Ui', $email, $check, PREG_SET_ORDER)) {
337            if(preg_match_all('/^mailto:(.+@.+\..+$)/Ui', $email, $matches, PREG_SET_ORDER)) {
338                $this->email = $email;
339                $this->sha1  = sha1($email);
340            } else {
341                $this->email = "mailto:" . $email;
342                $this->sha1  = sha1("mailto:" . $email);
343            }
344        }
345        $this->homepage = $homepage;
346        $this->foaf_uri = $foaf_uri;
347        $this->_url     = $foaf_uri;
348        $this->role     = $role;
349        $this->nick     = $nick;
350        $this->foaf_url = $foaf_url;
351        $this->sioc_url = $sioc_url;
352    }
353
354    public function getContent(&$exp): string {
355        $rdf = "<foaf:Person rdf:about=\"" . clean($this->foaf_uri) . "\">\n";
356        if($this->name) {
357            $rdf .= "    <foaf:name>" . $this->name . "</foaf:name>\n";
358        }
359        if($this->email) {
360            $rdf .= "    <foaf:mbox_sha1sum>" . $this->sha1 . "</foaf:mbox_sha1sum>\n";
361        }
362        if($this->foaf_url) {
363            $rdf .= "    <rdfs:seeAlso rdf:resource=\"" . $this->foaf_url . "\"/>\n";
364        }
365        $rdf .= "    <foaf:holdsAccount>\n";
366        $rdf .= "        <sioc:UserAccount rdf:about=\"" . clean($this->uri) . "\">\n";
367        if($this->nick) {
368            $rdf .= "            <sioc:name>" . $this->nick . "</sioc:name>\n";
369        }
370        if($this->email) {
371            if($exp->_export_email) {
372                $rdf .= "            <sioc:email rdf:resource=\"" . $this->email . "\"/>\n";
373            }
374            $rdf .= "            <sioc:email_sha1>" . $this->sha1 . "</sioc:email_sha1>\n";
375        }
376        if($this->role) {
377            $rdf .= "            <sioc:has_function>\n";
378            $rdf .= "                <sioc:Role>\n";
379            $rdf .= "                    <sioc:name>" . $this->role . "</sioc:name>\n";
380            $rdf .= "                </sioc:Role>\n";
381            $rdf .= "            </sioc:has_function>\n";
382        }
383        if($this->sioc_url) {
384            $rdf .= "            <rdfs:seeAlso rdf:resource=\"" . $this->sioc_url . "\"/>\n";
385        }
386        $rdf .= "        </sioc:UserAccount>\n";
387        $rdf .= "    </foaf:holdsAccount>\n";
388        $rdf .= "</foaf:Person>\n";
389        return $rdf;
390    }
391}
392
393// Export detaille d'un utilisateur
394
395/**
396 * SIOC::Thread object
397 *
398 * Contains information about a SIOC Thread in a SIOC Forum
399 * - list of posts in that thread
400 */
401class SIOCThread extends SIOCObject {
402
403    private $type = 'thread';
404    private $id;
405    private $url;
406    private $page;
407    private $posts;
408    private $next;
409    private $views;
410    private $tags;
411    private $related;
412    private $title;
413    private $created;
414    private $parents;
415    /**
416     * @var mixed|string
417     */
418    private $subject;
419
420    public function __construct($id, $url, $page, $views = '', $tags = array(), $subject = '', $created = '') {
421        $this->id      = $id;
422        $this->url     = $url;
423        $this->page    = $page;
424        $this->posts   = array();
425        $this->next    = false;
426        $this->views   = $views;
427        $this->tags    = $tags;
428        $this->related = array();
429        $this->subject = $subject;
430        $this->created = $created;
431    }
432
433    public function addPost($id, $url, $prev = '', $next = '') {
434        $this->posts[$id] = array("url" => $url, "prev" => $prev, "next" => $next);
435    }
436
437    // add links to things that are similar to this via sioc:related_to
438    public function addRelated($id, $url) {
439        $this->related[$id] = $url;
440    }
441
442    public function setNextPage($next) {
443        $this->next = $next;
444    }
445
446    public function addParentForum($id, $url) {
447        $this->parents[$id] = $url;
448    }
449
450    public function getContent(&$exp): string {
451        $rdf = '<sioc:Thread rdf:about="' . clean($this->url) . "\">\n";
452        $rdf .= "    <sioc:link rdf:resource=\"" . clean($this->url) . "\"/>\n";
453        if($this->views) $rdf .= "    <sioc:num_views>" . $this->views . "</sioc:num_views>\n";
454        if($this->note) $rdf .= "    <rdfs:comment>" . $this->note . "</rdfs:comment>\n";
455        if($this->subject) {
456            $rdf .= "    <dc:title>" . $this->subject . "</dc:title>\n";
457        }
458        if($this->created) {
459            $rdf .= "    <dcterms:created>" . $this->created . "</dcterms:created>\n";
460        }
461        if($this->parents) {
462            foreach($this->parents as $id => $uri) {
463                $rdf .= "    <sioc:has_parent>\n";
464                $rdf .= "        <sioc:Forum rdf:about=\"" . clean($uri) . "\">\n";
465                $rdf .= "            <rdfs:seeAlso rdf:resource=\"" . $exp->siocURL('forum', $id) . "\"/>\n";
466                $rdf .= "        </sioc:Forum>\n";
467                $rdf .= "    </sioc:has_parent>\n";
468            }
469        }
470        // here the tags are just used as keywords for dc:subject
471        if($this->tags) {
472            foreach($this->tags as $id => $tag) {
473                $rdf .= "    <dc:subject>" . $tag . "</dc:subject>\n";
474            }
475        }
476        // here the tags are used by creating a tag object with a blank node, with the keyword as moat:name - if you
477        // use this insert prefixes for moat and tags
478        // if ($this->tags) {
479        // $i=1;
480        // foreach ($this->tags as $id => $tag) {
481        // $rdf .= "    <tags:taggedWithTag>\n";
482        // $rdf .= "        <moat:tag rdf:nodeID=\"b$i\">\n";
483        // // actually, the best way is to have 'reference URIs' for tags, e.g. URIs for all the platform
484        // (http://tags.example.org/tag/soccer
485        // $rdf .= "            <moat:name>" . $tag . "</moat:name>\n";
486        // $rdf .= "        </moat:tag>\n";
487        // $rdf .= "    </moat:taggedWithTag>\n";
488        // $i++;
489        // }
490        // }
491
492        // here the tags are used are used for sioc:topic, each topic needs to have a URI
493        /*if($this->tags) {
494                foreach($this->tags as $url=>$topic) {
495                    $rdf .= "    <sioc:topic rdfs:label=\"$topic\" rdf:resource=\"" . clean($url) ."\"/>\n";
496                }
497            }
498            */
499        if($this->related) {
500            foreach($this->related as $id => $url) {
501                $rdf .= "    <sioc:related_to>\n";
502                $rdf .= "        <sioc:Thread rdf:about=\"" . clean($url) . "\"/>\n";
503                $rdf .= "    </sioc:related_to>\n"; // todo - each topic needs to have a URI
504            }
505        }
506
507        if($this->posts) {
508            foreach($this->posts as $id => $data) {
509                $rdf .= "    <sioc:container_of>\n";
510                $rdf .= "        <sioc:Post rdf:about=\"" . clean($data[url]) . "\">\n";
511                $rdf .= "            <rdfs:seeAlso rdf:resource=\"" . $exp->siocURL('post', $id) . "\"/>\n";
512                if($data[prev]) {
513                    $rdf .= "            <sioc:previous_by_date rdf:resource=\"" . clean($data[prev]) . "\"/>\n";
514                }
515                if($data[next]) {
516                    $rdf .= "            <sioc:next_by_date rdf:resource=\"" . clean($data[next]) . "\"/>\n";
517                }
518                $rdf .= "        </sioc:Post>\n";
519                $rdf .= "    </sioc:container_of>\n";
520            }
521        }
522        if($this->next) {
523            $rdf .= "\r<rdfs:seeAlso rdf:resource=\"" . $exp->siocURL('thread', $this->id, $this->page + 1)
524                . "\"/>\n";
525        }
526        $rdf .= "</sioc:Thread>\n";
527        return $rdf;
528    }
529}
530
531// Export d'un forum avec une liste de posts -variable (next with seeAlso)
532
533/**
534 * SIOC::Forum object
535 *
536 * Contains information about SIOC Forum (blog, ...):
537 *  - description of a forum
538 *  - list of posts within a forum [partial, paged]
539 */
540class SIOCForum extends SIOCObject {
541
542    private $type = 'forum';
543
544    private $id;
545    private $url;
546    private $page;
547    private $posts;
548    private $next;
549    private $blog_title;
550    private $description;
551    private $threads;
552    private $parents;
553    private $creator;
554    private $administrator;
555    /**
556     * @var array|mixed
557     */
558    private $links;
559
560    public function __construct(
561        $id,
562        $url,
563        $page,
564        $title = '',
565        $descr = '',
566        $type = 'sioc:Forum',
567        $creator = '',
568        $admin = '',
569        $links = array()
570    ) {
571        $this->id            = $id;
572        $this->url           = $url;
573        $this->page          = $page;
574        $this->posts         = array();
575        $this->next          = false;
576        $this->blog_title    = $title;
577        $this->description   = $descr;
578        $this->threads       = array();
579        $this->parents       = array();
580        $this->_type         = $type;
581        $this->creator       = $creator;
582        $this->administrator = $admin;
583        $this->links         = $links;
584    }
585
586    public function addPost($id, $url) {
587        $this->posts[$id] = $url;
588    }
589
590    public function addThread($id, $url) {
591        $this->threads[$id] = $url;
592    }
593
594    public function addParentForum($id, $url) {
595        $this->parents[$id] = $url;
596    }
597
598    public function setNextPage($next) {
599        $this->next = $next;
600    }
601
602    public function getContent(&$exp): string {
603        $rdf = '<' . $this->_type . ' rdf:about="' . clean($this->url) . "\">\n";
604        if($this->_type != 'sioc:Forum') $rdf .= "    <rdf:type rdf:resource=\"http://rdfs.org/sioc/ns#Forum\" />\n";
605        $rdf .= "    <sioc:link rdf:resource=\"" . clean($this->url) . "\"/>\n";
606        if($this->blog_title) $rdf .= "    <dc:title>" . $this->blog_title . "</dc:title>\n";
607        if($this->description) $rdf .= "    <dc:description>" . $this->description . "</dc:description>\n";
608        if($this->note) $rdf .= "    <rdfs:comment>" . $this->note . "</rdfs:comment>\n";
609
610        if($this->parents) {
611            foreach($this->parents as $id => $uri) {
612                $rdf .= "    <sioc:has_parent>\n";
613                $rdf .= "        <sioc:Forum rdf:about=\"" . clean($uri) . "\">\n";
614                $rdf .= "            <rdfs:seeAlso rdf:resource=\"" . $exp->siocURL('forum', $id) . "\"/>\n";
615                $rdf .= "        </sioc:Forum>\n";
616                $rdf .= "    </sioc:has_parent>\n";
617            }
618        }
619
620        if($this->threads) {
621            foreach($this->threads as $id => $uri) {
622                $rdf .= "    <sioc:parent_of>\n";
623                $rdf .= "        <sioc:Thread rdf:about=\"" . clean($uri) . "\">\n";
624                $rdf .= "            <rdfs:seeAlso rdf:resource=\"" . $exp->siocURL('thread', $id) . "\"/>\n";
625                $rdf .= "        </sioc:Thread>\n";
626                $rdf .= "    </sioc:parent_of>\n";
627            }
628        }
629
630        if($this->posts) {
631            foreach($this->posts as $id => $url) {
632                $rdf .= "    <sioc:container_of>\n";
633                $rdf .= "        <sioc:Post rdf:about=\"" . clean($url) . "\">\n";
634                $rdf .= "            <rdfs:seeAlso rdf:resource=\"" . $exp->siocURL('post', $id) . "\"/>\n";
635                $rdf .= "        </sioc:Post>\n";
636                $rdf .= "    </sioc:container_of>\n";
637            }
638        }
639
640        if($this->creator) {
641            if($this->creator->_id) {
642                $rdf .= "    <sioc:has_creator>\n";
643                $rdf .= "        <sioc:UserAccount rdf:about=\"" . clean($this->creator->_uri) . "\">\n";
644                if($this->creator->_sioc_url) {
645                    $rdf .= "            <rdfs:seeAlso rdf:resource=\"" . $this->creator->_sioc_url . "\"/>\n";
646                } else {
647                    $rdf .= "            <rdfs:seeAlso rdf:resource=\"" . $exp->siocURL('user', $this->creator->_id)
648                        . "\"/>\n";
649                }
650                $rdf .= "        </sioc:UserAccount>\n";
651                $rdf .= "    </sioc:has_creator>\n";
652                $rdf .= "    <foaf:maker>\n";
653                $rdf .= "        <foaf:Person rdf:about=\"" . clean($this->creator->_foaf_uri) . "\">\n";
654                if($this->creator->_foaf_url) {
655                    $rdf .= "            <rdfs:seeAlso rdf:resource=\"" . $this->creator->_foaf_url . "\"/>\n";
656                } else {
657                    $rdf .= "            <rdfs:seeAlso rdf:resource=\"" . $exp->siocURL('user', $this->creator->_id)
658                        . "\"/>\n";
659                }
660                $rdf .= "        </foaf:Person>\n";
661                $rdf .= "    </foaf:maker>\n";
662            } else {
663                $rdf .= "    <foaf:maker>\n";
664                $rdf .= "        <foaf:Person";
665                if($this->creator->_name) {
666                    $rdf .= " foaf:name=\"" . $this->creator->_name . "\"";
667                }
668                if($this->creator->_sha1) {
669                    $rdf .= " foaf:mbox_sha1sum=\"" . $this->creator->_sha1 . "\"";
670                }
671                if($this->creator->_name) {
672                    $rdf .= ">\n            <foaf:homepage rdf:resource=\"" . $this->creator->_homepage
673                        . "\"/>\n        </foaf:Person>\n";
674                } else {
675                    $rdf .= "/>\n";
676                }
677                $rdf .= "    </foaf:maker>\n";
678            }
679        }
680
681        if($this->administrator) {
682            if($this->administrator->_id) {
683                $rdf .= "    <sioc:has_administrator>\n";
684                $rdf .= "        <sioc:UserAccount rdf:about=\"" . clean($this->administrator->_uri) . "\">\n";
685                if($this->administrator->_sioc_url) {
686                    $rdf .= "            <rdfs:seeAlso rdf:resource=\"" . $this->administrator->_sioc_url . "\"/>\n";
687                } else $rdf .= "            <rdfs:seeAlso rdf:resource=\""
688                                                        . $exp->siocURL('user', $this->administrator->_id) . "\"/>\n";
689                $rdf .= "        </sioc:UserAccount>\n";
690                $rdf .= "    </sioc:has_administrator>\n";
691            }
692        }
693        if($this->links) {
694            foreach($this->links as $url => $link) {
695                $rdf .= "    <sioc:links_to rdfs:label=\"$link\" rdf:resource=\"" . clean($url) . "\"/>\n";
696            }
697        }
698
699        if($this->next) {
700            $rdf .= "\r<rdfs:seeAlso rdf:resource=\"" . $exp->siocURL('forum', $this->id, $this->page + 1) . "\"/>\n";
701        }
702        $rdf .= "</" . $this->_type . ">";
703
704        return $rdf;
705    }
706}
707
708/**
709 * SIOC::Post object
710 *
711 * Contains information about a post
712 */
713class SIOCPost extends SIOCObject {
714
715    private $type = 'post';
716
717    private $url;
718    private $subject;
719    private $content;
720    private $encoded;
721    private $creator;
722    private $created;
723    private $updated;
724    private $topics;
725    private $links;
726    private $comments;
727    private $reply_of;
728    private $has_part;
729
730    public function __construct(
731        $url,
732        $subject,
733        $content,
734        $encoded,
735        $creator,
736        $created,
737        $updated = "",
738        $topics = array(),
739        $links = array(),
740        $type = 'sioc:Post',
741        $has_part = array()
742    ) {
743        $this->url      = $url;
744        $this->subject  = $subject;
745        $this->content  = $content;
746        $this->encoded  = $encoded;
747        $this->creator  = $creator;
748        $this->created  = $created;
749        $this->updated  = $updated;
750        $this->topics   = $topics;
751        $this->links    = $links;
752        $this->comments = array();
753        $this->reply_of = array();
754        $this->_type    = $type;
755        $this->has_part = $has_part;
756    }
757
758    public function addComment($id, $url) {
759        $this->comments[$id] = $url;
760    }
761
762    public function addReplyOf($id, $url) {
763        $this->reply_of[$id] = $url;
764    }
765
766    public function getContent(&$exp): string {
767        $rdf = '<' . $this->_type . " rdf:about=\"" . clean($this->url) . "\">\n";
768        if($this->_type != 'sioc:Post') $rdf .= "    <rdf:type rdf:resource=\"http://rdfs.org/sioc/ns#Post\" />\n";
769        if($this->subject) {
770            $rdf .= "    <dc:title>" . $this->subject . "</dc:title>\n";
771        }
772        if($this->creator) {
773            if($this->creator->_id) {
774                $rdf .= "    <sioc:has_creator>\n";
775                $rdf .= "        <sioc:UserAccount rdf:about=\"" . clean($this->creator->_uri) . "\">\n";
776                if($this->creator->_sioc_url) {
777                    $rdf .= "            <rdfs:seeAlso rdf:resource=\"" . $this->creator->_sioc_url . "\"/>\n";
778                } else {
779                    $rdf .= "            <rdfs:seeAlso rdf:resource=\"" . $exp->siocURL('user', $this->creator->_id)
780                        . "\"/>\n";
781                }
782                $rdf .= "        </sioc:UserAccount>\n";
783                $rdf .= "    </sioc:has_creator>\n";
784                $rdf .= "    <foaf:maker>\n";
785                $rdf .= "        <foaf:Person rdf:about=\"" . clean($this->creator->_foaf_uri) . "\">\n";
786                if($this->creator->_foaf_url) {
787                    $rdf .= "            <rdfs:seeAlso rdf:resource=\"" . $this->creator->_foaf_url . "\"/>\n";
788                } else {
789                    $rdf .= "            <rdfs:seeAlso rdf:resource=\"" . $exp->siocURL('user', $this->creator->_id)
790                        . "\"/>\n";
791                }
792                $rdf .= "        </foaf:Person>\n";
793                $rdf .= "    </foaf:maker>\n";
794            } else {
795                $rdf .= "    <foaf:maker>\n";
796                $rdf .= "        <foaf:Person";
797                if($this->creator->_name) {
798                    $rdf .= " foaf:name=\"" . $this->creator->_name . "\"";
799                }
800                if($this->creator->_sha1) {
801                    $rdf .= " foaf:mbox_sha1sum=\"" . $this->creator->_sha1 . "\"";
802                }
803                if($this->creator->_name) {
804                    $rdf .= ">\n            <foaf:homepage rdf:resource=\"" . $this->creator->_homepage
805                        . "\"/>\n        </foaf:Person>\n";
806                } else {
807                    $rdf .= "/>\n";
808                }
809                $rdf .= "    </foaf:maker>\n";
810            }
811        }
812        $rdf .= "    <dcterms:created>" . $this->created . "</dcterms:created>\n";
813        if($this->updated and ($this->created != $this->updated)) $rdf .= "    <dcterms:modified>"
814            . $this->updated . "</dcterms:modified>\n";
815        $rdf .= "    <sioc:content>" . pureContent($this->content) . "</sioc:content>\n";
816
817        $rdf .= "    <content:encoded><![CDATA[" . $this->encoded . "]]></content:encoded>\n";
818        if($this->topics) {
819            foreach($this->topics as $url => $topic) {
820                $rdf .= "    <sioc:topic rdfs:label=\"$topic\" rdf:resource=\"" . clean($url) . "\"/>\n";
821            }
822        }
823        if($this->links) {
824            foreach($this->links as $url => $link) {
825                $rdf .= "    <sioc:links_to rdfs:label=\"$link\" rdf:resource=\"" . clean($url) . "\"/>\n";
826            }
827        }
828        if($this->has_part) {
829            foreach($this->has_part as $id => $url) {
830                $rdf .= "    <dcterms:hasPart>\n";
831                $rdf .= "        <dcmitype:Image rdf:about=\"" . clean($url) . "\"/>\n";
832                $rdf .= "    </dcterms:hasPart>\n";
833            }
834        }
835        if($this->reply_of) {
836            foreach($this->reply_of as $id => $url) {
837                $rdf .= "    <sioc:reply_of>\n";
838                $rdf .= "        <sioc:Post rdf:about=\"" . clean($url) . "\">\n";
839                $rdf .= "            <rdfs:seeAlso rdf:resource=\"" . $exp->siocURL('post', $id) . "\"/>\n";
840                $rdf .= "        </sioc:Post>\n";
841                $rdf .= "    </sioc:reply_of>\n";
842            }
843        }
844        if($this->comments) {
845            foreach($this->comments as $id => $url) {
846                $rdf .= "    <sioc:has_reply>\n";
847                $rdf .= "        <sioc:Post rdf:about=\"" . clean($url) . "\">\n";
848                //        if($comments->f('comment_trackback')) $rdf .= "            <sioc:type>"
849                // . POST_TRACKBACK . "</sioc:type>\n";
850                //        else $rdf .= "            <sioc:type>" . POST_COMMENT  . "</sioc:type>\n";
851                $rdf .= "            <rdfs:seeAlso rdf:resource=\"" . $exp->siocURL('comment', $id) . "\"/>\n";
852                $rdf .= "        </sioc:Post>\n";
853                $rdf .= "    </sioc:has_reply>\n";
854            }
855        }
856        $rdf .= "</" . $this->_type . ">\n";
857        return $rdf;
858    }
859}
860
861/**
862 * SIOC::WikiArticle object
863 *
864 * Contains information about a wiki article
865 */
866class SIOCWikiArticle extends SIOCObject {
867
868    private $type = 'sioct:WikiArticle';
869
870    private $url;
871    private $api = null;
872    private $subject;
873    private $redirpage;
874    private $creator;
875    private $created;
876    private $topics;
877    private $links;
878    private $ext_links;
879    private $previous_version;
880    private $next_version;
881    private $latest_version;
882    private $has_discussion;
883    private $has_container;
884
885    public function __construct(
886        $url,
887        $api,
888        $subject,
889        $redir,
890        $user,
891        $created,
892        $prev_vers,
893        $next_vers,
894        $latest_vers,
895        $has_discuss,
896        $container,
897        $topics = array(),
898        $links = array(),
899        $ext_links = array(),
900        $type = 'sioct:WikiArticle'
901    ) {
902        $this->url              = $url;
903        $this->api              = $api;
904        $this->subject          = $subject;
905        $this->redirpage        = $redir;
906        $this->creator          = $user;
907        $this->created          = $created;
908        $this->topics           = $topics;
909        $this->links            = $links;
910        $this->ext_links        = $ext_links;
911        $this->_type            = $type;
912        $this->previous_version = $prev_vers;
913        $this->next_version     = $next_vers;
914        $this->latest_version   = $latest_vers;
915        $this->has_discussion   = $has_discuss;
916        $this->has_container    = $container;
917    }
918
919    public function getContent(&$exp): string {
920        $rdf = '<' . $this->_type . " rdf:about=\"" . clean($this->url) . "\">\n";
921        if($this->subject) {
922            $rdf .= "    <dc:title>" . clean($this->subject) . "</dc:title>\n";
923            if(strcmp($this->has_container, 'http://en.wikipedia.org') === 0) {
924                $rdf .= "    <foaf:primaryTopic rdf:resource=\"" . clean(
925                        'http://dbpedia.org/resource/'
926                        . $this->subject
927                    ) . "\"/>\n";
928            }
929        }
930        if($this->creator->_nick) {
931            /*if ($this->creator->id) {
932                $rdf .= "    <sioc:has_creator>\n";
933                $rdf .= "        <sioc:UserAccount rdf:about=\"" . clean($this->creator->uri) ."\">\n";
934                if($this->creator->sioc_url) { $rdf .= "    <rdfs:seeAlso rdf:resource=\"". $this->creator->sioc_url
935                ."\"/>\n"; }
936                else $rdf .= "            <rdfs:seeAlso rdf:resource=\"" . $exp->siocURL('user', $this->creator->id)
937                        . "\"/>\n";
938                $rdf .= "        </sioc:UserAccount>\n";
939                $rdf .= "    </sioc:has_creator>\n";
940                $rdf .= "    <foaf:maker>\n";
941                $rdf .= "        <foaf:Person rdf:about=\"" . clean($this->creator->foaf_uri) ."\">\n";
942                if($this->creator->foaf_url) { $rdf .= "    <rdfs:seeAlso rdf:resource=\"". $this->creator->foaf_url
943                        ."\"/>\n"; }
944                else $rdf .= "            <rdfs:seeAlso rdf:resource=\"" . $exp->siocURL('user', $this->creator->id)
945                        . "\"/>\n";
946                $rdf .= "        </foaf:Person>\n";
947                $rdf .= "    </foaf:maker>\n";
948            } else {*/
949            $rdf .= "    <sioc:has_creator>\n";
950            $rdf .= "        <sioc:UserAccount rdf:about=\"" . clean($this->creator->_uri) . "\">\n";
951            $rdf .= "            <rdfs:seeAlso rdf:resource=\"" .
952                clean('http://ws.sioc-project.org/mediawiki/mediawiki.php?wiki=' . $this->creator->_uri);
953            if($this->api) {
954                $rdf .= clean("&api=" . $this->api);
955            }
956            $rdf .= "\"/>\n";
957            $rdf .= "        </sioc:UserAccount>\n";
958            $rdf .= "    </sioc:has_creator>\n";
959            $rdf .= "    <dc:contributor>" . clean($this->creator->_nick) . "</dc:contributor>\n";
960            /*$rdf .= "    <foaf:maker>\n";
961            $rdf .= "        <foaf:Person";
962            if($this->creator->name) $rdf .= " foaf:name=\"" . $this->creator->name ."\"";
963            if($this->creator->sha1) $rdf .= " foaf:mbox_sha1sum=\"" . $this->creator->sha1 ."\"";
964            if($this->creator->homepage) $rdf .= ">\n            <foaf:homepage rdf:resource=\""
965            . $this->creator->homepage ."\"/>\n        </foaf:Person>\n";
966            else $rdf .= "/>\n";
967            $rdf .= "    </foaf:maker>\n";
968        }*/
969        } else {
970            if($this->creator !== 'void') {
971                $rdf .= "    <sioc:has_creator>\n";
972                $rdf .= "        <sioc:UserAccount>\n";
973                $rdf .= "        </sioc:UserAccount>\n";
974                $rdf .= "    </sioc:has_creator>\n";
975            }
976        }
977        if($this->created) {
978            $rdf .= "    <dcterms:created>" . $this->created . "</dcterms:created>\n";
979        }
980        if(is_array($this->topics)) {
981            foreach($this->topics as $topic => $url) {
982                $rdf .= "    <sioc:topic>\n";
983                $rdf .= "        <sioct:Category rdf:about=\"" . clean($url) . "\">\n";
984                $rdf .= "            <rdfs:seeAlso rdf:resource=\"" .
985                    clean('http://ws.sioc-project.org/mediawiki/mediawiki.php?wiki=' . $url);
986                if($this->api) {
987                    $rdf .= clean("&api=" . $this->api);
988                }
989                $rdf .= "\"/>\n";
990                $rdf .= "        </sioct:Category>\n";
991                $rdf .= "    </sioc:topic>\n";
992            }
993        }
994        if(is_array($this->links)) {
995            foreach($this->links as $label => $url) {
996                $rdf .= "    <sioc:links_to>\n";
997                $rdf .= "        <sioct:WikiArticle rdf:about=\"" . clean($url) . "\">\n";
998                $rdf .= "            <rdfs:seeAlso rdf:resource=\"" .
999                    clean('http://ws.sioc-project.org/mediawiki/mediawiki.php?wiki=' . $url);
1000                if($this->api) {
1001                    $rdf .= clean("&api=" . $this->api);
1002                }
1003                $rdf .= "\"/>\n";
1004                $rdf .= "        </sioct:WikiArticle>\n";
1005                $rdf .= "    </sioc:links_to>\n";
1006            }
1007        } else {
1008            if($this->links) {
1009                $rdf .= "    <sioc:links_to>\n";
1010                $rdf .= "        <sioct:WikiArticle rdf:about=\"" . clean($this->links) . "\">\n";
1011                $rdf .= "            <rdfs:seeAlso rdf:resource=\"" .
1012                    clean('http://ws.sioc-project.org/mediawiki/mediawiki.php?wiki=' . $this->links);
1013                if($this->api) {
1014                    $rdf .= clean("&api=" . $this->api);
1015                }
1016                $rdf .= "\"/>\n";
1017                $rdf .= "        </sioct:WikiArticle>\n";
1018                $rdf .= "    </sioc:links_to>\n";
1019            }
1020        }
1021        if(is_array($this->ext_links)) {
1022            foreach($this->ext_links as $label => $url) {
1023                $rdf .= "    <sioc:links_to rdf:resource=\"" . clean($url) . "\"/>\n";
1024            }
1025        }
1026        if($this->previous_version) {
1027            $rdf .= "    <sioc:previous_version>\n";
1028            $rdf .= "        <sioct:WikiArticle rdf:about=\"" . clean($this->previous_version) . "\">\n";
1029            $rdf .= "            <rdfs:seeAlso rdf:resource=\"" .
1030                clean('http://ws.sioc-project.org/mediawiki/mediawiki.php?wiki=' . $this->previous_version);
1031            if($this->api) {
1032                $rdf .= clean("&api=" . $this->api);
1033            }
1034            $rdf .= "\"/>\n";
1035            $rdf .= "        </sioct:WikiArticle>\n";
1036            $rdf .= "    </sioc:previous_version>\n";
1037            /*If there is support for inference and transitivity the following is not needed
1038            $rdf .= "    <sioc:earlier_version>\n";
1039            $rdf .= "        <sioct:WikiArticle rdf:about=\"" . clean($this->previous_version) ."\">\n";
1040            $rdf .= "            <rdfs:seeAlso rdf:resource=\"" .
1041                    clean('http://ws.sioc-project.org/mediawiki/mediawiki.php?wiki='.$this->previous_version);
1042            if ($this->api) {
1043              $rdf .= clean("&api=" . $this->api);
1044            }
1045            $rdf .= "\"/>\n";
1046            $rdf .= "        </sioct:WikiArticle>\n";
1047            $rdf .= "    </sioc:earlier_version>\n";
1048             */
1049        }
1050        if($this->next_version) {
1051            $rdf .= "    <sioc:next_version>\n";
1052            $rdf .= "        <sioct:WikiArticle rdf:about=\"" . clean($this->next_version) . "\">\n";
1053            $rdf .= "            <rdfs:seeAlso rdf:resource=\"" .
1054                clean('http://ws.sioc-project.org/mediawiki/mediawiki.php?wiki=' . $this->next_version);
1055            if($this->api) {
1056                $rdf .= clean("&api=" . $this->api);
1057            }
1058            $rdf .= "\"/>\n";
1059            $rdf .= "        </sioct:WikiArticle>\n";
1060            $rdf .= "    </sioc:next_version>\n";
1061            /*If there is support for inference and transitivity the following is not needed
1062            $rdf .= "    <sioc:later_version>\n";
1063            $rdf .= "        <sioct:WikiArticle rdf:about=\"" . clean($this->next_version) ."\">\n";
1064            $rdf .= "            <rdfs:seeAlso rdf:resource=\"" .
1065                    clean('http://ws.sioc-project.org/mediawiki/mediawiki.php?wiki='.$this->next_version);
1066            if ($this->api) {
1067              $rdf .= clean("&api=" . $this->api);
1068            }
1069            $rdf .= "\"/>\n";
1070            $rdf .= "        </sioct:WikiArticle>\n";
1071            $rdf .= "    </sioc:later_version>\n";
1072             */
1073        }
1074        if($this->latest_version) {
1075            $rdf .= "    <sioc:latest_version>\n";
1076            $rdf .= "        <sioct:WikiArticle rdf:about=\"" . clean($this->latest_version) . "\">\n";
1077            $rdf .= "            <rdfs:seeAlso rdf:resource=\"" .
1078                clean('http://ws.sioc-project.org/mediawiki/mediawiki.php?wiki=' . $this->latest_version);
1079            if($this->api) {
1080                $rdf .= clean("&api=" . $this->api);
1081            }
1082            $rdf .= "\"/>\n";
1083            $rdf .= "        </sioct:WikiArticle>\n";
1084            $rdf .= "    </sioc:latest_version>\n";
1085        }
1086        if($this->has_discussion && (strpos($this->has_discussion, 'Talk:Talk:') == false)) {
1087            $rdf .= "    <sioc:has_discussion>\n";
1088            $rdf .= "        <sioct:WikiArticle rdf:about=\"" . clean($this->has_discussion) . "\">\n";
1089            $rdf .= "            <rdfs:seeAlso rdf:resource=\"" .
1090                clean('http://ws.sioc-project.org/mediawiki/mediawiki.php?wiki=' . $this->has_discussion);
1091            if($this->api) {
1092                $rdf .= clean("&api=" . $this->api);
1093            }
1094            $rdf .= "\"/>\n";
1095            $rdf .= "        </sioct:WikiArticle>\n";
1096            $rdf .= "    </sioc:has_discussion>\n";
1097        }
1098        if($this->has_container) {
1099            $rdf .= "    <sioc:has_container>\n";
1100            $rdf .= "        <sioct:Wiki rdf:about=\"" . clean($this->has_container) . "\"/>\n";
1101            $rdf .= "    </sioc:has_container>\n";
1102        }
1103        if($this->redirpage) {
1104            $rdf .= "    <owl:sameAs rdf:resource=\"" . clean($this->redirpage) . "\"/>\n";
1105            $rdf .= "    <rdfs:seeAlso rdf:resource=\"" .
1106                clean('http://ws.sioc-project.org/mediawiki/mediawiki.php?wiki=' . $this->redirpage);
1107            if($this->api) {
1108                $rdf .= clean("&api=" . $this->api);
1109            }
1110            $rdf .= "\"/>\n";
1111        }
1112
1113        $rdf .= "</" . $this->_type . ">\n";
1114        return $rdf;
1115    }
1116}
1117
1118/**
1119 * SIOC::Wiki object
1120 *
1121 * Contains information about a wiki site
1122 */
1123class SIOCWiki extends SIOCObject {
1124
1125    private $url;
1126    private $type;
1127
1128    public function __construct($url, $type = 'sioct:Wiki') {
1129        $this->url  = $url;
1130        $this->type = $type;
1131    }
1132
1133    public function getContent(&$exp): string {
1134        $rdf = '<' . $this->type . " rdf:about=\"" . clean($this->url) . "\"/>\n";
1135        return $rdf;
1136    }
1137}
1138
1139/**
1140 * SIOC::Category object
1141 *
1142 * Contains information about the category which is object of the sioc:topic property
1143 */
1144class SIOCCategory extends SIOCObject {
1145
1146    private $url;
1147    private $type;
1148
1149    public function __construct($url, $type = 'sioct:Category') {
1150        $this->url  = $url;
1151        $this->type = $type;
1152    }
1153
1154    public function getContent(&$exp): string {
1155        $rdf = '<' . $this->type . " rdf:about=\"" . clean($this->url) . "\"/>\n";
1156        return $rdf;
1157    }
1158}
1159
1160/**
1161 * "Clean" text
1162 *
1163 * Transforms text so that it can be safely put into XML markup
1164 */
1165if(!function_exists('clean')) {
1166    function clean($text, $url = false) {
1167#    return htmlentities( $text );
1168#    return htmlentities2( $text );
1169        // double encoding is preventable now
1170        // $text = htmlspecialchars_decode($text, ENT_COMPAT);
1171        if($url) {
1172            $text = str_replace('&amp;', '&', $text);
1173        }
1174        return htmlspecialchars($text, ENT_COMPAT, 'UTF-8');
1175    }
1176}
1177
1178/**
1179 * HTML Entities 2
1180 *
1181 * Same a HTMLEntities, but avoids double-encoding of entities
1182 */
1183if(!function_exists('htmlentities2')) {
1184    function htmlentities2($myHTML) {
1185        $translation_table          = get_html_translation_table(HTML_ENTITIES, ENT_QUOTES);
1186        $translation_table[chr(38)] = '&';
1187        return preg_replace(
1188            "/&(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,3};)/", "&amp;",
1189            strtr($myHTML, $translation_table)
1190        );
1191        //return htmlentities(strtr(str_replace(' ', '%20', $myHTML), $translation_table));
1192    }
1193}
1194
1195/**
1196 * pureContent
1197 *
1198 * Prepares text-only representation of HTML content
1199 */
1200if(!function_exists('pureContent')) {
1201    function pureContent($content) {
1202        // Remove HTML tags
1203        // May add more cleanup code later, if validation errors are found
1204        return strip_tags($content);
1205    }
1206}
1207