1<?php
2/*
3 * vim:set softtabstop=4 shiftwidth=4 expandtab:
4 *
5 * LICENSE: GNU Affero General Public License, version 3 (AGPL-3.0-or-later)
6 * Copyright 2001 - 2020 Ampache.org
7 *
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU Affero General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 * GNU Affero General Public License for more details.
17 *
18 * You should have received a copy of the GNU Affero General Public License
19 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20 *
21 */
22
23declare(strict_types=0);
24
25namespace Ampache\Repository\Model;
26
27use Ampache\Module\Authorization\Access;
28use Ampache\Module\Api\Ajax;
29use Ampache\Module\User\Activity\UserActivityPosterInterface;
30use Ampache\Module\Util\InterfaceImplementationChecker;
31use Ampache\Module\Util\Mailer;
32use Ampache\Module\Util\ObjectTypeToClassNameMapper;
33use Ampache\Config\AmpConfig;
34use Ampache\Module\System\Core;
35use Ampache\Module\System\Dba;
36use PDOStatement;
37use Ampache\Module\Util\Ui;
38
39class Shoutbox
40{
41    public $id;
42    public $object_type;
43    public $object_id;
44    public $user;
45    public $sticky;
46    public $text;
47    public $data;
48    public $date;
49
50    /**
51     * Constructor
52     * This pulls the shoutbox information from the database and returns
53     * a constructed object, uses user_shout table
54     * @param integer $shout_id
55     */
56    public function __construct($shout_id)
57    {
58        // Load the data from the database
59        $this->has_info($shout_id);
60
61        return true;
62    } // Constructor
63
64    public function getId(): int
65    {
66        return (int) $this->id;
67    }
68
69    /**
70     * has_info
71     * does the db call, reads from the user_shout table
72     * @param integer $shout_id
73     * @return boolean
74     */
75    private function has_info($shout_id)
76    {
77        $sql        = "SELECT * FROM `user_shout` WHERE `id` = ?";
78        $db_results = Dba::read($sql, array($shout_id));
79
80        $data = Dba::fetch_assoc($db_results);
81
82        foreach ($data as $key => $value) {
83            $this->$key = $value;
84        }
85
86        return true;
87    } // has_info
88
89    /**
90     * get_top
91     * This returns the top user_shouts, shoutbox objects are always shown regardless and count against the total
92     * number of objects shown
93     * @param integer $limit
94     * @param string $username
95     * @return integer[]
96     */
97    public static function get_top($limit, $username = null)
98    {
99        $shouts = self::get_sticky();
100
101        // If we've already got too many stop here
102        if (count($shouts) > $limit) {
103            $shouts = array_slice($shouts, 0, $limit);
104
105            return $shouts;
106        }
107
108        // Only get as many as we need
109        $limit  = (int)($limit) - count($shouts);
110        $params = array();
111        $sql    = "SELECT `user_shout`.`id` AS `id` FROM `user_shout` LEFT JOIN `user` ON `user`.`id` = `user_shout`.`user` WHERE `user_shout`.`sticky`='0' ";
112        if ($username !== null) {
113            $sql .= "AND `user`.`username` = ? ";
114            $params[] = $username;
115        }
116        $sql .= "ORDER BY `user_shout`.`date` DESC LIMIT " . $limit;
117        $db_results = Dba::read($sql, $params);
118
119        while ($row = Dba::fetch_assoc($db_results)) {
120            $shouts[] = (int)$row['id'];
121        }
122
123        return $shouts;
124    } // get_top
125
126    /**
127     * get_sticky
128     * This returns all current sticky shoutbox items
129     */
130    private static function get_sticky()
131    {
132        $sql = "SELECT * FROM `user_shout` WHERE `sticky`='1' ORDER BY `date` DESC";
133
134        $db_results = Dba::read($sql);
135        $results    = array();
136        while ($row = Dba::fetch_assoc($db_results)) {
137            $results[] = $row['id'];
138        }
139
140        return $results;
141    } // get_sticky
142
143    /**
144     * get_object
145     * This takes a type and an ID and returns a created object
146     * @param string $type
147     * @param integer $object_id
148     * @return Object
149     */
150    public static function get_object($type, $object_id)
151    {
152        if (!InterfaceImplementationChecker::is_library_item($type)) {
153            return null;
154        }
155
156        $class_name = ObjectTypeToClassNameMapper::map($type);
157        $object     = new $class_name($object_id);
158
159        if ($object->id > 0) {
160            if (strtolower((string)$type) === 'song') {
161                if (!$object->enabled) {
162                    $object = null;
163                }
164            }
165        } else {
166            $object = null;
167        }
168
169        return $object;
170    } // get_object
171
172    /**
173     * get_image
174     * This returns an image tag if the type of object we're currently rolling with
175     * has an image associated with it
176     */
177    public function get_image()
178    {
179        $image_string = '';
180        if (Art::has_db($this->object_id, $this->object_type)) {
181            $image_string = "<img class=\"shoutboximage\" height=\"75\" width=\"75\" src=\"" . AmpConfig::get('web_path') . "/image.php?object_id=" . $this->object_id . "&object_type=" . $this->object_type . "&thumb=1\" />";
182        }
183
184        return $image_string;
185    } // get_image
186
187    /**
188     * create
189     * This takes a key'd array of data as input and inserts a new shoutbox entry, it returns the auto_inc id
190     * @param array $data
191     * @return boolean|string|null
192     * @throws \PHPMailer\PHPMailer\Exception
193     */
194    public static function create(array $data)
195    {
196        if (!InterfaceImplementationChecker::is_library_item($data['object_type'])) {
197            return false;
198        }
199
200        $sticky  = isset($data['sticky']) ? 1 : 0;
201        $user    = (int)($data['user'] ?: Core::get_global('user')->id);
202        $date    = (int)($data['date'] ?: time());
203        $comment = strip_tags($data['comment']);
204
205        $sql = "INSERT INTO `user_shout` (`user`, `date`, `text`, `sticky`, `object_id`, `object_type`, `data`) VALUES (?, ?, ?, ?, ?, ?, ?)";
206        Dba::write($sql,
207            array($user, $date, $comment, $sticky, $data['object_id'], $data['object_type'], $data['data']));
208
209        static::getUserActivityPoster()->post((int) $user, 'shout', $data['object_type'], (int) $data['object_id'], time());
210
211        $insert_id = Dba::insert_id();
212
213        // Never send email in case of user impersonation
214        if (!isset($data['user']) && $insert_id !== null) {
215            $class_name    = ObjectTypeToClassNameMapper::map($data['object_type']);
216            $libitem       = new $class_name($data['object_id']);
217            $item_owner_id = $libitem->get_user_owner();
218            if ($item_owner_id) {
219                if (Preference::get_by_user($item_owner_id, 'notify_email')) {
220                    $item_owner = new User($item_owner_id);
221                    if (!empty($item_owner->email) && Mailer::is_mail_enabled()) {
222                        $libitem->format();
223                        $mailer = new Mailer();
224                        $mailer->set_default_sender();
225                        $mailer->recipient      = $item_owner->email;
226                        $mailer->recipient_name = $item_owner->fullname;
227                        $mailer->subject        = T_('New shout on your content');
228                        /* HINT: %1 username %2 item name being commented on */
229                        $mailer->message = sprintf(T_('You just received a new shout from %1$s on your content %2$s'),
230                            Core::get_global('user')->fullname, $libitem->get_fullname());
231                        $mailer->message .= "\n\n----------------------\n\n";
232                        $mailer->message .= $comment;
233                        $mailer->message .= "\n\n----------------------\n\n";
234                        $mailer->message .= AmpConfig::get('web_path') . "/shout.php?action=show_add_shout&type=" . $data['object_type'] . "&id=" . $data['object_id'] . "#shout" . $insert_id;
235                        $mailer->send();
236                    }
237                }
238            }
239        }
240
241        return $insert_id;
242    } // create
243
244    /**
245     * update
246     * This takes a key'd array of data as input and updates a shoutbox entry
247     * @param array $data
248     * @return mixed
249     */
250    public function update(array $data)
251    {
252        $sql = "UPDATE `user_shout` SET `text` = ?, `sticky` = ? WHERE `id` = ?";
253        Dba::write($sql, array($data['comment'], (int) make_bool($data['sticky']), $this->id));
254
255        return $this->id;
256    } // create
257
258    public function getStickyFormatted(): string
259    {
260        return $this->sticky == '0' ? 'No' : 'Yes';
261    }
262
263    public function getTextFormatted(): string
264    {
265        return preg_replace('/(\r\n|\n|\r)/', '<br />', $this->text);
266    }
267
268    public function getDateFormatted(): string
269    {
270        return get_datetime((int)$this->date);
271    }
272
273    /**
274     * @param boolean $details
275     * @param boolean $jsbuttons
276     * @return string
277     */
278    public function get_display($details = true, $jsbuttons = false)
279    {
280        $object = Shoutbox::get_object($this->object_type, $this->object_id);
281        $object->format();
282        $img  = $this->get_image();
283        $html = "<div class='shoutbox-item'>";
284        $html .= "<div class='shoutbox-data'>";
285        if ($details && $img) {
286            $html .= "<div class='shoutbox-img'>" . $img . "</div>";
287        }
288        $html .= "<div class='shoutbox-info'>";
289        if ($details) {
290            $html .= "<div class='shoutbox-object'>" . $object->f_link . "</div>";
291            $html .= "<div class='shoutbox-date'>" . get_datetime((int)$this->date) . "</div>";
292        }
293        $html .= "<div class='shoutbox-text'>" . $this->getTextFormatted() . "</div>";
294        $html .= "</div>";
295        $html .= "</div>";
296        $html .= "<div class='shoutbox-footer'>";
297        if ($details) {
298            $html .= "<div class='shoutbox-actions'>";
299            if ($jsbuttons) {
300                $html .= Ajax::button('?page=stream&action=directplay&playtype=' . $this->object_type . '&' . $this->object_type . '_id=' . $this->object_id,
301                    'play', T_('Play'), 'play_' . $this->object_type . '_' . $this->object_id);
302                $html .= Ajax::button('?action=basket&type=' . $this->object_type . '&id=' . $this->object_id, 'add',
303                    T_('Add'), 'add_' . $this->object_type . '_' . $this->object_id);
304            }
305            if (Access::check('interface', 25)) {
306                $html .= "<a href=\"" . AmpConfig::get('web_path') . "/shout.php?action=show_add_shout&type=" . $this->object_type . "&id=" . $this->object_id . "\">" . Ui::get_icon('comment',
307                        T_('Post Shout')) . "</a>";
308            }
309            $html .= "</div>";
310        }
311        $html .= "<div class='shoutbox-user'>" . T_('by') . " ";
312
313        if ($this->user > 0) {
314            $user = new User($this->user);
315            $user->format();
316            if ($details) {
317                $html .= $user->f_link;
318            } else {
319                $html .= $user->username;
320            }
321        } else {
322            $html .= T_('Guest');
323        }
324        $html .= "</div>";
325        $html .= "</div>";
326        $html .= "</div>";
327
328        return $html;
329    }
330
331    /**
332     * Migrate an object associate stats to a new object
333     * @param string $object_type
334     * @param integer $old_object_id
335     * @param integer $new_object_id
336     * @return PDOStatement|boolean
337     */
338    public static function migrate($object_type, $old_object_id, $new_object_id)
339    {
340        $sql = "UPDATE `user_shout` SET `object_id` = ? WHERE `object_type` = ? AND `object_id` = ?";
341
342        return Dba::write($sql, array($new_object_id, $object_type, $old_object_id));
343    }
344
345    /**
346     * @deprecated inject dependency
347     */
348    private static function getUserActivityPoster(): UserActivityPosterInterface
349    {
350        global $dic;
351
352        return $dic->get(UserActivityPosterInterface::class);
353    }
354}
355