Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
2 / 2
CRAP
100.00% covered (success)
100.00%
1 / 1
Sqlsrv
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
2 / 2
8
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 createTables
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
7
1<?php
2
3/**
4 * The phpMyFAQ instances database class with CREATE TABLE statements for MS SQL Server.
5 *
6 * This Source Code Form is subject to the terms of the Mozilla Public License,
7 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
8 * obtain one at https://mozilla.org/MPL/2.0/
9 *
10 * @package   phpMyFAQ
11 * @author    Thorsten Rinne <thorsten@phpmyfaq.de>
12 * @copyright 2015-2026 phpMyFAQ Team
13 * @license   https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
14 * @link      https://www.phpmyfaq.de
15 * @since     2015-04-06
16 */
17
18declare(strict_types=1);
19
20namespace phpMyFAQ\Instance\Database;
21
22use phpMyFAQ\Configuration;
23use phpMyFAQ\Instance\Database;
24
25/**
26 * Class Sqlsrv
27 *
28 * @package phpMyFAQ\Instance\Database
29 * @deprecated Use PDO instead. Will be removed in the v5.0 release.
30 */
31class Sqlsrv extends Database implements DriverInterface
32{
33    /** @var string[] */
34    private array $createTableStatements = [
35        'faqadminlog' => 'CREATE TABLE %sfaqadminlog (
36            id INTEGER NOT NULL,
37            time INTEGER NOT NULL,
38            usr INTEGER NOT NULL,
39            text NVARCHAR(4000) NOT NULL,
40            hash VARCHAR(64) NULL,
41            previous_hash VARCHAR(64) NULL,
42            ip NVARCHAR(64) NOT NULL,
43            PRIMARY KEY (id))',
44        'faqattachment' => 'CREATE TABLE %sfaqattachment (
45            id INTEGER NOT NULL,
46            record_id INTEGER NOT NULL,
47            record_lang NVARCHAR(5) NOT NULL,
48            real_hash CHAR(32) NOT NULL,
49            virtual_hash CHAR(32) NOT NULL,
50            password_hash CHAR(40) NULL,
51            filename NVARCHAR(255) NOT NULL,
52            filesize INTEGER NOT NULL,
53            encrypted INTEGER NOT NULL DEFAULT 0,
54            mime_type NVARCHAR(255) NULL,
55            PRIMARY KEY (id))',
56        'faqattachment_file' => 'CREATE TABLE %sfaqattachment_file (
57            virtual_hash CHAR(32) NOT NULL,
58            contents NVARCHAR(MAX) NOT NULL,
59            PRIMARY KEY (virtual_hash))',
60        'faqbackup' => 'CREATE TABLE %sfaqbackup (
61            id INT(11) NOT NULL,
62            filename VARCHAR(255) NOT NULL,
63            authkey VARCHAR(255) NOT NULL,
64            authcode VARCHAR(255) NOT NULL,
65            created timestamp NOT NULL,
66            PRIMARY KEY (id))',
67        'faqbookmarks' => 'CREATE TABLE %sfaqbookmarks (
68            userid INT(11) DEFAULT NULL,
69            faqid INT(11) DEFAULT NULL)',
70        'faqcaptcha' => 'CREATE TABLE %sfaqcaptcha (
71            id NVARCHAR(6) NOT NULL,
72            useragent NVARCHAR(255) NOT NULL,
73            language NVARCHAR(5) NOT NULL,
74            ip NVARCHAR(64) NOT NULL,
75            captcha_time INTEGER NOT NULL,
76            PRIMARY KEY (id))',
77        'faqcategories' => 'CREATE TABLE %sfaqcategories (
78            id INTEGER NOT NULL,
79            lang NVARCHAR(5) NOT NULL,
80            parent_id INTEGER NOT NULL,
81            name NVARCHAR(255) NOT NULL,
82            description NVARCHAR(255) DEFAULT NULL,
83            user_id INTEGER NOT NULL,
84            group_id INTEGER NOT NULL DEFAULT -1,
85            active INTEGER NULL DEFAULT 1,
86            image NVARCHAR(255) DEFAULT NULL,
87            show_home SMALLINT DEFAULT NULL,
88            PRIMARY KEY (id, lang))',
89        'faqcategoryrelations' => 'CREATE TABLE %sfaqcategoryrelations (
90            category_id INTEGER NOT NULL,
91            category_lang NVARCHAR(5) NOT NULL,
92            record_id INTEGER NOT NULL,
93            record_lang NVARCHAR(5) NOT NULL,
94            PRIMARY KEY (category_id, category_lang, record_id, record_lang))',
95        'faqcategoryrelations_idx' => 'CREATE INDEX idx_records ON %sfaqcategoryrelations (record_id, record_lang)',
96        'faqcategory_group' => 'CREATE TABLE %sfaqcategory_group (
97            category_id INTEGER NOT NULL,
98            group_id INTEGER NOT NULL,
99            PRIMARY KEY (category_id, group_id))',
100        'faqcategory_news' => 'CREATE TABLE %sfaqcategory_news (
101            category_id INTEGER NOT NULL,
102            news_id INTEGER NOT NULL,
103            PRIMARY KEY (category_id, news_id))',
104        'faqcategory_order' => 'CREATE TABLE %sfaqcategory_order (
105            category_id INTEGER NOT NULL,
106            parent_id INTEGER DEFAULT NULL,
107            position INTEGER NOT NULL,
108            PRIMARY KEY (category_id))',
109        'faqcategory_user' => 'CREATE TABLE %sfaqcategory_user (
110            category_id INTEGER NOT NULL,
111            user_id INTEGER NOT NULL,
112            PRIMARY KEY (category_id, user_id))',
113        'faqchanges' => 'CREATE TABLE %sfaqchanges (
114            id INTEGER NOT NULL,
115            beitrag SMALLINT NOT NULL,
116            lang NVARCHAR(5) NOT NULL,
117            revision_id INTEGER NOT NULL DEFAULT 0,
118            usr INTEGER NOT NULL ,
119            datum INTEGER NOT NULL,
120            what NVARCHAR(MAX) DEFAULT NULL,
121            PRIMARY KEY (id, lang))',
122        'faqcomments' => 'CREATE TABLE %sfaqcomments (
123            id_comment INTEGER NOT NULL,
124            id INTEGER NOT NULL,
125            type NVARCHAR(10) NOT NULL,
126            usr NVARCHAR(255) NOT NULL,
127            email NVARCHAR(255) NOT NULL,
128            comment NVARCHAR(MAX) NOT NULL,
129            datum NVARCHAR(64) NOT NULL,
130            helped NVARCHAR(MAX) DEFAULT NULL,
131            PRIMARY KEY (id_comment))',
132        'faqconfig' => 'CREATE TABLE %sfaqconfig (
133            config_name NVARCHAR(255) NOT NULL default \'\',
134            config_value TEXT DEFAULT NULL,
135            PRIMARY KEY (config_name))',
136        'faqdata' => 'CREATE TABLE %sfaqdata (
137            id INTEGER NOT NULL,
138            lang NVARCHAR(5) NOT NULL,
139            solution_id INTEGER NOT NULL,
140            revision_id INTEGER NOT NULL DEFAULT 0,
141            active char(3) NOT NULL,
142            sticky INTEGER NOT NULL,
143            keywords NVARCHAR(MAX) DEFAULT NULL,
144            thema NVARCHAR(MAX) NOT NULL,
145            content NVARCHAR(MAX) DEFAULT NULL,
146            author NVARCHAR(255) NOT NULL,
147            email NVARCHAR(255) NOT NULL,
148            comment char(1) default \'y\',
149            updated NVARCHAR(15) NOT NULL,
150            date_start NVARCHAR(14) NOT NULL DEFAULT \'00000000000000\',
151            date_end NVARCHAR(14) NOT NULL DEFAULT \'99991231235959\',
152            created DATETIME DEFAULT CURRENT_TIMESTAMP,
153            notes NVARCHAR(MAX) DEFAULT NULL,
154            sticky_order INTEGER DEFAULT NULL,
155            PRIMARY KEY (id, lang))',
156        'faqdata_revisions' => 'CREATE TABLE %sfaqdata_revisions (
157            id INTEGER NOT NULL,
158            lang NVARCHAR(5) NOT NULL,
159            solution_id INTEGER NOT NULL,
160            revision_id INTEGER NOT NULL DEFAULT 0,
161            active char(3) NOT NULL,
162            sticky INTEGER NOT NULL,
163            keywords NVARCHAR(MAX) DEFAULT NULL,
164            thema NVARCHAR(MAX) NOT NULL,
165            content NVARCHAR(MAX) DEFAULT NULL,
166            author NVARCHAR(255) NOT NULL,
167            email NVARCHAR(255) NOT NULL,
168            comment char(1) default \'y\',
169            updated NVARCHAR(15) NOT NULL,
170            date_start NVARCHAR(14) NOT NULL DEFAULT \'00000000000000\',
171            date_end NVARCHAR(14) NOT NULL DEFAULT \'99991231235959\',
172            created DATETIME DEFAULT CURRENT_TIMESTAMP,
173            notes NVARCHAR(MAX) DEFAULT NULL,
174            sticky_order INTEGER DEFAULT NULL,
175            PRIMARY KEY (id, lang, solution_id, revision_id))',
176        'faqdata_group' => 'CREATE TABLE %sfaqdata_group (
177            record_id INTEGER NOT NULL,
178            group_id INTEGER NOT NULL,
179            PRIMARY KEY (record_id, group_id))',
180        'faqdata_tags' => 'CREATE TABLE %sfaqdata_tags (
181            record_id INTEGER NOT NULL,
182            tagging_id INTEGER NOT NULL,
183            PRIMARY KEY (record_id, tagging_id))',
184        'faqdata_user' => 'CREATE TABLE %sfaqdata_user (
185            record_id INTEGER NOT NULL,
186            user_id INTEGER NOT NULL,
187            PRIMARY KEY (record_id, user_id))',
188        'faqforms' => 'CREATE TABLE %sfaqforms (
189            form_id INTEGER NOT NULL,
190            input_id INTEGER NOT NULL,
191            input_type NVARCHAR(1000) NOT NULL,
192            input_label NVARCHAR(500) NOT NULL,
193            input_active INTEGER NOT NULL,
194            input_required INTEGER NOT NULL,
195            input_lang NVARCHAR(11) NOT NULL)',
196        'faqglossary' => 'CREATE TABLE %sfaqglossary (
197            id INTEGER NOT NULL ,
198            lang NVARCHAR(5) NOT NULL ,
199            item NVARCHAR(255) NOT NULL ,
200            definition NVARCHAR(MAX) NOT NULL,
201            PRIMARY KEY (id, lang))',
202        'faqgroup' => 'CREATE TABLE %sfaqgroup (
203            group_id INTEGER NOT NULL,
204            name NVARCHAR(25) NULL,
205            description NVARCHAR(MAX) NULL,
206            auto_join INTEGER NULL,
207            PRIMARY KEY(group_id))',
208        'faqgroup_idx' => 'CREATE UNIQUE INDEX idx_name ON %sfaqgroup (name)',
209        'faqgroup_right' => 'CREATE TABLE %sfaqgroup_right (
210            group_id INTEGER NOT NULL,
211            right_id INTEGER NOT NULL,
212            PRIMARY KEY(group_id, right_id))',
213        'faqinstances' => 'CREATE TABLE %sfaqinstances (
214            id INT NOT NULL,
215            url NVARCHAR(255) NOT NULL,
216            instance NVARCHAR(255) NOT NULL,
217            comment NVARCHAR(MAX) NULL,
218            created DATETIME NOT NULL,
219            modified DATETIME NOT NULL,
220            PRIMARY KEY (id))',
221        'faqinstances_config' => 'CREATE TABLE %sfaqinstances_config (
222            instance_id INT NOT NULL,
223            config_name NVARCHAR(255) NOT NULL default \'\',
224            config_value NVARCHAR(255) DEFAULT NULL,
225            PRIMARY KEY (instance_id, config_name))',
226        'faqnews' => 'CREATE TABLE %sfaqnews (
227            id INTEGER NOT NULL,
228            lang NVARCHAR(5) NOT NULL,
229            header NVARCHAR(255) NOT NULL,
230            artikel NVARCHAR(MAX) NOT NULL,
231            datum NVARCHAR(14) NOT NULL,
232            author_name  NVARCHAR(255) NULL,
233            author_email NVARCHAR(255) NULL,
234            active char(1) default \'y\',
235            comment char(1) default \'n\',
236            link NVARCHAR(255) DEFAULT NULL,
237            linktitel NVARCHAR(255) DEFAULT NULL,
238            target NVARCHAR(255) NOT NULL,
239            PRIMARY KEY (id))',
240        'faqcustompages' => 'CREATE TABLE %sfaqcustompages (
241            id INT NOT NULL,
242            lang VARCHAR(5) NOT NULL,
243            page_title VARCHAR(255) NOT NULL,
244            slug VARCHAR(255) NOT NULL,
245            content NVARCHAR(MAX) NOT NULL,
246            author_name VARCHAR(255) NOT NULL,
247            author_email VARCHAR(255) NOT NULL,
248            active CHAR(1) NOT NULL DEFAULT \'n\',
249            created DATETIME NOT NULL DEFAULT GETDATE(),
250            updated DATETIME NULL,
251            seo_title VARCHAR(60) NULL,
252            seo_description VARCHAR(160) NULL,
253            seo_robots VARCHAR(50) NOT NULL DEFAULT \'index,follow\',
254            PRIMARY KEY (id, lang))',
255        'faqquestions' => 'CREATE TABLE %sfaqquestions (
256            id INTEGER NOT NULL,
257            lang NVARCHAR(5) NOT NULL,
258            username NVARCHAR(100) NOT NULL,
259            email NVARCHAR(100) NOT NULL,
260            category_id INTEGER NOT NULL,
261            question NVARCHAR(MAX) NOT NULL,
262            created NVARCHAR(20) NOT NULL,
263            is_visible char(1) default \'Y\',
264            answer_id INTEGER NOT NULL DEFAULT 0,
265            PRIMARY KEY (id))',
266        'faqright' => 'CREATE TABLE %sfaqright (
267            right_id INTEGER NOT NULL,
268            name NVARCHAR(50) NULL,
269            description NVARCHAR(MAX) NULL,
270            for_users INTEGER NULL DEFAULT 1,
271            for_groups INTEGER NULL DEFAULT 1,
272            for_sections INTEGER NULL DEFAULT 1,
273            PRIMARY KEY (right_id))',
274        'faqsearches' => 'CREATE TABLE %sfaqsearches (
275            id INTEGER NOT NULL ,
276            lang NVARCHAR(5) NOT NULL ,
277            searchterm NVARCHAR(255) NOT NULL ,
278            searchdate DATETIME,
279            PRIMARY KEY (id, lang))',
280        'faqseo' => 'CREATE TABLE %sfaqseo (
281            id INT NOT NULL,
282            type VARCHAR(32) NOT NULL,
283            reference_id INT NOT NULL,
284            reference_language VARCHAR(5) NOT NULL,
285            title TEXT NULL,
286            description TEXT NULL,
287            slug TEXT NULL,
288            created DATE NOT NULL DEFAULT GETDATE(),
289            PRIMARY KEY (id))',
290        'faqsessions' => 'CREATE TABLE %sfaqsessions (
291            sid INTEGER NOT NULL,
292            user_id INTEGER NOT NULL,
293            ip NVARCHAR(64) NOT NULL,
294            time INTEGER NOT NULL,
295            PRIMARY KEY (sid))',
296        'faqsessions_idx' => 'CREATE INDEX idx_time ON %sfaqsessions (time)',
297        'faqsearches_searchterm_idx' => 'CREATE INDEX idx_faqsearches_searchterm ON %sfaqsearches (searchterm)',
298        'faqsearches_date_term_idx' => 'CREATE INDEX idx_faqsearches_date_term ON %sfaqsearches (searchdate, searchterm)',
299        'faqsearches_date_term_lang_idx' => 'CREATE INDEX idx_faqsearches_date_term_lang ON %sfaqsearches (searchdate, searchterm, lang)',
300        'faqstopwords' => 'CREATE TABLE %sfaqstopwords (
301            id INTEGER NOT NULL,
302            lang NVARCHAR(5) NOT NULL,
303            stopword NVARCHAR(64) NOT NULL,
304            PRIMARY KEY (id, lang))',
305        'faqtags' => 'CREATE TABLE %sfaqtags (
306            tagging_id INTEGER NOT NULL,
307            tagging_name NVARCHAR(255) NOT NULL ,
308            PRIMARY KEY (tagging_id, tagging_name))',
309        'faquser' => 'CREATE TABLE %sfaquser (
310            user_id INTEGER NOT NULL,
311            login NVARCHAR(128) NOT NULL,
312            session_id NVARCHAR(150) NULL,
313            session_timestamp INTEGER NULL,
314            ip NVARCHAR(64) NULL,
315            account_status NVARCHAR(50) NULL,
316            last_login NVARCHAR(14) NULL,
317            auth_source NVARCHAR(100) NULL,
318            member_since NVARCHAR(14) NULL,
319            remember_me NVARCHAR(150) NULL,
320            success INTEGER NULL DEFAULT 1,
321            is_superadmin INTEGER NULL DEFAULT 0,
322            login_attempts INTEGER NULL DEFAULT 0,
323            refresh_token TEXT NULL DEFAULT NULL,
324            access_token TEXT NULL DEFAULT NULL,
325            code_verifier VARCHAR(255) NULL DEFAULT NULL,
326            jwt TEXT NULL DEFAULT NULL,
327            webauthnkeys TEXT NULL DEFAULT NULL,
328            PRIMARY KEY (user_id))',
329        'faquserdata' => 'CREATE TABLE %sfaquserdata (
330            user_id INTEGER NOT NULL,
331            last_modified NVARCHAR(14) NULL,
332            display_name NVARCHAR(128) NULL,
333            email NVARCHAR(128) NULL,
334            keycloak_sub NVARCHAR(255) NULL,
335            is_visible INTEGER NULL DEFAULT 0,
336            twofactor_enabled INTEGER NULL DEFAULT 0,
337            secret NVARCHAR(128) NULL DEFAULT NULL)',
338        'faquserlogin' => 'CREATE TABLE %sfaquserlogin (
339            login NVARCHAR(128) NOT NULL,
340            pass NVARCHAR(80) NULL,
341            domain NVARCHAR(255) NULL,
342            PRIMARY KEY (login))',
343        'faquser_group' => 'CREATE TABLE %sfaquser_group (
344            user_id INTEGER NOT NULL,
345            group_id INTEGER NOT NULL,
346            PRIMARY KEY (user_id, group_id))',
347        'faquser_right' => 'CREATE TABLE %sfaquser_right (
348            user_id INTEGER NOT NULL,
349            right_id INTEGER NOT NULL,
350            PRIMARY KEY (user_id, right_id))',
351        'faqvisits' => 'CREATE TABLE %sfaqvisits (
352            id INTEGER NOT NULL,
353            lang NVARCHAR(5) NOT NULL,
354            visits INTEGER NOT NULL,
355            last_visit INTEGER NOT NULL,
356            PRIMARY KEY (id, lang))',
357        'faqvoting' => 'CREATE TABLE %sfaqvoting (
358            id INTEGER NOT NULL,
359            artikel SMALLINT NOT NULL,
360            vote SMALLINT NOT NULL,
361            usr SMALLINT NOT NULL,
362            datum NVARCHAR(20) DEFAULT \'\',
363            ip NVARCHAR(15) DEFAULT \'\',
364            PRIMARY KEY (id))',
365        'faqchat_messages' => 'CREATE TABLE %sfaqchat_messages (
366            id INTEGER IDENTITY(1,1) NOT NULL,
367            sender_id INTEGER NOT NULL,
368            recipient_id INTEGER NOT NULL,
369            message NVARCHAR(MAX) NOT NULL,
370            is_read INTEGER NOT NULL DEFAULT 0,
371            created_at DATETIME DEFAULT GETDATE(),
372            PRIMARY KEY (id))',
373        'faqchat_messages_idx_sender' => 'CREATE INDEX idx_chat_sender ON %sfaqchat_messages (sender_id)',
374        'faqchat_messages_idx_recipient' => 'CREATE INDEX idx_chat_recipient ON %sfaqchat_messages (recipient_id)',
375        'faqchat_messages_idx_conversation' => 'CREATE INDEX idx_chat_conversation ON %sfaqchat_messages (sender_id, recipient_id)',
376        'faqchat_messages_idx_created' => 'CREATE INDEX idx_chat_created ON %sfaqchat_messages (created_at)',
377    ];
378
379    /**
380     * Constructor.
381     */
382    public function __construct(Configuration $configuration)
383    {
384        $this->configuration = $configuration;
385    }
386
387    /**
388     * Executes all CREATE TABLE and CREATE INDEX statements.
389     */
390    public function createTables(string $prefix = '', ?string $schema = null): bool
391    {
392        if ($schema !== null && $schema !== '') {
393            if (!preg_match('/^[A-Za-z0-9_]+$/', $schema)) {
394                return false;
395            }
396
397            $schemaLiteral = str_replace("'", replace: "''", subject: $schema);
398            $schemaIdentifier = sprintf('[%s]', str_replace(']', replace: ']]', subject: $schema));
399
400            if (!$this->configuration->getDb()->query(sprintf(
401                "IF NOT EXISTS (SELECT * FROM sys.schemas WHERE name = '%s') EXEC('CREATE SCHEMA %s')",
402                $schemaLiteral,
403                $schemaIdentifier,
404            ))) {
405                return false;
406            }
407        }
408
409        foreach ($this->createTableStatements as $createTableStatement) {
410            $result = $this->configuration->getDb()->query(sprintf($createTableStatement, $prefix));
411
412            if (!$result) {
413                return false;
414            }
415        }
416
417        return true;
418    }
419}