Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 61
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
generate
0.00% covered (danger)
0.00%
0 / 61
0.00% covered (danger)
0.00%
0 / 4
156
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
2
 configure
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 45
0.00% covered (danger)
0.00%
0 / 1
90
 commit_changes
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3*
4* This file is part of the phpBB Forum Software package.
5*
6* @copyright (c) phpBB Limited <https://www.phpbb.com>
7* @license GNU General Public License, version 2 (GPL-2.0)
8*
9* For full copyright and license information, please see
10* the docs/CREDITS.txt file.
11*
12*/
13
14namespace phpbb\console\command\thumbnail;
15
16use phpbb\attachment\attachment_category;
17use phpbb\cache\service;
18use phpbb\db\driver\driver_interface;
19use phpbb\filesystem\temp;
20use phpbb\language\language;
21use phpbb\storage\storage;
22use phpbb\user;
23use Symfony\Component\Console\Command\Command as symfony_command;
24use Symfony\Component\Console\Input\InputInterface;
25use Symfony\Component\Console\Output\OutputInterface;
26use Symfony\Component\Console\Style\SymfonyStyle;
27use Symfony\Component\Filesystem\Filesystem as symfony_filesystem;
28
29class generate extends \phpbb\console\command\command
30{
31
32    /**
33    * @var driver_interface
34    */
35    protected $db;
36
37    /**
38    * @var service
39    */
40    protected $cache;
41
42    /**
43     * @var language
44     */
45    protected $language;
46
47    /**
48     * @var symfony_filesystem
49     */
50    protected $symfony_filesystem;
51
52    /**
53     * @var storage
54     */
55    protected $storage;
56
57    /**
58     * @var temp
59     */
60    protected $temp;
61
62    /**
63    * phpBB root path
64    * @var string
65    */
66    protected $phpbb_root_path;
67
68    /**
69    * PHP extension.
70    *
71    * @var string
72    */
73    protected $php_ext;
74
75    /**
76    * Constructor
77    *
78    * @param user $user The user object (used to get language information)
79    * @param driver_interface $db Database connection
80    * @param service $cache The cache service
81    * @param language $language Language
82    * @param storage $storage Storage
83    * @param temp $temp Temp
84    * @param string $phpbb_root_path Root path
85    * @param string $php_ext PHP extension
86    */
87    public function __construct(user $user, driver_interface $db, service $cache, language $language, storage $storage, temp $temp, string $phpbb_root_path, string $php_ext)
88    {
89        $this->db = $db;
90        $this->cache = $cache;
91        $this->symfony_filesystem = new symfony_filesystem();
92        $this->language = $language;
93        $this->storage = $storage;
94        $this->temp = $temp;
95        $this->phpbb_root_path = $phpbb_root_path;
96        $this->php_ext = $php_ext;
97
98        parent::__construct($user);
99    }
100
101    /**
102    * Sets the command name and description
103    *
104    * @return void
105    */
106    protected function configure()
107    {
108        $this
109            ->setName('thumbnail:generate')
110            ->setDescription($this->language->lang('CLI_DESCRIPTION_THUMBNAIL_GENERATE'))
111        ;
112    }
113
114    /**
115    * Executes the command thumbnail:generate.
116    *
117    * Generate a thumbnail for all attachments which need one and don't have it yet.
118    *
119    * @param InputInterface $input The input stream used to get the argument and verboe option.
120    * @param OutputInterface $output The output stream, used for printing verbose-mode and error information.
121    *
122    * @return int 0.
123    */
124    protected function execute(InputInterface $input, OutputInterface $output): int
125    {
126        $io = new SymfonyStyle($input, $output);
127
128        $io->section($this->language->lang('CLI_THUMBNAIL_GENERATING'));
129
130        $sql = 'SELECT COUNT(*) AS nb_missing_thumbnails
131            FROM ' . ATTACHMENTS_TABLE . '
132            WHERE thumbnail = 0';
133        $result = $this->db->sql_query($sql);
134        $nb_missing_thumbnails = (int) $this->db->sql_fetchfield('nb_missing_thumbnails');
135        $this->db->sql_freeresult($result);
136
137        if ($nb_missing_thumbnails === 0)
138        {
139            $io->warning($this->language->lang('CLI_THUMBNAIL_NOTHING_TO_GENERATE'));
140            return symfony_command::SUCCESS;
141        }
142
143        $extensions = $this->cache->obtain_attach_extensions(true);
144
145        $sql = 'SELECT attach_id, physical_filename, extension, real_filename, mimetype
146            FROM ' . ATTACHMENTS_TABLE . '
147            WHERE thumbnail = 0';
148        $result = $this->db->sql_query($sql);
149
150        if (!function_exists('create_thumbnail'))
151        {
152            require($this->phpbb_root_path . 'includes/functions_posting.' . $this->php_ext);
153        }
154
155        $progress = $this->create_progress_bar($nb_missing_thumbnails, $io, $output);
156
157        $progress->setMessage($this->language->lang('CLI_THUMBNAIL_GENERATING'));
158
159        $progress->start();
160
161        $thumbnail_created = array();
162        while ($row = $this->db->sql_fetchrow($result))
163        {
164            if (isset($extensions[$row['extension']]['display_cat']) && $extensions[$row['extension']]['display_cat'] == attachment_category::IMAGE)
165            {
166                $source = $this->symfony_filesystem->tempnam($this->temp->get_dir(), 'thumbnail_source');
167                $destination = $this->symfony_filesystem->tempnam($this->temp->get_dir(), 'thumbnail_destination');
168
169                file_put_contents($source, $this->storage->read($row['physical_filename']));
170
171                if (create_thumbnail($source, $destination, $row['mimetype']))
172                {
173                    $this->storage->write('thumb_' . $row['physical_filename'], fopen($destination, 'rb'));
174
175                    $thumbnail_created[] = (int) $row['attach_id'];
176
177                    if (count($thumbnail_created) === 250)
178                    {
179                        $this->commit_changes($thumbnail_created);
180                        $thumbnail_created = array();
181                    }
182
183                    $progress->setMessage($this->language->lang('CLI_THUMBNAIL_GENERATED', $row['real_filename'], $row['physical_filename']));
184                }
185                else
186                {
187                    $progress->setMessage('<info>' . $this->language->lang('CLI_THUMBNAIL_SKIPPED', $row['real_filename'], $row['physical_filename']) . '</info>');
188                }
189
190                @unlink($source);
191                @unlink($destination);
192            }
193
194            $progress->advance();
195        }
196        $this->db->sql_freeresult($result);
197
198        if (!empty($thumbnail_created))
199        {
200            $this->commit_changes($thumbnail_created);
201        }
202
203        $progress->finish();
204
205        $io->newLine(2);
206        $io->success($this->language->lang('CLI_THUMBNAIL_GENERATING_DONE'));
207
208        return symfony_command::SUCCESS;
209    }
210
211    /**
212    * Commits the changes to the database
213    *
214    * @param array $thumbnail_created
215    */
216    protected function commit_changes(array $thumbnail_created)
217    {
218        $sql = 'UPDATE ' . ATTACHMENTS_TABLE . '
219                SET thumbnail = 1
220                WHERE ' . $this->db->sql_in_set('attach_id', $thumbnail_created);
221        $this->db->sql_query($sql);
222    }
223}