• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

AJenbo / agcms / 21420560247

28 Jan 2026 12:59AM UTC coverage: 52.306% (-1.4%) from 53.72%
21420560247

push

github

AJenbo
Bump phpunit/phpunit from 9.6.11 to 9.6.33 in /application

Bumps [phpunit/phpunit](https://github.com/sebastianbergmann/phpunit) from 9.6.11 to 9.6.33.
- [Release notes](https://github.com/sebastianbergmann/phpunit/releases)
- [Changelog](https://github.com/sebastianbergmann/phpunit/blob/9.6.33/ChangeLog-9.6.md)
- [Commits](https://github.com/sebastianbergmann/phpunit/compare/9.6.11...9.6.33)

---
updated-dependencies:
- dependency-name: phpunit/phpunit
  dependency-version: 9.6.33
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>

3039 of 5810 relevant lines covered (52.31%)

12.21 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

65.09
/application/inc/Models/Newsletter.php
1
<?php
2

3
namespace App\Models;
4

5
use App\Exceptions\Exception;
6
use App\Exceptions\Handler as ExceptionHandler;
7
use App\Services\ConfigService;
8
use App\Services\DbService;
9
use App\Services\EmailService;
10
use App\Services\OrmService;
11
use App\Services\RenderService;
12
use Throwable;
13

14
class Newsletter extends AbstractEntity implements InterfaceRichText
15
{
16
    /**  Table name in database. */
17
    public const TABLE_NAME = 'newsmails';
18

19
    // Backed by DB
20

21
    /** @var string Sender email address */
22
    private string $from = '';
23

24
    /** @var string Email subject */
25
    private string $subject = '';
26

27
    /** @var string Body */
28
    private string $html = '';
29

30
    /** @var bool Has it been sent. */
31
    private bool $sent = false;
32

33
    /** @var string[] List of topics is covered. */
34
    private array $interests = [];
35

36
    public function __construct(array $data = [])
37
    {
38
        $interests = [];
8✔
39
        $rawInterests = $data['interests'] ?? null;
8✔
40
        if (is_array($rawInterests)) {
8✔
41
            foreach ($rawInterests as $rawInterest) {
6✔
42
                $interests[] = valstring($rawInterest);
6✔
43
            }
44
        }
45

46
        $this->setFrom(valstring($data['from'] ?? ''))
8✔
47
            ->setSubject(valstring($data['subject'] ?? ''))
8✔
48
            ->setInterests($interests)
8✔
49
            ->setHtml(valstring($data['html'] ?? ''))
8✔
50
            ->setSent(valbool($data['sent'] ?? false))
8✔
51
            ->setId(intOrNull($data['id'] ?? null));
8✔
52
    }
53

54
    /**
55
     * @return $this
56
     */
57
    public function setFrom(string $from): self
58
    {
59
        $this->from = $from;
8✔
60

61
        return $this;
8✔
62
    }
63

64
    public function getFrom(): string
65
    {
66
        return $this->from;
1✔
67
    }
68

69
    /**
70
     * @return $this
71
     */
72
    public function setSubject(string $subject): self
73
    {
74
        $this->subject = $subject;
8✔
75

76
        return $this;
8✔
77
    }
78

79
    public function getSubject(): string
80
    {
81
        return $this->subject;
3✔
82
    }
83

84
    /**
85
     * @return $this
86
     */
87
    public function setHtml(string $html): InterfaceRichText
88
    {
89
        $this->html = $html;
8✔
90

91
        return $this;
8✔
92
    }
93

94
    public function getHtml(): string
95
    {
96
        return $this->html;
2✔
97
    }
98

99
    /**
100
     * @return $this
101
     */
102
    public function setSent(bool $sent): self
103
    {
104
        $this->sent = $sent;
8✔
105

106
        return $this;
8✔
107
    }
108

109
    public function isSent(): bool
110
    {
111
        return $this->sent;
5✔
112
    }
113

114
    /**
115
     * Set newsletter interests.
116
     *
117
     * @param string[] $interests
118
     *
119
     * @return $this
120
     */
121
    public function setInterests(array $interests): self
122
    {
123
        $this->interests = $interests;
8✔
124

125
        return $this;
8✔
126
    }
127

128
    /**
129
     * Get interests.
130
     *
131
     * @return string[]
132
     */
133
    public function getInterests(): array
134
    {
135
        return $this->interests;
2✔
136
    }
137

138
    public static function mapFromDB(array $data): array
139
    {
140
        $interests = explode('<', $data['interests']);
5✔
141
        $interests = array_map('html_entity_decode', $interests);
5✔
142

143
        return [
5✔
144
            'id'         => $data['id'],
5✔
145
            'from'       => $data['from'],
5✔
146
            'subject'    => $data['subject'],
5✔
147
            'html'       => $data['text'],
5✔
148
            'sent'       => (bool)$data['sendt'],
5✔
149
            'interests'  => $interests,
5✔
150
        ];
5✔
151
    }
152

153
    // ORM related functions
154

155
    public function getDbArray(): array
156
    {
157
        $interests = array_map('htmlspecialchars', $this->interests);
2✔
158
        $interests = implode('<', $interests);
2✔
159

160
        $db = app(DbService::class);
2✔
161

162
        return [
2✔
163
            'from'         => $db->quote($this->from),
2✔
164
            'subject'      => $db->quote($this->subject),
2✔
165
            'text'         => $db->quote($this->html),
2✔
166
            'sendt'        => $db->quote((string)(int)$this->sent),
2✔
167
            'interests'    => $db->quote($interests),
2✔
168
        ];
2✔
169
    }
170

171
    /**
172
     * Count number of recipients for this newsletter.
173
     */
174
    public function countRecipients(): int
175
    {
176
        $db = app(DbService::class);
4✔
177

178
        $db->addLoadedTable('email');
4✔
179
        $emails = $db->fetchOne(
4✔
180
            "
4✔
181
            SELECT count(DISTINCT email) as 'count'
182
            FROM `email`
183
            WHERE `email` NOT LIKE '' AND `kartotek` = '1'
184
            " . $this->getContactFilterSQL()
4✔
185
        );
4✔
186

187
        return (int)$emails['count'];
4✔
188
    }
189

190
    /**
191
     * Get SQL for filtering contacts based on interests.
192
     */
193
    private function getContactFilterSQL(): string
194
    {
195
        $andWhere = '';
4✔
196
        if ($this->interests) {
4✔
197
            foreach ($this->interests as $interest) {
4✔
198
                if ($andWhere) {
4✔
199
                    $andWhere .= ' OR ';
1✔
200
                }
201
                $andWhere .= '`interests` LIKE \'';
4✔
202
                $andWhere .= $interest;
4✔
203
                $andWhere .= '\' OR `interests` LIKE \'';
4✔
204
                $andWhere .= $interest;
4✔
205
                $andWhere .= '<%\' OR `interests` LIKE \'%<';
4✔
206
                $andWhere .= $interest;
4✔
207
                $andWhere .= '\' OR `interests` LIKE \'%<';
4✔
208
                $andWhere .= $interest;
4✔
209
                $andWhere .= '<%\'';
4✔
210
            }
211
            $andWhere = ' AND (' . $andWhere . ')';
4✔
212
        }
213

214
        return $andWhere;
4✔
215
    }
216

217
    /**
218
     * Send the newsletter.
219
     *
220
     * @todo resend failed emails, save bcc
221
     *
222
     * @throws Exception
223
     */
224
    public function send(): void
225
    {
226
        if ($this->sent) {
×
227
            throw new Exception(_('The newsletter has already been sent.'));
×
228
        }
229

230
        $andWhere = $this->getContactFilterSQL();
×
231
        $contacts = app(OrmService::class)->getByQuery(
×
232
            Contact::class,
×
233
            'SELECT * FROM email WHERE email NOT LIKE \'\' AND `kartotek` = \'1\' ' . $andWhere . ' GROUP BY `email`'
×
234
        );
×
235

236
        // Split in to groups of 99 to avoid server limit on bcc
237
        $contactsGroups = [];
×
238
        foreach ($contacts as $x => $contact) {
×
239
            $contactsGroups[(int)floor($x / 99) + 1][] = $contact;
×
240
        }
241

242
        $data = [
×
243
            'siteName' => ConfigService::getString('site_name'),
×
244
            'css'      => file_get_contents(
×
245
                app()->basePath('/theme/' . ConfigService::getString('theme', 'default') . '/style/email.css')
×
246
            ),
×
247
            'body'     => str_replace(' href="/', ' href="' . ConfigService::getString('base_url') . '/', $this->html),
×
248
        ];
×
249
        $emailService = app(EmailService::class);
×
250
        $failedCount = 0;
×
251

252
        $render = app(RenderService::class);
×
253

254
        foreach ($contactsGroups as $bcc) {
×
255
            $email = new Email([
×
256
                'subject'          => $this->subject,
×
257
                'body'             => $render->render('email/newsletter', $data),
×
258
                'senderName'       => ConfigService::getString('site_name'),
×
259
                'senderAddress'    => $this->from,
×
260
                'recipientName'    => ConfigService::getString('site_name'),
×
261
                'recipientAddress' => $this->from,
×
262
            ]);
×
263

264
            try {
265
                $emailService->send($email, $bcc);
×
266
            } catch (Throwable $exception) {
×
267
                app(ExceptionHandler::class)->report($exception);
×
268
                $failedCount += count($bcc);
×
269
            }
270
        }
271
        if ($failedCount) {
×
272
            throw new Exception(sprintf(_('Email %d/%d failed to be sent.'), $failedCount, count($contacts)));
×
273
        }
274

275
        $this->sent = true;
×
276
        $this->save();
×
277
    }
278
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc