Changeset 3346700
- Timestamp:
- 08/19/2025 02:06:48 AM (6 months ago)
- Location:
- lyxity/trunk
- Files:
-
- 3 added
- 12 edited
-
README.md (modified) (1 diff)
-
assets/css/dashboard.css (modified) (6 diffs)
-
assets/css/realtime.css (added)
-
assets/css/settings.css (modified) (1 diff)
-
assets/images/lyxity_background.svg (added)
-
assets/js/admin.js (modified) (13 diffs)
-
assets/js/dashboard.js (modified) (1 diff)
-
assets/js/main-page.js (modified) (18 diffs)
-
assets/js/realtime-status.js (added)
-
assets/js/settings.js (modified) (2 diffs)
-
includes/class-lyxity-api.php (modified) (12 diffs)
-
lyxity.php (modified) (12 diffs)
-
readme.txt (modified) (2 diffs)
-
templates/dashboard-page.php (modified) (2 diffs)
-
templates/settings-page.php (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
lyxity/trunk/README.md
r3345835 r3346700 92 92 93 93 ## Changelog 94 ### 1.4.0 95 - Added content creation feature 94 96 95 97 ### 1.3.0 -
lyxity/trunk/assets/css/dashboard.css
r3345835 r3346700 1 1 .lyxity-dashboard { 2 background: #f8f9fa; 3 padding: 20px; 4 margin: 20px 20px 0 0; 5 border-radius: 8px; 2 3 background: #fff; 4 padding: 0; 5 margin: 0; 6 border-radius: 0; 6 7 } 7 8 … … 10 11 padding-bottom: 20px; 11 12 border-bottom: 1px solid #e9ecef; 13 } 14 15 /* Dashboard logo sizing (match realtime, keep aspect ratio) */ 16 .dashboard-header .dashboard-logo { 17 height: 56px; 18 width: auto; 19 margin-right: 12px; 20 display: inline-block; 21 vertical-align: middle; 22 } 23 24 .dashboard-header .dashboard-logo img { 25 height: 100%; 26 width: auto; 27 display: block; 12 28 } 13 29 … … 35 51 border-radius: 6px; 36 52 box-shadow: 0 1px 3px rgba(0,0,0,0.1); 37 } 38 39 .date-range-selector .form-control { 40 width: 160px; 41 } 42 43 .dashboard-stats { 53 width: 100%; 54 } 55 56 /* Ensure date inputs are properly sized on mobile */ 57 .date-range-selector input[type="date"] { 58 min-height: 38px; 59 } 60 61 /* Date picker styling handled by Bootstrap */ 62 63 /* Dashboard Container Layout */ 64 .lyxity-dashboard-container { 65 display: flex; 66 flex-wrap: wrap; 67 gap: 24px; 68 margin-top: 16px; 69 margin-bottom: 24px; 70 } 71 72 .lyxity-dashboard-column { 73 flex: 1; 74 min-width: 300px; 75 background: #fff; 76 border-radius: 8px; 77 box-shadow: 0 1px 3px rgba(0,0,0,0.1); 78 overflow: hidden; 79 } 80 81 .lyxity-section-header { 82 background: #f8f9fa; 83 padding: 12px 16px; 84 border-bottom: 1px solid #e9ecef; 85 } 86 87 .lyxity-section-header h2 { 88 margin: 0; 89 font-size: 16px; 90 font-weight: 600; 91 color: #2c3338; 92 } 93 94 .lyxity-card-group { 95 padding: 16px; 44 96 display: grid; 45 grid-template-columns: repeat(auto-fi t, minmax(300px, 1fr));46 gap: 20px;47 margin-top: 30px; 48 } 49 50 . stat-card {97 grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); 98 gap: 16px; 99 } 100 101 /* Card Styling */ 102 .lyxity-card { 51 103 background: white; 52 padding: 25px;53 border-radius: 8px;54 box-shadow: 0 2px 4px rgba(0,0,0,0.08);104 padding: 14px 16px; 105 border-radius: 6px; 106 box-shadow: 0 1px 2px rgba(0,0,0,0.06); 55 107 transition: transform 0.2s ease; 56 108 position: relative; 57 109 overflow: hidden; 58 } 59 60 .stat-card:hover { 110 display: flex; 111 align-items: center; 112 min-height: 80px; 113 height: 100%; 114 } 115 116 .lyxity-card:hover { 61 117 transform: translateY(-2px); 62 118 box-shadow: 0 4px 8px rgba(0,0,0,0.12); 63 119 } 64 120 65 .stat-card h3 { 66 font-size: 16px; 67 color: #50575e; 68 margin: 0 0 15px 0; 69 font-weight: 500; 70 } 71 72 .stat-card .number { 73 font-size: 36px; 121 .lyxity-card-icon { 122 margin-right: 12px; 123 display: flex; 124 align-items: center; 125 justify-content: center; 126 width: 40px; 127 flex-shrink: 0; 128 } 129 130 .lyxity-card-icon i, 131 .lyxity-card-icon .dashicons { 132 font-size: 18px; 133 color: #b19044; 134 } 135 136 .lyxity-card-content { 137 flex: 1; 138 } 139 140 .lyxity-card-number { 141 font-size: 24px; 74 142 font-weight: 600; 75 143 color: #2c3338; 76 margin-bottom: 10px; 77 } 78 79 .stat-card i { 80 font-size: 24px; 81 margin-bottom: 15px; 144 margin-bottom: 4px; 145 line-height: 1.2; 146 } 147 148 .lyxity-card-label { 149 font-size: 13px; 150 color: #50575e; 151 margin: 0 0 2px 0; 152 font-weight: 500; 153 line-height: 1.2; 154 } 155 156 .lyxity-card-description { 157 font-size: 11px; 158 color: #6c757d; 159 margin: 0; 160 line-height: 1.3; 161 } 162 163 /* Card Color Styling */ 164 .update-card { 165 border-left: 3px solid #b19044; 166 } 167 168 .update-card .lyxity-card-icon i { 82 169 color: #b19044; 83 170 } 84 171 85 .stat-card p {86 font-size: 13px;87 margin: 0;88 }89 90 .update-card {91 border-left: 4px solid #b19044;92 }93 94 172 .enhance-card { 95 border-left: 4px solid #2271b1; 173 border-left: 3px solid #2271b1; 174 } 175 176 .enhance-card .lyxity-card-icon i { 177 color: #2271b1; 96 178 } 97 179 98 180 .stale-card { 99 border-left: 4px solid #dc3545;100 } 101 102 .stale-card i {181 border-left: 3px solid #dc3545; 182 } 183 184 .stale-card .lyxity-card-icon i { 103 185 color: #dc3545; 186 } 187 188 .queued-card { 189 border-left: 3px solid #b19044; 190 } 191 192 .processing-card { 193 border-left: 3px solid #2271b1; 194 } 195 196 .completed-card { 197 border-left: 3px solid #00a32a; 198 } 199 200 .completed-card .lyxity-card-icon .dashicons { 201 color: #00a32a; 202 } 203 204 .failed-card { 205 border-left: 3px solid #dc3545; 206 } 207 208 .failed-card .lyxity-card-icon .dashicons { 209 color: #dc3545; 210 } 211 212 .generated-card { 213 border-left: 3px solid #6f42c1; 214 } 215 216 .generated-card .lyxity-card-icon .dashicons { 217 color: #6f42c1; 218 } 219 220 .pending-card { 221 border-left: 3px solid #17a2b8; 222 } 223 224 .pending-card .lyxity-card-icon .dashicons { 225 color: #17a2b8; 104 226 } 105 227 … … 118 240 background-color: #f8f9fa; 119 241 border-right: none; 120 font-size: 2 6px; /* Increased from default to match textbox height*/242 font-size: 20px; /* Adjusted for better mobile display */ 121 243 padding: 0 10px; /* Adjusted padding for better alignment */ 244 min-width: 40px; 245 justify-content: center; 122 246 } 123 247 … … 135 259 } 136 260 261 @media (max-width: 1200px) { 262 .lyxity-card-group { 263 grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); 264 } 265 } 266 267 @media (max-width: 992px) { 268 .lyxity-dashboard-container { 269 flex-direction: column; 270 } 271 272 .lyxity-dashboard-column { 273 width: 100%; 274 margin-bottom: 16px; 275 } 276 277 .lyxity-card-group { 278 grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); 279 } 280 281 /* Date picker responsiveness handled by Bootstrap */ 282 } 283 137 284 @media (max-width: 782px) { 138 285 .lyxity-dashboard { … … 140 287 } 141 288 142 .da te-range-selector .d-flex{289 .dashboard-header { 143 290 flex-direction: column; 144 291 } 145 292 146 .date-range-selector .form-control { 147 margin: 10px 0; 148 } 149 150 .stat-card { 151 padding: 20px; 152 } 153 } 293 /* Date picker responsiveness handled by Bootstrap */ 294 295 .lyxity-card { 296 padding: 12px 14px; 297 } 298 299 .lyxity-card-group { 300 grid-template-columns: 1fr; 301 } 302 303 .lyxity-card-number { 304 font-size: 20px; 305 } 306 } 307 308 /* Write Articles Card - Basic Styling */ -
lyxity/trunk/assets/css/settings.css
r3345835 r3346700 5 5 */ 6 6 7 /* Import Libre Baskerville font from Google Fonts */ 8 @import url('https://fonts.googleapis.com/css2?family=Libre+Baskerville:ital,wght@0,400;0,700;1,400&display=swap'); 7 /* Clean Bootstrap-based styles */ 9 8 10 /* Apply font family only to elements within the settings page container */ 11 .lyxity-settings-wrap h1, 12 .lyxity-settings-wrap h2, 13 .lyxity-settings-wrap h3, 14 .lyxity-settings-wrap h4, 15 .lyxity-settings-wrap h5, 16 .lyxity-settings-wrap h6, 17 .lyxity-settings-wrap p, 18 .lyxity-settings-wrap input, 19 .lyxity-settings-wrap button, 20 .lyxity-settings-wrap select, 21 .lyxity-settings-wrap textarea { 22 font-family: 'Libre Baskerville', serif; 9 /* Only minimal custom styling needed */ 10 .lyxity-settings-wrap { 11 margin: 20px 0 0 0; 12 max-width: 100%; 13 padding-right: 20px; 23 14 } 24 15 25 .lyxity-settings-wrap { 26 background-color: #f8f8f5; 27 /* padding: 20px; */ 16 .lyxity-settings-wrap .container-fluid { 28 17 max-width: 100%; 29 position: relative; 30 overflow: hidden; 18 padding-right: 0; 31 19 } 32 20 33 /* Background images */ 34 .lyxity-settings-wrap:before, 35 .lyxity-settings-wrap:after { 36 content: ""; 37 position: absolute; 38 width: 250px; 39 height: 250px; 40 background-repeat: no-repeat; 41 background-size: contain; 42 opacity: 0.6; 43 z-index: 0; 21 /* Using Bootstrap mb-4 class */ 22 23 /* No background images - clean UI */ 24 25 /* Using Bootstrap d-flex and align-items-center classes */ 26 27 /* Using Bootstrap img-fluid class */ 28 29 /* Keep gradient text effect but use Bootstrap classes for size and weight */ 30 .lyxity-text-logo { 31 background: linear-gradient(90deg, #4285f4, #34a853); 32 -webkit-background-clip: text; 33 background-clip: text; 34 -webkit-text-fill-color: transparent; 44 35 } 45 36 46 .lyxity-settings-wrap:before { 47 top: 20px; 48 left: 20px; 49 background-image: url('../images/HH_Background.svg'); 50 background-position: top left; 37 /* Using Bootstrap h1 and text-dark classes */ 38 39 40 /* No background images */ 41 42 /* Using Bootstrap card component instead */ 43 #lyxity-settings-form .card { 44 max-width: none !important; 45 width: 100%; 46 border: 1px solid rgba(0,0,0,.125); 51 47 } 52 48 53 .lyxity-settings-wrap:after { 54 bottom: 20px; 55 right: 20px; 56 background-image: url('../images/HH_Background.svg'); 57 background-position: bottom right; 49 #lyxity-settings-form .card-header { 50 padding-left: 0; 51 padding-right: 0; 52 border-left: 0; 53 border-right: 0; 54 border-top: 0; 58 55 } 59 56 60 .lyxity-container { 61 background: white; 62 padding: 30px; 63 border-radius: 10px; 64 box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); 65 text-align: center; 66 /* max-width: 800px; */ 67 margin: 20px; 68 position: relative; 69 z-index: 1; 57 /* No background decorations - clean UI */ 58 59 /* Using Bootstrap spacing and typography classes */ 60 61 /* Using Bootstrap card and border utilities */ 62 63 /* Using Bootstrap row and col classes */ 64 /* Using Bootstrap img-fluid class */ 65 66 /* Using Bootstrap link classes */ 67 68 /* Using Bootstrap btn and btn-primary classes */ 69 70 /* Using Bootstrap form-group and form-control classes */ 71 72 /* Using Bootstrap img-fluid class */ 73 74 /* Using Bootstrap row and col-md-4 classes */ 75 76 /* Using Bootstrap responsive classes instead of custom media queries */ 77 78 /* Mobile responsive styles */ 79 @media (max-width: 782px) { 80 input.form-control { 81 font-size: 16px; /* Prevent zoom on iOS */ 82 } 83 84 .lyxity-settings-wrap { 85 margin: 10px 0 0 0; 86 padding-right: 10px; 87 } 88 89 .card-body { 90 padding: 15px !important; 91 } 70 92 } 71 93 72 .lyxity-logo { 73 margin-bottom: 20px; 74 text-align: center; 94 /* Using native Bootstrap utility classes instead */ 95 96 /* Using Bootstrap form-group and form-label classes */ 97 .form-group { 98 margin-bottom: 5px; 75 99 } 76 100 77 .lyxity-logo svg { 78 max-width: 180px; 79 height: auto; 101 .form-label { 102 display: block; 103 margin-bottom: 8px; 104 font-weight: 500; 105 font-size: 14px; 106 color: #23282d; 80 107 } 81 108 82 .lyxity-back { 83 display: block; 84 text-align: left; 85 margin-bottom: 15px; 86 text-decoration: none; 87 color: #333; 88 font-family: 'Libre Baskerville', serif; 109 110 /* Using Bootstrap form-control class */ 111 112 /* Using Bootstrap form-select class */ 113 114 /* Minimal input group styles - using Bootstrap for most styling */ 115 116 117 /* Minimal button styles - using Bootstrap for most styling */ 118 119 /* Using Bootstrap btn and btn-primary classes instead */ 120 121 /* Using Bootstrap ms-2 class */ 122 123 /* Using Bootstrap hover effects */ 124 125 /* Using Bootstrap spacing and flex utilities */ 126 127 /* Using Bootstrap form-text utility */ 128 129 /* Using Bootstrap alert components instead */ 130 131 /* Using Bootstrap hover effects */ 132 .password-control{ 133 width: unset !important; 89 134 } 90 91 .lyxity-btn-submit { 92 background-color: black; 93 color: white; 94 width: 100%; 95 padding: 10px; 96 border: none; 97 border-radius: 4px; 98 cursor: pointer; 99 font-weight: bold; 100 font-family: 'Libre Baskerville', serif; 101 text-transform: uppercase; 102 letter-spacing: 1px; 135 .dashicons { 136 vertical-align: middle !important; 103 137 } 104 105 .lyxity-btn-submit:hover {106 background-color: #333;107 }108 109 .lyxity-form-group {110 margin-bottom: 20px;111 }112 113 .lyxity-form-control {114 width: 100%;115 padding: 10px;116 border: 1px solid #ddd;117 border-radius: 4px;118 font-family: 'Libre Baskerville', serif;119 }120 121 .lyxity-img {122 max-width: 100px;123 height: auto;124 margin: 15px 0;125 }126 127 /* Settings page specific typography */128 .h4 {129 font-family: 'Libre Baskerville', serif;130 font-weight: 700;131 margin-bottom: 15px;132 font-size: 20px;133 }134 135 p {136 font-family: 'Libre Baskerville', serif;137 font-size: 14px;138 line-height: 1.6;139 color: #555;140 }141 142 /* Background image positioning */143 .lyxity-bg-top-left,144 .lyxity-bg-bottom-right {145 position: absolute;146 z-index: -1;147 pointer-events: none;148 }149 150 .lyxity-bg-top-left {151 top: 0;152 left: 0;153 max-width: 30%;154 opacity: 0.7;155 }156 157 .lyxity-bg-bottom-right {158 bottom: 0;159 right: 0;160 max-width: 30%;161 opacity: 0.7;162 }163 164 /* Add the CSS from the settings page */165 .lyxity-input-group {166 display: flex;167 align-items: center;168 position: relative;169 }170 171 .lyxity-btn {172 background: transparent;173 border: none;174 cursor: pointer;175 padding: 8px;176 color: #555;177 position: absolute;178 right: 8px;179 top: 50%;180 transform: translateY(-50%);181 z-index: 10;182 }183 184 .lyxity-btn:hover {185 color: #b19044;186 } -
lyxity/trunk/assets/js/admin.js
r3345835 r3346700 1 1 jQuery(document).ready(function($) { 2 console.log("Lyxity plugin initialized"); 3 4 // Debug info for buttons 5 if ($(".update-single-post").length) { 6 console.log("Found " + $(".update-single-post").length + " update buttons"); 7 } else { 8 console.log("No update buttons found on page"); 9 } 10 2 11 3 // Attach click handlers directly 12 4 $(document).on("click", ".update-single-post", function(e) { 13 5 e.preventDefault(); 14 console.log("Update button clicked");15 6 handleUpdateButtonClick($(this)); 16 7 }); … … 18 9 $(document).on("click", ".rewrite-single-post", function(e) { 19 10 e.preventDefault(); 20 console.log("Rewrite button clicked");21 11 handleRewriteButtonClick($(this)); 22 12 }); 23 13 24 14 function handleAjaxError(xhr, status, error, button, type) { 25 console.error("AJAX request failed:", error);26 console.log(xhr.responseText);27 15 var messages = { 28 16 update: lyxityVars.i18n.updateError, … … 44 32 function handleUpdateButtonClick(button) { 45 33 const postId = button.data("post-id"); 46 console.log("Processing update for post ID: " + postId);47 34 48 35 button.attr("disabled", true) … … 58 45 }, 59 46 success: function(response) { 60 console.log("AJAX response received", response);61 47 if (response.success) { 62 48 const data = response.data; … … 64 50 } else { 65 51 alertify.error(lyxityVars.i18n.updateError); 66 console.error("Error response:", response);67 52 } 68 53 … … 78 63 function handleRewriteButtonClick(button) { 79 64 const postId = button.data("post-id"); 80 console.log("Processing rewrite for post ID: " + postId);81 65 82 66 button.attr("disabled", true) … … 92 76 }, 93 77 success: function(response) { 94 console.log("AJAX response received", response);95 78 if (response.success) { 96 79 const data = response.data; … … 98 81 } else { 99 82 alertify.error(lyxityVars.i18n.rewriteError); 100 console.error("Error response:", response);101 83 } 102 84 … … 111 93 112 94 function showUpdatePreviewModal(postId, data) { 113 console.log("Showing update preview modal for post ID: " + postId);114 95 const modal = $("#update-preview-modal"); 115 96 … … 139 120 140 121 function showRewritePreviewModal(postId, data) { 141 console.log("Showing rewrite preview modal for post ID: " + postId);142 122 const modal = $("#rewrite-preview-modal"); 143 123 … … 168 148 $(document).on('click', '.modal .btn-close, .modal .btn-secondary[data-bs-dismiss="modal"]', function() { 169 149 const modalEl = $(this).closest('.modal')[0]; 170 console.log('Closing modal with Bootstrap 5:', modalEl.id);171 150 const modalInstance = bootstrap.Modal.getInstance(modalEl); 172 151 if (modalInstance) { … … 178 157 $(document).on("click", ".apply-update", function(e) { 179 158 e.preventDefault(); 180 console.log("Apply update button clicked");181 159 const modal = $("#update-preview-modal"); 182 160 const postId = modal.data("post-id"); … … 222 200 $(document).on("click", ".apply-rewrite", function(e) { 223 201 e.preventDefault(); 224 console.log("Apply rewrite button clicked");225 202 const modal = $("#rewrite-preview-modal"); 226 203 const postId = modal.data("post-id"); -
lyxity/trunk/assets/js/dashboard.js
r3345835 r3346700 53 53 }, 54 54 success: function(response) { 55 console.log(response);56 55 if(response.result){ 57 56 $('#update-count').text(response.updates); 58 57 $('#enhance-count').text(response.rewrites); 59 } else {60 console.error('API returned false result');61 58 } 62 59 }, 63 60 error: function() { 64 console.error('Error calling lyxity_get_dashboard_data_v2');61 // Error handled silently 65 62 } 66 63 }); -
lyxity/trunk/assets/js/main-page.js
r3345835 r3346700 43 43 function() { 44 44 $.ajax({ 45 url: ajaxurl,45 url: lyxityVars.ajaxUrl, 46 46 type: 'POST', 47 47 data: { … … 74 74 function() { 75 75 $.ajax({ 76 url: ajaxurl,76 url: lyxityVars.ajaxUrl, 77 77 type: 'POST', 78 78 data: { … … 137 137 138 138 $.ajax({ 139 url: ajaxurl,139 url: lyxityVars.ajaxUrl, 140 140 type: 'POST', 141 141 data: { … … 197 197 198 198 $.ajax({ 199 url: ajaxurl,199 url: lyxityVars.ajaxUrl, 200 200 type: 'POST', 201 201 data: { … … 250 250 $(document).on('click', '.modal .btn-close, .modal .btn-secondary[data-bs-dismiss="modal"]', function() { 251 251 const modalEl = $(this).closest('.modal')[0]; 252 console.log('Closing modal with Bootstrap 5:', modalEl.id);253 252 const modalInstance = bootstrap.Modal.getInstance(modalEl); 254 253 if (modalInstance) { … … 269 268 270 269 $.ajax({ 271 url: ajaxurl,270 url: lyxityVars.ajaxUrl, 272 271 type: 'POST', 273 272 data: { … … 311 310 312 311 $.ajax({ 313 url: ajaxurl,312 url: lyxityVars.ajaxUrl, 314 313 type: 'POST', 315 314 data: { … … 333 332 // Redirect to WordPress editor 334 333 if (response.edit_url) { 335 console.log('Redirecting to:', response.edit_url);336 334 window.location.href = response.edit_url; 337 } else {338 console.error('No edit_url in response');339 335 } 340 336 } else { 341 console.error('Error in response:', response.message);342 337 alertify.error(response.message || lyxityVars.i18n.updateError); 343 338 button.attr('disabled', false) … … 364 359 365 360 $.ajax({ 366 url: ajaxurl,361 url: lyxityVars.ajaxUrl, 367 362 type: 'POST', 368 363 data: { … … 395 390 // Apply rewrite and edit button click 396 391 $(document).on('click', '.apply-rewrite-and-edit', function() { 397 console.log('Enhance and Edit button clicked');398 392 const modal = $('#rewrite-preview-modal'); 399 393 const postId = modal.data('post-id'); … … 401 395 const button = $(this); 402 396 403 console.log('Post ID:', postId);404 console.log('Content available:', !!content);405 406 397 button.attr('disabled', true) 407 398 .html('<i class="fas fa-spinner fa-spin"></i> ' + lyxityVars.i18n.applying); 408 399 409 400 $.ajax({ 410 url: ajaxurl,401 url: lyxityVars.ajaxUrl, 411 402 type: 'POST', 412 403 data: { … … 417 408 }, 418 409 success: function(response) { 419 console.log('AJAX response received:', response);420 410 if (response.success) { 421 console.log('Success response, edit URL:', response.edit_url);422 411 alertify.success('Content enhanced successfully'); 423 412 … … 431 420 // Redirect to WordPress editor 432 421 if (response.edit_url) { 433 console.log('Redirecting to:', response.edit_url);434 422 window.location.href = response.edit_url; 435 423 } else { 436 console.error('No edit_url in response');437 424 } 438 425 } else { 439 console.error('Error in response:', response.message);440 426 alertify.error(response.message || lyxityVars.i18n.updateError); 441 427 button.attr('disabled', false) … … 454 440 function processUpdates(offset = 0) { 455 441 $.ajax({ 456 url: ajaxurl,442 url: lyxityVars.ajaxUrl, 457 443 type: 'POST', 458 444 data: { … … 484 470 } 485 471 } else { 486 console.error('Error:', response.data);487 472 alertify.error(lyxityVars.i18n.processingError); 488 473 $('#start-update').attr('disabled', false); … … 490 475 }, 491 476 error: function(xhr, status, error) { 492 console.error('AJAX Error:', error);493 console.log(xhr.responseText);494 477 alertify.error(lyxityVars.i18n.processingErrorDetails); 495 478 $('#start-update').attr('disabled', false); … … 500 483 // Helper function for AJAX errors 501 484 function handleAjaxError(xhr, status, error, button, type) { 502 console.error("AJAX request failed:", error);503 console.log(xhr.responseText);504 485 var messages = { 505 486 update: lyxityVars.i18n.updateError, … … 524 505 }); 525 506 507 // Write Articles functionality 508 let suggestedKeywords = []; 509 510 // Handle keyword source selection 511 function handleKeywordSourceChange() { 512 const selectedValue = $('input[name="keyword-source"]:checked').val(); 513 514 if (selectedValue === 'suggested') { 515 $('#suggested-keywords-section').show(); 516 $('#manual-keyword-section').hide(); 517 // Auto-fetch keywords when AI Suggested is selected 518 fetchSuggestedKeywords(); 519 } else if (selectedValue === 'manual') { 520 $('#suggested-keywords-section').hide(); 521 $('#manual-keyword-section').show(); 522 } else { 523 $('#suggested-keywords-section').hide(); 524 $('#manual-keyword-section').hide(); 525 } 526 527 // Reset and hide article generation section 528 $('#article-generation-section').hide(); 529 $('#final-keyword').val(''); 530 $('#write-articles-btn').prop('disabled', true); 531 $('#metrics-column').hide(); 532 } 533 534 // Store the currently selected keyword text 535 let currentSelectedKeyword = ''; 536 537 // Handle keyword selection from dropdown or manual input 538 function handleKeywordSelection(keyword) { 539 if (keyword) { 540 // Extract keyword text if it's an object 541 const keywordText = typeof keyword === 'object' && keyword.keyword ? keyword.keyword : keyword; 542 543 // Store the keyword text in our variable 544 currentSelectedKeyword = keywordText; 545 546 // Show article generation section 547 document.getElementById('article-generation-section').style.display = 'block'; 548 document.getElementById('write-articles-btn').disabled = false; 549 550 // Show the entire metrics column 551 document.getElementById('metrics-column').style.display = 'block'; 552 553 // Fetch and display keyword metrics 554 fetchKeywordMetrics(keyword); 555 } else { 556 // Clear selected keyword 557 currentSelectedKeyword = ''; 558 559 // Hide article generation section 560 document.getElementById('article-generation-section').style.display = 'none'; 561 document.getElementById('write-articles-btn').disabled = true; 562 563 // Hide the entire metrics column 564 document.getElementById('metrics-column').style.display = 'none'; 565 } 566 } 567 568 // Function to fetch suggested keywords 569 function fetchSuggestedKeywords() { 570 const $select = $('#keywords-select'); 571 const $loadingDiv = $('#keywords-loading'); 572 const $errorDiv = $('#keywords-error'); 573 574 // Show loading state 575 $select.html('<option value="">Loading keywords...</option>').prop('disabled', true); 576 $loadingDiv.show(); 577 $errorDiv.hide(); 578 579 $.ajax({ 580 url: lyxityVars.ajaxUrl, 581 type: 'POST', 582 data: { 583 action: 'lyxity_get_suggested_keywords', 584 nonce: lyxityVars.nonce 585 }, 586 success: function(response) { 587 588 if (response.success) { 589 let keywordsData = response.data.suggestedKeywords; 590 591 if (keywordsData && keywordsData.length > 0) { 592 suggestedKeywords = keywordsData; 593 populateKeywordsDropdown(keywordsData); 594 $select.prop('disabled', false); 595 $loadingDiv.hide(); 596 } else { 597 showKeywordsError('No keywords found in response'); 598 } 599 } else { 600 const errorMessage = (response.data && response.data.message) || response.message || 'Failed to fetch keywords'; 601 showKeywordsError(errorMessage); 602 } 603 }, 604 error: function(xhr, status, error) { 605 showKeywordsError('Error fetching keywords. Please try again.'); 606 }, 607 complete: function() { 608 $loadingDiv.hide(); 609 } 610 }); 611 } 612 613 // Function to populate keywords dropdown 614 function populateKeywordsDropdown(keywords) { 615 const $select = $('#keywords-select'); 616 $select.empty().append('<option value="">Choose a keyword...</option>'); 617 618 keywords.forEach(function(keyword) { 619 $select.append(`<option value="${keyword.keyword}">${keyword.keyword}</option>`); 620 }); 621 } 622 623 // Function to show keywords error 624 function showKeywordsError(message) { 625 $('#keywords-select').html('<option value="">Error loading keywords</option>').prop('disabled', true); 626 $('#keywords-error-message').text(message); 627 $('#keywords-error').show(); 628 $('#keywords-loading').hide(); 629 } 630 631 // Function to fetch and display keyword metrics 632 function fetchKeywordMetrics(selectedKeyword) { 633 // Find the keyword data from the suggestedKeywords array 634 const keywordData = suggestedKeywords.find(k => k.keyword === selectedKeyword); 635 636 if (keywordData) { 637 // Populate metrics with data from the keyword object 638 $('#metric-clicks').text(keywordData.clicks || '0'); 639 $('#metric-impressions').text(keywordData.impressions || '0'); 640 $('#metric-position').text(keywordData.position ? parseFloat(keywordData.position).toFixed(1) : '0.0'); 641 $('#metric-ctr').text(keywordData.ctr ? (parseFloat(keywordData.ctr) * 100).toFixed(2) : '0.00'); 642 $('#metric-score').text(keywordData.score || 'N/A'); 643 $('#metric-recommendation').text(keywordData.recommendation || 'No recommendation available'); 644 } else { 645 // If no data found, show default values 646 $('#metric-clicks').text('0'); 647 $('#metric-impressions').text('0'); 648 $('#metric-position').text('0.0'); 649 $('#metric-ctr').text('0.00'); 650 $('#metric-score').text('N/A'); 651 $('#metric-recommendation').text('No data available for this keyword'); 652 } 653 } 654 655 // Add event listeners to radio buttons 656 $('input[name="keyword-source"]').on('change', handleKeywordSourceChange); 657 658 // Handle dropdown selection 659 $('#keywords-select').on('change', function() { 660 const selectedKeyword = $(this).val(); 661 handleKeywordSelection(selectedKeyword); 662 }); 663 664 // Handle manual keyword input 665 $('#manual-keyword-input').on('input', function() { 666 const keyword = $(this).val().trim(); 667 handleKeywordSelection(keyword); 668 }); 669 670 // Legacy fetch keywords button click (if still exists) 671 $('#fetch-keywords-btn').on('click', function() { 672 const button = $(this); 673 const originalText = button.html(); 674 675 // Show loading state 676 button.prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i> Fetching...'); 677 $('#keywords-loading').show(); 678 $('#keywords-error').hide(); 679 $('#keywords-dropdown-section').hide(); 680 681 $.ajax({ 682 url: lyxityVars.ajaxUrl, 683 type: 'POST', 684 data: { 685 action: 'lyxity_get_suggested_keywords', 686 nonce: lyxityVars.nonce 687 }, 688 success: function(response) { 689 690 if (response.success) { 691 // The API returns data directly with suggestedKeywords at root level 692 let keywordsData = response.data.suggestedKeywords; 693 694 if (keywordsData && keywordsData.length > 0) { 695 suggestedKeywords = keywordsData; 696 populateKeywordsDropdown(keywordsData); 697 $('#keywords-dropdown-section').show(); 698 $('#keywords-loading').hide(); 699 700 // Show success message with keyword count 701 } else { 702 showKeywordsError('No keywords found in response'); 703 } 704 } else { 705 const errorMessage = (response.data && response.data.message) || response.message || 'Failed to fetch keywords'; 706 showKeywordsError(errorMessage); 707 } 708 }, 709 error: function(xhr, status, error) { 710 showKeywordsError('Error fetching keywords. Please try again.'); 711 }, 712 complete: function() { 713 button.prop('disabled', false).html(originalText); 714 $('#keywords-loading').hide(); 715 } 716 }); 717 }); 718 719 // Populate keywords dropdown 720 function populateKeywordsDropdown(keywords) { 721 const select = $('#keywords-select'); 722 select.empty().append('<option value="">Choose a keyword...</option>'); 723 724 725 keywords.forEach(function(keyword, index) { 726 // Create a more informative option text 727 const optionText = keyword.keyword + 728 ' (Score: ' + Math.round(keyword.opportunityScore) + 729 ', Pos: ' + keyword.metrics.averagePosition.toFixed(1) + 730 ', Clicks: ' + keyword.metrics.totalClicks + ')'; 731 732 const option = $('<option></option>') 733 .attr('value', index) 734 .text(optionText); 735 select.append(option); 736 737 }); 738 739 } 740 741 // Keyword selection change 742 $('#keywords-select').on('change', function() { 743 const selectedIndex = $(this).val(); 744 745 if (selectedIndex === '') { 746 handleKeywordSelection(null); // Clear keyword selection 747 return; 748 } 749 750 const selectedKeyword = suggestedKeywords[selectedIndex]; 751 handleKeywordSelection(selectedKeyword); // Use the handleKeywordSelection function 752 753 // Update metrics display 754 $('#metric-clicks').text(selectedKeyword.metrics.totalClicks.toLocaleString()); 755 $('#metric-impressions').text(selectedKeyword.metrics.totalImpressions.toLocaleString()); 756 $('#metric-position').text(selectedKeyword.metrics.averagePosition.toFixed(1)); 757 $('#metric-ctr').text(selectedKeyword.metrics.averageCtr.toFixed(2)); 758 $('#metric-score').text(Math.round(selectedKeyword.opportunityScore)); 759 $('#metric-recommendation').text(selectedKeyword.recommendation); 760 761 $('#keyword-details').show(); 762 }); 763 764 // Write articles button click 765 $('#write-articles-btn').on('click', function() { 766 const keywordValue = currentSelectedKeyword; // Use our variable instead of input field 767 const articleCount = parseInt($('#article-count').val()); 768 const articleStatus = $('#article-status').val(); 769 const ctaLink = $('#cta-link').val().trim(); 770 const ctaCaption = $('#cta-caption').val().trim(); 771 772 773 if (!keywordValue) { 774 alertify.error('Please select a keyword first.'); 775 return; 776 } 777 778 if (!articleCount || articleCount < 1 || articleCount > 10) { 779 alertify.error('Please enter a valid number of articles (1-10).'); 780 return; 781 } 782 783 // Validate CTA link and caption - if link is provided but caption is not, show error 784 if (ctaLink && !ctaCaption) { 785 alertify.error('Please provide a caption for your Call to Action link.'); 786 return; 787 } 788 789 // Show confirmation dialog 790 alertify.confirm( 791 'Write Articles', 792 'Are you sure you want to write ' + articleCount + ' article(s) for the keyword "' + keywordValue + '"?', 793 function() { 794 // User confirmed - submit data to API 795 const $button = $('#write-articles-btn'); 796 const originalText = $button.html(); 797 798 // Show loading state 799 $button.prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i> Processing...'); 800 801 // Get API key from plugin settings 802 const apiKey = lyxityVars.apiKey || ''; 803 804 // Get site URL 805 const siteUrl = lyxityVars.siteUrl || window.location.origin; 806 807 // Prepare data in the required format 808 const requestData = { 809 Keywords: keywordValue, // Use the keywordValue string directly 810 NumberOfArticles: articleCount, 811 SiteUrl: siteUrl, 812 ClickToActionText: ctaCaption, 813 ClickToActionLink: ctaLink, 814 ApiKey: apiKey, 815 ArticleStatus: articleStatus 816 }; 817 818 819 // Make request via WordPress AJAX to proxy through PHP 820 $.ajax({ 821 url: lyxityVars.ajaxUrl, 822 type: 'POST', 823 data: { 824 action: 'lyxity_generate_articles', 825 nonce: lyxityVars.nonce, 826 keywords: requestData.Keywords, 827 number_of_articles: requestData.NumberOfArticles, 828 site_url: requestData.SiteUrl, 829 cta_text: requestData.ClickToActionText, 830 cta_link: requestData.ClickToActionLink 831 }, 832 success: function(response) { 833 // Show a single white-background message per requirements 834 835 alertify.success('Article is being generated in background, you can track progress using Dashboard.'); 836 }, 837 error: function(xhr, status, error) { 838 alertify.error('Error generating articles: ' + (xhr.responseJSON?.message || error || 'Unknown error')); 839 }, 840 complete: function() { 841 // Restore button state 842 $button.prop('disabled', false).html(originalText); 843 } 844 }); 845 }, 846 function() { 847 // User cancelled 848 } 849 ); 850 }); 851 852 // Show keywords error 853 function showKeywordsError(message) { 854 $('#keywords-error-message').text(message); 855 $('#keywords-error').show(); 856 $('#keywords-dropdown-section').hide(); 857 $('#keywords-loading').hide(); 858 } 859 526 860 // Update select all checkbox when individual checkboxes change 527 861 $(document).on('change', '.post-checkbox', function() { -
lyxity/trunk/assets/js/settings.js
r3345835 r3346700 1 1 2 2 3 /** 4 * Settings page JavaScript 5 * 6 * @package Lyxity 7 */ 8 3 9 document.addEventListener('DOMContentLoaded', function() { 4 // Toggle passwordvisibility10 // Toggle API key visibility 5 11 document.getElementById('toggle-password-visibility').addEventListener('click', function(e) { 6 12 e.preventDefault(); … … 18 24 } 19 25 }); 26 27 // Toggle WordPress password visibility 28 if (document.getElementById('toggle-wp-password-visibility')) { 29 document.getElementById('toggle-wp-password-visibility').addEventListener('click', function(e) { 30 e.preventDefault(); 31 const passwordField = document.getElementById('lyxity_wp_password'); 32 const toggleIcon = this.querySelector('.dashicons'); 33 34 if (passwordField.type === 'password') { 35 passwordField.type = 'text'; 36 toggleIcon.classList.remove('dashicons-visibility'); 37 toggleIcon.classList.add('dashicons-hidden'); 38 } else { 39 passwordField.type = 'password'; 40 toggleIcon.classList.remove('dashicons-hidden'); 41 toggleIcon.classList.add('dashicons-visibility'); 42 } 43 }); 44 } 45 46 // Handle settings form submission 47 const settingsForm = document.getElementById('lyxity-settings-form'); 48 if (settingsForm) { 49 settingsForm.addEventListener('submit', function(e) { 50 e.preventDefault(); 51 52 // Get form data 53 const apiKey = document.getElementById('lyxity_api_key').value; 54 const wpApiUrl = document.getElementById('lyxity_wp_api_url').value; 55 const wpUsername = document.getElementById('lyxity_wp_username').value; 56 const wpPassword = document.getElementById('lyxity_wp_password').value; 57 const articleStatus = document.getElementById('lyxity_article_status').value; 58 const articleLength = document.getElementById('lyxity_article_length').value; 59 const defaultLanguage = document.getElementById('lyxity_default_language').value; 60 61 // Get site URL 62 const siteUrl = window.location.origin; 63 64 // Show loading state 65 const submitButton = document.getElementById('lyxity-settings-submit'); 66 const originalButtonText = submitButton.innerHTML; 67 submitButton.disabled = true; 68 submitButton.innerHTML = '<span class="dashicons dashicons-update-alt"></span> Saving...'; 69 70 // Prepare data for API 71 const data = { 72 action: 'lyxity_save_settings', 73 nonce: document.getElementById('lyxity_settings_nonce').value, 74 api_key: apiKey, 75 wp_api_url: wpApiUrl, 76 wp_username: wpUsername, 77 wp_password: wpPassword, 78 article_status: articleStatus, 79 article_length: articleLength, 80 default_language: defaultLanguage, 81 site_url: siteUrl 82 }; 83 84 // Send AJAX request 85 jQuery.post(ajaxurl, data, function(response) { 86 // Display response 87 const responseDiv = document.getElementById('settings-response'); 88 responseDiv.style.display = 'block'; 89 90 if (response.success) { 91 // Ensure we're using success class for successful responses 92 responseDiv.className = 'lyxity-form-response error'; 93 responseDiv.innerHTML = '<p>' + response.data.message + '</p>'; 94 95 } else { 96 responseDiv.className = 'lyxity-form-response error'; 97 responseDiv.innerHTML = '<p>' + response.data.message + '</p>'; 98 } 99 100 // Restore button state 101 submitButton.innerHTML = originalButtonText; 102 submitButton.disabled = false; 103 104 // Scroll to response 105 responseDiv.scrollIntoView({ behavior: 'smooth', block: 'center' }); 106 107 // Hide response after 5 seconds 108 setTimeout(function() { 109 if (response.success) { 110 jQuery(responseDiv).fadeOut(); 111 } 112 }, 5000); 113 }).fail(function(xhr, status, error) { 114 // Handle AJAX failure 115 const responseDiv = document.getElementById('settings-response'); 116 responseDiv.style.display = 'block'; 117 responseDiv.className = 'lyxity-form-response error'; 118 responseDiv.innerHTML = '<p>Error saving settings. Please try again.</p>'; 119 120 // Restore button state 121 submitButton.innerHTML = originalButtonText; 122 submitButton.disabled = false; 123 124 // Scroll to response 125 responseDiv.scrollIntoView({ behavior: 'smooth', block: 'center' }); 126 }); 127 }); 128 } 129 130 // Handle API key update button 131 const updateApiKeyButton = document.getElementById('update-api-key'); 132 if (updateApiKeyButton) { 133 updateApiKeyButton.addEventListener('click', function(e) { 134 e.preventDefault(); 135 136 const apiKey = document.getElementById('lyxity_api_key').value.trim(); 137 138 if (!apiKey) { 139 alertify.error('Please enter an API key.'); 140 return; 141 } 142 143 // Show loading state 144 const originalButtonText = this.innerHTML; 145 this.disabled = true; 146 this.innerHTML = '<span class="dashicons dashicons-update-alt"></span> Updating...'; 147 148 // Prepare data for AJAX 149 const data = { 150 action: 'lyxity_save_api_key', 151 nonce: document.getElementById('lyxity_settings_nonce').value, 152 api_key: apiKey 153 }; 154 155 // Send AJAX request 156 jQuery.post(ajaxurl, data, function(response) { 157 if (response.success) { 158 alertify.success(response.data.message || 'API key updated successfully!'); 159 } else { 160 alertify.error(response.data.message || 'Failed to update API key.'); 161 } 162 163 // Restore button state 164 updateApiKeyButton.innerHTML = originalButtonText; 165 updateApiKeyButton.disabled = false; 166 }).fail(function(xhr, status, error) { 167 alertify.error('Error updating API key. Please try again.'); 168 169 // Restore button state 170 updateApiKeyButton.innerHTML = originalButtonText; 171 updateApiKeyButton.disabled = false; 172 }); 173 }); 174 } 175 176 // Add focus effects to form fields 177 const formControls = document.querySelectorAll('.lyxity-form-control'); 178 formControls.forEach(function(control) { 179 control.addEventListener('focus', function() { 180 this.closest('.lyxity-form-group').classList.add('is-focused'); 181 }); 182 183 control.addEventListener('blur', function() { 184 this.closest('.lyxity-form-group').classList.remove('is-focused'); 185 }); 186 }); 20 187 }); -
lyxity/trunk/includes/class-lyxity-api.php
r3345835 r3346700 14 14 * API endpoint 15 15 */ 16 16 17 private $api_endpoint = 'https://lyxity.com/api/'; 18 17 19 /** 18 20 * Request timeout in seconds … … 29 31 /** 30 32 * Make API request 31 */ 32 private function make_request($endpoint, $data = array()) { 33 * 34 * @param string $endpoint API endpoint 35 * @param array $data Request data 36 * @return array|WP_Error Response array or WP_Error on failure 37 */ 38 public function make_request($endpoint, $data = array()) { 39 33 40 $api_key = $this->get_api_key(); 34 41 if (empty($api_key)) { … … 38 45 $url = $this->api_endpoint . $endpoint; 39 46 47 // Determine if we're using a local/dev environment based on the API endpoint 48 $is_dev_environment = (strpos($this->api_endpoint, 'localhost') !== false || strpos($this->api_endpoint, '127.0.0.1') !== false); 49 40 50 $args = array( 41 51 'headers' => array( … … 52 62 'curl_timeout' => $this->timeout 53 63 ); 54 55 64 56 65 57 66 $response = wp_remote_post($url, $args); … … 63 72 $status = wp_remote_retrieve_response_code($response); 64 73 $body = wp_remote_retrieve_body($response); 74 65 75 66 76 $decoded = json_decode($body, true); … … 69 79 return new WP_Error('json_decode_error', 'Failed to decode API response'); 70 80 } 81 71 82 72 83 return $decoded; … … 81 92 * @return array|WP_Error Response array or WP_Error on failure 82 93 */ 94 /** 95 * Get site settings from the API 96 * 97 * @return array|WP_Error Response array or WP_Error on failure 98 */ 99 public function get_settings() { 100 101 $site_url = get_site_url(); 102 $api_key = $this->get_api_key(); 103 104 $data = array( 105 'siteUrl' => $site_url, 106 'apiKey' => $api_key 107 ); 108 109 110 $result = $this->make_request('site/getsettings', $data); 111 112 113 return $result; 114 } 115 83 116 public function log_content_update($page_url, $appended_content, $update_type = 1) { 84 117 … … 105 138 106 139 140 // Determine if we're using a local/dev environment based on the API endpoint 141 $is_dev_environment = (strpos($this->api_endpoint, 'localhost') !== false || strpos($this->api_endpoint, '127.0.0.1') !== false); 142 107 143 $args = array( 108 144 'method' => 'POST', … … 114 150 'sslverify' => true 115 151 ); 152 116 153 117 154 $response = wp_remote_post($endpoint_url, $args); … … 123 160 $response_code = wp_remote_retrieve_response_code($response); 124 161 $body = wp_remote_retrieve_body($response); 162 125 163 126 164 $data = json_decode($body, true); … … 463 501 464 502 /** 503 * Generate articles via Lyxity API 504 * 505 * @param array $data Payload including Keys: Keywords, NumberOfArticles, SiteUrl, ClickToActionText, ClickToActionLink 506 * @return array|WP_Error 507 */ 508 public function generate_articles($data = array()) { 509 // Basic validation 510 if (empty($data['Keywords']) || empty($data['NumberOfArticles'])) { 511 return new WP_Error('missing_params', 'Keywords and NumberOfArticles are required'); 512 } 513 // Ensure ApiKey is included in payload as required by API 514 $api_key = $this->get_api_key(); 515 if (empty($api_key)) { 516 return new WP_Error('missing_api_key', 'API key is not configured'); 517 } 518 // API expects 'ApiKey' in the body (note casing) 519 $data['ApiKey'] = $api_key; 520 return $this->make_request('articles/generate', $data); 521 } 522 523 /** 524 * Get real-time status for article generation requests. 525 * 526 * Expected $params keys (casing per API): 527 * - SiteUrl (required) 528 * - RequestId (optional) 529 * - PageSize (optional) 530 * - Page (optional) 531 * 532 * @param array $params 533 * @return array|WP_Error 534 */ 535 public function get_realtime_status($params = array()) { 536 537 $api_key = $this->get_api_key(); 538 if (empty($api_key)) { 539 return new WP_Error('missing_api_key', 'API key is not configured'); 540 } 541 if (empty($params['SiteUrl'])) { 542 $params['SiteUrl'] = get_site_url(); 543 } 544 $payload = array( 545 'ApiKey' => $api_key, 546 'SiteUrl' => $params['SiteUrl'], 547 ); 548 if (!empty($params['RequestId'])) { 549 $payload['RequestId'] = (int) $params['RequestId']; 550 } 551 if (!empty($params['PageSize'])) { 552 $payload['PageSize'] = max(1, (int) $params['PageSize']); 553 } 554 if (!empty($params['Page'])) { 555 $payload['Page'] = max(1, (int) $params['Page']); 556 } 557 558 559 $result = $this->make_request('articles/realtime', $payload); 560 561 562 return $result; 563 } 564 565 /** 465 566 * Update content for a Gutenberg post 466 567 * … … 577 678 return $this->make_request('Dashboard/GetDashboardDataV2', $data); 578 679 } 680 681 /** 682 * Get suggested keywords from the API 683 * 684 * @return array|WP_Error Response array or WP_Error on failure 685 */ 686 public function get_suggested_keywords() { 687 688 $site_url = get_site_url(); 689 $api_key = $this->get_api_key(); 690 691 692 $data = array( 693 'siteUrl' => $site_url, 694 'apiKey' => $api_key 695 ); 696 697 698 $result = $this->make_request('articles/suggestkeyword', $data); 699 700 701 return $result; 702 } 579 703 } -
lyxity/trunk/lyxity.php
r3345835 r3346700 3 3 Plugin Name: Lyxity 4 4 Description: The art of modern search engine optimization 5 Version: 1. 3.05 Version: 1.4.0 6 6 Author: Infoforte 7 7 Author URI: https://infoforte.com … … 20 20 21 21 // Define plugin constants 22 define('LYXITY_VERSION', '1. 3.0');22 define('LYXITY_VERSION', '1.4.0'); 23 23 define('LYXITY_FILE', __FILE__); 24 24 define('LYXITY_PATH', plugin_dir_path(__FILE__)); 25 25 define('LYXITY_URL', plugin_dir_url(__FILE__)); 26 define('LYXITY_DEBUG', true); // Set to false inproduction26 define('LYXITY_DEBUG', false); // Disabled for production 27 27 28 28 // Remove the logger class include … … 68 68 add_action('wp_ajax_lyxity_schedule_bulk_rewrite', array($this, 'ajax_schedule_bulk_rewrite')); 69 69 70 // Add AJAX handlers for settings 71 add_action('wp_ajax_lyxity_save_settings', array($this, 'ajax_save_settings')); 72 add_action('wp_ajax_lyxity_save_api_key', array($this, 'ajax_save_api_key')); 73 add_action('wp_ajax_lyxity_get_suggested_keywords', array($this, 'ajax_get_suggested_keywords')); 74 // New: Generate articles via Lyxity API 75 add_action('wp_ajax_lyxity_generate_articles', array($this, 'ajax_generate_articles')); 76 // New: Real-time status proxy 77 add_action('wp_ajax_lyxity_realtime_status', array($this, 'ajax_realtime_status')); 78 70 79 // Add cron hooks 71 80 add_action('lyxity_bulk_update_cron', array($this, 'process_bulk_update'), 10, 1); … … 101 110 102 111 // Add dashboard-specific styles 112 113 if (strpos($hook, 'hazel-harlow-dashboard') !== false || strpos($hook, 'Lyxity-dashboard') !== false) { 114 wp_enqueue_style('hazel-dashboard', LYXITY_URL . 'assets/css/dashboard.css', array(), LYXITY_VERSION); 115 // Also load realtime styles on Dashboard (merged view) 116 wp_enqueue_style('lyxity-realtime-status', LYXITY_URL . 'assets/css/realtime.css', array(), LYXITY_VERSION); 117 } 118 119 // Add real-time status page styles 120 if (strpos($hook, 'Lyxity-realtime') !== false) { 121 wp_enqueue_style('lyxity-realtime-status', LYXITY_URL . 'assets/css/realtime.css', array(), LYXITY_VERSION); 122 } 123 103 124 if (strpos($hook, 'lyxity-dashboard') !== false || strpos($hook, 'Lyxity-dashboard') !== false) { 104 125 wp_enqueue_style('lyxity-dashboard', LYXITY_URL . 'assets/css/dashboard.css', array(), LYXITY_VERSION); … … 115 136 wp_register_script('bootstrap-js', LYXITY_URL . 'assets/js/vendor/bootstrap.bundle.min.js', array('jquery', 'popper-js'), '5.3.5', true); 116 137 wp_register_script('alertify-js', LYXITY_URL . 'assets/js/vendor/alertify.min.js', array('jquery'), '1.14.0', true); 138 wp_register_script('hazel-harlow-admin', LYXITY_URL . 'assets/js/admin.js', array('jquery', 'bootstrap-js', 'alertify-js'), LYXITY_VERSION, true); 139 wp_register_script('hazel-main-page-js', LYXITY_URL . 'assets/js/main-page.js', array('jquery', 'bootstrap-js', 'alertify-js'), LYXITY_VERSION, true); 140 // New: Real-time Status page script 141 wp_register_script('lyxity-realtime-status-js', LYXITY_URL . 'assets/js/realtime-status.js', array('jquery', 'bootstrap-js', 'alertify-js'), LYXITY_VERSION, true); 117 142 wp_register_script('lyxity-admin', LYXITY_URL . 'assets/js/admin.js', array('jquery', 'bootstrap-js', 'alertify-js'), LYXITY_VERSION, true); 118 143 wp_register_script('lyxity-main-page-js', LYXITY_URL . 'assets/js/main-page.js', array('jquery', 'bootstrap-js', 'alertify-js'), LYXITY_VERSION, true); … … 132 157 } 133 158 159 160 if (strpos($hook, 'hazel-harlow-dashboard') !== false || strpos($hook, 'Lyxity-dashboard') !== false) { 161 wp_enqueue_script('hazel-dashboard-js', LYXITY_URL . 'assets/js/dashboard.js', array('jquery'), LYXITY_VERSION, true); 162 // Also load realtime script on Dashboard (merged view) 163 wp_enqueue_script('lyxity-realtime-status-js'); 164 } 165 // Enqueue real-time status page assets 166 if (strpos($hook, 'Lyxity-realtime') !== false) { 167 wp_enqueue_script('lyxity-realtime-status-js'); 168 } 169 134 170 if (strpos($hook, 'lyxity-dashboard') !== false || strpos($hook, 'Lyxity-dashboard') !== false) { 135 171 wp_enqueue_script('lyxity-dashboard-js', LYXITY_URL . 'assets/js/dashboard.js', array('jquery'), LYXITY_VERSION, true); … … 178 214 179 215 // Localize all scripts that need these variables 216 217 wp_localize_script('hazel-harlow-admin', 'hazelHarlowVars', $localization_array); 218 wp_localize_script('hazel-main-page-js', 'hazelHarlowVars', $localization_array); 219 // New: also localize with lyxityVars for consistency across JS files 220 wp_localize_script('hazel-harlow-admin', 'lyxityVars', $localization_array); 221 wp_localize_script('hazel-main-page-js', 'lyxityVars', $localization_array); 222 // Localize real-time status script as well 223 if (wp_script_is('lyxity-realtime-status-js', 'registered')) { 224 wp_localize_script('lyxity-realtime-status-js', 'lyxityVars', $localization_array); 225 wp_localize_script('lyxity-realtime-status-js', 'hazelHarlowVars', $localization_array); 226 } 227 180 228 wp_localize_script('lyxity-admin', 'lyxityVars', $localization_array); 181 229 wp_localize_script('lyxity-main-page-js', 'lyxityVars', $localization_array); … … 187 235 'nonce' => wp_create_nonce('lyxity-ajax-nonce') 188 236 )); 189 } 237 // Also localize lyxityVars for dashboard 238 wp_localize_script('hazel-dashboard-js', 'lyxityVars', array( 239 'ajaxUrl' => esc_url(admin_url('admin-ajax.php')), 240 'nonce' => wp_create_nonce('lyxity-ajax-nonce') 241 )); 242 } 243 // END enqueue_admin_assets 244 } 245 246 /** 247 * Render Real-time Status page 248 */ 249 public function render_realtime_status_page() { 250 if (!current_user_can('edit_posts')) { 251 wp_die(esc_html__('You do not have sufficient permissions to access this page.', 'lyxity')); 252 } 253 $site_url = esc_url(get_site_url()); 254 ?> 255 <div class="wrap lyxity-dashboard"> 256 <div class="lyxity-dashboard-header"> 257 <h1 class="wp-heading-inline" style="display:flex;align-items:center;gap:10px;"> 258 <span class="dashboard-logo"> 259 <img src="<?php echo esc_url( defined('LYXITY_URL') ? LYXITY_URL . 'assets/images/logo.png' : plugins_url('assets/images/logo.png', __FILE__) ); ?>" alt="Lyxity" /> 260 </span> 261 <?php echo esc_html__('Article Generation Dashboard', 'lyxity'); ?> 262 </h1> 263 <div class="lyxity-dashboard-controls"> 264 <button type="button" class="button button-primary" id="rt-refresh-btn"> 265 <span class="dashicons dashicons-update" style="font-size:16px;line-height:1;"></span> 266 <?php echo esc_html__('Refresh', 'lyxity'); ?> 267 </button> 268 <label class="lyxity-auto-refresh-toggle"> 269 <input type="checkbox" id="rt-auto-refresh" /> 270 <span class="dashicons dashicons-clock" style="font-size:16px;margin-right:4px;"></span> 271 <?php echo esc_html__('Auto-refresh', 'lyxity'); ?> 272 </label> 273 </div> 274 </div> 275 276 <!-- System Status Cards --> 277 <div class="lyxity-status-grid" id="rt-system-status"> 278 <div class="lyxity-status-card lyxity-loading"> 279 <div class="lyxity-status-icon"> 280 <span class="dashicons dashicons-clock"></span> 281 </div> 282 <div class="lyxity-status-content"> 283 <div class="lyxity-status-number">—</div> 284 <div class="lyxity-status-label"><?php echo esc_html__('Queued', 'lyxity'); ?></div> 285 </div> 286 </div> 287 <div class="lyxity-status-card lyxity-loading"> 288 <div class="lyxity-status-icon"> 289 <span class="dashicons dashicons-update-alt"></span> 290 </div> 291 <div class="lyxity-status-content"> 292 <div class="lyxity-status-number">—</div> 293 <div class="lyxity-status-label"><?php echo esc_html__('Processing', 'lyxity'); ?></div> 294 </div> 295 </div> 296 <div class="lyxity-status-card lyxity-loading"> 297 <div class="lyxity-status-icon"> 298 <span class="dashicons dashicons-yes-alt"></span> 299 </div> 300 <div class="lyxity-status-content"> 301 <div class="lyxity-status-number">—</div> 302 <div class="lyxity-status-label"><?php echo esc_html__('Completed', 'lyxity'); ?></div> 303 </div> 304 </div> 305 <div class="lyxity-status-card lyxity-loading"> 306 <div class="lyxity-status-icon"> 307 <span class="dashicons dashicons-dismiss"></span> 308 </div> 309 <div class="lyxity-status-content"> 310 <div class="lyxity-status-number">—</div> 311 <div class="lyxity-status-label"><?php echo esc_html__('Failed', 'lyxity'); ?></div> 312 </div> 313 </div> 314 <div class="lyxity-status-card lyxity-loading"> 315 <div class="lyxity-status-icon"> 316 <span class="dashicons dashicons-edit-page"></span> 317 </div> 318 <div class="lyxity-status-content"> 319 <div class="lyxity-status-number">—</div> 320 <div class="lyxity-status-label"><?php echo esc_html__('Articles Generated', 'lyxity'); ?></div> 321 </div> 322 </div> 323 <div class="lyxity-status-card lyxity-loading"> 324 <div class="lyxity-status-icon"> 325 <span class="dashicons dashicons-wordpress-alt"></span> 326 </div> 327 <div class="lyxity-status-content"> 328 <div class="lyxity-status-number">—</div> 329 <div class="lyxity-status-label"><?php echo esc_html__('Pending WP Posts', 'lyxity'); ?></div> 330 </div> 331 </div> 332 </div> 333 334 <!-- Requests Table --> 335 <div class="lyxity-requests-section"> 336 <div class="lyxity-section-header"> 337 <h2><?php echo esc_html__('Recent Requests', 'lyxity'); ?></h2> 338 <div class="lyxity-pagination-controls"> 339 <button type="button" class="button" id="rt-prev-btn" disabled> 340 <span class="dashicons dashicons-arrow-left-alt2"></span> 341 </button> 342 <span id="rt-page-indicator" class="lyxity-page-info"><?php echo esc_html__('Page 1 of 1', 'lyxity'); ?></span> 343 <button type="button" class="button" id="rt-next-btn" disabled> 344 <span class="dashicons dashicons-arrow-right-alt2"></span> 345 </button> 346 </div> 347 </div> 348 <div class="lyxity-table-container"> 349 <table class="wp-list-table widefat fixed striped" id="rt-requests-table"> 350 <thead> 351 <tr> 352 <th class="column-keyword"><?php echo esc_html__('Keyword', 'lyxity'); ?></th> 353 <th class="column-created"><?php echo esc_html__('Created', 'lyxity'); ?></th> 354 <th class="column-updated"><?php echo esc_html__('Updated', 'lyxity'); ?></th> 355 <th class="column-articles"><?php echo esc_html__('Articles', 'lyxity'); ?></th> 356 <th class="column-status"><?php echo esc_html__('Status', 'lyxity'); ?></th> 357 </tr> 358 </thead> 359 <tbody> 360 <tr class="lyxity-loading-row"> 361 <td colspan="5"> 362 <div class="lyxity-loading-indicator"> 363 <span class="spinner is-active"></span> 364 <?php echo esc_html__('Loading requests...', 'lyxity'); ?> 365 </div> 366 </td> 367 </tr> 368 </tbody> 369 </table> 370 </div> 371 </div> 372 </div> 373 <?php 374 } 375 376 /** 377 * AJAX: Real-time status proxy 378 */ 379 public function ajax_realtime_status() { 380 // Security 381 if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'lyxity-ajax-nonce')) { 382 wp_send_json_error(array('message' => 'Security check failed.'), 403); 383 } 384 if (!current_user_can('edit_posts')) { 385 wp_send_json_error(array('message' => 'Insufficient permissions.'), 403); 386 } 387 388 // Sanitize inputs 389 $site_url = isset($_POST['site_url']) ? esc_url_raw(wp_unslash($_POST['site_url'])) : get_site_url(); 390 $request_id = isset($_POST['request_id']) ? absint($_POST['request_id']) : null; 391 $page_size = isset($_POST['page_size']) ? max(1, min(100, absint($_POST['page_size']))) : 50; 392 $page = isset($_POST['page']) ? max(1, absint($_POST['page'])) : 1; 393 394 $params = array( 395 'SiteUrl' => $site_url, 396 'PageSize' => $page_size, 397 'Page' => $page, 398 ); 399 if (!empty($request_id)) { 400 $params['RequestId'] = $request_id; 401 } 402 403 $api = new Lyxity_API(); 404 $result = $api->get_realtime_status($params); 405 406 if (is_wp_error($result)) { 407 wp_send_json_error(array('message' => $result->get_error_message()), 500); 408 } 409 wp_send_json_success($result); 190 410 } 191 411 … … 207 427 'absint' // Use WordPress core function directly 208 428 ); 429 430 // Register new settings 431 register_setting('lyxity-settings-group', 'lyxity_wp_api_url', 'sanitize_text_field'); 432 register_setting('lyxity-settings-group', 'lyxity_wp_username', 'sanitize_text_field'); 433 register_setting('lyxity-settings-group', 'lyxity_wp_password', 'sanitize_text_field'); 434 register_setting('lyxity-settings-group', 'lyxity_article_status', 'sanitize_text_field'); 435 register_setting('lyxity-settings-group', 'lyxity_article_length', 'absint'); 436 register_setting('lyxity-settings-group', 'lyxity_default_language', 'sanitize_text_field'); 209 437 } 210 438 … … 217 445 public function sanitize_api_key($input) { 218 446 return sanitize_text_field($input); 447 } 448 449 /** 450 * AJAX handler for saving settings to the API 451 */ 452 public function ajax_save_settings() { 453 // Check nonce 454 if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'lyxity_settings_nonce')) { 455 wp_send_json_error(array('message' => 'Security check failed.')); 456 } 457 458 // Check user capabilities 459 if (!current_user_can('manage_options')) { 460 wp_send_json_error(array('message' => 'You do not have permission to perform this action.')); 461 } 462 463 // Get API key 464 $api_key = isset($_POST['api_key']) ? sanitize_text_field($_POST['api_key']) : ''; 465 466 if (empty($api_key)) { 467 wp_send_json_error(array('message' => 'API key is required.')); 468 } 469 470 // Get other settings 471 $wp_api_url = isset($_POST['wp_api_url']) ? sanitize_text_field($_POST['wp_api_url']) : ''; 472 $wp_username = isset($_POST['wp_username']) ? sanitize_text_field($_POST['wp_username']) : ''; 473 $wp_password = isset($_POST['wp_password']) ? sanitize_text_field($_POST['wp_password']) : ''; 474 $article_status = isset($_POST['article_status']) ? sanitize_text_field($_POST['article_status']) : 'draft'; 475 $article_length = isset($_POST['article_length']) ? absint($_POST['article_length']) : 1500; 476 $default_language = isset($_POST['default_language']) ? sanitize_text_field($_POST['default_language']) : 'en-GB'; 477 $site_url = isset($_POST['site_url']) ? sanitize_text_field($_POST['site_url']) : get_site_url(); 478 479 // Create API instance 480 $api = new Lyxity_API(); 481 482 // Prepare data for API 483 $data = array( 484 'apiKey' => $api_key, 485 'wpApiUrl' => $wp_api_url, 486 'wpUsername' => $wp_username, 487 'wpPassword' => $wp_password, 488 'articleStatus' => $article_status, 489 'articleLength' => $article_length, 490 'defaultLanguage' => $default_language, 491 'siteUrl' => $site_url 492 ); 493 494 495 // Send to API 496 $response = $api->make_request('site/updatesettings', $data); 497 498 499 if (is_wp_error($response)) { 500 $error_message = $response->get_error_message(); 501 wp_send_json_error(array('message' => 'API Error: ' . $error_message)); 502 } 503 504 if (!isset($response['result']) || $response['result'] !== true) { 505 $error_message = isset($response['message']) ? $response['message'] : 'Unknown API error'; 506 wp_send_json_error(array('message' => $error_message)); 507 } 508 509 // Only save API key locally 510 // update_option('lyxity_api_key', $api_key); 511 // All other settings are only sent to the API, not stored locally 512 513 wp_send_json_success(array('message' => 'Settings saved successfully.')); 514 } 515 516 /** 517 * AJAX handler for saving API key locally 518 */ 519 public function ajax_save_api_key() { 520 // Check nonce 521 if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'lyxity_settings_nonce')) { 522 wp_send_json_error(array('message' => 'Security check failed.')); 523 } 524 525 // Check user capabilities 526 if (!current_user_can('manage_options')) { 527 wp_send_json_error(array('message' => 'You do not have permission to perform this action.')); 528 } 529 530 // Get API key 531 $api_key = isset($_POST['api_key']) ? sanitize_text_field($_POST['api_key']) : ''; 532 533 if (empty($api_key)) { 534 wp_send_json_error(array('message' => 'API key is required.')); 535 } 536 537 // Save API key locally 538 update_option('lyxity_api_key', $api_key); 539 540 wp_send_json_success(array('message' => 'API key updated successfully.')); 541 } 542 543 /** 544 * AJAX handler for fetching suggested keywords 545 */ 546 public function ajax_get_suggested_keywords() { 547 548 // Check nonce 549 if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'lyxity-ajax-nonce')) { 550 wp_send_json_error(array('message' => 'Security check failed.')); 551 } 552 553 554 // Check user capabilities 555 if (!current_user_can('edit_posts')) { 556 wp_send_json_error(array('message' => 'You do not have permission to perform this action.')); 557 } 558 559 560 // Get API instance and fetch suggested keywords 561 $api = new Lyxity_API(); 562 $response = $api->get_suggested_keywords(); 563 564 565 if (is_wp_error($response)) { 566 wp_send_json_error(array('message' => 'API Error: ' . $response->get_error_message())); 567 } 568 569 570 // For keywords API, check if suggestedKeywords exists instead of success flag 571 if (!isset($response['suggestedKeywords'])) { 572 $error_message = isset($response['message']) ? $response['message'] : 'No keywords found in response'; 573 wp_send_json_error(array('message' => $error_message)); 574 } 575 576 577 wp_send_json_success($response); 578 } 579 580 /** 581 * AJAX handler: Generate articles via Lyxity API 582 */ 583 public function ajax_generate_articles() { 584 // Verify nonce 585 check_ajax_referer('lyxity-ajax-nonce', 'nonce'); 586 587 // Capability check 588 if (!current_user_can('edit_posts')) { 589 wp_send_json_error(array('message' => 'Insufficient permissions')); 590 } 591 592 // Sanitize inputs 593 $keywords = isset($_POST['keywords']) ? sanitize_text_field(wp_unslash($_POST['keywords'])) : ''; 594 $number_of_articles = isset($_POST['number_of_articles']) ? absint(wp_unslash($_POST['number_of_articles'])) : 0; 595 $site_url = isset($_POST['site_url']) ? esc_url_raw(wp_unslash($_POST['site_url'])) : get_site_url(); 596 $cta_text = isset($_POST['cta_text']) ? sanitize_text_field(wp_unslash($_POST['cta_text'])) : ''; 597 $cta_link = isset($_POST['cta_link']) ? esc_url_raw(wp_unslash($_POST['cta_link'])) : ''; 598 599 if (empty($keywords) || $number_of_articles <= 0) { 600 wp_send_json_error(array('message' => 'Missing or invalid parameters')); 601 } 602 603 // Prepare payload for API (use expected casing) 604 $payload = array( 605 'Keywords' => $keywords, 606 'NumberOfArticles' => $number_of_articles, 607 'SiteUrl' => $site_url, 608 'ClickToActionText' => $cta_text, 609 'ClickToActionLink' => $cta_link, 610 ); 611 612 // Call API via centralized class 613 $api = new Lyxity_API(); 614 $response = $api->generate_articles($payload); 615 616 if (is_wp_error($response)) { 617 wp_send_json_error(array('message' => 'API Error: ' . $response->get_error_message())); 618 } 619 620 wp_send_json_success($response); 219 621 } 220 622 … … 296 698 array($this, 'render_settings_page') 297 699 ); 700 701 // Real-time Status submenu removed (merged into Dashboard) 298 702 } 299 703 … … 608 1012 <?php endif; ?> 609 1013 </div> 1014 1015 <!-- Write Articles Card --> 1016 <div class="card"> 1017 <h2><?php esc_html_e('Write Articles', 'lyxity'); ?></h2> 1018 1019 <div style="display: flex; gap: 30px; align-items: flex-start;"> 1020 <!-- Left Column: Form --> 1021 <div style="flex: 1; min-width: 0;"> 1022 <form id="write-articles-form"> 1023 <table class="form-table"> 1024 <tr> 1025 <th scope="row"><?php esc_html_e('Keyword Source:', 'lyxity'); ?></th> 1026 <td> 1027 <label> 1028 <input type="radio" name="keyword-source" value="suggested" id="suggested-source"> 1029 <?php esc_html_e('AI Suggested', 'lyxity'); ?> 1030 </label> 1031 1032 <label> 1033 <input type="radio" name="keyword-source" value="manual" id="manual-source"> 1034 <?php esc_html_e('Manual Entry', 'lyxity'); ?> 1035 </label> 1036 </td> 1037 </tr> 1038 </table> 1039 1040 <!-- Suggested Keywords Section --> 1041 <div id="suggested-keywords-section" style="display: none;"> 1042 <?php if (!$api_key_set): ?> 1043 <table class="form-table"> 1044 <tr> 1045 <th scope="row"></th> 1046 <td> 1047 <p class="description" style="color: #d63638;"><?php esc_html_e('Please set your API key in settings to use this feature', 'lyxity'); ?></p> 1048 </td> 1049 </tr> 1050 </table> 1051 <?php else: ?> 1052 <table class="form-table" id="keywords-dropdown-section"> 1053 <tr> 1054 <th scope="row" style="width: 150px;"><label for="keywords-select"><?php esc_html_e('Select Keyword:', 'lyxity'); ?></label></th> 1055 <td> 1056 <select id="keywords-select" style="width: 300px;"> 1057 <option value=""><?php esc_html_e('Loading keywords...', 'lyxity'); ?></option> 1058 </select> 1059 </td> 1060 </tr> 1061 </table> 1062 <?php endif; ?> 1063 </div> 1064 1065 <!-- Manual Keyword Section --> 1066 <div id="manual-keyword-section" style="display: none;"> 1067 <table class="form-table"> 1068 <tr> 1069 <th scope="row" style="width: 150px;"><label for="manual-keyword-input"><?php esc_html_e('Enter Keyword:', 'lyxity'); ?></label></th> 1070 <td> 1071 <input type="text" id="manual-keyword-input" class="regular-text" style="width: 300px;" placeholder="<?php esc_attr_e('e.g., best productivity apps', 'lyxity'); ?>"> 1072 </td> 1073 </tr> 1074 </table> 1075 </div> 1076 1077 <!-- Article Generation Section --> 1078 <div id="article-generation-section" style="display: none;"> 1079 <table class="form-table"> 1080 <tr> 1081 <th scope="row" style="width: 150px;"><label for="article-count"><?php esc_html_e('Number of Articles:', 'lyxity'); ?></label></th> 1082 <td> 1083 <input type="number" id="article-count" style="width: 300px;" min="1" max="10" value="1"> 1084 </td> 1085 </tr> 1086 1087 <tr> 1088 <th scope="row" style="width: 150px;"><label for="cta-link"><?php esc_html_e('Call to Action Link:', 'lyxity'); ?></label></th> 1089 <td> 1090 <input type="url" id="cta-link" style="width: 300px;" placeholder="<?php esc_attr_e('https://example.com/your-link', 'lyxity'); ?>"> 1091 <p class="description"><?php esc_html_e('Optional. Add a call to action link for your articles.', 'lyxity'); ?></p> 1092 </td> 1093 </tr> 1094 <tr> 1095 <th scope="row" style="width: 150px;"><label for="cta-caption"><?php esc_html_e('CTA Caption:', 'lyxity'); ?></label></th> 1096 <td> 1097 <input type="text" id="cta-caption" style="width: 300px;" placeholder="<?php esc_attr_e('Learn More', 'lyxity'); ?>"> 1098 <p class="description"><?php esc_html_e('Optional. Caption text for the call to action link.', 'lyxity'); ?></p> 1099 </td> 1100 </tr> 1101 <tr> 1102 <th scope="row" style="width: 150px;"></th> 1103 <td> 1104 <button type="button" id="write-articles-btn" class="button button-primary" disabled> 1105 <i class="fas fa-pen"></i> <?php esc_html_e('Write Articles', 'lyxity'); ?> 1106 </button> 1107 </td> 1108 </tr> 1109 </table> 1110 </div> 1111 1112 <div id="keywords-loading" style="display: none; text-align: center; padding: 20px;"> 1113 <i class="fas fa-spinner fa-spin"></i> <?php esc_html_e('Fetching suggested keywords...', 'lyxity'); ?> 1114 </div> 1115 1116 <div id="keywords-error" style="display: none;" class="notice notice-error"> 1117 <p id="keywords-error-message"></p> 1118 </div> 1119 </form> 1120 </div> 1121 1122 <!-- Right Column: Metrics --> 1123 <div id="metrics-column" style="flex: 1; min-width: 0; border-left: 1px solid #ddd; padding-left: 30px; display: none;"> 1124 <div id="keyword-details"> 1125 <h3 style="margin-top: 0;"><?php esc_html_e('Keyword Metrics', 'lyxity'); ?></h3> 1126 <table class="form-table"> 1127 <tr> 1128 <th scope="row"><?php esc_html_e('Performance Data:', 'lyxity'); ?></th> 1129 <td> 1130 <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin-bottom: 15px;"> 1131 <div> 1132 <strong><?php esc_html_e('Total Clicks:', 'lyxity'); ?></strong><br> 1133 <span id="metric-clicks" style="font-size: 1.2em; color: #0073aa;">-</span> 1134 </div> 1135 <div> 1136 <strong><?php esc_html_e('Total Impressions:', 'lyxity'); ?></strong><br> 1137 <span id="metric-impressions" style="font-size: 1.2em; color: #0073aa;">-</span> 1138 </div> 1139 <div> 1140 <strong><?php esc_html_e('Average Position:', 'lyxity'); ?></strong><br> 1141 <span id="metric-position" style="font-size: 1.2em; color: #0073aa;">-</span> 1142 </div> 1143 <div> 1144 <strong><?php esc_html_e('Average CTR:', 'lyxity'); ?></strong><br> 1145 <span id="metric-ctr" style="font-size: 1.2em; color: #0073aa;">-</span>% 1146 </div> 1147 </div> 1148 </td> 1149 </tr> 1150 <tr> 1151 <th scope="row"><?php esc_html_e('Opportunity Score:', 'lyxity'); ?></th> 1152 <td> 1153 <span id="metric-score" style="font-size: 1.5em; color: #d63638; font-weight: bold;">-</span> 1154 </td> 1155 </tr> 1156 <tr> 1157 <th scope="row"><?php esc_html_e('Recommendation:', 'lyxity'); ?></th> 1158 <td> 1159 <p id="metric-recommendation" style="margin: 0; font-style: italic; background: #f0f0f1; padding: 10px; border-radius: 4px;">-</p> 1160 </td> 1161 </tr> 1162 </table> 1163 </div> 1164 </div> 1165 </div> 610 1166 </div> 1167 1168 1169 </div> 1170 611 1171 612 1172 <!-- Update Preview Modal --> -
lyxity/trunk/readme.txt
r3345835 r3346700 4 4 Requires at least: 5.0 5 5 Tested up to: 6.8 6 Stable tag: 1. 3.06 Stable tag: 1.4.0 7 7 Requires PHP: 7.0 8 8 License: GPLv2 or later … … 82 82 83 83 == Changelog == 84 = 1.4.0 = 85 - Added content creation feature 86 84 87 = 1.3.0 = 85 88 * Complete WordPress.org compliance update -
lyxity/trunk/templates/dashboard-page.php
r3345835 r3346700 30 30 echo '<span style="font-size: 24px; font-weight: bold; margin-right: 10px;"><span style="color: black;">Lyxity</span></span>'; 31 31 } 32 echo '<span class="wp-heading-inline">' . esc_html__(' AnalyticsDashboard', 'lyxity') . '</span>';32 echo '<span class="wp-heading-inline">' . esc_html__('Dashboard', 'lyxity') . '</span>'; 33 33 ?> 34 34 </h1> 35 35 36 36 <div class="date-range-selector"> 37 <div class="d-flex align-items-center"> 38 <div class="d-flex align-items-center me-4"> 39 <label class="date-selector-label" for="start-date"><?php esc_html_e('Start Date', 'lyxity'); ?></label> 40 <input type="date" id="start-date" class="form-control"> 41 </div> 42 43 <div class="d-flex align-items-center"> 44 <label class="date-selector-label" for="end-date"><?php esc_html_e('End Date', 'lyxity'); ?></label> 45 <input type="date" id="end-date" class="form-control"> 37 <div class="row g-3"> 38 <div class="col-12 col-sm-6 col-lg-4 col-xl-3"> 39 <div class="d-flex flex-column flex-sm-row align-items-sm-center"> 40 <label class="date-selector-label me-sm-2 mb-1 mb-sm-0" for="start-date"><?php esc_html_e('Start Date', 'lyxity'); ?></label> 41 <input type="date" id="start-date" class="form-control w-100"> 42 </div> 43 </div> 44 45 <div class="col-12 col-sm-6 col-lg-4 col-xl-3"> 46 <div class="d-flex flex-column flex-sm-row align-items-sm-center"> 47 <label class="date-selector-label me-sm-2 mb-1 mb-sm-0" for="end-date"><?php esc_html_e('End Date', 'lyxity'); ?></label> 48 <input type="date" id="end-date" class="form-control w-100"> 49 </div> 46 50 </div> 47 51 </div> … … 49 53 </div> 50 54 51 <div class="dashboard-stats"> 52 <div class="stat-card update-card"> 53 <i class="fas fa-sync-alt"></i> 54 <h3><?php esc_html_e('Content Updates', 'lyxity'); ?></h3> 55 <div class="number" id="update-count">0</div> 56 <p class="text-muted"><?php esc_html_e('Total updates in selected period', 'lyxity'); ?></p> 55 <div class="lyxity-dashboard-container"> 56 <!-- Left Column: Content Updates Section --> 57 <div class="lyxity-dashboard-column"> 58 <div class="lyxity-section-header"> 59 <h2><?php echo esc_html__('Content Updates', 'lyxity'); ?></h2> 60 </div> 61 62 <div class="lyxity-card-group"> 63 <!-- Content Updates Card --> 64 <div class="lyxity-card update-card"> 65 <div class="lyxity-card-icon"> 66 <i class="fas fa-sync-alt"></i> 67 </div> 68 <div class="lyxity-card-content"> 69 <div class="lyxity-card-number" id="update-count">0</div> 70 <div class="lyxity-card-label"><?php esc_html_e('Content Updates', 'lyxity'); ?></div> 71 <p class="lyxity-card-description"><?php esc_html_e('Total updates in selected period', 'lyxity'); ?></p> 72 </div> 73 </div> 74 75 <!-- Content Enhancements Card --> 76 <div class="lyxity-card enhance-card"> 77 <div class="lyxity-card-icon"> 78 <i class="fas fa-pen-fancy"></i> 79 </div> 80 <div class="lyxity-card-content"> 81 <div class="lyxity-card-number" id="enhance-count">0</div> 82 <div class="lyxity-card-label"><?php esc_html_e('Content Enhancements', 'lyxity'); ?></div> 83 <p class="lyxity-card-description"><?php esc_html_e('Total enhancements in selected period', 'lyxity'); ?></p> 84 </div> 85 </div> 86 87 <!-- Stale Posts Card --> 88 <div class="lyxity-card stale-card"> 89 <div class="lyxity-card-icon"> 90 <i class="fas fa-clock"></i> 91 </div> 92 <div class="lyxity-card-content"> 93 <div class="lyxity-card-number" id="stale-count">1</div> 94 <div class="lyxity-card-label"><?php esc_html_e('Stale Posts', 'lyxity'); ?></div> 95 <p class="lyxity-card-description"><?php esc_html_e('Posts not updated in 30 days', 'lyxity'); ?></p> 96 </div> 97 </div> 98 </div> 57 99 </div> 58 100 59 <div class="stat-card enhance-card"> 60 <i class="fas fa-pen-fancy"></i> 61 <h3><?php esc_html_e('Content Enhancements', 'lyxity'); ?></h3> 62 <div class="number" id="enhance-count">0</div> 63 <p class="text-muted"><?php esc_html_e('Total enhancements in selected period', 'lyxity'); ?></p> 64 </div> 101 <!-- Right Column: Content Creation Section --> 102 <div class="lyxity-dashboard-column"> 103 <div class="lyxity-section-header"> 104 <h2><?php echo esc_html__('Content Creation', 'lyxity'); ?></h2> 105 </div> 106 107 <div class="lyxity-card-group"> 108 <!-- Queued Card --> 109 <div class="lyxity-card queued-card"> 110 <div class="lyxity-card-icon"> 111 <span class="dashicons dashicons-clock"></span> 112 </div> 113 <div class="lyxity-card-content"> 114 <div class="lyxity-card-number lyxity-status-number">—</div> 115 <div class="lyxity-card-label"><?php echo esc_html__('Queued', 'lyxity'); ?></div> 116 </div> 117 </div> 118 119 <!-- Processing Card --> 120 <div class="lyxity-card processing-card"> 121 <div class="lyxity-card-icon"> 122 <span class="dashicons dashicons-update-alt"></span> 123 </div> 124 <div class="lyxity-card-content"> 125 <div class="lyxity-card-number lyxity-status-number">—</div> 126 <div class="lyxity-card-label"><?php echo esc_html__('Processing', 'lyxity'); ?></div> 127 </div> 128 </div> 129 130 <!-- Completed Card --> 131 <div class="lyxity-card completed-card"> 132 <div class="lyxity-card-icon"> 133 <span class="dashicons dashicons-yes-alt"></span> 134 </div> 135 <div class="lyxity-card-content"> 136 <div class="lyxity-card-number lyxity-status-number">—</div> 137 <div class="lyxity-card-label"><?php echo esc_html__('Completed', 'lyxity'); ?></div> 138 </div> 139 </div> 140 141 <!-- Failed Card --> 142 <div class="lyxity-card failed-card"> 143 <div class="lyxity-card-icon"> 144 <span class="dashicons dashicons-dismiss"></span> 145 </div> 146 <div class="lyxity-card-content"> 147 <div class="lyxity-card-number lyxity-status-number">—</div> 148 <div class="lyxity-card-label"><?php echo esc_html__('Failed', 'lyxity'); ?></div> 149 </div> 150 </div> 151 152 <!-- Articles Generated Card --> 153 <div class="lyxity-card generated-card"> 154 <div class="lyxity-card-icon"> 155 <span class="dashicons dashicons-edit-page"></span> 156 </div> 157 <div class="lyxity-card-content"> 158 <div class="lyxity-card-number lyxity-status-number">—</div> 159 <div class="lyxity-card-label"><?php echo esc_html__('Articles Generated', 'lyxity'); ?></div> 160 </div> 161 </div> 162 163 <!-- Pending WP Posts Card --> 164 <div class="lyxity-card pending-card"> 165 <div class="lyxity-card-icon"> 166 <span class="dashicons dashicons-wordpress-alt"></span> 167 </div> 168 <div class="lyxity-card-content"> 169 <div class="lyxity-card-number lyxity-status-number">—</div> 170 <div class="lyxity-card-label"><?php echo esc_html__('Pending WP Posts', 'lyxity'); ?></div> 171 </div> 172 </div> 173 </div> 174 </div> 175 </div> 65 176 66 <div class="stat-card stale-card"> 67 <i class="fas fa-clock"></i> 68 <h3><?php esc_html_e('Stale Posts', 'lyxity'); ?></h3> 69 <div class="number" id="stale-count">0</div> 70 <p class="text-muted"><?php esc_html_e('Posts not updated in 30 days', 'lyxity'); ?></p> 177 <!-- Requests Table --> 178 <div class="lyxity-requests-section"> 179 <div class="lyxity-section-header"> 180 <h2><?php echo esc_html__('Recent Requests', 'lyxity'); ?></h2> 181 <div class="lyxity-pagination-controls"> 182 <button type="button" class="button" id="rt-prev-btn" disabled> 183 <span class="dashicons dashicons-arrow-left-alt2"></span> 184 </button> 185 <span id="rt-page-indicator" class="lyxity-page-info"><?php echo esc_html__('Page 1 of 1', 'lyxity'); ?></span> 186 <button type="button" class="button" id="rt-next-btn" disabled> 187 <span class="dashicons dashicons-arrow-right-alt2"></span> 188 </button> 189 </div> 190 </div> 191 <div class="lyxity-table-container"> 192 <table class="wp-list-table widefat fixed striped" id="rt-requests-table"> 193 <thead> 194 <tr> 195 <th class="column-keyword"><?php echo esc_html__('Keyword', 'lyxity'); ?></th> 196 <th class="column-created"><?php echo esc_html__('Created', 'lyxity'); ?></th> 197 <th class="column-updated"><?php echo esc_html__('Updated', 'lyxity'); ?></th> 198 <th class="column-articles"><?php echo esc_html__('Articles', 'lyxity'); ?></th> 199 <th class="column-status"><?php echo esc_html__('Status', 'lyxity'); ?></th> 200 </tr> 201 </thead> 202 <tbody> 203 <tr class="lyxity-loading-row"> 204 <td colspan="5"> 205 <div class="lyxity-loading-indicator"> 206 <span class="spinner is-active"></span> 207 <?php echo esc_html__('Loading requests...', 'lyxity'); ?> 208 </div> 209 </td> 210 </tr> 211 </tbody> 212 </table> 71 213 </div> 72 214 </div> -
lyxity/trunk/templates/settings-page.php
r3345835 r3346700 10 10 } 11 11 12 // Debug output - Only visible in development 13 if (defined('WP_DEBUG') && WP_DEBUG) { 14 echo '<!-- DEBUG: Settings page template loaded at ' . date('Y-m-d H:i:s') . ' -->'; 15 } 16 12 17 // Get the referrer URL for the back button 13 18 $referer = wp_get_referer() ? wp_get_referer() : admin_url('admin.php?page=Lyxity'); 14 19 20 // Load API key from WordPress settings 21 $api_key = get_option('lyxity_api_key', ''); 22 23 // Initialize default values 24 $wp_api_url = trailingslashit(get_site_url()) . 'wp-json/'; 25 $wp_username = ''; 26 $article_status = 'draft'; 27 $article_length = 1500; 28 $default_language = 'en-GB'; 29 30 // If API key is set, try to load settings from API 31 if (!empty($api_key)) { 32 // Debug output - Only visible in development 33 if (defined('WP_DEBUG') && WP_DEBUG) { 34 error_log('Lyxity: About to call get_settings() with API key present.'); 35 } 36 37 $api = new Lyxity_API(); 38 $settings = $api->get_settings(); 39 40 // Debug output - Only visible in development 41 if (defined('WP_DEBUG') && WP_DEBUG) { 42 error_log('Lyxity: get_settings() returned: ' . (is_wp_error($settings) ? 'WP_Error' : json_encode($settings))); 43 } 44 45 if (!is_wp_error($settings) && isset($settings['success']) && $settings['success'] === true && isset($settings['data'])) { 46 // Extract settings from API response 47 $data = $settings['data']; 48 49 if (isset($data['wordPressApiUrl'])) { 50 $wp_api_url = $data['wordPressApiUrl']; 51 } 52 53 if (isset($data['wordPressUsername'])) { 54 $wp_username = $data['wordPressUsername']; 55 } 56 57 if (isset($data['defaultArticleStatus'])) { 58 $article_status = $data['defaultArticleStatus']; 59 } 60 61 if (isset($data['articleLength'])) { 62 $article_length = $data['articleLength']; 63 } 64 65 if (isset($data['defaultLanguage'])) { 66 $default_language = $data['defaultLanguage']; 67 } 68 } 69 } 70 15 71 // Enqueue the settings page styles - Note: This is not needed anymore, as we handle enqueuing in the main plugin file 16 72 ?> 17 <div class="wrap lyxity-settings-wrap"> 18 19 <div class="lyxity-container"> 20 <!-- Background images --> 21 <?php 22 // Use wp_get_attachment_image if the image is in the media library 23 // Otherwise, use wp_get_attachment_image_src with a placeholder and style it appropriately 24 $bg_image_url = esc_url(LYXITY_URL . 'assets/images/HH_Background.svg'); 25 echo '<div class="lyxity-bg-top-left" style="background-image: url(' . esc_url($bg_image_url) . ');"></div>'; 26 echo '<div class="lyxity-bg-bottom-right" style="background-image: url(' . esc_url($bg_image_url) . ');"></div>'; 27 ?> 28 <div class="lyxity-logo mb-5"> 29 <a href="https://lyxity.com" target="_blank" rel="noopener noreferrer"> 30 <?php 31 // Fix the SVG logo path to use the correct plugin directory 32 $logo_path = LYXITY_PATH . 'assets/images/logo.png'; 33 34 if (file_exists($logo_path)) { 35 // Get the attachment ID if the image is in the media library 36 $attachment_id = attachment_url_to_postid(LYXITY_URL . 'assets/images/logo.png'); 73 <div class="wrap lyxity-settings-wrap container-fluid"> 74 <div class="container-fluid p-0" style="max-width: none;"> 75 <div class="row mb-4"> 76 77 <div class="card col-12 shadow-sm w-100"> 78 <div class="card-header bg-white d-flex justify-content-between align-items-center py-3"> 79 <div class="d-flex align-items-center"> 80 <a href="https://lyxity.com" target="_blank" rel="noopener noreferrer" class="me-3"> 81 <?php 82 $logo_path = LYXITY_PATH . 'assets/images/logo.png'; 83 84 if (file_exists($logo_path)) { 85 $attachment_id = attachment_url_to_postid(LYXITY_URL . 'assets/images/logo.png'); 86 87 if ($attachment_id) { 88 echo wp_get_attachment_image($attachment_id, 'full', false, array( 89 'class' => 'img-fluid', 90 'style' => 'max-height: 40px;', 91 'alt' => 'Lyxity' 92 )); 93 } else { 94 echo '<img src="' . esc_url(LYXITY_URL . 'assets/images/logo.png') . '" class="img-fluid" style="max-height: 40px;" alt="Lyxity">'; 95 } 96 } else { 97 echo '<span class="h4 fw-bold text-primary mb-0">Lyxity</span>'; 98 } 99 ?> 100 </a> 101 <h1 class="h4 mb-0"><?php esc_html_e('Settings', 'lyxity'); ?></h1> 102 </div> 103 104 </div> 37 105 38 if ($attachment_id) { 39 // If the image is in the media library, use wp_get_attachment_image 40 echo wp_get_attachment_image($attachment_id, 'full', false, array( 41 'class' => 'dashboard-logo', 42 'style' => 'max-width: 180px; height: auto;', 43 'alt' => 'Lyxity' 44 )); 45 } else { 46 // If not in media library, use a div with background image or other WordPress-approved method 47 echo '<div class="dashboard-logo" style="background-image: url(' . esc_url(LYXITY_URL . 'assets/images/logo.png') . '); 48 background-repeat: no-repeat; background-size: contain; width: 180px; height: 60px;" 49 role="img" aria-label="Lyxity"></div>'; 50 } 51 } else { 52 // Fallback if SVG file is not found 53 echo '<span style="font-size: 24px; font-weight: bold; color: black;">Lyxity</span>'; 54 } 55 ?> 56 </a> 106 <div class="card-body p-4"> 107 <div class="row mb-4"> 108 <div class="col-12"> 109 <h4 class="mb-3"><?php esc_html_e('Set Plugin API Key', 'lyxity'); ?></h4> 110 <p class="mb-1"><?php esc_html_e('Welcome to Lyxity.', 'lyxity'); ?></p> 111 <p> 112 <?php echo wp_kses( 113 __('Please enter your API key below. You can obtain API key from <a href="https://lyxity.com" target="_blank" rel="noopener noreferrer">Here</a>.', 'lyxity'), 114 array( 115 'a' => array( 116 'href' => array(), 117 'target' => array(), 118 'rel' => array() 119 ) 120 ) 121 ); ?> 122 </p> 123 </div> 124 </div> 125 126 <?php settings_errors(); ?> 127 128 <form id="lyxity-settings-form" method="post"> 129 <?php wp_nonce_field('lyxity_settings_nonce', 'lyxity_settings_nonce'); ?> 130 131 <div class="card mb-4 p-0 border-0 shadow-sm"> 132 <div class="card-header bg-light"> 133 <h5 class="mb-0 ps-3"><?php esc_html_e('API Configuration', 'lyxity'); ?></h5> 134 </div> 135 <div class="card-body p-4"> 136 <div class="row"> 137 <div class="col-12"> 138 <div class="mb-3"> 139 <label for="lyxity_api_key" class="form-label"><?php esc_html_e('API Key', 'lyxity'); ?></label> 140 <div class="input-group mb-3"> 141 <input type="password" id="lyxity_api_key" aria-describedby="toggle-password-visibility" name="lyxity_api_key" class="form-control password-control" 142 placeholder="<?php esc_attr_e('Enter your API key', 'lyxity'); ?>" 143 value="<?php echo esc_attr($api_key); ?>"> 144 <button class="btn btn-outline-secondary" type="button" id="toggle-password-visibility" title="<?php esc_attr_e('Show/Hide API key', 'lyxity'); ?>"> 145 <span class="dashicons dashicons-visibility"></span> 146 </button> 147 148 </div> 149 <button class="button button-primary" type="button" id="update-api-key" title="<?php esc_attr_e('Update API Key', 'lyxity'); ?>"> 150 <?php esc_html_e('Update', 'lyxity'); ?> 151 </button> 152 <div class="form-text"> 153 <?php echo wp_kses( 154 __('You can obtain an API key from <a href="https://lyxity.com" target="_blank" rel="noopener noreferrer">Lyxity.com</a>', 'lyxity'), 155 array( 156 'a' => array( 157 'href' => array(), 158 'target' => array(), 159 'rel' => array() 160 ) 161 ) 162 ); ?> 163 </div> 164 </div> 165 </div> 166 </div> 167 </div> 168 </div> 169 170 <div class="card mb-4 p-0 border-0 shadow-sm"> 171 <div class="card-header bg-light"> 172 <h5 class="mb-0 ps-3"><?php esc_html_e('WordPress Integration', 'lyxity'); ?></h5> 173 </div> 174 <div class="card-body p-4"> 175 <div class="row g-3"> 176 <div class="col-md-4"> 177 <div class="mb-3"> 178 <label for="lyxity_wp_api_url" class="form-label"><?php esc_html_e('WordPress Base API URL', 'lyxity'); ?></label> 179 <input type="text" id="lyxity_wp_api_url" name="lyxity_wp_api_url" class="form-control" 180 placeholder="<?php esc_attr_e('WordPress API URL', 'lyxity'); ?>" 181 value="<?php echo esc_attr($wp_api_url); ?>"> 182 <div class="form-text"><?php esc_html_e('Default is auto-detected', 'lyxity'); ?></div> 183 </div> 184 </div> 185 186 <div class="col-md-4"> 187 <div class="mb-3"> 188 <label for="lyxity_wp_username" class="form-label"><?php esc_html_e('WordPress Username', 'lyxity'); ?></label> 189 <input type="text" id="lyxity_wp_username" name="lyxity_wp_username" class="form-control" 190 placeholder="<?php esc_attr_e('WordPress Username', 'lyxity'); ?>" 191 value="<?php echo esc_attr($wp_username); ?>"> 192 </div> 193 </div> 194 195 <div class="col-md-4"> 196 <div class="mb-3"> 197 <label for="lyxity_wp_password" class="form-label"><?php esc_html_e('WordPress Password', 'lyxity'); ?></label> 198 <div class="input-group mb-3"> 199 <input type="password" id="lyxity_wp_password" name="lyxity_wp_password" class="form-control password-control" aria-describedby="toggle-wp-password-visibility" 200 placeholder="<?php esc_attr_e('WordPress Password', 'lyxity'); ?>" 201 value=""> 202 <button class="btn btn-outline-secondary" type="button" id="toggle-wp-password-visibility" title="<?php esc_attr_e('Show/Hide Password', 'lyxity'); ?>"> 203 <span class="dashicons dashicons-visibility"></span> 204 </button> 205 </div> 206 </div> 207 </div> 208 </div> 209 </div> 210 </div> 211 212 <div class="card mb-4 p-0 border-0 shadow-sm"> 213 <div class="card-header bg-light"> 214 <h5 class="mb-0 ps-3"><?php esc_html_e('Content Settings', 'lyxity'); ?></h5> 215 </div> 216 <div class="card-body p-4"> 217 <div class="row g-3"> 218 <div class="col-md-4"> 219 <div class="mb-3"> 220 <label for="lyxity_article_status" class="form-label"><?php esc_html_e('Default Article Status', 'lyxity'); ?></label> 221 <select id="lyxity_article_status" name="lyxity_article_status" class="form-control"> 222 <option value="draft" <?php selected($article_status, 'draft'); ?>> 223 <?php esc_html_e('Draft', 'lyxity'); ?> 224 </option> 225 <option value="publish" <?php selected($article_status, 'publish'); ?>> 226 <?php esc_html_e('Published', 'lyxity'); ?> 227 </option> 228 </select> 229 </div> 230 </div> 231 232 <div class="col-md-4"> 233 <div class="mb-3"> 234 <label for="lyxity_article_length" class="form-label"><?php esc_html_e('Article Length (characters)', 'lyxity'); ?></label> 235 <input type="number" id="lyxity_article_length" name="lyxity_article_length" class="form-control" 236 min="100" step="100" 237 value="<?php echo esc_attr($article_length); ?>"> 238 <div class="form-text"><?php esc_html_e('Maximum characters for generated articles', 'lyxity'); ?></div> 239 </div> 240 </div> 241 242 <div class="col-md-4"> 243 <div class="mb-3"> 244 <label for="lyxity_default_language" class="form-label"><?php esc_html_e('Default Language', 'lyxity'); ?></label> 245 <select id="lyxity_default_language" name="lyxity_default_language" class="form-control"> 246 <option value="en-GB" <?php selected($default_language, 'en-GB'); ?>> 247 <?php esc_html_e('English (UK)', 'lyxity'); ?> 248 </option> 249 <option value="en-US" <?php selected($default_language, 'en-US'); ?>> 250 <?php esc_html_e('English (US)', 'lyxity'); ?> 251 </option> 252 <option value="fr-FR" <?php selected($default_language, 'fr-FR'); ?>> 253 <?php esc_html_e('French', 'lyxity'); ?> 254 </option> 255 <option value="de-DE" <?php selected($default_language, 'de-DE'); ?>> 256 <?php esc_html_e('German', 'lyxity'); ?> 257 </option> 258 <option value="es-ES" <?php selected($default_language, 'es-ES'); ?>> 259 <?php esc_html_e('Spanish', 'lyxity'); ?> 260 </option> 261 </select> 262 </div> 263 </div> 264 </div> 265 </div> 266 </div> 267 268 <div id="settings-response" class="alert" style="display: none;"></div> 269 270 <div class="d-flex justify-content-start mt-4"> 271 <button type="submit" id="lyxity-settings-submit" class="button button-primary"> 272 <!-- <span class="dashicons dashicons-saved me-2"></span> --> 273 <?php esc_html_e('Save Settings', 'lyxity'); ?> 274 </button> 275 </div> 276 </form> 277 </div> 278 </div> 279 57 280 </div> 58 59 <!-- <?php60 // This is commented out, but updating to use WordPress functions for future reference61 $image_url = LYXITY_URL . 'assets/images/API_Key.png';62 $attachment_id = attachment_url_to_postid($image_url);63 64 if ($attachment_id) {65 echo wp_get_attachment_image($attachment_id, 'full', false, array(66 'class' => 'lyxity-img',67 'alt' => 'API Key Setup'68 ));69 } else {70 echo '<div class="lyxity-img" style="background-image: url(' . esc_url($image_url) . ');71 background-repeat: no-repeat; background-size: contain;" role="img" aria-label="API Key Setup"></div>';72 }73 ?> -->74 <h4 class="mb-5"><?php esc_html_e('Set Plugin API Key', 'lyxity'); ?></h4>75 76 <p class="mb-3">77 <?php esc_html_e('Welcome to Lyxity.', 'lyxity'); ?>78 79 </p>80 <p class="mb-3">81 <?php echo wp_kses(82 __('Please enter your API key below. You can obtain API key from <a href="https://lyxity.com" target="_blank" rel="noopener noreferrer">Here</a>.', 'lyxity'),83 array(84 'a' => array(85 'href' => array(),86 'target' => array(),87 'rel' => array()88 )89 )90 ); ?>91 </p>92 <?php settings_errors(); ?>93 94 <form method="post" action="options.php">95 <?php settings_fields('lyxity-settings-group'); ?>96 97 <div class="lyxity-form-group mb-3">98 <div class="lyxity-input-group">99 <input type="password" id="lyxity_api_key" name="lyxity_api_key" class="lyxity-form-control"100 placeholder="<?php esc_attr_e('Enter your API key', 'lyxity'); ?>"101 value="<?php echo esc_attr(get_option('lyxity_api_key')); ?>">102 <button type="button" class="lyxity-btn" id="toggle-password-visibility" title="<?php esc_attr_e('Show/Hide API key', 'lyxity'); ?>">103 <span class="dashicons dashicons-visibility"></span>104 </button>105 </div>106 </div>107 108 <button type="submit" class="lyxity-btn-submit"><?php esc_html_e('SUBMIT', 'lyxity'); ?></button>109 </form>110 281 </div> 111 282 </div>
Note: See TracChangeset
for help on using the changeset viewer.