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

Freegle / iznik-server / a3a11a6e-b72d-48a0-807c-9f018f8b47cd

pending completion
a3a11a6e-b72d-48a0-807c-9f018f8b47cd

push

circleci

Edward Hibbert
Duplicates when sharing popular posts.

26 of 26 new or added lines in 3 files covered. (100.0%)

19565 of 20566 relevant lines covered (95.13%)

32.31 hits per line

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

93.85
/include/group/GroupFacebook.php
1
<?php
2
namespace Freegle\Iznik;
3

4
use JanuSoftware\Facebook\FacebookSession;
5
use JanuSoftware\Facebook\FacebookJavaScriptLoginHelper;
6
use JanuSoftware\Facebook\FacebookCanvasLoginHelper;
7
use JanuSoftware\Facebook\FacebookRequest;
8
use JanuSoftware\Facebook\FacebookRequestException;
9

10
class GroupFacebook {
11
    static public $publicatts = ['name', 'token', 'type', 'authdate', 'valid', 'msgid', 'msgarrival', 'eventid', 'sharefrom', 'token', 'groupid', 'id', 'lastupdated', 'uid' ];
12

13
    const TYPE_PAGE = 'Page';
14

15
    const ACTION_DO = 'Do';
16
    const ACTION_HIDE = 'Hide';
17
    const ACTION_DO_POPULAR  = 'DoPopular';
18
    const ACTION_HIDE_POPULAR = 'HidePopular';
19
    const ACTION_SHARE_MESSAGE_TO_GROUP = 'ShareMessageToGroup';
20

21
    function __construct(LoggedPDO $dbhr, LoggedPDO $dbhm, $id = NULL, $fetched = NULL)
22
    {
23
        $this->dbhr = $dbhr;
36✔
24
        $this->dbhm = $dbhm;
36✔
25

26
        foreach (GroupFacebook::$publicatts as $att) {
36✔
27
            $this->$att = NULL;
36✔
28
        }
29

30
        $this->uid = $id;
36✔
31

32
        if ($id) {
36✔
33
            $groups = $fetched ? [ $fetched ] : $this->dbhr->preQuery("SELECT * FROM groups_facebook WHERE uid = ?;", [ $id ]);
6✔
34
            foreach ($groups as $group) {
6✔
35
                foreach (GroupFacebook::$publicatts as $att) {
4✔
36
                    $this->$att = $group[$att];
4✔
37
                }
38
            }
39
        }
40
    }
41

42
    public function getPublic() {
43
        $ret = [];
2✔
44
        foreach (GroupFacebook::$publicatts as $att) {
2✔
45
            $ret[$att] = $this->$att;
2✔
46
        }
47

48
        $ret['authdate'] = Utils::ISODate($ret['authdate']);
2✔
49
        $ret['msgarrival'] = Utils::ISODate($ret['msgarrival']);
2✔
50

51
        return($ret);
2✔
52
    }
53

54
    public function getFB($graffiti, $apptoken = FALSE) {
55
        #error_log("Get FB $graffiti");
56
        $fb = new \JanuSoftware\Facebook\Facebook([
4✔
57
            'app_id' => $graffiti ? FBGRAFFITIAPP_ID : FBAPP_ID,
4✔
58
            'app_secret' => $graffiti ? FBGRAFFITIAPP_SECRET : FBAPP_SECRET,
4✔
59
            'default_graph_version' =>  'v13.0'
60
        ]);
61

62
        if ($apptoken) {
4✔
63
            # Use an app access token
64
            $this->setToken($fb->getApplication()->getAccessToken());
1✔
65
        }
66

67
        return($fb);
4✔
68
    }
69

70
    public function setToken($token) {
71
        $this->token = $token;
1✔
72
    }
73

74
    public function add($groupid, $token, $name, $id, $type = GroupFacebook::TYPE_PAGE) {
75
        $this->dbhm->preExec("INSERT INTO groups_facebook (groupid, name, id, token, authdate, type) VALUES (?,?,?,?,NOW(), ?) ON DUPLICATE KEY UPDATE name = ?, id = ?, token = ?, authdate = NOW(), valid = 1, type = ?;",
7✔
76
            [
77
                $groupid,
78
                $name,
79
                $id,
80
                $token,
81
                $type,
82
                $name,
83
                $id,
84
                $token,
85
                $type
86
            ]);
87

88
        $created = $this->dbhm->lastInsertId();
7✔
89
        $this->token = $token;
7✔
90

91
        $n = new PushNotifications($this->dbhr, $this->dbhm);
7✔
92
        $n->notifyGroupMods($groupid);
7✔
93

94
        return($created);
7✔
95
    }
96

97
    public function remove($uid) {
98
        $this->dbhm->preExec("DELETE FROM groups_facebook WHERE uid = ?;", [ $uid ]);
2✔
99
    }
100

101
    public function getPostsToShare($sharefrom, $since = "last week", $token = NULL) {
102
        $fb = $this->getFB(TRUE, FALSE);
3✔
103
        $count = 0;
3✔
104
        #error_log("Scrape posts from $sharefrom");
105

106
        # Get posts we might want to share.  This returns only posts by the page itself.
107
        try {
108
            $url = $sharefrom . "/posts?limit=100&&fields=id,message,icon,full_picture,created_time";
3✔
109
            #error_log("Get from feed $url, $token");
110
            $ret = $fb->get($url, $token);
3✔
111
            #error_log("Got ok");
112

113
            $posts = $ret->getDecodedBody();
3✔
114

115
            foreach ($posts['data'] as $wallpost) {
3✔
116
                error_log("Got " . json_encode($wallpost));
3✔
117
                if (!Utils::pres('created_time', $wallpost) || (strtotime($wallpost['created_time']) >= strtotime($since))) {
3✔
118
                    error_log("Include");
3✔
119
                    $rc = $this->dbhm->preExec("INSERT IGNORE INTO groups_facebook_toshare (sharefrom, postid, data) VALUES (?,?,?);", [
3✔
120
                        $sharefrom,
121
                        $wallpost['id'],
3✔
122
                        json_encode($wallpost)
3✔
123
                    ]);
124

125
                    if ($rc) {
3✔
126
                        $id = $this->dbhm->lastInsertId();
3✔
127
                        $count++;
3✔
128

129
                        if ($id) {
3✔
130
                            # We only want one copy of this in our newsfeed because it's shown to everyone.
131
                            $n = new Newsfeed($this->dbhr, $this->dbhm);
3✔
132
                            $fid = $n->create(Newsfeed::TYPE_CENTRAL_PUBLICITY, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, $id);
3✔
133
                        }
134
                    }
135
                }
136
            }
137
        } catch (\Exception $e) {
1✔
138
            $code = $e->getCode();
1✔
139
            error_log("Failed to scrape code $code message " . $e->getMessage() . " token " . $this->token);
1✔
140
        }
141

142
        # Reset any rate-limited pages.
143
        $this->dbhm->preExec("UPDATE `groups_facebook` SET valid = 1, lasterror = 'Reset after rate limit' WHERE valid = 0 AND lasterror LIKE '%We limit how often you can post%' AND TIMESTAMPDIFF(MINUTE, lasterrortime, NOW()) > 120;");
3✔
144

145
        return($count);
3✔
146
    }
147

148
    public function listSocialActions(&$ctx, $me = NULL, $mindate = '7 days ago') {
149
        # We want posts which have been collected from the sharefrom page which have not already been shared, for
150
        # groups where we are a moderator.
151
        $mindate = date("Y-m-d H:i:s", strtotime($mindate));
30✔
152
        $me = $me ? $me : Session::whoAmI($this->dbhr, $this->dbhm);
30✔
153
        $ret = [];
30✔
154
        $dateq = $mindate ? " groups_facebook_toshare.date >= '$mindate' AND " : '';
30✔
155

156
        if ($me) {
30✔
157
            $minid = $ctx ? intval($ctx['id']) : 0;
30✔
158

159
            $modships = [];
30✔
160

161
            # Remove groups which aren't linked.
162
            $groups = $this->dbhr->preQuery("SELECT memberships.groupid FROM memberships INNER JOIN groups_facebook ON groups_facebook.groupid = memberships.groupid WHERE userid = ? AND role IN ('Owner', 'Moderator') AND valid = 1;",
30✔
163
                [
164
                    $me->getId()
30✔
165
                ]);
166

167
            foreach ($groups as $group) {
30✔
168
                # Only show social actions where we're an active mod.
169
                if ($me->activeModForGroup($group['groupid'])) {
5✔
170
                    $modships[] = $group['groupid'];
5✔
171
                }
172
            }
173

174
            if (count($modships) > 0) {
30✔
175
                # We want to find all Facebook pages where we haven't shared this post.  To make this scale better with
176
                # many groups we do a more complex query and then munge the data.
177
                $groupids = implode(',', $modships);
5✔
178
                $sql = "SELECT DISTINCT groups_facebook_toshare.*, groups_facebook.uid, 'Facebook' AS actiontype FROM groups_facebook_toshare 
5✔
179
INNER JOIN groups_facebook ON groups_facebook.sharefrom = groups_facebook_toshare.sharefrom AND valid = 1 
180
LEFT JOIN groups_facebook_shares ON groups_facebook_shares.postid = groups_facebook_toshare.postid AND groups_facebook_shares.uid = groups_facebook.uid 
181
WHERE 
182
$dateq 
183
groups_facebook_toshare.id > ?
184
AND groups_facebook.groupid IN ($groupids) 
185
AND groups_facebook_shares.postid IS NULL 
186
AND groups_facebook.type = 'Page' 
187
ORDER BY groups_facebook_toshare.id DESC;";
188
                $posts = $this->dbhr->preQuery($sql, [ $minid ]);
5✔
189

190
                $remaining = [];
5✔
191

192
                foreach ($posts as &$post) {
5✔
193
                    $ctx['id'] = $post['id'];
4✔
194

195
                    if (!array_key_exists($post['id'], $remaining)) {
4✔
196
                        # This is a new post which we've not considered so far.
197
                        $remaining[$post['id']] = $post;
4✔
198
                        unset($remaining[$post['id']]['uid']);
4✔
199
                        $remaining[$post['id']]['uids'] = [];
4✔
200
                        $data = json_decode($post['data'], TRUE);
4✔
201
                        $remaining[$post['id']]['full_picture'] = Utils::presdef('full_picture', $data, NULL);
4✔
202
                        $remaining[$post['id']]['message'] = Utils::presdef('message', $data, NULL);
4✔
203
                        $remaining[$post['id']]['type'] = Utils::presdef('type', $data, NULL);
4✔
204

205
                        if (preg_match('/(.*)_(.*)/', $post['postid'], $matches)) {
4✔
206
                            # Create the iframe version of the Facebook plugin.
207
                            $pageid = $matches[1];
4✔
208
                            $postid = $matches[2];
4✔
209
                            $remaining[$post['id']]['iframe'] = '<iframe src="https://www.facebook.com/plugins/post.php?href=https%3A%2F%2Fwww.facebook.com%2F' . $pageid . '%2Fposts%2F' . $postid . '%2F&width=auto&show_text=true&appId=' . FBGRAFFITIAPP_ID . '&height=500" width="500" height="500" style="border:none;overflow:hidden" scrolling="no" frameborder="0" allowTransparency="true"></iframe>';
4✔
210
                        }
211
                    }
212

213
                    # Add this Facebook page/group in for this post.
214
                    $remaining[$post['id']]['uids'][] = $post['uid'];
4✔
215
                }
216

217
                foreach ($remaining as $groupid => $post) {
5✔
218
                    $ret[] = $post;
4✔
219
                }
220
            }
221
        }
222

223
        return($ret);
30✔
224
    }
225

226
    public function performSocialAction($id) {
227
        $me = Session::whoAmI($this->dbhr, $this->dbhm);
1✔
228
        $ret = FALSE;
1✔
229

230
        if ($me) {
1✔
231
            # We need to be a mod on the relevant group.
232
            $modships = $me->getModeratorships();
1✔
233

234
            if (count($modships) > 0) {
1✔
235
                $groupids = implode(',', $modships);
1✔
236
                $sql = "SELECT DISTINCT groups_facebook_toshare.*, groups_facebook.type AS facebooktype, groups_facebook.uid, groups_facebook.groupid FROM groups_facebook_toshare INNER JOIN groups_facebook ON groups_facebook.sharefrom = groups_facebook_toshare.sharefrom AND groupid IN ($groupids) AND uid = ? AND groups_facebook_toshare.id = ?;";
1✔
237
                $actions = $this->dbhr->preQuery($sql, [ $this->uid, $id ]);
1✔
238

239
                foreach ($actions as $action) {
1✔
240
                    # Check that we've not already tried.
241
                    $shares = $this->dbhr->preQuery("SELECT * FROM groups_facebook_shares WHERE uid = ? AND groupid = ? AND postid = ?;", [
1✔
242
                        $action['uid'],
1✔
243
                        $action['groupid'],
1✔
244
                        $action['postid']
1✔
245
                    ]);
246

247
                    if (!count($shares)) {
1✔
248
                        # Whether or not this worked, remember that we've tried, so that we don't try again.
249
                        #error_log("Record INSERT IGNORE INTO groups_facebook_shares (uid, groupid, postid) VALUES ({$action['uid']},{$action['groupid']},{$action['postid']});");
250
                        $this->dbhm->preExec("INSERT IGNORE INTO groups_facebook_shares (uid, groupid, postid) VALUES (?,?,?);", [
1✔
251
                            $action['uid'],
1✔
252
                            $action['groupid'],
1✔
253
                            $action['postid']
1✔
254
                        ]);
255

256
                        $page = $action['facebooktype'] == GroupFacebook::TYPE_PAGE;
1✔
257
                        $fb = $this->getFB($page);
1✔
258

259
                        if ($page) {
1✔
260
                            # Like the original post.
261
                            try {
262
                                $res = $fb->post($action['postid'] . '/likes', [], $this->token);
1✔
263
                            } catch (\Exception $e) {
×
264
                                # Some likes can fail when using the user access token because some posts are
265
                                # strangely not visible.  Unclear why.  But don't mark the token as invalid just for
266
                                # these.
267
                                error_log("Like failed with " . $e->getMessage());
×
268
                            }
269
                            #error_log("Like returned " . var_export($res, true));
270
                        }
271

272
                        try {
273
                            $a = explode('_', $action['postid']);
1✔
274
                            $params['link'] = 'https://www.facebook.com/' . $a[0] . '/posts/' . $a[1];
1✔
275
                            $result = $fb->post($this->id . '/feed', $params, $this->token);
1✔
276
                            #error_log("Share via " . json_encode($params) . " returned " . var_export($result, TRUE));
277
                            $ret = TRUE;
1✔
278
                        } catch (\Exception $e) {
×
279
                            error_log("Share failed with " . $e->getMessage() . " params " . json_encode($params));
×
280
                            $msg = $e->getMessage();
×
281

282
                            if (strpos($msg, 'The url you supplied is invalid') === FALSE &&
×
283
                                strpos($msg, 'Please reduce the amount of data you\'re asking for') === FALSE) {
×
284
                                # These errors seems to happen occasionally at random, and doesn't mean the link is
285
                                # invalid.
286
                                # TODO Disabled because Facebook have suspended relevant permission.
287
//                            $this->dbhm->preExec("UPDATE groups_facebook SET valid = 0, lasterror = ?, lasterrortime = NOW() WHERE uid = ?", [
288
//                                $msg,
289
//                                $action['uid']
290
//                            ]);
291
                            }
292
                        }
293

294
                        $n = new PushNotifications($this->dbhr, $this->dbhm);
1✔
295
                        $n->notifyGroupMods($action['groupid']);
1✔
296
                    }
297
                }
298
            }
299
        }
300

301
        return $ret;
1✔
302
    }
303

304
    public function hideSocialAction($id) {
305
        $me = Session::whoAmI($this->dbhr, $this->dbhm);
1✔
306
        if ($me) {
1✔
307
            # We need to be a mod on the relevant group.
308
            $modships = $me->getModeratorships();
1✔
309

310
            if (count($modships) > 0) {
1✔
311
                $groupids = implode(',', $modships);
1✔
312

313
                $sql = "SELECT DISTINCT groups_facebook_toshare.*, groups_facebook.type AS facebooktype, groups_facebook.uid, groups_facebook.groupid FROM groups_facebook_toshare INNER JOIN groups_facebook ON groups_facebook.sharefrom = groups_facebook_toshare.sharefrom AND groupid IN ($groupids) AND uid = ? AND groups_facebook_toshare.id = ?;";
1✔
314
                $actions = $this->dbhr->preQuery($sql, [ $this->uid, $id ]);
1✔
315

316
                foreach ($actions as $action) {
1✔
317
                    $this->dbhm->preExec("INSERT IGNORE INTO groups_facebook_shares (uid, groupid, postid, status) VALUES (?,?,?, 'Hidden');", [
1✔
318
                        $this->uid,
1✔
319
                        $action['groupid'],
1✔
320
                        $action['postid']
1✔
321
                    ]);
322
                }
323
            }
324
        }
325
    }
326

327
    public static function listForGroup($dbhr, $dbhm, $groupid) {
328
        $ids = [];
10✔
329
        $groups = $dbhr->preQuery("SELECT uid FROM groups_facebook WHERE groupid = ?;", [ $groupid ]);
10✔
330
        foreach ($groups as $group) {
10✔
331
            $ids[] = $group['uid'];
4✔
332
        }
333

334
        return($ids);
10✔
335
    }
336

337
    public static function listForGroups($dbhr, $dbhm, $gids, $token = FALSE) {
338
        $ret = [];
25✔
339

340
        if (count($gids)) {
25✔
341
            $groups = $dbhr->preQuery("SELECT " . implode(',', GroupFacebook::$publicatts) . " FROM groups_facebook WHERE groupid IN (" . implode(',', $gids) . ");");
18✔
342
            foreach ($groups as &$group) {
18✔
343
                if (!$token) {
3✔
344
                    unset($group['token']);
3✔
345
                }
346

347
                $ret[$group['groupid']][] = $group;
3✔
348
            }
349
        }
350

351
        return($ret);
25✔
352
    }
353

354
    public function sharePopularMessage($groupid, $msgid = NULL, $batch = FALSE) {
355
        $ret = FALSE;
1✔
356

357
        if (!$msgid) {
1✔
358
            $pops = $this->dbhr->preQuery("SELECT * FROM messages_popular WHERE groupid = ? AND shared = 0 AND declined = 0 AND expired = 0;", [
×
359
                $groupid
360
            ]);
361

362
            foreach ($pops as $pop) {
×
363
                $msgid = $pop['msgid'];
×
364
            }
365
        }
366

367
        if ($msgid) {
1✔
368
            # Check we've not shared this.
369
            $msgs = $this->dbhr->preQuery("SELECT id FROM messages_popular WHERE groupid = ? AND msgid = ? AND shared = 0;", [
1✔
370
                $groupid,
371
                $msgid
372
            ]);
373

374
            if (!count($msgs)) {
1✔
375
                $me = Session::whoAmI($this->dbhr, $this->dbhm);
1✔
376

377
                if ($batch || $me && $me->isModOrOwner($groupid)) {
1✔
378
                    $pages = $this->dbhr->preQuery("SELECT * FROM groups_facebook WHERE groupid = ?;", [ $groupid ]);
1✔
379

380
                    foreach ($pages as $page)
1✔
381
                    {
382
                        $fb = $this->getFB(TRUE);
1✔
383
                        $token = $page['token'];
1✔
384

385
                        $g = Group::get($this->dbhr, $this->dbhm, $groupid);
1✔
386
                        $s = new Shortlink($this->dbhr, $this->dbhm);
1✔
387
                        $shortlinks = $s->listAll($groupid);
1✔
388

389
                        try
390
                        {
391
                            # Find the location.  We're assuming that this will encourage Facebook to show it to
392
                            # people nearby.
393
                            $m = new Message($this->dbhr, $this->dbhm, $msgid);
1✔
394
                            $atts = $m->getPublic();
1✔
395

396
                            $message = 'FREE!  Trending yesterday on ' . $g->getName() . ".";
1✔
397
                            $link = 'https://' . USER_SITE . '/explore/' . $g->getPrivate('nameshort') . '/' . $msgid;
1✔
398
                            $message .= "\n\n" . $m->getSubject() . "\n\nHop over to $link to see what else is being given away - or to ask for stuff you'd like.";
1✔
399

400
                            $picture = NULL;
1✔
401

402
                            if (Utils::pres('attachments', $atts) && count($atts['attachments'])) {
1✔
403
                                $picture = $atts['attachments'][0]['path'];
1✔
404
                            }
405

406
                            $fb->post($page['id'] . '/photos', [
1✔
407
                                'message' => $message,
408
                                'url' => $picture
409
                            ], $token);
410

411
                            $ret = TRUE;
×
412
                        } catch (\Exception $e) {
1✔
413
                            error_log("Share failed with " . $e->getMessage());
1✔
414
                        }
415
                    }
416
                }
417
            }
418
        }
419

420
        return $ret;
1✔
421
    }
422
}
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

© 2025 Coveralls, Inc