Changeset 3717902
- Timestamp:
- 09/28/2026 08:49:50 PM (3 weeks ago)
- Location:
- likoton-debug-logs/trunk
- Files:
-
- 26 edited
-
assets/admin-dark.css (modified) (13 diffs)
-
assets/admin.css (modified) (4 diffs)
-
assets/admin.js (modified) (6 diffs)
-
includes/class-likoton-debug-logs-admin.php (modified) (18 diffs)
-
includes/class-likoton-debug-logs-assets.php (modified) (1 diff)
-
includes/class-likoton-debug-logs-installer.php (modified) (3 diffs)
-
includes/class-likoton-debug-logs-source-colors.php (modified) (2 diffs)
-
languages/likoton-debug-logs-cs_CZ.mo (modified) ( previous)
-
languages/likoton-debug-logs-cs_CZ.po (modified) (1 diff)
-
languages/likoton-debug-logs-de_DE.mo (modified) ( previous)
-
languages/likoton-debug-logs-de_DE.po (modified) (1 diff)
-
languages/likoton-debug-logs-en_US.mo (modified) ( previous)
-
languages/likoton-debug-logs-en_US.po (modified) (1 diff)
-
languages/likoton-debug-logs-es_ES.mo (modified) ( previous)
-
languages/likoton-debug-logs-es_ES.po (modified) (1 diff)
-
languages/likoton-debug-logs-fr_FR.mo (modified) ( previous)
-
languages/likoton-debug-logs-fr_FR.po (modified) (1 diff)
-
languages/likoton-debug-logs-pl_PL.mo (modified) ( previous)
-
languages/likoton-debug-logs-pl_PL.po (modified) (1 diff)
-
languages/likoton-debug-logs-pt_PT.mo (modified) ( previous)
-
languages/likoton-debug-logs-pt_PT.po (modified) (1 diff)
-
languages/likoton-debug-logs-sk_SK.mo (modified) ( previous)
-
languages/likoton-debug-logs-sk_SK.po (modified) (1 diff)
-
languages/likoton-debug-logs.pot (modified) (1 diff)
-
likoton-debug-logs.php (modified) (1 diff)
-
readme.txt (modified) (6 diffs)
Legend:
- Unmodified
- Added
- Removed
-
likoton-debug-logs/trunk/assets/admin-dark.css
r3600839 r3717902 1 /* ------------------------------------------------------------- 2 * Dark mode background 3 * ------------------------------------------------------------- */ 1 /* Dark mode background */ 4 2 5 3 body.toplevel_page_likoton-debug-logs-logs, … … 9 7 } 10 8 11 /* ------------------------------------------------------------- 12 * Table styling 13 * ------------------------------------------------------------- */ 9 /* Table styling */ 14 10 15 11 .ldl-logs.wp-list-table { … … 36 32 } 37 33 38 /* ------------------------------------------------------------- 39 * Column widths 40 * ------------------------------------------------------------- */ 34 /* Column widths */ 41 35 .ldl-logs .column-id { 42 36 width: 60px; … … 62 56 } 63 57 64 /* ------------------------------------------------------------- 65 * Selects and inputs 66 * ------------------------------------------------------------- */ 58 /* Selects and inputs */ 67 59 68 60 select, … … 85 77 input[type="search"]:focus, 86 78 input[type="text"]:focus, 87 input[type="number"]:focus {79 input[type="number"]:focus { 88 80 border-color: #2271b1 !important; 89 81 box-shadow: 0 0 0 1px #2271b1 !important; 82 } 83 84 input.ldl-date-input:focus { 85 border-color: #2271b1 !important; 86 box-shadow: none !important; 87 outline: none !important; 88 } 89 90 input.ldl-date-input.is-active { 91 border-color: #2271b1 !important; 92 box-shadow: 0 0 0 1px #2271b1 !important; 93 } 94 95 .ldl-date-trigger { 96 color: #aaa !important; 97 } 98 99 .ldl-datepicker { 100 background-color: #1d2327 !important; 101 border-color: #2c3338 !important; 102 color: #f0f0f1 !important; 103 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5) !important; 104 } 105 106 .ldl-datepicker-footer button { 107 color: #2271b1; 108 } 109 110 .ldl-datepicker-day.is-selected { 111 background: #2271b1; 112 } 113 114 .ldl-datepicker-day.is-today { 115 box-shadow: inset 0 0 0 1px #2271b1; 116 } 117 118 .ldl-datepicker-nav { 119 color: #f0f0f1 !important; 120 } 121 122 .ldl-datepicker-nav:hover, 123 .ldl-datepicker-day:hover:not(:disabled) { 124 background-color: #2c3338 !important; 125 } 126 127 .ldl-datepicker-weekdays span { 128 color: #999 !important; 129 } 130 131 .ldl-datepicker-day:disabled { 132 color: #555 !important; 133 } 134 135 .ldl-datepicker-footer { 136 border-top-color: #2c3338 !important; 90 137 } 91 138 … … 107 154 } 108 155 109 /* ------------------------------------------------------------- 110 * Buttons 111 * ------------------------------------------------------------- */ 156 /* Buttons */ 112 157 113 158 .button, … … 118 163 } 119 164 120 /* ------------------------------------------------------------- 121 * Footer 122 * ------------------------------------------------------------- */ 165 /* Footer */ 123 166 124 167 .ldl-footer img { … … 136 179 } 137 180 138 /* ------------------------------------------------------------- 139 * Dark mode sorting indicators 140 * ------------------------------------------------------------- */ 181 /* Dark mode sorting indicators */ 141 182 142 183 #ldl-logs-table th.sorted-column { … … 149 190 } 150 191 151 /* ------------------------------------------------------------- 152 * Dark mode text 153 * ------------------------------------------------------------- */ 192 /* Dark mode text */ 154 193 .form-table th, h1, h2, h3 { 155 194 color: #f0f0f1; 156 195 } 157 196 158 /* ------------------------------------------------------------- 159 * WP7 NAV — Dark Mode 160 * ------------------------------------------------------------- */ 197 /* WP7 NAV — Dark Mode */ 161 198 162 199 .health-check-tabs-wrapper { … … 183 220 } 184 221 185 /* ------------------------------------------------------------- 186 * Auto save toast 187 * ------------------------------------------------------------- */ 222 /* Auto save toast */ 188 223 189 224 .ldl-toast { … … 191 226 } 192 227 193 /* ------------------------------------------------------------- 194 * Donation page — Dark Mode 195 * ------------------------------------------------------------- */ 228 /* Donation page — Dark Mode */ 196 229 197 230 body.likoton-debug-logs-logs_page_likoton-debug-logs-donation, … … 215 248 .ldl-donation-box img { 216 249 height: 32px; 217 filter: brightness(9); /* delikatne przyciemnienie dla lepszego kontrastu */250 filter: brightness(9); 218 251 transition: filter 0.2s ease; 219 252 } … … 228 261 } 229 262 230 /* ------------------------------------------------------------- 231 * Segmented Control 232 * ------------------------------------------------------------- */ 263 /* Segmented Control */ 233 264 234 265 .ldl-segment { -
likoton-debug-logs/trunk/assets/admin.css
r3647996 r3717902 72 72 padding: 0 8px; 73 73 box-sizing: border-box; 74 } 75 76 /* Date range filter */ 77 78 .ldl-date-range { 79 display: none; 80 align-items: center; 81 gap: 8px; 82 } 83 84 .ldl-date-range.is-visible { 85 display: flex; 86 } 87 88 .ldl-date-range label { 89 font-size: 12px; 90 white-space: nowrap; 91 } 92 93 .ldl-date-field { 94 position: relative; 95 display: inline-flex; 96 align-items: center; 97 } 98 99 .ldl-date-input { 100 height: 32px; 101 padding: 0 30px 0 8px; 102 border: 1px solid #ccc; 103 border-radius: 2px; 104 box-sizing: border-box; 105 background-color: #fff !important; 106 cursor: pointer; 107 width: 120px; 108 caret-color: transparent; 109 user-select: none; 110 } 111 112 input.ldl-date-input:focus { 113 border-color: #ccc !important; 114 box-shadow: none !important; 115 outline: none !important; 116 } 117 118 input.ldl-date-input.is-active { 119 border-color: var(--wp-admin-theme-color) !important; 120 box-shadow: 0 0 0 var(--wp-admin-border-width-focus, 1.5px) var(--wp-admin-theme-color) !important; 121 } 122 123 .ldl-date-trigger { 124 position: absolute; 125 right: 2px; 126 top: 50%; 127 transform: translateY(-50%); 128 background: none; 129 border: none; 130 cursor: pointer; 131 padding: 4px; 132 display: flex; 133 align-items: center; 134 color: #555; 135 } 136 137 .ldl-date-trigger .dashicons { 138 font-size: 18px; 139 width: 18px; 140 height: 18px; 141 } 142 143 /* Custom date picker popup */ 144 145 .ldl-datepicker { 146 display: none; 147 position: absolute; 148 z-index: 100000; 149 top: 100%; 150 left: 0; 151 margin-top: 4px; 152 background: #fff; 153 border: 1px solid #ccc; 154 border-radius: 4px; 155 box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); 156 padding: 10px; 157 width: 240px; 158 } 159 160 .ldl-datepicker.is-open { 161 display: block; 162 } 163 164 .ldl-datepicker-header { 165 display: flex; 166 align-items: center; 167 justify-content: space-between; 168 margin-bottom: 8px; 169 } 170 171 .ldl-datepicker-title { 172 font-weight: 600; 173 text-transform: capitalize; 174 font-size: 13px; 175 } 176 177 .ldl-datepicker-nav { 178 background: none; 179 border: none; 180 cursor: pointer; 181 font-size: 16px; 182 line-height: 1; 183 padding: 2px 8px; 184 color: #333; 185 border-radius: 2px; 186 } 187 188 .ldl-datepicker-nav:hover { 189 background: #f0f0f1; 190 } 191 192 .ldl-datepicker-weekdays, 193 .ldl-datepicker-days { 194 display: grid; 195 grid-template-columns: repeat(7, 1fr); 196 gap: 2px; 197 text-align: center; 198 } 199 200 .ldl-datepicker-weekdays span { 201 font-size: 11px; 202 color: #777; 203 text-transform: capitalize; 204 padding: 4px 0; 205 } 206 207 .ldl-datepicker-day { 208 background: none; 209 border: none; 210 padding: 6px 0; 211 font-size: 12px; 212 border-radius: 3px; 213 cursor: pointer; 214 color: inherit; 215 } 216 217 .ldl-datepicker-day:hover:not(:disabled) { 218 background: #f0f0f1; 219 } 220 221 .ldl-datepicker-day.is-empty { 222 cursor: default; 223 } 224 225 .ldl-datepicker-day.is-today { 226 box-shadow: inset 0 0 0 1px var(--wp-admin-theme-color); 227 } 228 229 .ldl-datepicker-day.is-selected { 230 background:var(--wp-admin-theme-color-darker-10); 231 color: #fff; 232 } 233 234 .ldl-datepicker-day:disabled { 235 color: #ccc; 236 cursor: not-allowed; 237 } 238 239 .ldl-datepicker-footer { 240 display: flex; 241 justify-content: space-between; 242 margin-top: 8px; 243 padding-top: 8px; 244 border-top: 1px solid #eee; 245 } 246 247 .ldl-datepicker-footer button { 248 background: none; 249 border: none; 250 color: var(--wp-admin-theme-color); 251 cursor: pointer; 252 font-size: 12px; 253 font-weight:600; 254 padding: 0; 255 } 256 257 .ldl-datepicker-footer button:hover { 258 text-decoration: underline; 74 259 } 75 260 … … 120 305 121 306 #ldl-logs-table { 122 border -radius: 2px;307 border:none; 123 308 min-width: 600px; 124 309 border-collapse: collapse; … … 222 407 .ldl-actions { 223 408 display: flex; 409 flex-direction: row; 410 align-items: stretch; 224 411 gap: 10px; 225 412 margin-top: 15px; … … 431 618 width: 100%; 432 619 } 433 434 .ldl-actions { 620 621 .ldl-date-range.is-visible { 622 flex-direction: column; 623 align-items: stretch; 624 width: 100%; 625 } 626 627 .ldl-date-field, 628 .ldl-date-input { 629 width: 100%; 630 } 631 632 .ldl-actions { 435 633 flex-direction: column; 436 634 align-items: stretch; 437 635 } 438 636 637 .ldl-datepicker { 638 left: auto; 639 right: 0; 640 } 641 439 642 .ldl-actions button { 440 643 width: 100%; -
likoton-debug-logs/trunk/assets/admin.js
r3647996 r3717902 53 53 const $filters = $('#ldl-filters'); 54 54 55 let scrollPage = 1; 56 let scrollLoading = false; 57 let scrollDone = false; 58 55 59 function reloadTable() { 56 60 const url = $filters.attr('action'); … … 63 67 applyDefaultDateSort(); 64 68 } 69 scrollPage = 1; 70 scrollDone = false; 65 71 }); 66 72 } … … 74 80 $filters.on('change', 'select', function () { 75 81 reloadTable(); 82 }); 83 84 const $dateRange = $('#ldl-date-range'); 85 const $dateFrom = $('#ldl-date-from'); 86 const $dateTo = $('#ldl-date-to'); 87 88 function toggleDateRange() { 89 $dateRange.toggleClass('is-visible', $filters.find('select[name="last"]').val() === 'range'); 90 } 91 92 function syncDateConstraints() { 93 if ($dateFrom.val()) { 94 $dateTo.attr('data-min', $dateFrom.val()); 95 } 96 if ($dateTo.val()) { 97 $dateFrom.attr('data-max', $dateTo.val()); 98 } 99 } 100 101 toggleDateRange(); 102 syncDateConstraints(); 103 104 $filters.on('change', 'select[name="last"]', toggleDateRange); 105 106 $filters.on('change', '.ldl-date-input', function () { 107 syncDateConstraints(); 108 reloadTable(); 109 }); 110 111 initDatePicker(); 112 } 113 114 function initDatePicker() { 115 const locale = (window.likotonDebugLogsData && likotonDebugLogsData.locale) || 'en-US'; 116 const i18n = (window.likotonDebugLogsData && likotonDebugLogsData.i18n) || {}; 117 const labelToday = i18n.today || 'Today'; 118 const labelClear = i18n.clear || 'Clear'; 119 120 const $panel = $( 121 '<div class="ldl-datepicker">' + 122 '<div class="ldl-datepicker-header">' + 123 '<button type="button" class="ldl-datepicker-nav ldl-datepicker-prev">‹</button>' + 124 '<span class="ldl-datepicker-title"></span>' + 125 '<button type="button" class="ldl-datepicker-nav ldl-datepicker-next">›</button>' + 126 '</div>' + 127 '<div class="ldl-datepicker-weekdays"></div>' + 128 '<div class="ldl-datepicker-days"></div>' + 129 '<div class="ldl-datepicker-footer">' + 130 '<button type="button" class="ldl-datepicker-clear"></button>' + 131 '<button type="button" class="ldl-datepicker-today"></button>' + 132 '</div>' + 133 '</div>' 134 ); 135 136 $panel.find('.ldl-datepicker-clear').text(labelClear); 137 $panel.find('.ldl-datepicker-today').text(labelToday); 138 139 let $activeInput = null; 140 let viewYear; 141 let viewMonth; 142 143 function pad(n) { 144 return (n < 10 ? '0' : '') + n; 145 } 146 147 function toISO(y, m, d) { 148 return y + '-' + pad(m + 1) + '-' + pad(d); 149 } 150 151 function parseISO(str) { 152 const parts = /^(\d{4})-(\d{2})-(\d{2})$/.exec(str || ''); 153 if (!parts) { 154 return null; 155 } 156 return { y: parseInt(parts[1], 10), m: parseInt(parts[2], 10) - 1, d: parseInt(parts[3], 10) }; 157 } 158 159 function weekdayNames() { 160 const fmt = new Intl.DateTimeFormat(locale, { weekday: 'short', timeZone: 'UTC' }); 161 const names = []; 162 const base = Date.UTC(2023, 0, 2); // A Monday. 163 for (let i = 0; i < 7; i++) { 164 names.push(fmt.format(new Date(base + i * 86400000))); 165 } 166 return names; 167 } 168 169 function monthTitle(y, m) { 170 const fmt = new Intl.DateTimeFormat(locale, { month: 'long', year: 'numeric' }); 171 return fmt.format(new Date(y, m, 1)); 172 } 173 174 function render() { 175 $panel.find('.ldl-datepicker-title').text(monthTitle(viewYear, viewMonth)); 176 177 const $wd = $panel.find('.ldl-datepicker-weekdays').empty(); 178 weekdayNames().forEach(function (n) { 179 $('<span></span>').text(n).appendTo($wd); 180 }); 181 182 const $days = $panel.find('.ldl-datepicker-days').empty(); 183 184 const firstOfMonth = new Date(viewYear, viewMonth, 1); 185 const startOffset = (firstOfMonth.getDay() + 6) % 7; // Monday = 0. 186 const daysInMonth = new Date(viewYear, viewMonth + 1, 0).getDate(); 187 188 const minAttr = $activeInput.attr('data-min') || ''; 189 const maxAttr = $activeInput.attr('data-max') || ''; 190 const selectedISO = $activeInput.val(); 191 const now = new Date(); 192 const todayISO = toISO(now.getFullYear(), now.getMonth(), now.getDate()); 193 194 for (let i = 0; i < startOffset; i++) { 195 $('<span class="ldl-datepicker-day is-empty"></span>').appendTo($days); 196 } 197 198 for (let d = 1; d <= daysInMonth; d++) { 199 const iso = toISO(viewYear, viewMonth, d); 200 const disabled = (minAttr && iso < minAttr) || (maxAttr && iso > maxAttr); 201 const $btn = $('<button type="button" class="ldl-datepicker-day"></button>') 202 .text(d) 203 .attr('data-date', iso) 204 .prop('disabled', disabled); 205 206 if (iso === todayISO) { 207 $btn.addClass('is-today'); 208 } 209 if (iso === selectedISO) { 210 $btn.addClass('is-selected'); 211 } 212 213 $btn.appendTo($days); 214 } 215 } 216 217 function open($input) { 218 if ($activeInput) { 219 $activeInput.removeClass('is-active'); 220 } 221 $activeInput = $input; 222 const parsed = parseISO($input.val()) || (function () { 223 const now = new Date(); 224 return { y: now.getFullYear(), m: now.getMonth(), d: now.getDate() }; 225 }()); 226 viewYear = parsed.y; 227 viewMonth = parsed.m; 228 render(); 229 230 const $field = $input.closest('.ldl-date-field'); 231 $field.append($panel.detach()); 232 $panel.addClass('is-open'); 233 $input.addClass('is-active'); 234 } 235 236 function close() { 237 $panel.removeClass('is-open'); 238 if ($activeInput) { 239 $activeInput.removeClass('is-active'); 240 } 241 $activeInput = null; 242 } 243 244 $(document).on('mousedown', '.ldl-date-input', function (e) { 245 e.preventDefault(); 246 }); 247 248 $(document).on('click', '.ldl-date-trigger, .ldl-date-input', function (e) { 249 e.preventDefault(); 250 if (document.activeElement && document.activeElement !== document.body) { 251 document.activeElement.blur(); 252 } 253 const $field = $(this).closest('.ldl-date-field'); 254 const $input = $field.find('.ldl-date-input'); 255 if ($activeInput && $activeInput[0] === $input[0] && $panel.hasClass('is-open')) { 256 close(); 257 } else { 258 open($input); 259 } 260 }); 261 262 $(document).on('click', function (e) { 263 if ($panel.hasClass('is-open') && !$(e.target).closest('.ldl-datepicker, .ldl-date-field').length) { 264 close(); 265 } 266 }); 267 268 $(document).on('keydown', function (e) { 269 if (e.key === 'Escape' && $panel.hasClass('is-open')) { 270 close(); 271 } 272 }); 273 274 $panel.on('click', '.ldl-datepicker-prev', function () { 275 viewMonth--; 276 if (viewMonth < 0) { 277 viewMonth = 11; 278 viewYear--; 279 } 280 render(); 281 }); 282 283 $panel.on('click', '.ldl-datepicker-next', function () { 284 viewMonth++; 285 if (viewMonth > 11) { 286 viewMonth = 0; 287 viewYear++; 288 } 289 render(); 290 }); 291 292 $panel.on('click', '.ldl-datepicker-day:not(:disabled):not(.is-empty)', function () { 293 const iso = $(this).attr('data-date'); 294 $activeInput.val(iso).trigger('change'); 295 close(); 296 }); 297 298 $panel.on('click', '.ldl-datepicker-today', function () { 299 const now = new Date(); 300 const iso = toISO(now.getFullYear(), now.getMonth(), now.getDate()); 301 const minAttr = $activeInput.attr('data-min') || ''; 302 const maxAttr = $activeInput.attr('data-max') || ''; 303 if ((minAttr && iso < minAttr) || (maxAttr && iso > maxAttr)) { 304 return; 305 } 306 $activeInput.val(iso).trigger('change'); 307 close(); 308 }); 309 310 $panel.on('click', '.ldl-datepicker-clear', function () { 311 const isFrom = $activeInput.attr('id') === 'ldl-date-from'; 312 const $from = $('#ldl-date-from'); 313 const $to = $('#ldl-date-to'); 314 315 const now = new Date(); 316 const today = toISO(now.getFullYear(), now.getMonth(), now.getDate()); 317 const yestDate = new Date(now); 318 yestDate.setDate(now.getDate() - 1); 319 const yesterday = toISO(yestDate.getFullYear(), yestDate.getMonth(), yestDate.getDate()); 320 321 if (isFrom) { 322 $from.val(yesterday); 323 if ($to.val() && $to.val() < yesterday) { 324 $to.val(today); 325 } 326 } else { 327 $to.val(today); 328 if ($from.val() && $from.val() > today) { 329 $from.val(yesterday); 330 } 331 } 332 333 $activeInput.trigger('change'); 334 close(); 76 335 }); 77 336 } … … 180 439 }); 181 440 182 let page = 1;183 let loading = false;184 let done = false;185 186 441 const $loader = $('<div class="ldl-scroll-loader">Loading…</div>').css({ 187 442 padding: '15px', … … 194 449 $wrapper.append($loader); 195 450 451 function filterValue(name) { 452 const found = $filters.serializeArray().find(function (item) { 453 return item.name === name; 454 }); 455 return found ? found.value : ''; 456 } 457 196 458 function loadMore() { 197 if ( loading || done) return;459 if (scrollLoading || scrollDone) return; 198 460 const $tableBody = $('#ldl-logs-table tbody'); 199 461 if (!$tableBody.length) return; 200 loading = true;462 scrollLoading = true; 201 463 $loader.css('opacity', 1); 202 const params = new URLSearchParams(window.location.search);203 464 $.get(ajaxUrl, { 204 465 action: 'likoton_debug_logs_load_more_logs', 205 page_num: page + 1,206 s: params.get('s') || '',207 level: params.get('level') || '',208 source: params.get('source') || '',209 last: params.get('last') || 50,466 page_num: scrollPage + 1, 467 s: filterValue('s'), 468 level: filterValue('level'), 469 source: filterValue('source'), 470 last: filterValue('last') || 50, 210 471 likoton_debug_logs_nonce: likotonDebugLogsData.nonce 211 472 }, function (response) { 212 473 if (response.success) { 213 474 if (response.data.done) { 214 done = true;475 scrollDone = true; 215 476 $loader.text('✔ All logs loaded'); 216 477 return; 217 478 } 218 page++;479 scrollPage++; 219 480 $tableBody.append(response.data.html); 220 481 } 221 482 }).always(function () { 222 loading = false;483 scrollLoading = false; 223 484 $loader.css('opacity', 0); 224 485 }); … … 243 504 data.push({ name: 'last', value: 50 }); 244 505 } 506 507 data.push({ name: 'auto_refresh', value: '1' }); 245 508 246 509 const url = $filters.attr('action'); -
likoton-debug-logs/trunk/includes/class-likoton-debug-logs-admin.php
r3647996 r3717902 6 6 } 7 7 8 /** 9 * Admin interface for LiKoToN Debug Logs 10 */ 8 /* Admin interface for LiKoToN Debug Logs */ 11 9 class Likoton_Debug_Logs_Admin { 12 10 … … 26 24 } 27 25 28 /** 29 * Levels 30 */ 26 /* Levels */ 31 27 private static function get_all_levels() { 32 28 return [ … … 39 35 } 40 36 41 /** 42 * Schedules 43 */ 37 /* Schedules */ 44 38 public static function add_schedules( $schedules ) { 45 39 $schedules['likoton_debug_logs_every_30_minutes'] = [ … … 56 50 } 57 51 58 /** 59 * Auto-save 60 */ 52 /* Auto-save */ 61 53 public static function ajax_save_settings() { 62 54 check_ajax_referer( 'likoton_debug_logs_save_settings', 'likoton_debug_logs_nonce' ); … … 117 109 $last_raw = isset( $_GET['last'] ) ? sanitize_text_field( wp_unslash( $_GET['last'] ) ) : 50; 118 110 119 if ( $last_raw === 'all' ) {111 if ( $last_raw === 'all' || $last_raw === 'range' ) { 120 112 wp_send_json_success( [ 'html' => '', 'done' => true ] ); 121 113 } 122 114 123 $per_page = (int) $last_raw; 124 if ( $per_page <= 0 ) { 125 $per_page = 50; 115 $time_windowed = in_array( $last_raw, [ 'hour', 'today', 'week' ], true ); 116 117 if ( $time_windowed ) { 118 $per_page = 100; 119 } else { 120 $per_page = (int) $last_raw; 121 if ( $per_page <= 0 ) { 122 $per_page = 50; 123 } 126 124 } 127 125 … … 132 130 'page' => $page, 133 131 'per_page' => $per_page, 132 'since' => $time_windowed ? self::compute_since( $last_raw ) : '', 134 133 ] ); 135 134 … … 156 155 } 157 156 158 /** 159 * Automatic log cleanup 160 */ 157 /* Automatic log cleanup */ 161 158 162 159 public static function cleanup_logs() { … … 191 188 } 192 189 193 /** 194 * Register admin menu 195 */ 190 /* Register admin menu */ 196 191 public static function menu() { 197 192 $capability = get_option( 'likoton_debug_logs_capability', 'manage_options' ); … … 239 234 } 240 235 241 /** 242 * Header 243 */ 236 /* Header */ 244 237 public static function render_header() { 245 238 ?> … … 248 241 } 249 242 250 /** 251 * Render Logs page 252 */ 243 /* Render Logs page */ 253 244 public static function render_logs_page() { 254 245 ?> … … 270 261 } 271 262 272 /** 273 * Render Settings page 274 */ 263 /* Render Settings page */ 275 264 public static function render_settings_page() { 276 265 … … 441 430 } 442 431 443 /** 444 * Render tabs navigation 445 */ 432 /* Render tabs navigation */ 446 433 private static function render_tabs( $active = 'logs' ) { 447 434 ?> … … 471 458 } 472 459 473 /** 474 * Format log level 475 */ 460 /* Format log level */ 476 461 private static function format_level( $level ) { 477 462 return ucwords( str_replace( '_', ' ', $level ) ); 478 463 } 479 464 480 /** 481 * Format UTC date to local WordPress time 482 */ 465 /* Compute the UTC datetime lower bound for the "hour" / "today" / "week" quick filters. */ 466 private static function compute_since( $last_raw ) { 467 $now = current_datetime(); 468 469 switch ( $last_raw ) { 470 case 'hour': 471 $since = $now->modify( '-1 hour' ); 472 break; 473 474 case 'today': 475 $since = $now->setTime( 0, 0, 0 ); 476 break; 477 478 case 'week': 479 $since = $now->modify( '-7 days' ); 480 break; 481 482 default: 483 return ''; 484 } 485 486 return $since->setTimezone( new \DateTimeZone( 'UTC' ) )->format( 'Y-m-d H:i:s' ); 487 } 488 489 /* Format UTC date to local WordPress time */ 483 490 public static function format_local_date( $utc_string ) { 484 491 … … 501 508 } 502 509 503 /** 504 * Delete logs 505 */ 510 /* Delete logs */ 506 511 public static function handle_delete_logs() { 507 512 … … 522 527 } 523 528 524 /** 525 * Export logs 526 */ 529 /* Export logs */ 527 530 public static function handle_export_logs() { 528 531 … … 578 581 } 579 582 580 /** 581 * Render logs table and filters 582 */ 583 /* Render logs table and filters */ 583 584 public static function render_logs_content() { 584 585 585 // phpcs:disable WordPress.Security.NonceVerification.Recommended 586 $search = isset( $_GET['s'] ) ? sanitize_text_field( wp_unslash( $_GET['s'] ) ) : ''; 587 $last_raw = isset( $_GET['last'] ) ? sanitize_text_field( wp_unslash( $_GET['last'] ) ) : '50'; 588 $level = isset( $_GET['level'] ) ? sanitize_text_field( wp_unslash( $_GET['level'] ) ) : ''; 589 $source = isset( $_GET['source'] ) ? sanitize_text_field( wp_unslash( $_GET['source'] ) ) : ''; 590 // phpcs:enable WordPress.Security.NonceVerification.Recommended 586 $filter_keys = [ 's', 'level', 'source', 'last', 'date_from', 'date_to' ]; 587 $user_id = get_current_user_id(); 588 589 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only presence check, no state change 590 $has_explicit_filters = (bool) array_filter( $filter_keys, function( $key ) { 591 return isset( $_GET[ $key ] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended 592 } ); 593 594 if ( $has_explicit_filters ) { 595 // phpcs:disable WordPress.Security.NonceVerification.Recommended 596 $search = isset( $_GET['s'] ) ? sanitize_text_field( wp_unslash( $_GET['s'] ) ) : ''; 597 $last_raw = isset( $_GET['last'] ) ? sanitize_text_field( wp_unslash( $_GET['last'] ) ) : 'today'; 598 $level = isset( $_GET['level'] ) ? sanitize_text_field( wp_unslash( $_GET['level'] ) ) : ''; 599 $source = isset( $_GET['source'] ) ? sanitize_text_field( wp_unslash( $_GET['source'] ) ) : ''; 600 $date_from = isset( $_GET['date_from'] ) ? sanitize_text_field( wp_unslash( $_GET['date_from'] ) ) : ''; 601 $date_to = isset( $_GET['date_to'] ) ? sanitize_text_field( wp_unslash( $_GET['date_to'] ) ) : ''; 602 // phpcs:enable WordPress.Security.NonceVerification.Recommended 603 604 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only flag, no state change itself 605 $is_auto_refresh = isset( $_GET['auto_refresh'] ); 606 607 if ( $user_id && ! $is_auto_refresh ) { 608 update_user_meta( $user_id, 'likoton_debug_logs_filters', [ 609 's' => $search, 610 'level' => $level, 611 'source' => $source, 612 'last' => $last_raw, 613 'date_from' => $date_from, 614 'date_to' => $date_to, 615 ] ); 616 } 617 } else { 618 $remembered = $user_id ? get_user_meta( $user_id, 'likoton_debug_logs_filters', true ) : ''; 619 $remembered = is_array( $remembered ) ? $remembered : []; 620 621 $search = isset( $remembered['s'] ) ? $remembered['s'] : ''; 622 $last_raw = isset( $remembered['last'] ) ? $remembered['last'] : 'today'; 623 $level = isset( $remembered['level'] ) ? $remembered['level'] : ''; 624 $source = isset( $remembered['source'] ) ? $remembered['source'] : ''; 625 $date_from = isset( $remembered['date_from'] ) ? $remembered['date_from'] : ''; 626 $date_to = isset( $remembered['date_to'] ) ? $remembered['date_to'] : ''; 627 } 591 628 592 629 $page = 1; 593 630 594 if ( $last_raw === 'all' ) { 631 $time_windowed = in_array( $last_raw, [ 'hour', 'today', 'week' ], true ); 632 633 if ( $last_raw === 'all' || $last_raw === 'range' ) { 595 634 $per_page = 999999; 635 } elseif ( $time_windowed ) { 636 $per_page = 100; 596 637 } else { 597 638 $per_page = (int) $last_raw; … … 603 644 $last = $last_raw; 604 645 646 $today_local = current_datetime()->format( 'Y-m-d' ); 647 648 if ( '' === $date_from ) { 649 $date_from = current_datetime()->modify( '-1 day' )->format( 'Y-m-d' ); 650 } 651 652 if ( '' === $date_to ) { 653 $date_to = $today_local; 654 } 655 605 656 $logs = Likoton_Debug_Logs_Installer::get_logs( [ 606 'search' => $search, 607 'level' => $level, 608 'source' => $source, 609 'page' => $page, 610 'per_page' => $per_page, 657 'search' => $search, 658 'level' => $level, 659 'source' => $source, 660 'page' => $page, 661 'per_page' => $per_page, 662 'date_from' => $last_raw === 'range' ? $date_from : '', 663 'date_to' => $last_raw === 'range' ? $date_to : '', 664 'since' => $time_windowed ? self::compute_since( $last_raw ) : '', 611 665 ] ); 612 666 ?> … … 647 701 648 702 <select name="last"> 649 <option value="20" <?php selected( $last, 20 ); ?>><?php esc_html_e( 'Last 20', 'likoton-debug-logs' ); ?></option> 650 <option value="50" <?php selected( $last, 50 ); ?>><?php esc_html_e( 'Last 50', 'likoton-debug-logs' ); ?></option> 651 <option value="100" <?php selected( $last, 100 ); ?>><?php esc_html_e( 'Last 100', 'likoton-debug-logs' ); ?></option> 652 <option value="200" <?php selected( $last, 200 ); ?>><?php esc_html_e( 'Last 200', 'likoton-debug-logs' ); ?></option> 653 <option value="500" <?php selected( $last, 500 ); ?>><?php esc_html_e( 'Last 500', 'likoton-debug-logs' ); ?></option> 654 <option value="1000" <?php selected( $last, 1000 ); ?>><?php esc_html_e( 'Last 1000', 'likoton-debug-logs' ); ?></option> 703 <option value="hour" <?php selected( $last, 'hour' ); ?>><?php esc_html_e( 'Last hour', 'likoton-debug-logs' ); ?></option> 704 <option value="today" <?php selected( $last, 'today' ); ?>><?php esc_html_e( 'Today', 'likoton-debug-logs' ); ?></option> 705 <option value="week" <?php selected( $last, 'week' ); ?>><?php esc_html_e( 'Last week', 'likoton-debug-logs' ); ?></option> 655 706 <option value="all" <?php selected( $last, 'all' ); ?>><?php esc_html_e( 'All logs', 'likoton-debug-logs' ); ?></option> 707 <option value="range" <?php selected( $last, 'range' ); ?>><?php esc_html_e( 'Date range', 'likoton-debug-logs' ); ?></option> 656 708 </select> 709 710 <div class="ldl-date-range<?php echo $last_raw === 'range' ? ' is-visible' : ''; ?>" id="ldl-date-range"> 711 <label for="ldl-date-from"><?php esc_html_e( 'From date', 'likoton-debug-logs' ); ?></label> 712 <div class="ldl-date-field"> 713 <input type="text" name="date_from" id="ldl-date-from" class="ldl-date-input" 714 value="<?php echo esc_attr( $date_from ); ?>" 715 data-max="<?php echo esc_attr( $date_to ); ?>" 716 readonly="readonly" autocomplete="off" /> 717 <button type="button" class="ldl-date-trigger" aria-label="<?php esc_attr_e( 'Choose date', 'likoton-debug-logs' ); ?>"> 718 <span class="dashicons dashicons-calendar-alt"></span> 719 </button> 720 </div> 721 722 <label for="ldl-date-to"><?php esc_html_e( 'To date', 'likoton-debug-logs' ); ?></label> 723 <div class="ldl-date-field"> 724 <input type="text" name="date_to" id="ldl-date-to" class="ldl-date-input" 725 value="<?php echo esc_attr( $date_to ); ?>" 726 data-min="<?php echo esc_attr( $date_from ); ?>" 727 data-max="<?php echo esc_attr( $today_local ); ?>" 728 readonly="readonly" autocomplete="off" /> 729 <button type="button" class="ldl-date-trigger" aria-label="<?php esc_attr_e( 'Choose date', 'likoton-debug-logs' ); ?>"> 730 <span class="dashicons dashicons-calendar-alt"></span> 731 </button> 732 </div> 733 </div> 657 734 658 735 </div> -
likoton-debug-logs/trunk/includes/class-likoton-debug-logs-assets.php
r3600839 r3717902 46 46 47 47 wp_localize_script( 'likoton-debug-logs-admin', 'likotonDebugLogsData', [ 48 'nonce' => wp_create_nonce( 'likoton_debug_logs_load_more_logs' ), 48 'nonce' => wp_create_nonce( 'likoton_debug_logs_load_more_logs' ), 49 'locale' => str_replace( '_', '-', get_user_locale() ), 50 'i18n' => [ 51 'today' => __( 'Today', 'likoton-debug-logs' ), 52 'clear' => __( 'Clear', 'likoton-debug-logs' ), 53 ], 49 54 ] ); 50 55 } -
likoton-debug-logs/trunk/includes/class-likoton-debug-logs-installer.php
r3647996 r3717902 11 11 const TABLE_NAME = 'likoton_debug_logs'; 12 12 13 /* *Default colors for known sources */13 /* Default colors for known sources */ 14 14 public static function get_default_source_colors() { 15 15 return [ … … 101 101 102 102 $defaults = [ 103 'search' => '', 104 'level' => '', 105 'source' => '', 106 'page' => 1, 107 'per_page' => 50, 103 'search' => '', 104 'level' => '', 105 'source' => '', 106 'page' => 1, 107 'per_page' => 50, 108 'date_from' => '', 109 'date_to' => '', 110 'since' => '', 108 111 ]; 109 112 $args = wp_parse_args( $args, $defaults ); 110 113 111 $search = $args['search']; 112 $level = sanitize_key( $args['level'] ); 113 $source = sanitize_key( $args['source'] ); 114 $page = max( 1, (int) $args['page'] ); 115 $per_page = max( 1, (int) $args['per_page'] ); 116 $offset = ( $page - 1 ) * $per_page; 114 $search = $args['search']; 115 $level = sanitize_key( $args['level'] ); 116 $source = sanitize_key( $args['source'] ); 117 $page = max( 1, (int) $args['page'] ); 118 $per_page = max( 1, (int) $args['per_page'] ); 119 $offset = ( $page - 1 ) * $per_page; 120 $date_from = sanitize_text_field( $args['date_from'] ); 121 $date_to = sanitize_text_field( $args['date_to'] ); 122 $since = sanitize_text_field( $args['since'] ); 117 123 118 124 // Table name is built from $wpdb->prefix + a hardcoded constant — safe to use with esc_sql. … … 157 163 } 158 164 165 $valid_date = '/^\d{4}-\d{2}-\d{2}$/'; 166 167 if ( $date_from !== '' && $date_to !== '' && preg_match( $valid_date, $date_from ) && preg_match( $valid_date, $date_to ) ) { 168 if ( $date_from > $date_to ) { 169 [ $date_from, $date_to ] = [ $date_to, $date_from ]; 170 } 171 172 $site_tz = wp_timezone(); 173 $utc_tz = new \DateTimeZone( 'UTC' ); 174 175 $range_start = \DateTime::createFromFormat( 'Y-m-d H:i:s', $date_from . ' 00:00:00', $site_tz ); 176 $range_end = \DateTime::createFromFormat( 'Y-m-d H:i:s', $date_to . ' 23:59:59', $site_tz ); 177 178 if ( $range_start && $range_end ) { 179 $range_start->setTimezone( $utc_tz ); 180 $range_end->setTimezone( $utc_tz ); 181 182 $where_extra .= ' AND created_at BETWEEN %s AND %s'; 183 $params[] = $range_start->format( 'Y-m-d H:i:s' ); 184 $params[] = $range_end->format( 'Y-m-d H:i:s' ); 185 } 186 } 187 188 if ( $since !== '' && preg_match( '/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/', $since ) ) { 189 $where_extra .= ' AND created_at >= %s'; 190 $params[] = $since; 191 } 192 159 193 $params[] = $per_page; 160 194 $params[] = $offset; -
likoton-debug-logs/trunk/includes/class-likoton-debug-logs-source-colors.php
r3650170 r3717902 35 35 } 36 36 37 // ---------------------------------------------------------------38 37 // Helpers 39 // ---------------------------------------------------------------40 38 41 39 /** Returns merged array: defaults overridden by saved custom colors */ … … 59 57 } 60 58 61 // --------------------------------------------------------------- 62 // Inline CSS injection 63 // --------------------------------------------------------------- 64 59 /** Inline CSS injection */ 65 60 public static function inject_inline_styles() { 66 61 $screen = get_current_screen(); -
likoton-debug-logs/trunk/languages/likoton-debug-logs-cs_CZ.po
r3647996 r3717902 47 47 msgid "All logs" 48 48 msgstr "Všechny záznamy" 49 50 msgid "Date range" 51 msgstr "Rozsah dat" 52 53 msgid "From date" 54 msgstr "Od data" 55 56 msgid "To date" 57 msgstr "Do data" 58 59 msgid "Choose date" 60 msgstr "Vybrat datum" 61 62 msgid "Today" 63 msgstr "Dnes" 64 65 msgid "Last hour" 66 msgstr "Poslední hodina" 67 68 msgid "Last week" 69 msgstr "Poslední týden" 70 71 msgid "Clear" 72 msgstr "Vymazat" 49 73 50 74 msgid "Level" -
likoton-debug-logs/trunk/languages/likoton-debug-logs-de_DE.po
r3647996 r3717902 47 47 msgid "All logs" 48 48 msgstr "Alle Protokolle" 49 50 msgid "Date range" 51 msgstr "Datumsbereich" 52 53 msgid "From date" 54 msgstr "Von Datum" 55 56 msgid "To date" 57 msgstr "Bis Datum" 58 59 msgid "Choose date" 60 msgstr "Datum auswählen" 61 62 msgid "Today" 63 msgstr "Heute" 64 65 msgid "Last hour" 66 msgstr "Letzte Stunde" 67 68 msgid "Last week" 69 msgstr "Letzte Woche" 70 71 msgid "Clear" 72 msgstr "Löschen" 49 73 50 74 msgid "Level" -
likoton-debug-logs/trunk/languages/likoton-debug-logs-en_US.po
r3647996 r3717902 47 47 msgid "All logs" 48 48 msgstr "All logs" 49 50 msgid "Date range" 51 msgstr "Date range" 52 53 msgid "From date" 54 msgstr "From date" 55 56 msgid "To date" 57 msgstr "To date" 58 59 msgid "Choose date" 60 msgstr "Choose date" 61 62 msgid "Today" 63 msgstr "Today" 64 65 msgid "Last hour" 66 msgstr "Last hour" 67 68 msgid "Last week" 69 msgstr "Last week" 70 71 msgid "Clear" 72 msgstr "Clear" 49 73 50 74 msgid "Level" -
likoton-debug-logs/trunk/languages/likoton-debug-logs-es_ES.po
r3647996 r3717902 47 47 msgid "All logs" 48 48 msgstr "Todos los registros" 49 50 msgid "Date range" 51 msgstr "Rango de fechas" 52 53 msgid "From date" 54 msgstr "Desde fecha" 55 56 msgid "To date" 57 msgstr "Hasta fecha" 58 59 msgid "Choose date" 60 msgstr "Elegir fecha" 61 62 msgid "Today" 63 msgstr "Hoy" 64 65 msgid "Last hour" 66 msgstr "Última hora" 67 68 msgid "Last week" 69 msgstr "Última semana" 70 71 msgid "Clear" 72 msgstr "Borrar" 49 73 50 74 msgid "Level" -
likoton-debug-logs/trunk/languages/likoton-debug-logs-fr_FR.po
r3647996 r3717902 47 47 msgid "All logs" 48 48 msgstr "Tous les journaux" 49 50 msgid "Date range" 51 msgstr "Plage de dates" 52 53 msgid "From date" 54 msgstr "Date de début" 55 56 msgid "To date" 57 msgstr "Date de fin" 58 59 msgid "Choose date" 60 msgstr "Choisir une date" 61 62 msgid "Today" 63 msgstr "Aujourd'hui" 64 65 msgid "Last hour" 66 msgstr "Dernière heure" 67 68 msgid "Last week" 69 msgstr "Dernière semaine" 70 71 msgid "Clear" 72 msgstr "Effacer" 49 73 50 74 msgid "Level" -
likoton-debug-logs/trunk/languages/likoton-debug-logs-pl_PL.po
r3647996 r3717902 47 47 msgid "All logs" 48 48 msgstr "Wszystkie logi" 49 50 msgid "Date range" 51 msgstr "Zakres dat" 52 53 msgid "From date" 54 msgstr "Od daty" 55 56 msgid "To date" 57 msgstr "Do daty" 58 59 msgid "Choose date" 60 msgstr "Wybierz datę" 61 62 msgid "Today" 63 msgstr "Dzisiaj" 64 65 msgid "Last hour" 66 msgstr "Ostatnia godzina" 67 68 msgid "Last week" 69 msgstr "Ostatni tydzień" 70 71 msgid "Clear" 72 msgstr "Wyczyść" 49 73 50 74 msgid "Level" -
likoton-debug-logs/trunk/languages/likoton-debug-logs-pt_PT.po
r3647996 r3717902 47 47 msgid "All logs" 48 48 msgstr "Todos os registos" 49 50 msgid "Date range" 51 msgstr "Intervalo de datas" 52 53 msgid "From date" 54 msgstr "De data" 55 56 msgid "To date" 57 msgstr "Até data" 58 59 msgid "Choose date" 60 msgstr "Escolher data" 61 62 msgid "Today" 63 msgstr "Hoje" 64 65 msgid "Last hour" 66 msgstr "Última hora" 67 68 msgid "Last week" 69 msgstr "Última semana" 70 71 msgid "Clear" 72 msgstr "Limpar" 49 73 50 74 msgid "Level" -
likoton-debug-logs/trunk/languages/likoton-debug-logs-sk_SK.po
r3647996 r3717902 47 47 msgid "All logs" 48 48 msgstr "Všetky záznamy" 49 50 msgid "Date range" 51 msgstr "Rozsah dátumov" 52 53 msgid "From date" 54 msgstr "Od dátumu" 55 56 msgid "To date" 57 msgstr "Do dátumu" 58 59 msgid "Choose date" 60 msgstr "Vybrať dátum" 61 62 msgid "Today" 63 msgstr "Dnes" 64 65 msgid "Last hour" 66 msgstr "Posledná hodina" 67 68 msgid "Last week" 69 msgstr "Posledný týždeň" 70 71 msgid "Clear" 72 msgstr "Vymazať" 49 73 50 74 msgid "Level" -
likoton-debug-logs/trunk/languages/likoton-debug-logs.pot
r3647996 r3717902 55 55 56 56 #: includes/class-likoton-debug-logs-admin.php 57 msgid "Date range" 58 msgstr "" 59 60 #: includes/class-likoton-debug-logs-admin.php 61 msgid "From date" 62 msgstr "" 63 64 #: includes/class-likoton-debug-logs-admin.php 65 msgid "To date" 66 msgstr "" 67 68 #: includes/class-likoton-debug-logs-admin.php 69 msgid "Choose date" 70 msgstr "" 71 72 #: includes/class-likoton-debug-logs-assets.php 73 msgid "Today" 74 msgstr "" 75 76 #: includes/class-likoton-debug-logs-admin.php 77 msgid "Last hour" 78 msgstr "" 79 80 #: includes/class-likoton-debug-logs-admin.php 81 msgid "Last week" 82 msgstr "" 83 84 #: includes/class-likoton-debug-logs-assets.php 85 msgid "Clear" 86 msgstr "" 87 88 #: includes/class-likoton-debug-logs-admin.php 57 89 msgid "Level" 58 90 msgstr "" -
likoton-debug-logs/trunk/likoton-debug-logs.php
r3647996 r3717902 5 5 * Description: Collects and displays WordPress and PHP debug logs with filters, live view and dark mode 6 6 * Author: LiKoToN 7 * Version: 1. 1.07 * Version: 1.2.0 8 8 * License: GPLv3 9 9 * License URI: License URI: https://www.gnu.org/licenses/gpl-3.0-standalone.html -
likoton-debug-logs/trunk/readme.txt
r3647996 r3717902 4 4 Requires at least: 6.0 5 5 Tested up to: 7.1 6 Stable tag: 1. 1.06 Stable tag: 1.2.0 7 7 License: GPLv3 8 8 License URI: https://www.gnu.org/licenses/gpl-3.0-standalone.html … … 25 25 - Infinite scroll 26 26 - Client-side sorting 27 - Search, level, source and "last X logs"filters27 - Search, level, source and time filters 28 28 - Color-coded badges 29 29 - Dark mode … … 32 32 33 33 Perfect for developers, administrators and anyone who needs a clear view of what happens inside WordPress. 34 35 == Description (pl_PL) ==36 37 LiKoToN Debug Logs to lekka i nowoczesna wtyczka debugująca dla WordPressa, która automatycznie zbiera:38 39 - błędy i ostrzeżenia PHP40 - błędy WordPressa (`wp_error_added`)41 - wywołania REST API (ścieżka, metoda, parametry)42 - logowania użytkowników43 - własne logi przez `Likoton_Debug_Logs_Logger::log()`44 45 Wtyczka oferuje czytelny panel logów z:46 47 - filtrowaniem AJAX48 - nieskończonym przewijaniem (infinite scroll)49 - sortowaniem po kolumnach50 - wyszukiwaniem, filtrem poziomu, źródła i ostatnich X logów51 - kolorowymi znacznikami poziomów i źródeł52 - trybem ciemnym53 - eksportem CSV (czas UTC + lokalny)54 - automatycznym czyszczeniem logów55 56 Idealna dla deweloperów i administratorów, którzy chcą mieć pełną kontrolę nad tym, co dzieje się w WordPressie.57 34 58 35 --- … … 70 47 - AJAX live filtering 71 48 - Infinite scroll 72 - Search, level, source, last X logs49 - Search, level, source, time 73 50 - Client-side sorting 74 51 - Color-coded badges … … 98 75 --- 99 76 100 ## Developer API77 ## Installation 101 78 102 ### Custom logs 103 ```php 104 Likoton_Debug_Logs_Logger::log( 'info', 'custom_source', 'Something happened', [ 'extra' => 'data' ] ); 79 ### From WordPress Dashboard 80 1. Upload the plugin ZIP 81 2. Activate **LiKoToN Debug Logs** 82 3. Go to **LiKoToN Debug Logs → Logs** 83 84 ### Manual Installation 85 1. Upload folder `likoton-debug-logs` to `/wp-content/plugins/` 86 2. Activate plugin 87 3. Logs appear in the main admin menu 88 89 --- 105 90 106 91 ## Supported log levels (PSR-3 + extended PHP levels): … … 145 130 Yes, each site has its own logs table. 146 131 132 == Screenshots == 133 134 1. Logs tab 135 2. Logs tab in dark mode 136 3. Settings in dark mode 137 147 138 ## Changelog ## 139 140 1.2.0 141 - A date range selection filter has been added 148 142 149 143 1.1.0
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)