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

Freegle / iznik-server / 418053ef-1b85-44d6-aeef-204bc752e075

26 Jun 2024 12:25PM UTC coverage: 94.239% (-0.6%) from 94.811%
418053ef-1b85-44d6-aeef-204bc752e075

push

circleci

edwh
Test fixes.

0 of 1 new or added line in 1 file covered. (0.0%)

225 existing lines in 5 files now uncovered.

25190 of 26730 relevant lines covered (94.24%)

31.51 hits per line

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

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

3
namespace Freegle\Iznik;
4

5

6
use Jenssegers\ImageHash\ImageHash;
7

8
# TODO:
9
# - migrate old images across
10
# - retire externalurl
11
# - retire archiving
12

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

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

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

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

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

52
        if ($this->externalurl) {
3✔
UNCOV
53
            return $this->externalurl;
×
54
        }
55

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

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

93
        $name = $thumb ? "t$name" : $name;
3✔
94
        $domain = ($this->archived || $archived) ? IMAGE_ARCHIVED_DOMAIN : IMAGE_DOMAIN;
3✔
95

96
        return ("https://$domain/{$name}_$id.jpg");
3✔
97
    }
98

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

106
        $ret['path'] = $this->getPath(false);
14✔
107
        $ret['paththumb'] = $this->getPath(true);
14✔
108
        $ret['mods'] = $this->externalmods;
14✔
109

110
        return ($ret);
14✔
111
    }
112

113
    function __construct(LoggedPDO $dbhr, LoggedPDO $dbhm, $id = null, $type = Attachment::TYPE_MESSAGE, $atts = null) {
114
        $this->dbhr = $dbhr;
355✔
115
        $this->dbhm = $dbhm;
355✔
116
        $this->id = $id;
355✔
117
        $this->type = $type;
355✔
118
        $this->archived = false;
355✔
119
        $url = '';
355✔
120
        $this->uidname = 'externaluid';
355✔
121
        $this->modsname = 'externalmods';
355✔
122
        $uid = ', externaluid';
355✔
123
        $mods = ', externalmods';
355✔
124

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

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

188
    public function create($id, $data, $uid = NULL, $url = null, $stripExif = TRUE, $mods = NULL) {
189
        if ($url) {
54✔
190
            # We need to fetch the data from an external URL.
191
            $ctx = stream_context_create(['http' =>
1✔
192
                [
1✔
193
                    'timeout' => 120
1✔
194
                ]
1✔
195
             ]);
1✔
196

197
            $data = @file_get_contents($url, false, $ctx);
1✔
198
        }
199

200
        if (!$uid && $data) {
54✔
201
            # We have the literal data, which we need to upload.
202
            $uc = new UploadCare();
52✔
203
            $uid = $uc->upload($data, 'image/jpeg');
52✔
204
        }
205

206
        if ($uid) {
54✔
207
            # We now have an image on Uploadcare.
208
            if ($stripExif) {
54✔
209
                // The uploaded photo will contain EXIF data, and there isn't currently a way to strip that out on
210
                // upload.  So we have to copy the image to a new one which "bakes in" the removal of the EXIF data.
211
                $uc = new UploadCare();
53✔
212
                $uid = $uc->stripExif($uid);
53✔
213

214
                $this->hash = $uc->getPerceptualHash($uid);
53✔
215
            }
216

217
            if ($this->externalurlname) {
54✔
UNCOV
218
                $rc = $this->dbhm->preExec(
×
UNCOV
219
                    "INSERT INTO {$this->table} (`{$this->idatt}`, `{$this->uidname}`, `{$this->externalurlname}`, `{$this->modsname}`, `hash`) VALUES (?, ?, ?, ?, ?);",
×
UNCOV
220
                    [
×
UNCOV
221
                        $id,
×
UNCOV
222
                        $uid,
×
223
                        $url,
×
224
                        json_encode($mods),
×
225
                        $this->hash,
×
UNCOV
226
                    ]
×
UNCOV
227
                );
×
228
            } else {
229
                $rc = $this->dbhm->preExec(
54✔
230
                    "INSERT INTO {$this->table} (`{$this->idatt}`, `{$this->uidname}`, `{$this->modsname}`, `hash`) VALUES (?, ?, ?, ?);",
54✔
231
                    [
54✔
232
                        $id,
54✔
233
                        $uid,
54✔
234
                        json_encode($mods),
54✔
235
                        $this->hash,
54✔
236
                    ]
54✔
237
                );
54✔
238
            }
239

240
            $imgid = $rc ? $this->dbhm->lastInsertId() : null;
54✔
241

242
            if ($imgid) {
54✔
243
                $this->id = $imgid;
54✔
244
                $this->externaluid = $uid;
54✔
245
                $this->externalmods = $mods;
54✔
246
                $this->externalurl = $url;
54✔
247
            }
248

249
            return ([$imgid, $uid]);
54✔
250
        }
251

NEW
252
        return NULL;
×
253
    }
254

255
    public function getById($id) {
256
        $sql = "SELECT id FROM {$this->table} WHERE {$this->idatt} = ? AND ((data IS NOT NULL AND LENGTH(data) > 0) OR archived = 1 OR externaluid IS NOT NULL) ORDER BY id;";
34✔
257
        $atts = $this->dbhr->preQuery($sql, [$id]);
34✔
258
        $ret = [];
34✔
259
        foreach ($atts as $att) {
34✔
260
            $ret[] = new Attachment($this->dbhr, $this->dbhm, $att['id']);
6✔
261
        }
262

263
        return ($ret);
34✔
264
    }
265

266
    public function getByIds($ids) {
267
        $ret = [];
105✔
268

269
        if (count($ids)) {
105✔
270
            $sql = "SELECT id, {$this->idatt}, hash, archived, externaluid, externalmods FROM {$this->table} 
102✔
271
                       WHERE {$this->idatt} IN (" . implode(',', $ids) . ") 
102✔
272
                       AND ((data IS NOT NULL AND LENGTH(data) > 0) OR archived = 1 OR externaluid IS NOT NULL) 
273
                       ORDER BY `primary` DESC, id;";
102✔
274
            #error_log($sql);
275
            $atts = $this->dbhr->preQuery($sql);
102✔
276
            foreach ($atts as $att) {
102✔
277
                $ret[] = new Attachment($this->dbhr, $this->dbhm, $att['id'], $this->type, $att);
9✔
278
            }
279
        }
280

281
        return ($ret);
105✔
282
    }
283

284
    public function getByImageIds($ids) {
285
        $ret = [];
2✔
286
        if (count($ids)) {
2✔
287
            $sql = "SELECT id, {$this->idatt}, hash, archived FROM {$this->table} WHERE id IN (" . implode(
2✔
288
                    ',',
2✔
289
                    $ids
2✔
290
                ) . ") AND ((data IS NOT NULL AND LENGTH(data) > 0) OR archived = 1 OR externaluid IS NOT NULL) ORDER BY id;";
2✔
291
            $atts = $this->dbhr->preQuery($sql);
2✔
292
            foreach ($atts as $att) {
2✔
293
                $ret[] = new Attachment($this->dbhr, $this->dbhm, $att['id'], $this->type, $att);
1✔
294
            }
295
        }
296

297
        return ($ret);
2✔
298
    }
299

300
    public function scp($host, $data, $fn, &$failed) {
UNCOV
301
        $connection = @ssh2_connect($host, 22);
×
UNCOV
302
        $failed = true;
×
303

UNCOV
304
        if ($connection) {
×
UNCOV
305
            if (@ssh2_auth_pubkey_file(
×
UNCOV
306
                $connection,
×
UNCOV
307
                CDN_SSH_USER,
×
UNCOV
308
                CDN_SSH_PUBLIC_KEY,
×
UNCOV
309
                CDN_SSH_PRIVATE_KEY
×
UNCOV
310
            )) {
×
UNCOV
311
                $temp = tempnam(sys_get_temp_dir(), "img_archive_$fn");
×
UNCOV
312
                file_put_contents($temp, $data);
×
UNCOV
313
                $rem = "/var/www/iznik/images/$fn";
×
UNCOV
314
                $retry = 0;
×
315

316
                do {
UNCOV
317
                    $rc = ssh2_scp_send($connection, $temp, $rem, 0644);
×
318

UNCOV
319
                    if (!$rc) {
×
UNCOV
320
                        $msg = "SCP of $rem failed, retry $retry";
×
UNCOV
321
                        \Sentry\captureMessage($msg);
×
UNCOV
322
                        error_log($msg);
×
UNCOV
323
                        sleep(1);
×
324
                    }
325

UNCOV
326
                    $retry++;
×
UNCOV
327
                } while (!$rc && $retry < 5);
×
UNCOV
328
                $failed = !$rc;
×
UNCOV
329
                unlink($temp);
×
UNCOV
330
                error_log("scp $temp to $host $rem returned $rc failed? $failed");
×
331
            }
332

333
            # Exit gracefully - might help with file truncation.
UNCOV
334
            ssh2_exec($connection, 'exit');
×
335
        }
336
    }
337

338
    public function archive() {
339
        if ($this->externalurl || $this->externaluid) {
1✔
340
            // We don't archive external images.
341
            return;
1✔
342
        }
343

344
        # We archive out of the DB onto our two CDN image hosts.  This reduces load on the servers because we don't
345
        # have to serve the images up, and it also reduces the disk space we need within the DB (which is not an ideal
346
        # place to store large amounts of image data);
347
        #
348
        # If we fail then we leave it unchanged for next time.
349
        $data = $this->getData();
×
350
        $rc = true;
×
351

352
        if ($data) {
×
353
            $rc = false;
×
354

355
            try {
356
                $name = null;
×
357

358
                # Only these types are in archive_attachments.
359
                switch ($this->type) {
×
360
                    case Attachment::TYPE_MESSAGE:
UNCOV
361
                        $tname = 'timg';
×
362
                        $name = 'img';
×
UNCOV
363
                        break;
×
364
                    case Attachment::TYPE_CHAT_MESSAGE:
365
                        $tname = 'tmimg';
×
366
                        $name = 'mimg';
×
367
                        break;
×
368
                    case Attachment::TYPE_NEWSFEED:
UNCOV
369
                        $tname = 'tfimg';
×
UNCOV
370
                        $name = 'fimg';
×
371
                        break;
×
372
                    case Attachment::TYPE_COMMUNITY_EVENT:
373
                        $tname = 'tcimg';
×
374
                        $name = 'cimg';
×
375
                        break;
×
376
                    case Attachment::TYPE_NOTICEBOARD:
UNCOV
377
                        $tname = 'tbimg';
×
UNCOV
378
                        $name = 'bimg';
×
379
                        break;
×
380
                }
381

UNCOV
382
                if ($name) {
×
UNCOV
383
                    $failed = false;
×
384

UNCOV
385
                    foreach ([CDN_HOST_1, CDN_HOST_2] as $host) {
×
386
                        # Upload the thumbnail.  If this fails we'll leave it untouched.
UNCOV
387
                        $i = new Image($data);
×
UNCOV
388
                        if ($i->img) {
×
UNCOV
389
                            $i->scale(250, 250);
×
UNCOV
390
                            $thumbdata = $i->getData(100);
×
UNCOV
391
                            $this->scp($host, $thumbdata, "{$tname}_{$this->id}.jpg", $failed2);
×
UNCOV
392
                            $failed |= $failed2;
×
UNCOV
393
                            $this->scp($host, $data, "{$name}_{$this->id}.jpg", $failed3);
×
UNCOV
394
                            $failed |= $failed3;
×
395
                        } else {
UNCOV
396
                            error_log("...failed to create image {$this->id}");
×
397
                        }
398
                    }
399

UNCOV
400
                    $rc = !$failed;
×
401
                }
UNCOV
402
            } catch (\Exception $e) {
×
UNCOV
403
                error_log("Archive failed " . $e->getMessage());
×
404
            }
405
        }
406

UNCOV
407
        if ($rc) {
×
408
            # Remove from the DB.
UNCOV
409
            $sql = "UPDATE {$this->table} SET archived = 1, data = NULL WHERE id = {$this->id};";
×
UNCOV
410
            $this->archived = true;
×
UNCOV
411
            $this->dbhm->exec($sql);
×
412
        }
413

UNCOV
414
        return ($rc);
×
415
    }
416

417
    public function setData($data) {
UNCOV
418
        $this->dbhm->preExec("UPDATE {$this->table} SET archived = 0, data = ? WHERE id = ?;", [
×
UNCOV
419
            $data,
×
UNCOV
420
            $this->id
×
UNCOV
421
        ]);
×
422
    }
423

424
    public function fgc($url, $use_include_path, $ctx) {
425
        return @file_get_contents($url, $use_include_path, $ctx);
29✔
426
    }
427

428
    public function canRedirect() {
429
        if ($this->externaluid) {
31✔
430
            $u = new UploadCare();
30✔
431
            return $u->getUrl($this->externaluid, $this->externalmods);
30✔
432
        } else if ($this->externalurl) {
1✔
433
            return $this->externalurl;
1✔
434
        } else {
UNCOV
435
            if ($this->archived) {
×
436
                # Only these types are in archive_attachments.
UNCOV
437
                switch ($this->type) {
×
438
                    case Attachment::TYPE_MESSAGE:
UNCOV
439
                        $tname = 'timg';
×
UNCOV
440
                        $name = 'img';
×
441
                        break;
×
442
                    case Attachment::TYPE_CHAT_MESSAGE:
UNCOV
443
                        $tname = 'tmimg';
×
UNCOV
444
                        $name = 'mimg';
×
UNCOV
445
                        break;
×
446
                    case Attachment::TYPE_NEWSFEED:
447
                        $tname = 'tfimg';
×
448
                        $name = 'fimg';
×
UNCOV
449
                        break;
×
450
                    case Attachment::TYPE_COMMUNITY_EVENT:
UNCOV
451
                        $tname = 'tcimg';
×
UNCOV
452
                        $name = 'cimg';
×
UNCOV
453
                        break;
×
454
                    case Attachment::TYPE_NOTICEBOARD:
UNCOV
455
                        $tname = 'tbimg';
×
UNCOV
456
                        $name = 'bimg';
×
UNCOV
457
                        break;
×
458
                }
459

UNCOV
460
                return 'https://' . IMAGE_ARCHIVED_DOMAIN . "/{$name}_{$this->id}.jpg";
×
461
            }
462
        }
463

UNCOV
464
        return false;
×
465
    }
466

467
    public function getData() {
468
        $ret = null;
29✔
469

470
        $url = $this->canRedirect();
29✔
471

472
        if ($url) {
29✔
473
            # This attachment has been archived out of our database, to a CDN.  Normally we would expect
474
            # that we wouldn't come through here, because we'd serve up an image link directly to the CDN, but
475
            # there is a timing window where we could archive after we've served up a link, so we have
476
            # to handle it.
477
            #
478
            # We fetch the data - not using SSL as we don't need to, and that host might not have a cert.  And
479
            # we put it back in the DB, because we are probably going to fetch it again.
480
            #
481
            # Apply a short timeout to avoid hanging the server if Azure is down.
482
            $ctx = stream_context_create(
29✔
483
                array(
29✔
484
                    'http' =>
29✔
485
                        array(
29✔
486
                            'timeout' => 2,
29✔
487
                        )
29✔
488
                )
29✔
489
            );
29✔
490

491
            $ret = $this->fgc($url, false, $ctx);
29✔
492
        } else {
UNCOV
493
            $sql = "SELECT * FROM {$this->table} WHERE id = ?;";
×
UNCOV
494
            $datas = $this->dbhr->preQuery($sql, [$this->id]);
×
495

UNCOV
496
            foreach ($datas as $data) {
×
UNCOV
497
                $ret = $data['data'];
×
498
            }
499
        }
500

501
        return ($ret);
29✔
502
    }
503

504
    public function findWebReferences() {
505
        # Find a web page containing this imge, if any.
UNCOV
506
        $ret = null;
×
507

UNCOV
508
        if ($this->type == Attachment::TYPE_MESSAGE) {
×
UNCOV
509
            $data = $this->getData();
×
UNCOV
510
            $base64 = base64_encode($data);
×
511

UNCOV
512
            $r_json = '{
×
513
                "requests": [
514
                    {
515
                      "image": {
UNCOV
516
                        "content":"' . $base64 . '"
×
517
                      },
518
                      "features": [
519
                          {
520
                            "type": "WEB_DETECTION",
521
                            "maxResults": 1
522
                          }
523
                      ]
524
                    }
525
                ]
UNCOV
526
            }';
×
527

UNCOV
528
            $curl = curl_init();
×
UNCOV
529
            curl_setopt(
×
UNCOV
530
                $curl,
×
UNCOV
531
                CURLOPT_URL,
×
UNCOV
532
                'https://vision.googleapis.com/v1/images:annotate?key=' . GOOGLE_VISION_KEY
×
UNCOV
533
            );
×
UNCOV
534
            curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
×
UNCOV
535
            curl_setopt($curl, CURLOPT_HTTPHEADER, array("Content-type: application/json"));
×
UNCOV
536
            curl_setopt($curl, CURLOPT_POST, true);
×
UNCOV
537
            curl_setopt($curl, CURLOPT_POSTFIELDS, $r_json);
×
UNCOV
538
            $json_response = curl_exec($curl);
×
UNCOV
539
            $status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
×
540

UNCOV
541
            if ($status) {
×
UNCOV
542
                $rsp = json_decode($json_response, true);
×
543
                #error_log("Identified {$this->id} by Google $json_response for $r_json");
UNCOV
544
                error_log("Matching " . var_export($rsp, true));
×
545

UNCOV
546
                if ($rsp &&
×
UNCOV
547
                    array_key_exists('responses', $rsp) &&
×
UNCOV
548
                    count($rsp['responses']) > 0 &&
×
UNCOV
549
                    array_key_exists('webDetection', $rsp['responses'][0]) &&
×
UNCOV
550
                    array_key_exists('pagesWithMatchingImages', $rsp['responses'][0]['webDetection'])) {
×
UNCOV
551
                    $rsps = $rsp['responses'][0]['webDetection']['pagesWithMatchingImages'];
×
552

UNCOV
553
                    foreach ($rsps as $r) {
×
UNCOV
554
                        if (array_key_exists('fullMatchingImages', $r) && strpos($r['url'], USER_SITE) === false) {
×
UNCOV
555
                            $ret = $r['url'];
×
556
                        }
557
                    }
UNCOV
558
                    error_log(var_export($rsps, true));
×
559
                }
560
            }
561

UNCOV
562
            curl_close($curl);
×
563
        }
564

UNCOV
565
        return ($ret);
×
566
    }
567

568
    public function ocr($data = null, $returnfull = false, $video = false) {
569
        # Identify text in an attachment using Google Vision API.
UNCOV
570
        $base64 = $data ? $data : base64_encode($this->getData());
×
571

UNCOV
572
        if ($video) {
×
573
//            "videoContext": {
574
//                "textDetectionConfig": {
575
//                    "languageHints": ["en"]
576
//                }
577
//              }
UNCOV
578
            $r_json = '{
×
UNCOV
579
              "inputContent": "' . $base64 . '",
×
580
              "features": ["TEXT_DETECTION"],
UNCOV
581
            }';
×
582
        } else {
UNCOV
583
            $r_json = '{
×
584
                "requests": [
585
                    {
586
                      "image": {
UNCOV
587
                        "content":"' . $base64 . '",
×
588
                      },
589
                      "features": [
590
                          {
591
                            "type": "TEXT_DETECTION"
592
                          }
593
                      ],
594
                      "imageContext": {
595
                        "languageHints": [
596
                          "en"
597
                        ]
598
                      }
599
                    }
600
                ]
UNCOV
601
            }';
×
602
        }
603

UNCOV
604
        $url = 'https://vision.googleapis.com/v1/images:annotate?key=' . GOOGLE_VISION_KEY;
×
UNCOV
605
        $curl = curl_init();
×
UNCOV
606
        curl_setopt($curl, CURLOPT_URL, $url);
×
UNCOV
607
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
×
UNCOV
608
        curl_setopt($curl, CURLOPT_HTTPHEADER, array("Content-type: application/json"));
×
UNCOV
609
        curl_setopt($curl, CURLOPT_POST, true);
×
UNCOV
610
        curl_setopt($curl, CURLOPT_POSTFIELDS, $r_json);
×
611

UNCOV
612
        if ($video) {
×
UNCOV
613
            curl_setopt($curl, CURLOPT_HTTPHEADER, array("Authorization: Bearer " . GOOGLE_VIDEO_KEY));
×
614
        }
615

UNCOV
616
        $json_response = curl_exec($curl);
×
UNCOV
617
        $status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
×
618

UNCOV
619
        $text = '';
×
UNCOV
620
        $rsps = null;
×
621

UNCOV
622
        if ($status) {
×
UNCOV
623
            error_log("Rsp $json_response");
×
UNCOV
624
            $rsp = json_decode($json_response, true);
×
625

UNCOV
626
            if ($rsp && array_key_exists('responses', $rsp) && count($rsp['responses']) > 0 && array_key_exists(
×
UNCOV
627
                    'textAnnotations',
×
UNCOV
628
                    $rsp['responses'][0]
×
UNCOV
629
                )) {
×
UNCOV
630
                $rsps = $rsp['responses'][0]['textAnnotations'];
×
631

UNCOV
632
                foreach ($rsps as $rsp) {
×
UNCOV
633
                    $text .= $rsp['description'] . "\n";
×
UNCOV
634
                    break;
×
635
                }
636
            }
637
        }
638

639
        curl_close($curl);
×
640

UNCOV
641
        return ($returnfull ? $rsps : $text);
×
642
    }
643

644
    public function setPrivate($att, $val) {
645
        $this->dbhm->preExec("UPDATE {$this->table} SET `$att` = ? WHERE id = {$this->id};", [$val]);
14✔
646
    }
647

648
    public function delete() {
UNCOV
649
        $this->dbhm->preExec("DELETE FROM {$this->table} WHERE id = {$this->id};");
×
650
    }
651

652
    public function getIdAtt() {
UNCOV
653
        return $this->idatt;
×
654
    }
655

656
    function rotate($rotate) {
657
        // Ensure $rotate is not negative.
658
        if ($this->externaluid) {
2✔
659
            # We can rotate this by changing external mods.
660
            $mods = json_decode($this->externalmods, TRUE);
2✔
661
            $rotate = ($rotate + 360) % 360;
2✔
662
            $mods['rotate'] = $rotate;
2✔
663
            $this->setPrivate('externalmods', json_encode($mods));
2✔
664
        } else {
665
            $data = $this->getData();
×
666
            $i = new Image($data);
×
667
            $i->rotate($rotate);
×
668
            $newdata = $i->getData(100);
×
669
            $this->setData($newdata);
×
670
        }
671

672
        if ($this->type == Attachment::TYPE_MESSAGE) {
2✔
673
            # Only some kinds of attachments record whether they are rotated.
674
            $this->recordRotate();
2✔
675
        }
676
    }
677

678
    public function recordRotate() {
679
        $this->setPrivate('rotated', 1);
2✔
680
    }
681
}
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