PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / 2.6.22
Search Atlas SEO – OTTO AI SEO Automation for WordPress v2.6.22
2.7.1 2.7.2 2.7.0 2.6.26 2.6.25 2.6.24 2.6.23 2.6.22 2.6.21 2.6.20 2.6.19 2.6.18 2.6.17 2.6.16 2.6.15 2.6.14 2.6.13 2.6.12 2.6.11 2.6.10 2.6.9 2.6.8 2.6.7 2.6.6 2.6.5 All 141 releases
metasync / robots-txt / class-metasync-robots-txt-database.php

class-metasync-robots-txt-database.php in Search Atlas SEO – OTTO AI SEO Automation for WordPress 2.6.22, at robots-txt/class-metasync-robots-txt-database.php

318 lines 7.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Robots.txt Database Class
5 *
6 * @package Search Atlas SEO
7 * @copyright Copyright (C) 2021-2025, Search Atlas Group - [email protected]
8 * @since 2.5.6
9 */
10
11 // If this file is called directly, abort.
12 if (!defined('WPINC')) {
13 die;
14 }
15
16 class Metasync_Robots_Txt_Database
17 {
18 /**
19 * Maximum number of backups retained in the table.
20 *
21 * The Backup History UI must display up to this many entries so the list
22 * reflects the full stored state. Displaying fewer than are retained makes
23 * deleted entries appear to "reappear" on refresh, as older backups that
24 * were hidden by a smaller display limit surface into view.
25 *
26 * @var int
27 */
28 const MAX_BACKUPS = 50;
29
30 /**
31 * Number of backups shown per page in the Backup History UI.
32 *
33 * @var int
34 */
35 const BACKUPS_PER_PAGE = 10;
36
37 /**
38 * Instance of this class
39 *
40 * @var Metasync_Robots_Txt_Database
41 */
42 private static $instance = null;
43
44 /**
45 * Table name
46 *
47 * @var string
48 */
49 private $table_name;
50
51 /**
52 * Get singleton instance
53 */
54 public static function get_instance()
55 {
56 if (null === self::$instance) {
57 self::$instance = new self();
58 }
59 return self::$instance;
60 }
61
62 /**
63 * Constructor
64 */
65 private function __construct()
66 {
67 global $wpdb;
68 $this->table_name = $wpdb->prefix . 'metasync_robots_txt_backups';
69 }
70
71 /**
72 * Create database table
73 */
74 public function create_table()
75 {
76 global $wpdb;
77
78 $charset_collate = $wpdb->get_charset_collate();
79
80 $sql = "CREATE TABLE IF NOT EXISTS {$this->table_name} (
81 id bigint(20) NOT NULL AUTO_INCREMENT,
82 content longtext NOT NULL,
83 created_at datetime DEFAULT CURRENT_TIMESTAMP,
84 created_by bigint(20) DEFAULT NULL,
85 PRIMARY KEY (id),
86 KEY created_at (created_at)
87 ) $charset_collate;";
88
89 require_once ABSPATH . 'wp-admin/includes/upgrade.php';
90 dbDelta($sql);
91 }
92
93 /**
94 * Create a backup
95 *
96 * @param string $content Content to backup
97 * @return int|bool Backup ID on success, false on failure
98 */
99 public function create_backup($content)
100 {
101 global $wpdb;
102
103 $result = $wpdb->insert(
104 $this->table_name,
105 array(
106 'content' => $content,
107 'created_at' => current_time('mysql', true),
108 'created_by' => get_current_user_id()
109 ),
110 array('%s', '%s', '%d')
111 );
112
113 if (false === $result) {
114 return false;
115 }
116
117 // Keep only last 50 backups
118 $this->cleanup_old_backups(self::MAX_BACKUPS);
119
120 return $wpdb->insert_id;
121 }
122
123 /**
124 * Get backups
125 *
126 * @param int $limit Number of backups to retrieve
127 * @param int $offset Number of backups to skip (for pagination)
128 * @return array Array of backups
129 */
130 public function get_backups($limit = 10, $offset = 0)
131 {
132 global $wpdb;
133
134 if ($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $this->table_name)) !== $this->table_name) {
135 $this->create_table();
136 return array();
137 }
138
139 $limit = absint($limit);
140 $offset = absint($offset);
141
142 $results = $wpdb->get_results(
143 $wpdb->prepare(
144 "SELECT b.*, u.display_name as created_by_name
145 FROM {$this->table_name} b
146 LEFT JOIN {$wpdb->users} u ON b.created_by = u.ID
147 ORDER BY b.created_at DESC
148 LIMIT %d OFFSET %d",
149 $limit,
150 $offset
151 ),
152 ARRAY_A
153 );
154
155 return $results ? $results : array();
156 }
157
158 /**
159 * Count the total number of stored backups
160 *
161 * @return int Total backup count
162 */
163 public function count_backups()
164 {
165 global $wpdb;
166
167 if ($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $this->table_name)) !== $this->table_name) {
168 return 0;
169 }
170
171 return (int) $wpdb->get_var("SELECT COUNT(*) FROM {$this->table_name}");
172 }
173
174 /**
175 * Get a single backup
176 *
177 * @param int $backup_id Backup ID
178 * @return array|null Backup data or null
179 */
180 public function get_backup($backup_id)
181 {
182 global $wpdb;
183
184 $result = $wpdb->get_row(
185 $wpdb->prepare(
186 "SELECT * FROM {$this->table_name} WHERE id = %d",
187 $backup_id
188 ),
189 ARRAY_A
190 );
191
192 return $result;
193 }
194
195 /**
196 * Delete a backup
197 *
198 * @param int $backup_id Backup ID
199 * @return bool True on success, false on failure
200 */
201 public function delete_backup($backup_id)
202 {
203 global $wpdb;
204
205 $result = $wpdb->delete(
206 $this->table_name,
207 array('id' => $backup_id),
208 array('%d')
209 );
210
211 // $wpdb->delete() returns the number of affected rows, or false on
212 // error. Treat "0 rows affected" as a failure: the entry was not
213 // actually removed (e.g. it no longer exists), so reporting success
214 // would let the UI drop the row while it persists in storage.
215 return false !== $result && $result > 0;
216 }
217
218 /**
219 * Cleanup old backups
220 *
221 * @param int $keep_count Number of backups to keep
222 * @return bool True on success, false on failure
223 */
224 private function cleanup_old_backups($keep_count = self::MAX_BACKUPS)
225 {
226 global $wpdb;
227
228 // First, get the IDs to keep (avoids MySQL subquery deadlock)
229 $ids_to_keep = $wpdb->get_col(
230 $wpdb->prepare(
231 "SELECT id FROM {$this->table_name}
232 ORDER BY created_at DESC
233 LIMIT %d",
234 $keep_count
235 )
236 );
237
238 if (empty($ids_to_keep)) {
239 return true;
240 }
241
242 // Delete all records NOT in the keep list
243 $placeholders = implode(',', array_fill(0, count($ids_to_keep), '%d'));
244 $wpdb->query(
245 $wpdb->prepare(
246 "DELETE FROM {$this->table_name}
247 WHERE id NOT IN ($placeholders)",
248 $ids_to_keep
249 )
250 );
251
252 return true;
253 }
254
255 /**
256 * Drop table (for uninstall)
257 */
258 public function drop_table()
259 {
260 global $wpdb;
261 $wpdb->query("DROP TABLE IF EXISTS {$this->table_name}");
262 }
263
264 /**
265 * Store virtual robots.txt content (when physical file cannot be created)
266 *
267 * @param string $content Content to store
268 * @return bool True on success, false on failure
269 */
270 public function store_virtual_content($content)
271 {
272 return update_option('metasync_robots_txt_virtual', $content, false);
273 }
274
275 /**
276 * Get virtual robots.txt content
277 *
278 * @return string|false Content or false if not found
279 */
280 public function get_virtual_content()
281 {
282 return get_option('metasync_robots_txt_virtual', false);
283 }
284
285 /**
286 * Check if virtual mode is active
287 *
288 * @return bool True if virtual mode is active
289 */
290 public function is_virtual_mode()
291 {
292 return (bool) get_option('metasync_robots_txt_virtual_mode', false);
293 }
294
295 /**
296 * Set virtual mode flag
297 *
298 * @param bool $enabled Whether virtual mode is enabled
299 * @return bool True on success
300 */
301 public function set_virtual_mode($enabled)
302 {
303 return update_option('metasync_robots_txt_virtual_mode', $enabled, false);
304 }
305
306 /**
307 * Clear virtual content and mode
308 *
309 * @return bool True on success
310 */
311 public function clear_virtual_content()
312 {
313 delete_option('metasync_robots_txt_virtual');
314 delete_option('metasync_robots_txt_virtual_mode');
315 return true;
316 }
317 }
318