| @@ -10,8 +10,9 @@ | ||
| 10 | 10 | { |
| 11 | 11 | const LIST_TYPE_STANDARD = 'standard'; |
| 12 | 12 | const LIST_TYPE_FRONT = 'front'; |
| 13 | 13 | const LIST_TYPE_FOLLOWUP = 'followup'; |
| 14 | + const COLOR_PARTS = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']; | |
| 14 | 15 | |
| 15 | 16 | public function __construct() |
| 16 | 17 | { |
| 17 | 18 | parent::__construct(); |
| @@ -28,16 +29,35 @@ | ||
| 28 | 29 | if ($value == 'lists.list_id') { |
| 29 | 30 | unset($settings['columns'][$key]); |
| 30 | 31 | continue; |
| 31 | 32 | } |
| 32 | - $settings['columns'][$key] = $key === 'join' ? $value : 'list.'.$value; | |
| 33 | + $settings['columns'][$key] = $key === 'join' ? acym_secureDBColumn($value) : 'list.'.acym_secureDBColumn($value); | |
| 33 | 34 | } |
| 34 | 35 | $columns = implode(', ', $settings['columns']); |
| 35 | 36 | } |
| 36 | 37 | |
| 37 | 38 | $query = 'SELECT '.$columns.' FROM #__acym_list AS list'; |
| 38 | - $queryCount = 'SELECT COUNT(list.id) FROM #__acym_list AS list'; | |
| 39 | + $queryCount = 'SELECT COUNT(list.id) AS total FROM #__acym_list AS list'; | |
| 39 | 40 | if (!empty($settings['join'])) $query .= $this->getJoinForQuery($settings['join']); |
| 41 | + /* | |
| 42 | + This query will return for example: | |
| 43 | + | |
| 44 | + array( | |
| 45 | + 6 => 0, | |
| 46 | + 2 => 1, | |
| 47 | + 5 => 2, | |
| 48 | + 12 => 3 | |
| 49 | + ) | |
| 50 | + | |
| 51 | + It would mean that: | |
| 52 | + | |
| 53 | + 6 lists are disabled and invisible 0 + (0*2) = 0 | |
| 54 | + 2 lists are active and invisible 1 + (0*2) = 1 | |
| 55 | + 5 lists are inactive and visible 0 + (1*2) = 2 | |
| 56 | + 12 lists are active and visible 1 + (1*2) = 3 | |
| 57 | + | |
| 58 | + So there are 2 + 12 active lists and 5 + 12 visible lists, get it? | |
| 59 | + */ | |
| 40 | 60 | $queryStatus = 'SELECT COUNT(id) AS number, active + (visible*2) AS score FROM #__acym_list AS list'; |
| 41 | 61 | $filters = []; |
| 42 | 62 | $listsId = []; |
| 43 | 63 | |
| @@ -107,9 +127,9 @@ | ||
| 107 | 127 | if (!empty($settings['join'])) { |
| 108 | 128 | $this->setSelectedList($results['elements'], $settings['join']); |
| 109 | 129 | } |
| 110 | 130 | foreach ($results['elements'] as $i => $oneList) { |
| 111 | - array_push($listsId, $oneList->id); | |
| 131 | + $listsId[] = $oneList->id; | |
| 112 | 132 | $results['elements'][$i]->sendable_users = 0; |
| 113 | 133 | $results['elements'][$i]->unconfirmed_users = 0; |
| 114 | 134 | $results['elements'][$i]->unsubscribed_users = 0; |
| 115 | 135 | $results['elements'][$i]->inactive_users = 0; |
| @@ -119,10 +139,10 @@ | ||
| 119 | 139 | |
| 120 | 140 | $countUserByList = []; |
| 121 | 141 | $countEvolByList = []; |
| 122 | 142 | if (!empty($listsId) && empty($settings['entitySelect'])) { |
| 123 | - $countUserByList = $this->getSubscribersCountPerStatusByListId($listsId); | |
| 124 | - $countEvolByList = $this->getSubscribersEvolutionByList($listsId); | |
| 143 | + $countUserByList = $this->getSubscribersCountPerStatusByListIds($listsId); | |
| 144 | + $countEvolByList = $this->getSubscribersEvolutionByListIds($listsId); | |
| 125 | 145 | } |
| 126 | 146 | |
| 127 | 147 | foreach ($results['elements'] as $i => $list) { |
| 128 | 148 | $results['elements'][$i]->tags = []; |
| @@ -137,12 +157,12 @@ | ||
| 137 | 157 | if (isset($countEvolByList[$list->id]->newSub)) $results['elements'][$i]->newSub = $countEvolByList[$list->id]->newSub; |
| 138 | 158 | if (isset($countEvolByList[$list->id]->newUnsub)) $results['elements'][$i]->newUnsub = $countEvolByList[$list->id]->newUnsub; |
| 139 | 159 | } |
| 140 | 160 | |
| 141 | - $results['total'] = acym_loadResult($queryCount); | |
| 161 | + $results['total'] = acym_loadObject($queryCount); | |
| 142 | 162 | |
| 143 | 163 | $listsPerStatus = acym_loadObjectList($queryStatus.' GROUP BY score', 'score'); |
| 144 | - for ($i = 0 ; $i < 4 ; $i++) { | |
| 164 | + for ($i = 0; $i < 4; $i++) { | |
| 145 | 165 | $listsPerStatus[$i] = empty($listsPerStatus[$i]) ? 0 : $listsPerStatus[$i]->number; |
| 146 | 166 | } |
| 147 | 167 | |
| 148 | 168 | $results['status'] = [ |
| @@ -155,9 +175,9 @@ | ||
| 155 | 175 | |
| 156 | 176 | return $results; |
| 157 | 177 | } |
| 158 | 178 | |
| 159 | - public function getOneById($id) | |
| 179 | + public function getOneById(int $id): ?object | |
| 160 | 180 | { |
| 161 | 181 | $list = parent::getOneById($id); |
| 162 | 182 | |
| 163 | 183 | if (!empty($list)) { |
| @@ -166,21 +186,23 @@ | ||
| 166 | 186 | |
| 167 | 187 | return $list; |
| 168 | 188 | } |
| 169 | 189 | |
| 170 | - private function setSelectedList(&$elements, $join) | |
| 190 | + private function setSelectedList(array $elements, string $join): void | |
| 171 | 191 | { |
| 172 | - if (strpos($join, 'join_list') !== false) { | |
| 173 | - $listsIds = explode('-', $join); | |
| 174 | - $listsIds = $listsIds[1]; | |
| 175 | - $listsIds = explode(',', $listsIds); | |
| 176 | - foreach ($elements as $key => $element) { | |
| 177 | - $elements[$key]->list_id = in_array($element->id, $listsIds) ? $element->id : null; | |
| 178 | - } | |
| 192 | + if (strpos($join, 'join_list') === false) { | |
| 193 | + return; | |
| 179 | 194 | } |
| 195 | + | |
| 196 | + $listsIds = explode('-', $join); | |
| 197 | + $listsIds = $listsIds[1]; | |
| 198 | + $listsIds = explode(',', $listsIds); | |
| 199 | + foreach ($elements as $element) { | |
| 200 | + $element->list_id = in_array($element->id, $listsIds) ? $element->id : null; | |
| 201 | + } | |
| 180 | 202 | } |
| 181 | 203 | |
| 182 | - public function getJoinForQuery($joinType) | |
| 204 | + private function getJoinForQuery(string $joinType): string | |
| 183 | 205 | { |
| 184 | 206 | if (strpos($joinType, 'join_mail') !== false) { |
| 185 | 207 | $mailId = explode('-', $joinType); |
| 186 | 208 | |
| @@ -194,9 +216,9 @@ | ||
| 194 | 216 | |
| 195 | 217 | return ''; |
| 196 | 218 | } |
| 197 | 219 | |
| 198 | - public function getListsWithIdNameCount($settings) | |
| 220 | + public function getListsWithIdNameCount(array $settings): array | |
| 199 | 221 | { |
| 200 | 222 | $filters = []; |
| 201 | 223 | |
| 202 | 224 | if (isset($settings['ids'])) { |
| @@ -254,24 +276,20 @@ | ||
| 254 | 276 | } |
| 255 | 277 | |
| 256 | 278 | $query .= ' GROUP BY list.id '; |
| 257 | 279 | |
| 258 | - $results['lists'] = acym_loadObjectList($query, '', $settings['offset'], $settings['listsPerPage']); | |
| 259 | - $results['total'] = acym_loadResult($queryCount); | |
| 260 | - | |
| 261 | - return $results; | |
| 280 | + return [ | |
| 281 | + 'lists' => acym_loadObjectList($query, '', $settings['offset'], $settings['listsPerPage']), | |
| 282 | + 'total' => (int)acym_loadResult($queryCount), | |
| 283 | + ]; | |
| 262 | 284 | } |
| 263 | 285 | |
| 264 | - public function getOneByName($name) | |
| 286 | + public function getListsByIds(array $ids): array | |
| 265 | 287 | { |
| 266 | - return acym_loadObject('SELECT * FROM #__acym_list WHERE `name` = '.acym_escapeDB($name)); | |
| 267 | - } | |
| 268 | - | |
| 269 | - public function getListsByIds($ids) | |
| 270 | - { | |
| 271 | - if (!is_array($ids)) $ids = [$ids]; | |
| 272 | 288 | acym_arrayToInteger($ids); |
| 273 | - if (empty($ids)) return []; | |
| 289 | + if (empty($ids)) { | |
| 290 | + return []; | |
| 291 | + } | |
| 274 | 292 | |
| 275 | 293 | $query = 'SELECT * FROM #__acym_list WHERE id IN ('.implode(', ', $ids).')'; |
| 276 | 294 | |
| 277 | 295 | return acym_loadObjectList($query); |
| @@ -276,42 +294,14 @@ | ||
| 276 | 294 | |
| 277 | 295 | return acym_loadObjectList($query); |
| 278 | 296 | } |
| 279 | 297 | |
| 280 | - public function getAllListUsers() | |
| 298 | + public function getSubscribersCount(array $listsIds): int | |
| 281 | 299 | { |
| 282 | - $query = 'SELECT #__acym_user_has_list.list_id, count(*) | |
| 283 | - FROM #__acym_list AS list | |
| 284 | - JOIN #__acym_user_has_list | |
| 285 | - ON list.id = #__acym_user_has_list.list_id | |
| 286 | - JOIN #__acym_user | |
| 287 | - ON #__acym_user.id = #__acym_user_has_list.user_id | |
| 288 | - GROUP BY list.id'; | |
| 289 | - | |
| 290 | - return acym_loadObjectList($query); | |
| 291 | - } | |
| 292 | - | |
| 293 | - public function getSubscribersCountByListId($id) | |
| 294 | - { | |
| 295 | - $confirmed = $this->config->get('require_confirmation', 1) == 1 ? ' AND users.confirmed = 1 ' : ''; | |
| 296 | - | |
| 297 | - $query = 'SELECT COUNT(userLists.user_id) AS subscribers | |
| 298 | - FROM #__acym_user_has_list AS userLists | |
| 299 | - JOIN #__acym_user AS users ON userLists.user_id = users.id | |
| 300 | - WHERE userLists.list_id = '.intval($id).' | |
| 301 | - AND userLists.status = 1 | |
| 302 | - AND users.active = 1 '.$confirmed.' | |
| 303 | - GROUP BY userLists.list_id'; | |
| 304 | - | |
| 305 | - $result = acym_loadResult($query); | |
| 306 | - | |
| 307 | - return empty($result) ? 0 : $result; | |
| 308 | - } | |
| 309 | - | |
| 310 | - public function getSubscribersCount($listsIds) | |
| 311 | - { | |
| 312 | 300 | acym_arrayToInteger($listsIds); |
| 313 | - if (empty($listsIds)) return 0; | |
| 301 | + if (empty($listsIds)) { | |
| 302 | + return 0; | |
| 303 | + } | |
| 314 | 304 | |
| 315 | 305 | $query = 'SELECT COUNT(DISTINCT user.id) |
| 316 | 306 | FROM #__acym_user AS user |
| 317 | 307 | JOIN #__acym_user_has_list AS userList ON user.id = userList.user_id |
| @@ -320,30 +310,22 @@ | ||
| 320 | 310 | if ($this->config->get('require_confirmation', 1) == 1) { |
| 321 | 311 | $query .= ' AND user.confirmed = 1'; |
| 322 | 312 | } |
| 323 | 313 | |
| 324 | - return acym_loadResult($query); | |
| 314 | + return (int)acym_loadResult($query); | |
| 325 | 315 | } |
| 326 | 316 | |
| 327 | - public function getSubscribersIdsById($listId, $returnUnsubscribed = false) | |
| 317 | + public function getSubscribersForList(array $options = []): array | |
| 328 | 318 | { |
| 329 | - $query = 'SELECT user_id FROM #__acym_user_has_list WHERE list_id = '.intval($listId); | |
| 330 | - | |
| 331 | - if (!$returnUnsubscribed) { | |
| 332 | - $query .= ' AND status = 1'; | |
| 333 | - } | |
| 334 | - | |
| 335 | - return acym_loadResultArray($query); | |
| 336 | - } | |
| 337 | - | |
| 338 | - public function getSubscribersForList(array $options = []) | |
| 339 | - { | |
| 340 | 319 | $listIds = $options['listIds'] ?? []; |
| 341 | 320 | $offset = $options['offset'] ?? 0; |
| 342 | 321 | $limit = $options['limit'] ?? 100; |
| 343 | 322 | $status = $options['status'] ?? null; |
| 344 | 323 | $orderBy = $options['orderBy'] ?? ''; |
| 345 | - $orderBySort = $options['orderBySort'] ?? ''; | |
| 324 | + $orderBySort = strtolower((string)($options['orderBySort'] ?? '')); | |
| 325 | + if (!in_array($orderBySort, ['asc', 'desc'], true)) { | |
| 326 | + $orderBySort = empty($orderBy) ? 'asc' : 'desc'; | |
| 327 | + } | |
| 346 | 328 | $subscribedAfter = $options['subscribed_after'] ?? null; |
| 347 | 329 | $unsubscribedAfter = $options['unsubscribed_after'] ?? null; |
| 348 | 330 | |
| 349 | 331 | if (empty($listIds)) { |
| @@ -368,11 +350,14 @@ | ||
| 368 | 350 | $requestSub = 'SELECT user.*, user_list.status, user_list.subscription_date, user_list.unsubscribe_date FROM #__acym_user AS user'; |
| 369 | 351 | $requestSub .= ' LEFT JOIN #__acym_user_has_list AS user_list ON user.id = user_list.user_id'; |
| 370 | 352 | $requestSub .= ' WHERE user.active = 1 AND user_list.list_id IN ('.implode(', ', $listIds).')'.$statusCondition; |
| 371 | 353 | |
| 372 | - if (!empty($orderBy)) { | |
| 373 | - if (empty($orderBySort)) $orderBySort = 'desc'; | |
| 374 | - $requestSub .= ' ORDER BY '.acym_secureDBColumn($orderBy).' '.acym_secureDBColumn($orderBySort); | |
| 354 | + $orderByColumn = empty($orderBy) ? 'user.id' : acym_secureDBColumn($orderBy); | |
| 355 | + $requestSub .= ' ORDER BY '.$orderByColumn.' '.$orderBySort; | |
| 356 | + | |
| 357 | + // Any other column can hold duplicates, we need a unique one to break the ties | |
| 358 | + if (!in_array($orderByColumn, ['id', 'user.id'], true)) { | |
| 359 | + $requestSub .= ', user.id ASC'; | |
| 375 | 360 | } |
| 376 | 361 | |
| 377 | 362 | return acym_loadObjectList( |
| 378 | 363 | $requestSub, |
| @@ -381,16 +366,17 @@ | ||
| 381 | 366 | $limit |
| 382 | 367 | ); |
| 383 | 368 | } |
| 384 | 369 | |
| 385 | - public function delete($elements, $force = false) | |
| 370 | + public function delete(array $elements, bool $force = false): int | |
| 386 | 371 | { |
| 387 | - if (!is_array($elements)) $elements = [$elements]; | |
| 388 | 372 | if (!$force) { |
| 389 | 373 | $this->onlyManageableLists($elements); |
| 390 | 374 | } |
| 391 | 375 | |
| 392 | - if (empty($elements)) return 0; | |
| 376 | + if (empty($elements)) { | |
| 377 | + return 0; | |
| 378 | + } | |
| 393 | 379 | |
| 394 | 380 | foreach ($elements as $id) { |
| 395 | 381 | acym_query('DELETE FROM #__acym_mail_has_list WHERE list_id = '.intval($id)); |
| 396 | 382 | acym_query('DELETE FROM #__acym_user_has_list WHERE list_id = '.intval($id)); |
| @@ -399,16 +385,18 @@ | ||
| 399 | 385 | |
| 400 | 386 | return parent::delete($elements); |
| 401 | 387 | } |
| 402 | 388 | |
| 403 | - public function synchDeleteCmsList($userId) | |
| 389 | + public function synchDeleteCmsList(int $userId): void | |
| 404 | 390 | { |
| 405 | 391 | $query = 'SELECT * FROM #__acym_list WHERE type = "'.self::LIST_TYPE_FRONT.'" AND cms_user_id = '.intval($userId); |
| 406 | 392 | $listFrontManagement = acym_loadObject($query); |
| 407 | - if (!empty($listFrontManagement)) $this->delete([$listFrontManagement->id]); | |
| 393 | + if (!empty($listFrontManagement)) { | |
| 394 | + $this->delete([$listFrontManagement->id]); | |
| 395 | + } | |
| 408 | 396 | } |
| 409 | 397 | |
| 410 | - public function save($list) | |
| 398 | + public function save(object $list): ?int | |
| 411 | 399 | { |
| 412 | 400 | if (isset($list->tags)) { |
| 413 | 401 | $tags = $list->tags; |
| 414 | 402 | unset($list->tags); |
| @@ -434,9 +422,9 @@ | ||
| 434 | 422 | |
| 435 | 423 | foreach ($list as $oneAttribute => $value) { |
| 436 | 424 | if (empty($value)) continue; |
| 437 | 425 | |
| 438 | - $list->$oneAttribute = strip_tags($value); | |
| 426 | + $list->$oneAttribute = acym_stripTags($value); | |
| 439 | 427 | } |
| 440 | 428 | |
| 441 | 429 | if (empty($list->description)) { |
| 442 | 430 | $list->description = ''; |
| @@ -453,9 +441,13 @@ | ||
| 453 | 441 | } |
| 454 | 442 | |
| 455 | 443 | $listID = parent::save($list); |
| 456 | 444 | |
| 457 | - if (!empty($listID) && isset($tags)) { | |
| 445 | + if (empty($listID)) { | |
| 446 | + return null; | |
| 447 | + } | |
| 448 | + | |
| 449 | + if (isset($tags)) { | |
| 458 | 450 | $tagClass = new TagClass(); |
| 459 | 451 | $tagClass->setTags(TagClass::TYPE_LIST, intval($listID), $tags); |
| 460 | 452 | } |
| 461 | 453 | |
| @@ -491,9 +483,9 @@ | ||
| 491 | 483 | |
| 492 | 484 | return $listsToReturn; |
| 493 | 485 | } |
| 494 | 486 | |
| 495 | - public function getAllForSelect($emptyFirst = true, $userFrontID = 0, $needTranslation = false, $needFrontLabel = false) | |
| 487 | + public function getAllForSelect(bool $emptyFirst = true, int $userFrontID = 0, bool $needTranslation = false, bool $needFrontLabel = false): array | |
| 496 | 488 | { |
| 497 | 489 | $groupCondition = ''; |
| 498 | 490 | if (!empty($userFrontID)) { |
| 499 | 491 | $userGroups = acym_getGroupsByUser($userFrontID); |
| @@ -507,9 +499,11 @@ | ||
| 507 | 499 | } |
| 508 | 500 | |
| 509 | 501 | $return = []; |
| 510 | 502 | |
| 511 | - if ($emptyFirst) $return[''] = acym_translation('ACYM_SELECT_A_LIST'); | |
| 503 | + if ($emptyFirst) { | |
| 504 | + $return[''] = acym_translation('ACYM_SELECT_A_LIST'); | |
| 505 | + } | |
| 512 | 506 | |
| 513 | 507 | foreach ($lists as $key => $list) { |
| 514 | 508 | $return[$key] = $needFrontLabel && !empty($list->display_name) ? $list->display_name : $list->name; |
| 515 | 509 | } |
| @@ -516,9 +510,9 @@ | ||
| 516 | 510 | |
| 517 | 511 | return $return; |
| 518 | 512 | } |
| 519 | 513 | |
| 520 | - public function getAllWithoutManagement($needTranslation = false) | |
| 514 | + public function getAllWithoutManagement(bool $needTranslation = false): array | |
| 521 | 515 | { |
| 522 | 516 | $lists = acym_loadObjectList('SELECT * FROM #__acym_list WHERE `type` = '.acym_escapeDB(self::LIST_TYPE_STANDARD), 'id'); |
| 523 | 517 | |
| 524 | 518 | if (acym_isMultilingual() && $needTranslation) { |
| @@ -527,9 +521,9 @@ | ||
| 527 | 521 | |
| 528 | 522 | return $lists; |
| 529 | 523 | } |
| 530 | 524 | |
| 531 | - public function getTranslatedNameDescription($lists) | |
| 525 | + public function getTranslatedNameDescription(array $lists): array | |
| 532 | 526 | { |
| 533 | 527 | $currentLanguageTag = acym_getLanguageTag(); |
| 534 | 528 | foreach ($lists as $id => $list) { |
| 535 | 529 | if (empty($list->translation)) continue; |
| @@ -536,17 +530,17 @@ | ||
| 536 | 530 | |
| 537 | 531 | $list->translation = json_decode($list->translation, true); |
| 538 | 532 | if (!empty($list->translation[$currentLanguageTag])) { |
| 539 | 533 | if (!empty($list->translation[$currentLanguageTag]['name'])) { |
| 540 | - $lists[$id]->name = $list->translation[$currentLanguageTag]['name']; | |
| 534 | + $list->name = $list->translation[$currentLanguageTag]['name']; | |
| 541 | 535 | } |
| 542 | 536 | |
| 543 | 537 | if (!empty($list->translation[$currentLanguageTag]['display_name'])) { |
| 544 | - $lists[$id]->display_name = $list->translation[$currentLanguageTag]['display_name']; | |
| 538 | + $list->display_name = $list->translation[$currentLanguageTag]['display_name']; | |
| 545 | 539 | } |
| 546 | 540 | |
| 547 | 541 | if (!empty($list->translation[$currentLanguageTag]['description'])) { |
| 548 | - $lists[$id]->description = $list->translation[$currentLanguageTag]['description']; | |
| 542 | + $list->description = $list->translation[$currentLanguageTag]['description']; | |
| 549 | 543 | } |
| 550 | 544 | } |
| 551 | 545 | } |
| 552 | 546 | |
| @@ -552,9 +546,12 @@ | ||
| 552 | 546 | |
| 553 | 547 | return $lists; |
| 554 | 548 | } |
| 555 | 549 | |
| 556 | - public function sendWelcome(int $userID, array $listIDs, bool $forceFront = false) | |
| 550 | + /** | |
| 551 | + * Sends the welcome emails attached to the specified lists | |
| 552 | + */ | |
| 553 | + public function sendWelcome(int $userID, array $listIDs, bool $forceFront = false): void | |
| 557 | 554 | { |
| 558 | 555 | if (!$forceFront && acym_isAdmin()) { |
| 559 | 556 | return; |
| 560 | 557 | } |
| @@ -571,13 +568,14 @@ | ||
| 571 | 568 | } |
| 572 | 569 | |
| 573 | 570 | $alreadySent = []; |
| 574 | 571 | $mailerHelper = new MailerHelper(); |
| 575 | - $mailerHelper->report = $this->config->get('welcome_message', 1); | |
| 572 | + $mailerHelper->report = (bool)$this->config->get('welcome_message', 1); | |
| 576 | 573 | foreach ($messages as $oneMessage) { |
| 577 | 574 | $mailid = $oneMessage->welcome_id; |
| 578 | 575 | if (empty($mailid)) continue; |
| 579 | 576 | |
| 577 | + //We don't send twice the same message | |
| 580 | 578 | if (isset($alreadySent[$mailid])) { |
| 581 | 579 | continue; |
| 582 | 580 | } |
| 583 | 581 | |
| @@ -586,9 +584,12 @@ | ||
| 586 | 584 | $alreadySent[$mailid] = true; |
| 587 | 585 | } |
| 588 | 586 | } |
| 589 | 587 | |
| 590 | - public function sendUnsubscribe($userID, $listIDs) | |
| 588 | + /** | |
| 589 | + * Sends the unsubscribe emails attached to the specified lists | |
| 590 | + */ | |
| 591 | + public function sendUnsubscribe(int $userID, array $listIDs): void | |
| 591 | 592 | { |
| 592 | 593 | if (acym_isAdmin()) { |
| 593 | 594 | return; |
| 594 | 595 | } |
| @@ -605,13 +606,14 @@ | ||
| 605 | 606 | } |
| 606 | 607 | |
| 607 | 608 | $alreadySent = []; |
| 608 | 609 | $mailerHelper = new MailerHelper(); |
| 609 | - $mailerHelper->report = $this->config->get('unsub_message', 1); | |
| 610 | + $mailerHelper->report = (bool)$this->config->get('unsub_message', 1); | |
| 610 | 611 | foreach ($messages as $oneMessage) { |
| 611 | 612 | if (!empty($oneMessage->unsubscribe_id)) { |
| 612 | 613 | $mailid = $oneMessage->unsubscribe_id; |
| 613 | 614 | |
| 615 | + //We don't send twice the same message | |
| 614 | 616 | if (isset($alreadySent[$mailid])) { |
| 615 | 617 | continue; |
| 616 | 618 | } |
| 617 | 619 | |
| @@ -634,10 +636,9 @@ | ||
| 634 | 636 | $this->save($defaultList); |
| 635 | 637 | } |
| 636 | 638 | } |
| 637 | 639 | |
| 638 | - | |
| 639 | - public function getTotalSubCount($ids) | |
| 640 | + public function getTotalSubCount(array $ids): int | |
| 640 | 641 | { |
| 641 | 642 | if (empty($ids)) { |
| 642 | 643 | return 0; |
| 643 | 644 | } |
| @@ -659,24 +660,32 @@ | ||
| 659 | 660 | |
| 660 | 661 | return intval(acym_loadResult($query)); |
| 661 | 662 | } |
| 662 | 663 | |
| 663 | - public function getMailsByListId($listId) | |
| 664 | + /** | |
| 665 | + * Get all mails attached to a list | |
| 666 | + */ | |
| 667 | + public function getMailsByListId(int $listId): array | |
| 664 | 668 | { |
| 665 | - $query = 'SELECT mail_id FROM #__acym_mail_has_list WHERE list_id = '.intval($listId); | |
| 669 | + $mailIds = acym_loadResultArray('SELECT mail_id FROM #__acym_mail_has_list WHERE list_id = '.intval($listId)); | |
| 666 | 670 | |
| 667 | - return acym_loadResultArray($query); | |
| 671 | + return empty($mailIds) ? [] : $mailIds; | |
| 668 | 672 | } |
| 669 | 673 | |
| 670 | - public function getSubscribersCountPerStatusByListId($listIds = []) | |
| 674 | + public function getSubscribersCountPerStatusByListIds(array $listIds = []): array | |
| 671 | 675 | { |
| 672 | 676 | $condList = ''; |
| 673 | 677 | if (!empty($listIds)) { |
| 674 | - if (!is_array($listIds)) $listIds = [$listIds]; | |
| 675 | 678 | acym_arrayToInteger($listIds); |
| 676 | 679 | $condList = 'AND userList.list_id IN ('.implode(',', $listIds).')'; |
| 677 | 680 | } |
| 678 | 681 | |
| 682 | + /* | |
| 683 | + * score: | |
| 684 | + * An unconfirmed and active user will have a score of 0 + 1*2 = 2 | |
| 685 | + * A confirmed and active user will have a score of 1 + 1*2 = 3 | |
| 686 | + * A confirmed and inactive user will have a score of 1 + 0*2 = 1 | |
| 687 | + */ | |
| 679 | 688 | $query = 'SELECT userList.list_id, COUNT(userList.user_id) AS users, acyuser.confirmed + acyuser.active*2 AS score |
| 680 | 689 | FROM #__acym_user_has_list AS userList |
| 681 | 690 | JOIN #__acym_user AS acyuser |
| 682 | 691 | ON acyuser.id = userList.user_id |
| @@ -691,8 +700,9 @@ | ||
| 691 | 700 | if (!isset($listsUserStats[$oneResult->list_id])) { |
| 692 | 701 | $listsUserStats[$oneResult->list_id] = $this->initList($oneResult->list_id); |
| 693 | 702 | } |
| 694 | 703 | |
| 704 | + // Joomla 4 casts the result into an int | |
| 695 | 705 | $oneResult->score = (string)$oneResult->score; |
| 696 | 706 | |
| 697 | 707 | if (in_array($oneResult->score, ['0', '1'])) { |
| 698 | 708 | $listsUserStats[$oneResult->list_id]->inactive_users += $oneResult->users; |
| @@ -723,13 +733,12 @@ | ||
| 723 | 733 | |
| 724 | 734 | return $listsUserStats; |
| 725 | 735 | } |
| 726 | 736 | |
| 727 | - public function getSubscribersEvolutionByList($listIds) | |
| 737 | + public function getSubscribersEvolutionByListIds(array $listIds): array | |
| 728 | 738 | { |
| 729 | 739 | $condList = ''; |
| 730 | 740 | if (!empty($listIds)) { |
| 731 | - if (!is_array($listIds)) $listIds = [$listIds]; | |
| 732 | 741 | acym_arrayToInteger($listIds); |
| 733 | 742 | $condList = ' AND list_id IN ('.implode(',', $listIds).')'; |
| 734 | 743 | } |
| 735 | 744 | $dateNow = acym_loadResult('SELECT DATE_SUB(NOW(), INTERVAL 1 MONTH)'); |
| @@ -743,9 +752,11 @@ | ||
| 743 | 752 | $queryEvolUnsub .= ' GROUP BY list_id'; |
| 744 | 753 | $newUnsubscribers = acym_loadObjectList($queryEvolUnsub, 'list_id'); |
| 745 | 754 | |
| 746 | 755 | foreach ($newUnsubscribers as $listId => $oneUnsub) { |
| 747 | - if (empty($evolSubscibers[$listId])) $evolSubscibers[$listId] = new \stdClass(); | |
| 756 | + if (empty($evolSubscibers[$listId])) { | |
| 757 | + $evolSubscibers[$listId] = new \stdClass(); | |
| 758 | + } | |
| 748 | 759 | $evolSubscibers[$listId]->newUnsub = $oneUnsub->newUnsub; |
| 749 | 760 | } |
| 750 | 761 | |
| 751 | 762 | return $evolSubscibers; |
| @@ -750,18 +761,19 @@ | ||
| 750 | 761 | |
| 751 | 762 | return $evolSubscibers; |
| 752 | 763 | } |
| 753 | 764 | |
| 754 | - public function getYearSubEvolutionPerList($listId) | |
| 765 | + public function getYearSubEvolutionPerList(int $listId): array | |
| 755 | 766 | { |
| 756 | - $listId = intval($listId); | |
| 757 | - $month = date('n') + 1; | |
| 758 | - $year = date('Y') - 1; | |
| 767 | + // Get next month from 1 year ago | |
| 768 | + $month = gmdate('n') + 1; | |
| 769 | + $year = gmdate('Y') - 1; | |
| 759 | 770 | $initDate = $year.'-'.$month.'-01'; |
| 760 | 771 | if ($month == 13) { |
| 761 | - $initDate = date('Y').'-01-01'; | |
| 772 | + $initDate = gmdate('Y').'-01-01'; | |
| 762 | 773 | } |
| 763 | 774 | |
| 775 | + // Get new subscribers per month | |
| 764 | 776 | $queryEvolSub = 'SELECT MONTH(subscription_date) as monthSub, DATE_FORMAT(subscription_date,"%Y_%m") AS unit, COUNT(user_id) AS nbUser'; |
| 765 | 777 | $queryEvolSub .= ' FROM `#__acym_user_has_list`'; |
| 766 | 778 | $queryEvolSub .= ' WHERE subscription_date >= "'.$initDate.'" AND list_id = '.$listId; |
| 767 | 779 | $queryEvolSub .= ' GROUP BY unit'; |
| @@ -766,8 +778,9 @@ | ||
| 766 | 778 | $queryEvolSub .= ' WHERE subscription_date >= "'.$initDate.'" AND list_id = '.$listId; |
| 767 | 779 | $queryEvolSub .= ' GROUP BY unit'; |
| 768 | 780 | $evolSubscibers = acym_loadObjectList($queryEvolSub, 'unit'); |
| 769 | 781 | |
| 782 | + // Get unsubscribers per month | |
| 770 | 783 | $queryEvolUnsub = 'SELECT MONTH(unsubscribe_date) as monthUnsub, DATE_FORMAT(unsubscribe_date,"%Y_%m") AS unit, COUNT(user_id) AS nbUser'; |
| 771 | 784 | $queryEvolUnsub .= ' FROM `#__acym_user_has_list`'; |
| 772 | 785 | $queryEvolUnsub .= ' WHERE unsubscribe_date >= "'.$initDate.'" AND list_id = '.$listId; |
| 773 | 786 | $queryEvolUnsub .= ' GROUP BY unit'; |
| @@ -778,9 +791,9 @@ | ||
| 778 | 791 | 'unsubscribers' => $evolUnsubscibers, |
| 779 | 792 | ]; |
| 780 | 793 | } |
| 781 | 794 | |
| 782 | - private function initList($listId) | |
| 795 | + private function initList(int $listId): object | |
| 783 | 796 | { |
| 784 | 797 | $list = new \stdClass(); |
| 785 | 798 | $list->list_id = $listId; |
| 786 | 799 | $list->sendable_users = 0; |
| @@ -790,12 +803,14 @@ | ||
| 790 | 803 | |
| 791 | 804 | return $list; |
| 792 | 805 | } |
| 793 | 806 | |
| 794 | - public function getManageableLists() | |
| 807 | + public function getManageableLists(): array | |
| 795 | 808 | { |
| 796 | 809 | $idCurrentUser = acym_currentUserId(); |
| 797 | - if (empty($idCurrentUser)) return []; | |
| 810 | + if (empty($idCurrentUser)) { | |
| 811 | + return []; | |
| 812 | + } | |
| 798 | 813 | |
| 799 | 814 | $userGroups = acym_getGroupsByUser($idCurrentUser); |
| 800 | 815 | |
| 801 | 816 | return acym_loadResultArray( |
| @@ -805,19 +820,21 @@ | ||
| 805 | 820 | OR (access LIKE "%,'.implode(',%" OR access LIKE "%,', $userGroups).',%")' |
| 806 | 821 | ); |
| 807 | 822 | } |
| 808 | 823 | |
| 809 | - public function onlyManageableLists(&$elements) | |
| 824 | + public function onlyManageableLists(&$elements): void | |
| 810 | 825 | { |
| 811 | - if (acym_isAdmin()) return; | |
| 826 | + if (acym_isAdmin()) { | |
| 827 | + return; | |
| 828 | + } | |
| 812 | 829 | |
| 813 | 830 | $manageableLists = $this->getManageableLists(); |
| 814 | 831 | $elements = array_intersect($elements, $manageableLists); |
| 815 | 832 | } |
| 816 | 833 | |
| 817 | - public function hasUserAccess($listId): bool | |
| 834 | + public function hasUserAccess(int $listId): bool | |
| 818 | 835 | { |
| 819 | - if (acym_isAdmin()) { | |
| 836 | + if (acym_isAdmin() && acym_isAllowed('lists')) { | |
| 820 | 837 | return true; |
| 821 | 838 | } |
| 822 | 839 | |
| 823 | 840 | return in_array($listId, $this->getManageableLists()); |
| @@ -822,16 +839,20 @@ | ||
| 822 | 839 | |
| 823 | 840 | return in_array($listId, $this->getManageableLists()); |
| 824 | 841 | } |
| 825 | 842 | |
| 826 | - public function getfrontManagementList() | |
| 843 | + public function getfrontManagementList(): int | |
| 827 | 844 | { |
| 828 | 845 | $idCurrentUser = acym_currentUserId(); |
| 829 | - if (empty($idCurrentUser)) return 0; | |
| 846 | + if (empty($idCurrentUser)) { | |
| 847 | + return 0; | |
| 848 | + } | |
| 830 | 849 | |
| 831 | 850 | $frontListId = acym_loadResult('SELECT id FROM #__acym_list WHERE type = '.acym_escapeDB(self::LIST_TYPE_FRONT).' AND cms_user_id = '.intval($idCurrentUser)); |
| 832 | 851 | |
| 833 | - if (!empty($frontListId)) return $frontListId; | |
| 852 | + if (!empty($frontListId)) { | |
| 853 | + return $frontListId; | |
| 854 | + } | |
| 834 | 855 | |
| 835 | 856 | $frontList = new \stdClass(); |
| 836 | 857 | $frontList->name = 'frontlist_'.$idCurrentUser; |
| 837 | 858 | $frontList->active = 1; |
| @@ -838,60 +859,78 @@ | ||
| 838 | 859 | $frontList->visible = 0; |
| 839 | 860 | $frontList->cms_user_id = $idCurrentUser; |
| 840 | 861 | $frontList->type = self::LIST_TYPE_FRONT; |
| 841 | 862 | |
| 842 | - return $this->save($frontList); | |
| 863 | + return (int)$this->save($frontList); | |
| 843 | 864 | } |
| 844 | 865 | |
| 845 | - public function setWelcomeUnsubEmail($listIds, $mailId, $type) | |
| 866 | + public function setWelcomeUnsubEmail(array $listIds, int $mailId, string $type): bool | |
| 846 | 867 | { |
| 847 | 868 | if (!in_array($type, [MailClass::TYPE_WELCOME, MailClass::TYPE_UNSUBSCRIBE]) || empty($mailId)) { |
| 848 | 869 | return false; |
| 849 | 870 | } |
| 850 | 871 | |
| 851 | - $column = acym_escape($type).'_id'; | |
| 872 | + $column = esc_attr($type).'_id'; | |
| 852 | 873 | $columnsList = acym_getColumns('list'); |
| 853 | - if (!in_array($column, $columnsList)) return false; | |
| 874 | + if (!in_array($column, $columnsList)) { | |
| 875 | + return false; | |
| 876 | + } | |
| 854 | 877 | |
| 855 | - if (!is_array($listIds)) { | |
| 856 | - if (!empty($listIds)) $listIds = [$listIds]; | |
| 878 | + acym_arrayToInteger($listIds); | |
| 879 | + $listIds = array_filter($listIds); | |
| 880 | + | |
| 881 | + if (!$this->removeWelcomeUnsubByMailId($mailId, $listIds, $column)) { | |
| 882 | + return false; | |
| 857 | 883 | } |
| 858 | 884 | |
| 859 | - if (!$this->removeWelcomeUnsubByMailId($mailId, $listIds, $column)) return false; | |
| 860 | - | |
| 861 | 885 | foreach ($listIds as $listId) { |
| 862 | - | |
| 863 | 886 | $list = $this->getOneById($listId); |
| 864 | - if (empty($list)) return false; | |
| 887 | + if (empty($list)) { | |
| 888 | + continue; | |
| 889 | + } | |
| 865 | 890 | |
| 866 | 891 | $list->{$column} = $mailId; |
| 867 | 892 | |
| 868 | - if (!$this->save($list)) return false; | |
| 893 | + $savedListId = $this->save($list); | |
| 894 | + if (empty($savedListId)) { | |
| 895 | + return false; | |
| 896 | + } | |
| 869 | 897 | } |
| 870 | 898 | |
| 871 | 899 | return true; |
| 872 | 900 | } |
| 873 | 901 | |
| 874 | - private function removeWelcomeUnsubByMailId($mailId, $listIds, $column) | |
| 902 | + private function removeWelcomeUnsubByMailId(int $mailId, array $listIds, string $column): bool | |
| 875 | 903 | { |
| 876 | - if (empty($mailId)) return false; | |
| 904 | + if (empty($mailId)) { | |
| 905 | + return false; | |
| 906 | + } | |
| 877 | 907 | |
| 878 | - $where = $column.' = '.intval($mailId); | |
| 879 | - if (!empty($listIds)) $where .= ' AND id NOT IN ('.implode(',', $listIds).')'; | |
| 908 | + $query = 'SELECT * FROM #__acym_list WHERE '.acym_secureDBColumn($column).' = '.$mailId; | |
| 880 | 909 | |
| 881 | - $lists = acym_loadObjectList('SELECT * FROM #__acym_list WHERE '.$where); | |
| 882 | - if (empty($lists)) return true; | |
| 910 | + if (!empty($listIds)) { | |
| 911 | + acym_arrayToInteger($listIds); | |
| 912 | + $query .= ' AND `id` NOT IN ('.implode(',', $listIds).')'; | |
| 913 | + } | |
| 883 | 914 | |
| 915 | + $lists = acym_loadObjectList($query); | |
| 916 | + if (empty($lists)) { | |
| 917 | + return true; | |
| 918 | + } | |
| 919 | + | |
| 884 | 920 | foreach ($lists as $list) { |
| 885 | 921 | $list->{$column} = null; |
| 886 | 922 | |
| 887 | - if (!$this->save($list)) return false; | |
| 923 | + $savedListId = $this->save($list); | |
| 924 | + if (empty($savedListId)) { | |
| 925 | + return false; | |
| 926 | + } | |
| 888 | 927 | } |
| 889 | 928 | |
| 890 | 929 | return true; |
| 891 | 930 | } |
| 892 | 931 | |
| 893 | - public function getListIdsByWelcomeUnsub($mailId, $welcome = true) | |
| 932 | + public function getListIdsByWelcomeUnsub(int $mailId, bool $welcome = true): array | |
| 894 | 933 | { |
| 895 | 934 | $type = $welcome ? 'welcome_id' : 'unsubscribe_id'; |
| 896 | 935 | |
| 897 | 936 | $return = acym_loadResultArray('SELECT id FROM #__acym_list WHERE '.$type.' = '.intval($mailId)); |
| @@ -898,21 +937,10 @@ | ||
| 898 | 937 | |
| 899 | 938 | return empty($return) ? [] : $return; |
| 900 | 939 | } |
| 901 | 940 | |
| 902 | - public function getAll($key = null, $needTranslation = false) | |
| 941 | + public function getUsersForSummaryModal(int $id, int $offset, int $limit, string $search): array | |
| 903 | 942 | { |
| 904 | - $lists = parent::getAll($key); | |
| 905 | - | |
| 906 | - if ($needTranslation && acym_isMultilingual()) { | |
| 907 | - $lists = $this->getTranslatedNameDescription($lists); | |
| 908 | - } | |
| 909 | - | |
| 910 | - return $lists; | |
| 911 | - } | |
| 912 | - | |
| 913 | - public function getUsersForSummaryModal($id, $offset, $limit, $search) | |
| 914 | - { | |
| 915 | 943 | $whereQuery = ' AND user_list.status = 1 AND user.active = 1'; |
| 916 | 944 | |
| 917 | 945 | if (!empty($search)) { |
| 918 | 946 | $search = acym_escapeDB('%'.$search.'%'); |
| @@ -931,9 +959,9 @@ | ||
| 931 | 959 | |
| 932 | 960 | return acym_loadObjectList($query, 'id'); |
| 933 | 961 | } |
| 934 | 962 | |
| 935 | - public function getXLists(array $options = []) | |
| 963 | + public function getXLists(array $options = []): array | |
| 936 | 964 | { |
| 937 | 965 | $limit = $options['limit'] ?? 10; |
| 938 | 966 | $offset = $options['offset'] ?? 0; |
| 939 | 967 | $filters = $options['filters'] ?? ['type' => self::LIST_TYPE_STANDARD]; |
| @@ -961,6 +989,78 @@ | ||
| 961 | 989 | $query .= ' WHERE '.implode(' AND ', $conditions); |
| 962 | 990 | } |
| 963 | 991 | |
| 964 | 992 | return acym_loadObjectList($query, $this->pkey, $offset, $limit); |
| 993 | + } | |
| 994 | + | |
| 995 | + public function getOneList(): bool | |
| 996 | + { | |
| 997 | + $firstList = acym_loadObject('SELECT * FROM #__acym_list WHERE `id` != 1'); | |
| 998 | + | |
| 999 | + return !empty($firstList); | |
| 1000 | + } | |
| 1001 | + | |
| 1002 | + public function getMostUsedLists(): array | |
| 1003 | + { | |
| 1004 | + return acym_loadObjectList( | |
| 1005 | + 'SELECT list_id | |
| 1006 | + FROM #__acym_mail_has_list | |
| 1007 | + GROUP BY list_id | |
| 1008 | + ORDER BY COUNT(*) DESC | |
| 1009 | + LIMIT 5' | |
| 1010 | + ); | |
| 1011 | + } | |
| 1012 | + | |
| 1013 | + public function getListsSubscribersAndUnsubscribeUsersFromUserHasList(): array | |
| 1014 | + { | |
| 1015 | + $query = 'SELECT list_id, | |
| 1016 | + SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) AS subscribers, | |
| 1017 | + SUM(CASE WHEN status = 0 THEN 1 ELSE 0 END) AS unsubscribed_users | |
| 1018 | + FROM `#__acym_user_has_list` | |
| 1019 | + GROUP BY list_id'; | |
| 1020 | + | |
| 1021 | + return acym_loadObjectList($query); | |
| 1022 | + } | |
| 1023 | + | |
| 1024 | + public function updateListsSubscribersAndUnsubscribeUsers(int $listId, int $subscribers, int $unsubscribeUsers, int $new_sub, int $new_unsub): void | |
| 1025 | + { | |
| 1026 | + acym_query( | |
| 1027 | + 'UPDATE `#__acym_list` | |
| 1028 | + SET `subscribers` = '.$subscribers.', `unsubscribed_users` = '.$unsubscribeUsers.', `new_sub` = '.$new_sub.', `new_unsub` = '.$new_unsub.' | |
| 1029 | + WHERE id = '.$listId | |
| 1030 | + ); | |
| 1031 | + } | |
| 1032 | + | |
| 1033 | + public function getMostUsedListsByIdsWithSubscribersData(array $ids = []): array | |
| 1034 | + { | |
| 1035 | + if (empty($ids)) { | |
| 1036 | + return []; | |
| 1037 | + } | |
| 1038 | + acym_arrayToInteger($ids); | |
| 1039 | + $query = 'SELECT name, subscribers, unsubscribed_users, new_sub, new_unsub FROM `#__acym_list` WHERE id IN ('.implode(', ', $ids).')'; | |
| 1040 | + | |
| 1041 | + return acym_loadObjectList($query); | |
| 1042 | + } | |
| 1043 | + | |
| 1044 | + public function getUserListEntries(array $userIds, array $listIds): array | |
| 1045 | + { | |
| 1046 | + if (empty($userIds) || empty($listIds)) { | |
| 1047 | + return []; | |
| 1048 | + } | |
| 1049 | + | |
| 1050 | + $userIdsStr = implode(',', array_map('intval', $userIds)); | |
| 1051 | + $listIdsStr = implode(',', array_map('intval', $listIds)); | |
| 1052 | + | |
| 1053 | + $query = 'SELECT * FROM `#__acym_user_has_list` | |
| 1054 | + WHERE user_id IN ('.$userIdsStr.') AND list_id IN ('.$listIdsStr.')'; | |
| 1055 | + | |
| 1056 | + $results = acym_loadObjectList($query); | |
| 1057 | + | |
| 1058 | + $entries = []; | |
| 1059 | + foreach ($results as $row) { | |
| 1060 | + $key = $row->user_id.'_'.$row->list_id; | |
| 1061 | + $entries[$key] = $row; | |
| 1062 | + } | |
| 1063 | + | |
| 1064 | + return $entries; | |
| 965 | 1065 | } |
| 966 | 1066 | } |