00001 <?
00008 abstract class BaseTags extends MyModule
00009 {
00013 protected $tagTable;
00014
00018 protected $joinerTable;
00019
00023 public $plural;
00024
00028 public $objectId = 0;
00029
00033 public static $cachePopularTags = true;
00034
00038 public static $popularTagsCacheLife = 3600;
00039
00043 public static $cacheRelatedTags = true;
00044
00048 public static $relatedTagsCacheLife = 3600;
00049
00053 public static $weightStyles = array(
00054 'font-size: 35px;',
00055 'font-size: 25px;',
00056 'font-size: 20px;',
00057 'font-side: 16px;',
00058 'font-size: 11px;'
00059 );
00060
00068 public function __construct($id, $tagTable, $joinerTable)
00069 {
00070 $this->english = 'tag';
00071 $this->plural = 'tags';
00072
00073 $this->tagTable = $tagTable;
00074 $this->joinerTable = $joinerTable;
00075 $this->objectId = (int)$id;
00076 }
00077
00083 public function getPagesXml()
00084 {
00085 $xml = parent::getPagesXml();
00086 $xml .= <<<XML
00087 <page name="view">
00088 <param name="tag" regex="/^\w*$/" required="1" />
00089 </page>
00090 XML;
00091 return $xml;
00092 }
00093
00097 public function initViewPage()
00098 {
00099 $tag = $this->params('tag');
00100
00101 if (!$this->findTag($tag))
00102 throw new PageError("The tag $tag does not exist.");
00103
00104 $this->pageTitle = self::prettyTag($tag);
00105
00106
00107 $this->doCachePage = true;
00108 }
00109
00113 public function drawViewPage()
00114 {
00115 $this->drawRelatedTags();
00116 }
00117
00121 public function drawRelatedTags()
00122 {
00123 echo "<h3>Related " . ucfirst($this->plural) . "</h3>\n";
00124 echo "<p>" . $this->getRelatedTagsList($this->params('tag')) . "</p>";
00125 }
00126
00130 public function initMainPage()
00131 {
00132 $this->pageTitle = 'Popular ' . ucfirst($this->plural);
00133
00134 $this->doCachePage = true;
00135 }
00136
00140 public function drawMainPage()
00141 {
00142 echo "<p>" . $this->getWeightedPopularTagsList() . "</p>";
00143 }
00144
00154 public static function cleanTag($tag)
00155 {
00156
00157 $tag = trim($tag);
00158
00159
00160 $tag = preg_replace("/\s/", "_", $tag);
00161
00162
00163 $tag = preg_replace("/\W/", "", $tag);
00164
00165 return $tag;
00166 }
00167
00175 public static function prettyTag($tag)
00176 {
00177 return str_replace("_", " ", $tag);
00178 }
00179
00188 public function addTag($tag)
00189 {
00190
00191 $tag = self::cleanTag($tag);
00192
00193
00194 $id = $this->findTag($tag);
00195
00196
00197 if (!$id)
00198 $id = $this->insertTag($tag);
00199
00200
00201 return $id;
00202 }
00203
00210 protected function insertTag($tag)
00211 {
00212 $tag = self::cleanTag($tag);
00213
00214
00215 $id = dbExecute("
00216 INSERT INTO $this->tagTable
00217 (tag)
00218 VALUES
00219 ('$tag')
00220 ", true);
00221
00222 return $id;
00223 }
00224
00232 public function deleteTag($tag)
00233 {
00234
00235 $tag = self::cleanTag($tag);
00236
00237
00238 $rows = dbExecute("
00239 DELETE FROM $this->tagTable
00240 WHERE tag = '$tag'
00241 LIMIT 1
00242 ", -1);
00243
00244 return $rows;
00245 }
00246
00254 public function findTag($tag)
00255 {
00256
00257 $tag = self::cleanTag($tag);
00258
00259
00260 $rs = dbQuery("
00261 SELECT id
00262 FROM $this->tagTable
00263 WHERE tag = '$tag'
00264 ");
00265
00266
00267 if (dbGetNumRows($rs))
00268 {
00269 $ar = dbFetchAssoc($rs);
00270 return $ar['id'];
00271 }
00272
00273
00274 return false;
00275 }
00276
00282 public function getTags()
00283 {
00284 $tags = array();
00285
00286
00287 if ($this->objectId)
00288 {
00289 $where = $this->getTagsWhere();
00290
00291
00292 $rs = dbQuery("
00293 SELECT t.id, t.tag
00294 FROM $this->tagTable t
00295 INNER JOIN $this->joinerTable j
00296 ON t.id = j.tag_id
00297 $where
00298 ORDER BY t.tag
00299 ");
00300
00301
00302 while ($ar = dbFetchAssoc($rs))
00303 $tags[$ar['id']] = $ar['tag'];
00304 }
00305
00306
00307 return $tags;
00308 }
00309
00310 function delete()
00311 {
00312 dbExecute("
00313 DELETE FROM $this->joinerTable WHERE object_id = $this->objectId
00314 ");
00315 }
00316
00322 protected function getTagsWhere()
00323 {
00324 return "WHERE j.object_id = '$this->objectId'";
00325 }
00326
00332 public function getPrettyTags()
00333 {
00334 $tags = $this->getTags();
00335
00336 foreach ($tags AS &$tag)
00337 $tag = self::prettyTag($tag);
00338
00339 return $tags;
00340 }
00341
00349 public function getObjectsSql($tagId)
00350 {
00351
00352 return "
00353 SELECT distinct(z.object_id)
00354 FROM $this->joinerTable z
00355 WHERE z.tag_id = '$tagId'
00356 ";
00357 }
00358
00367 public function getPopularTags($count = 30)
00368 {
00369
00370 $popularSql = "
00371 SELECT t.tag, count(j.object_id) AS popularity
00372 FROM $this->tagTable t
00373 INNER JOIN $this->joinerTable j
00374 ON t.id = j.tag_id
00375 GROUP BY t.tag
00376 ORDER BY popularity DESC
00377 LIMIT $count
00378 ";
00379 $key = "BaseJumper:tags:popular:" . sha1($popularSql);
00380
00381
00382 if (self::$cachePopularTags)
00383 $tags = CacheBot::get($key, self::$popularTagsCacheLife);
00384
00385
00386 if (!$tags)
00387 {
00388 $tags = array();
00389
00390
00391 $rs = dbQuery($popularSql);
00392
00393
00394 while ($ar = dbFetchAssoc($rs))
00395 $tags[] = $ar['tag'];
00396
00397
00398 if (self::$cachePopularTags)
00399 CacheBot::set($key, $tags, self::$popularTagsCacheLife);
00400 }
00401
00402
00403 return $tags;
00404 }
00405
00412 public function getPopularTagsList($count = 60)
00413 {
00414
00415 $tags = $this->getPopularTags($count);
00416
00417
00418 $links = $this->getLinkedTags($tags);
00419
00420
00421 return implode(", ", $links);
00422 }
00423
00430 public function getWeightedPopularTagsList($count = 100)
00431 {
00432
00433 $tags = $this->getPopularTags($count);
00434
00435
00436 $links = $this->getLinkedTags($tags);
00437
00438
00439 $i = 0;
00440 foreach ($links AS $link)
00441 {
00442 if ($i < 5)
00443 {
00444 $class = "WeightedTagHighest";
00445 $style = self::$weightStyles[0];
00446 }
00447 else if ($i < 20)
00448 {
00449 $class = "WeightedTagHigh";
00450 $style = self::$weightStyles[1];
00451 }
00452 else if ($i < 40)
00453 {
00454 $class = "WeightedTagMedium";
00455 $style = self::$weightStyles[2];
00456 }
00457 else if ($i < 65)
00458 {
00459 $class = "WeightedTagLow";
00460 $style = self::$weightStyles[3];
00461 }
00462 else
00463 {
00464 $class = "WeightedTagLowest";
00465 $style = self::$weightStyles[4];
00466 }
00467
00468 $weighted[] = "<span class=\"$class\" style=\"$style\">$link</span>";
00469
00470 $i++;
00471 }
00472
00473
00474 return implode(" ", $weighted);
00475 }
00476
00482 protected function getTagFieldParams()
00483 {
00484 return array(
00485 'id' => strtolower(get_class($this)),
00486 'value' => implode(", ", $this->getPrettyTags()),
00487 'title' => "Enter $this->plural<br/>(separated by commas)",
00488 'class' => 'BaseTags',
00489 'width' => '100%',
00490 'height' => '100px',
00491 );
00492 }
00493
00501 protected function getPopularFieldParams()
00502 {
00503 return array(
00504 'title' => "Popular $this->plural<br/>(click to add)",
00505 'text' => $this->getFormPopularCloud()
00506 );
00507 }
00508
00514 public function updateTagsForm($form)
00515 {
00516
00517 $data = $form->getData(strtolower(get_class($this)));
00518 $dataRs = explode(",", $data);
00519
00520
00521 $this->updateTags($dataRs);
00522 }
00523
00529 public function updateTags($data)
00530 {
00531
00532 $old = $this->getTags();
00533 $newTags = array();
00534 $oldTags = array();
00535
00536
00537 if (count($old))
00538 foreach ($old AS $tag)
00539 $oldTags[] = self::cleanTag($tag);
00540 if (count($data))
00541 foreach ($data AS $tag)
00542 $newTags[] = self::cleanTag($tag);
00543
00544
00545 if (count($newTags))
00546 {
00547 foreach ($newTags AS $tag)
00548 {
00549 if (!in_array($tag, $oldTags))
00550 $this->addTagLink($tag);
00551 }
00552 }
00553
00554
00555 if (count($oldTags))
00556 {
00557 foreach ($oldTags AS $tag)
00558 {
00559 if (!in_array($tag, $newTags))
00560 $this->removeTagLink($tag);
00561 }
00562 }
00563 }
00564
00570 public function addTagLink($tag)
00571 {
00572 $tag = self::cleanTag($tag);
00573
00574
00575 if ($tag != '')
00576 {
00577
00578 $tagId = $this->addTag($tag);
00579
00580
00581 if ($tagId)
00582 $this->addTagLinkDb($tagId);
00583 }
00584 }
00585
00591 protected function addTagLinkDb($tagId)
00592 {
00593 dbExecute("
00594 INSERT INTO $this->joinerTable
00595 (tag_id, object_id)
00596 VALUES
00597 ('$tagId', '$this->objectId')
00598 ");
00599 }
00600
00606 public function removeTagLink($tag)
00607 {
00608
00609 $tagId = $this->findTag($tag);
00610
00611
00612 if ($tagId)
00613 $this->removeTagLinkDb($tagId);
00614 }
00615
00621 public function removeTagLinkDb($tagId)
00622 {
00623 dbExecute("
00624 DELETE FROM $this->joinerTable
00625 WHERE tag_id = '$tagId'
00626 AND object_id = '$this->objectId'
00627 ");
00628 }
00629
00638 public function getRelatedTags($tag)
00639 {
00640
00641 $tagId = $this->findTag($tag);
00642
00643 $sql = "
00644 SELECT t.tag, count(t.tag) AS popularity
00645 FROM $this->tagTable t
00646 INNER JOIN $this->joinerTable j
00647 ON t.id = j.tag_id
00648 WHERE j.object_id IN (" . $this->getObjectsSql($tagId) . ")
00649 AND j.tag_id != '$tagId'
00650 GROUP BY t.tag
00651 ORDER BY popularity DESC, t.tag
00652 LIMIT 30
00653 ";
00654 $key = "BaseJumper:tags:popular:" . sha1($sql);
00655
00656
00657 if (self::$cacheRelatedTags)
00658 $tags = CacheBot::get($key, self::$relatedTagsCacheLife);
00659
00660
00661 if (!$tags)
00662 {
00663
00664 $tags = array();
00665
00666
00667 if ($tagId)
00668 {
00669
00670 $tagRs = dbQuery($sql);
00671
00672
00673 while ($tagAr = dbFetchAssoc($tagRs))
00674 $tags[] = $tagAr['tag'];
00675
00676
00677 if (self::$cacheRelatedTags)
00678 CacheBot::set($key, $tags, self::$relatedTagsCacheLife);
00679 }
00680 }
00681
00682 return $tags;
00683 }
00684
00692 public function getLinkedTags($tags)
00693 {
00694 $links = array();
00695
00696 if (count($tags))
00697 {
00698
00699 foreach ($tags AS $tag)
00700 $links[] = $this->getLink(".view?tag=$tag", self::prettyTag($tag));
00701 }
00702
00703 return $links;
00704 }
00705
00712 public function getTagList($tags = null)
00713 {
00714
00715 if ($tags === null)
00716 $tags = $this->getTags();
00717
00718
00719 $links = $this->getLinkedTags($tags);
00720
00721
00722 return implode(", ", $links);
00723 }
00724
00732 public function getRelatedTagsList($tag)
00733 {
00734 $tags = $this->getRelatedTags($tag);
00735
00736
00737 $links = $this->getLinkedTags($tags);
00738
00739
00740 return implode(", ", $links);
00741 }
00742
00748 protected function getFormPopularCloud()
00749 {
00750
00751 $tags = $this->getTags();
00752 $popular = $this->getPopularTags();
00753
00754
00755 $usable = array_diff($popular, $tags);
00756
00757
00758 if (count($usable))
00759 {
00760 foreach ($usable AS $tag)
00761 $links[] = "<a href=\"#\" onClick=\"return addTagToForm('" . strtolower(get_class($this)) . "', '" .
00762 self::prettyTag($tag) . "');\">" . self::prettyTag($tag) . "</a>";
00763
00764 return implode(", ", $links);
00765 }
00766
00767 return "No popular $this->plural found.";
00768 }
00769
00775 public function addTagField($form)
00776 {
00777 $form->add('TextAreaField', strtolower(get_class($this)), $this->getTagFieldParams());
00778 }
00779
00785 public function addPopularTagField($form)
00786 {
00787 $this->needsJs("lib/js/tags.js");
00788 $form->add('LabelField', 'popular_' . strtolower(get_class($this)), $this->getPopularFieldParams());
00789 }
00790
00797 public function addTagFields($form)
00798 {
00799 $this->addTagField($form);
00800 $this->addPopularTagField($form);
00801 }
00802
00808 public function getCreateTableSql()
00809 {
00810 $tagFields = implode(",\n", $this->getTagFieldsArray());
00811 $tagIndexes = implode(",\n", $this->getTagIndexesArray());
00812 $sql = "CREATE TABLE IF NOT EXISTS $this->tagTable\n(\n$tagFields,\n$tagIndexes\n);\n";
00813
00814 $joinerFields = implode(",\n", $this->getJoinerFieldsArray());
00815 $joinerIndexes = implode(",\n", $this->getJoinerIndexesArray());
00816 $sql .= "CREATE TABLE IF NOT EXISTS $this->joinerTable\n(\n$joinerFields,\n$joinerIndexes\n);\n";
00817
00818 return $sql;
00819 }
00820
00826 public function getTagFieldsArray()
00827 {
00828 $fields = array(
00829 "id" => "id INT(11) not null auto_increment",
00830 "tag" => "tag varchar(64) default '' not null"
00831 );
00832
00833 return $fields;
00834 }
00835
00841 public function getTagIndexesArray()
00842 {
00843 $fields = array(
00844 "id" => "PRIMARY KEY(id)",
00845 "tag" => "KEY (tag)"
00846 );
00847
00848 return $fields;
00849 }
00850
00856 public function getJoinerFieldsArray()
00857 {
00858 $fields = array(
00859 "tag_id" => "tag_id INT(11) not null default 0",
00860 "object_id" => "object_id INT(11) not null default 0",
00861 );
00862
00863 return $fields;
00864 }
00865
00871 public function getJoinerIndexesArray()
00872 {
00873 $fields = array(
00874 "tag_id" => "KEY(tag_id)",
00875 "object_id" => "KEY(object_id)",
00876 );
00877
00878 return $fields;
00879 }
00880 }
00881 ?>