1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* |
4
|
|
|
* Topic Image Preview. An extension for the phpBB Forum Software package. |
5
|
|
|
* |
6
|
|
|
* @copyright (c) 2017, Matt Friedman |
7
|
|
|
* @license GNU General Public License, version 2 (GPL-2.0) |
8
|
|
|
* |
9
|
|
|
*/ |
10
|
|
|
|
11
|
|
|
namespace vse\topicimagepreview\event; |
12
|
|
|
|
13
|
|
|
use phpbb\auth\auth; |
14
|
|
|
use phpbb\config\config; |
15
|
|
|
use phpbb\db\driver\driver_interface; |
16
|
|
|
use phpbb\user; |
17
|
|
|
use Symfony\Component\EventDispatcher\EventSubscriberInterface; |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* Topic Image Preview Event listener. |
21
|
|
|
*/ |
22
|
|
|
class preview implements EventSubscriberInterface |
23
|
|
|
{ |
24
|
|
|
/** @var auth */ |
25
|
|
|
protected $auth; |
26
|
|
|
|
27
|
|
|
/** @var config */ |
28
|
|
|
protected $config; |
29
|
|
|
|
30
|
|
|
/** @var driver_interface */ |
31
|
|
|
protected $db; |
32
|
|
|
|
33
|
|
|
/** @var user */ |
34
|
|
|
protected $user; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* {@inheritdoc} |
38
|
|
|
*/ |
39
|
|
|
public static function getSubscribedEvents() |
40
|
|
|
{ |
41
|
|
|
return [ |
42
|
|
|
'core.permissions' => 'add_permission', |
43
|
|
|
// Viewforum events |
44
|
|
|
'core.viewforum_modify_topics_data' => 'update_row_data', |
45
|
|
|
'core.viewforum_modify_topicrow' => 'update_tpl_data', |
46
|
|
|
// Search events |
47
|
|
|
'core.search_modify_rowset' => 'update_row_data', |
48
|
|
|
'core.search_modify_tpl_ary' => 'update_tpl_data', |
49
|
|
|
// Precise Similar Topics events |
50
|
|
|
'vse.similartopics.modify_rowset' => 'update_row_data', |
51
|
|
|
'vse.similartopics.modify_topicrow' => 'update_tpl_data', |
52
|
|
|
]; |
53
|
|
|
} |
54
|
|
|
|
55
|
|
|
/** |
56
|
|
|
* Constructor |
57
|
|
|
* |
58
|
|
|
* @param auth $auth |
59
|
|
|
* @param config $config |
60
|
|
|
* @param driver_interface $db |
61
|
|
|
* @param user $user |
62
|
|
|
*/ |
63
|
|
|
public function __construct(auth $auth, config $config, driver_interface $db, user $user) |
64
|
|
|
{ |
65
|
|
|
$this->auth = $auth; |
66
|
|
|
$this->config = $config; |
67
|
|
|
$this->db = $db; |
68
|
|
|
$this->user = $user; |
69
|
|
|
} |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* Add administrative permissions to manage forums |
73
|
|
|
* |
74
|
|
|
* @param \phpbb\event\data $event The event object |
75
|
|
|
* @return void |
76
|
|
|
*/ |
77
|
|
|
public function add_permission($event) |
78
|
|
|
{ |
79
|
|
|
$event->update_subarray('permissions', 'f_vse_tip', [ |
80
|
|
|
'lang' => 'ACL_F_VSE_TIP', |
81
|
|
|
'cat' => 'actions', |
82
|
|
|
]); |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
/** |
86
|
|
|
* Add post text containing images to topic row data. |
87
|
|
|
* |
88
|
|
|
* @param \phpbb\event\data $event The event object |
89
|
|
|
* |
90
|
|
|
* @return void |
91
|
|
|
*/ |
92
|
|
|
public function update_row_data($event) |
93
|
|
|
{ |
94
|
|
|
if (!$this->user_allowed()) |
95
|
|
|
{ |
96
|
|
|
return; |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
// Use topic_list from event, otherwise create one based on the rowset |
100
|
|
|
$topic_list = $event->offsetExists('topic_list') ? $event['topic_list'] : array_keys($event['rowset']); |
101
|
|
|
|
102
|
|
|
if (count($topic_list)) |
103
|
|
|
{ |
104
|
|
|
$event['rowset'] = $this->query_images($topic_list, $event['rowset']); |
105
|
|
|
} |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
/** |
109
|
|
|
* Add image previews to the template data. |
110
|
|
|
* |
111
|
|
|
* @param \phpbb\event\data $event The event object |
112
|
|
|
* |
113
|
|
|
* @return void |
114
|
|
|
*/ |
115
|
|
|
public function update_tpl_data($event) |
116
|
|
|
{ |
117
|
|
|
if (!$this->user_allowed() || !$this->forum_allowed($event['row']['forum_id']) || !$this->has_images($event)) |
118
|
|
|
{ |
119
|
|
|
return; |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
// Send the image string to the template |
123
|
|
|
$block = $event->offsetExists('topic_row') ? 'topic_row' : 'tpl_ary'; |
124
|
|
|
$event[$block] = array_merge($event[$block], ['TOPIC_IMAGES' => $this->extract_images($event['row']['post_text'])]); |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
/** |
128
|
|
|
* Run an SQL query on a group of topics, and find the newest (or oldest) |
129
|
|
|
* post with [IMG] images. Then update the topic's row set array to include |
130
|
|
|
* the post's text in the cases where images were found. |
131
|
|
|
* |
132
|
|
|
* @param array $topic_list An array of topic ids |
133
|
|
|
* @param array $rowset The row set of topic data |
134
|
|
|
* |
135
|
|
|
* @return array The updated row set of topic data which now includes |
136
|
|
|
* the post_text of a post containing images. |
137
|
|
|
*/ |
138
|
|
|
protected function query_images(array $topic_list, array $rowset) |
139
|
|
|
{ |
140
|
|
|
$sql_array = []; |
141
|
|
|
foreach ($topic_list as $topic_id) |
142
|
|
|
{ |
143
|
|
|
if (!$this->forum_allowed($rowset[$topic_id]['forum_id'])) |
144
|
|
|
{ |
145
|
|
|
continue; |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
$stmt = '(SELECT topic_id, post_text |
149
|
|
|
FROM ' . POSTS_TABLE . ' |
150
|
|
|
WHERE topic_id = ' . (int) $topic_id . ' |
151
|
|
|
AND post_visibility = ' . ITEM_APPROVED . ' |
152
|
|
|
AND post_text ' . $this->db->sql_like_expression('<r>' . $this->db->get_any_char() . '<IMG ' . $this->db->get_any_char()) . ' |
153
|
|
|
ORDER BY post_time ' . ($this->config->offsetGet('vse_tip_new') ? 'DESC' : 'ASC') . ' |
154
|
|
|
LIMIT 1)'; |
155
|
|
|
|
156
|
|
|
// SQLite3 doesn't like ORDER BY with UNION ALL, so treat $stmt as derived table |
157
|
|
|
if ($this->db->get_sql_layer() === 'sqlite3') |
158
|
|
|
{ |
159
|
|
|
$stmt = "SELECT * FROM $stmt AS d"; |
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
$sql_array[] = $stmt; |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
if ($sql_array) |
|
|
|
|
166
|
|
|
{ |
167
|
|
|
$sql = implode(' UNION ALL ', $sql_array); |
168
|
|
|
$result = $this->db->sql_query($sql); |
169
|
|
|
while ($row = $this->db->sql_fetchrow($result)) |
170
|
|
|
{ |
171
|
|
|
$rowset[$row['topic_id']]['post_text'] = $row['post_text']; |
172
|
|
|
} |
173
|
|
|
$this->db->sql_freeresult($result); |
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
return $rowset; |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
/** |
180
|
|
|
* Extract images from a post and return them as HTML image tags. |
181
|
|
|
* |
182
|
|
|
* @param string $post Post text from the database. |
183
|
|
|
* |
184
|
|
|
* @return string An string of HTML IMG tags. |
185
|
|
|
*/ |
186
|
|
|
protected function extract_images($post) |
187
|
|
|
{ |
188
|
|
|
// Extract the images |
189
|
|
|
$images = []; |
190
|
|
|
$dom = new \DOMDocument; |
191
|
|
|
$dom->loadXML($post); |
192
|
|
|
$xpath = new \DOMXPath($dom); |
193
|
|
|
foreach ($xpath->query('//IMG[not(ancestor::IMG)]/@src') as $image) |
194
|
|
|
{ |
195
|
|
|
$images[] = $image->textContent; |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
// Create a string of images |
199
|
|
|
return implode(' ', array_map(function ($image) { |
200
|
|
|
return "<img src='{$image}' alt='' style='max-width:{$this->config['vse_tip_dim']}px; max-height:{$this->config['vse_tip_dim']}px;' />"; |
201
|
|
|
}, array_slice($images, 0, (int) $this->config['vse_tip_num'], true))); |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
/** |
205
|
|
|
* Is the forum allowed to show topic image previews |
206
|
|
|
* |
207
|
|
|
* @param int $forum_id Forum identifier |
208
|
|
|
* @return bool True if allowed, false if not |
209
|
|
|
*/ |
210
|
|
|
protected function forum_allowed($forum_id) |
211
|
|
|
{ |
212
|
|
|
return (bool) $this->auth->acl_get('f_vse_tip', $forum_id); |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
/** |
216
|
|
|
* Does the user allow topic image previews? |
217
|
|
|
* |
218
|
|
|
* @return bool True if allowed, false if not |
219
|
|
|
*/ |
220
|
|
|
protected function user_allowed() |
221
|
|
|
{ |
222
|
|
|
return (bool) $this->user->data['user_vse_tip']; |
223
|
|
|
} |
224
|
|
|
|
225
|
|
|
/** |
226
|
|
|
* Check if we have post with images |
227
|
|
|
* |
228
|
|
|
* @param \phpbb\event\data $event The event object |
229
|
|
|
* @return bool True if images found in post text, false if not |
230
|
|
|
*/ |
231
|
|
|
protected function has_images($event) |
232
|
|
|
{ |
233
|
|
|
return !empty($event['row']['post_text']) && preg_match('/^<[r][ >]/', $event['row']['post_text']) && strpos($event['row']['post_text'], '<IMG ') !== false; |
234
|
|
|
} |
235
|
|
|
} |
236
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.