Changeset 3283338
- Timestamp:
- 04/28/2025 10:55:46 AM (10 months ago)
- Location:
- progress-planner
- Files:
-
- 24 added
- 14 deleted
- 114 edited
- 1 copied
-
tags/1.3.0 (copied) (copied from progress-planner/trunk)
-
tags/1.3.0/CHANGELOG.md (modified) (1 diff)
-
tags/1.3.0/assets/css/admin.css (modified) (2 diffs)
-
tags/1.3.0/assets/css/page-widgets/content-activity.css (added)
-
tags/1.3.0/assets/css/page-widgets/published-content.css (deleted)
-
tags/1.3.0/assets/css/page-widgets/suggested-tasks.css (modified) (2 diffs)
-
tags/1.3.0/assets/css/page-widgets/whats-new.css (modified) (2 diffs)
-
tags/1.3.0/assets/css/settings-page.css (modified) (1 diff)
-
tags/1.3.0/assets/images/icon_copywriting.svg (added)
-
tags/1.3.0/assets/images/icon_user.svg (added)
-
tags/1.3.0/assets/js/external-link-accessibility-helper.js (added)
-
tags/1.3.0/assets/js/settings-page.js (modified) (1 diff)
-
tags/1.3.0/assets/js/settings.js (modified) (1 diff)
-
tags/1.3.0/assets/js/web-components/prpl-big-counter.js (modified) (3 diffs)
-
tags/1.3.0/assets/js/web-components/prpl-suggested-task.js (modified) (2 diffs)
-
tags/1.3.0/assets/js/yoast-focus-element.js (modified) (1 diff)
-
tags/1.3.0/classes/actions/class-content-scan.php (modified) (1 diff)
-
tags/1.3.0/classes/actions/class-content.php (modified) (2 diffs)
-
tags/1.3.0/classes/activities/class-content-helpers.php (modified) (1 diff)
-
tags/1.3.0/classes/activities/class-suggested-task.php (modified) (1 diff)
-
tags/1.3.0/classes/admin/class-dashboard-widget-score.php (modified) (1 diff)
-
tags/1.3.0/classes/admin/class-page-settings.php (modified) (4 diffs)
-
tags/1.3.0/classes/admin/class-page.php (modified) (6 diffs)
-
tags/1.3.0/classes/admin/widgets/class-content-activity.php (added)
-
tags/1.3.0/classes/admin/widgets/class-published-content.php (deleted)
-
tags/1.3.0/classes/admin/widgets/class-suggested-tasks.php (modified) (1 diff)
-
tags/1.3.0/classes/badges/content/class-awesome-author.php (deleted)
-
tags/1.3.0/classes/badges/content/class-bold-blogger.php (deleted)
-
tags/1.3.0/classes/badges/content/class-content-curator.php (added)
-
tags/1.3.0/classes/badges/content/class-purposeful-publisher.php (added)
-
tags/1.3.0/classes/badges/content/class-revision-ranger.php (added)
-
tags/1.3.0/classes/badges/content/class-wonderful-writer.php (deleted)
-
tags/1.3.0/classes/class-badges.php (modified) (1 diff)
-
tags/1.3.0/classes/class-base.php (modified) (1 diff)
-
tags/1.3.0/classes/class-plugin-migrations.php (modified) (2 diffs)
-
tags/1.3.0/classes/class-plugin-upgrade-tasks.php (modified) (1 diff)
-
tags/1.3.0/classes/class-settings.php (modified) (1 diff)
-
tags/1.3.0/classes/class-suggested-tasks.php (modified) (4 diffs)
-
tags/1.3.0/classes/rest/class-stats.php (modified) (2 diffs)
-
tags/1.3.0/classes/suggested-tasks/class-local-tasks-manager.php (modified) (5 diffs)
-
tags/1.3.0/classes/suggested-tasks/data-collector/class-data-collector-manager.php (modified) (2 diffs)
-
tags/1.3.0/classes/suggested-tasks/data-collector/class-inactive-plugins.php (modified) (1 diff)
-
tags/1.3.0/classes/suggested-tasks/data-collector/class-last-published-post.php (modified) (5 diffs)
-
tags/1.3.0/classes/suggested-tasks/local-tasks/providers/class-local-tasks-interface.php (modified) (1 diff)
-
tags/1.3.0/classes/suggested-tasks/local-tasks/providers/class-local-tasks.php (modified) (3 diffs)
-
tags/1.3.0/classes/suggested-tasks/local-tasks/providers/class-user.php (modified) (1 diff)
-
tags/1.3.0/classes/suggested-tasks/local-tasks/providers/integrations/yoast/class-add-yoast-providers.php (modified) (1 diff)
-
tags/1.3.0/classes/suggested-tasks/local-tasks/providers/integrations/yoast/class-archive-author.php (modified) (3 diffs)
-
tags/1.3.0/classes/suggested-tasks/local-tasks/providers/integrations/yoast/class-archive-date.php (modified) (3 diffs)
-
tags/1.3.0/classes/suggested-tasks/local-tasks/providers/integrations/yoast/class-archive-format.php (modified) (3 diffs)
-
tags/1.3.0/classes/suggested-tasks/local-tasks/providers/integrations/yoast/class-crawl-settings-emoji-scripts.php (modified) (1 diff)
-
tags/1.3.0/classes/suggested-tasks/local-tasks/providers/integrations/yoast/class-crawl-settings-feed-authors.php (modified) (3 diffs)
-
tags/1.3.0/classes/suggested-tasks/local-tasks/providers/integrations/yoast/class-crawl-settings-feed-global-comments.php (modified) (1 diff)
-
tags/1.3.0/classes/suggested-tasks/local-tasks/providers/integrations/yoast/class-media-pages.php (modified) (1 diff)
-
tags/1.3.0/classes/suggested-tasks/local-tasks/providers/integrations/yoast/class-organization-logo.php (modified) (1 diff)
-
tags/1.3.0/classes/suggested-tasks/local-tasks/providers/one-time/class-set-valuable-post-types.php (added)
-
tags/1.3.0/classes/suggested-tasks/local-tasks/providers/one-time/class-settings-saved.php (modified) (2 diffs)
-
tags/1.3.0/classes/suggested-tasks/local-tasks/providers/repetitive/class-core-update.php (modified) (1 diff)
-
tags/1.3.0/classes/suggested-tasks/local-tasks/providers/repetitive/class-create.php (modified) (8 diffs)
-
tags/1.3.0/classes/suggested-tasks/local-tasks/providers/repetitive/class-review.php (modified) (8 diffs)
-
tags/1.3.0/classes/update/class-update-130.php (added)
-
tags/1.3.0/playwright.config.js (modified) (2 diffs)
-
tags/1.3.0/progress-planner.php (modified) (2 diffs)
-
tags/1.3.0/readme.txt (modified) (2 diffs)
-
tags/1.3.0/uninstall.php (modified) (1 diff)
-
tags/1.3.0/views/admin-page-header.php (modified) (1 diff)
-
tags/1.3.0/views/admin-page-settings.php (modified) (1 diff)
-
tags/1.3.0/views/page-settings/license.php (modified) (1 diff)
-
tags/1.3.0/views/page-settings/pages.php (modified) (1 diff)
-
tags/1.3.0/views/page-settings/post-types.php (added)
-
tags/1.3.0/views/page-settings/settings.php (modified) (1 diff)
-
tags/1.3.0/views/page-widgets/content-activity.php (added)
-
tags/1.3.0/views/page-widgets/published-content.php (deleted)
-
tags/1.3.0/views/page-widgets/suggested-tasks.php (modified) (1 diff)
-
tags/1.3.0/views/page-widgets/whats-new.php (modified) (1 diff)
-
tags/1.3.0/views/popovers/settings.php (deleted)
-
tags/1.3.0/views/setting/page-select.php (modified) (1 diff)
-
trunk/CHANGELOG.md (modified) (1 diff)
-
trunk/assets/css/admin.css (modified) (2 diffs)
-
trunk/assets/css/page-widgets/content-activity.css (added)
-
trunk/assets/css/page-widgets/published-content.css (deleted)
-
trunk/assets/css/page-widgets/suggested-tasks.css (modified) (2 diffs)
-
trunk/assets/css/page-widgets/whats-new.css (modified) (2 diffs)
-
trunk/assets/css/settings-page.css (modified) (1 diff)
-
trunk/assets/images/icon_copywriting.svg (added)
-
trunk/assets/images/icon_user.svg (added)
-
trunk/assets/js/external-link-accessibility-helper.js (added)
-
trunk/assets/js/settings-page.js (modified) (1 diff)
-
trunk/assets/js/settings.js (modified) (1 diff)
-
trunk/assets/js/web-components/prpl-big-counter.js (modified) (3 diffs)
-
trunk/assets/js/web-components/prpl-suggested-task.js (modified) (2 diffs)
-
trunk/assets/js/yoast-focus-element.js (modified) (1 diff)
-
trunk/classes/actions/class-content-scan.php (modified) (1 diff)
-
trunk/classes/actions/class-content.php (modified) (2 diffs)
-
trunk/classes/activities/class-content-helpers.php (modified) (1 diff)
-
trunk/classes/activities/class-suggested-task.php (modified) (1 diff)
-
trunk/classes/admin/class-dashboard-widget-score.php (modified) (1 diff)
-
trunk/classes/admin/class-page-settings.php (modified) (4 diffs)
-
trunk/classes/admin/class-page.php (modified) (6 diffs)
-
trunk/classes/admin/widgets/class-content-activity.php (added)
-
trunk/classes/admin/widgets/class-published-content.php (deleted)
-
trunk/classes/admin/widgets/class-suggested-tasks.php (modified) (1 diff)
-
trunk/classes/badges/content/class-awesome-author.php (deleted)
-
trunk/classes/badges/content/class-bold-blogger.php (deleted)
-
trunk/classes/badges/content/class-content-curator.php (added)
-
trunk/classes/badges/content/class-purposeful-publisher.php (added)
-
trunk/classes/badges/content/class-revision-ranger.php (added)
-
trunk/classes/badges/content/class-wonderful-writer.php (deleted)
-
trunk/classes/class-badges.php (modified) (1 diff)
-
trunk/classes/class-base.php (modified) (1 diff)
-
trunk/classes/class-plugin-migrations.php (modified) (2 diffs)
-
trunk/classes/class-plugin-upgrade-tasks.php (modified) (1 diff)
-
trunk/classes/class-settings.php (modified) (1 diff)
-
trunk/classes/class-suggested-tasks.php (modified) (4 diffs)
-
trunk/classes/rest/class-stats.php (modified) (2 diffs)
-
trunk/classes/suggested-tasks/class-local-tasks-manager.php (modified) (5 diffs)
-
trunk/classes/suggested-tasks/data-collector/class-data-collector-manager.php (modified) (2 diffs)
-
trunk/classes/suggested-tasks/data-collector/class-inactive-plugins.php (modified) (1 diff)
-
trunk/classes/suggested-tasks/data-collector/class-last-published-post.php (modified) (5 diffs)
-
trunk/classes/suggested-tasks/local-tasks/providers/class-local-tasks-interface.php (modified) (1 diff)
-
trunk/classes/suggested-tasks/local-tasks/providers/class-local-tasks.php (modified) (3 diffs)
-
trunk/classes/suggested-tasks/local-tasks/providers/class-user.php (modified) (1 diff)
-
trunk/classes/suggested-tasks/local-tasks/providers/integrations/yoast/class-add-yoast-providers.php (modified) (1 diff)
-
trunk/classes/suggested-tasks/local-tasks/providers/integrations/yoast/class-archive-author.php (modified) (3 diffs)
-
trunk/classes/suggested-tasks/local-tasks/providers/integrations/yoast/class-archive-date.php (modified) (3 diffs)
-
trunk/classes/suggested-tasks/local-tasks/providers/integrations/yoast/class-archive-format.php (modified) (3 diffs)
-
trunk/classes/suggested-tasks/local-tasks/providers/integrations/yoast/class-crawl-settings-emoji-scripts.php (modified) (1 diff)
-
trunk/classes/suggested-tasks/local-tasks/providers/integrations/yoast/class-crawl-settings-feed-authors.php (modified) (3 diffs)
-
trunk/classes/suggested-tasks/local-tasks/providers/integrations/yoast/class-crawl-settings-feed-global-comments.php (modified) (1 diff)
-
trunk/classes/suggested-tasks/local-tasks/providers/integrations/yoast/class-media-pages.php (modified) (1 diff)
-
trunk/classes/suggested-tasks/local-tasks/providers/integrations/yoast/class-organization-logo.php (modified) (1 diff)
-
trunk/classes/suggested-tasks/local-tasks/providers/one-time/class-set-valuable-post-types.php (added)
-
trunk/classes/suggested-tasks/local-tasks/providers/one-time/class-settings-saved.php (modified) (2 diffs)
-
trunk/classes/suggested-tasks/local-tasks/providers/repetitive/class-core-update.php (modified) (1 diff)
-
trunk/classes/suggested-tasks/local-tasks/providers/repetitive/class-create.php (modified) (8 diffs)
-
trunk/classes/suggested-tasks/local-tasks/providers/repetitive/class-review.php (modified) (8 diffs)
-
trunk/classes/update/class-update-130.php (added)
-
trunk/playwright.config.js (modified) (2 diffs)
-
trunk/progress-planner.php (modified) (2 diffs)
-
trunk/readme.txt (modified) (2 diffs)
-
trunk/uninstall.php (modified) (1 diff)
-
trunk/views/admin-page-header.php (modified) (1 diff)
-
trunk/views/admin-page-settings.php (modified) (1 diff)
-
trunk/views/page-settings/license.php (modified) (1 diff)
-
trunk/views/page-settings/pages.php (modified) (1 diff)
-
trunk/views/page-settings/post-types.php (added)
-
trunk/views/page-settings/settings.php (modified) (1 diff)
-
trunk/views/page-widgets/content-activity.php (added)
-
trunk/views/page-widgets/published-content.php (deleted)
-
trunk/views/page-widgets/suggested-tasks.php (modified) (1 diff)
-
trunk/views/page-widgets/whats-new.php (modified) (1 diff)
-
trunk/views/popovers/settings.php (deleted)
-
trunk/views/setting/page-select.php (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
progress-planner/tags/1.3.0/CHANGELOG.md
r3268602 r3283338 1 = 1.3.0 = 2 3 Enhancements: 4 5 * Improved checks when adding Ravi icon to the Yoast SEO settings page. 6 * Add "golden" tasks to weekly emails. 7 * Add text to clarify when the user has completed all tasks. 8 * Improve the content widget & stats to show more accurate data. It now shows content _activity_ instead of content _published_. 9 * Implemented "valuable post-types" and added settings for them. 10 * Changed the "create a post" task to "create valuable content". 11 * Renamed & migrated content badges. 12 * Added a link to the 'Create valuable content' task description. 13 * Improve accessibility of Recommendations (and other links) linking to external resources 14 15 Bugs we fixed: 16 17 * Fixed error during plugin uninstall. 18 * Archive_Format data collector hooks weren't registered early enough. 19 * Ensure fresh plugin list by clearing plugin cache before checking for inactive plugins after deletion. 20 * Clear plugin cache when checking for inactive plugins. 21 * Delete no-longer relevant pending tasks. 22 * Fixed timing issue for tasks added by 3rd-party plugins. 23 1 24 = 1.2.0 = 2 25 -
progress-planner/tags/1.3.0/assets/css/admin.css
r3264985 r3283338 421 421 Settings popover. 422 422 \*------------------------------------*/ 423 #prpl-settings-license-form,424 #prpl-settings-form {425 426 label {427 display: block;428 }429 430 p {431 max-width: 42em;432 }433 434 h3 {435 font-size: 1.15em;436 }437 438 button.button-primary {439 margin-top: 1em;440 }441 }442 443 .driver-popover.prpl-driverjs-theme {444 background-color: var(--prpl-background-orange);445 color: var(--prpl-color-text);446 447 .driver-popover-title {448 color: var(--prpl-color-headings);449 }450 451 button {452 color: var(--prpl-color-headings);453 }454 455 button:not(.driver-popover-close-btn):hover {456 background-color: var(--prpl-background-orange);457 }458 }459 460 423 #prpl-settings-license-form { 461 424 … … 466 429 gap: var(--prpl-padding); 467 430 } 468 } 469 431 432 p { 433 max-width: 42em; 434 } 435 436 h3 { 437 font-size: 1.15em; 438 } 439 440 button.button-primary { 441 margin-top: 1em; 442 } 443 } 444 445 .driver-popover.prpl-driverjs-theme { 446 background-color: var(--prpl-background-orange); 447 color: var(--prpl-color-text); 448 449 .driver-popover-title { 450 color: var(--prpl-color-headings); 451 } 452 453 button { 454 color: var(--prpl-color-headings); 455 } 456 457 button:not(.driver-popover-close-btn):hover { 458 background-color: var(--prpl-background-orange); 459 } 460 } 461 462 /*------------------------------------*\ 463 External link accessibility helper. 464 \*------------------------------------*/ 465 .prpl-external-link-icon { 466 display: inline-flex; 467 margin-inline-start: 0.25em; 468 vertical-align: middle; 469 470 svg { 471 width: 1em; 472 height: 1em; 473 } 474 } -
progress-planner/tags/1.3.0/assets/css/page-widgets/suggested-tasks.css
r3264985 r3283338 103 103 } 104 104 105 .prpl-no-suggested-tasks { 106 display: none; 107 } 108 105 109 hr { 106 110 display: block; … … 111 115 hr { 112 116 display: none; 117 } 118 119 .prpl-no-suggested-tasks { 120 display: block; 121 background-color: var(--prpl-background-green); 122 padding: calc(var(--prpl-padding) / 2); 113 123 } 114 124 } -
progress-planner/tags/1.3.0/assets/css/page-widgets/whats-new.css
r3217671 r3283338 11 11 li { 12 12 13 > a { 14 color: var(--prpl-color-gray-6); 15 text-decoration: none; 13 h3 { 14 margin-top: 0; 15 font-size: 1.15em; 16 font-weight: 600; 16 17 17 h3 { 18 margin-top: 0; 19 font-size: 1.15em; 20 font-weight: 600; 18 > a { 19 color: var(--prpl-color-headings); 20 text-decoration: none; 21 21 22 &::after { 23 content: url('data:image/svg+xml,<%3Fxml version="1.0" encoding="UTF-8"%3F><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12"><path fill="%23currentcolor" d="M6 1h5v5L8.86 3.85 4.7 8 4 7.3l4.15-4.16zM2 3h2v1H2v6h6V8h1v2a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1"/></svg>'); 24 margin-left: 0.25em; 25 width: 0.75em; 26 height: 0.75em; 27 display: inline-block; 22 .prpl-external-link-icon { 23 margin-inline-start: 0.15em; 28 24 } 29 25 } 30 26 } 27 31 28 32 29 img { … … 38 35 display: flex; 39 36 justify-content: flex-end; 40 41 a::after {42 content: url('data:image/svg+xml,<%3Fxml version="1.0" encoding="UTF-8"%3F><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12"><path fill="%23currentcolor" d="M6 1h5v5L8.86 3.85 4.7 8 4 7.3l4.15-4.16zM2 3h2v1H2v6h6V8h1v2a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1"/></svg>');43 margin-left: 0.25em;44 width: 1em;45 height: 1em;46 display: inline-block;47 }48 37 } 49 38 } -
progress-planner/tags/1.3.0/assets/css/settings-page.css
r3264985 r3283338 220 220 } 221 221 222 .prpl-license-keys-wrapper { 223 display: flex; 224 flex-direction: column; 225 gap: 1rem; 226 227 .prpl-license-key-wrapper { 228 display: flex; 229 align-items: center; 230 gap: 0.5rem; 231 232 .prpl-license-status { 233 width: 1rem; 234 height: 1rem; 235 236 svg { 222 /* Post types */ 223 .prpl-column-post-types { 224 225 .prpl-settings-section-title { 226 227 svg { 228 color: #038d88; 229 230 path { 231 fill: currentcolor; 232 } 233 } 234 235 background-color: #f3faf9; 236 } 237 238 } 239 240 /* Login destination */ 241 .prpl-column-login-destination { 242 243 .prpl-settings-section-title { 244 245 svg { 246 color: var(--prpl-color-accent-red); 247 } 248 249 background-color: var(--prpl-background-red); 250 } 251 252 } 253 254 255 /* License */ 256 .prpl-column-license { 257 258 .prpl-settings-section-title { 259 260 svg { 261 color: #0773b4; 262 } 263 background-color: #effbfe; 264 } 265 266 .prpl-license-keys-wrapper { 267 display: flex; 268 flex-direction: column; 269 gap: 1rem; 270 max-width: 40rem; 271 272 & > p:first-child { 273 margin-top: 0; 274 } 275 276 .prpl-license-key-wrapper { 277 display: flex; 278 align-items: center; 279 gap: 0.5rem; 280 281 .prpl-license-status { 237 282 width: 1rem; 238 283 height: 1rem; 239 } 240 } 241 } 242 243 input { 244 width: 20rem; 245 max-width: calc(100% - 2rem); 246 } 247 } 284 285 svg { 286 width: 1rem; 287 height: 1rem; 288 } 289 } 290 } 291 292 input { 293 width: 30rem; 294 max-width: calc(100% - 2rem); 295 border: 1px solid var(--prpl-color-gray-2); 296 box-shadow: 1px 2px 4px 0 rgba(0, 0, 0, 0.05); 297 } 298 } 299 300 } 301 302 303 /* Grid layout for wrapper for: 304 - Valuable post types 305 - Default login destination 306 - License keys 307 */ 308 #prpl-grid-column-wrapper { 309 display: grid; 310 margin-bottom: var(--prpl-gap); 311 312 /* There are 5 or less valuable post types */ 313 grid-template-columns: 1fr 1fr; 314 grid-template-rows: auto auto; 315 gap: var(--prpl-settings-page-gap); 316 317 .prpl-column { 318 align-self: stretch; 319 display: flex; 320 flex-direction: column; 321 322 .prpl-widget-wrapper { 323 flex: 1; 324 margin-bottom: 0; 325 } 326 } 327 328 /* Valuable post types */ 329 .prpl-column:nth-child(1) { 330 grid-column: 1; 331 grid-row: 1; 332 } 333 334 /* Default login destination */ 335 .prpl-column:nth-child(2) { 336 grid-column: 2; 337 grid-row: 1; 338 } 339 340 /* License keys */ 341 .prpl-column:nth-child(3) { 342 grid-column: 1 / span 2; 343 grid-row: 2; 344 } 345 346 /* We have more than 5 valuable post types */ 347 &:has([data-has-many-valuable-post-types]) { 348 grid-template-rows: auto auto; 349 350 /* Valuable post types */ 351 .prpl-column:nth-child(1) { 352 grid-column: 1; 353 grid-row: 1 / span 2; 354 355 /* Span 2 rows on the left */ 356 } 357 358 /* Default login destination */ 359 .prpl-column:nth-child(2) { 360 grid-column: 2; 361 grid-row: 1; 362 } 363 364 /* License keys */ 365 .prpl-column:nth-child(3) { 366 grid-column: 2; 367 grid-row: 2; 368 } 369 } 370 } 371 372 /* Valuable post types */ 373 #prpl-post-types-include-wrapper { 374 padding-top: 0.75rem; 375 376 label { 377 display: block; 378 margin-top: 0.75rem; 379 380 &:first-child { 381 margin-top: 0; 382 } 383 } 384 } -
progress-planner/tags/1.3.0/assets/js/settings-page.js
r3264985 r3283338 67 67 }; 68 68 formData.forEach( function ( value, key ) { 69 data[ key ] = value; 69 // Handle array notation in keys 70 if ( key.endsWith( '[]' ) ) { 71 const baseKey = key.slice( 0, -2 ); 72 if ( ! data[ baseKey ] ) { 73 data[ baseKey ] = []; 74 } 75 data[ baseKey ].push( value ); 76 } else { 77 data[ key ] = value; 78 } 70 79 } ); 71 80 const request = wp.ajax.post( 'prpl_settings_form', data ); -
progress-planner/tags/1.3.0/assets/js/settings.js
r3264985 r3283338 7 7 * Dependencies: progress-planner/ajax-request, progress-planner/onboard, wp-util, progress-planner/l10n 8 8 */ 9 document10 .getElementById( 'prpl-settings-form' )11 .addEventListener( 'submit', function ( event ) {12 event.preventDefault();13 const form = new FormData( this );14 const data = form.getAll( 'prpl-settings-post-types-include[]' );15 16 // Save the options.17 const request = wp.ajax.post( 'progress_planner_save_cpt_settings', {18 _ajax_nonce: progressPlanner.nonce,19 include_post_types: data.join( ',' ),20 } );21 request.done( () => {22 window.location.reload();23 } );24 25 document.getElementById( 'submit-include-post-types' ).disabled = true;26 document.getElementById( 'submit-include-post-types' ).innerHTML =27 prplL10n( 'saving' );28 } );29 9 30 10 // Submit the email. -
progress-planner/tags/1.3.0/assets/js/web-components/prpl-big-counter.js
r3198155 r3283338 17 17 backgroundColor || 'var(--prpl-background-purple)'; 18 18 19 const el = this; 20 19 21 this.innerHTML = ` 20 22 <div style=" … … 31 33 margin-bottom: var(--prpl-padding); 32 34 "> 35 <div class="container-width" style="width: 100%;"></div> 33 36 <span style=" 34 37 font-size: var(--prpl-font-size-5xl); … … 36 39 font-weight: 600; 37 40 ">${ number }</span> 38 <span style="font-size: var(--prpl-font-size-2xl);">${ content }</span> 41 <span style="font-size: var(--prpl-font-size-2xl);"> 42 <span class="resize" style="font-size: 100%; display: inline-block; width: max-content;">${ content }</span> 43 </span> 39 44 </div> 40 45 `; 46 47 const resizeFont = () => { 48 const element = el.querySelector( '.resize' ); 49 if ( ! element ) { 50 return; 51 } 52 53 element.style.fontSize = '100%'; 54 55 let size = 100; 56 while ( 57 element.clientWidth > 58 el.querySelector( '.container-width' ).clientWidth 59 ) { 60 if ( size < 80 ) { 61 element.style.fontSize = size + '%'; 62 element.style.width = '100%'; 63 break; 64 } 65 size -= 1; 66 element.style.fontSize = size + '%'; 67 } 68 }; 69 70 resizeFont(); 71 window.addEventListener( 'resize', resizeFont ); 41 72 } 42 73 } -
progress-planner/tags/1.3.0/assets/js/web-components/prpl-suggested-task.js
r3268602 r3283338 22 22 action = '', 23 23 url = '', 24 url_target = '_self', 24 25 dismissable = false, 25 26 provider_id = '', … … 38 39 let taskHeading = title; 39 40 if ( url ) { 40 taskHeading = `<a href="${ url }" >${ title }</a>`;41 taskHeading = `<a href="${ url }" target="${ url_target }">${ title }</a>`; 41 42 } 42 43 -
progress-planner/tags/1.3.0/assets/js/yoast-focus-element.js
r3268602 r3283338 6 6 7 7 /** 8 * Check if the value of the element matches the value specified in the task. 9 * 10 * @param {Element} element The element to check. 11 * @param {Object} task The task to check. 12 * @return {boolean} True if the value matches, false otherwise. 8 * Yoast Focus Element class. 13 9 */ 14 function checkTaskValue( element, task ) { 15 if ( ! task.valueElement ) { 16 return true; 17 } 18 19 const attributeName = task.valueElement.attributeName || 'value'; 20 const attributeValue = task.valueElement.attributeValue; 21 const operator = task.valueElement.operator || '='; 22 const currentValue = element.getAttribute( attributeName ) || ''; 23 24 return '!=' === operator 25 ? currentValue !== attributeValue 26 : currentValue === attributeValue; 27 } 28 29 /** 30 * Observe the Yoast sidebar clicks. 31 */ 32 function observeYoastSidebarClicks() { 33 const container = document.querySelector( '#yoast-seo-settings' ); 34 35 if ( ! container ) { 36 return; 37 } 38 39 const waitForNav = new MutationObserver( ( mutationsList, observer ) => { 40 const nav = container.querySelector( 41 'nav.yst-sidebar-navigation__sidebar' 10 class ProgressPlannerYoastFocus { 11 /** 12 * Constructor. 13 */ 14 constructor() { 15 this.container = document.querySelector( '#yoast-seo-settings' ); 16 this.tasks = progressPlannerYoastFocusElement.tasks; 17 this.baseUrl = progressPlannerYoastFocusElement.base_url; 18 19 if ( this.container ) { 20 this.init(); 21 } 22 } 23 24 /** 25 * Initialize the Yoast Focus Element. 26 */ 27 init() { 28 this.waitForMainAndObserveContent(); 29 this.observeYoastSidebarClicks(); 30 } 31 32 /** 33 * Check if the value of the element matches the value specified in the task. 34 * 35 * @param {Element} element The element to check. 36 * @param {Object} task The task to check. 37 * @return {boolean} True if the value matches, false otherwise. 38 */ 39 checkTaskValue( element, task ) { 40 if ( ! task.valueElement ) { 41 return true; 42 } 43 44 const attributeName = task.valueElement.attributeName || 'value'; 45 const attributeValue = task.valueElement.attributeValue; 46 const operator = task.valueElement.operator || '='; 47 const currentValue = element.getAttribute( attributeName ) || ''; 48 49 return '!=' === operator 50 ? currentValue !== attributeValue 51 : currentValue === attributeValue; 52 } 53 54 /** 55 * Observe the Yoast sidebar clicks. 56 */ 57 observeYoastSidebarClicks() { 58 const waitForNav = new MutationObserver( 59 ( mutationsList, observer ) => { 60 const nav = this.container.querySelector( 61 'nav.yst-sidebar-navigation__sidebar' 62 ); 63 if ( nav ) { 64 observer.disconnect(); 65 66 nav.addEventListener( 'click', ( e ) => { 67 const link = e.target.closest( 'a' ); 68 if ( link ) { 69 this.waitForMainAndObserveContent(); 70 } 71 } ); 72 } 73 } 42 74 ); 43 if ( nav ) { 44 // Sidebar nav loaded. 45 observer.disconnect(); 46 47 nav.addEventListener( 'click', ( e ) => { 48 const link = e.target.closest( 'a' ); 49 if ( link ) { 50 // Sidebar link clicked. 51 waitForMainAndObserveContent(); // re-run logic after clicking 52 } 53 } ); 54 } 55 } ); 56 57 waitForNav.observe( container, { 58 childList: true, 59 subtree: true, 60 } ); 61 } 62 63 /** 64 * Wait for the main content to load and observe the content. 65 */ 66 function waitForMainAndObserveContent() { 67 const container = document.querySelector( '#yoast-seo-settings' ); 68 if ( ! container ) { 69 return; 70 } 71 72 const waitForMain = new MutationObserver( ( mutationsList, observer ) => { 73 const main = container.querySelector( 'main.yst-paper' ); 74 if ( main ) { 75 // Main loaded. 76 observer.disconnect(); 77 78 const childObserver = new MutationObserver( ( mutations ) => { 79 for ( const mutation of mutations ) { 80 if ( 81 mutation.type === 'attributes' && 82 mutation.attributeName === 'class' 83 ) { 84 const el = mutation.target; 85 if ( 86 el.parentElement === main && 87 el.classList.contains( 'yst-opacity-100' ) 88 ) { 89 // Fully loaded content. 90 childObserver.disconnect(); 91 92 // Loop through the tasks and add the focus element. 93 for ( const task of progressPlannerYoastFocusElement.tasks ) { 94 // Try to find the toggleButton. 95 const valueElement = el.querySelector( 96 task.valueElement.elementSelector 97 ); 98 let raviIconPositionAbsolute = true; 99 100 if ( valueElement ) { 101 // We usually add icon to the option header. 102 let addIconElement = valueElement.closest( 103 task.iconElement 104 ); 105 106 // Exception is the upload input field. 75 76 waitForNav.observe( this.container, { 77 childList: true, 78 subtree: true, 79 } ); 80 } 81 82 /** 83 * Wait for the main content to load and observe the content. 84 */ 85 waitForMainAndObserveContent() { 86 const waitForMain = new MutationObserver( 87 ( mutationsList, observer ) => { 88 const main = this.container.querySelector( 'main.yst-paper' ); 89 if ( main ) { 90 observer.disconnect(); 91 92 const childObserver = new MutationObserver( 93 ( mutations ) => { 94 for ( const mutation of mutations ) { 95 if ( 96 mutation.type === 'attributes' && 97 mutation.attributeName === 'class' 98 ) { 99 const el = mutation.target; 107 100 if ( 108 ! addIconElement && 109 valueElement.type === 'hidden' 110 ) { 111 addIconElement = valueElement 112 .closest( 'fieldset' ) 113 .querySelector( task.iconElement ); 114 115 raviIconPositionAbsolute = false; 116 } 117 118 // Upload input field. 119 if ( ! addIconElement ) { 120 continue; 121 } 122 123 // Append next to the valueElemen, only if it's not already there. 124 if ( 125 ! addIconElement.querySelector( 126 '.prpl-form-row-ravi' 101 el.parentElement === main && 102 el.classList.contains( 103 'yst-opacity-100' 127 104 ) 128 105 ) { 129 // Check for value if specified in task. 130 const valueMatches = checkTaskValue( 131 valueElement, 132 task 133 ); 134 135 // Create a new span with the class prpl-form-row-ravi. 136 const raviIconWrapper = 137 document.createElement( 'span' ); 138 raviIconWrapper.classList.add( 139 'prpl-form-row-ravi', 140 'prpl-element-awards-points-icon-wrapper' 141 ); 142 143 if ( valueMatches ) { 144 raviIconWrapper.classList.add( 145 'complete' 146 ); 147 } 148 149 // Styling for absolute positioning. 150 if ( raviIconPositionAbsolute ) { 151 addIconElement.style.position = 152 'relative'; 153 154 raviIconWrapper.style.position = 155 'absolute'; 156 raviIconWrapper.style.right = 157 '3.5rem'; 158 raviIconWrapper.style.top = '-7px'; 159 } 160 161 raviIconWrapper.appendChild( 162 document.createElement( 'span' ) 163 ); 164 165 // Create an icon image. 166 const iconImg = 167 document.createElement( 'img' ); 168 iconImg.src = 169 progressPlannerYoastFocusElement.base_url + 170 '/assets/images/icon_progress_planner.svg'; 171 iconImg.alt = 'Ravi'; 172 iconImg.width = 16; 173 iconImg.height = 16; 174 175 // Append the icon image to the raviIconWrapper. 176 raviIconWrapper 177 .querySelector( 'span' ) 178 .appendChild( iconImg ); 179 180 // Add the points to the raviIconWrapper. 181 const pointsWrapper = 182 document.createElement( 'span' ); 183 pointsWrapper.classList.add( 184 'prpl-form-row-points' 185 ); 186 pointsWrapper.textContent = valueMatches 187 ? '✓' 188 : '+1'; 189 raviIconWrapper.appendChild( 190 pointsWrapper 191 ); 192 193 // Finally add the raviIconWrapper to the DOM. 194 addIconElement.appendChild( 195 raviIconWrapper 196 ); 197 198 // Watch for changes in aria-checked to update the icon dynamically 199 const valueElementObserver = 200 new MutationObserver( () => { 201 // Check value again if specified 202 const currentValueMatches = 203 checkTaskValue( 204 valueElement, 205 task 206 ); 207 208 if ( currentValueMatches ) { 209 raviIconWrapper.classList.add( 210 'complete' 211 ); 212 213 pointsWrapper.textContent = 214 '✓'; 215 } else { 216 raviIconWrapper.classList.remove( 217 'complete' 218 ); 219 220 pointsWrapper.textContent = 221 '+1'; 222 } 223 } ); 224 225 valueElementObserver.observe( 226 valueElement, 227 { 228 attributes: true, 229 attributeFilter: [ 230 task.valueElement 231 .attributeName, 232 ], 233 } 234 ); 106 this.processTasks( el ); 235 107 } 236 108 } 237 109 } 238 110 } 239 } 111 ); 112 113 main.querySelectorAll( ':scope > *' ).forEach( 114 ( child ) => { 115 childObserver.observe( child, { 116 attributes: true, 117 attributeFilter: [ 'class' ], 118 } ); 119 } 120 ); 240 121 } 241 } ); 242 243 // Watch direct children of main.yst-paper 244 main.querySelectorAll( ':scope > *' ).forEach( ( child ) => { 245 childObserver.observe( child, { 246 attributes: true, 247 attributeFilter: [ 'class' ], 248 } ); 249 } ); 250 } 251 } ); 252 253 waitForMain.observe( container, { 254 childList: true, 255 subtree: true, 256 } ); 122 } 123 ); 124 125 waitForMain.observe( this.container, { 126 childList: true, 127 subtree: true, 128 } ); 129 } 130 131 /** 132 * Process all tasks for a given element. 133 * 134 * @param {Element} el The element to process tasks for. 135 */ 136 processTasks( el ) { 137 for ( const task of this.tasks ) { 138 const valueElement = el.querySelector( 139 task.valueElement.elementSelector 140 ); 141 const raviIconPositionAbsolute = true; 142 143 if ( valueElement ) { 144 this.processTask( 145 valueElement, 146 task, 147 raviIconPositionAbsolute 148 ); 149 } 150 } 151 } 152 153 /** 154 * Process a single task. 155 * 156 * @param {Element} valueElement The value element to process. 157 * @param {Object} task The task to process. 158 * @param {boolean} raviIconPositionAbsolute Whether the icon should be absolutely positioned. 159 */ 160 processTask( valueElement, task, raviIconPositionAbsolute ) { 161 let addIconElement = valueElement.closest( task.iconElement ); 162 163 // Exception is the upload input field. 164 if ( ! addIconElement && valueElement.type === 'hidden' ) { 165 addIconElement = valueElement 166 .closest( 'fieldset' ) 167 .querySelector( task.iconElement ); 168 raviIconPositionAbsolute = false; 169 } 170 171 if ( ! addIconElement ) { 172 return; 173 } 174 175 if ( 176 ! addIconElement.querySelector( '[data-prpl-element="ravi-icon"]' ) 177 ) { 178 this.addIcon( 179 valueElement, 180 addIconElement, 181 task, 182 raviIconPositionAbsolute 183 ); 184 } 185 } 186 187 /** 188 * Add icon to the element. 189 * 190 * @param {Element} valueElement The value element. 191 * @param {Element} addIconElement The element to add the icon to. 192 * @param {Object} task The task. 193 * @param {boolean} raviIconPositionAbsolute Whether the icon should be absolutely positioned. 194 */ 195 addIcon( valueElement, addIconElement, task, raviIconPositionAbsolute ) { 196 const valueMatches = this.checkTaskValue( valueElement, task ); 197 198 // Create a new span with the class prpl-form-row-ravi. 199 const raviIconWrapper = document.createElement( 'span' ); 200 raviIconWrapper.classList.add( 201 'prpl-element-awards-points-icon-wrapper' 202 ); 203 raviIconWrapper.setAttribute( 'data-prpl-element', 'ravi-icon' ); 204 205 if ( valueMatches ) { 206 raviIconWrapper.classList.add( 'complete' ); 207 } 208 209 // Styling for absolute positioning. 210 if ( raviIconPositionAbsolute ) { 211 addIconElement.style.position = 'relative'; 212 213 raviIconWrapper.style.position = 'absolute'; 214 raviIconWrapper.style.right = '3.5rem'; 215 raviIconWrapper.style.top = '-7px'; 216 } 217 218 raviIconWrapper.appendChild( document.createElement( 'span' ) ); 219 220 // Create an icon image. 221 const iconImg = document.createElement( 'img' ); 222 iconImg.src = this.baseUrl + '/assets/images/icon_progress_planner.svg'; 223 iconImg.alt = 'Ravi'; 224 iconImg.width = 16; 225 iconImg.height = 16; 226 227 // Append the icon image to the raviIconWrapper. 228 raviIconWrapper.querySelector( 'span' ).appendChild( iconImg ); 229 230 // Add the points to the raviIconWrapper. 231 const pointsWrapper = document.createElement( 'span' ); 232 pointsWrapper.classList.add( 'prpl-form-row-points' ); 233 pointsWrapper.textContent = valueMatches ? '✓' : '+1'; 234 raviIconWrapper.appendChild( pointsWrapper ); 235 236 // Watch for changes in aria-checked to update the icon dynamically 237 const valueElementObserver = new MutationObserver( () => { 238 const currentValueMatches = this.checkTaskValue( 239 valueElement, 240 task 241 ); 242 243 if ( currentValueMatches ) { 244 raviIconWrapper.classList.add( 'complete' ); 245 pointsWrapper.textContent = '✓'; 246 } else { 247 raviIconWrapper.classList.remove( 'complete' ); 248 pointsWrapper.textContent = '+1'; 249 } 250 } ); 251 252 valueElementObserver.observe( valueElement, { 253 attributes: true, 254 attributeFilter: [ task.valueElement.attributeName ], 255 } ); 256 257 // Finally add the raviIconWrapper to the DOM. 258 addIconElement.appendChild( raviIconWrapper ); 259 } 257 260 } 258 261 259 // Run once on initial page load. 260 waitForMainAndObserveContent(); 261 observeYoastSidebarClicks(); 262 // Initialize the Yoast Focus Element. 263 new ProgressPlannerYoastFocus(); -
progress-planner/tags/1.3.0/classes/actions/class-content-scan.php
r3268602 r3283338 172 172 // Loop through the posts and update the stats. 173 173 foreach ( $posts as $post ) { 174 // Set the activity .175 $activities[ $post->ID ] = \progress_planner()->get_activities__content_helpers()->get_activity_from_post( $post );174 // Set the activity, we're dealing only with published posts (but just in case). 175 $activities[ $post->ID ] = \progress_planner()->get_activities__content_helpers()->get_activity_from_post( $post, 'publish' === $post->post_status ? 'publish' : 'update' ); 176 176 // Set the word count. 177 177 \progress_planner()->get_activities__content_helpers()->get_word_count( $post->post_content, $post->ID ); -
progress-planner/tags/1.3.0/classes/actions/class-content.php
r3268602 r3283338 281 281 } 282 282 283 $activity = \progress_planner()->get_activities__content_helpers()->get_activity_from_post( $post ); 284 $activity->type = $type; 283 $activity = \progress_planner()->get_activities__content_helpers()->get_activity_from_post( $post, $type ); 285 284 286 285 // Update the badges. … … 306 305 } 307 306 307 // We need to set the date explicitly since post_date & post_modified dates are not changed when the post is trashed. 308 if ( 'trash' === $type ) { 309 $activity->date = new \DateTime(); 310 } 311 308 312 $activity->save(); 309 313 -
progress-planner/tags/1.3.0/classes/activities/class-content-helpers.php
r3268602 r3283338 100 100 * 101 101 * @param \WP_Post $post The post object. 102 * @param string $activity_type The activity type. 102 103 * 103 104 * @return \Progress_Planner\Activities\Content 104 105 */ 105 public function get_activity_from_post( $post ) { 106 $type = 'publish' === $post->post_status ? 'publish' : 'update'; 107 $date = 'publish' === $post->post_status ? $post->post_date : $post->post_modified; 108 106 public function get_activity_from_post( $post, $activity_type = 'publish' ) { 109 107 $activity = new Activities_Content(); 110 108 $activity->category = 'content'; 111 $activity->type = $ type;112 $activity->date = \progress_planner()->get_utils__date()->get_datetime_from_mysql_date( $ date);109 $activity->type = $activity_type; 110 $activity->date = \progress_planner()->get_utils__date()->get_datetime_from_mysql_date( $post->post_modified ); 113 111 $activity->data_id = (string) $post->ID; 114 112 $activity->user_id = (int) $post->post_author; -
progress-planner/tags/1.3.0/classes/activities/class-suggested-task.php
r3268602 r3283338 66 66 67 67 // Default points for a suggested task. 68 $points = 1;69 $ create_post_provider = new Create();68 $points = 1; 69 $task_provider = \progress_planner()->get_suggested_tasks()->get_local()->get_task_provider( $this->data_id ); 70 70 71 $data = \progress_planner()->get_suggested_tasks()->get_local()->get_data_from_task_id( $this->data_id );72 if ( isset( $data['provider_id'] ) && $create_post_provider->get_provider_id() === $data['provider_id'] ) {73 $points = $ create_post_provider->get_points_for_task( $this->data_id);71 if ( $task_provider ) { 72 // Create post task provider had a different points system, this is for backwards compatibility. 73 $points = $task_provider instanceof Create ? $task_provider->get_points( $this->data_id ) : $task_provider->get_points(); 74 74 } 75 75 -
progress-planner/tags/1.3.0/classes/admin/class-dashboard-widget-score.php
r3264985 r3283338 48 48 49 49 \progress_planner()->get_admin__enqueue()->enqueue_style( "progress-planner/dashboard-widgets/{$this->id}" ); 50 51 \progress_planner()->get_admin__enqueue()->enqueue_script( 'external-link-accessibility-helper' ); 50 52 51 53 \progress_planner()->the_view( "dashboard-widgets/{$this->id}.php" ); -
progress-planner/tags/1.3.0/classes/admin/class-page-settings.php
r3264985 r3283338 172 172 173 173 $this->save_settings(); 174 $this->save_post_types(); 174 175 $this->save_license(); 175 176 176 do_action( 'progress_planner_settings_form_options_stored' );177 \do_action( 'progress_planner_settings_form_options_stored' ); 177 178 178 179 \wp_send_json_success( \esc_html__( 'Options stored successfully', 'progress-planner' ) ); … … 185 186 */ 186 187 public function save_settings() { 187 $redirect_on_login = isset( $_POST['prpl-redirect-on-login'] ) // phpcs:ignore WordPress.Security.NonceVerification.Missing 188 ? \sanitize_text_field( \wp_unslash( $_POST['prpl-redirect-on-login'] ) ) // phpcs:ignore WordPress.Security.NonceVerification.Missing 188 189 // Check the nonce. 190 \check_admin_referer( 'progress_planner' ); 191 192 $redirect_on_login = isset( $_POST['prpl-redirect-on-login'] ) 193 ? \sanitize_text_field( \wp_unslash( $_POST['prpl-redirect-on-login'] ) ) 189 194 : false; 190 195 … … 193 198 194 199 /** 200 * Save the post types. 201 * 202 * @return void 203 */ 204 public function save_post_types() { 205 206 // Check the nonce. 207 \check_admin_referer( 'progress_planner' ); 208 209 $include_post_types = isset( $_POST['prpl-post-types-include'] ) 210 ? array_map( 'sanitize_text_field', \wp_unslash( $_POST['prpl-post-types-include'] ) ) // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 211 // If no post types are selected, use the default post types (post and page can be deregistered). 212 : array_intersect( [ 'post', 'page' ], \progress_planner()->get_settings()->get_public_post_types() ); 213 214 \progress_planner()->get_settings()->set( 'include_post_types', $include_post_types ); 215 } 216 217 /** 195 218 * Save the license key. 196 219 * … … 198 221 */ 199 222 public function save_license() { 200 $license = isset( $_POST['prpl-pro-license-key'] ) // phpcs:ignore WordPress.Security.NonceVerification.Missing 201 ? \sanitize_text_field( \wp_unslash( $_POST['prpl-pro-license-key'] ) ) // phpcs:ignore WordPress.Security.NonceVerification.Missing 223 224 // Check the nonce. 225 \check_admin_referer( 'progress_planner' ); 226 227 $license = isset( $_POST['prpl-pro-license-key'] ) 228 ? \sanitize_text_field( \wp_unslash( $_POST['prpl-pro-license-key'] ) ) 202 229 : ''; 203 230 -
progress-planner/tags/1.3.0/classes/admin/class-page.php
r3268602 r3283338 28 28 \add_action( 'admin_menu', [ $this, 'add_page' ] ); 29 29 \add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] ); 30 \add_action( 'wp_ajax_progress_planner_save_cpt_settings', [ $this, 'save_cpt_settings' ] );31 30 \add_action( 'in_admin_header', [ $this, 'remove_admin_notices' ], PHP_INT_MAX ); 32 31 … … 52 51 \progress_planner()->get_admin__widgets__latest_badge(), 53 52 \progress_planner()->get_admin__widgets__badge_streak(), 54 \progress_planner()->get_admin__widgets__ published_content(),53 \progress_planner()->get_admin__widgets__content_activity(), 55 54 \progress_planner()->get_admin__widgets__whats_new(), 56 55 ]; … … 188 187 \progress_planner()->get_admin__enqueue()->enqueue_script( 'onboard', $default_localization_data ); 189 188 } 189 190 \progress_planner()->get_admin__enqueue()->enqueue_script( 'external-link-accessibility-helper' ); 190 191 } 191 192 … … 200 201 ] 201 202 ); 203 204 \progress_planner()->get_admin__enqueue()->enqueue_script( 'external-link-accessibility-helper' ); 202 205 } 203 206 } … … 292 295 293 296 /** 294 * Save the post types settings.295 *296 * @return void297 */298 public function save_cpt_settings() {299 \check_ajax_referer( 'progress_planner', 'nonce', false );300 $include_post_types = isset( $_POST['include_post_types'] )301 ? \sanitize_text_field( \wp_unslash( $_POST['include_post_types'] ) )302 : 'post,page';303 $include_post_types = \explode( ',', $include_post_types );304 \progress_planner()->get_settings()->set( 'include_post_types', $include_post_types );305 306 \wp_send_json_success(307 [308 'message' => \esc_html__( 'Settings saved.', 'progress-planner' ),309 ]310 );311 }312 313 /**314 297 * Remove all admin notices when the user is on the Progress Planner page. 315 298 * … … 363 346 .update-plugins { 364 347 position: absolute; 365 left: 22px;366 top: 0px;348 left: 18px; 349 bottom: 0px; 367 350 min-width: 15px; 368 351 height: 15px; -
progress-planner/tags/1.3.0/classes/admin/widgets/class-suggested-tasks.php
r3268602 r3283338 81 81 $task_details['action'] = 'celebrate'; 82 82 $task_details['status'] = 'pending_celebration'; 83 84 // Award 2 points if last created post was long.85 $create_provider = new Create();86 if ( $create_provider->get_provider_id() === $task_provider->get_provider_id() ) {87 $task_details['points'] = $create_provider->get_points_for_task( $task_id );88 }89 83 90 84 $tasks[] = $task_details; -
progress-planner/tags/1.3.0/classes/class-badges.php
r3268602 r3283338 55 55 public function __construct() { 56 56 $this->content = [ 57 \progress_planner()->get_badges__content__ wonderful_writer(),58 \progress_planner()->get_badges__content__ bold_blogger(),59 \progress_planner()->get_badges__content__ awesome_author(),57 \progress_planner()->get_badges__content__content_curator(), 58 \progress_planner()->get_badges__content__revision_ranger(), 59 \progress_planner()->get_badges__content__purposeful_publisher(), 60 60 ]; 61 61 -
progress-planner/tags/1.3.0/classes/class-base.php
r3268602 r3283338 194 194 'get_chart' => [ 'get_ui__chart', '1.1.1' ], 195 195 'get_popover' => [ 'get_ui__popover', '1.1.1' ], 196 197 'get_admin__widgets__published_content' => [ 'get_admin__widgets__content_activity', '1.3.0' ], 196 198 ]; 197 199 -
progress-planner/tags/1.3.0/classes/class-plugin-migrations.php
r3268602 r3283338 11 11 12 12 use Progress_Planner\Update\Update_111; 13 use Progress_Planner\Update\Update_130; 13 14 14 15 /** … … 42 43 private const UPGRADE_CLASSES = [ 43 44 '1.1.1' => Update_111::class, 45 '1.3.0' => Update_130::class, 44 46 ]; 45 47 -
progress-planner/tags/1.3.0/classes/class-plugin-upgrade-tasks.php
r3268602 r3283338 25 25 26 26 // Check if the plugin was upgraded or new plugin was activated. 27 \add_action( 'init', [ $this, 'handle_activation_or_upgrade' ], 10 );27 \add_action( 'init', [ $this, 'handle_activation_or_upgrade' ], 100 ); // We need to run this after the Local_Tasks_Manager::init() is called. 28 28 } 29 29 -
progress-planner/tags/1.3.0/classes/class-settings.php
r3198155 r3283338 111 111 return $this->save_settings(); 112 112 } 113 114 /** 115 * Get an array of post-types names for the stats. 116 * 117 * @return string[] 118 */ 119 public function get_post_types_names() { 120 static $include_post_types; 121 122 if ( ! doing_action( 'init' ) && ! did_action( 'init' ) ) { 123 \trigger_error( // phpcs:ignore 124 sprintf( 125 '%1$s was called too early. Wait for init hook to be called to have access to the post types.', 126 \esc_html( get_class() . '::' . __FUNCTION__ ) 127 ), 128 E_USER_WARNING 129 ); 130 } 131 132 // Since we're working with CPTs, dont cache until init. 133 if ( isset( $include_post_types ) && ! empty( $include_post_types ) ) { 134 return $include_post_types; 135 } 136 137 $public_post_types = $this->get_public_post_types(); 138 139 // Post or pages can be deregistered. 140 $default = array_intersect( [ 'post', 'page' ], $public_post_types ); 141 142 // Filter the saved post types. 143 $include_post_types = array_intersect( $this->get( [ 'include_post_types' ], $default ), $public_post_types ); 144 145 return empty( $include_post_types ) ? $default : \array_values( $include_post_types ); 146 } 147 148 /** 149 * Get the public post types. 150 * 151 * @return string[] 152 */ 153 public function get_public_post_types() { 154 $public_post_types = \array_filter( \get_post_types( [ 'public' => true ] ), 'is_post_type_viewable' ); 155 156 unset( $public_post_types['attachment'] ); 157 unset( $public_post_types['elementor_library'] ); // Elementor templates are not a post type we want to track. 158 159 /** 160 * Filter the public post types. 161 * 162 * @param string[] $public_post_types The public post types. 163 * 164 * @return string[] 165 */ 166 return \apply_filters( 'progress_planner_public_post_types', $public_post_types ); 167 } 113 168 } -
progress-planner/tags/1.3.0/classes/class-suggested-tasks.php
r3268602 r3283338 44 44 45 45 if ( \is_admin() ) { 46 \add_action( 'init', [ $this, 'init' ], 1 );46 \add_action( 'init', [ $this, 'init' ], 100 ); // Wait for the post types to be initialized. 47 47 } 48 48 … … 580 580 581 581 /** 582 * Add a remote task to the pending tasks. 583 * 584 * @param string $task_id The task ID. 585 * 586 * @return bool 587 */ 588 public function add_remote_task_to_pending_tasks( $task_id ) { 589 $remote_task_data = $this->get_remote_task_by_task_id( $task_id ); 590 591 if ( ! $remote_task_data ) { 592 return false; 593 } 594 595 return \progress_planner()->get_suggested_tasks()->get_local()->add_pending_task( 596 [ 597 'task_id' => $task_id, 598 'provider_id' => $remote_task_data['category'] ?? '', // Remote tasks use the category as provider_id. 599 'category' => $remote_task_data['category'] ?? '', 600 ] 601 ); 602 } 603 604 605 /** 582 606 * Handle the suggested task action. 583 607 * … … 601 625 // We need to add the task to the pending tasks first, before marking it as completed. 602 626 if ( false !== strpos( $task_id, 'remote-task' ) ) { 603 $remote_task_data = $this->get_remote_task_by_task_id( $task_id ); 604 \progress_planner()->get_suggested_tasks()->get_local()->add_pending_task( 605 [ 606 'task_id' => $task_id, 607 'provider_id' => $remote_task_data['category'] ?? '', // Remote tasks use the category as provider_id. 608 'category' => $remote_task_data['category'] ?? '', 609 ] 610 ); 627 $this->add_remote_task_to_pending_tasks( $task_id ); 611 628 } 612 629 … … 627 644 case 'snooze': 628 645 $duration = isset( $_POST['duration'] ) ? \sanitize_text_field( \wp_unslash( $_POST['duration'] ) ) : ''; 629 $updated = $this->snooze_task( $task_id, $duration ); 646 647 // We need to add the task to the pending tasks first, before marking it as snoozed. 648 if ( false !== strpos( $task_id, 'remote-task' ) ) { 649 $this->add_remote_task_to_pending_tasks( $task_id ); 650 } 651 652 $updated = $this->snooze_task( $task_id, $duration ); 630 653 break; 631 654 -
progress-planner/tags/1.3.0/classes/rest/class-stats.php
r3268602 r3283338 163 163 $data['recommendations'] = []; 164 164 foreach ( $ravis_recommendations as $recommendation ) { 165 $ data['recommendations'][]= [165 $r = [ 166 166 'id' => $recommendation['task_id'], 167 167 'title' => $recommendation['title'], … … 169 169 'provider_id' => $recommendation['provider_id'], 170 170 ]; 171 172 if ( 'user' === $recommendation['provider_id'] ) { 173 $r['points'] = isset( $recommendation['points'] ) ? $recommendation['points'] : 0; 174 } 175 $data['recommendations'][] = $r; 171 176 } 172 177 -
progress-planner/tags/1.3.0/classes/suggested-tasks/class-local-tasks-manager.php
r3268602 r3283338 29 29 use Progress_Planner\Suggested_Tasks\Local_Tasks\Providers\Integrations\Yoast\Add_Yoast_Providers; 30 30 use Progress_Planner\Suggested_Tasks\Local_Tasks\Providers\User as User_Tasks; 31 use Progress_Planner\Suggested_Tasks\Local_Tasks\Providers\One_Time\Set_Valuable_Post_Types; 31 32 32 33 /** … … 65 66 new Search_Engine_Visibility(), 66 67 new User_Tasks(), 68 new Set_Valuable_Post_Types(), 67 69 ]; 68 70 … … 71 73 72 74 // At this point both local and task providers for the plugins we integrate with are instantiated, so initialize them. 73 \add_action( ' plugins_loaded', [ $this, 'init' ], 11 );75 \add_action( 'init', [ $this, 'init' ], 99 ); // Wait for the post types to be initialized. 74 76 75 77 // Add the cleanup action. … … 239 241 $task_id = $task_data['task_id']; 240 242 243 // Check if the task is no longer relevant. 244 $task_object = Local_Task_Factory::create_task_from( 'id', $task_id ); 245 $task_provider = $this->get_task_provider( $task_object->get_provider_id() ); 246 if ( $task_provider && ! $task_provider->is_task_relevant() ) { 247 // Remove the task from the pending tasks. 248 \progress_planner()->get_suggested_tasks()->delete_task( $task_id ); 249 } 250 241 251 $task_result = $this->evaluate_task( $task_id ); 242 252 if ( false !== $task_result ) { … … 353 363 $tasks, 354 364 function ( $task ) { 355 // If the task was already completed, remove it. 356 if ( 'completed' === $task['status'] ) { 357 return false; 358 } 359 360 if ( isset( $task['date'] ) ) { 365 366 if ( 'pending' === $task['status'] && isset( $task['date'] ) ) { 361 367 return (string) \gmdate( 'YW' ) === (string) $task['date']; 362 368 } -
progress-planner/tags/1.3.0/classes/suggested-tasks/data-collector/class-data-collector-manager.php
r3268602 r3283338 14 14 use Progress_Planner\Suggested_Tasks\Data_Collector\Post_Author; 15 15 use Progress_Planner\Suggested_Tasks\Data_Collector\Last_Published_Post; 16 use Progress_Planner\Suggested_Tasks\Data_Collector\Archive_Format; 16 17 17 18 /** … … 40 41 new Post_Author(), 41 42 new Last_Published_Post(), 43 new Archive_Format(), 42 44 ]; 43 45 -
progress-planner/tags/1.3.0/classes/suggested-tasks/data-collector/class-inactive-plugins.php
r3268602 r3283338 51 51 } 52 52 53 // Clear the plugins cache, so get_plugins() returns the latest plugins. 54 wp_cache_delete( 'plugins', 'plugins' ); 55 53 56 $plugins = get_plugins(); 54 57 $plugins_active = 0; -
progress-planner/tags/1.3.0/classes/suggested-tasks/data-collector/class-last-published-post.php
r3268602 r3283338 23 23 24 24 /** 25 * The include post types. 26 * 27 * @var string[] 28 */ 29 protected $include_post_types = []; 30 31 /** 25 32 * Initialize the data collector. 26 33 * … … 28 35 */ 29 36 public function init() { 37 \add_action( 'init', [ $this, 'set_include_post_types' ], 99 ); // Wait for all CPTs to be registered. 30 38 \add_action( 'transition_post_status', [ $this, 'update_last_published_post_cache' ], 10, 3 ); 39 } 40 41 /** 42 * Set the include post types. 43 * 44 * @return void 45 */ 46 public function set_include_post_types() { 47 $this->include_post_types = \progress_planner()->get_settings()->get_post_types_names(); 31 48 } 32 49 … … 41 58 */ 42 59 public function update_last_published_post_cache( $new_status, $old_status, $post ) { 43 if ( $new_status === 'publish' || $old_status === 'publish') {60 if ( true === \in_array( get_post_type( $post ), $this->include_post_types, true ) && ( $new_status === 'publish' || $old_status === 'publish' ) ) { 44 61 $this->update_cache(); 45 62 } … … 67 84 'orderby' => 'date', 68 85 'order' => 'DESC', 86 'post_type' => $this->include_post_types, 69 87 ] 70 88 ); … … 73 91 $data = [ 74 92 'post_id' => $last_created_posts[0]->ID, 75 'long' => \progress_planner()->get_activities__content_helpers()->is_post_long( $last_created_posts[0]->ID ) ? true : false,76 93 'post_date' => $last_created_posts[0]->post_date, 77 94 ]; -
progress-planner/tags/1.3.0/classes/suggested-tasks/local-tasks/providers/class-local-tasks-interface.php
r3264985 r3283338 81 81 */ 82 82 public function capability_required(); 83 84 /** 85 * Check if the task is still relevant. 86 * For example, we have a task to disable author archives if there is only one author. 87 * If in the meantime more authors are added, the task is no longer relevant and the task should be removed. 88 * 89 * @return bool 90 */ 91 public function is_task_relevant(); 83 92 } -
progress-planner/tags/1.3.0/classes/suggested-tasks/local-tasks/providers/class-local-tasks.php
r3268602 r3283338 80 80 81 81 /** 82 * The task URL target. 83 * 84 * @var string 85 */ 86 protected $url_target = '_self'; 87 88 /** 82 89 * The task link setting. 83 90 * … … 159 166 160 167 return ''; 168 } 169 170 /** 171 * Get the task URL. 172 * 173 * @return string 174 */ 175 public function get_url_target() { 176 return $this->url_target ? $this->url_target : '_self'; 161 177 } 162 178 … … 265 281 return false; 266 282 } 283 284 /** 285 * Check if the task is still relevant. 286 * For example, we have a task to disable author archives if there is only one author. 287 * If in the meantime more authors are added, the task is no longer relevant and the task should be removed. 288 * 289 * @return bool 290 */ 291 public function is_task_relevant() { 292 return true; 293 } 267 294 } -
progress-planner/tags/1.3.0/classes/suggested-tasks/local-tasks/providers/class-user.php
r3264985 r3283338 94 94 'points' => 0, 95 95 'url' => '', 96 'url_target' => '_self', 96 97 'description' => '', 97 98 'link_setting' => [], -
progress-planner/tags/1.3.0/classes/suggested-tasks/local-tasks/providers/integrations/yoast/class-add-yoast-providers.php
r3268602 r3283338 47 47 foreach ( $this->providers as $provider ) { 48 48 49 // Add Ravi icon if the task is pending or completed.50 if ( $provider-> should_add_task() ) {49 // Add Ravi icon if the task is pending or is completed. 50 if ( $provider->is_task_relevant() || \progress_planner()->get_suggested_tasks()->was_task_completed( $provider->get_task_id() ) ) { 51 51 $focus_task = $provider->get_focus_tasks(); 52 52 53 53 if ( $focus_task ) { 54 $focus_tasks [] = $focus_task;54 $focus_tasks = array_merge( $focus_tasks, $focus_task ); 55 55 } 56 56 } -
progress-planner/tags/1.3.0/classes/suggested-tasks/local-tasks/providers/integrations/yoast/class-archive-author.php
r3268602 r3283338 73 73 public function get_focus_tasks() { 74 74 return [ 75 'iconElement' => '.yst-toggle-field__header', 76 'valueElement' => [ 77 'elementSelector' => 'button[data-id="input-wpseo_titles-disable-author"]', 78 'attributeName' => 'aria-checked', 79 'attributeValue' => 'false', 80 'operator' => '=', 75 [ 76 'iconElement' => '.yst-toggle-field__header', 77 'valueElement' => [ 78 'elementSelector' => 'button[data-id="input-wpseo_titles-disable-author"]', 79 'attributeName' => 'aria-checked', 80 'attributeValue' => 'false', 81 'operator' => '=', 82 ], 81 83 ], 82 84 ]; … … 89 91 */ 90 92 public function should_add_task() { 93 94 if ( ! $this->is_task_relevant() ) { 95 return false; 96 } 97 91 98 // If the author archive is already disabled, we don't need to add the task. 92 99 if ( YoastSEO()->helpers->options->get( 'disable-author' ) === true ) { … … 94 101 } 95 102 103 return true; 104 } 105 106 /** 107 * Check if the task is still relevant. 108 * For example, we have a task to disable author archives if there is only one author. 109 * If in the meantime more authors are added, the task is no longer relevant and the task should be removed. 110 * 111 * @return bool 112 */ 113 public function is_task_relevant() { 96 114 // If there is more than one author, we don't need to add the task. 97 115 if ( $this->data_collector->collect() > self::MINIMUM_AUTHOR_WITH_POSTS ) { -
progress-planner/tags/1.3.0/classes/suggested-tasks/local-tasks/providers/integrations/yoast/class-archive-date.php
r3268602 r3283338 56 56 public function get_focus_tasks() { 57 57 return [ 58 'iconElement' => '.yst-toggle-field__header', 59 'valueElement' => [ 60 'elementSelector' => 'button[data-id="input-wpseo_titles-disable-date"]', 61 'attributeName' => 'aria-checked', 62 'attributeValue' => 'false', 63 'operator' => '=', 58 [ 59 'iconElement' => '.yst-toggle-field__header', 60 'valueElement' => [ 61 'elementSelector' => 'button[data-id="input-wpseo_titles-disable-date"]', 62 'attributeName' => 'aria-checked', 63 'attributeValue' => 'false', 64 'operator' => '=', 65 ], 64 66 ], 65 67 ]; … … 72 74 */ 73 75 public function should_add_task() { 76 77 if ( ! $this->is_task_relevant() ) { 78 return false; 79 } 80 81 // If the date archive is already disabled, we don't need to add the task. 82 return YoastSEO()->helpers->options->get( 'disable-date' ) !== true; 83 } 84 85 /** 86 * Check if the task is still relevant. 87 * For example, we have a task to disable author archives if there is only one author. 88 * If in the meantime more authors are added, the task is no longer relevant and the task should be removed. 89 * 90 * @return bool 91 */ 92 public function is_task_relevant() { 74 93 // If the permalink structure includes %year%, %monthnum%, or %day%, we don't need to add the task. 75 94 $permalink_structure = \get_option( 'permalink_structure' ); … … 78 97 } 79 98 80 // If the date archive is already disabled, we don't need to add the task. 81 return YoastSEO()->helpers->options->get( 'disable-date' ) !== true; 99 return true; 82 100 } 83 101 } -
progress-planner/tags/1.3.0/classes/suggested-tasks/local-tasks/providers/integrations/yoast/class-archive-format.php
r3268602 r3283338 73 73 public function get_focus_tasks() { 74 74 return [ 75 'iconElement' => '.yst-toggle-field__header', 76 'valueElement' => [ 77 'elementSelector' => 'button[data-id="input-wpseo_titles-disable-post_format"]', 78 'attributeName' => 'aria-checked', 79 'attributeValue' => 'false', 80 'operator' => '=', 75 [ 76 'iconElement' => '.yst-toggle-field__header', 77 'valueElement' => [ 78 'elementSelector' => 'button[data-id="input-wpseo_titles-disable-post_format"]', 79 'attributeName' => 'aria-checked', 80 'attributeValue' => 'false', 81 'operator' => '=', 82 ], 81 83 ], 82 84 ]; … … 89 91 */ 90 92 public function should_add_task() { 91 $archive_format_count = $this->data_collector->collect(); 92 93 // If there are more than X posts with a post format, we don't need to add the task. X is set in the class. 94 if ( $archive_format_count > static::MINIMUM_POSTS_WITH_FORMAT ) { 93 if ( ! $this->is_task_relevant() ) { 95 94 return false; 96 95 } … … 103 102 return true; 104 103 } 104 105 /** 106 * Check if the task is still relevant. 107 * For example, we have a task to disable author archives if there is only one author. 108 * If in the meantime more authors are added, the task is no longer relevant and the task should be removed. 109 * 110 * @return bool 111 */ 112 public function is_task_relevant() { 113 $archive_format_count = $this->data_collector->collect(); 114 115 // If there are more than X posts with a post format, we don't need to add the task. X is set in the class. 116 if ( $archive_format_count > static::MINIMUM_POSTS_WITH_FORMAT ) { 117 return false; 118 } 119 120 return true; 121 } 105 122 } -
progress-planner/tags/1.3.0/classes/suggested-tasks/local-tasks/providers/integrations/yoast/class-crawl-settings-emoji-scripts.php
r3268602 r3283338 56 56 public function get_focus_tasks() { 57 57 return [ 58 'iconElement' => '.yst-toggle-field__header', 59 'valueElement' => [ 60 'elementSelector' => 'button[data-id="input-wpseo-remove_emoji_scripts"]', 61 'attributeName' => 'aria-checked', 62 'attributeValue' => 'true', 63 'operator' => '=', 58 [ 59 'iconElement' => '.yst-toggle-field__header', 60 'valueElement' => [ 61 'elementSelector' => 'button[data-id="input-wpseo-remove_emoji_scripts"]', 62 'attributeName' => 'aria-checked', 63 'attributeValue' => 'true', 64 'operator' => '=', 65 ], 64 66 ], 65 67 ]; -
progress-planner/tags/1.3.0/classes/suggested-tasks/local-tasks/providers/integrations/yoast/class-crawl-settings-feed-authors.php
r3268602 r3283338 73 73 public function get_focus_tasks() { 74 74 return [ 75 'iconElement' => '.yst-toggle-field__header', 76 'valueElement' => [ 77 'elementSelector' => 'button[data-id="input-wpseo-remove_feed_authors"]', 78 'attributeName' => 'aria-checked', 79 'attributeValue' => 'true', 80 'operator' => '=', 75 [ 76 'iconElement' => '.yst-toggle-field__header', 77 'valueElement' => [ 78 'elementSelector' => 'button[data-id="input-wpseo-remove_feed_authors"]', 79 'attributeName' => 'aria-checked', 80 'attributeValue' => 'true', 81 'operator' => '=', 82 ], 81 83 ], 82 84 ]; … … 89 91 */ 90 92 public function should_add_task() { 91 // If there is more than one author, we don't need to add the task. 92 if ( $this->data_collector->collect() > self::MINIMUM_AUTHOR_WITH_POSTS) {93 94 if ( ! $this->is_task_relevant() ) { 93 95 return false; 94 96 } … … 104 106 return true; 105 107 } 108 109 /** 110 * Check if the task is still relevant. 111 * For example, we have a task to disable author archives if there is only one author. 112 * If in the meantime more authors are added, the task is no longer relevant and the task should be removed. 113 * 114 * @return bool 115 */ 116 public function is_task_relevant() { 117 // If there is more than one author, we don't need to add the task. 118 if ( $this->data_collector->collect() > self::MINIMUM_AUTHOR_WITH_POSTS ) { 119 return false; 120 } 121 122 return true; 123 } 106 124 } -
progress-planner/tags/1.3.0/classes/suggested-tasks/local-tasks/providers/integrations/yoast/class-crawl-settings-feed-global-comments.php
r3268602 r3283338 56 56 public function get_focus_tasks() { 57 57 return [ 58 'iconElement' => '.yst-toggle-field__header', 59 'valueElement' => [ 60 'elementSelector' => 'button[data-id="input-wpseo-remove_feed_global_comments"]', 61 'attributeName' => 'aria-checked', 62 'attributeValue' => 'true', 63 'operator' => '=', 58 [ 59 'iconElement' => '.yst-toggle-field__header', 60 'valueElement' => [ 61 'elementSelector' => 'button[data-id="input-wpseo-remove_feed_global_comments"]', 62 'attributeName' => 'aria-checked', 63 'attributeValue' => 'true', 64 'operator' => '=', 65 ], 64 66 ], 65 67 ]; -
progress-planner/tags/1.3.0/classes/suggested-tasks/local-tasks/providers/integrations/yoast/class-media-pages.php
r3268602 r3283338 56 56 public function get_focus_tasks() { 57 57 return [ 58 'iconElement' => '.yst-toggle-field__header', 59 'valueElement' => [ 60 'elementSelector' => 'button[data-id="input-wpseo_titles-disable-attachment"]', 61 'attributeName' => 'aria-checked', 62 'attributeValue' => 'false', 63 'operator' => '=', 58 [ 59 'iconElement' => '.yst-toggle-field__header', 60 'valueElement' => [ 61 'elementSelector' => 'button[data-id="input-wpseo_titles-disable-attachment"]', 62 'attributeName' => 'aria-checked', 63 'attributeValue' => 'false', 64 'operator' => '=', 65 ], 64 66 ], 65 67 ]; -
progress-planner/tags/1.3.0/classes/suggested-tasks/local-tasks/providers/integrations/yoast/class-organization-logo.php
r3268602 r3283338 71 71 public function get_focus_tasks() { 72 72 return [ 73 'iconElement' => 'legend.yst-label', 74 'valueElement' => [ 75 'elementSelector' => $this->yoast_seo->helpers->options->get( 'company_or_person', 'company' ) !== 'person' 76 ? 'input[name="wpseo_titles.company_logo"]' 77 : 'input[name="wpseo_titles.person_logo"]', 78 'attributeName' => 'value', 79 'attributeValue' => '', 80 'operator' => '!=', 73 [ 74 'iconElement' => 'legend.yst-label', 75 'valueElement' => [ 76 'elementSelector' => 'input[name="wpseo_titles.company_logo"]', 77 'attributeName' => 'value', 78 'attributeValue' => '', 79 'operator' => '!=', 80 ], 81 ], 82 [ 83 'iconElement' => 'legend.yst-label', 84 'valueElement' => [ 85 'elementSelector' => 'input[name="wpseo_titles.person_logo"]', 86 'attributeName' => 'value', 87 'attributeValue' => '', 88 'operator' => '!=', 89 ], 81 90 ], 82 91 ]; -
progress-planner/tags/1.3.0/classes/suggested-tasks/local-tasks/providers/one-time/class-settings-saved.php
r3268602 r3283338 21 21 */ 22 22 protected const PROVIDER_ID = 'settings-saved'; 23 24 /** 25 * The task priority. 26 * 27 * @var string 28 */ 29 protected $priority = 'high'; 23 30 24 31 /** … … 51 58 */ 52 59 public function get_description() { 53 return sprintf( 54 /* translators: %s:<a href="https://prpl.fyi/fill-settings-page" target="_blank">settings page</a> link */ 55 \esc_html__( 'Head over to the settings page and fill in the required information. %s', 'progress-planner' ), 56 '<a href="https://prpl.fyi/fill-settings-page" target="_blank">' . \esc_html__( 'settings page', 'progress-planner' ) . '</a>' 57 ); 60 return \esc_html__( 'Head over to the settings page and fill in the required information.', 'progress-planner' ); 58 61 } 59 62 -
progress-planner/tags/1.3.0/classes/suggested-tasks/local-tasks/providers/repetitive/class-core-update.php
r3268602 r3283338 145 145 'dismissable' => $this->is_dismissable(), 146 146 'url' => $this->get_url(), 147 'url_target' => $this->get_url_target(), 147 148 'description' => $this->get_description(), 148 149 ]; -
progress-planner/tags/1.3.0/classes/suggested-tasks/local-tasks/providers/repetitive/class-create.php
r3268602 r3283338 38 38 39 39 /** 40 * The task URL target. 41 * 42 * @var string 43 */ 44 protected $url_target = '_blank'; 45 46 /** 40 47 * The data collector. 41 48 * … … 49 56 public function __construct() { 50 57 $this->data_collector = new Last_Published_Post_Data_Collector(); 51 $this->url = \admin_url( 'post-new.php?post_type=post' );58 $this->url = 'https://prpl.fyi/valuable-content'; 52 59 } 53 60 … … 58 65 */ 59 66 public function get_title() { 60 return esc_html__( 'Create a post', 'progress-planner' );67 return esc_html__( 'Create valuable content', 'progress-planner' ); 61 68 } 62 69 … … 67 74 */ 68 75 public function get_description() { 69 return esc_html__( 'Create a new, relevant post. If you write an in-depth post you may earn an extra point.', 'progress-planner' ); 76 return sprintf( 77 /* translators: %s: "Read more" link. */ 78 \esc_html__( 'Time to add more valuable content to your site! Check our blog for inspiration. %s.', 'progress-planner' ), 79 '<a href="https://prpl.fyi/valuable-content" target="_blank">' . \esc_html__( 'Read more', 'progress-planner' ) . '</a>' 80 ); 70 81 } 71 82 … … 77 88 * @return array 78 89 */ 79 public function modify_ task_data( $task_data ) {90 public function modify_evaluated_task_data( $task_data ) { 80 91 $last_published_post_data = $this->data_collector->collect(); 81 92 … … 86 97 // Add the post ID and post length to the task data. 87 98 $task_data['post_id'] = $last_published_post_data['post_id']; 88 $task_data['long'] = $last_published_post_data['long'];89 99 90 100 return $task_data; … … 133 143 'dismissable' => $this->is_dismissable(), 134 144 'url' => $this->get_url(), 145 'url_target' => $this->get_url_target(), 135 146 'description' => $this->get_description(), 136 147 ]; … … 147 158 * @return int 148 159 */ 149 public function get_points _for_task( $task_id = '' ) {160 public function get_points( $task_id = '' ) { 150 161 151 162 if ( ! $task_id ) { 152 // Get the post that was created last.153 $post_data = $this->data_collector->collect();154 } else {155 $post_data = \progress_planner()->get_suggested_tasks()->get_tasks_by( 'task_id', $task_id );156 $post_data = $post_data[0] ?? false;157 }158 159 // Post was created, but then deleted?160 if ( ! $post_data || empty( $post_data['post_id'] ) ) {161 163 return $this->points; 162 164 } 163 165 164 return true === $post_data['long'] ? 2 : 1; 166 $post_data = \progress_planner()->get_suggested_tasks()->get_tasks_by( 'task_id', $task_id ); 167 $post_data = $post_data[0] ?? false; 168 169 // Backwards compatibility. 170 if ( $post_data && isset( $post_data['long'] ) ) { 171 return true === $post_data['long'] ? 2 : 1; 172 } 173 174 return $this->points; 165 175 } 166 176 } -
progress-planner/tags/1.3.0/classes/suggested-tasks/local-tasks/providers/repetitive/class-review.php
r3268602 r3283338 73 73 74 74 /** 75 * The include post types. 76 * 77 * @var string[] 78 */ 79 protected $include_post_types = []; 80 81 /** 75 82 * Initialize the task provider. 76 83 * … … 78 85 */ 79 86 public function init() { 87 $this->include_post_types = \progress_planner()->get_settings()->get_post_types_names(); // Wait for the post types to be initialized. 88 80 89 \add_filter( 'progress_planner_update_posts_tasks_args', [ $this, 'filter_update_posts_args' ] ); 81 90 } … … 179 188 $last_updated_posts = $this->get_old_posts( 180 189 [ 181 'post__in' => $important_page_ids, 190 'post__in' => $important_page_ids, 191 'post_type' => 'any', 182 192 ] 183 193 ); … … 212 222 213 223 $this->task_post_mappings[ $task_id ] = [ 214 'task_id' => $task_id, 215 'post_id' => $post->ID, 224 'task_id' => $task_id, 225 'post_id' => $post->ID, 226 'post_type' => $post->post_type, 216 227 ]; 217 228 } … … 247 258 'category' => $this->get_provider_category(), 248 259 'post_id' => $task_data['post_id'], 260 'post_type' => $task_data['post_type'], 249 261 'date' => \gmdate( 'YW' ), 250 262 ]; … … 278 290 'dismissable' => $this->is_dismissable(), 279 291 'url' => $this->get_url( $task_id ), 292 'url_target' => $this->get_url_target(), 280 293 'description' => $this->get_description( $task_id ), 281 294 ]; … … 322 335 */ 323 336 public function get_old_posts( $args = [] ) { 324 $args = wp_parse_args( 325 $args, 326 [ 327 'posts_per_page' => static::ITEMS_TO_INJECT, 328 'post_type' => [ 'page', 'post' ], 329 'post_status' => 'publish', 330 'orderby' => 'modified', 331 'order' => 'ASC', 332 'date_query' => [ 333 [ 334 'column' => 'post_modified', 335 'before' => '-6 months', 337 $posts = []; 338 339 if ( ! empty( $this->include_post_types ) ) { 340 $args = wp_parse_args( 341 $args, 342 [ 343 'posts_per_page' => static::ITEMS_TO_INJECT, 344 'post_type' => $this->include_post_types, 345 'post_status' => 'publish', 346 'orderby' => 'modified', 347 'order' => 'ASC', 348 'date_query' => [ 349 [ 350 'column' => 'post_modified', 351 'before' => '-6 months', 352 ], 336 353 ], 337 ], 338 ] 339 ); 340 341 /** 342 * Filters the args for the posts & pages we want user to review. 343 * 344 * @param array $args The get_postsargs. 345 */ 346 $args = apply_filters( 'progress_planner_update_posts_tasks_args', $args ); 347 348 // Get the post that was updated last. 349 $posts = \get_posts( $args ); 354 ] 355 ); 356 357 /** 358 * Filters the args for the posts & pages we want user to review. 359 * 360 * @param array $args The get_postsargs. 361 */ 362 $args = apply_filters( 'progress_planner_update_posts_tasks_args', $args ); 363 364 // Get the post that was updated last. 365 $posts = \get_posts( $args ); 366 } 367 368 // Get the pages saved in the settings that have not been updated in the last 6 months. 369 $saved_page_type_ids = $this->get_saved_page_types(); 370 371 if ( ! empty( $saved_page_type_ids ) ) { 372 $pages_to_update = \get_posts( 373 [ 374 'post_type' => 'any', 375 'post_status' => 'publish', 376 'orderby' => 'modified', 377 'order' => 'ASC', 378 'ignore_sticky_posts' => true, 379 'date_query' => [ 380 [ 381 'column' => 'post_modified', 382 'before' => '-6 months', 383 ], 384 ], 385 'post__in' => $saved_page_type_ids, 386 ] 387 ); 388 389 // Merge the posts & pages to update. Put the pages first. 390 $posts = array_merge( $pages_to_update, $posts ); 391 } 350 392 351 393 return $posts ? $posts : []; … … 398 440 399 441 /** 442 * Get the saved page-types. 443 * 444 * @return int[] 445 */ 446 protected function get_saved_page_types() { 447 $ids = []; 448 // Add the saved page-types to the post__not_in array. 449 $page_types = \progress_planner()->get_admin__page_settings()->get_settings(); 450 foreach ( $page_types as $page_type ) { 451 if ( isset( $page_type['value'] ) && 0 !== (int) $page_type['value'] ) { 452 $ids[] = (int) $page_type['value']; 453 } 454 } 455 return $ids; 456 } 457 458 /** 400 459 * Check if a specific task is completed. 401 460 * -
progress-planner/tags/1.3.0/playwright.config.js
r3264985 r3283338 4 4 testDir: './tests/e2e', 5 5 timeout: 30000, 6 fullyParallel: false,7 6 forbidOnly: !! process.env.CI, 8 7 retries: process.env.CI ? 2 : 0, 9 workers: 1,10 8 reporter: 'html', 11 globalSetup: require.resolve( './tests/e2e/auth.setup.js' ), 9 globalSetup: './tests/e2e/auth.setup.js', 10 globalTeardown: './tests/e2e/auth.setup.js', 12 11 use: { 13 12 baseURL: process.env.WORDPRESS_URL || 'http://localhost:8080', … … 18 17 projects: [ 19 18 { 20 name: ' chromium',19 name: 'sequential', 21 20 use: { ...devices[ 'Desktop Chrome' ] }, 21 testMatch: 'sequential.spec.js', 22 fullyParallel: false, 23 workers: 1, 24 }, 25 { 26 name: 'parallel', 27 use: { ...devices[ 'Desktop Chrome' ] }, 28 testIgnore: [ 29 'onboarding.spec.js', 30 'task-tagline.spec.js', 31 'todo.spec.js', 32 'todo-reorder.spec.js', 33 'todo-complete.spec.js', 34 'sequential.spec.js', 35 ], 36 fullyParallel: true, 37 workers: 4, 22 38 }, 23 39 ], -
progress-planner/tags/1.3.0/progress-planner.php
r3268602 r3283338 10 10 * Requires at least: 6.3 11 11 * Requires PHP: 7.4 12 * Version: 1. 2.012 * Version: 1.3.0 13 13 * Author: Team Emilia Projects 14 14 * Author URI: https://prpl.fyi/about … … 67 67 'Progress_Planner\Onboard' => [ 'Progress_Planner\Utils\Onboard', '1.1.1' ], 68 68 'Progress_Planner\Playground' => [ 'Progress_Planner\Utils\Playground', '1.1.1' ], 69 70 'Progress_Planner\Admin\Widgets\Published_Content' => [ 'Progress_Planner\Admin\Widgets\Content_Activity', '1.3.0' ], 69 71 ]; 70 72 -
progress-planner/tags/1.3.0/readme.txt
r3268602 r3283338 5 5 Tested up to: 6.8 6 6 Requires PHP: 7.4 7 Stable tag: 1. 2.07 Stable tag: 1.3.0 8 8 License: GPL3+ 9 9 License URI: https://www.gnu.org/licenses/gpl-3.0.en.html … … 110 110 111 111 == Changelog == 112 113 = 1.3.0 = 114 115 Enhancements: 116 117 * Improved checks when adding Ravi icon to the Yoast SEO settings page. 118 * Add "golden" tasks to weekly emails. 119 * Add text to clarify when the user has completed all tasks. 120 * Improve the content widget & stats to show more accurate data. It now shows content _activity_ instead of content _published_. 121 * Implemented "valuable post-types" and added settings for them. 122 * Changed the "create a post" task to "create valuable content". 123 * Renamed & migrated content badges. 124 * Added a link to the 'Create valuable content' task description. 125 * Improve accessibility of Recommendations (and other links) linking to external resources 126 127 Bugs we fixed: 128 129 * Fixed error during plugin uninstall. 130 * Archive_Format data collector hooks weren't registered early enough. 131 * Ensure fresh plugin list by clearing plugin cache before checking for inactive plugins after deletion. 132 * Clear plugin cache when checking for inactive plugins. 133 * Delete no-longer relevant pending tasks. 134 * Fixed timing issue for tasks added by 3rd-party plugins. 112 135 113 136 = 1.2.0 = -
progress-planner/tags/1.3.0/uninstall.php
r3268602 r3283338 14 14 15 15 require_once __DIR__ . '/classes/class-settings.php'; 16 require_once __DIR__ . '/classes/ class-query.php';16 require_once __DIR__ . '/classes/activities/class-query.php'; 17 17 18 18 /** -
progress-planner/tags/1.3.0/views/admin-page-header.php
r3268602 r3283338 35 35 </button> 36 36 <?php 37 // Render the settings button.38 \progress_planner()->get_ui__popover()->the_popover( 'settings' )->render_button(39 '',40 \progress_planner()->get_asset( 'images/icon_settings.svg' ) . '<span class="screen-reader-text">' . \esc_html__( 'Settings', 'progress-planner' ) . '</span>'41 );42 // Render the settings popover.43 \progress_planner()->get_ui__popover()->the_popover( 'settings' )->render();44 37 45 38 // Render the subscribe form button and popover if the license key is not set. -
progress-planner/tags/1.3.0/views/admin-page-settings.php
r3264985 r3283338 35 35 <form id="prpl-settings"> 36 36 <?php \progress_planner()->the_view( 'page-settings/pages.php' ); ?> 37 <?php \progress_planner()->the_view( 'page-settings/settings.php' ); ?> 38 <?php \progress_planner()->the_view( 'page-settings/license.php' ); ?> 37 38 <div id="prpl-grid-column-wrapper"> 39 <?php \progress_planner()->the_view( 'page-settings/post-types.php' ); ?> 40 <?php \progress_planner()->the_view( 'page-settings/settings.php' ); ?> 41 <?php \progress_planner()->the_view( 'page-settings/license.php' ); ?> 42 </div> 39 43 40 44 <?php wp_nonce_field( 'progress_planner' ); ?> -
progress-planner/tags/1.3.0/views/page-settings/license.php
r3238385 r3283338 16 16 ?> 17 17 18 <div class="prpl-column ">18 <div class="prpl-column prpl-column-license"> 19 19 <div class="prpl-widget-wrapper"> 20 20 <h2 class="prpl-settings-section-title prpl-settings-section-license"> -
progress-planner/tags/1.3.0/views/page-settings/pages.php
r3238385 r3283338 12 12 ?> 13 13 14 <div class="prpl-column ">14 <div class="prpl-column prpl-column-pages"> 15 15 <div class="prpl-widget-wrapper"> 16 16 <h2 class="prpl-settings-section-title"> -
progress-planner/tags/1.3.0/views/page-settings/settings.php
r3264985 r3283338 14 14 ?> 15 15 16 <div class="prpl-column ">16 <div class="prpl-column prpl-column-login-destination"> 17 17 <div class="prpl-widget-wrapper"> 18 18 <h2 class="prpl-settings-section-title"> 19 19 <span class="icon"> 20 <?php \progress_planner()->the_asset( 'images/icon_ forward.svg' ); ?>20 <?php \progress_planner()->the_asset( 'images/icon_user.svg' ); ?> 21 21 </span> 22 22 <span> -
progress-planner/tags/1.3.0/views/page-widgets/suggested-tasks.php
r3268602 r3283338 27 27 <ul style="display:none"></ul> 28 28 <ul class="prpl-suggested-tasks-list"></ul> 29 <p class="prpl-no-suggested-tasks"> 30 <?php \esc_html_e( 'You have completed all recommended tasks.', 'progress-planner' ); ?> 31 <br> 32 <?php \esc_html_e( 'Check back later for new tasks!', 'progress-planner' ); ?> 33 </p> 29 34 <hr> 30 35 </div> -
progress-planner/tags/1.3.0/views/page-widgets/whats-new.php
r3268602 r3283338 25 25 ?> 26 26 <li> 27 <a href="<?php echo \esc_url( $prpl_blog_post['link'] ); ?>" target="_blank"> 28 <h3><?php echo \esc_html( $prpl_blog_post['title']['rendered'] ); ?></h3> 29 <?php if ( $prpl_blog_post_image_url ) : ?> 27 <h3> 28 <a href="<?php echo \esc_url( $prpl_blog_post['link'] ); ?>" target="_blank"> 29 <?php echo \esc_html( $prpl_blog_post['title']['rendered'] ); ?> 30 </a> 31 </h3> 32 <?php if ( $prpl_blog_post_image_url ) : ?> 33 <a href="<?php echo \esc_url( $prpl_blog_post['link'] ); ?>" target="_blank"> 30 34 <div class="prpl-blog-post-image" style="background-image:url(<?php echo \esc_url( $prpl_blog_post_image_url ); ?>)"></div> 31 < ?php endif; ?>32 < /a>35 </a> 36 <?php endif; ?> 33 37 <p><?php echo \esc_html( \wp_trim_words( \wp_strip_all_tags( $prpl_blog_post['content']['rendered'] ), 55 ) ); ?></p> 34 38 <hr /> -
progress-planner/tags/1.3.0/views/setting/page-select.php
r3268602 r3283338 95 95 </div> 96 96 <?php endif; ?> 97 98 99 97 </div> 100 98 <?php endforeach; ?> -
progress-planner/trunk/CHANGELOG.md
r3268602 r3283338 1 = 1.3.0 = 2 3 Enhancements: 4 5 * Improved checks when adding Ravi icon to the Yoast SEO settings page. 6 * Add "golden" tasks to weekly emails. 7 * Add text to clarify when the user has completed all tasks. 8 * Improve the content widget & stats to show more accurate data. It now shows content _activity_ instead of content _published_. 9 * Implemented "valuable post-types" and added settings for them. 10 * Changed the "create a post" task to "create valuable content". 11 * Renamed & migrated content badges. 12 * Added a link to the 'Create valuable content' task description. 13 * Improve accessibility of Recommendations (and other links) linking to external resources 14 15 Bugs we fixed: 16 17 * Fixed error during plugin uninstall. 18 * Archive_Format data collector hooks weren't registered early enough. 19 * Ensure fresh plugin list by clearing plugin cache before checking for inactive plugins after deletion. 20 * Clear plugin cache when checking for inactive plugins. 21 * Delete no-longer relevant pending tasks. 22 * Fixed timing issue for tasks added by 3rd-party plugins. 23 1 24 = 1.2.0 = 2 25 -
progress-planner/trunk/assets/css/admin.css
r3264985 r3283338 421 421 Settings popover. 422 422 \*------------------------------------*/ 423 #prpl-settings-license-form,424 #prpl-settings-form {425 426 label {427 display: block;428 }429 430 p {431 max-width: 42em;432 }433 434 h3 {435 font-size: 1.15em;436 }437 438 button.button-primary {439 margin-top: 1em;440 }441 }442 443 .driver-popover.prpl-driverjs-theme {444 background-color: var(--prpl-background-orange);445 color: var(--prpl-color-text);446 447 .driver-popover-title {448 color: var(--prpl-color-headings);449 }450 451 button {452 color: var(--prpl-color-headings);453 }454 455 button:not(.driver-popover-close-btn):hover {456 background-color: var(--prpl-background-orange);457 }458 }459 460 423 #prpl-settings-license-form { 461 424 … … 466 429 gap: var(--prpl-padding); 467 430 } 468 } 469 431 432 p { 433 max-width: 42em; 434 } 435 436 h3 { 437 font-size: 1.15em; 438 } 439 440 button.button-primary { 441 margin-top: 1em; 442 } 443 } 444 445 .driver-popover.prpl-driverjs-theme { 446 background-color: var(--prpl-background-orange); 447 color: var(--prpl-color-text); 448 449 .driver-popover-title { 450 color: var(--prpl-color-headings); 451 } 452 453 button { 454 color: var(--prpl-color-headings); 455 } 456 457 button:not(.driver-popover-close-btn):hover { 458 background-color: var(--prpl-background-orange); 459 } 460 } 461 462 /*------------------------------------*\ 463 External link accessibility helper. 464 \*------------------------------------*/ 465 .prpl-external-link-icon { 466 display: inline-flex; 467 margin-inline-start: 0.25em; 468 vertical-align: middle; 469 470 svg { 471 width: 1em; 472 height: 1em; 473 } 474 } -
progress-planner/trunk/assets/css/page-widgets/suggested-tasks.css
r3264985 r3283338 103 103 } 104 104 105 .prpl-no-suggested-tasks { 106 display: none; 107 } 108 105 109 hr { 106 110 display: block; … … 111 115 hr { 112 116 display: none; 117 } 118 119 .prpl-no-suggested-tasks { 120 display: block; 121 background-color: var(--prpl-background-green); 122 padding: calc(var(--prpl-padding) / 2); 113 123 } 114 124 } -
progress-planner/trunk/assets/css/page-widgets/whats-new.css
r3217671 r3283338 11 11 li { 12 12 13 > a { 14 color: var(--prpl-color-gray-6); 15 text-decoration: none; 13 h3 { 14 margin-top: 0; 15 font-size: 1.15em; 16 font-weight: 600; 16 17 17 h3 { 18 margin-top: 0; 19 font-size: 1.15em; 20 font-weight: 600; 18 > a { 19 color: var(--prpl-color-headings); 20 text-decoration: none; 21 21 22 &::after { 23 content: url('data:image/svg+xml,<%3Fxml version="1.0" encoding="UTF-8"%3F><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12"><path fill="%23currentcolor" d="M6 1h5v5L8.86 3.85 4.7 8 4 7.3l4.15-4.16zM2 3h2v1H2v6h6V8h1v2a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1"/></svg>'); 24 margin-left: 0.25em; 25 width: 0.75em; 26 height: 0.75em; 27 display: inline-block; 22 .prpl-external-link-icon { 23 margin-inline-start: 0.15em; 28 24 } 29 25 } 30 26 } 27 31 28 32 29 img { … … 38 35 display: flex; 39 36 justify-content: flex-end; 40 41 a::after {42 content: url('data:image/svg+xml,<%3Fxml version="1.0" encoding="UTF-8"%3F><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12"><path fill="%23currentcolor" d="M6 1h5v5L8.86 3.85 4.7 8 4 7.3l4.15-4.16zM2 3h2v1H2v6h6V8h1v2a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1"/></svg>');43 margin-left: 0.25em;44 width: 1em;45 height: 1em;46 display: inline-block;47 }48 37 } 49 38 } -
progress-planner/trunk/assets/css/settings-page.css
r3264985 r3283338 220 220 } 221 221 222 .prpl-license-keys-wrapper { 223 display: flex; 224 flex-direction: column; 225 gap: 1rem; 226 227 .prpl-license-key-wrapper { 228 display: flex; 229 align-items: center; 230 gap: 0.5rem; 231 232 .prpl-license-status { 233 width: 1rem; 234 height: 1rem; 235 236 svg { 222 /* Post types */ 223 .prpl-column-post-types { 224 225 .prpl-settings-section-title { 226 227 svg { 228 color: #038d88; 229 230 path { 231 fill: currentcolor; 232 } 233 } 234 235 background-color: #f3faf9; 236 } 237 238 } 239 240 /* Login destination */ 241 .prpl-column-login-destination { 242 243 .prpl-settings-section-title { 244 245 svg { 246 color: var(--prpl-color-accent-red); 247 } 248 249 background-color: var(--prpl-background-red); 250 } 251 252 } 253 254 255 /* License */ 256 .prpl-column-license { 257 258 .prpl-settings-section-title { 259 260 svg { 261 color: #0773b4; 262 } 263 background-color: #effbfe; 264 } 265 266 .prpl-license-keys-wrapper { 267 display: flex; 268 flex-direction: column; 269 gap: 1rem; 270 max-width: 40rem; 271 272 & > p:first-child { 273 margin-top: 0; 274 } 275 276 .prpl-license-key-wrapper { 277 display: flex; 278 align-items: center; 279 gap: 0.5rem; 280 281 .prpl-license-status { 237 282 width: 1rem; 238 283 height: 1rem; 239 } 240 } 241 } 242 243 input { 244 width: 20rem; 245 max-width: calc(100% - 2rem); 246 } 247 } 284 285 svg { 286 width: 1rem; 287 height: 1rem; 288 } 289 } 290 } 291 292 input { 293 width: 30rem; 294 max-width: calc(100% - 2rem); 295 border: 1px solid var(--prpl-color-gray-2); 296 box-shadow: 1px 2px 4px 0 rgba(0, 0, 0, 0.05); 297 } 298 } 299 300 } 301 302 303 /* Grid layout for wrapper for: 304 - Valuable post types 305 - Default login destination 306 - License keys 307 */ 308 #prpl-grid-column-wrapper { 309 display: grid; 310 margin-bottom: var(--prpl-gap); 311 312 /* There are 5 or less valuable post types */ 313 grid-template-columns: 1fr 1fr; 314 grid-template-rows: auto auto; 315 gap: var(--prpl-settings-page-gap); 316 317 .prpl-column { 318 align-self: stretch; 319 display: flex; 320 flex-direction: column; 321 322 .prpl-widget-wrapper { 323 flex: 1; 324 margin-bottom: 0; 325 } 326 } 327 328 /* Valuable post types */ 329 .prpl-column:nth-child(1) { 330 grid-column: 1; 331 grid-row: 1; 332 } 333 334 /* Default login destination */ 335 .prpl-column:nth-child(2) { 336 grid-column: 2; 337 grid-row: 1; 338 } 339 340 /* License keys */ 341 .prpl-column:nth-child(3) { 342 grid-column: 1 / span 2; 343 grid-row: 2; 344 } 345 346 /* We have more than 5 valuable post types */ 347 &:has([data-has-many-valuable-post-types]) { 348 grid-template-rows: auto auto; 349 350 /* Valuable post types */ 351 .prpl-column:nth-child(1) { 352 grid-column: 1; 353 grid-row: 1 / span 2; 354 355 /* Span 2 rows on the left */ 356 } 357 358 /* Default login destination */ 359 .prpl-column:nth-child(2) { 360 grid-column: 2; 361 grid-row: 1; 362 } 363 364 /* License keys */ 365 .prpl-column:nth-child(3) { 366 grid-column: 2; 367 grid-row: 2; 368 } 369 } 370 } 371 372 /* Valuable post types */ 373 #prpl-post-types-include-wrapper { 374 padding-top: 0.75rem; 375 376 label { 377 display: block; 378 margin-top: 0.75rem; 379 380 &:first-child { 381 margin-top: 0; 382 } 383 } 384 } -
progress-planner/trunk/assets/js/settings-page.js
r3264985 r3283338 67 67 }; 68 68 formData.forEach( function ( value, key ) { 69 data[ key ] = value; 69 // Handle array notation in keys 70 if ( key.endsWith( '[]' ) ) { 71 const baseKey = key.slice( 0, -2 ); 72 if ( ! data[ baseKey ] ) { 73 data[ baseKey ] = []; 74 } 75 data[ baseKey ].push( value ); 76 } else { 77 data[ key ] = value; 78 } 70 79 } ); 71 80 const request = wp.ajax.post( 'prpl_settings_form', data ); -
progress-planner/trunk/assets/js/settings.js
r3264985 r3283338 7 7 * Dependencies: progress-planner/ajax-request, progress-planner/onboard, wp-util, progress-planner/l10n 8 8 */ 9 document10 .getElementById( 'prpl-settings-form' )11 .addEventListener( 'submit', function ( event ) {12 event.preventDefault();13 const form = new FormData( this );14 const data = form.getAll( 'prpl-settings-post-types-include[]' );15 16 // Save the options.17 const request = wp.ajax.post( 'progress_planner_save_cpt_settings', {18 _ajax_nonce: progressPlanner.nonce,19 include_post_types: data.join( ',' ),20 } );21 request.done( () => {22 window.location.reload();23 } );24 25 document.getElementById( 'submit-include-post-types' ).disabled = true;26 document.getElementById( 'submit-include-post-types' ).innerHTML =27 prplL10n( 'saving' );28 } );29 9 30 10 // Submit the email. -
progress-planner/trunk/assets/js/web-components/prpl-big-counter.js
r3198155 r3283338 17 17 backgroundColor || 'var(--prpl-background-purple)'; 18 18 19 const el = this; 20 19 21 this.innerHTML = ` 20 22 <div style=" … … 31 33 margin-bottom: var(--prpl-padding); 32 34 "> 35 <div class="container-width" style="width: 100%;"></div> 33 36 <span style=" 34 37 font-size: var(--prpl-font-size-5xl); … … 36 39 font-weight: 600; 37 40 ">${ number }</span> 38 <span style="font-size: var(--prpl-font-size-2xl);">${ content }</span> 41 <span style="font-size: var(--prpl-font-size-2xl);"> 42 <span class="resize" style="font-size: 100%; display: inline-block; width: max-content;">${ content }</span> 43 </span> 39 44 </div> 40 45 `; 46 47 const resizeFont = () => { 48 const element = el.querySelector( '.resize' ); 49 if ( ! element ) { 50 return; 51 } 52 53 element.style.fontSize = '100%'; 54 55 let size = 100; 56 while ( 57 element.clientWidth > 58 el.querySelector( '.container-width' ).clientWidth 59 ) { 60 if ( size < 80 ) { 61 element.style.fontSize = size + '%'; 62 element.style.width = '100%'; 63 break; 64 } 65 size -= 1; 66 element.style.fontSize = size + '%'; 67 } 68 }; 69 70 resizeFont(); 71 window.addEventListener( 'resize', resizeFont ); 41 72 } 42 73 } -
progress-planner/trunk/assets/js/web-components/prpl-suggested-task.js
r3268602 r3283338 22 22 action = '', 23 23 url = '', 24 url_target = '_self', 24 25 dismissable = false, 25 26 provider_id = '', … … 38 39 let taskHeading = title; 39 40 if ( url ) { 40 taskHeading = `<a href="${ url }" >${ title }</a>`;41 taskHeading = `<a href="${ url }" target="${ url_target }">${ title }</a>`; 41 42 } 42 43 -
progress-planner/trunk/assets/js/yoast-focus-element.js
r3268602 r3283338 6 6 7 7 /** 8 * Check if the value of the element matches the value specified in the task. 9 * 10 * @param {Element} element The element to check. 11 * @param {Object} task The task to check. 12 * @return {boolean} True if the value matches, false otherwise. 8 * Yoast Focus Element class. 13 9 */ 14 function checkTaskValue( element, task ) { 15 if ( ! task.valueElement ) { 16 return true; 17 } 18 19 const attributeName = task.valueElement.attributeName || 'value'; 20 const attributeValue = task.valueElement.attributeValue; 21 const operator = task.valueElement.operator || '='; 22 const currentValue = element.getAttribute( attributeName ) || ''; 23 24 return '!=' === operator 25 ? currentValue !== attributeValue 26 : currentValue === attributeValue; 27 } 28 29 /** 30 * Observe the Yoast sidebar clicks. 31 */ 32 function observeYoastSidebarClicks() { 33 const container = document.querySelector( '#yoast-seo-settings' ); 34 35 if ( ! container ) { 36 return; 37 } 38 39 const waitForNav = new MutationObserver( ( mutationsList, observer ) => { 40 const nav = container.querySelector( 41 'nav.yst-sidebar-navigation__sidebar' 10 class ProgressPlannerYoastFocus { 11 /** 12 * Constructor. 13 */ 14 constructor() { 15 this.container = document.querySelector( '#yoast-seo-settings' ); 16 this.tasks = progressPlannerYoastFocusElement.tasks; 17 this.baseUrl = progressPlannerYoastFocusElement.base_url; 18 19 if ( this.container ) { 20 this.init(); 21 } 22 } 23 24 /** 25 * Initialize the Yoast Focus Element. 26 */ 27 init() { 28 this.waitForMainAndObserveContent(); 29 this.observeYoastSidebarClicks(); 30 } 31 32 /** 33 * Check if the value of the element matches the value specified in the task. 34 * 35 * @param {Element} element The element to check. 36 * @param {Object} task The task to check. 37 * @return {boolean} True if the value matches, false otherwise. 38 */ 39 checkTaskValue( element, task ) { 40 if ( ! task.valueElement ) { 41 return true; 42 } 43 44 const attributeName = task.valueElement.attributeName || 'value'; 45 const attributeValue = task.valueElement.attributeValue; 46 const operator = task.valueElement.operator || '='; 47 const currentValue = element.getAttribute( attributeName ) || ''; 48 49 return '!=' === operator 50 ? currentValue !== attributeValue 51 : currentValue === attributeValue; 52 } 53 54 /** 55 * Observe the Yoast sidebar clicks. 56 */ 57 observeYoastSidebarClicks() { 58 const waitForNav = new MutationObserver( 59 ( mutationsList, observer ) => { 60 const nav = this.container.querySelector( 61 'nav.yst-sidebar-navigation__sidebar' 62 ); 63 if ( nav ) { 64 observer.disconnect(); 65 66 nav.addEventListener( 'click', ( e ) => { 67 const link = e.target.closest( 'a' ); 68 if ( link ) { 69 this.waitForMainAndObserveContent(); 70 } 71 } ); 72 } 73 } 42 74 ); 43 if ( nav ) { 44 // Sidebar nav loaded. 45 observer.disconnect(); 46 47 nav.addEventListener( 'click', ( e ) => { 48 const link = e.target.closest( 'a' ); 49 if ( link ) { 50 // Sidebar link clicked. 51 waitForMainAndObserveContent(); // re-run logic after clicking 52 } 53 } ); 54 } 55 } ); 56 57 waitForNav.observe( container, { 58 childList: true, 59 subtree: true, 60 } ); 61 } 62 63 /** 64 * Wait for the main content to load and observe the content. 65 */ 66 function waitForMainAndObserveContent() { 67 const container = document.querySelector( '#yoast-seo-settings' ); 68 if ( ! container ) { 69 return; 70 } 71 72 const waitForMain = new MutationObserver( ( mutationsList, observer ) => { 73 const main = container.querySelector( 'main.yst-paper' ); 74 if ( main ) { 75 // Main loaded. 76 observer.disconnect(); 77 78 const childObserver = new MutationObserver( ( mutations ) => { 79 for ( const mutation of mutations ) { 80 if ( 81 mutation.type === 'attributes' && 82 mutation.attributeName === 'class' 83 ) { 84 const el = mutation.target; 85 if ( 86 el.parentElement === main && 87 el.classList.contains( 'yst-opacity-100' ) 88 ) { 89 // Fully loaded content. 90 childObserver.disconnect(); 91 92 // Loop through the tasks and add the focus element. 93 for ( const task of progressPlannerYoastFocusElement.tasks ) { 94 // Try to find the toggleButton. 95 const valueElement = el.querySelector( 96 task.valueElement.elementSelector 97 ); 98 let raviIconPositionAbsolute = true; 99 100 if ( valueElement ) { 101 // We usually add icon to the option header. 102 let addIconElement = valueElement.closest( 103 task.iconElement 104 ); 105 106 // Exception is the upload input field. 75 76 waitForNav.observe( this.container, { 77 childList: true, 78 subtree: true, 79 } ); 80 } 81 82 /** 83 * Wait for the main content to load and observe the content. 84 */ 85 waitForMainAndObserveContent() { 86 const waitForMain = new MutationObserver( 87 ( mutationsList, observer ) => { 88 const main = this.container.querySelector( 'main.yst-paper' ); 89 if ( main ) { 90 observer.disconnect(); 91 92 const childObserver = new MutationObserver( 93 ( mutations ) => { 94 for ( const mutation of mutations ) { 95 if ( 96 mutation.type === 'attributes' && 97 mutation.attributeName === 'class' 98 ) { 99 const el = mutation.target; 107 100 if ( 108 ! addIconElement && 109 valueElement.type === 'hidden' 110 ) { 111 addIconElement = valueElement 112 .closest( 'fieldset' ) 113 .querySelector( task.iconElement ); 114 115 raviIconPositionAbsolute = false; 116 } 117 118 // Upload input field. 119 if ( ! addIconElement ) { 120 continue; 121 } 122 123 // Append next to the valueElemen, only if it's not already there. 124 if ( 125 ! addIconElement.querySelector( 126 '.prpl-form-row-ravi' 101 el.parentElement === main && 102 el.classList.contains( 103 'yst-opacity-100' 127 104 ) 128 105 ) { 129 // Check for value if specified in task. 130 const valueMatches = checkTaskValue( 131 valueElement, 132 task 133 ); 134 135 // Create a new span with the class prpl-form-row-ravi. 136 const raviIconWrapper = 137 document.createElement( 'span' ); 138 raviIconWrapper.classList.add( 139 'prpl-form-row-ravi', 140 'prpl-element-awards-points-icon-wrapper' 141 ); 142 143 if ( valueMatches ) { 144 raviIconWrapper.classList.add( 145 'complete' 146 ); 147 } 148 149 // Styling for absolute positioning. 150 if ( raviIconPositionAbsolute ) { 151 addIconElement.style.position = 152 'relative'; 153 154 raviIconWrapper.style.position = 155 'absolute'; 156 raviIconWrapper.style.right = 157 '3.5rem'; 158 raviIconWrapper.style.top = '-7px'; 159 } 160 161 raviIconWrapper.appendChild( 162 document.createElement( 'span' ) 163 ); 164 165 // Create an icon image. 166 const iconImg = 167 document.createElement( 'img' ); 168 iconImg.src = 169 progressPlannerYoastFocusElement.base_url + 170 '/assets/images/icon_progress_planner.svg'; 171 iconImg.alt = 'Ravi'; 172 iconImg.width = 16; 173 iconImg.height = 16; 174 175 // Append the icon image to the raviIconWrapper. 176 raviIconWrapper 177 .querySelector( 'span' ) 178 .appendChild( iconImg ); 179 180 // Add the points to the raviIconWrapper. 181 const pointsWrapper = 182 document.createElement( 'span' ); 183 pointsWrapper.classList.add( 184 'prpl-form-row-points' 185 ); 186 pointsWrapper.textContent = valueMatches 187 ? '✓' 188 : '+1'; 189 raviIconWrapper.appendChild( 190 pointsWrapper 191 ); 192 193 // Finally add the raviIconWrapper to the DOM. 194 addIconElement.appendChild( 195 raviIconWrapper 196 ); 197 198 // Watch for changes in aria-checked to update the icon dynamically 199 const valueElementObserver = 200 new MutationObserver( () => { 201 // Check value again if specified 202 const currentValueMatches = 203 checkTaskValue( 204 valueElement, 205 task 206 ); 207 208 if ( currentValueMatches ) { 209 raviIconWrapper.classList.add( 210 'complete' 211 ); 212 213 pointsWrapper.textContent = 214 '✓'; 215 } else { 216 raviIconWrapper.classList.remove( 217 'complete' 218 ); 219 220 pointsWrapper.textContent = 221 '+1'; 222 } 223 } ); 224 225 valueElementObserver.observe( 226 valueElement, 227 { 228 attributes: true, 229 attributeFilter: [ 230 task.valueElement 231 .attributeName, 232 ], 233 } 234 ); 106 this.processTasks( el ); 235 107 } 236 108 } 237 109 } 238 110 } 239 } 111 ); 112 113 main.querySelectorAll( ':scope > *' ).forEach( 114 ( child ) => { 115 childObserver.observe( child, { 116 attributes: true, 117 attributeFilter: [ 'class' ], 118 } ); 119 } 120 ); 240 121 } 241 } ); 242 243 // Watch direct children of main.yst-paper 244 main.querySelectorAll( ':scope > *' ).forEach( ( child ) => { 245 childObserver.observe( child, { 246 attributes: true, 247 attributeFilter: [ 'class' ], 248 } ); 249 } ); 250 } 251 } ); 252 253 waitForMain.observe( container, { 254 childList: true, 255 subtree: true, 256 } ); 122 } 123 ); 124 125 waitForMain.observe( this.container, { 126 childList: true, 127 subtree: true, 128 } ); 129 } 130 131 /** 132 * Process all tasks for a given element. 133 * 134 * @param {Element} el The element to process tasks for. 135 */ 136 processTasks( el ) { 137 for ( const task of this.tasks ) { 138 const valueElement = el.querySelector( 139 task.valueElement.elementSelector 140 ); 141 const raviIconPositionAbsolute = true; 142 143 if ( valueElement ) { 144 this.processTask( 145 valueElement, 146 task, 147 raviIconPositionAbsolute 148 ); 149 } 150 } 151 } 152 153 /** 154 * Process a single task. 155 * 156 * @param {Element} valueElement The value element to process. 157 * @param {Object} task The task to process. 158 * @param {boolean} raviIconPositionAbsolute Whether the icon should be absolutely positioned. 159 */ 160 processTask( valueElement, task, raviIconPositionAbsolute ) { 161 let addIconElement = valueElement.closest( task.iconElement ); 162 163 // Exception is the upload input field. 164 if ( ! addIconElement && valueElement.type === 'hidden' ) { 165 addIconElement = valueElement 166 .closest( 'fieldset' ) 167 .querySelector( task.iconElement ); 168 raviIconPositionAbsolute = false; 169 } 170 171 if ( ! addIconElement ) { 172 return; 173 } 174 175 if ( 176 ! addIconElement.querySelector( '[data-prpl-element="ravi-icon"]' ) 177 ) { 178 this.addIcon( 179 valueElement, 180 addIconElement, 181 task, 182 raviIconPositionAbsolute 183 ); 184 } 185 } 186 187 /** 188 * Add icon to the element. 189 * 190 * @param {Element} valueElement The value element. 191 * @param {Element} addIconElement The element to add the icon to. 192 * @param {Object} task The task. 193 * @param {boolean} raviIconPositionAbsolute Whether the icon should be absolutely positioned. 194 */ 195 addIcon( valueElement, addIconElement, task, raviIconPositionAbsolute ) { 196 const valueMatches = this.checkTaskValue( valueElement, task ); 197 198 // Create a new span with the class prpl-form-row-ravi. 199 const raviIconWrapper = document.createElement( 'span' ); 200 raviIconWrapper.classList.add( 201 'prpl-element-awards-points-icon-wrapper' 202 ); 203 raviIconWrapper.setAttribute( 'data-prpl-element', 'ravi-icon' ); 204 205 if ( valueMatches ) { 206 raviIconWrapper.classList.add( 'complete' ); 207 } 208 209 // Styling for absolute positioning. 210 if ( raviIconPositionAbsolute ) { 211 addIconElement.style.position = 'relative'; 212 213 raviIconWrapper.style.position = 'absolute'; 214 raviIconWrapper.style.right = '3.5rem'; 215 raviIconWrapper.style.top = '-7px'; 216 } 217 218 raviIconWrapper.appendChild( document.createElement( 'span' ) ); 219 220 // Create an icon image. 221 const iconImg = document.createElement( 'img' ); 222 iconImg.src = this.baseUrl + '/assets/images/icon_progress_planner.svg'; 223 iconImg.alt = 'Ravi'; 224 iconImg.width = 16; 225 iconImg.height = 16; 226 227 // Append the icon image to the raviIconWrapper. 228 raviIconWrapper.querySelector( 'span' ).appendChild( iconImg ); 229 230 // Add the points to the raviIconWrapper. 231 const pointsWrapper = document.createElement( 'span' ); 232 pointsWrapper.classList.add( 'prpl-form-row-points' ); 233 pointsWrapper.textContent = valueMatches ? '✓' : '+1'; 234 raviIconWrapper.appendChild( pointsWrapper ); 235 236 // Watch for changes in aria-checked to update the icon dynamically 237 const valueElementObserver = new MutationObserver( () => { 238 const currentValueMatches = this.checkTaskValue( 239 valueElement, 240 task 241 ); 242 243 if ( currentValueMatches ) { 244 raviIconWrapper.classList.add( 'complete' ); 245 pointsWrapper.textContent = '✓'; 246 } else { 247 raviIconWrapper.classList.remove( 'complete' ); 248 pointsWrapper.textContent = '+1'; 249 } 250 } ); 251 252 valueElementObserver.observe( valueElement, { 253 attributes: true, 254 attributeFilter: [ task.valueElement.attributeName ], 255 } ); 256 257 // Finally add the raviIconWrapper to the DOM. 258 addIconElement.appendChild( raviIconWrapper ); 259 } 257 260 } 258 261 259 // Run once on initial page load. 260 waitForMainAndObserveContent(); 261 observeYoastSidebarClicks(); 262 // Initialize the Yoast Focus Element. 263 new ProgressPlannerYoastFocus(); -
progress-planner/trunk/classes/actions/class-content-scan.php
r3268602 r3283338 172 172 // Loop through the posts and update the stats. 173 173 foreach ( $posts as $post ) { 174 // Set the activity .175 $activities[ $post->ID ] = \progress_planner()->get_activities__content_helpers()->get_activity_from_post( $post );174 // Set the activity, we're dealing only with published posts (but just in case). 175 $activities[ $post->ID ] = \progress_planner()->get_activities__content_helpers()->get_activity_from_post( $post, 'publish' === $post->post_status ? 'publish' : 'update' ); 176 176 // Set the word count. 177 177 \progress_planner()->get_activities__content_helpers()->get_word_count( $post->post_content, $post->ID ); -
progress-planner/trunk/classes/actions/class-content.php
r3268602 r3283338 281 281 } 282 282 283 $activity = \progress_planner()->get_activities__content_helpers()->get_activity_from_post( $post ); 284 $activity->type = $type; 283 $activity = \progress_planner()->get_activities__content_helpers()->get_activity_from_post( $post, $type ); 285 284 286 285 // Update the badges. … … 306 305 } 307 306 307 // We need to set the date explicitly since post_date & post_modified dates are not changed when the post is trashed. 308 if ( 'trash' === $type ) { 309 $activity->date = new \DateTime(); 310 } 311 308 312 $activity->save(); 309 313 -
progress-planner/trunk/classes/activities/class-content-helpers.php
r3268602 r3283338 100 100 * 101 101 * @param \WP_Post $post The post object. 102 * @param string $activity_type The activity type. 102 103 * 103 104 * @return \Progress_Planner\Activities\Content 104 105 */ 105 public function get_activity_from_post( $post ) { 106 $type = 'publish' === $post->post_status ? 'publish' : 'update'; 107 $date = 'publish' === $post->post_status ? $post->post_date : $post->post_modified; 108 106 public function get_activity_from_post( $post, $activity_type = 'publish' ) { 109 107 $activity = new Activities_Content(); 110 108 $activity->category = 'content'; 111 $activity->type = $ type;112 $activity->date = \progress_planner()->get_utils__date()->get_datetime_from_mysql_date( $ date);109 $activity->type = $activity_type; 110 $activity->date = \progress_planner()->get_utils__date()->get_datetime_from_mysql_date( $post->post_modified ); 113 111 $activity->data_id = (string) $post->ID; 114 112 $activity->user_id = (int) $post->post_author; -
progress-planner/trunk/classes/activities/class-suggested-task.php
r3268602 r3283338 66 66 67 67 // Default points for a suggested task. 68 $points = 1;69 $ create_post_provider = new Create();68 $points = 1; 69 $task_provider = \progress_planner()->get_suggested_tasks()->get_local()->get_task_provider( $this->data_id ); 70 70 71 $data = \progress_planner()->get_suggested_tasks()->get_local()->get_data_from_task_id( $this->data_id );72 if ( isset( $data['provider_id'] ) && $create_post_provider->get_provider_id() === $data['provider_id'] ) {73 $points = $ create_post_provider->get_points_for_task( $this->data_id);71 if ( $task_provider ) { 72 // Create post task provider had a different points system, this is for backwards compatibility. 73 $points = $task_provider instanceof Create ? $task_provider->get_points( $this->data_id ) : $task_provider->get_points(); 74 74 } 75 75 -
progress-planner/trunk/classes/admin/class-dashboard-widget-score.php
r3264985 r3283338 48 48 49 49 \progress_planner()->get_admin__enqueue()->enqueue_style( "progress-planner/dashboard-widgets/{$this->id}" ); 50 51 \progress_planner()->get_admin__enqueue()->enqueue_script( 'external-link-accessibility-helper' ); 50 52 51 53 \progress_planner()->the_view( "dashboard-widgets/{$this->id}.php" ); -
progress-planner/trunk/classes/admin/class-page-settings.php
r3264985 r3283338 172 172 173 173 $this->save_settings(); 174 $this->save_post_types(); 174 175 $this->save_license(); 175 176 176 do_action( 'progress_planner_settings_form_options_stored' );177 \do_action( 'progress_planner_settings_form_options_stored' ); 177 178 178 179 \wp_send_json_success( \esc_html__( 'Options stored successfully', 'progress-planner' ) ); … … 185 186 */ 186 187 public function save_settings() { 187 $redirect_on_login = isset( $_POST['prpl-redirect-on-login'] ) // phpcs:ignore WordPress.Security.NonceVerification.Missing 188 ? \sanitize_text_field( \wp_unslash( $_POST['prpl-redirect-on-login'] ) ) // phpcs:ignore WordPress.Security.NonceVerification.Missing 188 189 // Check the nonce. 190 \check_admin_referer( 'progress_planner' ); 191 192 $redirect_on_login = isset( $_POST['prpl-redirect-on-login'] ) 193 ? \sanitize_text_field( \wp_unslash( $_POST['prpl-redirect-on-login'] ) ) 189 194 : false; 190 195 … … 193 198 194 199 /** 200 * Save the post types. 201 * 202 * @return void 203 */ 204 public function save_post_types() { 205 206 // Check the nonce. 207 \check_admin_referer( 'progress_planner' ); 208 209 $include_post_types = isset( $_POST['prpl-post-types-include'] ) 210 ? array_map( 'sanitize_text_field', \wp_unslash( $_POST['prpl-post-types-include'] ) ) // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 211 // If no post types are selected, use the default post types (post and page can be deregistered). 212 : array_intersect( [ 'post', 'page' ], \progress_planner()->get_settings()->get_public_post_types() ); 213 214 \progress_planner()->get_settings()->set( 'include_post_types', $include_post_types ); 215 } 216 217 /** 195 218 * Save the license key. 196 219 * … … 198 221 */ 199 222 public function save_license() { 200 $license = isset( $_POST['prpl-pro-license-key'] ) // phpcs:ignore WordPress.Security.NonceVerification.Missing 201 ? \sanitize_text_field( \wp_unslash( $_POST['prpl-pro-license-key'] ) ) // phpcs:ignore WordPress.Security.NonceVerification.Missing 223 224 // Check the nonce. 225 \check_admin_referer( 'progress_planner' ); 226 227 $license = isset( $_POST['prpl-pro-license-key'] ) 228 ? \sanitize_text_field( \wp_unslash( $_POST['prpl-pro-license-key'] ) ) 202 229 : ''; 203 230 -
progress-planner/trunk/classes/admin/class-page.php
r3268602 r3283338 28 28 \add_action( 'admin_menu', [ $this, 'add_page' ] ); 29 29 \add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] ); 30 \add_action( 'wp_ajax_progress_planner_save_cpt_settings', [ $this, 'save_cpt_settings' ] );31 30 \add_action( 'in_admin_header', [ $this, 'remove_admin_notices' ], PHP_INT_MAX ); 32 31 … … 52 51 \progress_planner()->get_admin__widgets__latest_badge(), 53 52 \progress_planner()->get_admin__widgets__badge_streak(), 54 \progress_planner()->get_admin__widgets__ published_content(),53 \progress_planner()->get_admin__widgets__content_activity(), 55 54 \progress_planner()->get_admin__widgets__whats_new(), 56 55 ]; … … 188 187 \progress_planner()->get_admin__enqueue()->enqueue_script( 'onboard', $default_localization_data ); 189 188 } 189 190 \progress_planner()->get_admin__enqueue()->enqueue_script( 'external-link-accessibility-helper' ); 190 191 } 191 192 … … 200 201 ] 201 202 ); 203 204 \progress_planner()->get_admin__enqueue()->enqueue_script( 'external-link-accessibility-helper' ); 202 205 } 203 206 } … … 292 295 293 296 /** 294 * Save the post types settings.295 *296 * @return void297 */298 public function save_cpt_settings() {299 \check_ajax_referer( 'progress_planner', 'nonce', false );300 $include_post_types = isset( $_POST['include_post_types'] )301 ? \sanitize_text_field( \wp_unslash( $_POST['include_post_types'] ) )302 : 'post,page';303 $include_post_types = \explode( ',', $include_post_types );304 \progress_planner()->get_settings()->set( 'include_post_types', $include_post_types );305 306 \wp_send_json_success(307 [308 'message' => \esc_html__( 'Settings saved.', 'progress-planner' ),309 ]310 );311 }312 313 /**314 297 * Remove all admin notices when the user is on the Progress Planner page. 315 298 * … … 363 346 .update-plugins { 364 347 position: absolute; 365 left: 22px;366 top: 0px;348 left: 18px; 349 bottom: 0px; 367 350 min-width: 15px; 368 351 height: 15px; -
progress-planner/trunk/classes/admin/widgets/class-suggested-tasks.php
r3268602 r3283338 81 81 $task_details['action'] = 'celebrate'; 82 82 $task_details['status'] = 'pending_celebration'; 83 84 // Award 2 points if last created post was long.85 $create_provider = new Create();86 if ( $create_provider->get_provider_id() === $task_provider->get_provider_id() ) {87 $task_details['points'] = $create_provider->get_points_for_task( $task_id );88 }89 83 90 84 $tasks[] = $task_details; -
progress-planner/trunk/classes/class-badges.php
r3268602 r3283338 55 55 public function __construct() { 56 56 $this->content = [ 57 \progress_planner()->get_badges__content__ wonderful_writer(),58 \progress_planner()->get_badges__content__ bold_blogger(),59 \progress_planner()->get_badges__content__ awesome_author(),57 \progress_planner()->get_badges__content__content_curator(), 58 \progress_planner()->get_badges__content__revision_ranger(), 59 \progress_planner()->get_badges__content__purposeful_publisher(), 60 60 ]; 61 61 -
progress-planner/trunk/classes/class-base.php
r3268602 r3283338 194 194 'get_chart' => [ 'get_ui__chart', '1.1.1' ], 195 195 'get_popover' => [ 'get_ui__popover', '1.1.1' ], 196 197 'get_admin__widgets__published_content' => [ 'get_admin__widgets__content_activity', '1.3.0' ], 196 198 ]; 197 199 -
progress-planner/trunk/classes/class-plugin-migrations.php
r3268602 r3283338 11 11 12 12 use Progress_Planner\Update\Update_111; 13 use Progress_Planner\Update\Update_130; 13 14 14 15 /** … … 42 43 private const UPGRADE_CLASSES = [ 43 44 '1.1.1' => Update_111::class, 45 '1.3.0' => Update_130::class, 44 46 ]; 45 47 -
progress-planner/trunk/classes/class-plugin-upgrade-tasks.php
r3268602 r3283338 25 25 26 26 // Check if the plugin was upgraded or new plugin was activated. 27 \add_action( 'init', [ $this, 'handle_activation_or_upgrade' ], 10 );27 \add_action( 'init', [ $this, 'handle_activation_or_upgrade' ], 100 ); // We need to run this after the Local_Tasks_Manager::init() is called. 28 28 } 29 29 -
progress-planner/trunk/classes/class-settings.php
r3198155 r3283338 111 111 return $this->save_settings(); 112 112 } 113 114 /** 115 * Get an array of post-types names for the stats. 116 * 117 * @return string[] 118 */ 119 public function get_post_types_names() { 120 static $include_post_types; 121 122 if ( ! doing_action( 'init' ) && ! did_action( 'init' ) ) { 123 \trigger_error( // phpcs:ignore 124 sprintf( 125 '%1$s was called too early. Wait for init hook to be called to have access to the post types.', 126 \esc_html( get_class() . '::' . __FUNCTION__ ) 127 ), 128 E_USER_WARNING 129 ); 130 } 131 132 // Since we're working with CPTs, dont cache until init. 133 if ( isset( $include_post_types ) && ! empty( $include_post_types ) ) { 134 return $include_post_types; 135 } 136 137 $public_post_types = $this->get_public_post_types(); 138 139 // Post or pages can be deregistered. 140 $default = array_intersect( [ 'post', 'page' ], $public_post_types ); 141 142 // Filter the saved post types. 143 $include_post_types = array_intersect( $this->get( [ 'include_post_types' ], $default ), $public_post_types ); 144 145 return empty( $include_post_types ) ? $default : \array_values( $include_post_types ); 146 } 147 148 /** 149 * Get the public post types. 150 * 151 * @return string[] 152 */ 153 public function get_public_post_types() { 154 $public_post_types = \array_filter( \get_post_types( [ 'public' => true ] ), 'is_post_type_viewable' ); 155 156 unset( $public_post_types['attachment'] ); 157 unset( $public_post_types['elementor_library'] ); // Elementor templates are not a post type we want to track. 158 159 /** 160 * Filter the public post types. 161 * 162 * @param string[] $public_post_types The public post types. 163 * 164 * @return string[] 165 */ 166 return \apply_filters( 'progress_planner_public_post_types', $public_post_types ); 167 } 113 168 } -
progress-planner/trunk/classes/class-suggested-tasks.php
r3268602 r3283338 44 44 45 45 if ( \is_admin() ) { 46 \add_action( 'init', [ $this, 'init' ], 1 );46 \add_action( 'init', [ $this, 'init' ], 100 ); // Wait for the post types to be initialized. 47 47 } 48 48 … … 580 580 581 581 /** 582 * Add a remote task to the pending tasks. 583 * 584 * @param string $task_id The task ID. 585 * 586 * @return bool 587 */ 588 public function add_remote_task_to_pending_tasks( $task_id ) { 589 $remote_task_data = $this->get_remote_task_by_task_id( $task_id ); 590 591 if ( ! $remote_task_data ) { 592 return false; 593 } 594 595 return \progress_planner()->get_suggested_tasks()->get_local()->add_pending_task( 596 [ 597 'task_id' => $task_id, 598 'provider_id' => $remote_task_data['category'] ?? '', // Remote tasks use the category as provider_id. 599 'category' => $remote_task_data['category'] ?? '', 600 ] 601 ); 602 } 603 604 605 /** 582 606 * Handle the suggested task action. 583 607 * … … 601 625 // We need to add the task to the pending tasks first, before marking it as completed. 602 626 if ( false !== strpos( $task_id, 'remote-task' ) ) { 603 $remote_task_data = $this->get_remote_task_by_task_id( $task_id ); 604 \progress_planner()->get_suggested_tasks()->get_local()->add_pending_task( 605 [ 606 'task_id' => $task_id, 607 'provider_id' => $remote_task_data['category'] ?? '', // Remote tasks use the category as provider_id. 608 'category' => $remote_task_data['category'] ?? '', 609 ] 610 ); 627 $this->add_remote_task_to_pending_tasks( $task_id ); 611 628 } 612 629 … … 627 644 case 'snooze': 628 645 $duration = isset( $_POST['duration'] ) ? \sanitize_text_field( \wp_unslash( $_POST['duration'] ) ) : ''; 629 $updated = $this->snooze_task( $task_id, $duration ); 646 647 // We need to add the task to the pending tasks first, before marking it as snoozed. 648 if ( false !== strpos( $task_id, 'remote-task' ) ) { 649 $this->add_remote_task_to_pending_tasks( $task_id ); 650 } 651 652 $updated = $this->snooze_task( $task_id, $duration ); 630 653 break; 631 654 -
progress-planner/trunk/classes/rest/class-stats.php
r3268602 r3283338 163 163 $data['recommendations'] = []; 164 164 foreach ( $ravis_recommendations as $recommendation ) { 165 $ data['recommendations'][]= [165 $r = [ 166 166 'id' => $recommendation['task_id'], 167 167 'title' => $recommendation['title'], … … 169 169 'provider_id' => $recommendation['provider_id'], 170 170 ]; 171 172 if ( 'user' === $recommendation['provider_id'] ) { 173 $r['points'] = isset( $recommendation['points'] ) ? $recommendation['points'] : 0; 174 } 175 $data['recommendations'][] = $r; 171 176 } 172 177 -
progress-planner/trunk/classes/suggested-tasks/class-local-tasks-manager.php
r3268602 r3283338 29 29 use Progress_Planner\Suggested_Tasks\Local_Tasks\Providers\Integrations\Yoast\Add_Yoast_Providers; 30 30 use Progress_Planner\Suggested_Tasks\Local_Tasks\Providers\User as User_Tasks; 31 use Progress_Planner\Suggested_Tasks\Local_Tasks\Providers\One_Time\Set_Valuable_Post_Types; 31 32 32 33 /** … … 65 66 new Search_Engine_Visibility(), 66 67 new User_Tasks(), 68 new Set_Valuable_Post_Types(), 67 69 ]; 68 70 … … 71 73 72 74 // At this point both local and task providers for the plugins we integrate with are instantiated, so initialize them. 73 \add_action( ' plugins_loaded', [ $this, 'init' ], 11 );75 \add_action( 'init', [ $this, 'init' ], 99 ); // Wait for the post types to be initialized. 74 76 75 77 // Add the cleanup action. … … 239 241 $task_id = $task_data['task_id']; 240 242 243 // Check if the task is no longer relevant. 244 $task_object = Local_Task_Factory::create_task_from( 'id', $task_id ); 245 $task_provider = $this->get_task_provider( $task_object->get_provider_id() ); 246 if ( $task_provider && ! $task_provider->is_task_relevant() ) { 247 // Remove the task from the pending tasks. 248 \progress_planner()->get_suggested_tasks()->delete_task( $task_id ); 249 } 250 241 251 $task_result = $this->evaluate_task( $task_id ); 242 252 if ( false !== $task_result ) { … … 353 363 $tasks, 354 364 function ( $task ) { 355 // If the task was already completed, remove it. 356 if ( 'completed' === $task['status'] ) { 357 return false; 358 } 359 360 if ( isset( $task['date'] ) ) { 365 366 if ( 'pending' === $task['status'] && isset( $task['date'] ) ) { 361 367 return (string) \gmdate( 'YW' ) === (string) $task['date']; 362 368 } -
progress-planner/trunk/classes/suggested-tasks/data-collector/class-data-collector-manager.php
r3268602 r3283338 14 14 use Progress_Planner\Suggested_Tasks\Data_Collector\Post_Author; 15 15 use Progress_Planner\Suggested_Tasks\Data_Collector\Last_Published_Post; 16 use Progress_Planner\Suggested_Tasks\Data_Collector\Archive_Format; 16 17 17 18 /** … … 40 41 new Post_Author(), 41 42 new Last_Published_Post(), 43 new Archive_Format(), 42 44 ]; 43 45 -
progress-planner/trunk/classes/suggested-tasks/data-collector/class-inactive-plugins.php
r3268602 r3283338 51 51 } 52 52 53 // Clear the plugins cache, so get_plugins() returns the latest plugins. 54 wp_cache_delete( 'plugins', 'plugins' ); 55 53 56 $plugins = get_plugins(); 54 57 $plugins_active = 0; -
progress-planner/trunk/classes/suggested-tasks/data-collector/class-last-published-post.php
r3268602 r3283338 23 23 24 24 /** 25 * The include post types. 26 * 27 * @var string[] 28 */ 29 protected $include_post_types = []; 30 31 /** 25 32 * Initialize the data collector. 26 33 * … … 28 35 */ 29 36 public function init() { 37 \add_action( 'init', [ $this, 'set_include_post_types' ], 99 ); // Wait for all CPTs to be registered. 30 38 \add_action( 'transition_post_status', [ $this, 'update_last_published_post_cache' ], 10, 3 ); 39 } 40 41 /** 42 * Set the include post types. 43 * 44 * @return void 45 */ 46 public function set_include_post_types() { 47 $this->include_post_types = \progress_planner()->get_settings()->get_post_types_names(); 31 48 } 32 49 … … 41 58 */ 42 59 public function update_last_published_post_cache( $new_status, $old_status, $post ) { 43 if ( $new_status === 'publish' || $old_status === 'publish') {60 if ( true === \in_array( get_post_type( $post ), $this->include_post_types, true ) && ( $new_status === 'publish' || $old_status === 'publish' ) ) { 44 61 $this->update_cache(); 45 62 } … … 67 84 'orderby' => 'date', 68 85 'order' => 'DESC', 86 'post_type' => $this->include_post_types, 69 87 ] 70 88 ); … … 73 91 $data = [ 74 92 'post_id' => $last_created_posts[0]->ID, 75 'long' => \progress_planner()->get_activities__content_helpers()->is_post_long( $last_created_posts[0]->ID ) ? true : false,76 93 'post_date' => $last_created_posts[0]->post_date, 77 94 ]; -
progress-planner/trunk/classes/suggested-tasks/local-tasks/providers/class-local-tasks-interface.php
r3264985 r3283338 81 81 */ 82 82 public function capability_required(); 83 84 /** 85 * Check if the task is still relevant. 86 * For example, we have a task to disable author archives if there is only one author. 87 * If in the meantime more authors are added, the task is no longer relevant and the task should be removed. 88 * 89 * @return bool 90 */ 91 public function is_task_relevant(); 83 92 } -
progress-planner/trunk/classes/suggested-tasks/local-tasks/providers/class-local-tasks.php
r3268602 r3283338 80 80 81 81 /** 82 * The task URL target. 83 * 84 * @var string 85 */ 86 protected $url_target = '_self'; 87 88 /** 82 89 * The task link setting. 83 90 * … … 159 166 160 167 return ''; 168 } 169 170 /** 171 * Get the task URL. 172 * 173 * @return string 174 */ 175 public function get_url_target() { 176 return $this->url_target ? $this->url_target : '_self'; 161 177 } 162 178 … … 265 281 return false; 266 282 } 283 284 /** 285 * Check if the task is still relevant. 286 * For example, we have a task to disable author archives if there is only one author. 287 * If in the meantime more authors are added, the task is no longer relevant and the task should be removed. 288 * 289 * @return bool 290 */ 291 public function is_task_relevant() { 292 return true; 293 } 267 294 } -
progress-planner/trunk/classes/suggested-tasks/local-tasks/providers/class-user.php
r3264985 r3283338 94 94 'points' => 0, 95 95 'url' => '', 96 'url_target' => '_self', 96 97 'description' => '', 97 98 'link_setting' => [], -
progress-planner/trunk/classes/suggested-tasks/local-tasks/providers/integrations/yoast/class-add-yoast-providers.php
r3268602 r3283338 47 47 foreach ( $this->providers as $provider ) { 48 48 49 // Add Ravi icon if the task is pending or completed.50 if ( $provider-> should_add_task() ) {49 // Add Ravi icon if the task is pending or is completed. 50 if ( $provider->is_task_relevant() || \progress_planner()->get_suggested_tasks()->was_task_completed( $provider->get_task_id() ) ) { 51 51 $focus_task = $provider->get_focus_tasks(); 52 52 53 53 if ( $focus_task ) { 54 $focus_tasks [] = $focus_task;54 $focus_tasks = array_merge( $focus_tasks, $focus_task ); 55 55 } 56 56 } -
progress-planner/trunk/classes/suggested-tasks/local-tasks/providers/integrations/yoast/class-archive-author.php
r3268602 r3283338 73 73 public function get_focus_tasks() { 74 74 return [ 75 'iconElement' => '.yst-toggle-field__header', 76 'valueElement' => [ 77 'elementSelector' => 'button[data-id="input-wpseo_titles-disable-author"]', 78 'attributeName' => 'aria-checked', 79 'attributeValue' => 'false', 80 'operator' => '=', 75 [ 76 'iconElement' => '.yst-toggle-field__header', 77 'valueElement' => [ 78 'elementSelector' => 'button[data-id="input-wpseo_titles-disable-author"]', 79 'attributeName' => 'aria-checked', 80 'attributeValue' => 'false', 81 'operator' => '=', 82 ], 81 83 ], 82 84 ]; … … 89 91 */ 90 92 public function should_add_task() { 93 94 if ( ! $this->is_task_relevant() ) { 95 return false; 96 } 97 91 98 // If the author archive is already disabled, we don't need to add the task. 92 99 if ( YoastSEO()->helpers->options->get( 'disable-author' ) === true ) { … … 94 101 } 95 102 103 return true; 104 } 105 106 /** 107 * Check if the task is still relevant. 108 * For example, we have a task to disable author archives if there is only one author. 109 * If in the meantime more authors are added, the task is no longer relevant and the task should be removed. 110 * 111 * @return bool 112 */ 113 public function is_task_relevant() { 96 114 // If there is more than one author, we don't need to add the task. 97 115 if ( $this->data_collector->collect() > self::MINIMUM_AUTHOR_WITH_POSTS ) { -
progress-planner/trunk/classes/suggested-tasks/local-tasks/providers/integrations/yoast/class-archive-date.php
r3268602 r3283338 56 56 public function get_focus_tasks() { 57 57 return [ 58 'iconElement' => '.yst-toggle-field__header', 59 'valueElement' => [ 60 'elementSelector' => 'button[data-id="input-wpseo_titles-disable-date"]', 61 'attributeName' => 'aria-checked', 62 'attributeValue' => 'false', 63 'operator' => '=', 58 [ 59 'iconElement' => '.yst-toggle-field__header', 60 'valueElement' => [ 61 'elementSelector' => 'button[data-id="input-wpseo_titles-disable-date"]', 62 'attributeName' => 'aria-checked', 63 'attributeValue' => 'false', 64 'operator' => '=', 65 ], 64 66 ], 65 67 ]; … … 72 74 */ 73 75 public function should_add_task() { 76 77 if ( ! $this->is_task_relevant() ) { 78 return false; 79 } 80 81 // If the date archive is already disabled, we don't need to add the task. 82 return YoastSEO()->helpers->options->get( 'disable-date' ) !== true; 83 } 84 85 /** 86 * Check if the task is still relevant. 87 * For example, we have a task to disable author archives if there is only one author. 88 * If in the meantime more authors are added, the task is no longer relevant and the task should be removed. 89 * 90 * @return bool 91 */ 92 public function is_task_relevant() { 74 93 // If the permalink structure includes %year%, %monthnum%, or %day%, we don't need to add the task. 75 94 $permalink_structure = \get_option( 'permalink_structure' ); … … 78 97 } 79 98 80 // If the date archive is already disabled, we don't need to add the task. 81 return YoastSEO()->helpers->options->get( 'disable-date' ) !== true; 99 return true; 82 100 } 83 101 } -
progress-planner/trunk/classes/suggested-tasks/local-tasks/providers/integrations/yoast/class-archive-format.php
r3268602 r3283338 73 73 public function get_focus_tasks() { 74 74 return [ 75 'iconElement' => '.yst-toggle-field__header', 76 'valueElement' => [ 77 'elementSelector' => 'button[data-id="input-wpseo_titles-disable-post_format"]', 78 'attributeName' => 'aria-checked', 79 'attributeValue' => 'false', 80 'operator' => '=', 75 [ 76 'iconElement' => '.yst-toggle-field__header', 77 'valueElement' => [ 78 'elementSelector' => 'button[data-id="input-wpseo_titles-disable-post_format"]', 79 'attributeName' => 'aria-checked', 80 'attributeValue' => 'false', 81 'operator' => '=', 82 ], 81 83 ], 82 84 ]; … … 89 91 */ 90 92 public function should_add_task() { 91 $archive_format_count = $this->data_collector->collect(); 92 93 // If there are more than X posts with a post format, we don't need to add the task. X is set in the class. 94 if ( $archive_format_count > static::MINIMUM_POSTS_WITH_FORMAT ) { 93 if ( ! $this->is_task_relevant() ) { 95 94 return false; 96 95 } … … 103 102 return true; 104 103 } 104 105 /** 106 * Check if the task is still relevant. 107 * For example, we have a task to disable author archives if there is only one author. 108 * If in the meantime more authors are added, the task is no longer relevant and the task should be removed. 109 * 110 * @return bool 111 */ 112 public function is_task_relevant() { 113 $archive_format_count = $this->data_collector->collect(); 114 115 // If there are more than X posts with a post format, we don't need to add the task. X is set in the class. 116 if ( $archive_format_count > static::MINIMUM_POSTS_WITH_FORMAT ) { 117 return false; 118 } 119 120 return true; 121 } 105 122 } -
progress-planner/trunk/classes/suggested-tasks/local-tasks/providers/integrations/yoast/class-crawl-settings-emoji-scripts.php
r3268602 r3283338 56 56 public function get_focus_tasks() { 57 57 return [ 58 'iconElement' => '.yst-toggle-field__header', 59 'valueElement' => [ 60 'elementSelector' => 'button[data-id="input-wpseo-remove_emoji_scripts"]', 61 'attributeName' => 'aria-checked', 62 'attributeValue' => 'true', 63 'operator' => '=', 58 [ 59 'iconElement' => '.yst-toggle-field__header', 60 'valueElement' => [ 61 'elementSelector' => 'button[data-id="input-wpseo-remove_emoji_scripts"]', 62 'attributeName' => 'aria-checked', 63 'attributeValue' => 'true', 64 'operator' => '=', 65 ], 64 66 ], 65 67 ]; -
progress-planner/trunk/classes/suggested-tasks/local-tasks/providers/integrations/yoast/class-crawl-settings-feed-authors.php
r3268602 r3283338 73 73 public function get_focus_tasks() { 74 74 return [ 75 'iconElement' => '.yst-toggle-field__header', 76 'valueElement' => [ 77 'elementSelector' => 'button[data-id="input-wpseo-remove_feed_authors"]', 78 'attributeName' => 'aria-checked', 79 'attributeValue' => 'true', 80 'operator' => '=', 75 [ 76 'iconElement' => '.yst-toggle-field__header', 77 'valueElement' => [ 78 'elementSelector' => 'button[data-id="input-wpseo-remove_feed_authors"]', 79 'attributeName' => 'aria-checked', 80 'attributeValue' => 'true', 81 'operator' => '=', 82 ], 81 83 ], 82 84 ]; … … 89 91 */ 90 92 public function should_add_task() { 91 // If there is more than one author, we don't need to add the task. 92 if ( $this->data_collector->collect() > self::MINIMUM_AUTHOR_WITH_POSTS) {93 94 if ( ! $this->is_task_relevant() ) { 93 95 return false; 94 96 } … … 104 106 return true; 105 107 } 108 109 /** 110 * Check if the task is still relevant. 111 * For example, we have a task to disable author archives if there is only one author. 112 * If in the meantime more authors are added, the task is no longer relevant and the task should be removed. 113 * 114 * @return bool 115 */ 116 public function is_task_relevant() { 117 // If there is more than one author, we don't need to add the task. 118 if ( $this->data_collector->collect() > self::MINIMUM_AUTHOR_WITH_POSTS ) { 119 return false; 120 } 121 122 return true; 123 } 106 124 } -
progress-planner/trunk/classes/suggested-tasks/local-tasks/providers/integrations/yoast/class-crawl-settings-feed-global-comments.php
r3268602 r3283338 56 56 public function get_focus_tasks() { 57 57 return [ 58 'iconElement' => '.yst-toggle-field__header', 59 'valueElement' => [ 60 'elementSelector' => 'button[data-id="input-wpseo-remove_feed_global_comments"]', 61 'attributeName' => 'aria-checked', 62 'attributeValue' => 'true', 63 'operator' => '=', 58 [ 59 'iconElement' => '.yst-toggle-field__header', 60 'valueElement' => [ 61 'elementSelector' => 'button[data-id="input-wpseo-remove_feed_global_comments"]', 62 'attributeName' => 'aria-checked', 63 'attributeValue' => 'true', 64 'operator' => '=', 65 ], 64 66 ], 65 67 ]; -
progress-planner/trunk/classes/suggested-tasks/local-tasks/providers/integrations/yoast/class-media-pages.php
r3268602 r3283338 56 56 public function get_focus_tasks() { 57 57 return [ 58 'iconElement' => '.yst-toggle-field__header', 59 'valueElement' => [ 60 'elementSelector' => 'button[data-id="input-wpseo_titles-disable-attachment"]', 61 'attributeName' => 'aria-checked', 62 'attributeValue' => 'false', 63 'operator' => '=', 58 [ 59 'iconElement' => '.yst-toggle-field__header', 60 'valueElement' => [ 61 'elementSelector' => 'button[data-id="input-wpseo_titles-disable-attachment"]', 62 'attributeName' => 'aria-checked', 63 'attributeValue' => 'false', 64 'operator' => '=', 65 ], 64 66 ], 65 67 ]; -
progress-planner/trunk/classes/suggested-tasks/local-tasks/providers/integrations/yoast/class-organization-logo.php
r3268602 r3283338 71 71 public function get_focus_tasks() { 72 72 return [ 73 'iconElement' => 'legend.yst-label', 74 'valueElement' => [ 75 'elementSelector' => $this->yoast_seo->helpers->options->get( 'company_or_person', 'company' ) !== 'person' 76 ? 'input[name="wpseo_titles.company_logo"]' 77 : 'input[name="wpseo_titles.person_logo"]', 78 'attributeName' => 'value', 79 'attributeValue' => '', 80 'operator' => '!=', 73 [ 74 'iconElement' => 'legend.yst-label', 75 'valueElement' => [ 76 'elementSelector' => 'input[name="wpseo_titles.company_logo"]', 77 'attributeName' => 'value', 78 'attributeValue' => '', 79 'operator' => '!=', 80 ], 81 ], 82 [ 83 'iconElement' => 'legend.yst-label', 84 'valueElement' => [ 85 'elementSelector' => 'input[name="wpseo_titles.person_logo"]', 86 'attributeName' => 'value', 87 'attributeValue' => '', 88 'operator' => '!=', 89 ], 81 90 ], 82 91 ]; -
progress-planner/trunk/classes/suggested-tasks/local-tasks/providers/one-time/class-settings-saved.php
r3268602 r3283338 21 21 */ 22 22 protected const PROVIDER_ID = 'settings-saved'; 23 24 /** 25 * The task priority. 26 * 27 * @var string 28 */ 29 protected $priority = 'high'; 23 30 24 31 /** … … 51 58 */ 52 59 public function get_description() { 53 return sprintf( 54 /* translators: %s:<a href="https://prpl.fyi/fill-settings-page" target="_blank">settings page</a> link */ 55 \esc_html__( 'Head over to the settings page and fill in the required information. %s', 'progress-planner' ), 56 '<a href="https://prpl.fyi/fill-settings-page" target="_blank">' . \esc_html__( 'settings page', 'progress-planner' ) . '</a>' 57 ); 60 return \esc_html__( 'Head over to the settings page and fill in the required information.', 'progress-planner' ); 58 61 } 59 62 -
progress-planner/trunk/classes/suggested-tasks/local-tasks/providers/repetitive/class-core-update.php
r3268602 r3283338 145 145 'dismissable' => $this->is_dismissable(), 146 146 'url' => $this->get_url(), 147 'url_target' => $this->get_url_target(), 147 148 'description' => $this->get_description(), 148 149 ]; -
progress-planner/trunk/classes/suggested-tasks/local-tasks/providers/repetitive/class-create.php
r3268602 r3283338 38 38 39 39 /** 40 * The task URL target. 41 * 42 * @var string 43 */ 44 protected $url_target = '_blank'; 45 46 /** 40 47 * The data collector. 41 48 * … … 49 56 public function __construct() { 50 57 $this->data_collector = new Last_Published_Post_Data_Collector(); 51 $this->url = \admin_url( 'post-new.php?post_type=post' );58 $this->url = 'https://prpl.fyi/valuable-content'; 52 59 } 53 60 … … 58 65 */ 59 66 public function get_title() { 60 return esc_html__( 'Create a post', 'progress-planner' );67 return esc_html__( 'Create valuable content', 'progress-planner' ); 61 68 } 62 69 … … 67 74 */ 68 75 public function get_description() { 69 return esc_html__( 'Create a new, relevant post. If you write an in-depth post you may earn an extra point.', 'progress-planner' ); 76 return sprintf( 77 /* translators: %s: "Read more" link. */ 78 \esc_html__( 'Time to add more valuable content to your site! Check our blog for inspiration. %s.', 'progress-planner' ), 79 '<a href="https://prpl.fyi/valuable-content" target="_blank">' . \esc_html__( 'Read more', 'progress-planner' ) . '</a>' 80 ); 70 81 } 71 82 … … 77 88 * @return array 78 89 */ 79 public function modify_ task_data( $task_data ) {90 public function modify_evaluated_task_data( $task_data ) { 80 91 $last_published_post_data = $this->data_collector->collect(); 81 92 … … 86 97 // Add the post ID and post length to the task data. 87 98 $task_data['post_id'] = $last_published_post_data['post_id']; 88 $task_data['long'] = $last_published_post_data['long'];89 99 90 100 return $task_data; … … 133 143 'dismissable' => $this->is_dismissable(), 134 144 'url' => $this->get_url(), 145 'url_target' => $this->get_url_target(), 135 146 'description' => $this->get_description(), 136 147 ]; … … 147 158 * @return int 148 159 */ 149 public function get_points _for_task( $task_id = '' ) {160 public function get_points( $task_id = '' ) { 150 161 151 162 if ( ! $task_id ) { 152 // Get the post that was created last.153 $post_data = $this->data_collector->collect();154 } else {155 $post_data = \progress_planner()->get_suggested_tasks()->get_tasks_by( 'task_id', $task_id );156 $post_data = $post_data[0] ?? false;157 }158 159 // Post was created, but then deleted?160 if ( ! $post_data || empty( $post_data['post_id'] ) ) {161 163 return $this->points; 162 164 } 163 165 164 return true === $post_data['long'] ? 2 : 1; 166 $post_data = \progress_planner()->get_suggested_tasks()->get_tasks_by( 'task_id', $task_id ); 167 $post_data = $post_data[0] ?? false; 168 169 // Backwards compatibility. 170 if ( $post_data && isset( $post_data['long'] ) ) { 171 return true === $post_data['long'] ? 2 : 1; 172 } 173 174 return $this->points; 165 175 } 166 176 } -
progress-planner/trunk/classes/suggested-tasks/local-tasks/providers/repetitive/class-review.php
r3268602 r3283338 73 73 74 74 /** 75 * The include post types. 76 * 77 * @var string[] 78 */ 79 protected $include_post_types = []; 80 81 /** 75 82 * Initialize the task provider. 76 83 * … … 78 85 */ 79 86 public function init() { 87 $this->include_post_types = \progress_planner()->get_settings()->get_post_types_names(); // Wait for the post types to be initialized. 88 80 89 \add_filter( 'progress_planner_update_posts_tasks_args', [ $this, 'filter_update_posts_args' ] ); 81 90 } … … 179 188 $last_updated_posts = $this->get_old_posts( 180 189 [ 181 'post__in' => $important_page_ids, 190 'post__in' => $important_page_ids, 191 'post_type' => 'any', 182 192 ] 183 193 ); … … 212 222 213 223 $this->task_post_mappings[ $task_id ] = [ 214 'task_id' => $task_id, 215 'post_id' => $post->ID, 224 'task_id' => $task_id, 225 'post_id' => $post->ID, 226 'post_type' => $post->post_type, 216 227 ]; 217 228 } … … 247 258 'category' => $this->get_provider_category(), 248 259 'post_id' => $task_data['post_id'], 260 'post_type' => $task_data['post_type'], 249 261 'date' => \gmdate( 'YW' ), 250 262 ]; … … 278 290 'dismissable' => $this->is_dismissable(), 279 291 'url' => $this->get_url( $task_id ), 292 'url_target' => $this->get_url_target(), 280 293 'description' => $this->get_description( $task_id ), 281 294 ]; … … 322 335 */ 323 336 public function get_old_posts( $args = [] ) { 324 $args = wp_parse_args( 325 $args, 326 [ 327 'posts_per_page' => static::ITEMS_TO_INJECT, 328 'post_type' => [ 'page', 'post' ], 329 'post_status' => 'publish', 330 'orderby' => 'modified', 331 'order' => 'ASC', 332 'date_query' => [ 333 [ 334 'column' => 'post_modified', 335 'before' => '-6 months', 337 $posts = []; 338 339 if ( ! empty( $this->include_post_types ) ) { 340 $args = wp_parse_args( 341 $args, 342 [ 343 'posts_per_page' => static::ITEMS_TO_INJECT, 344 'post_type' => $this->include_post_types, 345 'post_status' => 'publish', 346 'orderby' => 'modified', 347 'order' => 'ASC', 348 'date_query' => [ 349 [ 350 'column' => 'post_modified', 351 'before' => '-6 months', 352 ], 336 353 ], 337 ], 338 ] 339 ); 340 341 /** 342 * Filters the args for the posts & pages we want user to review. 343 * 344 * @param array $args The get_postsargs. 345 */ 346 $args = apply_filters( 'progress_planner_update_posts_tasks_args', $args ); 347 348 // Get the post that was updated last. 349 $posts = \get_posts( $args ); 354 ] 355 ); 356 357 /** 358 * Filters the args for the posts & pages we want user to review. 359 * 360 * @param array $args The get_postsargs. 361 */ 362 $args = apply_filters( 'progress_planner_update_posts_tasks_args', $args ); 363 364 // Get the post that was updated last. 365 $posts = \get_posts( $args ); 366 } 367 368 // Get the pages saved in the settings that have not been updated in the last 6 months. 369 $saved_page_type_ids = $this->get_saved_page_types(); 370 371 if ( ! empty( $saved_page_type_ids ) ) { 372 $pages_to_update = \get_posts( 373 [ 374 'post_type' => 'any', 375 'post_status' => 'publish', 376 'orderby' => 'modified', 377 'order' => 'ASC', 378 'ignore_sticky_posts' => true, 379 'date_query' => [ 380 [ 381 'column' => 'post_modified', 382 'before' => '-6 months', 383 ], 384 ], 385 'post__in' => $saved_page_type_ids, 386 ] 387 ); 388 389 // Merge the posts & pages to update. Put the pages first. 390 $posts = array_merge( $pages_to_update, $posts ); 391 } 350 392 351 393 return $posts ? $posts : []; … … 398 440 399 441 /** 442 * Get the saved page-types. 443 * 444 * @return int[] 445 */ 446 protected function get_saved_page_types() { 447 $ids = []; 448 // Add the saved page-types to the post__not_in array. 449 $page_types = \progress_planner()->get_admin__page_settings()->get_settings(); 450 foreach ( $page_types as $page_type ) { 451 if ( isset( $page_type['value'] ) && 0 !== (int) $page_type['value'] ) { 452 $ids[] = (int) $page_type['value']; 453 } 454 } 455 return $ids; 456 } 457 458 /** 400 459 * Check if a specific task is completed. 401 460 * -
progress-planner/trunk/playwright.config.js
r3264985 r3283338 4 4 testDir: './tests/e2e', 5 5 timeout: 30000, 6 fullyParallel: false,7 6 forbidOnly: !! process.env.CI, 8 7 retries: process.env.CI ? 2 : 0, 9 workers: 1,10 8 reporter: 'html', 11 globalSetup: require.resolve( './tests/e2e/auth.setup.js' ), 9 globalSetup: './tests/e2e/auth.setup.js', 10 globalTeardown: './tests/e2e/auth.setup.js', 12 11 use: { 13 12 baseURL: process.env.WORDPRESS_URL || 'http://localhost:8080', … … 18 17 projects: [ 19 18 { 20 name: ' chromium',19 name: 'sequential', 21 20 use: { ...devices[ 'Desktop Chrome' ] }, 21 testMatch: 'sequential.spec.js', 22 fullyParallel: false, 23 workers: 1, 24 }, 25 { 26 name: 'parallel', 27 use: { ...devices[ 'Desktop Chrome' ] }, 28 testIgnore: [ 29 'onboarding.spec.js', 30 'task-tagline.spec.js', 31 'todo.spec.js', 32 'todo-reorder.spec.js', 33 'todo-complete.spec.js', 34 'sequential.spec.js', 35 ], 36 fullyParallel: true, 37 workers: 4, 22 38 }, 23 39 ], -
progress-planner/trunk/progress-planner.php
r3268602 r3283338 10 10 * Requires at least: 6.3 11 11 * Requires PHP: 7.4 12 * Version: 1. 2.012 * Version: 1.3.0 13 13 * Author: Team Emilia Projects 14 14 * Author URI: https://prpl.fyi/about … … 67 67 'Progress_Planner\Onboard' => [ 'Progress_Planner\Utils\Onboard', '1.1.1' ], 68 68 'Progress_Planner\Playground' => [ 'Progress_Planner\Utils\Playground', '1.1.1' ], 69 70 'Progress_Planner\Admin\Widgets\Published_Content' => [ 'Progress_Planner\Admin\Widgets\Content_Activity', '1.3.0' ], 69 71 ]; 70 72 -
progress-planner/trunk/readme.txt
r3268602 r3283338 5 5 Tested up to: 6.8 6 6 Requires PHP: 7.4 7 Stable tag: 1. 2.07 Stable tag: 1.3.0 8 8 License: GPL3+ 9 9 License URI: https://www.gnu.org/licenses/gpl-3.0.en.html … … 110 110 111 111 == Changelog == 112 113 = 1.3.0 = 114 115 Enhancements: 116 117 * Improved checks when adding Ravi icon to the Yoast SEO settings page. 118 * Add "golden" tasks to weekly emails. 119 * Add text to clarify when the user has completed all tasks. 120 * Improve the content widget & stats to show more accurate data. It now shows content _activity_ instead of content _published_. 121 * Implemented "valuable post-types" and added settings for them. 122 * Changed the "create a post" task to "create valuable content". 123 * Renamed & migrated content badges. 124 * Added a link to the 'Create valuable content' task description. 125 * Improve accessibility of Recommendations (and other links) linking to external resources 126 127 Bugs we fixed: 128 129 * Fixed error during plugin uninstall. 130 * Archive_Format data collector hooks weren't registered early enough. 131 * Ensure fresh plugin list by clearing plugin cache before checking for inactive plugins after deletion. 132 * Clear plugin cache when checking for inactive plugins. 133 * Delete no-longer relevant pending tasks. 134 * Fixed timing issue for tasks added by 3rd-party plugins. 112 135 113 136 = 1.2.0 = -
progress-planner/trunk/uninstall.php
r3268602 r3283338 14 14 15 15 require_once __DIR__ . '/classes/class-settings.php'; 16 require_once __DIR__ . '/classes/ class-query.php';16 require_once __DIR__ . '/classes/activities/class-query.php'; 17 17 18 18 /** -
progress-planner/trunk/views/admin-page-header.php
r3268602 r3283338 35 35 </button> 36 36 <?php 37 // Render the settings button.38 \progress_planner()->get_ui__popover()->the_popover( 'settings' )->render_button(39 '',40 \progress_planner()->get_asset( 'images/icon_settings.svg' ) . '<span class="screen-reader-text">' . \esc_html__( 'Settings', 'progress-planner' ) . '</span>'41 );42 // Render the settings popover.43 \progress_planner()->get_ui__popover()->the_popover( 'settings' )->render();44 37 45 38 // Render the subscribe form button and popover if the license key is not set. -
progress-planner/trunk/views/admin-page-settings.php
r3264985 r3283338 35 35 <form id="prpl-settings"> 36 36 <?php \progress_planner()->the_view( 'page-settings/pages.php' ); ?> 37 <?php \progress_planner()->the_view( 'page-settings/settings.php' ); ?> 38 <?php \progress_planner()->the_view( 'page-settings/license.php' ); ?> 37 38 <div id="prpl-grid-column-wrapper"> 39 <?php \progress_planner()->the_view( 'page-settings/post-types.php' ); ?> 40 <?php \progress_planner()->the_view( 'page-settings/settings.php' ); ?> 41 <?php \progress_planner()->the_view( 'page-settings/license.php' ); ?> 42 </div> 39 43 40 44 <?php wp_nonce_field( 'progress_planner' ); ?> -
progress-planner/trunk/views/page-settings/license.php
r3238385 r3283338 16 16 ?> 17 17 18 <div class="prpl-column ">18 <div class="prpl-column prpl-column-license"> 19 19 <div class="prpl-widget-wrapper"> 20 20 <h2 class="prpl-settings-section-title prpl-settings-section-license"> -
progress-planner/trunk/views/page-settings/pages.php
r3238385 r3283338 12 12 ?> 13 13 14 <div class="prpl-column ">14 <div class="prpl-column prpl-column-pages"> 15 15 <div class="prpl-widget-wrapper"> 16 16 <h2 class="prpl-settings-section-title"> -
progress-planner/trunk/views/page-settings/settings.php
r3264985 r3283338 14 14 ?> 15 15 16 <div class="prpl-column ">16 <div class="prpl-column prpl-column-login-destination"> 17 17 <div class="prpl-widget-wrapper"> 18 18 <h2 class="prpl-settings-section-title"> 19 19 <span class="icon"> 20 <?php \progress_planner()->the_asset( 'images/icon_ forward.svg' ); ?>20 <?php \progress_planner()->the_asset( 'images/icon_user.svg' ); ?> 21 21 </span> 22 22 <span> -
progress-planner/trunk/views/page-widgets/suggested-tasks.php
r3268602 r3283338 27 27 <ul style="display:none"></ul> 28 28 <ul class="prpl-suggested-tasks-list"></ul> 29 <p class="prpl-no-suggested-tasks"> 30 <?php \esc_html_e( 'You have completed all recommended tasks.', 'progress-planner' ); ?> 31 <br> 32 <?php \esc_html_e( 'Check back later for new tasks!', 'progress-planner' ); ?> 33 </p> 29 34 <hr> 30 35 </div> -
progress-planner/trunk/views/page-widgets/whats-new.php
r3268602 r3283338 25 25 ?> 26 26 <li> 27 <a href="<?php echo \esc_url( $prpl_blog_post['link'] ); ?>" target="_blank"> 28 <h3><?php echo \esc_html( $prpl_blog_post['title']['rendered'] ); ?></h3> 29 <?php if ( $prpl_blog_post_image_url ) : ?> 27 <h3> 28 <a href="<?php echo \esc_url( $prpl_blog_post['link'] ); ?>" target="_blank"> 29 <?php echo \esc_html( $prpl_blog_post['title']['rendered'] ); ?> 30 </a> 31 </h3> 32 <?php if ( $prpl_blog_post_image_url ) : ?> 33 <a href="<?php echo \esc_url( $prpl_blog_post['link'] ); ?>" target="_blank"> 30 34 <div class="prpl-blog-post-image" style="background-image:url(<?php echo \esc_url( $prpl_blog_post_image_url ); ?>)"></div> 31 < ?php endif; ?>32 < /a>35 </a> 36 <?php endif; ?> 33 37 <p><?php echo \esc_html( \wp_trim_words( \wp_strip_all_tags( $prpl_blog_post['content']['rendered'] ), 55 ) ); ?></p> 34 38 <hr /> -
progress-planner/trunk/views/setting/page-select.php
r3268602 r3283338 95 95 </div> 96 96 <?php endif; ?> 97 98 99 97 </div> 100 98 <?php endforeach; ?>
Note: See TracChangeset
for help on using the changeset viewer.