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 / admin / js / metasync-excluded-urls.js

metasync-excluded-urls.js in Search Atlas SEO – OTTO AI SEO Automation for WordPress 2.6.22, at admin/js/metasync-excluded-urls.js

317 lines 12.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /* global metasyncExcludedUrlsData, jQuery */
2 /**
3 * MetaSync OTTO Excluded URLs Management
4 *
5 * Extracted for Phase 5, #887.
6 * Handles adding, listing, rechecking, and deleting excluded URLs.
7 *
8 * Localized object: metasyncExcludedUrlsData
9 * - ajaxUrl (string)
10 * - nonce (string)
11 *
12 * @since Phase 5
13 */
14 (function () {
15 if (typeof jQuery === 'undefined') {
16 setTimeout(arguments.callee, 100);
17 return;
18 }
19
20 jQuery(document).ready(function ($) {
21 var currentPage = 1;
22 var perPage = 10;
23 var ajaxUrl = metasyncExcludedUrlsData.ajaxUrl;
24 var nonce = metasyncExcludedUrlsData.nonce;
25
26 // Load excluded URLs on page load
27 loadExcludedURLs();
28
29 // Add excluded URL button click
30 $('#otto-add-excluded-url-btn').on('click', function (e) {
31 e.preventDefault();
32 addExcludedURL();
33 });
34
35 // Handle Enter key in URL pattern input
36 $('#otto-url-pattern').on('keypress', function (e) {
37 if (e.which === 13) {
38 e.preventDefault();
39 addExcludedURL();
40 }
41 });
42
43 function addExcludedURL() {
44 var $button = $('#otto-add-excluded-url-btn');
45 var $status = $('#otto-add-status');
46 var urlPattern = $('#otto-url-pattern').val().trim();
47 var patternType = $('#otto-pattern-type').val();
48 var description = $('#otto-description').val().trim();
49
50 // Validate inputs
51 if (!urlPattern) {
52 showStatus('error', 'URL pattern is required');
53 return;
54 }
55
56 // Disable button and show loading
57 $button.prop('disabled', true).text('Adding...');
58 $status.hide();
59
60 $.ajax({
61 url: ajaxUrl,
62 type: 'POST',
63 data: {
64 action: 'metasync_otto_add_excluded_url',
65 nonce: nonce,
66 url_pattern: urlPattern,
67 pattern_type: patternType,
68 description: description
69 },
70 success: function (response) {
71 if (response.success) {
72 showStatus('success', response.data.message || 'URL excluded successfully');
73 // Clear form
74 $('#otto-url-pattern').val('');
75 $('#otto-description').val('');
76 $('#otto-pattern-type').val('exact');
77 // Reload list
78 loadExcludedURLs();
79 } else {
80 showStatus('error', response.data.message || 'Failed to add excluded URL');
81 }
82 },
83 error: function () {
84 showStatus('error', 'An error occurred. Please try again.');
85 },
86 complete: function () {
87 $button.prop('disabled', false).text('\u2795 Add Excluded URL');
88 }
89 });
90 }
91
92 function loadExcludedURLs(page) {
93 page = page || currentPage;
94 var $container = $('#otto-excluded-urls-table-container');
95
96 $container.html('<div style="text-align: center; padding: 40px; color: #6c757d;"><p>Loading...</p></div>');
97
98 $.ajax({
99 url: ajaxUrl,
100 type: 'POST',
101 data: {
102 action: 'metasync_otto_get_excluded_urls',
103 nonce: nonce,
104 page: page,
105 per_page: perPage
106 },
107 success: function (response) {
108 if (response.success) {
109 currentPage = page;
110 renderExcludedURLsTable(response.data.records, response.data.pagination);
111 } else {
112 $container.html('<div style="text-align: center; padding: 40px; color: #dc3545;"><p>Error loading excluded URLs</p></div>');
113 }
114 },
115 error: function () {
116 $container.html('<div style="text-align: center; padding: 40px; color: #dc3545;"><p>Failed to load excluded URLs</p></div>');
117 }
118 });
119 }
120
121 function renderExcludedURLsTable(records, pagination) {
122 var $container = $('#otto-excluded-urls-table-container');
123
124 if (!records || records.length === 0) {
125 $container.html('<div style="text-align: center; padding: 40px; color: #6c757d;"><p>No excluded URLs found. Add one above to get started.</p></div>');
126 return;
127 }
128
129 var html = '<table class="wp-list-table widefat fixed striped" style="margin-top: 10px;">';
130 html += '<thead><tr>';
131 html += '<th style="width: 30%;">URL Pattern</th>';
132 html += '<th style="width: 12%;">Match Type</th>';
133 html += '<th style="width: 25%;">Description</th>';
134 html += '<th style="width: 13%;">Recheck After</th>';
135 html += '<th style="width: 20%;">Actions</th>';
136 html += '</tr></thead><tbody>';
137
138 records.forEach(function (record) {
139 html += '<tr>';
140 html += '<td><code>' + escapeHtml(record.url_pattern) + '</code></td>';
141 html += '<td><span class="otto-pattern-type-badge otto-pattern-' + record.pattern_type + '">' + formatPatternType(record.pattern_type) + '</span></td>';
142 html += '<td>' + (record.description ? escapeHtml(record.description) : '<span style="color: #999;">\u2014</span>') + '</td>';
143 html += '<td>' + formatRecheckAfter(record) + '</td>';
144 html += '<td><span class="otto-actions">';
145 html += '<button type="button" class="button button-small otto-recheck-url" data-id="' + record.id + '" style="margin-right: 5px;">\uD83D\uDD04 Recheck</button>';
146 html += '<button type="button" class="button button-small otto-delete-url" data-id="' + record.id + '" style="color: #dc3545;">\uD83D\uDDD1\uFE0F Delete</button>';
147 html += '</span></td>';
148 html += '</tr>';
149 });
150
151 html += '</tbody></table>';
152
153 // Add pagination
154 if (pagination.total_pages > 1) {
155 html += '<div class="tablenav" style="margin-top: 15px; display: flex; justify-content: space-between; align-items: center;">';
156 html += '<div class="tablenav-pages">';
157 html += '<span class="displaying-num">' + pagination.total_count + ' items</span>';
158
159 html += '<span class="pagination-links">';
160
161 // First page
162 if (pagination.current_page > 1) {
163 html += '<a class="button otto-page-nav" data-page="1" href="#">\u00AB</a> ';
164 html += '<a class="button otto-page-nav" data-page="' + (pagination.current_page - 1) + '" href="#">\u2039</a> ';
165 } else {
166 html += '<span class="button disabled">\u00AB</span> ';
167 html += '<span class="button disabled">\u2039</span> ';
168 }
169
170 html += '<span class="paging-input">';
171 html += '<span class="tablenav-paging-text">' + pagination.current_page + ' of <span class="total-pages">' + pagination.total_pages + '</span></span>';
172 html += '</span> ';
173
174 // Next/Last page
175 if (pagination.current_page < pagination.total_pages) {
176 html += '<a class="button otto-page-nav" data-page="' + (pagination.current_page + 1) + '" href="#">\u203A</a> ';
177 html += '<a class="button otto-page-nav" data-page="' + pagination.total_pages + '" href="#">\u00BB</a>';
178 } else {
179 html += '<span class="button disabled">\u203A</span> ';
180 html += '<span class="button disabled">\u00BB</span>';
181 }
182
183 html += '</span></div></div>';
184 }
185
186 $container.html(html);
187
188 // Attach delete button handlers
189 $('.otto-delete-url').on('click', function (e) {
190 e.preventDefault();
191 var id = $(this).data('id');
192 deleteExcludedURL(id);
193 });
194
195 // Attach recheck button handlers
196 $('.otto-recheck-url').on('click', function (e) {
197 e.preventDefault();
198 var $btn = $(this);
199 var id = $btn.data('id');
200 recheckExcludedURL(id, $btn);
201 });
202
203 // Attach pagination handlers
204 $('.otto-page-nav').on('click', function (e) {
205 e.preventDefault();
206 var page = $(this).data('page');
207 loadExcludedURLs(page);
208 });
209 }
210
211 function recheckExcludedURL(id, $btn) {
212 var originalText = $btn.text();
213 $btn.prop('disabled', true).text('Checking...');
214
215 $.ajax({
216 url: ajaxUrl,
217 type: 'POST',
218 data: {
219 action: 'metasync_otto_recheck_excluded_url',
220 nonce: nonce,
221 id: id
222 },
223 success: function (response) {
224 $btn.prop('disabled', false).text(originalText);
225 if (response.success) {
226 if (response.data.available) {
227 if (confirm('This URL is now available. Do you want to remove it from the excluded list?')) {
228 deleteExcludedURL(id);
229 }
230 } else {
231 alert('This URL is still returning 404.');
232 }
233 } else {
234 alert('Error: ' + (response.data.message || 'Recheck failed'));
235 }
236 },
237 error: function (xhr, status, err) {
238 $btn.prop('disabled', false).text(originalText);
239 alert('An error occurred while rechecking. Please try again.');
240 }
241 });
242 }
243
244 function deleteExcludedURL(id) {
245 if (!confirm('Are you sure you want to delete this excluded URL?')) {
246 return;
247 }
248
249 $.ajax({
250 url: ajaxUrl,
251 type: 'POST',
252 data: {
253 action: 'metasync_otto_delete_excluded_url',
254 nonce: nonce,
255 id: id
256 },
257 success: function (response) {
258 if (response.success) {
259 loadExcludedURLs();
260 } else {
261 alert('Error: ' + (response.data.message || 'Failed to delete'));
262 }
263 },
264 error: function () {
265 alert('An error occurred. Please try again.');
266 }
267 });
268 }
269
270 function showStatus(type, message) {
271 var $status = $('#otto-add-status');
272 $status.removeClass('otto-status-success otto-status-error');
273 $status.addClass('otto-status-' + type);
274 $status.text(message).fadeIn();
275
276 setTimeout(function () {
277 $status.fadeOut();
278 }, 5000);
279 }
280
281 function formatPatternType(type) {
282 var types = {
283 'exact': 'Exact',
284 'contain': 'Contains',
285 'start': 'Starts With',
286 'end': 'Ends With',
287 'regex': 'Regex'
288 };
289 return types[type] || type;
290 }
291
292 function formatRecheckAfter(record) {
293 if (!record.auto_excluded || !record.recheck_after) {
294 return '<span style="color: #999;">NA</span>';
295 }
296 var d = new Date(record.recheck_after.replace(' ', 'T'));
297 if (isNaN(d.getTime())) {
298 return escapeHtml(record.recheck_after);
299 }
300 var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
301 return months[d.getMonth()] + ' ' + d.getDate() + ', ' + d.getFullYear();
302 }
303
304 function escapeHtml(text) {
305 if (!text) return '';
306 var map = {
307 '&': '&amp;',
308 '<': '&lt;',
309 '>': '&gt;',
310 '"': '&quot;',
311 "'": '&#039;'
312 };
313 return String(text).replace(/[&<>"']/g, function (m) { return map[m]; });
314 }
315 });
316 })();
317