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

Freegle / iznik-server / 0a312c38-58e2-4613-9dc7-19fb0c677c25

12 Jun 2024 08:10AM UTC coverage: 94.806% (-0.05%) from 94.856%
0a312c38-58e2-4613-9dc7-19fb0c677c25

push

circleci

edwh
Uploadcare - chat images

1 of 3 new or added lines in 1 file covered. (33.33%)

92 existing lines in 4 files now uncovered.

25424 of 26817 relevant lines covered (94.81%)

31.51 hits per line

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

80.72
/include/message/Attachment.php
1
<?php
2

3
namespace Freegle\Iznik;
4

5

6
use Jenssegers\ImageHash\ImageHash;
7

8
//use Google\Cloud\VideoIntelligence\V1\VideoIntelligenceServiceClient;
9
//use Google\Cloud\VideoIntelligence\V1\Feature;
10

11
# This is a base class
12
class Attachment {
13
    /** @var  $dbhr LoggedPDO */
14
    private $dbhr;
15
    /** @var  $dbhm LoggedPDO */
16
    private $dbhm;
17
    private $id, $table, $hash, $archived, $externalurl, $externaluid, $externalmods;
18

19
    /**
20
     * @return null
21
     */
22
    public function getId() {
23
        return $this->id;
1✔
24
    }
25

26
    const TYPE_MESSAGE = 'Message';
27
    const TYPE_GROUP = 'Group';
28
    const TYPE_NEWSLETTER = 'Newsletter';
29
    const TYPE_COMMUNITY_EVENT = 'CommunityEvent';
30
    const TYPE_CHAT_MESSAGE = 'ChatMessage';
31
    const TYPE_USER = 'User';
32
    const TYPE_NEWSFEED = 'Newsfeed';
33
    const TYPE_VOLUNTEERING = 'Volunteering';
34
    const TYPE_STORY = 'Story';
35
    const TYPE_NOTICEBOARD = 'Noticeboard';
36

37
    /**
38
     * @return mixed
39
     */
40
    public function getHash() {
41
        return $this->hash;
11✔
42
    }
43

44
    public function getPath($thumb = false, $id = null, $archived = false) {
45
        if ($this->externaluid) {
54✔
46
            $u = new UploadCare();
2✔
47
            return $u->getUrl($this->externaluid, $this->externalmods);
2✔
48
        }
49

50
        if ($this->externalurl) {
52✔
UNCOV
51
            return $this->externalurl;
×
52
        }
53

54
        # We serve up our attachment names as though they are files.
55
        # When these are fetched it will go through image.php
56
        $id = $id ? $id : $this->id;
52✔
57

58
        switch ($this->type) {
52✔
59
            case Attachment::TYPE_MESSAGE:
60
                $name = 'img';
34✔
61
                break;
34✔
62
            case Attachment::TYPE_GROUP:
63
                $name = 'gimg';
2✔
64
                break;
2✔
65
            case Attachment::TYPE_NEWSLETTER:
66
                $name = 'nimg';
2✔
67
                break;
2✔
68
            case Attachment::TYPE_COMMUNITY_EVENT:
69
                $name = 'cimg';
2✔
70
                break;
2✔
71
            case Attachment::TYPE_VOLUNTEERING:
72
                $name = 'oimg';
2✔
73
                break;
2✔
74
            case Attachment::TYPE_CHAT_MESSAGE:
75
                $name = 'mimg';
5✔
76
                break;
5✔
77
            case Attachment::TYPE_USER:
78
                $name = 'uimg';
1✔
79
                break;
1✔
80
            case Attachment::TYPE_NEWSFEED:
81
                $name = 'fimg';
1✔
82
                break;
1✔
83
            case Attachment::TYPE_STORY:
84
                $name = 'simg';
1✔
85
                break;
1✔
86
            case Attachment::TYPE_NOTICEBOARD:
87
                $name = 'bimg';
2✔
88
                break;
2✔
89
        }
90

91
        $name = $thumb ? "t$name" : $name;
52✔
92
        $domain = ($this->archived || $archived) ? IMAGE_ARCHIVED_DOMAIN : IMAGE_DOMAIN;
52✔
93

94
        return ("https://$domain/{$name}_$id.jpg");
52✔
95
    }
96

97
    public function getPublic() {
98
        $ret = array(
13✔
99
            'id' => $this->id,
13✔
100
            'hash' => $this->hash,
13✔
101
            $this->idatt => $this->{$this->idatt}
13✔
102
        );
13✔
103

104
        $ret['path'] = $this->getPath(false);
13✔
105
        $ret['paththumb'] = $this->getPath(true);
13✔
106
        $ret['mods'] = $this->externalmods;
13✔
107

108
        return ($ret);
13✔
109
    }
110

111
    function __construct(LoggedPDO $dbhr, LoggedPDO $dbhm, $id = null, $type = Attachment::TYPE_MESSAGE, $atts = null) {
112
        $this->dbhr = $dbhr;
368✔
113
        $this->dbhm = $dbhm;
368✔
114
        $this->id = $id;
368✔
115
        $this->type = $type;
368✔
116
        $this->archived = false;
368✔
117
        $url = '';
368✔
118
        $uid = '';
368✔
119
        $mods = '';
368✔
120

121
        switch ($type) {
122
            case Attachment::TYPE_MESSAGE:
123
            {
256✔
124
                $this->table = 'messages_attachments';
256✔
125
                $this->idatt = 'msgid';
256✔
126
                $this->uidname = 'externaluid';
256✔
127
                $this->modsname = 'externalmods';
256✔
128
                $uid = ', externaluid';
256✔
129
                $mods = ', externalmods';
256✔
130
                break;
256✔
131
            }
256✔
132
            case Attachment::TYPE_GROUP:
133
                $this->table = 'groups_images';
216✔
134
                $this->idatt = 'groupid';
216✔
135
                break;
216✔
136
            case Attachment::TYPE_NEWSLETTER:
137
                $this->table = 'newsletters_images';
3✔
138
                $this->idatt = 'articleid';
3✔
139
                break;
3✔
140
            case Attachment::TYPE_COMMUNITY_EVENT:
141
                $this->table = 'communityevents_images';
2✔
142
                $this->idatt = 'eventid';
2✔
143
                break;
2✔
144
            case Attachment::TYPE_VOLUNTEERING:
145
                $this->table = 'volunteering_images';
2✔
146
                $this->idatt = 'opportunityid';
2✔
147
                break;
2✔
148
            case Attachment::TYPE_CHAT_MESSAGE:
149
                $this->table = 'chat_images';
6✔
150
                $this->idatt = 'chatmsgid';
6✔
151
                $this->uidname = 'externaluid';
6✔
152
                $this->modsname = 'externalmods';
6✔
153
                $uid = ', externaluid';
6✔
154
                $mods = ', externalmods';
6✔
155
                break;
6✔
156
            case Attachment::TYPE_USER:
157
            {
2✔
158
                $this->table = 'users_images';
2✔
159
                $this->idatt = 'userid';
2✔
160
                $this->externalurlname = 'externalurl';
2✔
161
                $url = ', url';
2✔
162
                break;
2✔
163
            }
2✔
164
            case Attachment::TYPE_NEWSFEED:
165
                $this->table = 'newsfeed_images';
2✔
166
                $this->idatt = 'newsfeedid';
2✔
167
                break;
2✔
168
            case Attachment::TYPE_STORY:
169
                $this->table = 'users_stories_images';
1✔
170
                $this->idatt = 'storyid';
1✔
171
                break;
1✔
172
            case Attachment::TYPE_NOTICEBOARD:
173
                $this->table = 'noticeboards_images';
2✔
174
                $this->idatt = 'noticeboardid';
2✔
175
                break;
2✔
176
        }
177

178
        if ($id) {
368✔
179
            $sql = "SELECT {$this->idatt}, hash, archived $url $uid $mods FROM {$this->table} WHERE id = ?;";
44✔
180
            $as = $atts ? [$atts] : $this->dbhr->preQuery($sql, [$id]);
44✔
181
            foreach ($as as $att) {
44✔
182
                $this->hash = $att['hash'];
34✔
183
                $this->archived = $att['archived'];
34✔
184
                $this->externalurl = Utils::presdef($this->externalurlname, $att, null);
34✔
185
                $this->externaluid = Utils::presdef($this->uidname, $att, null);
34✔
186
                $this->externalmods = Utils::presdef($this->modsname, $att, null);
34✔
187
                $this->{$this->idatt} = $att[$this->idatt];
34✔
188
            }
189
        }
190
    }
191

192
    public function create($id, $data, $uid = null, $url = null, $stripExif = TRUE, $mods = NULL) {
193
        if ($uid) {
67✔
194
            if ($stripExif && UPLOADCARE_PUBLIC_KEY) {
2✔
195
                // Image data is held externally on uploadcare.  The uploaded photo will contain EXIF data, and there
196
                // isn't currently a way to strip that out on upload.  So we have to copy the image to a new one which
197
                // "bakes in" the removal of the EXIF data.
UNCOV
198
                $uc = new UploadCare();
×
UNCOV
199
                $uid = $uc->stripExif($uid);
×
UNCOV
200
                $this->hash = $uc->getPerceptualHash($uid);
×
201
            }
202

203
            if ($this->externalurlname) {
2✔
UNCOV
204
                $rc = $this->dbhm->preExec(
×
UNCOV
205
                    "INSERT INTO {$this->table} (`{$this->idatt}`, `{$this->uidname}`, `{$this->externalurlname}`, `{$this->modsname}`, `hash`) VALUES (?, ?, ?, ?, ?);",
×
UNCOV
206
                    [
×
UNCOV
207
                        $id,
×
UNCOV
208
                        $uid,
×
UNCOV
209
                        $url,
×
UNCOV
210
                        json_encode($mods),
×
UNCOV
211
                        $this->hash,
×
UNCOV
212
                    ]
×
UNCOV
213
                );
×
214
            } else {
215
                $rc = $this->dbhm->preExec(
2✔
216
                    "INSERT INTO {$this->table} (`{$this->idatt}`, `{$this->uidname}`, `{$this->modsname}`, `hash`) VALUES (?, ?, ?, ?);",
2✔
217
                    [
2✔
218
                        $id,
2✔
219
                        $uid,
2✔
220
                        json_encode($mods),
2✔
221
                        $this->hash,
2✔
222
                    ]
2✔
223
                );
2✔
224
            }
225

226
            $imgid = $rc ? $this->dbhm->lastInsertId() : null;
2✔
227

228
            if ($imgid) {
2✔
229
                $this->id = $imgid;
2✔
230
                $this->externaluid = $uid;
2✔
231
                $this->externalmods = $mods;
2✔
232
                $this->externalurl = $url;
2✔
233
            }
234

235
            return ([$imgid, $uid]);
2✔
236
        } else {
237
            # We generate a perceptual hash.  This allows us to spot duplicate or similar images later.
238
            $hasher = new ImageHash;
65✔
239
            $img = @imagecreatefromstring($data);
65✔
240
            $hash = $img ? $hasher->hash($img) : null;
65✔
241

242
            $rc = $this->dbhm->preExec(
65✔
243
                "INSERT INTO {$this->table} (`{$this->idatt}`, `data`, `hash`) VALUES (?, ?, ?);",
65✔
244
                [
65✔
245
                    $id,
65✔
246
                    $data,
65✔
247
                    $hash
65✔
248
                ]
65✔
249
            );
65✔
250

251
            $imgid = $rc ? $this->dbhm->lastInsertId() : null;
65✔
252

253
            if ($imgid) {
65✔
254
                $this->id = $imgid;
65✔
255
                $this->hash = $hash;
65✔
256
            }
257

258
            return ($imgid);
65✔
259
        }
260
    }
261

262
    public function getById($id) {
263
        $sql = "SELECT id FROM {$this->table} WHERE {$this->idatt} = ? AND ((data IS NOT NULL AND LENGTH(data) > 0) OR archived = 1) ORDER BY id;";
34✔
264
        $atts = $this->dbhr->preQuery($sql, [$id]);
34✔
265
        $ret = [];
34✔
266
        foreach ($atts as $att) {
34✔
267
            $ret[] = new Attachment($this->dbhr, $this->dbhm, $att['id']);
6✔
268
        }
269

270
        return ($ret);
34✔
271
    }
272

273
    public function getByIds($ids) {
274
        $ret = [];
105✔
275

276
        $extstr = '';
105✔
277
        $extwhere = '';
105✔
278

279
        switch ($this->type) {
105✔
280
            case Attachment::TYPE_MESSAGE:
281
                $extstr = ', externaluid, externalmods ';
105✔
282
                $extwhere = ' OR externaluid IS NOT NULL';
105✔
283
                break;
105✔
284
        }
285

286
        if (count($ids)) {
105✔
287
            $sql = "SELECT id, {$this->idatt}, hash, archived $extstr FROM {$this->table} 
102✔
288
                       WHERE {$this->idatt} IN (" . implode(',', $ids) . ") 
102✔
289
                       AND ((data IS NOT NULL AND LENGTH(data) > 0) OR archived = 1 $extwhere) 
102✔
290
                       ORDER BY `primary` DESC, id;";
102✔
291
            #error_log($sql);
292
            $atts = $this->dbhr->preQuery($sql);
102✔
293
            foreach ($atts as $att) {
102✔
294
                $ret[] = new Attachment($this->dbhr, $this->dbhm, $att['id'], $this->type, $att);
9✔
295
            }
296
        }
297

298
        return ($ret);
105✔
299
    }
300

301
    public function getByImageIds($ids) {
302
        $ret = [];
2✔
303

304
        if (count($ids)) {
2✔
305
            $sql = "SELECT id, {$this->idatt}, hash, archived FROM {$this->table} WHERE id IN (" . implode(
2✔
306
                    ',',
2✔
307
                    $ids
2✔
308
                ) . ") AND ((data IS NOT NULL AND LENGTH(data) > 0) OR archived = 1) ORDER BY id;";
2✔
309
            $atts = $this->dbhr->preQuery($sql);
2✔
310
            foreach ($atts as $att) {
2✔
311
                $ret[] = new Attachment($this->dbhr, $this->dbhm, $att['id'], $this->type, $att);
1✔
312
            }
313
        }
314

315
        return ($ret);
2✔
316
    }
317

318
    public function scp($host, $data, $fn, &$failed) {
319
        $connection = @ssh2_connect($host, 22);
×
320
        $failed = true;
×
321

322
        if ($connection) {
×
UNCOV
323
            if (@ssh2_auth_pubkey_file(
×
UNCOV
324
                $connection,
×
325
                CDN_SSH_USER,
×
326
                CDN_SSH_PUBLIC_KEY,
×
327
                CDN_SSH_PRIVATE_KEY
×
328
            )) {
×
329
                $temp = tempnam(sys_get_temp_dir(), "img_archive_$fn");
×
UNCOV
330
                file_put_contents($temp, $data);
×
UNCOV
331
                $rem = "/var/www/iznik/images/$fn";
×
UNCOV
332
                $retry = 0;
×
333

334
                do {
UNCOV
335
                    $rc = ssh2_scp_send($connection, $temp, $rem, 0644);
×
336

UNCOV
337
                    if (!$rc) {
×
UNCOV
338
                        $msg = "SCP of $rem failed, retry $retry";
×
UNCOV
339
                        \Sentry\captureMessage($msg);
×
UNCOV
340
                        error_log($msg);
×
UNCOV
341
                        sleep(1);
×
342
                    }
343

UNCOV
344
                    $retry++;
×
UNCOV
345
                } while (!$rc && $retry < 5);
×
UNCOV
346
                $failed = !$rc;
×
UNCOV
347
                unlink($temp);
×
UNCOV
348
                error_log("scp $temp to $host $rem returned $rc failed? $failed");
×
349
            }
350

351
            # Exit gracefully - might help with file truncation.
UNCOV
352
            ssh2_exec($connection, 'exit');
×
353
        }
354
    }
355

356
    public function archive() {
357
        if ($this->externalurl) {
11✔
358
            // We don't archive external URLs.
UNCOV
359
            return;
×
360
        }
361

362
        # We archive out of the DB onto our two CDN image hosts.  This reduces load on the servers because we don't
363
        # have to serve the images up, and it also reduces the disk space we need within the DB (which is not an ideal
364
        # place to store large amounts of image data);
365
        #
366
        # If we fail then we leave it unchanged for next time.
367
        $data = $this->getData();
11✔
368
        $rc = true;
11✔
369

370
        if ($data) {
11✔
371
            $rc = false;
10✔
372

373
            try {
374
                $name = null;
10✔
375

376
                # Only these types are in archive_attachments.
377
                switch ($this->type) {
10✔
378
                    case Attachment::TYPE_MESSAGE:
379
                        $tname = 'timg';
1✔
380
                        $name = 'img';
1✔
381
                        break;
1✔
382
                    case Attachment::TYPE_CHAT_MESSAGE:
383
                        $tname = 'tmimg';
1✔
384
                        $name = 'mimg';
1✔
385
                        break;
1✔
386
                    case Attachment::TYPE_NEWSFEED:
387
                        $tname = 'tfimg';
1✔
388
                        $name = 'fimg';
1✔
389
                        break;
1✔
390
                    case Attachment::TYPE_COMMUNITY_EVENT:
391
                        $tname = 'tcimg';
1✔
392
                        $name = 'cimg';
1✔
393
                        break;
1✔
394
                    case Attachment::TYPE_NOTICEBOARD:
395
                        $tname = 'tbimg';
1✔
396
                        $name = 'bimg';
1✔
397
                        break;
1✔
398
                }
399

400
                if ($name) {
10✔
401
                    $failed = false;
5✔
402

403
                    foreach ([CDN_HOST_1, CDN_HOST_2] as $host) {
5✔
404
                        # Upload the thumbnail.  If this fails we'll leave it untouched.
405
                        $i = new Image($data);
5✔
406
                        if ($i->img) {
5✔
407
                            $i->scale(250, 250);
5✔
408
                            $thumbdata = $i->getData(100);
5✔
409
                            $this->scp($host, $thumbdata, "{$tname}_{$this->id}.jpg", $failed2);
5✔
410
                            $failed |= $failed2;
5✔
411
                            $this->scp($host, $data, "{$name}_{$this->id}.jpg", $failed3);
5✔
412
                            $failed |= $failed3;
5✔
413
                        } else {
UNCOV
414
                            error_log("...failed to create image {$this->id}");
×
415
                        }
416
                    }
417

418
                    $rc = !$failed;
10✔
419
                }
UNCOV
420
            } catch (\Exception $e) {
×
UNCOV
421
                error_log("Archive failed " . $e->getMessage());
×
422
            }
423
        }
424

425
        if ($rc) {
11✔
426
            # Remove from the DB.
427
            $sql = "UPDATE {$this->table} SET archived = 1, data = NULL WHERE id = {$this->id};";
6✔
428
            $this->archived = true;
6✔
429
            $this->dbhm->exec($sql);
6✔
430
        }
431

432
        return ($rc);
11✔
433
    }
434

435
    public function setData($data) {
436
        $this->dbhm->preExec("UPDATE {$this->table} SET archived = 0, data = ? WHERE id = ?;", [
2✔
437
            $data,
2✔
438
            $this->id
2✔
439
        ]);
2✔
440
    }
441

442
    public function fgc($url, $use_include_path, $ctx) {
443
        return @file_get_contents($url, $use_include_path, $ctx);
1✔
444
    }
445

446
    public function canRedirect() {
447
        if ($this->externaluid) {
46✔
448
            $u = new UploadCare();
2✔
449
            return $u->getUrl($this->externaluid, $this->externalmods);
2✔
450
        } else if ($this->externalurl) {
44✔
UNCOV
451
            return $this->externalurl;
×
452
        } else {
453
            if ($this->archived) {
44✔
454
                # Only these types are in archive_attachments.
455
                switch ($this->type) {
5✔
456
                    case Attachment::TYPE_MESSAGE:
457
                        $tname = 'timg';
1✔
458
                        $name = 'img';
1✔
459
                        break;
1✔
460
                    case Attachment::TYPE_CHAT_MESSAGE:
461
                        $tname = 'tmimg';
1✔
462
                        $name = 'mimg';
1✔
463
                        break;
1✔
464
                    case Attachment::TYPE_NEWSFEED:
465
                        $tname = 'tfimg';
1✔
466
                        $name = 'fimg';
1✔
467
                        break;
1✔
468
                    case Attachment::TYPE_COMMUNITY_EVENT:
469
                        $tname = 'tcimg';
1✔
470
                        $name = 'cimg';
1✔
471
                        break;
1✔
472
                    case Attachment::TYPE_NOTICEBOARD:
473
                        $tname = 'tbimg';
1✔
474
                        $name = 'bimg';
1✔
475
                        break;
1✔
476
                }
477

478
                return 'https://' . IMAGE_ARCHIVED_DOMAIN . "/{$name}_{$this->id}.jpg";
5✔
479
            }
480
        }
481

482
        return false;
44✔
483
    }
484

485
    public function getData() {
486
        $ret = null;
45✔
487

488
        $url = $this->canRedirect();
45✔
489

490
        if ($url) {
45✔
491
            # This attachment has been archived out of our database, to a CDN.  Normally we would expect
492
            # that we wouldn't come through here, because we'd serve up an image link directly to the CDN, but
493
            # there is a timing window where we could archive after we've served up a link, so we have
494
            # to handle it.
495
            #
496
            # We fetch the data - not using SSL as we don't need to, and that host might not have a cert.  And
497
            # we put it back in the DB, because we are probably going to fetch it again.
498
            #
499
            # Apply a short timeout to avoid hanging the server if Azure is down.
500
            $ctx = stream_context_create(
6✔
501
                array(
6✔
502
                    'http' =>
6✔
503
                        array(
6✔
504
                            'timeout' => 2,
6✔
505
                        )
6✔
506
                )
6✔
507
            );
6✔
508

509
            $ret = $this->fgc($url, false, $ctx);
6✔
510
        } else {
511
            $sql = "SELECT * FROM {$this->table} WHERE id = ?;";
44✔
512
            $datas = $this->dbhr->preQuery($sql, [$this->id]);
44✔
513

514
            foreach ($datas as $data) {
44✔
515
                $ret = $data['data'];
44✔
516
            }
517
        }
518

519
        return ($ret);
45✔
520
    }
521

522
    public function identify() {
523
        # Identify objects in an attachment using Google Vision API.  Only for messages.
524
        $items = [];
3✔
525
        if ($this->type == Attachment::TYPE_MESSAGE) {
3✔
526
            $data = $this->getData();
3✔
527
            $base64 = base64_encode($data);
3✔
528

529
            $r_json = '{
3✔
530
                "requests": [
531
                    {
532
                      "image": {
533
                        "content":"' . $base64 . '"
3✔
534
                      },
535
                      "features": [
536
                          {
537
                            "type": "LABEL_DETECTION",
538
                            "maxResults": 20
539
                          }
540
                      ]
541
                    }
542
                ]
543
            }';
3✔
544

545
            $curl = curl_init();
3✔
546
            curl_setopt(
3✔
547
                $curl,
3✔
548
                CURLOPT_URL,
3✔
549
                'https://vision.googleapis.com/v1/images:annotate?key=' . GOOGLE_VISION_KEY
3✔
550
            );
3✔
551
            curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
3✔
552
            curl_setopt($curl, CURLOPT_HTTPHEADER, array("Content-type: application/json"));
3✔
553
            curl_setopt($curl, CURLOPT_POST, true);
3✔
554
            curl_setopt($curl, CURLOPT_POSTFIELDS, $r_json);
3✔
555
            $json_response = curl_exec($curl);
3✔
556
            $status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
3✔
557

558
            if ($status) {
3✔
559
                $rsp = json_decode($json_response, true);
3✔
560
                #error_log("Identified {$this->id} by Google $json_response for $r_json");
561

562
                if ($rsp && array_key_exists('responses', $rsp) && count($rsp['responses']) > 0 && array_key_exists(
3✔
563
                        'labelAnnotations',
3✔
564
                        $rsp['responses'][0]
3✔
565
                    )) {
3✔
566
                    $rsps = $rsp['responses'][0]['labelAnnotations'];
3✔
567
                    $i = new Item($this->dbhr, $this->dbhm);
3✔
568

569
                    foreach ($rsps as $rsp) {
3✔
570
                        $found = $i->findByName($rsp['description']);
3✔
571
                        $wasfound = false;
3✔
572
                        foreach ($found as $item) {
3✔
573
                            $this->dbhm->background(
2✔
574
                                "INSERT INTO messages_attachments_items (attid, itemid) VALUES ({$this->id}, {$item['id']});"
2✔
575
                            );
2✔
576
                            $wasfound = true;
2✔
577
                        }
578

579
                        if (!$wasfound) {
3✔
580
                            # Record items which were suggested but not considered as items by us.  This allows us to find common items which we ought to
581
                            # add.
582
                            #
583
                            # This is usually because they're too vague.
584
                            $url = "https://" . IMAGE_DOMAIN . "/img_{$this->id}.jpg";
3✔
585
                            $this->dbhm->background(
3✔
586
                                "INSERT INTO items_non (name, lastexample) VALUES (" . $this->dbhm->quote(
3✔
587
                                    $rsp['description']
3✔
588
                                ) . ", " . $this->dbhm->quote(
3✔
589
                                    $url
3✔
590
                                ) . ") ON DUPLICATE KEY UPDATE popularity = popularity + 1, lastexample = " . $this->dbhm->quote(
3✔
591
                                    $url
3✔
592
                                ) . ";"
3✔
593
                            );
3✔
594
                        }
595

596
                        $items = array_merge($items, $found);
3✔
597
                    }
598
                }
599
            }
600

601
            curl_close($curl);
3✔
602
        }
603

604
        return ($items);
3✔
605
    }
606

607
    public function findWebReferences() {
608
        # Find a web page containing this imge, if any.
609
        $ret = null;
×
610

611
        if ($this->type == Attachment::TYPE_MESSAGE) {
×
612
            $data = $this->getData();
×
613
            $base64 = base64_encode($data);
×
614

615
            $r_json = '{
×
616
                "requests": [
617
                    {
618
                      "image": {
619
                        "content":"' . $base64 . '"
×
620
                      },
621
                      "features": [
622
                          {
623
                            "type": "WEB_DETECTION",
624
                            "maxResults": 1
625
                          }
626
                      ]
627
                    }
628
                ]
629
            }';
×
630

631
            $curl = curl_init();
×
632
            curl_setopt(
×
UNCOV
633
                $curl,
×
634
                CURLOPT_URL,
×
635
                'https://vision.googleapis.com/v1/images:annotate?key=' . GOOGLE_VISION_KEY
×
636
            );
×
UNCOV
637
            curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
×
UNCOV
638
            curl_setopt($curl, CURLOPT_HTTPHEADER, array("Content-type: application/json"));
×
639
            curl_setopt($curl, CURLOPT_POST, true);
×
UNCOV
640
            curl_setopt($curl, CURLOPT_POSTFIELDS, $r_json);
×
UNCOV
641
            $json_response = curl_exec($curl);
×
UNCOV
642
            $status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
×
643

UNCOV
644
            if ($status) {
×
UNCOV
645
                $rsp = json_decode($json_response, true);
×
646
                #error_log("Identified {$this->id} by Google $json_response for $r_json");
UNCOV
647
                error_log("Matching " . var_export($rsp, true));
×
648

UNCOV
649
                if ($rsp &&
×
UNCOV
650
                    array_key_exists('responses', $rsp) &&
×
UNCOV
651
                    count($rsp['responses']) > 0 &&
×
UNCOV
652
                    array_key_exists('webDetection', $rsp['responses'][0]) &&
×
UNCOV
653
                    array_key_exists('pagesWithMatchingImages', $rsp['responses'][0]['webDetection'])) {
×
UNCOV
654
                    $rsps = $rsp['responses'][0]['webDetection']['pagesWithMatchingImages'];
×
655

UNCOV
656
                    foreach ($rsps as $r) {
×
UNCOV
657
                        if (array_key_exists('fullMatchingImages', $r) && strpos($r['url'], USER_SITE) === false) {
×
UNCOV
658
                            $ret = $r['url'];
×
659
                        }
660
                    }
UNCOV
661
                    error_log(var_export($rsps, true));
×
662
                }
663
            }
664

UNCOV
665
            curl_close($curl);
×
666
        }
667

UNCOV
668
        return ($ret);
×
669
    }
670

671
    public function ocr($data = null, $returnfull = false, $video = false) {
672
        # Identify text in an attachment using Google Vision API.
673
        $base64 = $data ? $data : base64_encode($this->getData());
1✔
674

675
        if ($video) {
1✔
676
//            "videoContext": {
677
//                "textDetectionConfig": {
678
//                    "languageHints": ["en"]
679
//                }
680
//              }
UNCOV
681
            $r_json = '{
×
UNCOV
682
              "inputContent": "' . $base64 . '",
×
683
              "features": ["TEXT_DETECTION"],
UNCOV
684
            }';
×
685
        } else {
686
            $r_json = '{
1✔
687
                "requests": [
688
                    {
689
                      "image": {
690
                        "content":"' . $base64 . '",
1✔
691
                      },
692
                      "features": [
693
                          {
694
                            "type": "TEXT_DETECTION"
695
                          }
696
                      ],
697
                      "imageContext": {
698
                        "languageHints": [
699
                          "en"
700
                        ]
701
                      }
702
                    }
703
                ]
704
            }';
1✔
705
        }
706

707
        $url = 'https://vision.googleapis.com/v1/images:annotate?key=' . GOOGLE_VISION_KEY;
1✔
708
        $curl = curl_init();
1✔
709
        curl_setopt($curl, CURLOPT_URL, $url);
1✔
710
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
1✔
711
        curl_setopt($curl, CURLOPT_HTTPHEADER, array("Content-type: application/json"));
1✔
712
        curl_setopt($curl, CURLOPT_POST, true);
1✔
713
        curl_setopt($curl, CURLOPT_POSTFIELDS, $r_json);
1✔
714

715
        if ($video) {
1✔
UNCOV
716
            curl_setopt($curl, CURLOPT_HTTPHEADER, array("Authorization: Bearer " . GOOGLE_VIDEO_KEY));
×
717
        }
718

719
        $json_response = curl_exec($curl);
1✔
720
        $status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
1✔
721

722
        $text = '';
1✔
723
        $rsps = null;
1✔
724

725
        if ($status) {
1✔
726
            error_log("Rsp $json_response");
1✔
727
            $rsp = json_decode($json_response, true);
1✔
728

729
            if ($rsp && array_key_exists('responses', $rsp) && count($rsp['responses']) > 0 && array_key_exists(
1✔
730
                    'textAnnotations',
1✔
731
                    $rsp['responses'][0]
1✔
732
                )) {
1✔
733
                $rsps = $rsp['responses'][0]['textAnnotations'];
1✔
734

735
                foreach ($rsps as $rsp) {
1✔
736
                    $text .= $rsp['description'] . "\n";
1✔
737
                    break;
1✔
738
                }
739
            }
740
        }
741

742
        curl_close($curl);
1✔
743

744
        return ($returnfull ? $rsps : $text);
1✔
745
    }
746

747
    public function objects($data = null) {
748
        # Identify objects in an attachment using Google Vision API.
749
        $base64 = $data ? $data : base64_encode($this->getData());
1✔
750

751
        $r_json = '{
1✔
752
            "requests": [
753
                {
754
                  "image": {
755
                    "content":"' . $base64 . '"
1✔
756
                  },
757
                  "features": [
758
                      {
759
                        "type": "OBJECT_LOCALIZATION"
760
                      }
761
                  ]
762
                }
763
            ]
764
        }';
1✔
765

766
        $curl = curl_init();
1✔
767
        curl_setopt($curl, CURLOPT_URL, 'https://vision.googleapis.com/v1/images:annotate?key=' . GOOGLE_VISION_KEY);
1✔
768
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
1✔
769
        curl_setopt($curl, CURLOPT_HTTPHEADER, array("Content-type: application/json"));
1✔
770
        curl_setopt($curl, CURLOPT_POST, true);
1✔
771
        curl_setopt($curl, CURLOPT_POSTFIELDS, $r_json);
1✔
772
        $json_response = curl_exec($curl);
1✔
773
        $status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
1✔
774

775
        $rsp = null;
1✔
776

777
        if ($status) {
1✔
778
            $rsp = json_decode($json_response, true);
1✔
779
        }
780

781
        curl_close($curl);
1✔
782

783
        return ($rsp);
1✔
784
    }
785

786
    public function setPrivate($att, $val) {
787
        $this->dbhm->preExec("UPDATE {$this->table} SET `$att` = ? WHERE id = {$this->id};", [$val]);
14✔
788
    }
789

790
    public function delete() {
791
        $this->dbhm->preExec("DELETE FROM {$this->table} WHERE id = {$this->id};");
10✔
792
    }
793

794
    public function getIdAtt() {
UNCOV
795
        return $this->idatt;
×
796
    }
797

798
    function rotate($rotate) {
799
        if ($this->externaluid) {
2✔
800
            # We can rotate this by changing external mods.
UNCOV
801
            $mods = json_decode($this->externalmods, TRUE);
×
UNCOV
802
            $mods['rotate'] = $rotate;
×
UNCOV
803
            $this->setPrivate('externalmods', json_encode($mods));
×
804
        } else {
805
            $data = $this->getData();
2✔
806
            $i = new Image($data);
2✔
807
            $i->rotate($rotate);
2✔
808
            $newdata = $i->getData(100);
2✔
809
            $this->setData($newdata);
2✔
810
        }
811

812
        if ($this->type == Attachment::TYPE_MESSAGE) {
2✔
813
            # Only some kinds of attachments record whether they are rotated.
814
            $this->recordRotate();
2✔
815
        }
816
    }
817

818
    public function recordRotate() {
819
        $this->setPrivate('rotated', 1);
2✔
820
    }
821
}
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