. ", " . intval($this->fetch_field("tagid")) . ", " . $this->registry->userinfo['userid'] . ", " . TIMENOW . " ) "); } public function detach_content($type, $id) { $this->dbobject->query_write(" DELETE FROM " . TABLE_PREFIX . "tagcontent WHERE entitytype = '" . $this->dbobject->escape_string($type) . "' AND entityid = " . intval($id) . " AND tagid = " . intval($this->fetch_field("tagid")) ); } /** * Make this tag a synonym for another tag * * Any associations between this tag and content will be transfered to the parent. * * @param int $canonical_id id for the tag that this will become the synonym of */ public function make_synonym($canonical_id) { //if we already have synonyms attach them to the new canonical id as well //we only allow one level of synonyms foreach ($this->fetch_synonyms() as $synonym) { $synonym->make_synonym($canonical_id); } //actually make this a synonym $this->set("canonicaltagid", $canonical_id); $this->save(); //fix any associated content items $associated_content = $this->fetch_associated_content(); foreach ($associated_content as $contenttypeid => $contentids) { foreach ($contentids as $contentid) { $replace_threads[] = "($canonical_id, " . intval($contenttypeid) . ", $contentid, " . $this->registry->userinfo['userid'] . ", " . TIMENOW . ")"; } } // add new tag to affected threads if (sizeof($replace_threads)) { $this->dbobject->query_write(" REPLACE INTO " . TABLE_PREFIX . "tagcontent (tagid, contenttypeid, contentid, userid, dateline) VALUES " . implode(',', $replace_threads) . " "); } //clear old category associations. $this->dbobject->query_write(" DELETE FROM " . TABLE_PREFIX . "tagcontent WHERE tagid = " . intval($this->fetch_field("tagid")) ); $this->handle_associated_content_removals($associated_content); //update the tag search cloud datastore to reflect the change $this->dbobject->query_write(" UPDATE " . TABLE_PREFIX . "tagsearch SET tagid = " . intval($canonical_id). " WHERE tagid = " . intval($this->fetch_field("tagid")) ); return true; } /** * Unlink a synonym from its canonical parent. * * This will not reestablish any relationships between the tag and content that * may have been transfered to the parent when the tag was made a synonym. */ public function make_independent() { $this->set("canonicaltagid", 0); $this->save(); } public function pre_save($doquery) { if (!$this->condition AND !$this->fetch_field('dateline')) { $this->set('dateline', TIMENOW); } return true; } public function pre_delete($doquery) { $contentsql = "DELETE FROM " . TABLE_PREFIX . "tagcontent WHERE tagid = " . intval($this->fetch_field("tagid")); $searchtagssql = "DELETE FROM " . TABLE_PREFIX . "tagsearch WHERE tagid = " . intval($this->fetch_field("tagid")); //if we have synonyms for this tag, delete those as well. if ($doquery) { foreach ($this->fetch_synonyms() as $synonym) { $synonym->delete(); } $associated_content = $this->fetch_associated_content(); $this->dbobject->query_write($contentsql); $this->dbobject->query_write($searchtagssql); $this->handle_associated_content_removals($associated_content); } else { //probably should log the other non sql actions $this->log_query($contentsql); $this->log_query($searchtagssql); } return true; } private function fetch_associated_content() { $result = $this->dbobject->query_read(" SELECT contenttypeid, contentid FROM " . TABLE_PREFIX . "tagcontent WHERE tagid = " . intval($this->fetch_field("tagid")) ); $associated_content = array(); while (list($contenttypeid, $contentid) = $this->dbobject->fetch_row($result)) { $associated_content[$contenttypeid][] = $contentid; } $this->dbobject->free_result($result); return $associated_content; } /** * Propogate any content specific effects for removing this tag from a content item * * Some content tables may store the list of associated tags in as part of the record * in addition to the main association table. While this is more efficient for lookups * we need to keep track of it when tags are removed. This can happen because a tag * was deleted or because it was replaced with another tag. * * @param Array $associated_content An array of the form 'contenttypeid' => 'contentids' * containing all of the content ids associated with a particular tag */ private function handle_associated_content_removals($associated_content) { //As we add additional content types, we should look at how this logic can be //generalized // update thread taglists foreach ($associated_content as $contenttypeid => $contendids) { foreach ($contendids as $contendid) { $item = vB_Taggable_Content_Item::create($this->registry, $contenttypeid, $contendid); if ($item) { $item->rebuild_content_tags(); } } } } /* Should probably get moved to the base class, but I'm not quite ready to do that. */ protected function load_existing() { if ($this->condition) { $fields = array_keys($