Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
9 / 9
CRAP
100.00% covered (success)
100.00%
42 / 42
GSActor
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
9 / 9
40
100.00% covered (success)
100.00%
42 / 42
 setId
n/a
0 / 0
1
n/a
0 / 0
 getId
n/a
0 / 0
1
n/a
0 / 0
 setNickname
n/a
0 / 0
1
n/a
0 / 0
 getNickname
n/a
0 / 0
1
n/a
0 / 0
 setNormalizedNickname
n/a
0 / 0
1
n/a
0 / 0
 getNormalizedNickname
n/a
0 / 0
1
n/a
0 / 0
 setFullname
n/a
0 / 0
1
n/a
0 / 0
 getFullname
n/a
0 / 0
1
n/a
0 / 0
 setRoles
n/a
0 / 0
1
n/a
0 / 0
 getRoles
n/a
0 / 0
1
n/a
0 / 0
 setHomepage
n/a
0 / 0
1
n/a
0 / 0
 getHomepage
n/a
0 / 0
1
n/a
0 / 0
 setBio
n/a
0 / 0
1
n/a
0 / 0
 getBio
n/a
0 / 0
1
n/a
0 / 0
 setLocation
n/a
0 / 0
1
n/a
0 / 0
 getLocation
n/a
0 / 0
1
n/a
0 / 0
 setLat
n/a
0 / 0
1
n/a
0 / 0
 getLat
n/a
0 / 0
1
n/a
0 / 0
 setLon
n/a
0 / 0
1
n/a
0 / 0
 getLon
n/a
0 / 0
1
n/a
0 / 0
 setLocationId
n/a
0 / 0
1
n/a
0 / 0
 getLocationId
n/a
0 / 0
1
n/a
0 / 0
 setLocationService
n/a
0 / 0
1
n/a
0 / 0
 getLocationService
n/a
0 / 0
1
n/a
0 / 0
 setCreated
n/a
0 / 0
1
n/a
0 / 0
 getCreated
n/a
0 / 0
1
n/a
0 / 0
 setModified
n/a
0 / 0
1
n/a
0 / 0
 getModified
n/a
0 / 0
1
n/a
0 / 0
 getAvatarUrl
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
3 / 3
 getFromId
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
3 / 3
 getFromNickname
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
3 / 3
 getNicknameFromId
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
3 / 3
 getSelfTags
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
4 / 4
 setSelfTags
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
11 / 11
 getFollowersCount
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
5 / 5
 getFollowedCount
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
5 / 5
 schemaDef
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
5 / 5
1<?php
2
3// {{{ License
4
5// This file is part of GNU social - https://www.gnu.org/software/social
6//
7// GNU social is free software: you can redistribute it and/or modify
8// it under the terms of the GNU Affero General Public License as published by
9// the Free Software Foundation, either version 3 of the License, or
10// (at your option) any later version.
11//
12// GNU social is distributed in the hope that it will be useful,
13// but WITHOUT ANY WARRANTY; without even the implied warranty of
14// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15// GNU Affero General Public License for more details.
16//
17// You should have received a copy of the GNU Affero General Public License
18// along with GNU social.  If not, see <http://www.gnu.org/licenses/>.
19
20// }}}
21
22namespace App\Entity;
23
24use App\Core\Cache;
25use App\Core\DB\DB;
26use App\Core\Entity;
27use App\Core\Event;
28use App\Core\UserRoles;
29use DateTimeInterface;
30use Functional as F;
31
32/**
33 * Entity for actors
34 *
35 * @category  DB
36 * @package   GNUsocial
37 *
38 * @author    Zach Copley <zach@status.net>
39 * @copyright 2010 StatusNet Inc.
40 * @author    Mikael Nordfeldth <mmn@hethane.se>
41 * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
42 * @author    Hugo Sales <hugo@hsal.es>
43 * @copyright 2020-2021 Free Software Foundation, Inc http://www.fsf.org
44 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
45 */
46class GSActor extends Entity
47{
48    // {{{ Autocode
49    // @codeCoverageIgnoreStart
50    private int $id;
51    private string $nickname;
52    private string $normalized_nickname;
53    private ?string $fullname;
54    private int $roles = 4;
55    private ?string $homepage;
56    private ?string $bio;
57    private ?string $location;
58    private ?float $lat;
59    private ?float $lon;
60    private ?int $location_id;
61    private ?int $location_service;
62    private \DateTimeInterface $created;
63    private \DateTimeInterface $modified;
64
65    public function setId(int $id): self
66    {
67        $this->id = $id;
68        return $this;
69    }
70
71    public function getId(): int
72    {
73        return $this->id;
74    }
75
76    public function setNickname(string $nickname): self
77    {
78        $this->nickname = $nickname;
79        return $this;
80    }
81
82    public function getNickname(): string
83    {
84        return $this->nickname;
85    }
86
87    public function setNormalizedNickname(string $normalized_nickname): self
88    {
89        $this->normalized_nickname = $normalized_nickname;
90        return $this;
91    }
92
93    public function getNormalizedNickname(): string
94    {
95        return $this->normalized_nickname;
96    }
97
98    public function setFullname(?string $fullname): self
99    {
100        $this->fullname = $fullname;
101        return $this;
102    }
103
104    public function getFullname(): ?string
105    {
106        return $this->fullname;
107    }
108
109    public function setRoles(int $roles): self
110    {
111        $this->roles = $roles;
112        return $this;
113    }
114
115    public function getRoles(): int
116    {
117        return $this->roles;
118    }
119
120    public function setHomepage(?string $homepage): self
121    {
122        $this->homepage = $homepage;
123        return $this;
124    }
125
126    public function getHomepage(): ?string
127    {
128        return $this->homepage;
129    }
130
131    public function setBio(?string $bio): self
132    {
133        $this->bio = $bio;
134        return $this;
135    }
136
137    public function getBio(): ?string
138    {
139        return $this->bio;
140    }
141
142    public function setLocation(?string $location): self
143    {
144        $this->location = $location;
145        return $this;
146    }
147
148    public function getLocation(): ?string
149    {
150        return $this->location;
151    }
152
153    public function setLat(?float $lat): self
154    {
155        $this->lat = $lat;
156        return $this;
157    }
158
159    public function getLat(): ?float
160    {
161        return $this->lat;
162    }
163
164    public function setLon(?float $lon): self
165    {
166        $this->lon = $lon;
167        return $this;
168    }
169
170    public function getLon(): ?float
171    {
172        return $this->lon;
173    }
174
175    public function setLocationId(?int $location_id): self
176    {
177        $this->location_id = $location_id;
178        return $this;
179    }
180
181    public function getLocationId(): ?int
182    {
183        return $this->location_id;
184    }
185
186    public function setLocationService(?int $location_service): self
187    {
188        $this->location_service = $location_service;
189        return $this;
190    }
191
192    public function getLocationService(): ?int
193    {
194        return $this->location_service;
195    }
196
197    public function setCreated(DateTimeInterface $created): self
198    {
199        $this->created = $created;
200        return $this;
201    }
202
203    public function getCreated(): DateTimeInterface
204    {
205        return $this->created;
206    }
207
208    public function setModified(DateTimeInterface $modified): self
209    {
210        $this->modified = $modified;
211        return $this;
212    }
213
214    public function getModified(): DateTimeInterface
215    {
216        return $this->modified;
217    }
218
219    // @codeCoverageIgnoreEnd
220    // }}} Autocode
221
222    public function getAvatarUrl()
223    {
224        $url = null;
225        Event::handle('GetAvatarUrl', [$this->getId(), &$url]);
226        return $url;
227    }
228
229    public static function getFromId(int $id): ?self
230    {
231        return Cache::get('gsactor-id-' . $id, function () use ($id) {
232            return DB::find('gsactor', ['id' => $id]);
233        });
234    }
235
236    public static function getFromNickname(string $nickname): ?self
237    {
238        return Cache::get('gsactor-nick-' . $nickname, function () use ($nickname) {
239            return DB::findOneBy('gsactor', ['nickname' => $nickname]);
240        });
241    }
242
243    public static function getNicknameFromId(int $id): string
244    {
245        return Cache::get('gsactor-nick-id-' . $id, function () use ($id) {
246            return self::getFromId($id)->getNickname();
247        });
248    }
249
250    public function getSelfTags(bool $_test_force_recompute = false): array
251    {
252        return Cache::get('selftags-' . $this->id,
253                          function () {
254                              return DB::findBy('gsactor_tag', ['tagger' => $this->id, 'tagged' => $this->id]);
255                          }, beta: $_test_force_recompute ? INF : 1.0);
256    }
257
258    public function setSelfTags(array $tags, array $existing): void
259    {
260        $tag_existing  = F\map($existing, function ($pt) { return $pt->getTag(); });
261        $tag_to_add    = array_diff($tags, $tag_existing);
262        $tag_to_remove = array_diff($tag_existing, $tags);
263        $pt_to_remove  = F\filter($existing, function ($pt) use ($tag_to_remove) { return in_array($pt->getTag(), $tag_to_remove); });
264        foreach ($tag_to_add as $tag) {
265            $pt = GSActorTag::create(['tagger' => $this->id, 'tagged' => $this->id, 'tag' => $tag]);
266            DB::persist($pt);
267        }
268        foreach ($pt_to_remove as $pt) {
269            DB::remove($pt);
270        }
271        Cache::delete('selftags-' . $this->id);
272    }
273
274    public function getFollowersCount()
275    {
276        return Cache::get('followers-' . $this->id,
277                          function () {
278                              return DB::dql('select count(f) from App\Entity\Follow f where f.followed = :followed',
279                                             ['followed' => $this->id])[0][1] - 1; // Remove self follow
280                          });
281    }
282
283    public function getFollowedCount()
284    {
285        return Cache::get('followed-' . $this->id,
286                          function () {
287                              return DB::dql('select count(f) from App\Entity\Follow f where f.follower = :follower',
288                                             ['follower' => $this->id])[0][1] - 1; // Remove self follow
289                          });
290    }
291
292    public static function schemaDef(): array
293    {
294        $def = [
295            'name'        => 'gsactor',
296            'description' => 'local and remote users, groups and bots are gsactors, for instance',
297            'fields'      => [
298                'id'               => ['type' => 'serial', 'not null' => true, 'description' => 'unique identifier'],
299                'nickname'         => ['type' => 'varchar', 'length' => 64, 'not null' => true, 'description' => 'nickname or username'],
300                'fullname'         => ['type' => 'text', 'description' => 'display name'],
301                'roles'            => ['type' => 'int', 'not null' => true, 'default' => UserRoles::USER, 'description' => 'Bitmap of permissions this gsactor has'],
302                'homepage'         => ['type' => 'text', 'description' => 'identifying URL'],
303                'bio'              => ['type' => 'text', 'description' => 'descriptive biography'],
304                'location'         => ['type' => 'text', 'description' => 'physical location'],
305                'lat'              => ['type' => 'numeric', 'precision' => 10, 'scale' => 7, 'description' => 'latitude'],
306                'lon'              => ['type' => 'numeric', 'precision' => 10, 'scale' => 7, 'description' => 'longitude'],
307                'location_id'      => ['type' => 'int', 'description' => 'location id if possible'],
308                'location_service' => ['type' => 'int', 'description' => 'service used to obtain location id'],
309                'created'          => ['type' => 'datetime',  'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was created'],
310                'modified'         => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
311            ],
312            'primary key' => ['id'],
313            'indexes'     => [
314                'gsactor_nickname_idx' => ['nickname'],
315            ],
316            'fulltext indexes' => [
317                'gsactor_fulltext_idx' => ['nickname', 'fullname', 'location', 'bio', 'homepage'],
318            ],
319        ];
320
321        return $def;
322    }
323}