Completed
Push — development ( 1b87d2...43bb99 )
by Thomas
06:02
created

htdocs/lib2/logic/cache.class.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/***************************************************************************
3
 * for license information see LICENSE.md
4
 *   get/set has to be committed with save
5
 *   add/remove etc. is executed instantly
6
 ***************************************************************************/
7
8
require_once __DIR__ . '/logtypes.inc.php';
9
10
class cache
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
11
{
12
    public $nCacheId = 0;
13
14
    public $reCache;
15
16
    /**
17
     * @param $wp
18
     * @return int|null
19
     */
20
    public static function cacheIdFromWP($wp)
21
    {
22
        if (mb_strtoupper(mb_substr($wp, 0, 2)) === 'GC') {
23
            $rs = sql("SELECT `cache_id` FROM `caches` WHERE `wp_gc_maintained`='&1'", $wp);
24
            if (sql_num_rows($rs) !== 1) {
25
                sql_free_result($rs);
26
27
                return null;
28
            }
29
            $r = sql_fetch_assoc($rs);
30
            sql_free_result($rs);
31
32
            $cacheId = $r['cache_id'];
33
        } else {
34
            $cacheId = sql_value("SELECT `cache_id` FROM `caches` WHERE `wp_oc`='&1'", 0, $wp);
35
        }
36
37
        return $cacheId;
38
    }
39
40
    /**
41
     * @param $wp
42
     * @return cache|null
43
     */
44
    public static function fromWP($wp)
45
    {
46
        $cacheId = self::cacheIdFromWP($wp);
47
        if ($cacheId === 0) {
48
            return null;
49
        }
50
51
        return new self($cacheId);
52
    }
53
54
    /**
55
     * @param $uuid
56
     * @return mixed
57
     */
58
    public static function cacheIdFromUUID($uuid)
59
    {
60
        $cacheId = sql_value("SELECT `cache_id` FROM `caches` WHERE `uuid`='&1'", 0, $uuid);
61
62
        return $cacheId;
63
    }
64
65
    /**
66
     * @param $uuid
67
     * @return cache|null
68
     */
69
    public static function fromUUID($uuid)
70
    {
71
        $cacheId = self::cacheIdFromUUID($uuid);
72
        if ($cacheId === 0) {
73
            return null;
74
        }
75
76
        return new self($cacheId);
77
    }
78
79
    public function __construct($nNewCacheId = ID_NEW)
80
    {
81
        $this->reCache = new rowEditor('caches');
82
        $this->reCache->addPKInt('cache_id', null, false, RE_INSERT_AUTOINCREMENT);
83
        $this->reCache->addString('uuid', '', false, RE_INSERT_AUTOUUID);
84
        $this->reCache->addInt('node', 0, false);
85
        $this->reCache->addDate('date_created', time(), true, RE_INSERT_IGNORE);
86
        $this->reCache->addDate('last_modified', time(), true, RE_INSERT_IGNORE);
87
        $this->reCache->addDate('listing_last_modified', time(), true, RE_INSERT_IGNORE);
88
        $this->reCache->addInt('user_id', 0, false);
89
        $this->reCache->addString('name', '', false);
90
        $this->reCache->addDouble('longitude', 0, false);
91
        $this->reCache->addDouble('latitude', 0, false);
92
        $this->reCache->addInt('type', 1, false);
93
        $this->reCache->addInt('status', 5, false);
94
        $this->reCache->addString('country', '', false);
95
        $this->reCache->addDate('date_hidden', time(), false);
96
        $this->reCache->addInt('size', 1, false);
97
        $this->reCache->addFloat('difficulty', 1, false);
98
        $this->reCache->addFloat('terrain', 1, false);
99
        $this->reCache->addString('logpw', '', false);
100
        $this->reCache->addFloat('search_time', 0, false);
101
        $this->reCache->addFloat('way_length', 0, false);
102
        $this->reCache->addString('wp_oc', null, true);
103
        $this->reCache->addString('wp_gc', '', false);
104
        $this->reCache->addString('wp_gc_maintained', '', false);
105
        $this->reCache->addString('wp_nc', '', false);
106
        $this->reCache->addString('desc_languages', '', false, RE_INSERT_IGNORE);
107
        $this->reCache->addString('default_desclang', '', false);
108
        $this->reCache->addDate('date_activate', null, true);
109
        $this->reCache->addInt('need_npa_recalc', 1, false, RE_INSERT_IGNORE);
110
        $this->reCache->addInt('show_cachelists', 1, false);
111
        $this->reCache->addInt('protect_old_coords', 0, false);
112
        $this->reCache->addInt('needs_maintenance', 0, false);
113
        $this->reCache->addInt('listing_outdated', 0, false);
114
        $this->reCache->addDate('flags_last_modified', '0000-00-00 00:00:00', false);
115
116
        $this->nCacheId = $nNewCacheId + 0;
117
118
        if ($nNewCacheId == ID_NEW) {
119
            $this->reCache->addNew(null);
120
        } else {
121
            $this->reCache->load($this->nCacheId);
122
        }
123
    }
124
125
    /**
126
     * @return bool
127
     */
128
    public function exist()
129
    {
130
        return $this->reCache->exist();
131
    }
132
133
    /**
134
     * @return int
135
     */
136
    public function getCacheId()
137
    {
138
        return $this->nCacheId;
139
    }
140
141
    public function getStatus()
142
    {
143
        return $this->reCache->getValue('status');
144
    }
145
146
    public function getType()
147
    {
148
        return $this->reCache->getValue('type');
149
    }
150
151
    public function getName()
152
    {
153
        return $this->reCache->getValue('name');
154
    }
155
156
    public function getLongitude()
157
    {
158
        return $this->reCache->getValue('longitude');
159
    }
160
161
    public function getLatitude()
162
    {
163
        return $this->reCache->getValue('latitude');
164
    }
165
166
    public function getUserId()
167
    {
168
        return $this->reCache->getValue('user_id');
169
    }
170
171
    public function getUsername()
172
    {
173
        return sql_value("SELECT `username` FROM `user` WHERE `user_id`='&1'", '', $this->getUserId());
174
    }
175
176
    public function getWPOC()
177
    {
178
        return $this->reCache->getValue('wp_oc');
179
    }
180
181
    public function getWPGC()
182
    {
183
        return $this->reCache->getValue('wp_gc');
184
    }
185
186
    public function getWPGC_maintained()
187
    {
188
        return $this->reCache->getValue('wp_gc_maintained');
189
    }
190
191
    public function getUUID()
192
    {
193
        return $this->reCache->getValue('uuid');
194
    }
195
196
    public function getDateCreated()
197
    {
198
        return $this->reCache->getValue('date_created');
199
    }
200
201
    public function getLastModified()
202
    {
203
        return $this->reCache->getValue('last_modified');
204
    }
205
206
    public function getListingLastModified()
207
    {
208
        return $this->reCache->getValue('listing_last_modified');
209
    }
210
211
    public function getNode()
212
    {
213
        return $this->reCache->getValue('node');
214
    }
215
216
    public function setNode($value)
217
    {
218
        return $this->reCache->setValue('node', $value);
219
    }
220
221
    public function setStatus($value)
222
    {
223
        global $login;
224
        if (sql_value("SELECT COUNT(*) FROM `cache_status` WHERE `id`='&1'", 0, $value) == 1) {
225
            sql("SET @STATUS_CHANGE_USER_ID='&1'", $login->userid);
226
227
            return $this->reCache->setValue('status', $value);
228
        }
229
230
        return false;
231
    }
232
233
    public function getDescLanguages()
234
    {
235
        return explode($this->reCache->getValue('desc_languages'), ',');
236
    }
237
238
    public function getDefaultDescLanguage()
239
    {
240
        return $this->reCache->getValue('default_desclang');
241
    }
242
243
    public function getProtectOldCoords()
244
    {
245
        return $this->reCache->getValue('protect_old_coords');
246
    }
247
248
    public function setProtectOldCoords($value)
249
    {
250
        return $this->reCache->setValue('protect_old_coords', $value);
251
    }
252
253
    // cache condition flags
254
    public function getNeedsMaintenance()
255
    {
256
        return $this->reCache->getValue('needs_maintenance');
257
    }
258
259
    public function setNeedsMaintenance($value)
260
    {
261
        return $this->reCache->setValue('needs_maintenance', $value);
262
    }
263
264
    public function getListingOutdated()
265
    {
266
        return $this->reCache->getValue('listing_outdated');
267
    }
268
269
    public function getListingOutdatedLogUrl()
270
    {
271
        $url = '';
272
        $rs = sql(
273
            "SELECT `id`, `listing_outdated`
274
             FROM `cache_logs`
275
             WHERE `cache_id`='&1'
276
             AND `listing_outdated`>0
277
             ORDER BY `order_date` DESC, `date_created` DESC, `id` DESC",
278
            // same sorting order as in DB function sp_update_logstat()
279
            $this->getCacheId()
280
        );
281
        if ($r = sql_fetch_assoc($rs)) {
282
            if ($r['listing_outdated'] == 2) {
283
                $url = 'viewlogs.php?cacheid=' . $this->getCacheId() . '#log' . $r['id'];
284
            }
285
        }
286
        sql_free_result($rs);
287
288
        return $url;
289
    }
290
291
    // test if af the time of insertion of an existing log the listing
292
    // was outdated and there is no newer listing-[not]-outdated-log
293
294
    public function getListingOutdatedRelativeToLog($logId)
295
    {
296
        $logDate = sql_value("SELECT `order_date` FROM `cache_logs` WHERE `id`='&1'", '', $logId);
297
298
        return
299
            sql_value(  // Is there a newer log with LO flag?
300
                "SELECT 1
301
                 FROM `cache_logs`
302
                 WHERE `cache_id`='&1'
303
                 AND `order_date`>'&2'
304
                 AND `listing_outdated`>0
305
                 LIMIT 1",
306
                0,
307
                $this->getCacheId(),
308
                $logDate
309
            ) == 0 && sql_value(  // Was the listing outdated before the log was posted?
310
                "SELECT `listing_outdated`
311
                 FROM `cache_logs`
312
                 WHERE `cache_id`='&1'
313
                 AND `listing_outdated`>0
314
                 AND `id`!='&2'
315
                 ORDER BY `order_date` DESC, `date_created` DESC, `id` DESC
316
                 LIMIT 1",
317
                0,
318
                $this->getCacheId(),
319
                $logId
320
            ) == 2;
321
    }
322
323
    public function setListingOutdated($value)
324
    {
325
        return $this->reCache->setValue('listing_outdated', $value);
326
    }
327
328
    public function getConditionHistory()
329
    {
330
        $cache_id = $this->nCacheId;
331
        $rs = sql(
332
            "SELECT `date`, `needs_maintenance`, `listing_outdated`
333
         FROM `cache_logs`
334
         WHERE `cache_id`='&1' AND (`needs_maintenance` IS NOT NULL OR `listing_outdated` IS NOT NULL)
335
         ORDER BY `date`, `id`",
336
            $cache_id
337
        );
338
        $nm = $lo = 0;
339
        $cond = [
340
            [
341
                'needs_maintenance' => $nm,
342
                'listing_outdated' => $lo,
343
                'date' => '0000-00-00 00:00:00',
344
            ],
345
        ];
346
        while ($r = sql_fetch_assoc($rs)) {
347
            if ($r['needs_maintenance'] != $nm || $r['listing_outdated'] != $lo) {
348
                $nm = $r['needs_maintenance'];
349
                $lo = $r['listing_outdated'];
350
                $cond[] = [
351
                    'needs_maintenance' => $nm,
352
                    'listing_outdated' => $lo,
353
                    'date' => $r['date'],
354
                ];
355
            }
356
        }
357
        sql_free_result($rs);
358
        $cond[] = [
359
            'needs_maintenance' => $nm,
360
            'listing_outdated' => $lo,
361
            'date' => '9999-12-31 23:59:59',
362
        ];
363
364
        return $cond;
365
    }
366
367
    /**
368
     * @param int $attr_id
369
     * @return bool
370
     */
371
    public function hasAttribute($attr_id)
372
    {
373
        return sql_value(
374
            "SELECT 1
375
             FROM `caches_attributes` `ca`
376
             WHERE `ca`.`cache_id`='&1'
377
             AND `attrib_id`='&2'",
378
            0,
379
            $this->nCacheId,
380
            $attr_id
381
        ) == 1;
382
    }
383
384
    // other
385
    public function getAnyChanged()
386
    {
387
        return $this->reCache->getAnyChanged();
388
    }
389
390
    // return if successful (with insert)
391
    public function save()
392
    {
393
        if ($this->reCache->save()) {
394
            sql_slave_exclude();
395
396
            return true;
397
        }
398
399
        return false;
400
    }
401
402
    public function requireLogPW()
403
    {
404
        return $this->reCache->getValue('logpw') != '';
405
    }
406
407
    // TODO: use prepared one way hash
408
    public function validateLogPW($nLogType, $sLogPW)
409
    {
410
        if (!$this->requireLogPW()) {
411
            return true;
412
        }
413
414
        if (sql_value("SELECT `require_password` FROM `log_types` WHERE `id`='&1'", 0, $nLogType) == 0) {
415
            return true;
416
        }
417
418
        return ($sLogPW == $this->reCache->getValue('logpw'));
419
    }
420
421
    /**
422
     * @param $nVisitUserId
423
     * @param $sRemoteAddr
424
     * @param $nCacheId
425
     */
426
    public static function visitCounter($nVisitUserId, $sRemoteAddr, $nCacheId)
427
    {
428
        global $opt, $_SERVER;
429
430
        // delete cache_visits older 1 day 60*60*24 = 86400
431
        sql(
432
            "DELETE FROM `cache_visits`
433
             WHERE `cache_id`='&1'
434
             AND `user_id_ip`!='0'
435
             AND NOW()-`last_modified`>86400",
436
            $nCacheId
437
        );
438
439
        if ($nVisitUserId == 0) {
440
            $se = explode(';', $opt['logic']['search_engines']);
441
            $ua = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
442
            foreach ($se as $s) {
443
                if (strpos($ua, $s) !== false) {
444
                    return;
445
                }
446
            }   // do not count search engine views
447
448
            $sIdentifier = $sRemoteAddr;
449
        } else {
450
            $sIdentifier = $nVisitUserId;
451
        }
452
453
        // note the visit of this user
454
        sql(
455
            "INSERT INTO `cache_visits` (`cache_id`, `user_id_ip`, `count`)
456
             VALUES (&1, '&2', 1)
457
             ON DUPLICATE KEY UPDATE `count`=`count`+1",
458
            $nCacheId,
459
            $sIdentifier
460
        );
461
462
        // if the previous statement does an INSERT, it was the first visit for this user today
463
        if (sql_affected_rows() == 1) {
464
            if ($nVisitUserId != sql_value("SELECT `user_id` FROM `caches` WHERE `cache_id`='&1'", 0, $nCacheId)) {
465
                // increment the counter for this cache
466
                sql(
467
                    "INSERT INTO `cache_visits` (`cache_id`, `user_id_ip`, `count`)
468
                     VALUES (&1, '0', 1)
469
                     ON DUPLICATE KEY UPDATE `count`=`count`+1",
470
                    $nCacheId
471
                );
472
            }
473
        }
474
    }
475
476
    /**
477
     * @param $cacheId
478
     * @return array
479
     */
480
    public static function getLogsCount($cacheId)
481
    {
482
        //prepare the logs
483
        $rsLogs = sql("SELECT COUNT(*) FROM `cache_logs` WHERE `cache_id`='&1'", $cacheId);
484
        $rLog = sql_fetch_assoc($rsLogs);
485
        sql_free_result($rsLogs);
486
487
        return $rLog;
488
    }
489
490
    /**
491
     * @param int $start
492
     * @param int $count
493
     * @param bool $deleted
494
     * @param bool $protect_old_coords
495
     * @return array
496
     */
497
    public function getLogsArray($start, $count, $deleted = false, $protect_old_coords = false)
498
    {
499
        global $login, $translate;
500
501
        // negative or abornally high numbers like 1.0E+15 can crash the LIMIT statement
502
        if ($count <= 0 || $count > 10000) {
503
            return [];
504
        }
505
506
        $coords = [];
507
        if ($this->getType() != 7 && $this->getType() != 8 &&  // quiz cache
508
            $this->hasAttribute(61) != true &&                 // safari cache
509
            $this->getStatus() != 5                            // unpublished cache
510
        ) {
511
            $rsCoords = sql(
512
                "SELECT
513
                    `date_created` `date`,
514
                    `date_created` - INTERVAL 4 SECOND AS `adjusted_date_created`,
515
                     /*
516
                        The first cache_coordinates entry is created immediately after
517
                        inserting the cache into caches table. This usually takes
518
                        a few milliseconds. We apply a 4 seconds 'safety margin' for
519
                        detecting the original coords. In the extremly unlikely case that
520
                        it took more than 4 seconds AND the owner did a coordinate change
521
                        before publish AND someone posts a log entry with date inbetween
522
                        the coord change and the publish, the coords before the change
523
                        will be exposed (which would be unpleasant but not critical).
524
525
                        Increasing the 4 seconds lag would increase the chance that
526
                        original coords which were set immediately before publish are
527
                        hidden (which would be unpleasant but not critical).
528
529
                        4 seconds was chosen as estimated shortest interval someone will
530
                        need to change coordinates of an unpublished cache AND publish
531
                        it, minus 1 second.
532
                     */
533
                    `latitude`,
534
                    `longitude`
535
                 FROM `cache_coordinates`
536
                 WHERE `cache_id`='&1'
537
                 ORDER BY `date_created` DESC",
538
                $this->nCacheId
539
            );
540
            $publish_date = $this->getDateCreated();
541
            while ($rCoord = sql_fetch_assoc($rsCoords)) {
542
                $coords[] = $rCoord;
543
                if (strtotime($rCoord['adjusted_date_created']) <= $publish_date) {
544
                    // This are the cache coords at publish time, which we will
545
                    // show as "original coords". Older coordinate changes
546
                    // (before publish) are discarded.
547
                    break;
548
                }
549
            }
550
            sql_free_result($rsCoords);
551
        }
552
553
        if ($deleted && ($login->admin && ADMIN_USER) > 0) {
554
            // admins may view owner-deleted logs
555
            $table = 'cache_logs_archived';
556
            $delfields = 'IFNULL(`u2`.`username`,"") AS `deleted_by_name`, `deletion_date`, "1" AS `deleted`';
557
            $addjoin = 'LEFT JOIN `user` `u2` ON `u2`.`user_id`=`cache_logs`.`deleted_by`';
558
        } else {
559
            $table = 'cache_logs';
560
            $delfields = '"" AS `deleted_by_name`, NULL AS `deletion_date`, "0" AS `deleted`';
561
            $addjoin = '';
562
        }
563
564
        $rsLogs = sql(
565
            "SELECT `cache_logs`.`user_id` AS `userid`,
566
                    `cache_logs`.`id` AS `id`,
567
                    `cache_logs`.`uuid` AS `uuid`,
568
                    `cache_logs`.`date` AS `date`,
569
                    `cache_logs`.`order_date` AS `order_date`,
570
                    `cache_logs`.`entry_last_modified`,
571
                    (`cache_logs`.`node` != 1 OR `cache_logs`.`entry_last_modified` != '2017-02-14 22:49:20')  /* see redmine #1109 */
572
                    AND DATEDIFF(`cache_logs`.`entry_last_modified`, `cache_logs`.`date_created`) >= 1 AS `late_modified`,
573
                    substr(`cache_logs`.`date`,12) AS `time`,  /* 00:00:01 = 00:00 logged, 00:00:00 = no time */
574
                    `cache_logs`.`type` AS `type`,
575
                    `cache_logs`.`oc_team_comment` AS `oc_team_comment`,
576
                    `cache_logs`.`needs_maintenance` AS `needs_maintenance`,
577
                    `cache_logs`.`listing_outdated` AS `listing_outdated`,
578
                    `cache_logs`.`text` AS `text`,
579
                    `cache_logs`.`text_html` AS `texthtml`,
580
                    `cache_logs`.`picture`,
581
                    " . $delfields . ",
582
                    `user`.`username` AS `username`,
583
                    IF(ISNULL(`cache_rating`.`cache_id`), 0, `cache_logs`.`type` IN (1,7)) AS `recommended`,
584
                    FALSE AS `cache_moved`
585
             FROM $table AS `cache_logs`
586
             INNER JOIN `user`
587
                 ON `user`.`user_id` = `cache_logs`.`user_id`
588
             LEFT JOIN `cache_rating`
589
                 ON `cache_logs`.`cache_id`=`cache_rating`.`cache_id`
590
                 AND `cache_logs`.`user_id`=`cache_rating`.`user_id`
591
                 AND `cache_logs`.`date`=`cache_rating`.`rating_date`
592
             " . $addjoin . "
593
             WHERE `cache_logs`.`cache_id`='&1'
594
             ORDER BY `cache_logs`.`order_date` DESC, `cache_logs`.`date_created` DESC, `id` DESC
595
             LIMIT &2, &3",
596
            $this->nCacheId,
597
            $start + 0,
598
            $count + 0
599
        );
600
601
        $logs = [];
602
        $coord_changes = (count($coords) > 1);
603
        if ($coord_changes) {
604
            $coordpos = 0;
605
            $current_coord = new coordinate($coords[0]['latitude'], $coords[0]['longitude']);
606
        }
607
        $displayed_coord_changes = 0;
608
609
        while ($rLog = sql_fetch_assoc($rsLogs)) {
610
            $pictures = [];
611
            $rsPictures = sql(
612
                "SELECT `url`, `title`, `uuid`, `id`, `spoiler`
613
                 FROM `pictures`
614
                 WHERE `object_id`='&1'
615
                 AND `object_type`=1
616
                 ORDER BY `seq`",
617
                $rLog['id']
618
            );
619
            while ($rPicture = sql_fetch_assoc($rsPictures)) {
620
                if (trim($rPicture['title']) == '') {
621
                    $rPicture['title'] = $translate->t('Picture', '', '', 0) . ' ' . (count($pictures) + 1);
622
                }
623
                $rPicture['url'] = use_current_protocol($rPicture['url']);
624
                $pictures[] = $rPicture;
625
            }
626
            sql_free_result($rsPictures);
627
            $rLog['pictures'] = $pictures;
628
            $rLog['text'] = use_current_protocol_in_html($rLog['text']);
629
630
            if ($coord_changes) {
631
                $newcoord = false;
632
                while ($coordpos < count($coords) && $coords[$coordpos]['date'] > $rLog['order_date']) {
633
                    if (!$newcoord) {
634
                        $newcoord = new coordinate($coords[$coordpos]['latitude'], $coords[$coordpos]['longitude']);
635
                    }
636
                    ++$coordpos;
637
                }
638
                if ($newcoord) {
639
                    if ($coordpos < count($coords)) {
640
                        $distance = geomath::calcDistance(
641
                            $newcoord->nLat,
642
                            $newcoord->nLon,
643
                            $coords[$coordpos]['latitude'],
644
                            $coords[$coordpos]['longitude']
645
                        );
646
                        if (abs($distance) > 0.005) {
647
                            $rLog['newcoord'] = $newcoord->getDecimalMinutes($protect_old_coords && $new != $current_coord);
648
                            if ($protect_old_coords) {
649
                                $rLog['movedbykm'] = false;
650
                            } elseif ($distance <= 1) {
651
                                $rLog['movedbym'] = floor($distance * 1000);
652
                            } elseif ($distance < 10) {
653
                                $rLog['movedbykm'] = sprintf('%1.1f', $distance);
654
                            } else {
655
                                $rLog['movedbykm'] = round($distance);
656
                            }
657
                            $rLog['cache_moved'] = true;
658
                            ++$displayed_coord_changes;
659
                        }
660
                    } else {
661
                        // This is the original coord of the cache.
662
                        $rLog['newcoord'] = $newcoord->getDecimalMinutes($protect_old_coords);
663
                        $rLog['cache_moved'] = false;
664
                    }
665
                }
666
            }
667
668
            $logs[] = $rLog;
669
        }
670
        sql_free_result($rsLogs);
671
672
        // Append a dummy log entry for the original coordinate, if it was
673
        // not added to a a real log entry because there are logs older than the
674
        // OC cache listing (cmp. https://redmine.opencaching.de/issues/1102):
675
676
        if ($displayed_coord_changes > 0 && $coordpos < count($coords) && count($logs) > 0) {
677
            $coord = new coordinate($coords[$coordpos]['latitude'], $coords[$coordpos]['longitude']);
678
            $logs[] = [
679
                'newcoord' => $coord->getDecimalMinutes($protect_old_coords),
680
                'cache_moved' => false,
681
                'type' => false,
682
            ];
683
        }
684
685
        return $logs;
686
    }
687
688
    /**
689
     * @param $userId
690
     * @param $reportReason
691
     * @param $reportNote
692
     * @return bool
693
     */
694
    public function report($userId, $reportReason, $reportNote)
695
    {
696
        sql(
697
            "INSERT INTO cache_reports (`cacheid`, `userid`, `reason`, `note`)
698
             VALUES(&1, &2, &3, '&4')",
699
            $this->nCacheId,
700
            $userId,
701
            $reportReason,
702
            $reportNote
703
        );
704
705
        return true;
706
    }
707
708
    /**
709
     * @param $userId
710
     * @return bool|string
711
     */
712
    public function addAdoption($userId)
713
    {
714
        if ($this->allowEdit() == false) {
715
            return 'noaccess';
716
        }
717
718
        if (sql_value("SELECT COUNT(*) FROM `user` WHERE `user_id`='&1'", 0, $userId) == 0) {
719
            return 'userunknown';
720
        }
721
722
        if (sql_value("SELECT COUNT(*) FROM `user` WHERE `user_id`='&1' AND `is_active_flag`=1", 0, $userId) == 0) {
723
            return 'userdisabled';
724
        }
725
726
        // same user?
727
        if ($this->getUserId() == $userId) {
728
            return 'self';
729
        }
730
731
        sql(
732
            "INSERT IGNORE INTO `cache_adoption` (`cache_id`, `user_id`)
733
             VALUES ('&1', '&2')",
734
            $this->nCacheId,
735
            $userId
736
        );
737
738
        return true;
739
    }
740
741
    /**
742
     * @param int $userId
743
     * @return bool
744
     */
745
    public function cancelAdoption($userId)
746
    {
747
        global $login;
748
749
        if ($this->allowEdit() == false && $login->userid != $userId) {
750
            return false;
751
        }
752
753
        sql(
754
            "DELETE FROM `cache_adoption`
755
             WHERE `user_id`='&1'
756
             AND `cache_id`='&2'",
757
            $userId,
758
            $this->nCacheId
759
        );
760
761
        return true;
762
    }
763
764
    /**
765
     * @param int $userId
766
     * @return bool
767
     */
768
    public function commitAdoption($userId)
769
    {
770
        global $login;
771
772
        // cache_adoption exists?
773
        if (sql_value(
774
                "SELECT COUNT(*) FROM `cache_adoption` WHERE `cache_id`='&1' AND `user_id`='&2'",
775
                0,
776
                $this->nCacheId,
777
                $userId
778
            ) == 0
779
        ) {
780
            return false;
781
        }
782
783
        // new user active?
784
        if (sql_value("SELECT `is_active_flag` FROM `user` WHERE `user_id`='&1'", 0, $userId) != 1) {
785
            return false;
786
        }
787
788
        sql(
789
            "INSERT INTO `logentries` (`module`, `eventid`, `userid`, `objectid1`, `objectid2`, `logtext`)
790
             VALUES ('cache', 5, '&1', '&2', '&3', '&4')",
791
            $login->userid,
792
            $this->nCacheId,
793
            0,
794
            'Cache ' . sql_escape($this->nCacheId) . ' has changed the owner from userid ' . sql_escape(
795
                $this->getUserId()
796
            ) . ' to ' . sql_escape($userId) . ' by ' . sql_escape($login->userid)
797
        );
798
        // Adoptions now are recorded by trigger in cache_adoptions table.
799
        // Recording adoptions in 'logentries' may be discarded after ensuring that the
800
        // log entries are not used anywhere.
801
        sql("UPDATE `caches` SET `user_id`='&1' WHERE `cache_id`='&2'", $userId, $this->nCacheId);
802
        sql("DELETE FROM `cache_adoption` WHERE `cache_id`='&1'", $this->nCacheId);
803
804
        $this->reCache->setValue('user_id', $userId);
805
806
        return true;
807
    }
808
809
    // checks if $userId has adopted this cache
810
    public function hasAdopted($userId)
811
    {
812
        // cache_adoption exists?
813
        return sql_value(
814
            "SELECT COUNT(*)
815
             FROM `cache_adoption`
816
             WHERE `cache_id`='&1'
817
             AND `user_id`='&2'",
818
            0,
819
            $this->nCacheId,
820
            $userId
821
        ) != 0;
822
    }
823
824
    // true if anyone can view the cache
825
    public function isPublic()
826
    {
827
        return (sql_value("SELECT `allow_user_view` FROM `cache_status` WHERE `id`='&1'", 0, $this->getStatus()) == 1);
828
    }
829
830
    public function allowView()
831
    {
832
        global $login;
833
834
        if ($this->isPublic()) {
835
            return true;
836
        }
837
838
        $login->verify();
839
840
        if (($login->admin & ADMIN_USER) == ADMIN_USER) {
841
            return true;
842
        } elseif ($this->getUserId() == $login->userid) {
843
            return true;
844
        }
845
846
        return false;
847
    }
848
849 View Code Duplication
    public function allowEdit()
850
    {
851
        global $login;
852
853
        $login->verify();
854
        if ($this->getUserId() == $login->userid) {
855
            return true;
856
        }
857
858
        return false;
859
    }
860
861
    public function allowLog()
862
    {
863
        global $login;
864
865
        $login->verify();
866
        if ($this->getUserId() == $login->userid || $login->hasAdminPriv(ADMIN_USER)) {
867
            return true;
868
        }
869
870
        return (sql_value("SELECT `allow_user_log` FROM `cache_status` WHERE `id`='&1'", 0, $this->getStatus()) == 1);
871
    }
872
873
    public function isRecommendedByUser($nUserId)
874
    {
875
        return sql_value(
876
            "SELECT COUNT(*) FROM `cache_rating` WHERE `cache_id`='&1' AND `user_id`='&2'",
877
            0,
878
            $this->nCacheId,
879
            $nUserId
880
        ) > 0;
881
    }
882
883
    public function addRecommendation($nUserId, $logdate)
884
    {
885
        // rating_date will be set to NOW() by Insert-trigger
886
        sql(
887
            "INSERT IGNORE INTO `cache_rating` (`cache_id`, `user_id`, `rating_date`) VALUES ('&1', '&2', '&3')",
888
            $this->nCacheId,
889
            $nUserId,
890
            $logdate
891
        );
892
    }
893
894
    public function removeRecommendation($nUserId)
895
    {
896
        sql("DELETE FROM `cache_rating` WHERE `cache_id`='&1' AND `user_id`='&2'", $this->nCacheId, $nUserId);
897
    }
898
899
    // retrieves admin cache history data and stores it to template variables
900
    // for display by adminhistory.tpl and adminreports.tpl
901
    public function setTplHistoryData($exclude_report_id)
902
    {
903
        global $opt, $tpl;
904
905
        // (other) reports for this cache
906
        $rs = sql(
907
            "SELECT `cr`.`id`,
908
                    `cr`.`date_created`,
909
                    `cr`.`lastmodified`,
910
                    `cr`.`userid`,
911
                    `cr`.`adminid`,
912
                    `users`.`username` AS `usernick`,
913
                    `admins`.`username` AS `adminnick`,
914
                    IFNULL(`tt`.`text`, `crs`.`name`) AS `status`,
915
                    IFNULL(`tt2`.`text`, `crr`.`name`) AS `reason`
916
             FROM `cache_reports` AS `cr`
917
             LEFT JOIN `cache_report_reasons` AS `crr`
918
                 ON `cr`.`reason`=`crr`.`id`
919
             LEFT JOIN `user` AS `users`
920
                 ON `users`.`user_id`=`cr`.`userid`
921
             LEFT JOIN `user` AS `admins`
922
                 ON `admins`.`user_id`=`cr`.`adminid`
923
             LEFT JOIN `cache_report_status` AS `crs`
924
                 ON `cr`.`status`=`crs`.`id`
925
             LEFT JOIN `sys_trans_text` AS `tt`
926
                 ON `crs`.`trans_id`=`tt`.`trans_id`
927
                 AND `tt`.`lang`='&2'
928
             LEFT JOIN `sys_trans_text` AS `tt2`
929
                 ON `crr`.`trans_id`=`tt2`.`trans_id`
930
                 AND `tt2`.`lang`='&2'
931
             WHERE `cr`.`cacheid`='&1'
932
                 AND `cr`.`id`<>'&3'
933
             ORDER BY `cr`.`status`,`cr`.`id` DESC",
934
            $this->getCacheId(),
935
            $opt['template']['locale'],
936
            $exclude_report_id
937
        );
938
        $tpl->assign_rs('reports', $rs);
939
        sql_free_result($rs);
940
941
        // user; deleted logs
942
        $rs = sql("SELECT * FROM `caches` WHERE `cache_id`='&1'", $this->getCacheId());
943
        $rCache = sql_fetch_array($rs);
944
        $tpl->assign('cache', $rCache);
945
        sql_free_result($rs);
946
        $tpl->assign(
947
            'ownername',
948
            sql_value("SELECT `username` FROM `user` WHERE `user_id`='&1'", '', $rCache['user_id'])
949
        );
950
951
        $tpl->assign('deleted_logs', $this->getLogsArray(0, 1000, true));
952
953
        // status changes
954
        $rs = sql(
955
            "SELECT `csm`.`date_modified`,
956
                    `csm`.`old_state` AS `old_status_id`,
957
                    `csm`.`new_state` AS `new_status_id`,
958
                    `user`.`username`,
959
                    `user`.`user_id`,
960
                    IFNULL(`stt_old`.`text`,`cs_old`.`name`) AS `old_status`,
961
                    IFNULL(`stt_new`.`text`,`cs_new`.`name`) AS `new_status`
962
            FROM `cache_status_modified` `csm`
963
            LEFT JOIN `cache_status` `cs_old`
964
                ON `cs_old`.`id`=`csm`.`old_state`
965
            LEFT JOIN `sys_trans_text` `stt_old`
966
                ON `stt_old`.`trans_id`=`cs_old`.`trans_id`
967
                AND `stt_old`.`lang`='&2'
968
            LEFT JOIN `cache_status` `cs_new`
969
                ON `cs_new`.`id`=`csm`.`new_state`
970
            LEFT JOIN `sys_trans_text` `stt_new`
971
                ON `stt_new`.`trans_id`=`cs_new`.`trans_id`
972
                AND `stt_new`.`lang`='&2'
973
            LEFT JOIN `user`
974
                ON `user`.`user_id`=`csm`.`user_id`
975
            WHERE `cache_id`='&1'
976
            ORDER BY `date_modified` DESC",
977
            $this->getCacheId(),
978
            $opt['template']['locale']
979
        );
980
        $tpl->assign_rs('status_changes', $rs);
981
        sql_free_result($rs);
982
983
        // coordinate changes
984
        $rs = sql(
985
            "SELECT `cc`.`date_created`, `cc`.`longitude`, `cc`.`latitude`,
986
                          IFNULL(`admin`.`user_id`, `owner`.`user_id`) AS `user_id`,
987
                          IFNULL(`admin`.`username`, `owner`.`username`) AS `username`
988
                     FROM `cache_coordinates` `cc`
989
                LEFT JOIN `caches` ON `caches`.`cache_id`=`cc`.`cache_id`
990
                LEFT JOIN `user` `owner` ON `owner`.`user_id`=`caches`.`user_id`
991
                LEFT JOIN `user` `admin` ON `admin`.`user_id`=`cc`.`restored_by`
992
                    WHERE `cc`.`cache_id`='&1'
993
                 ORDER BY `cc`.`date_created` DESC",
994
            $this->getCacheId()
995
        );
996
        $coords = [];
997
        while ($rCoord = sql_fetch_assoc($rs)) {
998
            $coord = new coordinate($rCoord['latitude'], $rCoord['longitude']);
999
            $coords[] = [
1000
                'date' => $rCoord['date_created'],
1001
                'coord' => $coord->getDecimalMinutes(),
1002
                'user_id' => $rCoord['user_id'],
1003
                'username' => $rCoord['username'],
1004
            ];
1005
        }
1006
        sql_free_result($rs);
1007
        $tpl->assign('coordinates', $coords);
1008
1009
        // Adoptions
1010
        $rs = sql(
1011
            "SELECT `cache_adoptions`.`date`,
1012
                          `cache_adoptions`.`from_user_id`,
1013
                          `cache_adoptions`.`to_user_id`,
1014
                          `from_user`.`username` AS `from_username`,
1015
                          `to_user`.`username` AS `to_username`
1016
                     FROM `cache_adoptions`
1017
                LEFT JOIN `user` `from_user` ON `from_user`.`user_id`=`from_user_id`
1018
                LEFT JOIN `user` `to_user` ON `to_user`.`user_id`=`to_user_id`
1019
                    WHERE `cache_id`='&1'
1020
                 ORDER BY `cache_adoptions`.`date`, `cache_adoptions`.`id`",
1021
            $this->getCacheId()
1022
        );
1023
        $tpl->assign_rs('adoptions', $rs);
1024
        sql_free_result($rs);
1025
    }
1026
1027
    public function logTypeAllowed($logType, $oldLogType = 0)
1028
    {
1029
        // check if given logType is valid for this cache type
1030
        return logtype_ok($this->getCacheId(), $logType, $oldLogType);
1031
    }
1032
1033
    /**
1034
     * @param int $logId
1035
     * @param int $oldLogType
1036
     * @param int $newLogType
1037
     */
1038
    public function updateCacheStatusFromLatestLog($logId, $oldLogType, $newLogType)
1039
    {
1040
        if ($newLogType != $oldLogType) {
1041
            // get new cache-status property of the new log type
1042
            $cacheStatus = sql_value(
1043
                "SELECT `cache_status` FROM `log_types` WHERE `id`='&1'",
1044
                0,
1045
                $newLogType
1046
            );
1047
1048
            if ($cacheStatus != 0) {
1049
                // If the new log type is a status-changing type, it's easy -
1050
                // we can directly change the cache status:
1051
1052
                $this->setStatus($cacheStatus);
1053
            } else {
1054
                // If the new log type is not status-changing, but the old type was, the
1055
                // cache status may need to be recalculated. To keep things simple, we will
1056
                // always recalculate the cache status in this case.
1057
1058
                $cacheStatus = sql_value(
1059
                    "SELECT `cache_status` FROM `log_types` WHERE `id`='&1'",
1060
                    0,
1061
                    $oldLogType
1062
                );
1063
                if ($cacheStatus != 0) {
1064
                    // The only safe way to restore an old cache status is from table
1065
                    // cache_status_modified. This is available since April 2013. As we
1066
                    // do not allow status-changes by editing logs that are older than
1067
                    // half a year, this is ok.
1068
1069
                    // The cache status is updated in a separate operation immediatly
1070
                    // (usually within a millisecond or so) after saving a log, so we must
1071
                    // apply some time lag to be on the safe side. It should be safe to
1072
                    // assume that users will not edit the log type twice within <= 2 seconds:
1073
1074
                    $logCreated = sql_value(
1075
                        "SELECT `date_created` FROM `cache_logs` WHERE `id`='&1'",
1076
                        null,
1077
                        $logId
1078
                    );
1079
                    $oldCacheStatus = sql_value(
1080
                        "SELECT `old_state`
1081
                         FROM `cache_status_modified`
1082
                         WHERE `cache_id`='&1'
1083
                         AND '&2' <= `date_modified`
1084
                         AND TIMESTAMPDIFF(SECOND, '&2', `date_modified`) <= 2
1085
                         ORDER BY `date_modified` DESC
1086
                         LIMIT 1",
1087
                        0,
1088
                        $this->getCacheId(),
1089
                        $logCreated
1090
                    );
1091
1092
                    if ($oldCacheStatus > 0) {
1093
                        $this->setStatus($oldCacheStatus);
1094
                    }
1095
                }
1096
            }
1097
        }
1098
    }
1099
1100
    // $userLogType:
1101
    //   Logtype selected by the user, or null if not applicable
1102
1103
    public function getUserLogTypes($userLogType, $oldLogType = 0, $statusLogs = true)
1104
    {
1105
        $logTypes = [];
1106
1107
        $logtypeNames = get_logtype_names();
1108
        $allowedLogtypes = get_cache_log_types($this->getCacheId(), $oldLogType, $statusLogs);
1109
        $defaultLogType = $userLogType;
1110
        if (!logtype_ok($this->getCacheId(), $defaultLogType, $oldLogType, $statusLogs)) {
1111
            $defaultLogType = $allowedLogtypes[0];
1112
        }
1113
1114
        // prepare array
1115
        $i = 0;
1116
        foreach ($allowedLogtypes as $logtype) {
1117
            $logTypes[$i]['selected'] = ($logtype == $defaultLogType) ? true : false;
1118
            $logTypes[$i]['name'] = $logtypeNames[$logtype];
1119
            $logTypes[$i]['id'] = $logtype;
1120
            $i++;
1121
        }
1122
1123
        // return
1124
        return $logTypes;
1125
    }
1126
1127
    public function teamcommentAllowed($logType, $oldTeamComment = false)
1128
    {
1129
        // checks if teamcomment is allowed
1130
        return teamcomment_allowed($this->getCacheId(), $logType, $oldTeamComment);
1131
    }
1132
1133
    public function statusUserLogAllowed()
1134
    {
1135
        return sql_value(
1136
            "SELECT `cache_status`.`allow_user_log`
1137
             FROM `cache_status`,`caches`
1138
             WHERE `caches`.`status`=`cache_status`.`id`
1139
             AND `caches`.`cache_id`='&1'",
1140
            0,
1141
            $this->getCacheId()
1142
        ) == 1;
1143
    }
1144
1145
    /**
1146
     * @param $logId
1147
     * @return bool
1148
     */
1149
    public function statusChangeAllowedForLog($logId)
1150
    {
1151
        $latestLogId = sql_value(
1152
            "SELECT `id` FROM `cache_logs`
1153
             WHERE `cache_id`='&1' AND DATEDIFF(NOW(), `date_created`) < 180
1154
             ORDER BY `order_date` DESC, `date_created` DESC, `id` DESC
1155
             LIMIT 1",
1156
            0,
1157
            $this->nCacheId
1158
        );
1159
1160
        return ($logId == $latestLogId);
1161
    }
1162
}
1163