'/^[^\n\r\t]+$/', 'body' => '/.+/', ); function getField($field, $insert = FALSE) { $prefix = !$insert ? 't.' : ''; return "$prefix$field"; } function getPrimaryKey() { return 'thread_id'; } function getTable() { return 'k4_threads'; } function getSelectFields() { return "DISTINCT t.thread_id, t.*, ts.tag_name, GROUP_CONCAT(ts.tag_name SEPARATOR ',') AS tags, u.name AS user_name, uu.name AS edit_user_name"; } function getSelectGroupby() { return 't.thread_id'; } // TODO:{[make it so that you don't need to use this for getting authors but you can just join them in to the threads query.[getSelectTables[ThreadsDefinition} function getSelectTables() { return 'k4_thread_tags tt, k4_tags ts, k4_threads t, k4_users u, k4_users uu'; } function getSelectWhere() { return 't.thread_id=tt.thread_id AND ts.tag_id=tt.tag_id AND t.user_id=u.user_id AND t.edit_user_id=uu.user_id'; } } class ThreadsRecord extends FARecord { //-------------------------------------------- // Result sets that get cached.. //-------------------------------------------- var $_cache = array( 'comments' => FALSE, 'authors' => FALSE, 'invites' => FALSE, 'tags' => FALSE, 'my_tags' => FALSE, 'revisions' => FALSE, ); var $_comments; var $_authors; var $_invites; var $_tags; var $_my_tags; var $_revisions; //-------------------------------------------- // Other useful variables used in such things as // normal operations, adding and editing. //-------------------------------------------- var $_user; var $_update_user_karma = FALSE; var $_edit = FALSE; var $_quick_edit = FALSE; var $_coauthors = array(); //-------------------------------------------- // Stuff for revisions.. //-------------------------------------------- var $_old_name = ''; var $_old_body = ''; var $_old_tags = ''; var $_old_edit_msg = ''; var $_old_edit_time = 0; var $_old_edit_userid = 0; var $_clear_revisions = FALSE; //-------------------------------------------- // Define what in this records object is cacheable. //-------------------------------------------- function cacheAble() { return array( // table columns 'thread_id', 'created', 'deleted', 'name', 'body', 'type', 'num_comments', 'views', 'recent_cid', 'recent_created', 'recent_uid', 'recent_user', 'recent_name', 'karma_risk', 'karma_reward', 'allow_coauthors', 'user_id', 'edit_user_id', 'edit_message', 'edit_time', // sub cacheable objects 'comments', 'authors', 'tags', ); } //-------------------------------------------- // Create a comment. //-------------------------------------------- function &createComment() { // TODO:{[It is possible that this is an unsaved record[createComment[ThreadsRecord} $finder = &$this->getFinder('comments'); $comment = &$finder->createRecord(); $comment->setThread($this); $comment->set('created', time()); return $comment; } //-------------------------------------------- // Get a thread's comments as a sub-list //-------------------------------------------- function &getComments() { if (!$this->_cache['comments']) { $finder = &$this->getFinder('comments'); $this->_comments = &$finder->findAllBy("thread_id", $this->getId()); $this->_cache['comments'] = TRUE; } return $this->_comments; } //-------------------------------------------- // Get a thread's authors. //-------------------------------------------- function &getAuthors() { if (!$this->_cache['authors']) { $finder = &$this->getFinder('thread_users'); $this->_authors = &$finder->findAllBy("thread_id", $this->getId()); $this->_cache['authors'] = TRUE; } return $this->_authors; } //-------------------------------------------- // Figure out some simple things that this user // can/can't do with this thread based on their // permissions, if their a co/author, etc. //-------------------------------------------- function setPermissions(&$resp) { $this->_userDependency(); $can_moderate = $this->_user->canDo('moderate', 'threads'); $can_tag = $this->_user->canDo('tag', 'threads'); $can_edit = $can_delete = $display_buttons = intval($can_moderate); $can_rate = 1; $is_author = FALSE; } //-------------------------------------------- // Get a thread's tags as a sub-list. //-------------------------------------------- function &getTags() { if (!$this->_cache['tags']) { $tags = explode(',', $this->_get('tags')); $this->_tags = &new FAArrayIterator($tags); $this->_cache['tags'] = TRUE; } return $this->_tags; } //-------------------------------------------- // If this user is allowed to tag threads, it's // possible that they've tagged this thread. So, // get any tags that they've used. //-------------------------------------------- function &getMyTags() { $this->_userDependency(); //-------------------------------------------- // Get the tags. //-------------------------------------------- if(!$this->_cache['my_tags']) { $finder = &$this->getFinder('thread_user_tags'); $this->_my_tags = &$finder->findAllWhere("tut.thread_id=? AND tut.user_id=?", array($this->getId(), $this->_user->getId())); $this->_cache['my_tags'] = TRUE; } return $this->_my_tags; } //-------------------------------------------- // Get the revision history for this thread. //-------------------------------------------- function &getRevisions() { if(!$this->_cache['revisions']) { $finder = &$this->getFinder('thread_revisions'); $this->_revisions = &$finder->findAllBy("thread_id", $this->getId()); $this->_cache['revisions'] = TRUE; } return $this->_revisions; } //-------------------------------------------- // Tell the save function to update the users karma //-------------------------------------------- function setUserKarma() { $this->_update_user_karma = TRUE; } //-------------------------------------------- // Tell the save function that we are editing this // thread. //-------------------------------------------- function setEdit() { //-------------------------------------------- // Store some old info. //-------------------------------------------- if($this->get('edit_time')) { $this->_userDependency(); //-------------------------------------------- // Set the info. //-------------------------------------------- $this->_old_edit_time = $this->get('edit_time'); $this->_old_edit_msg = $this->get('edit_message'); } //-------------------------------------------- // The last user to edit this thread. //-------------------------------------------- $this->_old_edit_userid = (!$this->get('edit_user_id') || $this->get('edit_user_id') == 0) ? $this->_user->getId() : $this->get('edit_user_id'); //-------------------------------------------- // Say we're editing. //-------------------------------------------- $this->_edit = TRUE; } //-------------------------------------------- // Tell the save function that we are editing in // quick edit mode. //-------------------------------------------- function setQuickEdit() { $this->setEdit(); $this->_quick_edit = TRUE; } //-------------------------------------------- // Save this thread to the database. //-------------------------------------------- function save() { if ($ret = parent::save() && $this->_tags != NULL) { $this->_tags->reset(); //-------------------------------------------- // If we've got tags, build a SQL query to add them // to this thread. //-------------------------------------------- if ($this->_tags->hasNext()) { //-------------------------------------------- // if we're editing but NOT quick editing //-------------------------------------------- if($this->_edit && !$this->_quick_edit) { //-------------------------------------------- // So, I'm being lazy here.. but if we're editing (full edit), // we'll just wipe out all of the old tags and re-add // them. //-------------------------------------------- $finder = &$this->getFinder('thread_tags'); $finder->deleteBy("thread_id", $this->getId()); } //-------------------------------------------- // Build the SQL to insert all of the thread tags. //-------------------------------------------- $id = $this->getId(); $tags = array(); $sql = "INSERT INTO k4_thread_tags (thread_id, tag_id) VALUES "; $finder = &$this->getFinder('tags'); //-------------------------------------------- // Loop over the tags and check if they exist, if they // don't, create them, otherwise get their info. //-------------------------------------------- while ($this->_tags->next()) { $tag_name = $this->_tags->current(); $tag = &$finder->findBy('tag_name', $tag_name); if($tag === NULL || empty($tag)) { $tag = &$finder->createRecord(); $tag->set('tag_name', $tag_name); $tag->save(); } $tag_id = intval($tag->getId()); if($tag_id > 0) { $sql .= "($id, ". $tag_id ."),"; } } //-------------------------------------------- // Add the tags to the thread //-------------------------------------------- $this->_dba->executeUpdate(substr_utf($sql, 0, -1), array()); } //-------------------------------------------- $finder = &$this->getFinder('users'); //-------------------------------------------- // Decrease a users karma for posting this thread. //-------------------------------------------- $update_sql = ""; $update_array = array(); if($this->_update_user_karma) { if($this->_user->getId() > K4_GUEST_ID) { $update_sql .= "karma=karma-? "; $update_array[] = $this->get('karma_risk'); } } //-------------------------------------------- // Co-author stuff below.. //-------------------------------------------- if(!$this->_edit) { //-------------------------------------------- // Add the user as the main author. (as long as // the user isn't a guest user) //-------------------------------------------- if($this->_user->getId() > K4_GUEST_ID) { $finder = &$this->getFinder('thread_users'); $author = &$finder->createRecord(); $author->set('thread_id', $this->getId()); $author->set('user_id', $this->_user->getId()); $author->save(); $update_sql .= ($update_sql != '' ? ',' : '') . "num_threads=num_threads+1"; } } } //-------------------------------------------- // Update the poster, if necessary. //-------------------------------------------- if($update_sql != '') { $update_array[] = $this->_user->getId(); $finder = &$this->getFinder('users'); $finder->updateWhere($update_sql, "user_id=?", $update_array); } //-------------------------------------------- // We're done! //-------------------------------------------- return $ret; } //-------------------------------------------- // Set the tags of a thread. //-------------------------------------------- function setTags($tags) { if($this->_edit) { $this->_old_tags = $this->getRawTags(); } //-------------------------------------------- // Make it so that we can pass an array or a string // through this function. //-------------------------------------------- $temp = array(); if(is_string($tags)) { $temp = preg_split("~,~", $tags, -1, PREG_SPLIT_NO_EMPTY); } else if(is_array($tags)) { $temp = $tags; } $tags = array(); //-------------------------------------------- // Loop over the tags and format them. //-------------------------------------------- $tags = clean_tags($temp); $this->_tags = &new FAArrayIterator($tags); } //-------------------------------------------- // Set the user info of the user that's creating this // thread. //-------------------------------------------- function setUser(&$user) { $this->_user = &$user; } //-------------------------------------------- // Set the karma risk of this thread. //-------------------------------------------- function setKarma($risk = 1) { $this->set('karma_risk', $risk); } //-------------------------------------------- // Set the karma reward of this thread when a comment // is posted. //-------------------------------------------- function setKarmaForComment() { $this->set('karma_reward', $this->get('karma_reward')+0.1); } //-------------------------------------------- // Unparse bbcode and return the original, 'raw' // body text of this thread. //-------------------------------------------- function &getRawBody() { $filter = &new K4BBRevertFilter(); return $filter->filter($this->get('body')); } //-------------------------------------------- // Add in quick tag linking. We don't want to overwrite // getBody because then if we reverted the body text, it // would include those urls. //-------------------------------------------- function &getPostBody() { $filter = &new K4TextTagsFilter(); return $filter->filter($this->get('body')); } //-------------------------------------------- // Get a comma seperated list of the tags. //-------------------------------------------- function &getRawTags() { return $this->_get('tags'); } //-------------------------------------------- // Set the name of a thread. //-------------------------------------------- function setThreadName($name) { $name = trim($name); $name = substr_utf($name, 0, 150); if($name != '') { if($this->_edit && !$this->_clear_revisions) { $this->_old_name = $this->get('name'); } $this->set('name', $name); } } //-------------------------------------------- // Set the body of a thread. //-------------------------------------------- function setThreadBody($body) { $body = trim($body); $body = substr_utf($body, 0, 10000); if($body != '') { if($this->_edit && !$this->_clear_revisions && $body != $this->get('body')) { $this->_old_body = $this->getRawBody(); } $this->set('body', $body); } } //-------------------------------------------- // Remove all revisions of a thread. //-------------------------------------------- function clearRevisions() { if(!$this->_edit) { trigger_error("This function must be used when editing a thread."); } $this->_clear_revisions = TRUE; //-------------------------------------------- // Delete this thread's revisions from the database. // Also, remove its coauthors. //-------------------------------------------- $finder = &$this->getFinder('thread_revisions'); $finder->deleteBy("thread_id", $this->getId()); $finder = &$this->getFinder('thread_users'); $finder->deleteWhere("thread_id=? AND user_id<>? AND user_id<>?", array($this->getId(), $this->get('user_id'), $this->_user->getId())); } //-------------------------------------------- // Create a revision of this thread. //-------------------------------------------- function createRevision() { //-------------------------------------------- // If any of these clear, don't bother going // any further. //-------------------------------------------- if(!$this->_edit || $this->_clear_revisions) { return NULL; } //-------------------------------------------- // Should we create a revision? //-------------------------------------------- $changed_title = FALSE; $changed_body = FALSE; //-------------------------------------------- // Check if we've changed the name. //-------------------------------------------- if($this->_old_name !== NULL) { $changed_title = $this->_old_name == $this->get('name'); } //-------------------------------------------- // Check if we've changed the body text //-------------------------------------------- $changed_body = $this->_old_body !== NULL; //-------------------------------------------- // Nothing's been changed... //-------------------------------------------- if(!$changed_title && !$changed_body) { return NULL; } //-------------------------------------------- // If we've changed something, create a revision // for it. //-------------------------------------------- $finder = &$this->getFinder('thread_revisions'); $revision = &$finder->createRecord(); //-------------------------------------------- $revision->set('edit_message', $this->_old_edit_msg); $revision->set('edit_time', $this->_old_edit_time); $revision->set('edit_user_id', $this->_old_edit_userid); $revision->set('body', $this->_old_body); $revision->set('name', $this->_old_name); $revision->set('tags', $this->_old_tags); $revision->set('thread_id', $this->getId()); $revision->save(); //-------------------------------------------- // Now that that revision has been made, let's // see if the editor should be added as a co-author. //-------------------------------------------- if($this->_user->getId() != $this->get('user_id')) { $finder = &$this->getFinder('thread_users'); $coauthor = &$finder->findWhere("tu.thread_id=? AND u.user_id=?", array($this->getId(), $this->_user->getId())); //-------------------------------------------- // The coauthor doesn't exist in the database // yet, we'll create them. //-------------------------------------------- if($coauthor === NULL) { $coauthor = &$finder->createRecord(); $coauthor->set('thread_id', $this->getId()); $coauthor->set('user_id', $this->_user->getId()); $coauthor->save(); } } return TRUE; } //-------------------------------------------- // Soft delete a thread. //-------------------------------------------- function softDelete() { $this->set('deleted', time()); $this->save(); return TRUE; } //-------------------------------------------- // Hard delete a thread. //-------------------------------------------- function hardDelete() { //-------------------------------------------- // Hard delete a thread.. (really delete it) //-------------------------------------------- $finder = &$this->getFinder('threads'); $finder->delete($this->getId()); //-------------------------------------------- // Delete all of this threads revisions. //-------------------------------------------- $finder = &$this->getFinder('thread_revisions'); $finder->deleteBy("thread_id", $this->getId()); //-------------------------------------------- // Delete all of this threads authors //-------------------------------------------- $finder = &$this->getFinder('thread_users'); $finder->deleteBy("thread_id", $this->getId()); //-------------------------------------------- // Delete all of this threads tags. //-------------------------------------------- $finder = &$this->getFinder('thread_tags'); $finder->deleteBy("thread_id", $this->getId()); //-------------------------------------------- // Delete all bookmark tags on this thread. //-------------------------------------------- $finder = &$this->getFinder('thread_user_tags'); $finder->deleteBy("thread_id", $this->getId()); //-------------------------------------------- // Delete all karma records relating to this thread. //-------------------------------------------- $finder = &$this->getFinder('post_karma'); $finder->deleteBy("thread_id", $this->getId()); //-------------------------------------------- // Delete all of the comments of this thread and // change the comment counts of the posters of // those comments. //-------------------------------------------- $finder = &$this->getFinder('comments'); $comments = &$finder->findAllBy("thread_id", $this->getId()); $user_ids = array(); $comment_ids = ""; $user_ids_single = ""; //-------------------------------------------- // Loop through the comments, derive their id's // and the id's of their posters. //-------------------------------------------- if($comments !== NULL) { while($comments->next()) { $comment = &$comments->current(); $comment_ids .= "'". $comment->getId() ."',"; //-------------------------------------------- // This user isn't a guest so let's make sure // that we will update their comment count. //-------------------------------------------- if($comment->get('user_id') != K4_GUEST_ID) { if(!isset($user_ids[$comment->get('user_id')])) { $user_ids[$comment->get('user_id')] = 0; } $user_ids[$comment->get('user_id')]++; } } $comment_ids = substr($comment_ids, 0, -1); //-------------------------------------------- // Delete the comments. //-------------------------------------------- $finder = &$this->getFinder('comments'); $finder->deleteWhere("comment_id IN(". $comment_ids .")", array()); } if(count($user_ids) > 0) { $finder = &$this->getFinder('users'); foreach($user_ids as $user_id => $num_comments) { if($num_comments == 1) { //-------------------------------------------- // Add this user's id to the list of ids to be // mass updated. //-------------------------------------------- $user_ids_single .= "'". $user_id ."',"; } else { //-------------------------------------------- // Update this users post count if s/he has // made > 1 comment in this thread. //-------------------------------------------- $finder->updateWhere("num_comments=num_comments-1", "user_id=?", array($user_id)); } } //-------------------------------------------- // Update multiple users post counts. //-------------------------------------------- if($user_ids_single != "") { $user_ids_single = substr($user_ids_single, 0, -1); $finder->updateWhere("num_comments=num_comments-1", "user_id IN({$user_ids_single})", array()); } } //-------------------------------------------- // Update the posters thread count. //-------------------------------------------- if($this->get('user_id') > K4_GUEST_ID) { $finder = &$this->getFinder('users'); $finder->updateWhere("num_threads=num_threads-1", "user_id=? AND num_threads>0", array($this->get('user_id'))); } //-------------------------------------------- // This thread is NOT coming back :P //-------------------------------------------- return TRUE; } //-------------------------------------------- // Revert a thread to a previous revision. //-------------------------------------------- function revertTo($revision_id) { $this->_userDependency(); //-------------------------------------------- // Get the revision. //-------------------------------------------- $finder = &$this->getFinder('thread_revisions'); $revision = &$finder->find($revision_id); if($revision !== NULL) { //-------------------------------------------- // Set the information. //-------------------------------------------- $filter = &new K4BBParserFilter(); $this->setEdit(); $this->setThreadBody($filter->filter(html_entity_decode($revision->get('body'), ENT_QUOTES))); $this->setThreadName($revision->get('name')); $this->setTags(explode(",", $revision->get('tags'))); $this->set('edit_time', time()); $this->set('edit_user_id', $this->_user->getId()); $this->set('edit_message', '[Reverted to revision #'. $revision->getId() .']'); //-------------------------------------------- // Create a revision holding all of the current // thread information. //-------------------------------------------- if($this->createRevision() !== NULL) { //-------------------------------------------- // Save all of the new thread information to // this thread. //-------------------------------------------- $this->save(); } } } //-------------------------------------------- // Make sure that a functions dependancy // on the user object is satisfied. //-------------------------------------------- function _userDependency() { if(!$this->_user || $this->_user === NULL) { trigger_error("[ERROR] You must set the user instance to the thread model to use this method.", E_USER_ERROR); } } } ?>