| 1 | <?php |
|---|
| 2 | require_once(WCF_DIR.'/lib/util/TaggingUtil.class.php'); |
|---|
| 3 | require_once(WCF_DIR.'/lib/util/ArrayUtil.class.php'); |
|---|
| 4 | require_once(WCF_DIR.'/lib/util/StringUtil.class.php'); |
|---|
| 5 | require_once(WCF_DIR.'lib/data/tag/Tag.class.php'); |
|---|
| 6 | require_once(WCF_DIR.'lib/data/tag/TagCloudWrapper.class.php'); |
|---|
| 7 | require_once(WCF_DIR.'lib/data/tag/TagEngine.class.php'); |
|---|
| 8 | |
|---|
| 9 | /** |
|---|
| 10 | * Util for Tagging Operations |
|---|
| 11 | * |
|---|
| 12 | * @author Torben Brodt |
|---|
| 13 | * @package de.easy-coding.wcf.taggingreloaded |
|---|
| 14 | * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-3.0.html> |
|---|
| 15 | */ |
|---|
| 16 | class TaggingReloadedUtil { |
|---|
| 17 | public static function readFormParameters() { |
|---|
| 18 | $tags3 = array(); |
|---|
| 19 | |
|---|
| 20 | // handle reloaded input |
|---|
| 21 | if (isset($_REQUEST['taggingname']) && isset($_REQUEST['taggingval'])) { |
|---|
| 22 | if(count($_REQUEST['taggingname']) > count($_REQUEST['taggingval'])) { |
|---|
| 23 | $_REQUEST['taggingval'] = array_slice($_REQUEST['taggingval'], 0, count($_REQUEST['taggingname'])); |
|---|
| 24 | } else if(count($_REQUEST['taggingname']) < count($_REQUEST['taggingval'])) { |
|---|
| 25 | $_REQUEST['taggingname'] = array_slice($_REQUEST['taggingname'], 0, count($_REQUEST['taggingval'])); |
|---|
| 26 | } |
|---|
| 27 | $tags3 = array_combine($_REQUEST['taggingname'], $_REQUEST['taggingval']); |
|---|
| 28 | } |
|---|
| 29 | |
|---|
| 30 | // handle classic input |
|---|
| 31 | if (!isset($_REQUEST['wheeltags_enabled']) || !($_REQUEST['wheeltags_enabled'])) { |
|---|
| 32 | // copy reference |
|---|
| 33 | $tags = $tags3; |
|---|
| 34 | $tags3 = array(); |
|---|
| 35 | |
|---|
| 36 | // proceed with classic |
|---|
| 37 | if (isset($_REQUEST['tags']) && !empty($_REQUEST['tags'])) { |
|---|
| 38 | foreach(TaggingUtil::splitString($_REQUEST['tags']) as $tag) { |
|---|
| 39 | // use weights from modern input (even if hidden) |
|---|
| 40 | if(array_key_exists($tag, $tags)) { |
|---|
| 41 | $tags3[$tag] = $tags[$tag]; |
|---|
| 42 | } else if(array_key_exists($tag, $tags3)) { |
|---|
| 43 | $tags3[$tag] *= 1.33; // increase with 33% |
|---|
| 44 | } else { |
|---|
| 45 | $tags3[$tag] = 100; |
|---|
| 46 | } |
|---|
| 47 | } |
|---|
| 48 | } |
|---|
| 49 | } |
|---|
| 50 | |
|---|
| 51 | // conversion from post to Tag |
|---|
| 52 | foreach($tags3 as $name => $weight) { |
|---|
| 53 | if(strlen($name) < 3) { |
|---|
| 54 | unset($tags3[$name]); |
|---|
| 55 | continue; |
|---|
| 56 | } |
|---|
| 57 | |
|---|
| 58 | $tags3[$name] = new Tag(null, array( |
|---|
| 59 | 'name' => $name, |
|---|
| 60 | 'weight' => $weight |
|---|
| 61 | )); |
|---|
| 62 | } |
|---|
| 63 | |
|---|
| 64 | return $tags3; |
|---|
| 65 | } |
|---|
| 66 | |
|---|
| 67 | protected static function deleteFromTagging3($userID, $tags, Tagged $object, $languageIDArray) { |
|---|
| 68 | if(!count($tags)) return; |
|---|
| 69 | |
|---|
| 70 | $ids = array(); |
|---|
| 71 | foreach($tags as $tag) { |
|---|
| 72 | $ids[] = $tag->tagID; |
|---|
| 73 | } |
|---|
| 74 | |
|---|
| 75 | $sql = "DELETE FROM wcf".WCF_N."_tagging3 |
|---|
| 76 | WHERE taggableID = ".$object->getTaggable()->getTaggableID()." |
|---|
| 77 | AND languageID IN (".implode(',', $languageIDArray).") |
|---|
| 78 | AND objectID = ".$object->getObjectID()." |
|---|
| 79 | AND tagID IN (".implode(",", $ids).") |
|---|
| 80 | AND userID = ".intval($userID); |
|---|
| 81 | WCF::getDB()->sendQuery($sql); |
|---|
| 82 | $result = WCF::getDB()->sendQuery($sql); |
|---|
| 83 | } |
|---|
| 84 | |
|---|
| 85 | /** |
|---|
| 86 | * this is a reimplemenation of TagEngine::deleteObjectTags with a slite modification. |
|---|
| 87 | * not all tags are deleted. just some tagIDs |
|---|
| 88 | */ |
|---|
| 89 | protected static function deleteFromSystem($tags, Tagged $object, $languageIDArray) { |
|---|
| 90 | if(!count($tags)) return; |
|---|
| 91 | |
|---|
| 92 | $ids = array(); |
|---|
| 93 | foreach($tags as $tag) { |
|---|
| 94 | $ids[] = $tag->tagID; |
|---|
| 95 | } |
|---|
| 96 | |
|---|
| 97 | $sql = "DELETE FROM wcf".WCF_N."_tag_to_object |
|---|
| 98 | WHERE taggableID = ".$object->getTaggable()->getTaggableID()." |
|---|
| 99 | AND languageID IN (".implode(',', $languageIDArray).") |
|---|
| 100 | AND objectID = ".$object->getObjectID()." |
|---|
| 101 | AND tagID IN (".implode(",", $ids).")"; |
|---|
| 102 | WCF::getDB()->sendQuery($sql); |
|---|
| 103 | $result = WCF::getDB()->sendQuery($sql); |
|---|
| 104 | } |
|---|
| 105 | |
|---|
| 106 | protected static function addToSystem($tags, Tagged $object, $languageID) { |
|---|
| 107 | if(!count($tags)) return; |
|---|
| 108 | |
|---|
| 109 | $tags = array_keys($tags); |
|---|
| 110 | try { |
|---|
| 111 | TagEngine::getInstance()->addTags($tags, $object, $languageID); |
|---|
| 112 | } catch(Exception $e) { |
|---|
| 113 | // please handle |
|---|
| 114 | } |
|---|
| 115 | } |
|---|
| 116 | |
|---|
| 117 | /** |
|---|
| 118 | * this is a reimplementation of TagEngine::addTags with a slite modification to write to our table and to update existing tables with weight |
|---|
| 119 | */ |
|---|
| 120 | protected static function addToTagging3($userID, $tags, Tagged $object, $languageID) { |
|---|
| 121 | if(!count($tags)) return; |
|---|
| 122 | |
|---|
| 123 | // store tagging3 database |
|---|
| 124 | $tagIDs = array(); |
|---|
| 125 | foreach ($tags as $tag) { |
|---|
| 126 | $tagID = $tag->tagID ? $tag->tagID : Tag::test($tag->name, $languageID); |
|---|
| 127 | if (!$tagID) $tagID = Tag::insert($tag->name, $languageID); |
|---|
| 128 | |
|---|
| 129 | if (empty($tag->userID)) $tag->userID = $userID; |
|---|
| 130 | $tagIDs[$tagID] = $tag; |
|---|
| 131 | } |
|---|
| 132 | |
|---|
| 133 | $sql = "REPLACE INTO wcf".WCF_N."_tagging3 |
|---|
| 134 | (objectID, tagID, taggableID, languageID, userID, weight) |
|---|
| 135 | VALUES "; |
|---|
| 136 | foreach ($tagIDs as $tagID => $tag) { |
|---|
| 137 | $sql .= "(" . $object->getObjectID() . ", " . $tagID . ", " . $object->getTaggable()->getTaggableID() . ", |
|---|
| 138 | ".$languageID.", ".$tag->userID.", ".$tag->weight."),"; |
|---|
| 139 | } |
|---|
| 140 | $sql = StringUtil::substring($sql, 0, StringUtil::length($sql) - 1); |
|---|
| 141 | $result = WCF::getDB()->sendQuery($sql); |
|---|
| 142 | } |
|---|
| 143 | |
|---|
| 144 | /** |
|---|
| 145 | * save |
|---|
| 146 | */ |
|---|
| 147 | public static function tagging3Save($userID, $tagged, $languageID, array $tags3, array $existingTagsUser = array(), array $existingTagsObject = array()) { |
|---|
| 148 | $languageIDArray = array($languageID); |
|---|
| 149 | |
|---|
| 150 | // remove old links |
|---|
| 151 | if(count($existingTagsUser) || count($existingTagsObject)) { |
|---|
| 152 | |
|---|
| 153 | // tagging3: remove the link to the current user |
|---|
| 154 | $deleteFromTagging3 = TaggingReloadedUtil::diff($existingTagsUser, $tags3); |
|---|
| 155 | |
|---|
| 156 | // woltlab system: when no other user has this tag, remove the link from tag to object |
|---|
| 157 | $deleteFromSystem = TaggingReloadedUtil::diff($deleteFromTagging3, $existingTagsObject); |
|---|
| 158 | |
|---|
| 159 | // tagging3: ids are known.. delete is easy |
|---|
| 160 | self::deleteFromTagging3($userID, $deleteFromTagging3, $tagged, $languageIDArray); |
|---|
| 161 | |
|---|
| 162 | // woltlab system: ids are known.. delete is easy |
|---|
| 163 | self::deleteFromSystem($deleteFromSystem, $tagged, $languageIDArray); |
|---|
| 164 | } |
|---|
| 165 | |
|---|
| 166 | // tagging3: add user specific tags |
|---|
| 167 | $addToTagging3 = TaggingReloadedUtil::diff($tags3, $existingTagsUser); |
|---|
| 168 | |
|---|
| 169 | // woltlab system: add link from tag to object |
|---|
| 170 | $addToSystem = TaggingReloadedUtil::diff($addToTagging3, $existingTagsObject); |
|---|
| 171 | |
|---|
| 172 | // woltlab system: save |
|---|
| 173 | self::addToSystem($addToSystem, $tagged, $languageID); |
|---|
| 174 | |
|---|
| 175 | // tagging3: save |
|---|
| 176 | // attention: use $tags3 instead of the diff, because we want to update all weights |
|---|
| 177 | self::addToTagging3($userID, $tags3, $tagged, $languageID); |
|---|
| 178 | } |
|---|
| 179 | |
|---|
| 180 | /** |
|---|
| 181 | * @return Tag[] |
|---|
| 182 | */ |
|---|
| 183 | public static function getTagsByObject($userID, Tagged $tagged, $languageID, $even = true) { |
|---|
| 184 | $taggableID = $tagged->getTaggable()->getTaggableID(); |
|---|
| 185 | $objectID = $tagged->getObjectID(); |
|---|
| 186 | |
|---|
| 187 | $sign = $even ? '=' : '!='; |
|---|
| 188 | $signline = $even === null ? '' : "AND userID $sign ".intval($userID); |
|---|
| 189 | |
|---|
| 190 | $sql = "SELECT tag.*, |
|---|
| 191 | userID, |
|---|
| 192 | weight |
|---|
| 193 | FROM ( |
|---|
| 194 | SELECT tagID, |
|---|
| 195 | userID, |
|---|
| 196 | weight |
|---|
| 197 | FROM wcf".WCF_N."_tagging3 |
|---|
| 198 | WHERE taggableID = ".intval($taggableID)." |
|---|
| 199 | AND languageID = ".intval($languageID)." |
|---|
| 200 | AND objectID = ".intval($objectID)." |
|---|
| 201 | ".$signline." |
|---|
| 202 | ) x |
|---|
| 203 | INNER JOIN wcf".WCF_N."_tag tag USING(tagID) |
|---|
| 204 | "; |
|---|
| 205 | $result = WCF::getDB()->sendQuery($sql); |
|---|
| 206 | $tags = array(); |
|---|
| 207 | while ($row = WCF::getDB()->fetchArray($result)) { |
|---|
| 208 | $tags[$row['name']] = new Tag(null, $row); |
|---|
| 209 | } |
|---|
| 210 | return $tags; |
|---|
| 211 | } |
|---|
| 212 | |
|---|
| 213 | /** |
|---|
| 214 | * gets possible tags depending on rewrite rules |
|---|
| 215 | * |
|---|
| 216 | * @param string $name |
|---|
| 217 | */ |
|---|
| 218 | public static function getPossibleFromRewritten($name) { |
|---|
| 219 | // try with "-" and without |
|---|
| 220 | if(strpos($name, '-') !== null) { |
|---|
| 221 | $names[] = str_replace('-', ' ', $name); |
|---|
| 222 | } |
|---|
| 223 | |
|---|
| 224 | // try umlauts |
|---|
| 225 | $umlauts = array( |
|---|
| 226 | 'ae' => 'À', |
|---|
| 227 | 'oe' => 'ö', |
|---|
| 228 | 'ue' => 'Ì', |
|---|
| 229 | 'Ã' => 'ss', |
|---|
| 230 | ); |
|---|
| 231 | if(preg_match('/('.implode('|', array_keys($umlauts)).')/', $name)) { |
|---|
| 232 | $names[] = str_replace(array_keys($umlauts), $umlauts, $name); |
|---|
| 233 | } |
|---|
| 234 | return $names; |
|---|
| 235 | } |
|---|
| 236 | |
|---|
| 237 | /** |
|---|
| 238 | * returns difference between two lists full of tag objects |
|---|
| 239 | * |
|---|
| 240 | * @param array<Tag> $list1 |
|---|
| 241 | * @param array<Tag> $list2 |
|---|
| 242 | */ |
|---|
| 243 | public static function diff($list1, $list2) { |
|---|
| 244 | $diff = array_diff(array_keys($list1), array_keys($list2)); |
|---|
| 245 | foreach($list1 as $key => $val) { |
|---|
| 246 | if(!in_array($key, $diff)) { |
|---|
| 247 | unset($list1[$key]); |
|---|
| 248 | } |
|---|
| 249 | } |
|---|
| 250 | return $list1; |
|---|
| 251 | } |
|---|
| 252 | |
|---|
| 253 | /** |
|---|
| 254 | * begins with biggest value |
|---|
| 255 | */ |
|---|
| 256 | public static function sort($result, $column = 'counter', $sort = 'ASC') { |
|---|
| 257 | uasort($result, "TaggingReloadedUtil::sort".ucfirst($column)); |
|---|
| 258 | if($sort == 'DESC') { |
|---|
| 259 | arsort($result); |
|---|
| 260 | } |
|---|
| 261 | return $result; |
|---|
| 262 | } |
|---|
| 263 | |
|---|
| 264 | public static function sortCounter($a, $b) { |
|---|
| 265 | $key = 'counter'; |
|---|
| 266 | if ($a->$key == $b->$key) { |
|---|
| 267 | return 0; |
|---|
| 268 | } |
|---|
| 269 | return ($a->$key > $b->$key) ? 1 : -1; |
|---|
| 270 | } |
|---|
| 271 | |
|---|
| 272 | public static function sortName($a, $b) { |
|---|
| 273 | $key = 'name'; |
|---|
| 274 | if ($a->$key == $b->$key) { |
|---|
| 275 | return 0; |
|---|
| 276 | } |
|---|
| 277 | return ($a->$key > $b->$key) ? 1 : -1; |
|---|
| 278 | } |
|---|
| 279 | |
|---|
| 280 | /** |
|---|
| 281 | * |
|---|
| 282 | */ |
|---|
| 283 | public static function fromMagicString($query) { |
|---|
| 284 | $list = array(); |
|---|
| 285 | |
|---|
| 286 | // clean |
|---|
| 287 | $query = preg_replace('/(site:[^ ]+)/', '', $query); |
|---|
| 288 | $query = str_replace('\\', '', $query); |
|---|
| 289 | |
|---|
| 290 | // aggregated |
|---|
| 291 | if(preg_match_all('/"([^"]+)"|\'([^"]+)\'/', $query, $match)) { |
|---|
| 292 | $list = array_merge($match[1], $match[2]); |
|---|
| 293 | $list = array_filter($list); |
|---|
| 294 | $query = str_replace($match[0], ' ', $query); |
|---|
| 295 | } |
|---|
| 296 | |
|---|
| 297 | // c++ |
|---|
| 298 | $query = preg_replace('/[Cc]\+\+/', 'cpp', $query); |
|---|
| 299 | $query = preg_replace('/[\(\)\+\/]/', ' ', $query); |
|---|
| 300 | |
|---|
| 301 | $list2 = TaggingUtil::splitString($query, ',. '); |
|---|
| 302 | $list = array_merge($list, $list2); |
|---|
| 303 | |
|---|
| 304 | $negative_file = dirname(__FILE__).'/negative.list'; |
|---|
| 305 | if(file_exists($negative_file)) { |
|---|
| 306 | $negative = file($negative_file); |
|---|
| 307 | $negative = array_map("StringUtil::trim", $negative); |
|---|
| 308 | $list = array_diff($list, $negative); |
|---|
| 309 | } |
|---|
| 310 | |
|---|
| 311 | $arr = array(); |
|---|
| 312 | foreach($list as $tag) { |
|---|
| 313 | $arr[$tag] = new Tag(null, array( |
|---|
| 314 | 'name' => $tag |
|---|
| 315 | )); |
|---|
| 316 | } |
|---|
| 317 | |
|---|
| 318 | return $arr; |
|---|
| 319 | } |
|---|
| 320 | } |
|---|
| 321 | ?> |
|---|