Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 138
0.00% covered (danger)
0.00%
0 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
SlideController
0.00% covered (danger)
0.00%
0 / 138
0.00% covered (danger)
0.00%
0 / 7
1260
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 addSlide
0.00% covered (danger)
0.00%
0 / 46
0.00% covered (danger)
0.00%
0 / 1
56
 getSlides
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
20
 deleteSlide
0.00% covered (danger)
0.00%
0 / 20
0.00% covered (danger)
0.00%
0 / 1
30
 updateSlide
0.00% covered (danger)
0.00%
0 / 43
0.00% covered (danger)
0.00%
0 / 1
90
 sanitizeString
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
20
 sanitizeInt
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
30
1<?php
2/**
3 * Slide Controller
4 *
5 * Handles CRUD operations for store-specific slides.
6 *
7 * @package BuyerKiosk\DigitalSign\Controllers
8 * @since December 2025 - Added input sanitization and consolidated logging
9 */
10
11namespace BuyerKiosk\DigitalSign\Controllers;
12
13use BuyerKiosk\DigitalSign\Constants;
14
15class SlideController extends \BuyerKiosk\Core\Controllers\BaseController
16{
17    /** @var \Store */
18    private $store;
19
20    /** @var \PDO */
21    private $storeDB;
22
23    /** @var \PDO */
24    private $global_db;
25
26    /** @var \KLogger */
27    private $log;
28
29    /**
30     * Constructor
31     *
32     * @param \Slim\Slim $app The Slim application instance
33     * @param \Store $store The store entity
34     */
35    public function __construct($app, \Store $store)
36    {
37        parent::__construct($app);
38        $this->store = $store;
39        global $db_name;
40        $this->global_db = dbConnectByName($db_name);
41        $this->storeDB = dbConnectByName($this->store->getDbName());
42        $this->log = new \KLogger($_ENV['LOG_DIR'] . "digital_sign.log", \KLogger::DEBUG);
43    }
44
45    /**
46     * Add a new slide to the store's slide library
47     *
48     * @return array|false Array with success and slideId on success, false on failure
49     */
50    public function addSlide()
51    {
52        // Sanitize inputs using Slim's request wrapper
53        $slideName = $this->sanitizeString($this->_app->request->post('slideName'));
54        $fileName = $this->sanitizeString($this->_app->request->post('fileName'));
55        $type = $this->sanitizeInt($this->_app->request->post('type'));
56
57        // Validate required fields
58        if (empty($slideName) || empty($fileName) || $type === null) {
59            $this->log->LogWarning("addSlide: Missing required fields", [
60                'slideName' => !empty($slideName),
61                'fileName' => !empty($fileName),
62                'type' => $type !== null
63            ]);
64            return false;
65        }
66
67        // Validate type is a known slide type
68        if (!in_array($type, [Constants::SLIDE_TYPE_IMAGE, Constants::SLIDE_TYPE_VIDEO], true)) {
69            $this->log->LogWarning("addSlide: Invalid slide type", ['type' => $type]);
70            return false;
71        }
72
73        try {
74            $insertQuery = $this->storeDB->prepare(
75                "INSERT INTO dsSlides (slideName, fileName, type, enabled)
76                 VALUES (:slideName, :fileName, :type, :enabled)"
77            );
78            $insertQuery->bindParam(":slideName", $slideName, \PDO::PARAM_STR);
79            $insertQuery->bindParam(":fileName", $fileName, \PDO::PARAM_STR);
80            $insertQuery->bindParam(":type", $type, \PDO::PARAM_INT);
81            $enabled = Constants::ENABLED_YES;
82            $insertQuery->bindParam(":enabled", $enabled, \PDO::PARAM_INT);
83
84            if ($insertQuery->execute() === false) {
85                $this->log->LogError("addSlide: Database insert failed", [
86                    'error_code' => $insertQuery->errorInfo()[0],
87                    'slideName' => $slideName
88                ]);
89                return false;
90            }
91
92            // Get the new slide ID
93            $slideId = (int) $this->storeDB->lastInsertId();
94
95            $this->log->LogDebug("addSlide: Slide added successfully", [
96                'slideName' => $slideName,
97                'type' => $type,
98                'slideId' => $slideId
99            ]);
100
101            return [
102                'success' => true,
103                'slideId' => $slideId,
104                'slideName' => $slideName,
105                'fileName' => $fileName
106            ];
107
108        } catch (\PDOException $e) {
109            $this->log->LogError("addSlide: PDO exception", [
110                'message' => $e->getMessage(),
111                'slideName' => $slideName
112            ]);
113            return false;
114        }
115    }
116
117    /**
118     * Get all slides for the current store
119     *
120     * @return array|false Array of slides on success, false on failure
121     */
122    public function getSlides()
123    {
124        try {
125            $getSlidesQuery = $this->storeDB->prepare("SELECT * FROM dsSlides ORDER BY dateAdded DESC");
126
127            if ($getSlidesQuery->execute() === false) {
128                $this->log->LogError("getSlides: Database query failed", [
129                    'error_code' => $getSlidesQuery->errorInfo()[0]
130                ]);
131                return false;
132            }
133
134            $slides = $getSlidesQuery->fetchAll(\PDO::FETCH_ASSOC);
135            return $slides ?: [];
136
137        } catch (\PDOException $e) {
138            $this->log->LogError("getSlides: PDO exception", [
139                'message' => $e->getMessage()
140            ]);
141            return false;
142        }
143    }
144
145    /**
146     * Delete a slide from the store's slide library
147     *
148     * @return bool True on success, false on failure
149     */
150    public function deleteSlide()
151    {
152        // Sanitize input
153        $slideID = $this->sanitizeInt($this->_app->request->post('slideID'));
154
155        if ($slideID === null || $slideID <= 0) {
156            $this->log->LogWarning("deleteSlide: Invalid slideID", ['slideID' => $slideID]);
157            return false;
158        }
159
160        try {
161            $deleteQuery = $this->storeDB->prepare("DELETE FROM dsSlides WHERE id = :slideID");
162            $deleteQuery->bindParam(":slideID", $slideID, \PDO::PARAM_INT);
163
164            if ($deleteQuery->execute() === false) {
165                $this->log->LogError("deleteSlide: Database delete failed", [
166                    'error_code' => $deleteQuery->errorInfo()[0],
167                    'slideID' => $slideID
168                ]);
169                return false;
170            }
171
172            $this->log->LogDebug("deleteSlide: Slide deleted", ['slideID' => $slideID]);
173            return true;
174
175        } catch (\PDOException $e) {
176            $this->log->LogError("deleteSlide: PDO exception", [
177                'message' => $e->getMessage(),
178                'slideID' => $slideID
179            ]);
180            return false;
181        }
182    }
183
184    /**
185     * Update an existing slide
186     *
187     * @return bool True on success, false on failure
188     */
189    public function updateSlide()
190    {
191        // Sanitize inputs
192        $slideID = $this->sanitizeInt($this->_app->request->post('slideID'));
193        $slideName = $this->sanitizeString($this->_app->request->post('slideName'));
194        $fileName = $this->sanitizeString($this->_app->request->post('fileName'));
195        $type = $this->sanitizeInt($this->_app->request->post('type'));
196
197        // Validate required fields
198        if ($slideID === null || $slideID <= 0) {
199            $this->log->LogWarning("updateSlide: Invalid slideID", ['slideID' => $slideID]);
200            return false;
201        }
202
203        if (empty($slideName) || empty($fileName) || $type === null) {
204            $this->log->LogWarning("updateSlide: Missing required fields", [
205                'slideID' => $slideID,
206                'slideName' => !empty($slideName),
207                'fileName' => !empty($fileName),
208                'type' => $type !== null
209            ]);
210            return false;
211        }
212
213        // Validate type
214        if (!in_array($type, [Constants::SLIDE_TYPE_IMAGE, Constants::SLIDE_TYPE_VIDEO], true)) {
215            $this->log->LogWarning("updateSlide: Invalid slide type", ['type' => $type]);
216            return false;
217        }
218
219        try {
220            $updateQuery = $this->storeDB->prepare(
221                "UPDATE dsSlides
222                 SET slideName = :slideName, fileName = :fileName, type = :type
223                 WHERE id = :slideID"
224            );
225            $updateQuery->bindParam(":slideID", $slideID, \PDO::PARAM_INT);
226            $updateQuery->bindParam(":slideName", $slideName, \PDO::PARAM_STR);
227            $updateQuery->bindParam(":fileName", $fileName, \PDO::PARAM_STR);
228            $updateQuery->bindParam(":type", $type, \PDO::PARAM_INT);
229
230            if ($updateQuery->execute() === false) {
231                $this->log->LogError("updateSlide: Database update failed", [
232                    'error_code' => $updateQuery->errorInfo()[0],
233                    'slideID' => $slideID
234                ]);
235                return false;
236            }
237
238            $this->log->LogDebug("updateSlide: Slide updated", [
239                'slideID' => $slideID,
240                'slideName' => $slideName
241            ]);
242            return true;
243
244        } catch (\PDOException $e) {
245            $this->log->LogError("updateSlide: PDO exception", [
246                'message' => $e->getMessage(),
247                'slideID' => $slideID
248            ]);
249            return false;
250        }
251    }
252
253    /**
254     * Sanitize a string input
255     *
256     * @param mixed $value The value to sanitize
257     * @return string|null Sanitized string or null
258     */
259    private function sanitizeString($value): ?string
260    {
261        if ($value === null || $value === '') {
262            return null;
263        }
264        // Strip tags and encode special characters
265        $sanitized = filter_var(trim($value), FILTER_SANITIZE_SPECIAL_CHARS);
266        return $sanitized !== false ? $sanitized : null;
267    }
268
269    /**
270     * Sanitize an integer input
271     *
272     * @param mixed $value The value to sanitize
273     * @return int|null Sanitized integer or null
274     */
275    private function sanitizeInt($value): ?int
276    {
277        if ($value === null || $value === '') {
278            return null;
279        }
280        $sanitized = filter_var($value, FILTER_SANITIZE_NUMBER_INT);
281        if ($sanitized === false || $sanitized === '') {
282            return null;
283        }
284        return (int) $sanitized;
285    }
286}