topics = array(); } public function &getTopics() { return $this->topcis; } } class Topic { private $name; private $description; private $posts; public function __construct($name, $description) { $this->name = $name; $this->description = $description; $this->posts = array(); } public function getName() { return $this->name; } public function getDescription() { return $this->description; } public function &getPosts() { return $this->posts; } } class Post { private $author; private $time; private $title; private $body; private $comments; private $versions; public function __construct($author, $time, $title, $body) { $this->author = $author; $this->time = $time; $this->title = $title; $this->body = $body; $this->comments = array(); $this->versions = array(); } public function getAuthor() { return $this->author; } public function getTime() { return $this->time; } public function getTitle() { return $this->title; } public function getBody() { return $this->body; } public function &getComments() { return $this->comments; } public function &getVersions() { return $this->versions; } } class Comment { private $author; private $time; private $number; private $text; private $comments; private $versions; public function __construct($author, $time, $number, $text) { $this->author = $author; $this->time = $time; $this->number = $number; $this->text = $text; $this->comments = array(); $this->versions = array(); } public function getAuthor() { return $this->author; } public function getTime() { return $this->time; } public function getNumber() { return $this->number; } public function getText() { return $this->text; } public function &getComments() { return $this->comments; } public function &getVersions() { return $this->versions; } } use PHPCR\SimpleCredentials; use PHPCR\NodeType\NodeTypeInterface; use PHPCR\Query\QueryInterface; class ForumException extends Exception { public function __construct($msg) { parent::__construct($msg); } } class ForumManager { // use dash instead of space and special characters in path parts static function nameFixUp($name) { return preg_replace('|[^A-Za-z0-9#-]|', '', preg_replace('|[ /]|', '-', $name)); } // number formats: // #n // #n.m function getAutoNumber($parent) { $num = (string)(count($parent->getNodes('*')) + 1); if($this->isPost($parent)) { return '#' . $num; } else if($this->isComment($parent)) { return $parent->getProperty('number')->getString() . '.' . $num; } else { throw new ForumException('Comment parent is neither post nor comment'); } } // check that can be used to avoid duplicates function alreadyHasChild($parent, $nodename) { return count($parent->getNodes($nodename)) > 0; } // a permanent open session per ForumManager instance private $session; // version support private $verssup; public function __construct($repo, $usr, $pwd, $verssup = true) { // login $this->session = $repo->login(new SimpleCredentials($usr, $pwd)); // ensure forum root exists if(!$this->session->getRootNode()->hasNode('F')) { $this->session->getRootNode()->addNode('F'); } // version support $this->verssup = $verssup; } function getSession() { return $this->session; } function getVerssup() { return $this->verssup; } // test nodes function isTopic($n) { return $n->hasProperty('name') && $n->hasProperty('description'); } function isPost($n) { return $n->hasProperty('title') && $n->hasProperty('body') && $n->hasProperty('author'); } function isComment($n) { return $n->hasProperty('number') && $n->hasProperty('text') && $n->hasProperty('author'); } // find nodes function getAnyNode($path) { return $this->session->getRootNode()->getNode(substr($path, 1)); } // add nodes function addTopic($topicName, $topicDescription) { $parent = $this->getAnyNode('/F'); $nodename = ForumManager::nameFixUp($topicName); if(!$this->alreadyHasChild($parent, $nodename)) { $child = $parent->addNode($nodename); $child->setProperty('name', $topicName); $child->setProperty('description', $topicDescription); $this->session->save(); return '/F/' . $nodename; } else { return null; } } function addPost($path, $postTitle, $postBody, $postAuthor) { $parent = $this->getAnyNode($path); if(!$this->isTopic($parent)) { throw new ForumException('Trying to add post to non-topic'); } $nodename = ForumManager::nameFixUp($postTitle); if(!$this->alreadyHasChild($parent, $nodename)) { $child = $parent->addNode($nodename); if($this->verssup) $child->addMixin(NodeTypeInterface::MIX_VERSIONABLE); $child->setProperty('title', $postTitle); $child->setProperty('body', $postBody); $child->setProperty('author', $postAuthor); $child->setProperty('time', time()); $this->session->save(); return $path . '/' . $nodename; } else { return null; } } function addComment($path, $commentNumber, $commentText, $commentAuthor) { $parent = $this->getAnyNode($path); if(!$this->isPost($parent) && !$this->isComment($parent)) { throw new ForumException('Trying to add comment to non-post and non-comment'); } $nodename = ForumManager::nameFixUp($commentNumber); if(!$this->alreadyHasChild($parent, $nodename)) { $child = $parent->addNode($nodename); if($this->verssup) $child->addMixin(NodeTypeInterface::MIX_VERSIONABLE); $child->setProperty('number', $commentNumber); $child->setProperty('text', $commentText); $child->setProperty('author', $commentAuthor); $child->setProperty('time', time()); $this->session->save(); return $path . '/' . $nodename; } else { return null; } } function addCommentAutoNumber($path, $commentText, $commentAuthor) { $cn = $this->getAutoNumber($this->getAnyNode($path)); return $this->addComment($path, $cn, $commentText, $commentAuthor); } // update nodes function updatePost($path, $newPostBody) { if(!$this->verssup) throw new ForumException('Versions not supported'); $vm = $this->session->getWorkspace()->getVersionManager(); // if first update save original version if(count($vm->getVersionHistory($path)->getAllVersions()) <= 1) { $vm->checkout($path); $this->session->save(); $vm->checkin($path); $this->session->save(); } // checkout $vm->checkout($path); $this->session->save(); // update $node = $this->getAnyNode($path); if(!$this->isPost($node)) { throw new ForumException('Updating non-post as post'); } $node->setProperty('body', $newPostBody); $node->setProperty('time', time()); $this->session->save(); // checkin $vm->checkin($path); $this->session->save(); } function updateComment($path, $newCommentText) { if(!$this->verssup) throw new ForumException('Versions not supported'); $vm = $this->session->getWorkspace()->getVersionManager(); // if first update save original version if(count($vm->getVersionHistory($path)->getAllVersions()) <= 1) { $vm->checkout($path); $this->session->save(); $vm->checkin($path); $this->session->save(); } // checkout $vm->checkout($path); $this->session->save(); // update $node = $this->getAnyNode($path); if(!$this->isComment($node)) { throw new ForumException('Updating non-comment as comment'); } $node->setProperty('text', $newCommentText); $node->setProperty('time', time()); $this->session->save(); // checkin $vm->checkin($path); $this->session->save(); } } class ModelMapper { private function nodeToTopic($n) { return new Topic($n->getProperty('name')->getString(), $n->getProperty('description')->getString()); } private function nodeToPost($n) { return new Post($n->getProperty('author')->getString(), $n->getProperty('time')->getLong(), $n->getProperty('title')->getString(), $n->getProperty('body')->getString()); } private function nodeToComment($n) { return new Comment($n->getProperty('author')->getString(), $n->getProperty('time')->getLong(), $n->getProperty('number')->getString(), $n->getProperty('text')->getString()); } private function loadTopics(&$topics, $recurse) { $it = $this->forummanager->getSession()->getRootNode()->getNode('F')->getNodes('*'); while($it->valid()) { $n = $it->current(); if($this->forummanager->isTopic($n)) { $t = $this->nodeToTopic($n); $topics[] = $t; if(recurse) { $this->loadPosts($t->getPosts(), $n, true); } } else { throw new ForumException('Forum has non-topic child'); } $it->next(); } } private function loadPosts(&$posts, $parent, $recurse) { if(!$this->forummanager->isTopic($parent)) { throw new ForumException('Trying to get posts of non-topic'); } $it = $parent->getNodes('*'); while($it->valid()) { $n = $it->current(); if($this->forummanager->isPost($n)) { $p = $this->nodeToPost($n); $posts[] = $p; if($this->forummanager->getVerssup()) { $vm = $this->forummanager->getSession()->getWorkspace()->getVersionMAnager(); $vhist = $vm->getVersionHistory($n->getPath()); $vit = $vhist->getAllVersions(); while($vit->valid()) { $v = $vit->current(); $vnit = $v->getNodes(); while($vnit->valid()) { $vn = $vnit->current(); if($this->forummanager->isPost($vn)) { $p->getVersions()[] = $this->nodeToPost($vn); } $vnit->next(); } $vit->next(); } } if(recurse) { $this->loadComments($p->getComments(), $n, true); } } else { throw new ForumException('Topic has non-post child'); } $it->next(); } } private function loadComments(&$comments, $parent, $recurse) { if(!$this->forummanager->isPost($parent) && !$this->forummanager->isComment($parent)) { throw new ForumException('Trying to get posts of non-post and non-comment'); } $it = $parent->getNodes('*'); while($it->valid()) { $n = $it->current(); if($this->forummanager->isComment($n)) { $c = $this->nodeToComment($n); $comments[] = $c; if($this->forummanager->getVerssup()) { $vm = $this->forummanager->getSession()->getWorkspace()->getVersionMAnager(); $vhist = $vm->getVersionHistory($n->getPath()); $vit = $vhist->getAllVersions(); while($vit->valid()) { $v = $vit->current(); $vnit = $v->getNodes(); while($vnit->valid()) { $vn = $vnit->current(); if($this->forummanager->isComment($vn)) { $c->getVersions()[] = $this->nodeToComment($vn); } $vnit->next(); } $vit->next(); } } if(recurse) { $this->loadComments($c->getComments(), $n, true); } } else { throw new ForumException('Post/comment has non-comment child'); } $it->next(); } } // mapping between model and PHPCR private $forummanager; public function __construct($forummanager) { $this->forummanager = $forummanager; } // load public function loadForum() { $f = new Forum(); $this->loadTopics($f->getTopics(), true); return $f; } public function loadTopic($path, $recurse = false) { $n = $this->forummanager->getAnyNode($path); if($this->forummanager->isTopic($n)) { $t = $this->nodeToTopic($n); if($recurse) { $this->loadPosts($t->getPosts(), $n, true); } return $t; } else { throw new ForumException('Path is not a topic path: ' . $path); } } public function loadPost($path, $recurse = false) { $n = $this->forummanager->getAnyNode($path); if($this->forummanager->isPost($n)) { $p = $this->nodeToPost($n); if($recurse) { $this->loadComments($p->getComments(), $n, true); } return $p; } else { throw new ForumException('Path is not a post path: ' . $path); } } public function loadComment($path, $recurse = false) { $n = $this->forummanager->getAnyNode($path); if($this->forummanager->isComment($n)) { $c = $this->nodeToComment($n); if($recurse) { $this->loadComments($c->getComments(), $n, true); } return $c; } else { throw new ForumException('Path is not a comment path: ' . $path); } } function findPostByAuthor($author, $recurse = false) { $res = array(); $q = $this->forummanager->getSession()->getWorkspace()->getQueryManager()->createQuery('SELECT * FROM [nt:unstructured] WHERE author = \'' . $author . '\' AND title IS NOT NULL AND number IS NULL', QueryInterface::JCR_SQL2); $qr = $q->execute(); $nit = $qr->getNodes(); while($nit->valid()) { $n = $nit->current(); if($this->forummanager->isPost($n)) { $p = $this->nodeToPost($n); $res[] = $p; if($recurse) { $this->loadComments($p->getComments(), $n, true); } } $nit->next(); } return $res; } function findPostByContent($content, $recurse = false) { $res = array(); $q = $this->forummanager->getSession()->getWorkspace()->getQueryManager()->createQuery('SELECT * FROM [nt:unstructured] WHERE CONTAINS(body,\'' . $content . '\') AND title IS NOT NULL AND number IS NULL', QueryInterface::JCR_SQL2); $qr = $q->execute(); $nit = $qr->getNodes(); while($nit->valid()) { $n = $nit->current(); if($this->forummanager->isPost($n)) { $p = $this->nodeToPost($n); $res[] = $p; if($recurse) { $this->loadComments($p->getComments(), $n, true); } } $nit->next(); } return $res; } function findCommentByAuthor($author, $recurse = false) { $res = array(); $q = $this->forummanager->getSession()->getWorkspace()->getQueryManager()->createQuery('SELECT * FROM [nt:unstructured] WHERE author = \'' . $author . '\' AND title IS NULL AND number IS NOT NULL', QueryInterface::JCR_SQL2); $qr = $q->execute(); $nit = $qr->getNodes(); while($nit->valid()) { $n = $nit->current(); if($this->forummanager->isComment($n)) { $c = $this->nodeToComment($n); $res[] = $c; if($recurse) { $this->loadComments($c->getComments(), $n, true); } } $nit->next(); } return $res; } function findCommentByContent($content, $recurse = false) { $res = array(); $q = $this->forummanager->getSession()->getWorkspace()->getQueryManager()->createQuery('SELECT * FROM [nt:unstructured] WHERE CONTAINS(text,\'' . $content . '\') AND title IS NULL AND number IS NOT NULL', QueryInterface::JCR_SQL2); $qr = $q->execute(); $nit = $qr->getNodes(); while($nit->valid()) { $n = $nit->current(); if($this->forummanager->isComment($n)) { $c = $this->nodeToComment($n); $res[] = $c; if($recurse) { $this->loadComments($c->getComments(), $n, true); } } $nit->next(); } return $res; } } ?>