| Total Complexity | 93 |
| Total Lines | 504 |
| Duplicated Lines | 0 % |
| Changes | 0 | ||
Complex classes like TagHandler often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use TagHandler, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 30 | class TagHandler extends \XoopsPersistableObjectHandler |
||
| 31 | { |
||
| 32 | public $table_link; |
||
| 33 | public $table_stats; |
||
| 34 | |||
| 35 | /** |
||
| 36 | * Constructor |
||
| 37 | * |
||
| 38 | * @param \XoopsDatabase|null $db reference to the {@link XoopsDatabase} |
||
| 39 | * object |
||
| 40 | */ |
||
| 41 | public function __construct(\XoopsDatabase $db = null) |
||
| 42 | { |
||
| 43 | parent::__construct($db, 'tag_tag', Tag::class, 'tag_id', 'tag_term'); |
||
| 44 | $this->table_link = $this->db->prefix('tag_link'); |
||
| 45 | $this->table_stats = $this->db->prefix('tag_stats'); |
||
| 46 | } |
||
| 47 | |||
| 48 | /** |
||
| 49 | * Get tags linked to an item |
||
| 50 | * |
||
| 51 | * @access public |
||
| 52 | * @param int $itemid item ID |
||
| 53 | * @param int $modid module ID, optional |
||
| 54 | * @param int $catid id of corresponding category, optional |
||
| 55 | * @return array associative array of tags (id, term) |
||
| 56 | */ |
||
| 57 | public function getByItem($itemid, $modid = 0, $catid = 0) |
||
| 58 | { |
||
| 59 | $ret = []; |
||
| 60 | |||
| 61 | $itemid = (int)$itemid; |
||
| 62 | $modid = (empty($modid) && is_object($GLOBALS['xoopsModule']) |
||
| 63 | && 'tag' !== $GLOBALS['xoopsModule']->getVar('dirname')) ? $GLOBALS['xoopsModule']->getVar('mid') : (int)$modid; |
||
| 64 | if (empty($itemid) || empty($modid)) { |
||
| 65 | return $ret; |
||
| 66 | } |
||
| 67 | |||
| 68 | $sql = 'SELECT o.tag_id, o.tag_term' |
||
| 69 | . " FROM {$this->table_link} AS l " |
||
| 70 | . " LEFT JOIN {$this->table} AS o ON o.{$this->keyName} = l.{$this->keyName} " |
||
| 71 | . " WHERE l.tag_itemid = {$itemid} AND l.tag_modid = {$modid}" |
||
| 72 | . (empty($catid) ? '' : (' AND l.tag_catid=' . (int)$catid)) |
||
| 73 | . ' ORDER BY o.tag_count DESC'; |
||
| 74 | if (false === ($result = $this->db->query($sql))) { |
||
| 75 | return $ret; |
||
| 76 | } |
||
| 77 | while (false !== ($myrow = $this->db->fetchArray($result))) { |
||
| 78 | $ret[$myrow[$this->keyName]] = $myrow['tag_term']; |
||
| 79 | } |
||
| 80 | |||
| 81 | return $ret; |
||
| 82 | } |
||
| 83 | |||
| 84 | /** |
||
| 85 | * Update tags linked to an item |
||
| 86 | * |
||
| 87 | * @access public |
||
| 88 | * @param array|string $tags array of $tags or a single tag |
||
| 89 | * @param int $itemid item ID |
||
| 90 | * @param int|string $modid module ID or module dirname, optional |
||
| 91 | * @param int $catid id of corresponding category, optional |
||
| 92 | * @return bool |
||
| 93 | */ |
||
| 94 | public function updateByItem($tags, $itemid, $modid = '', $catid = 0) |
||
| 95 | { |
||
| 96 | $catid = (int)$catid; |
||
| 97 | $itemid = (int)$itemid; |
||
| 98 | |||
| 99 | if (!empty($modid) && !is_numeric($modid)) { |
||
| 100 | if (($GLOBALS['xoopsModule'] instanceof \XoopsModule) |
||
| 101 | && ($modid == $GLOBALS['xoopsModule']->getVar('dirname'))) { |
||
| 102 | $modid = $GLOBALS['xoopsModule']->getVar('mid'); |
||
| 103 | } else { |
||
| 104 | /** @var \XoopsModuleHandler $moduleHandler */ |
||
| 105 | $moduleHandler = xoops_getHandler('module'); |
||
| 106 | $modid = ($module_obj = $moduleHandler->getByDirname($modid)) ? $module_obj->getVar('mid') : 0; |
||
| 107 | } |
||
| 108 | } elseif ($GLOBALS['xoopsModule'] instanceof XoopsModule) { |
||
|
|
|||
| 109 | $modid = $GLOBALS['xoopsModule']->getVar('mid'); |
||
| 110 | } |
||
| 111 | |||
| 112 | if (empty($itemid) || empty($modid)) { |
||
| 113 | return false; |
||
| 114 | } |
||
| 115 | |||
| 116 | if (empty($tags)) { |
||
| 117 | $tags = []; |
||
| 118 | } elseif (!is_array($tags)) { |
||
| 119 | require_once $GLOBALS['xoops']->path('/modules/tag/include/functions.php'); |
||
| 120 | $tags = tag_parse_tag(addslashes(stripslashes($tags))); |
||
| 121 | } |
||
| 122 | |||
| 123 | $tags_existing = $this->getByItem($itemid, $modid, $catid); |
||
| 124 | $tags_delete = array_diff(array_values($tags_existing), $tags); |
||
| 125 | $tags_add = array_diff($tags, array_values($tags_existing)); |
||
| 126 | $tags_update = []; |
||
| 127 | |||
| 128 | if (!empty($tags_delete)) { |
||
| 129 | $tags_delete = array_map([$this->db, 'quoteString'], $tags_delete); |
||
| 130 | if ($tags_id = &$this->getIds(new \Criteria('tag_term', '(' . implode(', ', $tags_delete) . ')', 'IN'))) { |
||
| 131 | $sql = "DELETE FROM {$this->table_link}" . ' WHERE ' . " {$this->keyName} IN (" . implode(', ', $tags_id) . ')' . " AND tag_modid = {$modid} AND tag_catid = {$catid} AND tag_itemid = {$itemid}"; |
||
| 132 | if (false === ($result = $this->db->queryF($sql))) { |
||
| 133 | //@todo: decide if we should do something here on failure |
||
| 134 | } |
||
| 135 | $sql = 'DELETE FROM ' . $this->table . ' WHERE ' . ' tag_count < 2 AND ' . " {$this->keyName} IN (" . implode(', ', $tags_id) . ')'; |
||
| 136 | if (false === ($result = $this->db->queryF($sql))) { |
||
| 137 | //xoops_error($this->db->error()); |
||
| 138 | } |
||
| 139 | |||
| 140 | $sql = 'UPDATE ' . $this->table . ' SET tag_count = tag_count - 1' . ' WHERE ' . " {$this->keyName} IN (" . implode(', ', $tags_id) . ')'; |
||
| 141 | if (false === ($result = $this->db->queryF($sql))) { |
||
| 142 | //xoops_error($this->db->error()); |
||
| 143 | } |
||
| 144 | $tags_update = $tags_id; |
||
| 145 | } |
||
| 146 | } |
||
| 147 | |||
| 148 | if (!empty($tags_add)) { |
||
| 149 | $tag_link = []; |
||
| 150 | $tag_count = []; |
||
| 151 | foreach ($tags_add as $tag) { |
||
| 152 | if ($tags_id = &$this->getIds(new \Criteria('tag_term', $tag))) { |
||
| 153 | $tag_id = $tags_id[0]; |
||
| 154 | $tag_count[] = $tag_id; |
||
| 155 | } else { |
||
| 156 | $tag_obj = $this->create(); |
||
| 157 | $tag_obj->setVar('tag_term', $tag); |
||
| 158 | $tag_obj->setVar('tag_count', 1); |
||
| 159 | $this->insert($tag_obj); |
||
| 160 | $tag_id = $tag_obj->getVar('tag_id'); |
||
| 161 | unset($tag_obj); |
||
| 162 | } |
||
| 163 | $tag_link[] = "({$tag_id}, {$itemid}, {$catid}, {$modid}, " . time() . ')'; |
||
| 164 | $tags_update[] = $tag_id; |
||
| 165 | } |
||
| 166 | $sql = "INSERT INTO {$this->table_link}" . ' (tag_id, tag_itemid, tag_catid, tag_modid, tag_time) ' . ' VALUES ' . implode(', ', $tag_link); |
||
| 167 | if (false === ($result = $this->db->queryF($sql))) { |
||
| 168 | //xoops_error($this->db->error()); |
||
| 169 | } |
||
| 170 | if (!empty($tag_count)) { |
||
| 171 | $sql = 'UPDATE ' . $this->table . ' SET tag_count = tag_count+1' . ' WHERE ' . " {$this->keyName} IN (" . implode(', ', $tag_count) . ')'; |
||
| 172 | if (false === ($result = $this->db->queryF($sql))) { |
||
| 173 | //xoops_error($this->db->error()); |
||
| 174 | } |
||
| 175 | } |
||
| 176 | } |
||
| 177 | if (is_array($tags_update)) { |
||
| 178 | foreach ($tags_update as $tag_id) { |
||
| 179 | $this->update_stats($tag_id, $modid, $catid); |
||
| 180 | } |
||
| 181 | } |
||
| 182 | |||
| 183 | return true; |
||
| 184 | } |
||
| 185 | |||
| 186 | /** |
||
| 187 | * Update count stats sor tag |
||
| 188 | * |
||
| 189 | * @access public |
||
| 190 | * @param int $tag_id |
||
| 191 | * @param int $modid |
||
| 192 | * @param int $catid |
||
| 193 | * @return bool |
||
| 194 | */ |
||
| 195 | public function update_stats($tag_id, $modid = 0, $catid = 0) |
||
| 196 | { |
||
| 197 | $tag_id = (int)$tag_id; |
||
| 198 | if (empty($tag_id)) { |
||
| 199 | return true; |
||
| 200 | } |
||
| 201 | |||
| 202 | $modid = (int)$modid; |
||
| 203 | $catid = empty($modid) ? -1 : (int)$catid; |
||
| 204 | $count = 0; |
||
| 205 | $sql = 'SELECT COUNT(*) ' . " FROM {$this->table_link}" . " WHERE tag_id = {$tag_id}" . (empty($modid) ? '' : " AND tag_modid = {$modid}") . (($catid < 0) ? '' : " AND tag_catid = {$catid}"); |
||
| 206 | |||
| 207 | if ($result = $this->db->query($sql)) { |
||
| 208 | list($count) = $this->db->fetchRow($result); |
||
| 209 | } |
||
| 210 | if (empty($modid)) { |
||
| 211 | $tag_obj = $this->get($tag_id); |
||
| 212 | if (empty($count)) { |
||
| 213 | $this->delete($tag_obj); |
||
| 214 | } else { |
||
| 215 | $tag_obj->setVar('tag_count', $count); |
||
| 216 | $this->insert($tag_obj, true); |
||
| 217 | } |
||
| 218 | } else { |
||
| 219 | if (empty($count)) { |
||
| 220 | $sql = "DELETE FROM {$this->table_stats}" . ' WHERE ' . " {$this->keyName} = {$tag_id}" . " AND tag_modid = {$modid}" . " AND tag_catid = {$catid}"; |
||
| 221 | |||
| 222 | if (false === $result = $this->db->queryF($sql)) { |
||
| 223 | //xoops_error($this->db->error()); |
||
| 224 | } |
||
| 225 | } else { |
||
| 226 | $ts_id = null; |
||
| 227 | $sql = 'SELECT ts_id, tag_count ' . " FROM {$this->table_stats}" . " WHERE {$this->keyName} = {$tag_id}" . " AND tag_modid = {$modid}" . " AND tag_catid = {$catid}"; |
||
| 228 | if ($result = $this->db->query($sql)) { |
||
| 229 | list($ts_id, $tag_count) = $this->db->fetchRow($result); |
||
| 230 | } |
||
| 231 | $sql = ''; |
||
| 232 | if ($ts_id && $tag_count != $count) { |
||
| 233 | $sql = "UPDATE {$this->table_stats}" . " SET tag_count = {$count}" . ' WHERE ' . " ts_id = {$ts_id}"; |
||
| 234 | } elseif (!$ts_id) { |
||
| 235 | $sql = "INSERT INTO {$this->table_stats}" . ' (tag_id, tag_modid, tag_catid, tag_count)' . " VALUES ({$tag_id}, {$modid}, {$catid}, {$count})"; |
||
| 236 | } |
||
| 237 | |||
| 238 | if (!empty($sql) && false === ($result = $this->db->queryF($sql))) { |
||
| 239 | //xoops_error($this->db->error()); |
||
| 240 | } |
||
| 241 | } |
||
| 242 | } |
||
| 243 | |||
| 244 | return true; |
||
| 245 | } |
||
| 246 | |||
| 247 | /** |
||
| 248 | * Get tags with item count |
||
| 249 | * |
||
| 250 | * @access public |
||
| 251 | * @param int $limit |
||
| 252 | * @param int $start |
||
| 253 | * @param null|\CriteriaElement|\CriteriaCompo $criteria {@link Criteria} |
||
| 254 | * @param null $fields |
||
| 255 | * @param bool $fromStats fetch from tag-stats table |
||
| 256 | * @return array associative array of tags (id, term, count) |
||
| 257 | */ |
||
| 258 | public function &getByLimit( |
||
| 259 | $limit = 0, |
||
| 260 | $start = 0, |
||
| 261 | \CriteriaElement $criteria = null, |
||
| 262 | $fields = null, |
||
| 263 | $fromStats = true)//&getByLimit($criteria = null, $fromStats = true) |
||
| 264 | { |
||
| 265 | $ret = []; |
||
| 266 | if ($fromStats) { |
||
| 267 | $sql = "SELECT DISTINCT(o.{$this->keyName}), o.tag_term, o.tag_status, SUM(l.tag_count) AS count, l.tag_modid" . " FROM {$this->table} AS o LEFT JOIN {$this->table_stats} AS l ON l.{$this->keyName} = o.{$this->keyName}"; |
||
| 268 | } else { |
||
| 269 | $sql = "SELECT DISTINCT(o.{$this->keyName}), o.tag_term, o.tag_status, COUNT(l.tl_id) AS count, l.tag_modid" . " FROM {$this->table} AS o LEFT JOIN {$this->table_link} AS l ON l.{$this->keyName} = o.{$this->keyName}"; |
||
| 270 | } |
||
| 271 | |||
| 272 | $limit = null; |
||
| 273 | $start = null; |
||
| 274 | $sort = ''; |
||
| 275 | $order = ''; |
||
| 276 | if (null !== $criteria && is_subclass_of($criteria, 'CriteriaCompo')) { |
||
| 277 | $sql .= ' ' . $criteria->renderWhere(); |
||
| 278 | $sort = $criteria->getSort(); |
||
| 279 | $order = $criteria->getOrder(); |
||
| 280 | $limit = $criteria->getLimit(); |
||
| 281 | $start = $criteria->getStart(); |
||
| 282 | } |
||
| 283 | $sql .= " GROUP BY o.{$this->keyName}"; |
||
| 284 | |||
| 285 | $order = mb_strtoupper($order); |
||
| 286 | $sort = mb_strtolower($sort); |
||
| 287 | switch ($sort) { |
||
| 288 | case 'a': |
||
| 289 | case 'alphabet': |
||
| 290 | $order = ('DESC' !== $order) ? 'ASC' : 'DESC'; |
||
| 291 | $sql .= " ORDER BY o.tag_term {$order}"; |
||
| 292 | break; |
||
| 293 | case 'id': |
||
| 294 | case 'time': |
||
| 295 | $order = ('ASC' !== $order) ? 'DESC' : 'ASC'; |
||
| 296 | $sql .= " ORDER BY o.{$this->keyName} {$order}"; |
||
| 297 | break; |
||
| 298 | case 'c': |
||
| 299 | case 'count': |
||
| 300 | default: |
||
| 301 | $order = ('ASC' !== $order) ? 'DESC' : 'ASC'; |
||
| 302 | $sql .= " ORDER BY count {$order}"; |
||
| 303 | break; |
||
| 304 | } |
||
| 305 | |||
| 306 | if (false === ($result = $this->db->query($sql, $limit, $start))) { |
||
| 307 | //xoops_error($this->db->error()); |
||
| 308 | $ret = null; |
||
| 309 | } else { |
||
| 310 | while (false !== ($myrow = $this->db->fetchArray($result))) { |
||
| 311 | $ret[$myrow[$this->keyName]] = [ |
||
| 312 | 'id' => $myrow[$this->keyName], |
||
| 313 | 'term' => htmlspecialchars($myrow['tag_term'], ENT_QUOTES | ENT_HTML5), |
||
| 314 | 'status' => $myrow['tag_status'], |
||
| 315 | 'modid' => $myrow['tag_modid'], |
||
| 316 | 'count' => (int)$myrow['count'], |
||
| 317 | ]; |
||
| 318 | } |
||
| 319 | } |
||
| 320 | |||
| 321 | return $ret; |
||
| 322 | } |
||
| 323 | |||
| 324 | /** |
||
| 325 | * Get count of tags |
||
| 326 | * |
||
| 327 | * @access public |
||
| 328 | * @param null|\CriteriaElement|\CriteriaCompo $criteria {@link Criteria) |
||
| 329 | * |
||
| 330 | * @return int count |
||
| 331 | */ |
||
| 332 | public function getCount(\CriteriaElement $criteria = null) |
||
| 333 | { |
||
| 334 | /* |
||
| 335 | $catid = (int)($catid); |
||
| 336 | $modid = (int)($modid); |
||
| 337 | */ |
||
| 338 | $sql = "SELECT COUNT(DISTINCT o.{$this->keyName})" . " FROM {$this->table} AS o LEFT JOIN {$this->table_link} AS l ON l.{$this->keyName} = o.{$this->keyName}"; |
||
| 339 | if ((null !== $criteria) && is_subclass_of($criteria, 'CriteriaElement')) { |
||
| 340 | $sql .= ' ' . $criteria->renderWhere(); |
||
| 341 | } |
||
| 342 | /* |
||
| 343 | $sql_where = " WHERE 1 = 1"; |
||
| 344 | if (!empty($modid)) { |
||
| 345 | $sql_where .= " AND l.tag_modid = {$modid}"; |
||
| 346 | } |
||
| 347 | if (empty($catid) || $catid > 0) { |
||
| 348 | $sql_where .= " AND l.tag_catid = {$catid}"; |
||
| 349 | } |
||
| 350 | |||
| 351 | $sql = $sql_select . " " . $sql_from . " " . $sql_where; |
||
| 352 | */ |
||
| 353 | if (false === ($result = $this->db->query($sql))) { |
||
| 354 | //xoops_error($this->db->error()); |
||
| 355 | $ret = 0; |
||
| 356 | } else { |
||
| 357 | list($ret) = $this->db->fetchRow($result); |
||
| 358 | } |
||
| 359 | |||
| 360 | return $ret; |
||
| 361 | } |
||
| 362 | |||
| 363 | /** |
||
| 364 | * Get items linked with a tag |
||
| 365 | * |
||
| 366 | * @param \CriteriaElement $criteria {@link Criteria} |
||
| 367 | * |
||
| 368 | * @return array associative array of items (id, modid, catid) |
||
| 369 | */ |
||
| 370 | public function getItems(\CriteriaElement $criteria = null) |
||
| 371 | { |
||
| 372 | $ret = []; |
||
| 373 | $sql = ' SELECT o.tl_id, o.tag_itemid, o.tag_modid, o.tag_catid, o.tag_time'; |
||
| 374 | $sql .= " FROM {$this->table_link} AS o LEFT JOIN {$this->table} AS l ON l.{$this->keyName} = o.{$this->keyName}"; |
||
| 375 | |||
| 376 | $limit = null; |
||
| 377 | $start = null; |
||
| 378 | $sort = ''; |
||
| 379 | $order = ''; |
||
| 380 | if ((null !== $criteria) && is_subclass_of($criteria, 'CriteriaElement')) { |
||
| 381 | $sql .= ' ' . $criteria->renderWhere(); |
||
| 382 | $sort = $criteria->getSort(); |
||
| 383 | $order = $criteria->getOrder(); |
||
| 384 | $limit = $criteria->getLimit(); |
||
| 385 | $start = $criteria->getStart(); |
||
| 386 | } |
||
| 387 | |||
| 388 | $order = mb_strtoupper($order); |
||
| 389 | $sort = mb_strtolower($sort); |
||
| 390 | switch ($sort) { |
||
| 391 | case 'i': |
||
| 392 | case 'item': |
||
| 393 | $order = ('DESC' !== $order) ? 'ASC' : 'DESC'; |
||
| 394 | $sql .= " ORDER BY o.tag_itemid {$order}, o.tl_id DESC"; |
||
| 395 | break; |
||
| 396 | case 'm': |
||
| 397 | case 'module': |
||
| 398 | $order = ('DESC' !== $order) ? 'ASC' : 'DESC'; |
||
| 399 | $sql .= " ORDER BY o.tag_modid {$order}, o.tl_id DESC"; |
||
| 400 | break; |
||
| 401 | case 't': |
||
| 402 | case 'time': |
||
| 403 | default: |
||
| 404 | $order = ('ASC' !== $order) ? 'DESC' : 'ASC'; |
||
| 405 | $sql .= " ORDER BY o.tl_id {$order}"; |
||
| 406 | break; |
||
| 407 | } |
||
| 408 | |||
| 409 | if (false === ($result = $this->db->query($sql, $limit, $start))) { |
||
| 410 | //xoops_error($this->db->error()); |
||
| 411 | $ret = []; |
||
| 412 | } else { |
||
| 413 | while (false !== ($myrow = $this->db->fetchArray($result))) { |
||
| 414 | $ret[$myrow['tl_id']] = [ |
||
| 415 | 'itemid' => $myrow['tag_itemid'], |
||
| 416 | 'modid' => $myrow['tag_modid'], |
||
| 417 | 'catid' => $myrow['tag_catid'], |
||
| 418 | 'time' => $myrow['tag_time'], |
||
| 419 | ]; |
||
| 420 | } |
||
| 421 | } |
||
| 422 | |||
| 423 | return $ret; |
||
| 424 | } |
||
| 425 | |||
| 426 | /** |
||
| 427 | * Get count of items linked with a tag |
||
| 428 | * |
||
| 429 | * @access public |
||
| 430 | * @param int $tag_id |
||
| 431 | * @param int $modid id of corresponding module, optional: 0 for all; >1 for a specific module |
||
| 432 | * @param int $catid id of corresponding category, optional |
||
| 433 | * @return int count |
||
| 434 | */ |
||
| 435 | public function getItemCount($tag_id, $modid = 0, $catid = 0) |
||
| 463 | } |
||
| 464 | |||
| 465 | /** |
||
| 466 | * delete an object as well as links relying on it |
||
| 467 | * |
||
| 468 | * @access public |
||
| 469 | * @param \XoopsObject $object $object {@link Tag} |
||
| 470 | * @param bool $force flag to force the query execution despite security settings |
||
| 471 | * @return bool |
||
| 472 | */ |
||
| 473 | public function delete(\XoopsObject $object, $force = true) |
||
| 503 | } |
||
| 504 | |||
| 505 | /** |
||
| 506 | * clean orphan links from database |
||
| 507 | * |
||
| 508 | * @access public |
||
| 509 | * @param string $table_link |
||
| 510 | * @param string $field_link |
||
| 511 | * @param string $field_object |
||
| 512 | * @return bool true on success |
||
| 513 | */ |
||
| 514 | public function cleanOrphan($table_link = '', $field_link = '', $field_object = '') |
||
| 515 | { |
||
| 516 | require_once $GLOBALS['xoops']->path('/modules/tag/functions.recon.php'); |
||
| 517 | |||
| 518 | //mod_loadFunctions("recon"); |
||
| 519 | return tag_cleanOrphan(); |
||
| 520 | } |
||
| 521 | |||
| 522 | /** |
||
| 523 | * get item Ids {@see XoopsPersistableObjectHandler} |
||
| 524 | * Overloads default method to provide type hint since |
||
| 525 | * this is a public function called by plugins |
||
| 526 | * |
||
| 527 | * @access public |
||
| 528 | * @param \CriteriaElement $ids |
||
| 529 | * @return array|bool object IDs or false on failure |
||
| 530 | */ |
||
| 531 | public function &getIds(\CriteriaElement $ids = null) |
||
| 534 | } |
||
| 535 | } |
||
| 536 |