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

Freegle / iznik-server / 47a5b201-7ecf-4f63-90f4-d23fc98a47f7

09 Dec 2024 04:36PM UTC coverage: 92.436% (+0.001%) from 92.435%
47a5b201-7ecf-4f63-90f4-d23fc98a47f7

push

circleci

edwh
Don't autorepost if past deadline.

2 of 2 new or added lines in 1 file covered. (100.0%)

2 existing lines in 1 file now uncovered.

25478 of 27563 relevant lines covered (92.44%)

31.47 hits per line

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

94.81
/include/user/Story.php
1
<?php
2
namespace Freegle\Iznik;
3

4
class Story extends Entity
5
{
6
    /** @var  $dbhm LoggedPDO */
7
    var $publicatts = array('id', 'date', 'public', 'headline', 'story', 'reviewed', 'newsletterreviewed', 'newsletter');
8
    var $settableatts = array('public', 'headline', 'story', 'reviewed', 'newsletterreviewed', 'newsletter');
9

10
    const ASK_OUTCOME_THRESHOLD = 3;
11
    const ASK_OFFER_THRESHOLD = 5;
12

13
    const LIKE = 'Like';
14
    const UNLIKE = 'Unlike';
15

16
    function __construct(LoggedPDO $dbhr, LoggedPDO $dbhm, $id = NULL)
17
    {
18
        $this->fetch($dbhr, $dbhm, $id, 'users_stories', 'story', $this->publicatts);
34✔
19
    }
20

21
    public function setAttributes($settings) {
22
        $myid = Session::whoAmId($this->dbhr, $this->dbhm);
4✔
23

24
        foreach ($this->settableatts as $att) {
4✔
25
            if (array_key_exists($att, $settings)) {
4✔
26
                $this->setPrivate($att, $settings[$att]);
4✔
27

28
                if ($myid) {
4✔
29
                    if ($att == 'reviewed') {
2✔
30
                        $this->setPrivate('reviewedby', $myid);
2✔
31
                    } else if ($att == 'newsletterreviewed') {
2✔
32
                        $this->setPrivate('newsletterreviewedby', $myid);
1✔
33
                    }
34
                }
35
            }
36
        }
37
    }
38

39
    public function create($userid, $public, $headline, $story, $photo = NULL) {
40
        $id = NULL;
6✔
41

42
        $rc = $this->dbhm->preExec("INSERT INTO users_stories (public, userid, headline, story) VALUES (?,?,?,?);", [
6✔
43
            $public,
6✔
44
            $userid,
6✔
45
            $headline,
6✔
46
            $story
6✔
47
        ]);
6✔
48

49
        if ($rc) {
6✔
50
            $id = $this->dbhm->lastInsertId();
6✔
51

52
            if ($id) {
6✔
53
                $this->fetch($this->dbhm, $this->dbhm, $id, 'users_stories', 'story', $this->publicatts);
6✔
54
            }
55

56
            if ($photo) {
6✔
57
                $this->setPhoto($photo);
×
58
            }
59
        }
60

61
        return($id);
6✔
62
    }
63

64
    public function setPhoto($photoid) {
65
        $this->dbhm->preExec("UPDATE users_stories_images SET storyid = ? WHERE id = ?;", [ $this->id, $photoid ]);
×
66
    }
67

68
    public function getPublic() {
69
        $ret = parent::getPublic();
5✔
70

71
        $ret['date'] = Utils::ISODate($ret['date']);
5✔
72
        $me = Session::whoAmI($this->dbhr, $this->dbhm);
5✔
73

74
        $u = User::get($this->dbhr, $this->dbhm, $this->story['userid']);
5✔
75

76
        if ($me && $me->isModerator() && $this->story['userid']) {
5✔
77
            if ($me->hasPermission(User::PERM_NEWSLETTER) || $me->moderatorForUser($this->story['userid'], TRUE)) {
2✔
78
                $ret['user'] = $u->getPublic();
2✔
79
                $ret['user']['email'] = $u->getEmailPreferred();
2✔
80
            }
81
        }
82

83
        $membs = $u->getMemberships();
5✔
84
        $groupname = NULL;
5✔
85
        $groupid = NULL;
5✔
86

87
        if (count($membs) > 0) {
5✔
88
            shuffle($membs);
2✔
89
            foreach ($membs as $memb) {
2✔
90
                if ($memb['type'] == Group::GROUP_FREEGLE && $memb['onmap']) {
2✔
91
                    $groupname = $memb['namedisplay'];
1✔
92
                    $groupid = $memb['id'];
1✔
93
                }
94
            }
95
        }
96

97
        $ret['groupname'] = $groupname;
5✔
98
        $ret['groupid'] = $groupid;
5✔
99

100
        $likes = $this->dbhr->preQuery("SELECT COUNT(*) AS count FROM users_stories_likes WHERE storyid = ?;", [
5✔
101
            $this->id
5✔
102
        ]);
5✔
103

104
        $ret['likes'] = $likes[0]['count'];
5✔
105
        $ret['liked'] = FALSE;
5✔
106
        if ($me) {
5✔
107
            $likes = $this->dbhr->preQuery("SELECT COUNT(*) AS count FROM users_stories_likes WHERE storyid = ? AND userid = ?;", [
2✔
108
                $this->id,
2✔
109
                $me->getId()
2✔
110
            ]);
2✔
111
            $ret['liked'] = $likes[0]['count'] > 0;
2✔
112
        }
113

114
        $photos = $this->dbhr->preQuery("SELECT id FROM users_stories_images WHERE storyid = ?;", [ $this->id ]);
5✔
115
        foreach ($photos as $photo) {
5✔
116
            $a = new Attachment($this->dbhr, $this->dbhm, $photo['id'], Attachment::TYPE_STORY);
×
117

118
            $ret['photo'] = [
×
119
                'id' => $photo['id'],
×
120
                'path' => $a->getPath(FALSE),
×
121
                'paththumb' => $a->getPath(TRUE)
×
122
            ];
×
123
        }
124

125
        $ret['url'] = 'https://' . USER_SITE . '/story/' . $ret['id'];
5✔
126

127
        return($ret);
5✔
128
    }
129

130
    public function canSee() {
131
        # Can see our own, or all if we have permissions, or if it's public
132
        $me = Session::whoAmI($this->dbhr, $this->dbhm);
1✔
133
        $myid = Session::whoAmId($this->dbhr, $this->dbhm);
1✔
134
        return($this->story['public'] || $this->story['userid'] == $myid || ($me && $me->isAdminOrSupport()));
1✔
135
    }
136

137
    public function canMod() {
138
        $ret = FALSE;
2✔
139

140
        if ($this->story) {
2✔
141
            # We can modify if it's ours, we are an admin, or a mod on a group that the author is a member of.
142
            $me = Session::whoAmI($this->dbhr, $this->dbhm);
2✔
143
            $myid = Session::whoAmId($this->dbhr, $this->dbhm);
2✔
144
            $author = User::get($this->dbhr, $this->dbhm, $this->story['userid']);
2✔
145
            $authormembs = $author->getMemberships(FALSE);
2✔
146
            $ret = ($this->story['userid'] == $myid) || ($me && $me->isAdminOrSupport());
2✔
147

148
            if ($myid) {
2✔
149
                $membs = $me->getMemberships(TRUE);
2✔
150
                foreach ($membs as $memb) {
2✔
151
                    foreach ($authormembs as $authormemb) {
1✔
152
                        if ($authormemb['id'] == $memb['id']) {
1✔
153
                            $ret = TRUE;
1✔
154
                        }
155
                    }
156
                }
157
            }
158
        }
159

160
        return($ret);
2✔
161
    }
162

163
    public function getForReview($groupids, $newsletter) {
164
        $mysqltime = date("Y-m-d", strtotime("31 days ago"));
1✔
165
        $sql = $newsletter ? ("SELECT DISTINCT users_stories.id FROM users_stories INNER JOIN memberships ON memberships.userid = users_stories.userid WHERE reviewed = 1 AND public = 1 AND newsletterreviewed = 0 ORDER BY date DESC") :
1✔
166
            ("SELECT DISTINCT users_stories.id FROM users_stories INNER JOIN memberships ON memberships.userid = users_stories.userid WHERE memberships.groupid IN (" . implode(',', $groupids) . ") AND users_stories.date > '$mysqltime' AND reviewed = 0 ORDER BY date DESC");
1✔
167
        $ids = $this->dbhr->preQuery($sql);
1✔
168
        $ret = [];
1✔
169

170
        foreach ($ids as $id) {
1✔
171
            $s = new Story($this->dbhr, $this->dbhm, $id['id']);
1✔
172
            $ret[] = $s->getPublic();
1✔
173
        }
174

175
        return($ret);
1✔
176
    }
177

178
    public function getReviewCount($newsletter, $me = NULL, $mygroups = NULL) {
179
        $me = $me ? $me : Session::whoAmI($this->dbhr, $this->dbhm);
27✔
180
        $mysqltime = date("Y-m-d", strtotime("31 days ago"));
27✔
181

182
        if ($newsletter) {
27✔
183
            $sql = "SELECT COUNT(DISTINCT(users_stories.id)) AS count FROM users_stories INNER JOIN memberships ON memberships.userid = users_stories.userid WHERE reviewed = 1 AND public = 1 AND newsletterreviewed = 0 ORDER BY date DESC";
×
184
        } else {
185
            if (!$mygroups) {
27✔
186
                $mygroups = $me->getMemberships(TRUE, Group::GROUP_FREEGLE, FALSE, FALSE, NULL, FALSE);
9✔
187
            }
188

189
            $groupids = [0];
27✔
190
            foreach ($mygroups as $mygroup) {
27✔
191
                # This group might have turned stories off.  Bypass the Group object in the interest of performance
192
                # for people on many groups.
193
                if (($mygroup['role'] == User::ROLE_MODERATOR || $mygroup['role'] == User::ROLE_OWNER) && $me->activeModForGroup($mygroup['id'])) {
20✔
194
                    if (!array_key_exists('stories', $mygroup['settings']) || $mygroup['settings']['stories']) {
18✔
195
                        $groupids[] = $mygroup['id'];
18✔
196
                    }
197
                }
198
            }
199

200
            $sql = "SELECT COUNT(DISTINCT users_stories.id) AS count FROM users_stories INNER JOIN memberships ON memberships.userid = users_stories.userid WHERE memberships.groupid IN (" . implode(',', $groupids) . ")  AND users_stories.date > '$mysqltime' AND reviewed = 0 ORDER BY date DESC;";
27✔
201
        }
202

203
        $ids = $this->dbhr->preQuery($sql);
27✔
204
        return($ids[0]['count']);
27✔
205
    }
206

207
    public function getStories($groupid, $authorityid, $story, $limit = 20, $reviewnewsletter = FALSE) {
208
        $limit = intval($limit);
2✔
209
        if ($reviewnewsletter) {
2✔
210
            # This is for mods reviewing stories for inclusion in the newsletter
211
            $last = $this->dbhr->preQuery("SELECT MAX(created) AS max FROM newsletters WHERE type = 'Stories';");
1✔
212
            $since = $last[0]['max'];
1✔
213
            $dateq = $since ? "AND date >= '$since'": '';
1✔
214
            $sql = "SELECT DISTINCT users_stories.id FROM users_stories WHERE newsletter = 1 AND public = 1 AND newsletterreviewed = 1 AND mailedtomembers = 0 $dateq ORDER BY RAND();";
1✔
215
            $ids = $this->dbhr->preQuery($sql);
1✔
216
        } else {
217
            if ($groupid) {
2✔
218
                # Get stories where the user is a member of this group.  May cause same story to be visible across multiple groups.
219
                $sql = "SELECT DISTINCT users_stories.id FROM users_stories INNER JOIN memberships ON memberships.userid = users_stories.userid WHERE memberships.groupid = $groupid AND reviewed = 1 AND public = 1 AND users_stories.userid IS NOT NULL ORDER BY date DESC LIMIT $limit;";
1✔
220
                $ids = $this->dbhr->preQuery($sql);
1✔
221
            } else if ($authorityid) {
1✔
222
                # Get stories where the user has a location within the authority.  May omit users where we don't know their location.  Bit slow.
223
                $a = new Authority($this->dbhr, $this->dbhm, $authorityid);
1✔
224
                $stories = $this->dbhr->preQuery("SELECT id, userid FROM users_stories WHERE reviewed = 1 AND public = 1 AND userid IS NOT NULL ORDER BY date DESC;");
1✔
225
                $ids = [];
1✔
226

227
                foreach ($stories as $story) {
1✔
228
                    $u = User::get($this->dbhr, $this->dbhm, $story['userid']);
1✔
229
                    list ($lat, $lng, $loc) = $u->getLatLng();
1✔
230

231
                    if (($lat || $lng) && $a->contains($lat, $lng)) {
1✔
232
                        $ids[] = $story;
1✔
233
                    }
234

235
                    if (count($ids) >= $limit) {
1✔
236
                        break;
×
237
                    }
238
                }
239

240
            } else {
241
                $sql = "SELECT DISTINCT users_stories.id FROM users_stories WHERE reviewed = 1 AND public = 1 AND userid IS NOT NULL ORDER BY date DESC LIMIT $limit;";
×
242
                $ids = $this->dbhr->preQuery($sql);
×
243
            }
244
        }
245

246
        $ret = [];
2✔
247

248
        foreach ($ids as $id) {
2✔
249
            $s = new Story($this->dbhr, $this->dbhm, $id['id']);
2✔
250
            $thisone = $s->getPublic();
2✔
251
            if (!$story) {
2✔
252
                unset($thisone['story']);
×
253
            }
254

255
            $ret[] = $thisone;
2✔
256
        }
257

258
        return($ret);
2✔
259
    }
260

261
    public function askForStories($earliest, $userid = NULL, $outcomethreshold = Story::ASK_OUTCOME_THRESHOLD, $offerthreshold = Story::ASK_OFFER_THRESHOLD, $groupid = NULL, $force = FALSE) {
262
        $userq = $userid ? " AND fromuser = $userid " : "";
1✔
263
        $groupq = $groupid ? " INNER JOIN messages_groups ON messages_groups.msgid = messages.id AND messages_groups.groupid = $groupid " : "";
1✔
264
        $forceq = $force ? '' : " AND users_stories_requested.date IS NULL ";
1✔
265
        $sql = "SELECT DISTINCT fromuser FROM messages $groupq 
1✔
266
            LEFT OUTER JOIN users_stories_requested ON users_stories_requested.userid = messages.fromuser 
267
            WHERE messages.arrival > ? AND fromuser IS NOT NULL $forceq $userq;";
1✔
268
        $users = $this->dbhr->preQuery($sql, [ $earliest ]);
1✔
269
        $asked = 0;
1✔
270

271
        error_log("Found " . count($users) . " possible users");
1✔
272

273
        foreach ($users as $user) {
1✔
274
            $outcomes = $this->dbhr->preQuery("SELECT COUNT(*) AS count FROM messages_by WHERE userid = ?;", [ $user['fromuser'] ]);
1✔
275
            $outcomecount = $outcomes[0]['count'];
1✔
276
            $offers = $this->dbhr->preQuery("SELECT COUNT(*) AS count FROM messages WHERE fromuser = ? AND type = 'Offer';", [ $user['fromuser'] ]);
1✔
277
            $offercount = $offers[0]['count'];
1✔
278
            #error_log("Userid {$user['fromuser']} outcome count $outcomecount offer count $offercount");
279

280
            if ($outcomecount > $outcomethreshold || $offercount > $offerthreshold) {
1✔
281
                # Record that we've thought about asking.  This means we won't consider them repeatedly.
282
                $this->dbhm->preExec("INSERT INTO users_stories_requested (userid) VALUES (?);", [ $user['fromuser'] ]);
1✔
283

284
                # We only want to ask if they are a member of a group which has stories enabled.
285
                $u = new User($this->dbhr, $this->dbhm, $user['fromuser']);
1✔
286
                $membs = $u->getMemberships();
1✔
287
                $ask = FALSE;
1✔
288
                foreach ($membs as $memb) {
1✔
289
                    $g = Group::get($this->dbhr, $this->dbhm, $memb['id']);
1✔
290
                    $stories = $g->getSetting('stories', 1);
1✔
291
                    #error_log("Consider send for " . $u->getEmailPreferred() . " stories $stories, groupid $groupid vs {$memb['id']}");
292
                    if ($stories && (!$groupid || $groupid == $memb['id'])) {
1✔
293
                        $ask = TRUE;
1✔
294
                    }
295
                }
296

297
                if ($ask) {
1✔
298
                    $asked++;
1✔
299
                    $url = $u->loginLink(USER_SITE, $user['fromuser'], '/stories');
1✔
300

301
                    $loader = new \Twig_Loader_Filesystem(IZNIK_BASE . '/mailtemplates/twig/stories');
1✔
302
                    $twig = new \Twig_Environment($loader);
1✔
303

304
                    $html = $twig->render('ask.html', [
1✔
305
                        'name' => $u->getName(),
1✔
306
                        'email' => $u->getEmailPreferred(),
1✔
307
                        'unsubscribe' => $u->loginLink(USER_SITE, $u->getId(), "/unsubscribe", NULL)
1✔
308
                    ]);
1✔
309

310
                    error_log("..." . $u->getEmailPreferred());
1✔
311

312
                    try {
313
                        $message = \Swift_Message::newInstance()
1✔
314
                            ->setSubject("Tell us your Freegle story!")
1✔
315
                            ->setFrom([NOREPLY_ADDR => SITE_NAME])
1✔
316
                            ->setReturnPath($u->getBounce())
1✔
317
                            ->setTo([ $u->getEmailPreferred() => $u->getName() ])
1✔
318
                            ->setBody("We'd love to hear your Freegle story.  Tell us at $url");
1✔
319

320
                        # Add HTML in base-64 as default quoted-printable encoding leads to problems on
321
                        # Outlook.
322
                        $htmlPart = \Swift_MimePart::newInstance();
1✔
323
                        $htmlPart->setCharset('utf-8');
1✔
324
                        $htmlPart->setEncoder(new \Swift_Mime_ContentEncoder_Base64ContentEncoder);
1✔
325
                        $htmlPart->setContentType('text/html');
1✔
326
                        $htmlPart->setBody($html);
1✔
327
                        $message->attach($htmlPart);
1✔
328

329
                        Mail::addHeaders($this->dbhr, $this->dbhm, $message, Mail::STORY_ASK, $u->getId());
1✔
330

331
                        list ($transport, $mailer) = Mail::getMailer();
1✔
332
                        $mailer->send($message);
1✔
UNCOV
333
                    } catch (\Exception $e) {}
×
334
                }
335
            }
336
        }
337

338
        return($asked);
1✔
339
    }
340

341
    public function delete() {
342
        $rc = $this->dbhm->preExec("DELETE FROM users_stories WHERE id = ?;", [ $this->id ]);
1✔
343
        return($rc);
1✔
344
    }
345

346
    public function sendIt($mailer, $message) {
UNCOV
347
        $mailer->send($message);
×
348
    }
349

350
    public function sendToCentral($id = NULL) {
351
        $idq = $id ? " AND id = $id " : "";
1✔
352
        $stories = $this->dbhr->preQuery("SELECT id FROM users_stories WHERE newsletter = 1 AND public = 1 AND newsletterreviewed = 1 AND mailedtomembers = 0 AND mailedtocentral = 0 $idq;");
1✔
353
        $url = "https://" . USER_SITE . "/stories/fornewsletter";
1✔
354
        $preview = "Please go to $url to vote for which go into the next member newsletter.";
1✔
355

356
        $thestories = [];
1✔
357

358
        foreach ($stories as $story) {
1✔
359
            $s = new Story($this->dbhr, $this->dbhm, $story['id']);
1✔
360
            $atts = $s->getPublic();
1✔
361

362
            $thestories[] = [
1✔
363
                'headline' => $atts['headline'],
1✔
364
                'story' => $atts['story'],
1✔
365
                'groupname' => $atts['groupname'],
1✔
366
                'photo' => Utils::presdef('photo', $atts, NULL) ? $atts['photo']['path'] : NULL
1✔
367
            ];
1✔
368

369
            $this->dbhm->preExec("UPDATE users_stories SET mailedtocentral = 1 WHERE id = ?;", [ $story['id'] ]);
1✔
370
        }
371

372
        if (count($thestories) > 0) {
1✔
373
            $loader = new \Twig_Loader_Filesystem(IZNIK_BASE . '/mailtemplates/twig/stories');
1✔
374
            $twig = new \Twig_Environment($loader);
1✔
375

376
            $html = $twig->render('central.html', [
1✔
377
                'previewtext' => $preview,
1✔
378
                'vote' => $url,
1✔
379
                'stories' => $thestories
1✔
380
            ]);
1✔
381

382
            $message = \Swift_Message::newInstance()
1✔
383
                ->setSubject(date("d-M-Y")." Recent stories from freeglers - please vote")
1✔
384
                ->setFrom([CENTRAL_MAIL_FROM => SITE_NAME])
1✔
385
                ->setReturnPath(CENTRAL_MAIL_FROM)
1✔
386
                ->setTo('edward@ehibbert.org.uk')
1✔
387
                #->setTo(CENTRAL_MAIL_TO)
1✔
388
                ->setBody($preview);
1✔
389

390
            # Add HTML in base-64 as default quoted-printable encoding leads to problems on
391
            # Outlook.
392
            $htmlPart = \Swift_MimePart::newInstance();
1✔
393
            $htmlPart->setCharset('utf-8');
1✔
394
            $htmlPart->setEncoder(new \Swift_Mime_ContentEncoder_Base64ContentEncoder);
1✔
395
            $htmlPart->setContentType('text/html');
1✔
396
            $htmlPart->setBody($html);
1✔
397
            $message->attach($htmlPart);
1✔
398

399
            Mail::addHeaders($this->dbhr, $this->dbhm, $message, Mail::STORY);
1✔
400

401
            list ($transport, $mailer) = Mail::getMailer();
1✔
402
            $this->sendIt($mailer, $message);
1✔
403
        }
404

405
        return(count($thestories));
1✔
406
    }
407

408
    public function generateNewsletter($min = 3, $max = 10, $id = NULL) {
409
        # We generate a newsletter from stories which have been marked as suitable for publication.
410
        $nid = NULL;
1✔
411
        $html = NULL;
1✔
412
        $count = 0;
1✔
413

414
        # Find the date of the last story sent in a newsletter; we're only interested in stories since then.
415
        $last = $this->dbhr->preQuery("SELECT MAX(created) AS max FROM newsletters WHERE type = 'Stories';");
1✔
416
        $since = $last[0]['max'];
1✔
417

418
        # Get unsent stories.  Pick the ones we have voted for most often.
419
        $idq = $id ? " AND users_stories.id = $id " : "";
1✔
420
        $sql = "SELECT users_stories.id, users_stories_images.id AS photoid, COUNT(*) AS count FROM users_stories LEFT JOIN users_stories_likes ON storyid = users_stories.id LEFT JOIN users_stories_images ON users_stories_images.storyid = users_stories.id WHERE newsletterreviewed = 1 AND newsletter = 1 AND mailedtomembers = 0 $idq AND (? IS NULL OR updated > ?) GROUP BY id ORDER BY count DESC LIMIT $max;";
1✔
421
        #error_log("$sql $since");
422
        $stories = $this->dbhr->preQuery($sql, [
1✔
423
            $since,
1✔
424
            $since
1✔
425
        ]);
1✔
426

427
        if (count($stories) >= $min) {
1✔
428
            # Enough to be worth sending a newsletter.
429
            shuffle($stories);
1✔
430

431
            $n = new Newsletter($this->dbhr, $this->dbhm);
1✔
432
            $preview = "This is a selection of recent stories from other freeglers.  If you can't read the HTML version, have a look at https://" . USER_SITE . '/stories';
1✔
433
            $nid = $n->create(NULL,
1✔
434
                "Lovely stories from other freeglers!",
1✔
435
                $preview);
1✔
436
            $n->setPrivate('type', 'Stories');
1✔
437

438
            $thestories = [];
1✔
439

440
            foreach ($stories as $story) {
1✔
441
                $s = new Story($this->dbhr, $this->dbhm, $story['id']);
1✔
442
                $atts = $s->getPublic();
1✔
443

444
                $thestories[] = [
1✔
445
                    'headline' => $atts['headline'],
1✔
446
                    'story' => $atts['story'],
1✔
447
                    'groupname' => $atts['groupname'],
1✔
448
                    'photo' => Utils::presdef('photo', $atts, NULL) ? $atts['photo']['path'] : NULL
1✔
449
                ];
1✔
450

451
                $count++;
1✔
452

453
                error_log("Sending {$story['id']}");
1✔
454
                $this->dbhm->preExec("UPDATE users_stories SET mailedtomembers = 1 WHERE id = ?;", [ $story['id'] ]);
1✔
455
            }
456

457
            $loader = new \Twig_Loader_Filesystem(IZNIK_BASE . '/mailtemplates/twig/stories');
1✔
458
            $twig = new \Twig_Environment($loader);
1✔
459

460
            $img = rand(1, 5);
1✔
461
            $image = 'https://' . USER_SITE  . "/images/story$img.png";
1✔
462

463
            $html = $twig->render('newsletter.html', [
1✔
464
                'previewtext' => $preview,
1✔
465
                'headerimage' => $image,
1✔
466
                'tell' => 'https://' . USER_SITE . '/stories?src=storynewsletter',
1✔
467
                'give' => 'https://' . USER_SITE . '/give?src=storynewsletter',
1✔
468
                'find' => 'https://' . USER_SITE . '/find?src=storynewsletter',
1✔
469
                'email' => '{{email}}',
1✔
470
                'noemail' => '{{noemail}}',
1✔
471
                'stories' => $thestories
1✔
472
            ]);
1✔
473
        }
474

475
        return ($count >= $min ? [ $nid, $html ] : [ NULL, NULL ]);
1✔
476
    }
477

478
    public function like() {
479
        $me = Session::whoAmI($this->dbhr, $this->dbhm);
1✔
480
        if ($me) {
1✔
481
            $this->dbhm->preExec("INSERT IGNORE INTO users_stories_likes (storyid, userid) VALUES (?,?);", [
1✔
482
                $this->id,
1✔
483
                $me->getId()
1✔
484
            ]);
1✔
485
        }
486
    }
487

488
    public function unlike() {
489
        $me = Session::whoAmI($this->dbhr, $this->dbhm);
1✔
490
        if ($me) {
1✔
491
            $this->dbhm->preExec("DELETE FROM users_stories_likes WHERE storyid = ? AND userid = ?;", [
1✔
492
                $this->id,
1✔
493
                $me->getId()
1✔
494
            ]);
1✔
495
        }
496
    }
497
}
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