Changeset 3238385
- Timestamp:
- 02/11/2025 08:09:10 AM (12 months ago)
- Location:
- progress-planner
- Files:
-
- 30 added
- 4 deleted
- 74 edited
- 1 copied
-
tags/1.0.4 (copied) (copied from progress-planner/trunk)
-
tags/1.0.4/CHANGELOG.md (modified) (4 diffs)
-
tags/1.0.4/assets/css/page-widgets/suggested-tasks.css (modified) (1 diff)
-
tags/1.0.4/assets/css/settings-page.css (modified) (1 diff)
-
tags/1.0.4/assets/images/icon_key.svg (added)
-
tags/1.0.4/assets/js/web-components/prpl-badge.js (modified) (1 diff)
-
tags/1.0.4/assets/js/web-components/prpl-suggested-task.js (modified) (8 diffs)
-
tags/1.0.4/assets/js/widgets/suggested-tasks.js (modified) (8 diffs)
-
tags/1.0.4/classes/activities/class-suggested-task.php (modified) (1 diff)
-
tags/1.0.4/classes/admin/class-page-settings.php (modified) (4 diffs)
-
tags/1.0.4/classes/admin/class-page.php (modified) (6 diffs)
-
tags/1.0.4/classes/badges/class-monthly.php (modified) (1 diff)
-
tags/1.0.4/classes/class-base.php (modified) (3 diffs)
-
tags/1.0.4/classes/class-playground.php (modified) (2 diffs)
-
tags/1.0.4/classes/class-plugin-deactivation.php (added)
-
tags/1.0.4/classes/class-rest-api-stats.php (modified) (2 diffs)
-
tags/1.0.4/classes/class-suggested-tasks.php (modified) (6 diffs)
-
tags/1.0.4/classes/class-widget.php (deleted)
-
tags/1.0.4/classes/suggested-tasks/class-local-tasks-manager.php (modified) (6 diffs)
-
tags/1.0.4/classes/suggested-tasks/class-remote-tasks.php (modified) (1 diff)
-
tags/1.0.4/classes/suggested-tasks/local-tasks/class-local-task-factory.php (modified) (2 diffs)
-
tags/1.0.4/classes/suggested-tasks/local-tasks/class-task-local.php (modified) (1 diff)
-
tags/1.0.4/classes/suggested-tasks/local-tasks/providers/class-content-abstract.php (modified) (1 diff)
-
tags/1.0.4/classes/suggested-tasks/local-tasks/providers/class-content-create.php (modified) (5 diffs)
-
tags/1.0.4/classes/suggested-tasks/local-tasks/providers/class-content-review.php (added)
-
tags/1.0.4/classes/suggested-tasks/local-tasks/providers/class-content-update.php (deleted)
-
tags/1.0.4/classes/suggested-tasks/local-tasks/providers/class-core-blogdescription.php (added)
-
tags/1.0.4/classes/suggested-tasks/local-tasks/providers/class-core-siteicon.php (added)
-
tags/1.0.4/classes/suggested-tasks/local-tasks/providers/class-core-update.php (modified) (7 diffs)
-
tags/1.0.4/classes/suggested-tasks/local-tasks/providers/class-debug-display.php (added)
-
tags/1.0.4/classes/suggested-tasks/local-tasks/providers/class-hello-world.php (added)
-
tags/1.0.4/classes/suggested-tasks/local-tasks/providers/class-local-onetime-tasks-abstract.php (added)
-
tags/1.0.4/classes/suggested-tasks/local-tasks/providers/class-local-repetitive-tasks-abstract.php (added)
-
tags/1.0.4/classes/suggested-tasks/local-tasks/providers/class-local-tasks-abstract.php (modified) (4 diffs)
-
tags/1.0.4/classes/suggested-tasks/local-tasks/providers/class-local-tasks-interface.php (modified) (1 diff)
-
tags/1.0.4/classes/suggested-tasks/local-tasks/providers/class-sample-page.php (added)
-
tags/1.0.4/classes/suggested-tasks/local-tasks/providers/class-settings-saved.php (modified) (4 diffs)
-
tags/1.0.4/classes/widgets/class-activity-scores.php (modified) (1 diff)
-
tags/1.0.4/classes/widgets/class-badge-streak.php (modified) (1 diff)
-
tags/1.0.4/classes/widgets/class-challenge.php (modified) (1 diff)
-
tags/1.0.4/classes/widgets/class-latest-badge.php (modified) (1 diff)
-
tags/1.0.4/classes/widgets/class-published-content.php (modified) (1 diff)
-
tags/1.0.4/classes/widgets/class-suggested-tasks.php (modified) (10 diffs)
-
tags/1.0.4/classes/widgets/class-todo.php (modified) (1 diff)
-
tags/1.0.4/classes/widgets/class-whats-new.php (modified) (1 diff)
-
tags/1.0.4/classes/widgets/class-widget.php (added)
-
tags/1.0.4/progress-planner.php (modified) (1 diff)
-
tags/1.0.4/readme.txt (modified) (2 diffs)
-
tags/1.0.4/views/admin-page-settings.php (modified) (1 diff)
-
tags/1.0.4/views/page-settings (added)
-
tags/1.0.4/views/page-settings/license.php (added)
-
tags/1.0.4/views/page-settings/pages.php (added)
-
tags/1.0.4/views/page-settings/settings.php (added)
-
tags/1.0.4/views/page-widgets/parts/monthly-badges.php (modified) (1 diff)
-
tags/1.0.4/views/page-widgets/suggested-tasks.php (modified) (4 diffs)
-
trunk/CHANGELOG.md (modified) (4 diffs)
-
trunk/assets/css/page-widgets/suggested-tasks.css (modified) (1 diff)
-
trunk/assets/css/settings-page.css (modified) (1 diff)
-
trunk/assets/images/icon_key.svg (added)
-
trunk/assets/js/web-components/prpl-badge.js (modified) (1 diff)
-
trunk/assets/js/web-components/prpl-suggested-task.js (modified) (8 diffs)
-
trunk/assets/js/widgets/suggested-tasks.js (modified) (8 diffs)
-
trunk/classes/activities/class-suggested-task.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/badges/class-monthly.php (modified) (1 diff)
-
trunk/classes/class-base.php (modified) (3 diffs)
-
trunk/classes/class-playground.php (modified) (2 diffs)
-
trunk/classes/class-plugin-deactivation.php (added)
-
trunk/classes/class-rest-api-stats.php (modified) (2 diffs)
-
trunk/classes/class-suggested-tasks.php (modified) (6 diffs)
-
trunk/classes/class-widget.php (deleted)
-
trunk/classes/suggested-tasks/class-local-tasks-manager.php (modified) (6 diffs)
-
trunk/classes/suggested-tasks/class-remote-tasks.php (modified) (1 diff)
-
trunk/classes/suggested-tasks/local-tasks/class-local-task-factory.php (modified) (2 diffs)
-
trunk/classes/suggested-tasks/local-tasks/class-task-local.php (modified) (1 diff)
-
trunk/classes/suggested-tasks/local-tasks/providers/class-content-abstract.php (modified) (1 diff)
-
trunk/classes/suggested-tasks/local-tasks/providers/class-content-create.php (modified) (5 diffs)
-
trunk/classes/suggested-tasks/local-tasks/providers/class-content-review.php (added)
-
trunk/classes/suggested-tasks/local-tasks/providers/class-content-update.php (deleted)
-
trunk/classes/suggested-tasks/local-tasks/providers/class-core-blogdescription.php (added)
-
trunk/classes/suggested-tasks/local-tasks/providers/class-core-siteicon.php (added)
-
trunk/classes/suggested-tasks/local-tasks/providers/class-core-update.php (modified) (7 diffs)
-
trunk/classes/suggested-tasks/local-tasks/providers/class-debug-display.php (added)
-
trunk/classes/suggested-tasks/local-tasks/providers/class-hello-world.php (added)
-
trunk/classes/suggested-tasks/local-tasks/providers/class-local-onetime-tasks-abstract.php (added)
-
trunk/classes/suggested-tasks/local-tasks/providers/class-local-repetitive-tasks-abstract.php (added)
-
trunk/classes/suggested-tasks/local-tasks/providers/class-local-tasks-abstract.php (modified) (4 diffs)
-
trunk/classes/suggested-tasks/local-tasks/providers/class-local-tasks-interface.php (modified) (1 diff)
-
trunk/classes/suggested-tasks/local-tasks/providers/class-sample-page.php (added)
-
trunk/classes/suggested-tasks/local-tasks/providers/class-settings-saved.php (modified) (4 diffs)
-
trunk/classes/widgets/class-activity-scores.php (modified) (1 diff)
-
trunk/classes/widgets/class-badge-streak.php (modified) (1 diff)
-
trunk/classes/widgets/class-challenge.php (modified) (1 diff)
-
trunk/classes/widgets/class-latest-badge.php (modified) (1 diff)
-
trunk/classes/widgets/class-published-content.php (modified) (1 diff)
-
trunk/classes/widgets/class-suggested-tasks.php (modified) (10 diffs)
-
trunk/classes/widgets/class-todo.php (modified) (1 diff)
-
trunk/classes/widgets/class-whats-new.php (modified) (1 diff)
-
trunk/classes/widgets/class-widget.php (added)
-
trunk/progress-planner.php (modified) (1 diff)
-
trunk/readme.txt (modified) (2 diffs)
-
trunk/views/admin-page-settings.php (modified) (1 diff)
-
trunk/views/page-settings (added)
-
trunk/views/page-settings/license.php (added)
-
trunk/views/page-settings/pages.php (added)
-
trunk/views/page-settings/settings.php (added)
-
trunk/views/page-widgets/parts/monthly-badges.php (modified) (1 diff)
-
trunk/views/page-widgets/suggested-tasks.php (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
progress-planner/tags/1.0.4/CHANGELOG.md
r3217671 r3238385 1 = 1.0.4 = 2 3 Enhancements: 4 5 * We've moved Ravi's recommendations to the top left of your Progress Planner dashboard. They're the most important thing on there, so we wanted to give it prime placement. 6 * We changed "Update post" to "Review post" / "Review page" and [wrote better instructions for reviewing old posts and pages](https://progressplanner.com/recommendations/review-post/). These tasks now prioritize the most important pages, like your About page, Privacy policy, Contact page and FAQ page. 7 * Added an option to redirect users to the Progress Planner dashboard after login. The WordPress dashboard isn't particularly useful in our eyes, this mind entice you to action more. 8 * Added a plugin-deactivation feedback form (we tell you, because you'll never see it, right? :) ). 9 * Removed the celebration for "Perform all updates" if it was done by WordPress's automatic update. We all love confetti, but when it comes all the time without you doing anything, it loses its value, right? Hence this fix. 10 11 We've added the following Recommendations from Ravi: 12 13 * [Setting site icon](https://progressplanner.com/recommendations/set-a-site-icon-aka-favicon/). 14 * [Setting the tagline](https://progressplanner.com/recommendations/set-tagline/). 15 * [Deactivating the display of PHP debug messages](https://progressplanner.com/recommendations/set-wp-debug/). 16 * [Removing the default WP "Hello world" post](https://progressplanner.com/recommendations/delete-the-default-wordpress-hello-world-post/). 17 * [Removing the default WP "Sample page" page](https://progressplanner.com/recommendations/delete-the-default-wordpress-sample-page-post/). 18 19 Under the hood: 20 21 * Improvements to the REST-API endpoint for getting stats. 22 * Removed admin notices on the Progress Planner page. 23 24 = 1.0.3 = 25 26 Fixed: 27 28 * Detection of page-types in the settings page. 29 * Properly resetting caches for monthly badges. 30 31 Enhancements: 32 33 * Added a new "Challenges" widget to the dashboard. 34 1 35 = 1.0.2 = 2 36 … … 22 56 * Assets versioning. 23 57 * Duplicate update-core tasks. 24 * Update old post task being celebrated as completed when post is trashed.25 58 * Information icon for 'Create a long post' task was showing text of 'create a short post' task. 26 59 * Numerous other minor bugfixes. … … 48 81 = 0.9.5 = 49 82 83 Enhancements: 84 85 * Added functionality to make it easier to demo the plugin on the WordPress playground. 86 * Improved the onboarding and added a tour of the plugin. 87 50 88 Fixed: 51 89 … … 68 106 Security: 69 107 70 * Stricter sanitization & escaping of data in to-do items. Props to [justakazh](https://github.com/justakazh) for reporting through our [PatchStack Vulnerability Disclosure Program](https://patchstack.com/database/vdp/progress-planner).108 * Stricter sanitization & escaping of data in to-do items. Props to [justakazh](https://github.com/justakazh) for reporting through our [PatchStack Vulnerability Disclosure Program](https://patchstack.com/database/vdp/progress-planner). 71 109 * Restrict access to the plugin's dashboard widgets to users with the `publish_posts` capability. 72 110 -
progress-planner/tags/1.0.4/assets/css/page-widgets/suggested-tasks.css
r3210975 r3238385 100 100 101 101 hr { 102 display: initial;102 display: block; 103 103 } 104 104 } -
progress-planner/tags/1.0.4/assets/css/settings-page.css
r3226821 r3238385 69 69 width: 1.25em; 70 70 height: 1.25em; 71 72 svg path {73 fill: currentcolor;74 }75 71 } 76 72 } -
progress-planner/tags/1.0.4/assets/js/web-components/prpl-badge.js
r3217671 r3238385 12 12 complete = 13 13 true === complete && 'true' === this.getAttribute( 'complete' ); 14 15 badgeId = badgeId || this.getAttribute( 'badge-id' ); 14 16 this.innerHTML = ` 15 17 <img 16 18 src="${ 17 19 progressPlannerBadge.remoteServerRootUrl 18 }/wp-json/progress-planner-saas/v1/badge-svg/?badge_id=${ 19 badgeId || this.getAttribute( 'badge-id' ) 20 }" 20 }/wp-json/progress-planner-saas/v1/badge-svg/?badge_id=${ badgeId }" 21 21 alt="Badge" 22 22 ${ false === complete ? 'style="filter: grayscale(1);opacity: 0.25;"' : '' } -
progress-planner/tags/1.0.4/assets/js/web-components/prpl-suggested-task.js
r3210975 r3238385 13 13 taskPoints, 14 14 taskAction = '', 15 taskUrl = '' 15 taskUrl = '', 16 taskDismissable = false, 17 taskType = '' 16 18 ) { 17 19 // Get parent class properties … … 26 28 27 29 const isRemoteTask = taskId.startsWith( 'remote-task-' ); 30 const isDismissable = taskDismissable || isRemoteTask; 28 31 29 32 const actionButtons = { … … 52 55 <span class="screen-reader-text">${ progressPlannerSuggestedTask.i18n.snooze }</span> 53 56 </button>`, 54 complete: is RemoteTask57 complete: isDismissable 55 58 ? `<button 56 59 type="button" … … 69 72 70 73 this.innerHTML = ` 71 <li class="prpl-suggested-task" data-task-id="${ taskId }" data-task-action="${ taskAction }" data-task-url="${ taskUrl }" >74 <li class="prpl-suggested-task" data-task-id="${ taskId }" data-task-action="${ taskAction }" data-task-url="${ taskUrl }" data-task-type="${ taskType }" data-task-points="${ taskPoints }"> 72 75 <h3><span>${ taskHeading }</span></h3> 73 76 <div class="prpl-suggested-task-actions"> … … 278 281 runTaskAction = ( taskId, actionType, snoozeDuration ) => { 279 282 taskId = taskId.toString(); 283 const type = 284 this.querySelector( 'li' ).getAttribute( 'data-task-type' ); 280 285 281 286 const data = { … … 308 313 ) { 309 314 window.progressPlannerSuggestedTasks.tasks.snoozed.push( 310 taskId 315 { 316 id: taskId, 317 } 311 318 ); 312 319 } … … 321 328 el.setAttribute( 'data-task-action', 'celebrate' ); 322 329 330 const event = new CustomEvent( 331 'prplUpdateRaviGaugeEvent', 332 { 333 detail: { 334 pointsDiff: parseInt( 335 this.querySelector( 'li' ).getAttribute( 336 'data-task-points' 337 ) 338 ), 339 }, 340 } 341 ); 342 document.dispatchEvent( event ); 343 323 344 // Trigger the celebration event. 324 345 document.dispatchEvent( … … 329 350 } 330 351 331 const event = new Event( 'prplMaybeInjectSuggestedTaskEvent' ); 352 const event = new CustomEvent( 353 'prplMaybeInjectSuggestedTaskEvent', 354 { 355 detail: { 356 taskId, 357 type, 358 }, 359 } 360 ); 332 361 document.dispatchEvent( event ); 333 362 } ); -
progress-planner/tags/1.0.4/assets/js/widgets/suggested-tasks.js
r3217671 r3238385 1 /* global customElements, progressPlannerSuggestedTasks, confetti, prplDocumentReady , progressPlannerSuggestedTask*/1 /* global customElements, progressPlannerSuggestedTasks, confetti, prplDocumentReady */ 2 2 3 3 /** 4 4 * Count the number of items in the list. 5 5 * 6 * @param {string} type The type of items to count. 6 7 * @return {number} The number of items in the list. 7 8 */ 8 const progressPlannerCountItems = () => { 9 const items = document.querySelectorAll( '.prpl-suggested-task' ); 9 const progressPlannerCountItems = ( type ) => { 10 // We want to display all pending celebration tasks on page load. 11 if ( 'pending_celebration' === type ) { 12 return 0; 13 } 14 15 const items = document.querySelectorAll( 16 `.prpl-suggested-task[data-task-type="${ type }"]` 17 ); 10 18 return items.length; 11 19 }; … … 14 22 * Get the next item to inject. 15 23 * 24 * @param {string} type The type of items to get the next item from. 16 25 * @return {Object} The next item to inject. 17 26 */ 18 const progressPlannerGetNextItem = () => { 27 const progressPlannerGetNextItemFromType = ( type ) => { 28 // If the are no items of this type, return null. 29 if ( 30 'undefined' === 31 typeof progressPlannerSuggestedTasks.tasks.details[ type ] 32 ) { 33 return null; 34 } 35 19 36 // Remove completed and snoozed items. 20 37 const tasks = progressPlannerSuggestedTasks.tasks; 21 const items = tasks.details ;38 const items = tasks.details[ type ]; 22 39 const completed = tasks.completed; 23 40 const snoozed = tasks.snoozed; … … 71 88 /** 72 89 * Inject the next item. 90 * @param {string} type The type of items to inject the next item from. 73 91 */ 74 const progressPlannerInjectNextItem = ( ) => {75 const nextItem = progressPlannerGetNextItem ();92 const progressPlannerInjectNextItem = ( type ) => { 93 const nextItem = progressPlannerGetNextItemFromType( type ); 76 94 if ( ! nextItem ) { 77 95 return; … … 95 113 details.points ?? 1, 96 114 details.action ?? '', 97 details.url ?? '' 115 details.url ?? '', 116 details.dismissable ?? false, 117 details.type ?? '' 98 118 ); 99 119 … … 155 175 156 176 const progressPlannerRenderAttemptshoot = () => { 157 confetti( { 158 ...prplConfettiDefaults, 159 particleCount: 40, 160 scalar: 1.2, 161 shapes: [ 'star' ], 162 } ); 163 164 confetti( { 165 ...prplConfettiDefaults, 166 particleCount: 10, 167 scalar: 0.75, 168 shapes: [ 'circle' ], 169 } ); 177 let confettiOptions = [ 178 { 179 particleCount: 40, 180 scalar: 1.2, 181 shapes: [ 'star' ], 182 }, 183 { 184 particleCount: 10, 185 scalar: 0.75, 186 shapes: [ 'circle' ], 187 }, 188 ]; 189 190 // Tripple check if the confetti options are an array and not undefined. 191 if ( 192 'undefined' !== 193 typeof progressPlannerSuggestedTasks.confettiOptions && 194 true === 195 Array.isArray( 196 progressPlannerSuggestedTasks.confettiOptions 197 ) && 198 progressPlannerSuggestedTasks.confettiOptions.length 199 ) { 200 confettiOptions = progressPlannerSuggestedTasks.confettiOptions; 201 } 202 203 for ( const value of confettiOptions ) { 204 confetti( { 205 ...prplConfettiDefaults, 206 ...value, 207 } ); 208 } 170 209 }; 171 210 … … 192 231 .querySelectorAll( '.prpl-suggested-task-celebrated' ) 193 232 .forEach( ( item ) => { 194 const taskId = item.getAttribute( 'data-task-id' ); 195 196 const request = wp.ajax.post( 197 'progress_planner_suggested_task_action', 233 const taskId = item.getAttribute( 'data-task-id' ), 234 type = item.getAttribute( 'data-task-type' ); 235 const el = document.querySelector( 236 `.prpl-suggested-task[data-task-id="${ taskId }"]` 237 ); 238 239 if ( el ) { 240 el.parentElement.remove(); 241 } 242 243 // Remove the task from the pending celebration. 244 window.progressPlannerSuggestedTasks.tasks.pending_celebration = 245 window.progressPlannerSuggestedTasks.tasks.pending_celebration.filter( 246 ( id ) => id !== taskId 247 ); 248 249 // Add the task to the completed tasks. 250 if ( 251 window.progressPlannerSuggestedTasks.tasks.completed.indexOf( 252 taskId 253 ) === -1 254 ) { 255 window.progressPlannerSuggestedTasks.tasks.completed.push( 256 taskId 257 ); 258 } 259 260 // Refresh the list. 261 const event = new CustomEvent( 262 'prplMaybeInjectSuggestedTaskEvent', 198 263 { 199 task_id: taskId, 200 nonce: progressPlannerSuggestedTask.nonce, 201 action_type: 'celebrated', 264 detail: { 265 taskId, 266 type, 267 }, 202 268 } 203 269 ); 204 request.done( () => { 205 const el = document.querySelector( 206 `.prpl-suggested-task[data-task-id="${ taskId }"]` 207 ); 208 209 if ( el ) { 210 el.parentElement.remove(); 211 } 212 213 // Remove the task from the pending celebration. 214 window.progressPlannerSuggestedTasks.tasks.pending_celebration = 215 window.progressPlannerSuggestedTasks.tasks.pending_celebration.filter( 216 ( id ) => id !== taskId 217 ); 218 219 // Add the task to the completed tasks. 220 if ( 221 window.progressPlannerSuggestedTasks.tasks.completed.indexOf( 222 taskId 223 ) === -1 224 ) { 225 window.progressPlannerSuggestedTasks.tasks.completed.push( 226 taskId 227 ); 228 } 229 230 // Refresh the list. 231 const event = new Event( 232 'prplMaybeInjectSuggestedTaskEvent' 233 ); 234 document.dispatchEvent( event ); 235 } ); 270 document.dispatchEvent( event ); 236 271 } ); 237 272 }, 2000 ); … … 260 295 } 261 296 262 // Inject items, until we reach the maximum number of items. 263 while ( 264 progressPlannerCountItems() < 265 parseInt( progressPlannerSuggestedTasks.maxItems ) && 266 progressPlannerGetNextItem() 267 ) { 268 progressPlannerInjectNextItem(); 269 } 270 271 const event = new Event( 'prplResizeAllGridItemsEvent' ); 297 // Loop through each type and inject items. 298 for ( const type in progressPlannerSuggestedTasks.tasks.details ) { 299 // Inject items, until we reach the maximum number of channel items. 300 while ( 301 progressPlannerCountItems( type ) < 302 parseInt( 303 progressPlannerSuggestedTasks.maxItemsPerType[ type ] 304 ) && 305 progressPlannerGetNextItemFromType( type ) 306 ) { 307 progressPlannerInjectNextItem( type ); 308 } 309 } 310 311 const event = new CustomEvent( 'prplResizeAllGridItemsEvent' ); 272 312 document.dispatchEvent( event ); 273 313 } ); … … 422 462 ); 423 463 464 const prplGetRaviGaugeProps = () => { 465 const gauge = document.getElementById( 'prpl-gauge-ravi' ); 466 if ( ! gauge ) { 467 return; 468 } 469 470 return { 471 id: gauge.id, 472 background: gauge.getAttribute( 'background' ), 473 color: gauge.getAttribute( 'color' ), 474 max: gauge.getAttribute( 'data-max' ), 475 value: gauge.getAttribute( 'data-value' ), 476 badgeId: gauge.getAttribute( 'data-badge-id' ), 477 }; 478 }; 479 480 const prplUpdateRaviGauge = ( pointsDiff = 0 ) => { 481 if ( ! pointsDiff ) { 482 return; 483 } 484 485 const gaugeProps = prplGetRaviGaugeProps(); 486 487 if ( ! gaugeProps ) { 488 return; 489 } 490 491 let newValue = parseInt( gaugeProps.value ) + pointsDiff; 492 newValue = Math.round( newValue ); 493 newValue = Math.max( 0, newValue ); 494 newValue = Math.min( newValue, parseInt( gaugeProps.max ) ); 495 496 const Gauge = customElements.get( 'prpl-gauge' ); 497 const gauge = new Gauge( 498 { 499 max: parseInt( gaugeProps.max ), 500 value: parseFloat( newValue / parseInt( gaugeProps.max ) ), 501 background: gaugeProps.background, 502 color: gaugeProps.color, 503 maxDeg: '180deg', 504 start: '270deg', 505 cutout: '57%', 506 contentFontSize: 'var(--prpl-font-size-6xl)', 507 contentPadding: 508 'var(--prpl-padding) var(--prpl-padding) calc(var(--prpl-padding) * 2) var(--prpl-padding)', 509 marginBottom: 'var(--prpl-padding)', 510 }, 511 `<prpl-badge complete="true" badge-id="${ gaugeProps.badgeId }"></prpl-badge>` 512 ); 513 gauge.id = gaugeProps.id; 514 gauge.setAttribute( 'background', gaugeProps.background ); 515 gauge.setAttribute( 'color', gaugeProps.color ); 516 gauge.setAttribute( 'data-max', gaugeProps.max ); 517 gauge.setAttribute( 'data-value', newValue ); 518 gauge.setAttribute( 'data-badge-id', gaugeProps.badgeId ); 519 520 // Replace the old gauge with the new one. 521 const oldGauge = document.getElementById( gaugeProps.id ); 522 if ( oldGauge ) { 523 oldGauge.replaceWith( gauge ); 524 } 525 526 const oldCounter = document.getElementById( 527 'prpl-widget-content-ravi-points-number' 528 ); 529 if ( oldCounter ) { 530 oldCounter.textContent = newValue + 'pt'; 531 } 532 }; 533 534 // Listen for the event. 535 document.addEventListener( 536 'prplUpdateRaviGaugeEvent', 537 ( e ) => { 538 prplUpdateRaviGauge( e.detail.pointsDiff ); 539 }, 540 false 541 ); 542 424 543 // Listen for the event. 425 544 document.addEventListener( 426 545 'prplMaybeInjectSuggestedTaskEvent', 427 () => { 546 ( e ) => { 547 const type = e.detail.type; 548 549 if ( 'pending_celebration' === type ) { 550 return; 551 } 552 428 553 while ( 429 progressPlannerCountItems() < 430 parseInt( progressPlannerSuggestedTasks.maxItems ) && 431 progressPlannerGetNextItem() 554 progressPlannerCountItems( type ) < 555 parseInt( 556 progressPlannerSuggestedTasks.maxItemsPerType[ type ] 557 ) && 558 progressPlannerGetNextItemFromType( type ) 432 559 ) { 433 progressPlannerInjectNextItem( );560 progressPlannerInjectNextItem( type ); 434 561 } 435 562 -
progress-planner/tags/1.0.4/classes/activities/class-suggested-task.php
r3210975 r3238385 59 59 60 60 $data = \progress_planner()->get_suggested_tasks()->get_local()->get_data_from_task_id( $this->data_id ); 61 if ( isset( $data['type'] ) && ( 'create-post' === $data['type'] || ' update-post' === $data['type'] ) && isset( $data['long'] ) && true === $data['long'] ) {61 if ( isset( $data['type'] ) && ( 'create-post' === $data['type'] || 'review-post' === $data['type'] ) && isset( $data['long'] ) && true === $data['long'] ) { 62 62 $points = 2; 63 63 } -
progress-planner/tags/1.0.4/classes/admin/class-page-settings.php
r3226821 r3238385 160 160 161 161 // Skip if the ID is not set. 162 if ( 1 > (int) $page_args['id'] ) {162 if ( ! isset( $page_args['id'] ) || 1 > (int) $page_args['id'] ) { 163 163 continue; 164 164 } … … 171 171 } 172 172 173 $this->save_settings(); 173 174 $this->save_license(); 174 175 … … 176 177 177 178 \wp_send_json_success( \esc_html__( 'Options stored successfully', 'progress-planner' ) ); 179 } 180 181 /** 182 * Save the settings. 183 * 184 * @return void 185 */ 186 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 189 : false; 190 191 \update_user_meta( \get_current_user_id(), 'prpl_redirect_on_login', (bool) $redirect_on_login ); 178 192 } 179 193 … … 205 219 // Call the custom API. 206 220 $response = \wp_remote_post( 207 'https://progressplanner.com',221 \progress_planner()->get_remote_server_root_url(), 208 222 [ 209 223 'timeout' => 15, -
progress-planner/tags/1.0.4/classes/admin/class-page.php
r3226821 r3238385 29 29 \add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] ); 30 30 \add_action( 'wp_ajax_progress_planner_save_cpt_settings', [ $this, 'save_cpt_settings' ] ); 31 \add_action( 'in_admin_header', [ $this, 'remove_admin_notices' ], PHP_INT_MAX ); 31 32 } 32 33 … … 34 35 * Get the widgets objects 35 36 * 36 * @return array<\Progress_Planner\Widget >37 * @return array<\Progress_Planner\Widgets\Widget> 37 38 */ 38 39 public function get_widgets() { 39 40 $widgets = [ 41 \progress_planner()->get_widgets__suggested_tasks(), 40 42 \progress_planner()->get_widgets__activity_scores(), 41 \progress_planner()->get_widgets__suggested_tasks(),42 43 \progress_planner()->get_widgets__todo(), 43 44 \progress_planner()->get_widgets__challenge(), … … 51 52 * Filter the widgets. 52 53 * 53 * @param array<\Progress_Planner\Widget > $widgets The widgets.54 * @param array<\Progress_Planner\Widgets\Widget> $widgets The widgets. 54 55 * 55 * @return array<\Progress_Planner\Widget >56 * @return array<\Progress_Planner\Widgets\Widget> 56 57 */ 57 58 return \apply_filters( 'progress_planner_admin_widgets', $widgets ); … … 63 64 * @param string $id The widget ID. 64 65 * 65 * @return \Progress_Planner\Widget |void66 * @return \Progress_Planner\Widgets\Widget|void 66 67 */ 67 68 public function get_widget( $id ) { … … 174 175 ); 175 176 } 177 178 $prpl_privacy_policy_accepted = \progress_planner()->is_privacy_policy_accepted(); 179 if ( ! $prpl_privacy_policy_accepted ) { 180 // Enqueue welcome styles. 181 \wp_enqueue_style( 182 'progress-planner-welcome', 183 PROGRESS_PLANNER_URL . '/assets/css/welcome.css', 184 [], 185 \progress_planner()->get_file_version( PROGRESS_PLANNER_DIR . '/assets/css/welcome.css' ) 186 ); 187 188 // Enqueue onboarding styles. 189 \wp_enqueue_style( 190 'progress-planner-onboard', 191 PROGRESS_PLANNER_URL . '/assets/css/onboard.css', 192 [], 193 \progress_planner()->get_file_version( PROGRESS_PLANNER_DIR . '/assets/css/onboard.css' ) 194 ); 195 } 176 196 } 177 197 … … 193 213 ); 194 214 } 215 216 /** 217 * Remove all admin notices when the user is on the Progress Planner page. 218 * 219 * @return void 220 */ 221 public function remove_admin_notices() { 222 $current_screen = \get_current_screen(); 223 if ( ! $current_screen ) { 224 return; 225 } 226 if ( ! \in_array( 227 $current_screen->id, 228 [ 229 'toplevel_page_progress-planner', 230 'progress-planner_page_progress-planner-settings', 231 ], 232 true 233 ) ) { 234 return; 235 } 236 237 \remove_all_actions( 'admin_notices' ); 238 } 195 239 } -
progress-planner/tags/1.0.4/classes/badges/class-monthly.php
r3226821 r3238385 55 55 56 56 $activation_date = \progress_planner()->get_base()->get_activation_date(); 57 $start_date = $activation_date->modify( 'first day of this month' ); 57 if ( $activation_date < new \DateTime( 'first day of November 2024' ) ) { // When badges were introduced. 58 $start_date = $activation_date->modify( 'first day of November 2024' ); 59 } else { 60 $start_date = $activation_date->modify( 'first day of this month' ); 61 } 58 62 59 63 // Year when plugin was released. -
progress-planner/tags/1.0.4/classes/class-base.php
r3226821 r3238385 99 99 $this->cached['badges'] = new Badges(); 100 100 101 // Dont add the widget if the privacy policy is not accepted.102 101 if ( true === $this->is_privacy_policy_accepted() ) { 103 102 $this->cached['settings_page'] = new Admin_Page_Settings(); 104 } 103 104 new Plugin_Deactivation(); 105 } 106 107 /** 108 * Redirect on login. 109 */ 110 \add_action( 'wp_login', [ $this, 'redirect_on_login' ], 10, 2 ); 105 111 } 106 112 … … 156 162 */ 157 163 public function get_placeholder_svg( $width = 1200, $height = 675 ) { 158 return 'data:image/svg+xml;base64,' . base64_encode( sprintf( '<svg width="%1$d" height="%2$d" xmlns="http://www.w3.org/2000/svg"><rect x="2" y="2" width="%3$d" height="%4$d" style="fill:#F6F5FB;stroke:#534786;stroke-width:2"/><text x="50%%" y="50%%" font-size="20" text-anchor="middle" alignment-baseline="middle" font-family="monospace" fill="#534786">progressplanner.com</text></svg>', $width, $height, ( $width - 4 ), ( $height - 4 ) ) ); 164 return 'data:image/svg+xml;base64,' . base64_encode( sprintf( '<svg width="%1$d" height="%2$d" xmlns="http://www.w3.org/2000/svg"><rect x="2" y="2" width="%3$d" height="%4$d" style="fill:#F6F5FB;stroke:#534786;stroke-width:2"/><text x="50%%" y="50%%" font-size="20" text-anchor="middle" alignment-baseline="middle" font-family="monospace" fill="#534786">progressplanner.com</text></svg>', $width, $height, ( $width - 4 ), ( $height - 4 ) ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode 159 165 } 160 166 … … 370 376 && 'valid' === \get_option( 'progress_planner_pro_license_status' ); 371 377 } 378 379 /** 380 * Redirect on login. 381 * 382 * @param string $user_login The user login. 383 * @param \WP_User $user The user object. 384 * 385 * @return void 386 */ 387 public function redirect_on_login( $user_login, $user ) { 388 // Check if the $user can `manage_options`. 389 if ( ! $user->has_cap( 'manage_options' ) ) { 390 return; 391 } 392 393 // Check if the user has the `prpl_redirect_on_login` meta. 394 if ( ! \get_user_meta( $user->ID, 'prpl_redirect_on_login', true ) ) { 395 return; 396 } 397 398 // Redirect to the Progress Planner dashboard. 399 \wp_safe_redirect( \admin_url( 'admin.php?page=progress-planner' ) ); 400 exit; 401 } 372 402 } 373 403 // phpcs:enable Generic.Commenting.Todo -
progress-planner/tags/1.0.4/classes/class-playground.php
r3156816 r3238385 153 153 $this->create_random_post(); 154 154 } 155 for ( $i = 0; $i < 5; $i++ ) { 156 $this->create_random_post( true, 'page' ); 157 } 155 158 // One post for today. 156 159 $this->create_random_post( false ); … … 160 163 * Create a random post. 161 164 * 162 * @param bool $random_date Whether to use a random date or not. 165 * @param bool $random_date Whether to use a random date or not. 166 * @param string $post_type The post type to create. 163 167 * 164 168 * @return int Post ID. 165 169 */ 166 private function create_random_post( $random_date = true ) {170 private function create_random_post( $random_date = true, $post_type = 'post' ) { 167 171 $postarr = [ 168 172 'post_title' => str_replace( '.', '', $this->create_random_string( 5 ) ), 169 173 'post_content' => $this->create_random_string( wp_rand( 200, 500 ) ), 170 174 'post_status' => 'publish', 171 'post_type' => 'post',175 'post_type' => $post_type, 172 176 'post_date' => $this->get_random_date_last_12_months(), 173 177 ]; -
progress-planner/tags/1.0.4/classes/class-rest-api-stats.php
r3217671 r3238385 167 167 $data['todo'] = $pending_todo_items; 168 168 169 $ravis_recommendations = \progress_planner()->get_suggested_tasks()->get_tasks(); 170 $data['recommendations'] = []; 171 foreach ( $ravis_recommendations as $recommendation ) { 172 $data['recommendations'][] = [ 173 'id' => $recommendation['task_id'], 174 'title' => $recommendation['title'], 175 'url' => isset( $recommendation['url'] ) ? $recommendation['url'] : '', 176 ]; 177 } 178 169 179 $data['plugin_url'] = \esc_url( \get_admin_url( null, 'admin.php?page=progress-planner' ) ); 170 180 … … 180 190 */ 181 191 public function validate_token( $token ) { 182 $token = str_replace( 'token/', '', $token ); 192 $token = str_replace( 'token/', '', $token ); 193 if ( \progress_planner()->is_pro_site() && $token === \get_option( 'progress_planner_pro_license_key' ) ) { 194 return true; 195 } 183 196 $license_key = \get_option( 'progress_planner_license_key', false ); 184 197 if ( ! $license_key || 'no-license' === $license_key ) { -
progress-planner/tags/1.0.4/classes/class-suggested-tasks.php
r3210975 r3238385 11 11 use Progress_Planner\Suggested_Tasks\Remote_Tasks; 12 12 use Progress_Planner\Activities\Suggested_Task; 13 use Progress_Planner\Suggested_Tasks\Local_Tasks\Local_Task_Factory; 13 14 14 15 /** … … 52 53 \add_action( 'init', [ $this, 'init' ], 1 ); 53 54 } 55 56 // Add the automatic updates complete action. 57 \add_action( 'automatic_updates_complete', [ $this, 'on_automatic_updates_complete' ] ); 54 58 } 55 59 … … 67 71 68 72 foreach ( $completed_tasks as $task_id ) { 73 // Change the task status to pending celebration. 69 74 $this->mark_task_as_pending_celebration( $task_id ); 70 75 71 76 // Insert an activity. 72 $activity = new Suggested_Task(); 73 $activity->type = 'completed'; 74 $activity->data_id = (string) $task_id; 75 $activity->date = new \DateTime(); 76 $activity->user_id = \get_current_user_id(); 77 $activity->save(); 78 79 // Allow other classes to react to the completion of a suggested task. 80 do_action( 'progress_planner_suggested_task_completed', $task_id ); 77 $this->insert_activity( $task_id ); 78 } 79 } 80 81 /** 82 * Insert an activity. 83 * 84 * @param string $task_id The task ID. 85 * 86 * @return void 87 */ 88 public function insert_activity( $task_id ) { 89 // Insert an activity. 90 $activity = new Suggested_Task(); 91 $activity->type = 'completed'; 92 $activity->data_id = (string) $task_id; 93 $activity->date = new \DateTime(); 94 $activity->user_id = \get_current_user_id(); 95 $activity->save(); 96 97 // Allow other classes to react to the completion of a suggested task. 98 do_action( 'progress_planner_suggested_task_completed', $task_id ); 99 } 100 101 /** 102 * If done via automatic updates, the "core update" task should be marked as "completed" (and skip "pending celebration" status). 103 * 104 * @param array $update_results The update results. 105 * 106 * @return void 107 */ 108 public function on_automatic_updates_complete( $update_results ) { 109 110 $pending_tasks = $this->local->get_pending_tasks(); // @phpstan-ignore-line method.nonObject 111 112 if ( empty( $pending_tasks ) ) { 113 return; 114 } 115 116 // TODO: Get this from task provider. 117 $update_core_task_id = 'update-core'; 118 119 foreach ( $pending_tasks as $task_id ) { 120 $task_object = ( new Local_Task_Factory( $task_id ) )->get_task(); 121 $task_data = $task_object->get_data(); 122 123 if ( $task_data['type'] === $update_core_task_id && \gmdate( 'YW' ) === $task_data['year_week'] ) { 124 // Remove from local (pending tasks). 125 $this->local->remove_pending_task( $task_id ); // @phpstan-ignore-line method.nonObject 126 127 // Change the task status to completed. 128 $this->mark_task_as_completed( $task_id ); 129 130 // Insert an activity. 131 $this->insert_activity( $task_id ); 132 break; 133 } 81 134 } 82 135 } … … 470 523 471 524 /** 525 * Check if a task was completed. 526 * 527 * @param string $task_id The task ID. 528 * 529 * @return bool 530 */ 531 public function was_task_completed( $task_id ) { 532 return true === $this->check_task_condition( 533 [ 534 'type' => 'completed', 535 'task_id' => $task_id, 536 ] 537 ); 538 } 539 540 /** 472 541 * Handle the suggested task action. 473 542 * … … 489 558 switch ( $action ) { 490 559 case 'complete': 560 // It's local task, remove it from pending tasks. 561 if ( false === strpos( $task_id, 'remote-task' ) ) { 562 $this->local->remove_pending_task( $task_id ); // @phpstan-ignore-line method.nonObject 563 } 564 565 // Mark the task as completed. 491 566 $this->mark_task_as( 'completed', $task_id ); 567 568 // Insert an activity. 569 $this->insert_activity( $task_id ); 492 570 $updated = true; 493 571 break; … … 498 576 break; 499 577 500 case 'celebrated':501 // We dont need to do anything here, since the task is already marked as completed.502 $updated = true;503 break;504 505 578 default: 506 579 \wp_send_json_error( [ 'message' => \esc_html__( 'Invalid action.', 'progress-planner' ) ] ); -
progress-planner/tags/1.0.4/classes/suggested-tasks/class-local-tasks-manager.php
r3217671 r3238385 10 10 use Progress_Planner\Suggested_Tasks\Local_Tasks\Local_Task_Factory; 11 11 use Progress_Planner\Suggested_Tasks\Local_Tasks\Providers\Content_Create; 12 use Progress_Planner\Suggested_Tasks\Local_Tasks\Providers\Content_ Update;12 use Progress_Planner\Suggested_Tasks\Local_Tasks\Providers\Content_Review; 13 13 use Progress_Planner\Suggested_Tasks\Local_Tasks\Providers\Core_Update; 14 use Progress_Planner\Suggested_Tasks\Local_Tasks\Providers\Core_Blogdescription; 14 15 use Progress_Planner\Suggested_Tasks\Local_Tasks\Providers\Settings_Saved; 15 16 use Progress_Planner\Suggested_Tasks\Local_Tasks\Providers\Debug_Display; 17 use Progress_Planner\Suggested_Tasks\Local_Tasks\Providers\Sample_Page; 18 use Progress_Planner\Suggested_Tasks\Local_Tasks\Providers\Hello_World; 19 use Progress_Planner\Suggested_Tasks\Local_Tasks\Providers\Core_Siteicon; 16 20 17 21 /** … … 45 49 $this->task_providers = [ 46 50 new Content_Create(), 47 new Content_ Update(),51 new Content_Review(), 48 52 new Core_Update(), 53 new Core_Blogdescription(), 49 54 new Settings_Saved(), 55 new Debug_Display(), 56 new Sample_Page(), 57 new Hello_World(), 58 new Core_Siteicon(), 50 59 ]; 51 60 … … 88 97 * Get a task provider by its type. 89 98 * 90 * @param string $provider_ type The provider type.99 * @param string $provider_id The provider ID. 91 100 * 92 101 * @return \Progress_Planner\Suggested_Tasks\Local_Tasks\Providers\Local_Tasks_Interface|null 93 102 */ 94 public function get_task_provider( $provider_ type) {103 public function get_task_provider( $provider_id ) { 95 104 foreach ( $this->task_providers as $provider_instance ) { 96 if ( $provider_instance->get_provider_ type() === $provider_type) {105 if ( $provider_instance->get_provider_id() === $provider_id ) { 97 106 return $provider_instance; 98 107 } … … 156 165 public function evaluate_task( $task_id ) { 157 166 $task_object = ( new Local_Task_Factory( $task_id ) )->get_task(); 158 $task_provider = $this->get_task_provider( $task_object->get_provider_ type() );167 $task_provider = $this->get_task_provider( $task_object->get_provider_id() ); 159 168 160 169 if ( ! $task_provider ) { … … 174 183 public function get_task_details( $task_id ) { 175 184 $task_object = ( new Local_Task_Factory( $task_id ) )->get_task(); 176 $task_provider = $this->get_task_provider( $task_object->get_provider_ type() );185 $task_provider = $this->get_task_provider( $task_object->get_provider_id() ); 177 186 178 187 if ( ! $task_provider ) { … … 262 271 $task_data = $task_object->get_data(); 263 272 273 // If the task was already completed, remove it. 274 if ( true === \progress_planner()->get_suggested_tasks()->was_task_completed( $task_data['task_id'] ) ) { 275 return false; 276 } 277 264 278 if ( isset( $task_data['year_week'] ) ) { 265 279 return \gmdate( 'YW' ) === $task_data['year_week']; 266 280 } 267 281 282 // We have changed type name, so we need to remove all tasks of the old type. 283 if ( isset( $task_data['type'] ) && 'update-post' === $task_data['type'] ) { 284 return false; 285 } 286 268 287 return true; 269 288 } -
progress-planner/tags/1.0.4/classes/suggested-tasks/class-remote-tasks.php
r3226821 r3238385 41 41 continue; 42 42 } 43 44 // If the task with this id is completed, don't add a task. 45 if ( true === \progress_planner()->get_suggested_tasks()->was_task_completed( "remote-task-{$item['task_id']}" ) ) { 46 continue; 47 } 48 49 // TODO: Maybe skip task which don't have type defined (to not allow wrongly defined 3rd party tasks to override default type). 50 $item['type'] = 'remote-' . ( isset( $item['type'] ) ? $item['type'] : 'default' ); 43 51 $item['task_id'] = "remote-task-{$item['task_id']}"; 44 52 $items[] = $item; -
progress-planner/tags/1.0.4/classes/suggested-tasks/local-tasks/class-local-task-factory.php
r3217671 r3238385 38 38 // Parse simple format, e.g. 'update-core-202449'. 39 39 if ( ! str_contains( $this->task_id, '|' ) ) { 40 40 41 $last_pos = strrpos( $this->task_id, '-' ); 41 if ( false === $last_pos ) { 42 return new Task_Local( [ 'task_id' => $this->task_id ] ); 42 43 // Check if the task ID ends with a '-12345' or not. 44 if ( $last_pos === false || ! preg_match( '/-\d+$/', $this->task_id ) ) { 45 return new Task_Local( 46 [ 47 'task_id' => $this->task_id, 48 'type' => $this->task_id, 49 ] 50 ); 43 51 } 44 52 … … 50 58 return new Task_Local( 51 59 [ 60 'task_id' => $this->task_id, 52 61 'type' => $type, 53 62 $task_suffix_key => $task_suffix, -
progress-planner/tags/1.0.4/classes/suggested-tasks/local-tasks/class-task-local.php
r3210975 r3238385 48 48 /** 49 49 * Alias for get_type(). 50 * For compatibility with the old provider system. 50 51 * 51 52 * @return string 52 53 */ 53 public function get_provider_ type() {54 public function get_provider_id() { 54 55 return $this->get_type(); 55 56 } -
progress-planner/tags/1.0.4/classes/suggested-tasks/local-tasks/providers/class-content-abstract.php
r3210975 r3238385 19 19 */ 20 20 protected $capability = 'edit_others_posts'; 21 22 /** 23 * The provider type. 24 * 25 * @var string 26 */ 27 const TYPE = 'writing'; 21 28 22 29 /** -
progress-planner/tags/1.0.4/classes/suggested-tasks/local-tasks/providers/class-content-create.php
r3210975 r3238385 20 20 * @var string 21 21 */ 22 const TYPE = 'create-post'; 22 const ID = 'create-post'; 23 24 /** 25 * The provider type. 26 * 27 * @var string 28 */ 29 const TYPE = 'content-new'; 23 30 24 31 /** … … 28 35 */ 29 36 const ITEMS_TO_INJECT = 2; 30 31 /**32 * Get the provider ID.33 *34 * @return string35 */36 public function get_provider_type() {37 return self::TYPE;38 }39 37 40 38 /** … … 88 86 89 87 // If the task with this length and id is completed, don't add a task. 90 if ( true === \progress_planner()->get_suggested_tasks()->check_task_condition( 91 [ 92 'type' => 'completed', 93 'task_id' => $task_id, 94 ] 95 ) ) { 88 if ( true === \progress_planner()->get_suggested_tasks()->was_task_completed( $task_id ) ) { 96 89 return []; 97 90 } … … 157 150 * @return array 158 151 */ 159 public function get_task_details( $task_id ) { 152 public function get_task_details( $task_id = '' ) { 153 154 if ( ! $task_id ) { 155 return []; 156 } 160 157 161 158 $data = $this->get_data_from_task_id( $task_id ); … … 168 165 'parent' => 0, 169 166 'priority' => 'medium', 170 'type' => 'writing',167 'type' => $this->get_provider_type(), 171 168 'points' => isset( $data['long'] ) && $data['long'] ? 2 : 1, 172 169 'url' => \esc_url( \admin_url( 'post-new.php?post_type=post' ) ), -
progress-planner/tags/1.0.4/classes/suggested-tasks/local-tasks/providers/class-core-update.php
r3217671 r3238385 12 12 * Add tasks for Core updates. 13 13 */ 14 class Core_Update extends Local_ Tasks_Abstract {14 class Core_Update extends Local_Repetitive_Tasks_Abstract { 15 15 16 16 /** … … 22 22 23 23 /** 24 * The provider type. 25 * 26 * @var string 27 */ 28 const TYPE = 'maintenance'; 29 30 /** 24 31 * The provider ID. 25 32 * 26 33 * @var string 27 34 */ 28 const TYPE = 'update-core'; 29 30 /** 31 * Get the provider ID. 32 * 33 * @return string 34 */ 35 public function get_provider_type() { 36 return self::TYPE; 37 } 35 const ID = 'update-core'; 38 36 39 37 /** … … 44 42 * @return bool|string 45 43 */ 46 public function evaluate_task( $task_id ) {47 44 48 // Early bail if the user does not have the capability to update the core, since \wp_get_update_data()['counts']['total'] will return 0. 49 if ( ! $this->capability_required() ) { 50 return false; 51 } 52 45 /** 46 * Check if the task condition is met. 47 * 48 * @return bool 49 */ 50 public function check_task_condition() { 53 51 // Without this \wp_get_update_data() might not return correct data for the core updates (depending on the timing). 54 52 if ( ! function_exists( 'get_core_updates' ) ) { … … 56 54 } 57 55 58 $task_object = ( new Local_Task_Factory( $task_id ) )->get_task(); 59 $task_data = $task_object->get_data(); 60 61 if ( $task_data['type'] === self::TYPE && \gmdate( 'YW' ) === $task_data['year_week'] && 0 === \wp_get_update_data()['counts']['total'] ) { 62 return $task_id; 63 } 64 return false; 65 } 66 67 /** 68 * Get an array of tasks to inject. 69 * 70 * @return array 71 */ 72 public function get_tasks_to_inject() { 73 74 // Early bail if the user does not have the capability to update the core or if the task is snoozed. 75 if ( true === $this->is_task_type_snoozed() || ! $this->capability_required() ) { 76 return []; 77 } 78 79 // Without this \wp_get_update_data() might not return correct data for the core updates (depending on the timing). 80 if ( ! function_exists( 'get_core_updates' ) ) { 81 require_once ABSPATH . 'wp-admin/includes/update.php'; // @phpstan-ignore requireOnce.fileNotFound 82 } 83 84 // If all updates are performed, do not add the task. 85 if ( 0 === \wp_get_update_data()['counts']['total'] ) { 86 return []; 87 } 88 89 return [ 90 $this->get_task_details( self::TYPE . '-' . \gmdate( 'YW' ) ), 91 ]; 56 return 0 === \wp_get_update_data()['counts']['total'] ? true : false; 92 57 } 93 58 … … 101 66 public function get_task_details( $task_id ) { 102 67 68 if ( ! $task_id ) { 69 $task_id = $this->get_provider_id() . '-' . \gmdate( 'YW' ); 70 } 71 103 72 return [ 104 73 'task_id' => $task_id, … … 106 75 'parent' => 0, 107 76 'priority' => 'high', 108 'type' => 'maintenance',77 'type' => $this->get_provider_type(), 109 78 'points' => 1, 110 79 'url' => $this->capability_required() ? \esc_url( \admin_url( 'update-core.php' ) ) : '', … … 112 81 ]; 113 82 } 114 115 /**116 * Get the data from a task-ID.117 *118 * @param string $task_id The task ID.119 *120 * @return array The data.121 */122 public function get_data_from_task_id( $task_id ) {123 $data = [124 'type' => self::TYPE,125 'id' => $task_id,126 ];127 128 return $data;129 }130 131 /**132 * Check if a task type is snoozed.133 *134 * @return bool135 */136 public function is_task_type_snoozed() {137 $snoozed = \progress_planner()->get_suggested_tasks()->get_snoozed_tasks();138 if ( ! \is_array( $snoozed ) || empty( $snoozed ) ) {139 return false;140 }141 142 foreach ( $snoozed as $task ) {143 $task_object = ( new Local_Task_Factory( $task['id'] ) )->get_task();144 $task_data = $task_object->get_data();145 if ( $task_data['type'] === self::TYPE ) {146 return true;147 }148 }149 150 return false;151 }152 83 } -
progress-planner/tags/1.0.4/classes/suggested-tasks/local-tasks/providers/class-local-tasks-abstract.php
r3210975 r3238385 9 9 10 10 use Progress_Planner\Suggested_Tasks\Local_Tasks\Providers\Local_Tasks_Interface; 11 use Progress_Planner\Suggested_Tasks\Local_Tasks\Local_Task_Factory; 11 12 12 13 /** … … 16 17 17 18 /** 19 * The type of the task. 20 * 21 * @var string 22 */ 23 const TYPE = ''; 24 25 /** 26 * The ID of the task. 27 * 28 * @var string 29 */ 30 const ID = ''; 31 32 /** 18 33 * The capability required to perform the task. 19 34 * … … 21 36 */ 22 37 protected $capability = 'manage_options'; 38 39 /** 40 * Get the provider type. 41 * 42 * @return string 43 */ 44 public function get_provider_type() { 45 return static::TYPE; 46 } 47 48 /** 49 * Get the provider ID. 50 * 51 * @return string 52 */ 53 public function get_provider_id() { 54 return static::ID; 55 } 23 56 24 57 /** … … 32 65 : true; 33 66 } 67 68 /** 69 * Get the data from a task-ID. 70 * 71 * @param string $task_id The task ID (unused here). 72 * 73 * @return array The data. 74 */ 75 public function get_data_from_task_id( $task_id ) { 76 $data = [ 77 'type' => $this->get_provider_id(), 78 'id' => $task_id, 79 ]; 80 81 return $data; 82 } 83 84 /** 85 * Check if a task type is snoozed. 86 * 87 * @return bool 88 */ 89 public function is_task_type_snoozed() { 90 $snoozed = \progress_planner()->get_suggested_tasks()->get_snoozed_tasks(); 91 if ( ! \is_array( $snoozed ) || empty( $snoozed ) ) { 92 return false; 93 } 94 95 foreach ( $snoozed as $task ) { 96 $task_object = ( new Local_Task_Factory( $task['id'] ) )->get_task(); 97 $provider_id = $task_object->get_provider_id(); 98 99 if ( $provider_id === $this->get_provider_id() ) { 100 return true; 101 } 102 } 103 104 return false; 105 } 34 106 } -
progress-planner/tags/1.0.4/classes/suggested-tasks/local-tasks/providers/class-local-tasks-interface.php
r3210975 r3238385 48 48 49 49 /** 50 * Get the provider type. 51 * 52 * @return string 53 */ 54 public function get_provider_type(); 55 56 /** 50 57 * Get the provider ID. 51 58 * 52 59 * @return string 53 60 */ 54 public function get_provider_ type();61 public function get_provider_id(); 55 62 56 63 /** -
progress-planner/tags/1.0.4/classes/suggested-tasks/local-tasks/providers/class-settings-saved.php
r3210975 r3238385 11 11 * Add tasks for settings saved. 12 12 */ 13 class Settings_Saved extends Local_Tasks_Abstract { 13 class Settings_Saved extends Local_OneTime_Tasks_Abstract { 14 15 /** 16 * The provider type. 17 * 18 * @var string 19 */ 20 const TYPE = 'configuration'; 14 21 15 22 /** … … 18 25 * @var string 19 26 */ 20 const TYPE= 'settings-saved';27 const ID = 'settings-saved'; 21 28 22 29 /** 23 * Get the provider ID.30 * Check if the task condition is met. 24 31 * 25 * @return string32 * @return bool 26 33 */ 27 public function get_provider_type() { 28 return self::TYPE; 29 } 30 31 /** 32 * Evaluate a task. 33 * 34 * @param string $task_id The task ID. 35 * 36 * @return bool|string 37 */ 38 public function evaluate_task( $task_id ) { 39 40 // Early bail if the user does not have the capability to manage options. 41 if ( ! $this->capability_required() ) { 42 return false; 43 } 44 45 if ( 0 === strpos( $task_id, self::TYPE ) && false !== \get_option( 'progress_planner_pro_license_key', false ) ) { 46 return $task_id; 47 } 48 return false; 49 } 50 51 /** 52 * Get an array of tasks to inject. 53 * 54 * @return array 55 */ 56 public function get_tasks_to_inject() { 57 58 // Early bail if the user does not have the capability to manage options or if the task is snoozed. 59 if ( true === $this->is_task_type_snoozed() || ! $this->capability_required() ) { 60 return []; 61 } 62 34 public function check_task_condition() { 63 35 $prpl_pro_license_key = \get_option( 'progress_planner_pro_license_key', false ); 64 36 65 if ( false !== $prpl_pro_license_key ) { 66 return []; 67 } 68 69 $task_id = self::TYPE . '-' . \gmdate( 'YW' ); 70 71 // If the task with this id is completed, don't add a task. 72 if ( true === \progress_planner()->get_suggested_tasks()->check_task_condition( 73 [ 74 'type' => 'completed', 75 'task_id' => $task_id, 76 ] 77 ) ) { 78 return []; 79 } 80 81 return [ 82 $this->get_task_details( self::TYPE . '-' . \gmdate( 'YW' ) ), 83 ]; 37 return false !== $prpl_pro_license_key ? true : false; 84 38 } 85 39 … … 91 45 * @return array 92 46 */ 93 public function get_task_details( $task_id ) { 47 public function get_task_details( $task_id = '' ) { 48 49 if ( ! $task_id ) { 50 $task_id = $this->get_provider_id(); 51 } 94 52 95 53 return [ … … 98 56 'parent' => 0, 99 57 'priority' => 'high', 100 'type' => 'maintenance',58 'type' => $this->get_provider_type(), 101 59 'points' => 1, 102 'url' => \current_user_can( 'manage_options') ? \esc_url( \admin_url( 'admin.php?page=progress-planner-settings' ) ) : '',60 'url' => $this->capability_required() ? \esc_url( \admin_url( 'admin.php?page=progress-planner-settings' ) ) : '', 103 61 'description' => '<p>' . \esc_html__( 'Head over to the settings page and fill in the required information.', 'progress-planner' ) . '</p>', 104 62 ]; 105 63 } 106 107 /**108 * Get the data from a task-ID.109 *110 * @param string $task_id The task ID.111 *112 * @return array The data.113 */114 public function get_data_from_task_id( $task_id ) {115 $data = [116 'type' => self::TYPE,117 'id' => $task_id,118 ];119 120 return $data;121 }122 123 /**124 * Check if a task type is snoozed.125 *126 * @return bool127 */128 public function is_task_type_snoozed() {129 $snoozed = \progress_planner()->get_suggested_tasks()->get_snoozed_tasks();130 if ( ! \is_array( $snoozed ) || empty( $snoozed ) ) {131 return false;132 }133 134 foreach ( $snoozed as $task ) {135 if ( self::TYPE === $task['id'] ) {136 return true;137 }138 }139 140 return false;141 }142 64 } -
progress-planner/tags/1.0.4/classes/widgets/class-activity-scores.php
r3226821 r3238385 8 8 namespace Progress_Planner\Widgets; 9 9 10 use Progress_Planner\Widget;11 10 use Progress_Planner\Goals\Goal_Recurring; 12 11 use Progress_Planner\Goals\Goal; -
progress-planner/tags/1.0.4/classes/widgets/class-badge-streak.php
r3210975 r3238385 7 7 8 8 namespace Progress_Planner\Widgets; 9 10 use Progress_Planner\Widget;11 9 12 10 /** -
progress-planner/tags/1.0.4/classes/widgets/class-challenge.php
r3226821 r3238385 11 11 * Challenge class. 12 12 */ 13 final class Challenge extends \Progress_Planner\Widget {13 final class Challenge extends Widget { 14 14 15 15 /** -
progress-planner/tags/1.0.4/classes/widgets/class-latest-badge.php
r3210975 r3238385 7 7 8 8 namespace Progress_Planner\Widgets; 9 10 use Progress_Planner\Widget;11 9 12 10 /** -
progress-planner/tags/1.0.4/classes/widgets/class-published-content.php
r3210975 r3238385 7 7 8 8 namespace Progress_Planner\Widgets; 9 10 use Progress_Planner\Widget;11 9 12 10 /** -
progress-planner/tags/1.0.4/classes/widgets/class-suggested-tasks.php
r3217671 r3238385 8 8 namespace Progress_Planner\Widgets; 9 9 10 use Progress_Planner\Widget;11 10 use Progress_Planner\Badges\Monthly; 12 11 use Progress_Planner\Suggested_Tasks\Local_Tasks\Local_Task_Factory; … … 23 22 */ 24 23 protected $id = 'suggested-tasks'; 24 25 /** 26 * The tasks. 27 * 28 * @var array|null 29 */ 30 protected $pending_tasks = null; 25 31 26 32 /** … … 55 61 56 62 $pending_celebration = \progress_planner()->get_suggested_tasks()->get_pending_celebration(); 63 $pending_tasks = $this->get_pending_tasks(); 57 64 $deps = [ 58 65 'progress-planner-todo', … … 63 70 64 71 // Check if need to load confetti. 65 if ( ! empty( $pending_celebration ) ) {72 if ( isset( $pending_tasks['content-update'] ) || ! empty( $pending_celebration ) ) { 66 73 $deps[] = 'particles-confetti'; 67 74 } else { … … 90 97 $handle = 'progress-planner-' . $this->id; 91 98 92 $current_screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;93 94 99 // Enqueue the script. 95 100 \wp_enqueue_script( $handle ); … … 99 104 100 105 // Get pending tasks. 101 $tasks['details'] = \progress_planner()->get_suggested_tasks()->get_tasks();106 $tasks['details'] = $this->get_pending_tasks(); 102 107 103 108 // Insert the pending celebration tasks as high priority tasks, so they are shown always. … … 105 110 106 111 $task_object = ( new Local_Task_Factory( $task_id ) )->get_task(); 107 $task_provider = \progress_planner()->get_suggested_tasks()->get_local()->get_task_provider( $task_object->get_provider_ type() );108 109 if ( $task_provider ->capability_required() ) {112 $task_provider = \progress_planner()->get_suggested_tasks()->get_local()->get_task_provider( $task_object->get_provider_id() ); 113 114 if ( $task_provider && $task_provider->capability_required() ) { 110 115 $task_details = \progress_planner()->get_suggested_tasks()->get_local()->get_task_details( $task_id ); 111 116 … … 113 118 $task_details['priority'] = 'high'; // Celebrate tasks are always on top. 114 119 $task_details['action'] = 'celebrate'; 115 $tasks['details'][] = $task_details; 120 $task_details['type'] = 'pending_celebration'; 121 122 if ( ! isset( $tasks['details']['pending_celebration'] ) ) { 123 $tasks['details']['pending_celebration'] = []; 124 } 125 126 $tasks['details']['pending_celebration'][] = $task_details; 116 127 } 117 128 … … 119 130 \progress_planner()->get_suggested_tasks()->transition_task_status( $task_id, 'pending_celebration', 'completed' ); 120 131 } 132 } 133 134 $max_items_per_type = []; 135 foreach ( $tasks['details'] as $type => $items ) { 136 $max_items_per_type[ $type ] = $type === 'content-update' ? 2 : 1; 137 } 138 139 // We want all pending_celebration' tasks to be shown. 140 if ( isset( $max_items_per_type['pending_celebration'] ) ) { 141 $max_items_per_type['pending_celebration'] = 99; 142 } 143 144 // Check if current date is between Feb 12-16. 145 $confetti_options = []; 146 $year = \gmdate( 'Y' ); 147 $current_date = $year . '-' . \gmdate( 'm-d' ); 148 149 // TODO: GET params just for testing. 150 $start_date = $year . '-' . ( isset( $_GET['start_date'] ) ? \sanitize_text_field( \wp_unslash( $_GET['start_date'] ) ) : '02-12' ); 151 $end_date = $year . '-' . ( isset( $_GET['end_date'] ) ? \sanitize_text_field( \wp_unslash( $_GET['end_date'] ) ) : '02-16' ); 152 153 if ( $current_date >= $start_date && $current_date <= $end_date ) { 154 $confetti_options = [ 155 [ 156 'particleCount' => 50, 157 'scalar' => 2.2, 158 'shapes' => [ 'heart' ], 159 'colors' => [ 'FFC0CB', 'FF69B4', 'FF1493', 'C71585' ], 160 ], 161 [ 162 'particleCount' => 20, 163 'scalar' => 3.2, 164 'shapes' => [ 'heart' ], 165 'colors' => [ 'FFC0CB', 'FF69B4', 'FF1493', 'C71585' ], 166 ], 167 ]; 121 168 } 122 169 … … 126 173 'progressPlannerSuggestedTasks', 127 174 [ 128 'ajaxUrl' => \admin_url( 'admin-ajax.php' ), 129 'nonce' => \wp_create_nonce( 'progress_planner' ), 130 'tasks' => $tasks, 131 'maxItems' => $current_screen && 'dashboard' === $current_screen->id ? 3 : 5, 175 'ajaxUrl' => \admin_url( 'admin-ajax.php' ), 176 'nonce' => \wp_create_nonce( 'progress_planner' ), 177 'tasks' => $tasks, 178 'maxItemsPerType' => apply_filters( 'progress_planner_suggested_tasks_max_items_per_type', $max_items_per_type ), 179 'confettiOptions' => $confetti_options, 132 180 ] 133 181 ); 134 182 } 183 184 /** 185 * Get the tasks. 186 * 187 * @return array The tasks. 188 */ 189 public function get_pending_tasks() { 190 191 if ( null === $this->pending_tasks ) { 192 $tasks = []; 193 $pending_tasks = \progress_planner()->get_suggested_tasks()->get_tasks(); 194 195 // Sort them by type (channel). 196 foreach ( $pending_tasks as $task ) { 197 198 if ( ! isset( $tasks[ $task['type'] ] ) ) { 199 $tasks[ $task['type'] ] = []; 200 } 201 202 $tasks[ $task['type'] ][] = $task; 203 } 204 205 $this->pending_tasks = $tasks; 206 } 207 208 return $this->pending_tasks; 209 } 135 210 } -
progress-planner/tags/1.0.4/classes/widgets/class-todo.php
r3210975 r3238385 7 7 8 8 namespace Progress_Planner\Widgets; 9 10 use Progress_Planner\Widget;11 9 12 10 /** -
progress-planner/tags/1.0.4/classes/widgets/class-whats-new.php
r3210975 r3238385 8 8 namespace Progress_Planner\Widgets; 9 9 10 use Progress_Planner\Widget;11 10 use Progress_Planner\Cache; 12 11 -
progress-planner/tags/1.0.4/progress-planner.php
r3226821 r3238385 10 10 * Requires at least: 6.3 11 11 * Requires PHP: 7.4 12 * Version: 1.0. 312 * Version: 1.0.4 13 13 * Author: Team Emilia Projects 14 14 * Author URI: https://prpl.fyi/about -
progress-planner/tags/1.0.4/readme.txt
r3226821 r3238385 5 5 Tested up to: 6.7 6 6 Requires PHP: 7.4 7 Stable tag: 1.0. 37 Stable tag: 1.0.4 8 8 License: GPL3+ 9 9 License URI: https://www.gnu.org/licenses/gpl-3.0.en.html … … 78 78 79 79 == Changelog == 80 81 = 1.0.4 = 82 83 Enhancements: 84 85 * We've moved Ravi's recommendations to the top left of your Progress Planner dashboard. They're the most important thing on there, so we wanted to give it prime placement. 86 * We changed "Update post" to "Review post" / "Review page" and [wrote better instructions for reviewing old posts and pages](https://progressplanner.com/recommendations/review-post/). These tasks now prioritize the most important pages, like your About page, Privacy policy, Contact page and FAQ page. 87 * Added an option to redirect users to the Progress Planner dashboard after login. The WordPress dashboard isn't particularly useful in our eyes, this mind entice you to action more. 88 * Added a plugin-deactivation feedback form (we tell you, because you'll never see it, right? :) ). 89 * Removed the celebration for "Perform all updates" if it was done by WordPress's automatic update. We all love confetti, but when it comes all the time without you doing anything, it loses its value, right? Hence this fix. 90 91 We've added the following Recommendations from Ravi: 92 93 * [Setting site icon](https://progressplanner.com/recommendations/set-a-site-icon-aka-favicon/). 94 * [Setting the tagline](https://progressplanner.com/recommendations/set-tagline/). 95 * [Deactivating the display of PHP debug messages](https://progressplanner.com/recommendations/set-wp-debug/). 96 * [Removing the default WP "Hello world" post](https://progressplanner.com/recommendations/delete-the-default-wordpress-hello-world-post/). 97 * [Removing the default WP "Sample page" page](https://progressplanner.com/recommendations/delete-the-default-wordpress-sample-page-post/). 98 99 Under the hood: 100 101 * Improvements to the REST-API endpoint for getting stats. 102 * Removed admin notices on the Progress Planner page. 80 103 81 104 = 1.0.3 = -
progress-planner/tags/1.0.4/views/admin-page-settings.php
r3226821 r3238385 34 34 35 35 <form id="prpl-settings"> 36 <div class="prpl-column"> 37 <div class="prpl-widget-wrapper"> 38 <h2 class="prpl-settings-section-title"> 39 <span class="icon"> 40 <?php \progress_planner()->the_asset( 'images/icon_pages.svg' ); ?> 41 </span> 42 <span> 43 <?php esc_html_e( 'Your pages', 'progress-planner' ); ?> 44 </span> 45 </h2> 46 <p> 47 <?php esc_html_e( 'Let us know if you have following pages.', 'progress-planner' ); ?> 48 </p> 49 <div class="prpl-pages-list"> 50 <?php 51 foreach ( \progress_planner()->get_admin__page_settings()->get_settings() as $prpl_setting ) { 52 \progress_planner()->the_view( "setting/{$prpl_setting['type']}.php", [ 'prpl_setting' => $prpl_setting ] ); 53 } 54 ?> 55 </div> 56 </div> 57 </div> 58 59 <div class="prpl-column"> 60 <div class="prpl-widget-wrapper"> 61 <h2 class="prpl-settings-section-license"> 62 <span> 63 <?php \esc_html_e( 'License', 'progress-planner' ); ?> 64 </span> 65 </h2> 66 <div class="prpl-license-keys-wrapper"> 67 <?php 68 $prpl_pro_license = \get_option( 'progress_planner_pro_license_key', '' ); 69 $prpl_pro_license_status = \get_option( 'progress_planner_pro_license_status', '' ); 70 ?> 71 <?php if ( empty( $prpl_pro_license ) || 'valid' !== $prpl_pro_license_status ) : ?> 72 <p> 73 <?php 74 printf( 75 // translators: %s is a link to the Pro page, with the text "Progress Planner Pro". 76 \esc_html__( 'Take part in interactive challenges to solve website problems like broken links and sharpen your skills with in-context mini courses. Upgrade to %s!', 'progress-planner' ), 77 '<a href="https://progressplanner.com/pro/" target="_blank">Progress Planner Pro</a>' 78 ); 79 ?> 80 </p> 81 <?php endif; ?> 82 <label for="prpl-setting-pro-license-key"> 83 <?php \esc_html_e( 'Progress Planner Pro license key', 'progress-planner' ); ?> 84 </label> 85 <div class="prpl-license-key-wrapper"> 86 <input 87 id="prpl-setting-pro-license-key" 88 name="prpl-pro-license-key" 89 type="text" 90 value="<?php echo \esc_attr( $prpl_pro_license ); ?>" 91 /> 92 <?php if ( ! empty( $prpl_pro_license ) ) : ?> 93 <span class="prpl-license-status prpl-license-status-<?php echo ( 'valid' === $prpl_pro_license_status ) ? 'valid' : 'invalid'; ?>"> 94 <?php if ( 'valid' === $prpl_pro_license_status ) : ?> 95 <span class="prpl-license-status-valid" title="<?php esc_attr_e( 'Valid', 'progress-planner' ); ?>"> 96 <?php \progress_planner()->the_asset( 'images/icon_check_circle.svg' ); ?> 97 </span> 98 <?php else : ?> 99 <span class="prpl-license-status-invalid" title="<?php esc_attr_e( 'Invalid', 'progress-planner' ); ?>"> 100 <?php \progress_planner()->the_asset( 'images/icon_exclamation_circle.svg' ); ?> 101 </span> 102 <?php endif; ?> 103 </span> 104 <?php endif; ?> 105 </div> 106 </div> 107 </div> 108 </div> 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' ); ?> 109 39 110 40 <?php wp_nonce_field( 'prpl-settings' ); ?> -
progress-planner/tags/1.0.4/views/page-widgets/parts/monthly-badges.php
r3226821 r3238385 35 35 <?php if ( $prpl_badges ) : ?> 36 36 <?php 37 $prpl_badges_per_row = 3;38 $prpl_badges_count = count( $prpl_badges );39 $prpl_scroll_to_row = 1;37 $prpl_badges_per_row = 3; 38 $prpl_badges_count = count( $prpl_badges ); 39 $prpl_scroll_to_row = 1; 40 40 $prpl_current_month_badge_id = Monthly::get_badge_id_from_date( new \DateTime() ); 41 41 if ( 'popover' !== $prpl_location ) { -
progress-planner/tags/1.0.4/views/page-widgets/suggested-tasks.php
r3226821 r3238385 15 15 $prpl_badge = \progress_planner()->get_badges()->get_badge( Monthly::get_badge_id_from_date( new \DateTime() ) ); 16 16 ?> 17 18 19 <div class="prpl-dashboard-widget-suggested-tasks"> 20 <h2 class="prpl-widget-title"> 21 <?php \esc_html_e( 'Ravi\'s Recommendations', 'progress-planner' ); ?> 22 </h2> 23 24 <ul style="display:none"></ul> 25 <ul class="prpl-suggested-tasks-list"></ul> 26 27 <hr> 28 </div> 29 17 30 <?php if ( $prpl_badge ) : ?> 18 31 <h2 class="prpl-widget-title"> … … 44 57 </h2> 45 58 46 <prpl-gauge background="var(--prpl-background-orange)" color="var(--prpl-color-accent-orange)"> 59 <prpl-gauge 60 id="prpl-gauge-ravi" 61 background="var(--prpl-background-orange)" 62 color="var(--prpl-color-accent-orange)" 63 data-max="<?php echo (int) Monthly::TARGET_POINTS; ?>" 64 data-value="<?php echo (float) $prpl_widget->get_score(); ?>" 65 data-badge-id="<?php echo esc_attr( $prpl_badge->get_id() ); ?>" 66 > 47 67 <progress max="<?php echo (int) Monthly::TARGET_POINTS; ?>" value="<?php echo (float) $prpl_widget->get_score(); ?>"> 48 68 <prpl-badge complete="true" badge-id="<?php echo esc_attr( $prpl_badge->get_id() ); ?>"></prpl-badge> … … 52 72 <div class="prpl-widget-content-points"> 53 73 <span><?php \esc_html_e( 'Progress monthly badge', 'progress-planner' ); ?></span> 54 <span class="prpl-widget-content-points-number">74 <span id="prpl-widget-content-ravi-points-number" class="prpl-widget-content-points-number"> 55 75 <?php echo (int) $prpl_widget->get_score(); ?>pt 56 76 </span> … … 59 79 <hr> 60 80 <?php endif; ?> 61 62 <div class="prpl-dashboard-widget-suggested-tasks">63 <h2 class="prpl-widget-title">64 <?php \esc_html_e( 'Ravi\'s Recommendations', 'progress-planner' ); ?>65 </h2>66 67 <ul style="display:none"></ul>68 <ul class="prpl-suggested-tasks-list"></ul>69 70 <hr>71 </div>72 81 73 82 <div class="prpl-widget-content"> -
progress-planner/trunk/CHANGELOG.md
r3217671 r3238385 1 = 1.0.4 = 2 3 Enhancements: 4 5 * We've moved Ravi's recommendations to the top left of your Progress Planner dashboard. They're the most important thing on there, so we wanted to give it prime placement. 6 * We changed "Update post" to "Review post" / "Review page" and [wrote better instructions for reviewing old posts and pages](https://progressplanner.com/recommendations/review-post/). These tasks now prioritize the most important pages, like your About page, Privacy policy, Contact page and FAQ page. 7 * Added an option to redirect users to the Progress Planner dashboard after login. The WordPress dashboard isn't particularly useful in our eyes, this mind entice you to action more. 8 * Added a plugin-deactivation feedback form (we tell you, because you'll never see it, right? :) ). 9 * Removed the celebration for "Perform all updates" if it was done by WordPress's automatic update. We all love confetti, but when it comes all the time without you doing anything, it loses its value, right? Hence this fix. 10 11 We've added the following Recommendations from Ravi: 12 13 * [Setting site icon](https://progressplanner.com/recommendations/set-a-site-icon-aka-favicon/). 14 * [Setting the tagline](https://progressplanner.com/recommendations/set-tagline/). 15 * [Deactivating the display of PHP debug messages](https://progressplanner.com/recommendations/set-wp-debug/). 16 * [Removing the default WP "Hello world" post](https://progressplanner.com/recommendations/delete-the-default-wordpress-hello-world-post/). 17 * [Removing the default WP "Sample page" page](https://progressplanner.com/recommendations/delete-the-default-wordpress-sample-page-post/). 18 19 Under the hood: 20 21 * Improvements to the REST-API endpoint for getting stats. 22 * Removed admin notices on the Progress Planner page. 23 24 = 1.0.3 = 25 26 Fixed: 27 28 * Detection of page-types in the settings page. 29 * Properly resetting caches for monthly badges. 30 31 Enhancements: 32 33 * Added a new "Challenges" widget to the dashboard. 34 1 35 = 1.0.2 = 2 36 … … 22 56 * Assets versioning. 23 57 * Duplicate update-core tasks. 24 * Update old post task being celebrated as completed when post is trashed.25 58 * Information icon for 'Create a long post' task was showing text of 'create a short post' task. 26 59 * Numerous other minor bugfixes. … … 48 81 = 0.9.5 = 49 82 83 Enhancements: 84 85 * Added functionality to make it easier to demo the plugin on the WordPress playground. 86 * Improved the onboarding and added a tour of the plugin. 87 50 88 Fixed: 51 89 … … 68 106 Security: 69 107 70 * Stricter sanitization & escaping of data in to-do items. Props to [justakazh](https://github.com/justakazh) for reporting through our [PatchStack Vulnerability Disclosure Program](https://patchstack.com/database/vdp/progress-planner).108 * Stricter sanitization & escaping of data in to-do items. Props to [justakazh](https://github.com/justakazh) for reporting through our [PatchStack Vulnerability Disclosure Program](https://patchstack.com/database/vdp/progress-planner). 71 109 * Restrict access to the plugin's dashboard widgets to users with the `publish_posts` capability. 72 110 -
progress-planner/trunk/assets/css/page-widgets/suggested-tasks.css
r3210975 r3238385 100 100 101 101 hr { 102 display: initial;102 display: block; 103 103 } 104 104 } -
progress-planner/trunk/assets/css/settings-page.css
r3226821 r3238385 69 69 width: 1.25em; 70 70 height: 1.25em; 71 72 svg path {73 fill: currentcolor;74 }75 71 } 76 72 } -
progress-planner/trunk/assets/js/web-components/prpl-badge.js
r3217671 r3238385 12 12 complete = 13 13 true === complete && 'true' === this.getAttribute( 'complete' ); 14 15 badgeId = badgeId || this.getAttribute( 'badge-id' ); 14 16 this.innerHTML = ` 15 17 <img 16 18 src="${ 17 19 progressPlannerBadge.remoteServerRootUrl 18 }/wp-json/progress-planner-saas/v1/badge-svg/?badge_id=${ 19 badgeId || this.getAttribute( 'badge-id' ) 20 }" 20 }/wp-json/progress-planner-saas/v1/badge-svg/?badge_id=${ badgeId }" 21 21 alt="Badge" 22 22 ${ false === complete ? 'style="filter: grayscale(1);opacity: 0.25;"' : '' } -
progress-planner/trunk/assets/js/web-components/prpl-suggested-task.js
r3210975 r3238385 13 13 taskPoints, 14 14 taskAction = '', 15 taskUrl = '' 15 taskUrl = '', 16 taskDismissable = false, 17 taskType = '' 16 18 ) { 17 19 // Get parent class properties … … 26 28 27 29 const isRemoteTask = taskId.startsWith( 'remote-task-' ); 30 const isDismissable = taskDismissable || isRemoteTask; 28 31 29 32 const actionButtons = { … … 52 55 <span class="screen-reader-text">${ progressPlannerSuggestedTask.i18n.snooze }</span> 53 56 </button>`, 54 complete: is RemoteTask57 complete: isDismissable 55 58 ? `<button 56 59 type="button" … … 69 72 70 73 this.innerHTML = ` 71 <li class="prpl-suggested-task" data-task-id="${ taskId }" data-task-action="${ taskAction }" data-task-url="${ taskUrl }" >74 <li class="prpl-suggested-task" data-task-id="${ taskId }" data-task-action="${ taskAction }" data-task-url="${ taskUrl }" data-task-type="${ taskType }" data-task-points="${ taskPoints }"> 72 75 <h3><span>${ taskHeading }</span></h3> 73 76 <div class="prpl-suggested-task-actions"> … … 278 281 runTaskAction = ( taskId, actionType, snoozeDuration ) => { 279 282 taskId = taskId.toString(); 283 const type = 284 this.querySelector( 'li' ).getAttribute( 'data-task-type' ); 280 285 281 286 const data = { … … 308 313 ) { 309 314 window.progressPlannerSuggestedTasks.tasks.snoozed.push( 310 taskId 315 { 316 id: taskId, 317 } 311 318 ); 312 319 } … … 321 328 el.setAttribute( 'data-task-action', 'celebrate' ); 322 329 330 const event = new CustomEvent( 331 'prplUpdateRaviGaugeEvent', 332 { 333 detail: { 334 pointsDiff: parseInt( 335 this.querySelector( 'li' ).getAttribute( 336 'data-task-points' 337 ) 338 ), 339 }, 340 } 341 ); 342 document.dispatchEvent( event ); 343 323 344 // Trigger the celebration event. 324 345 document.dispatchEvent( … … 329 350 } 330 351 331 const event = new Event( 'prplMaybeInjectSuggestedTaskEvent' ); 352 const event = new CustomEvent( 353 'prplMaybeInjectSuggestedTaskEvent', 354 { 355 detail: { 356 taskId, 357 type, 358 }, 359 } 360 ); 332 361 document.dispatchEvent( event ); 333 362 } ); -
progress-planner/trunk/assets/js/widgets/suggested-tasks.js
r3217671 r3238385 1 /* global customElements, progressPlannerSuggestedTasks, confetti, prplDocumentReady , progressPlannerSuggestedTask*/1 /* global customElements, progressPlannerSuggestedTasks, confetti, prplDocumentReady */ 2 2 3 3 /** 4 4 * Count the number of items in the list. 5 5 * 6 * @param {string} type The type of items to count. 6 7 * @return {number} The number of items in the list. 7 8 */ 8 const progressPlannerCountItems = () => { 9 const items = document.querySelectorAll( '.prpl-suggested-task' ); 9 const progressPlannerCountItems = ( type ) => { 10 // We want to display all pending celebration tasks on page load. 11 if ( 'pending_celebration' === type ) { 12 return 0; 13 } 14 15 const items = document.querySelectorAll( 16 `.prpl-suggested-task[data-task-type="${ type }"]` 17 ); 10 18 return items.length; 11 19 }; … … 14 22 * Get the next item to inject. 15 23 * 24 * @param {string} type The type of items to get the next item from. 16 25 * @return {Object} The next item to inject. 17 26 */ 18 const progressPlannerGetNextItem = () => { 27 const progressPlannerGetNextItemFromType = ( type ) => { 28 // If the are no items of this type, return null. 29 if ( 30 'undefined' === 31 typeof progressPlannerSuggestedTasks.tasks.details[ type ] 32 ) { 33 return null; 34 } 35 19 36 // Remove completed and snoozed items. 20 37 const tasks = progressPlannerSuggestedTasks.tasks; 21 const items = tasks.details ;38 const items = tasks.details[ type ]; 22 39 const completed = tasks.completed; 23 40 const snoozed = tasks.snoozed; … … 71 88 /** 72 89 * Inject the next item. 90 * @param {string} type The type of items to inject the next item from. 73 91 */ 74 const progressPlannerInjectNextItem = ( ) => {75 const nextItem = progressPlannerGetNextItem ();92 const progressPlannerInjectNextItem = ( type ) => { 93 const nextItem = progressPlannerGetNextItemFromType( type ); 76 94 if ( ! nextItem ) { 77 95 return; … … 95 113 details.points ?? 1, 96 114 details.action ?? '', 97 details.url ?? '' 115 details.url ?? '', 116 details.dismissable ?? false, 117 details.type ?? '' 98 118 ); 99 119 … … 155 175 156 176 const progressPlannerRenderAttemptshoot = () => { 157 confetti( { 158 ...prplConfettiDefaults, 159 particleCount: 40, 160 scalar: 1.2, 161 shapes: [ 'star' ], 162 } ); 163 164 confetti( { 165 ...prplConfettiDefaults, 166 particleCount: 10, 167 scalar: 0.75, 168 shapes: [ 'circle' ], 169 } ); 177 let confettiOptions = [ 178 { 179 particleCount: 40, 180 scalar: 1.2, 181 shapes: [ 'star' ], 182 }, 183 { 184 particleCount: 10, 185 scalar: 0.75, 186 shapes: [ 'circle' ], 187 }, 188 ]; 189 190 // Tripple check if the confetti options are an array and not undefined. 191 if ( 192 'undefined' !== 193 typeof progressPlannerSuggestedTasks.confettiOptions && 194 true === 195 Array.isArray( 196 progressPlannerSuggestedTasks.confettiOptions 197 ) && 198 progressPlannerSuggestedTasks.confettiOptions.length 199 ) { 200 confettiOptions = progressPlannerSuggestedTasks.confettiOptions; 201 } 202 203 for ( const value of confettiOptions ) { 204 confetti( { 205 ...prplConfettiDefaults, 206 ...value, 207 } ); 208 } 170 209 }; 171 210 … … 192 231 .querySelectorAll( '.prpl-suggested-task-celebrated' ) 193 232 .forEach( ( item ) => { 194 const taskId = item.getAttribute( 'data-task-id' ); 195 196 const request = wp.ajax.post( 197 'progress_planner_suggested_task_action', 233 const taskId = item.getAttribute( 'data-task-id' ), 234 type = item.getAttribute( 'data-task-type' ); 235 const el = document.querySelector( 236 `.prpl-suggested-task[data-task-id="${ taskId }"]` 237 ); 238 239 if ( el ) { 240 el.parentElement.remove(); 241 } 242 243 // Remove the task from the pending celebration. 244 window.progressPlannerSuggestedTasks.tasks.pending_celebration = 245 window.progressPlannerSuggestedTasks.tasks.pending_celebration.filter( 246 ( id ) => id !== taskId 247 ); 248 249 // Add the task to the completed tasks. 250 if ( 251 window.progressPlannerSuggestedTasks.tasks.completed.indexOf( 252 taskId 253 ) === -1 254 ) { 255 window.progressPlannerSuggestedTasks.tasks.completed.push( 256 taskId 257 ); 258 } 259 260 // Refresh the list. 261 const event = new CustomEvent( 262 'prplMaybeInjectSuggestedTaskEvent', 198 263 { 199 task_id: taskId, 200 nonce: progressPlannerSuggestedTask.nonce, 201 action_type: 'celebrated', 264 detail: { 265 taskId, 266 type, 267 }, 202 268 } 203 269 ); 204 request.done( () => { 205 const el = document.querySelector( 206 `.prpl-suggested-task[data-task-id="${ taskId }"]` 207 ); 208 209 if ( el ) { 210 el.parentElement.remove(); 211 } 212 213 // Remove the task from the pending celebration. 214 window.progressPlannerSuggestedTasks.tasks.pending_celebration = 215 window.progressPlannerSuggestedTasks.tasks.pending_celebration.filter( 216 ( id ) => id !== taskId 217 ); 218 219 // Add the task to the completed tasks. 220 if ( 221 window.progressPlannerSuggestedTasks.tasks.completed.indexOf( 222 taskId 223 ) === -1 224 ) { 225 window.progressPlannerSuggestedTasks.tasks.completed.push( 226 taskId 227 ); 228 } 229 230 // Refresh the list. 231 const event = new Event( 232 'prplMaybeInjectSuggestedTaskEvent' 233 ); 234 document.dispatchEvent( event ); 235 } ); 270 document.dispatchEvent( event ); 236 271 } ); 237 272 }, 2000 ); … … 260 295 } 261 296 262 // Inject items, until we reach the maximum number of items. 263 while ( 264 progressPlannerCountItems() < 265 parseInt( progressPlannerSuggestedTasks.maxItems ) && 266 progressPlannerGetNextItem() 267 ) { 268 progressPlannerInjectNextItem(); 269 } 270 271 const event = new Event( 'prplResizeAllGridItemsEvent' ); 297 // Loop through each type and inject items. 298 for ( const type in progressPlannerSuggestedTasks.tasks.details ) { 299 // Inject items, until we reach the maximum number of channel items. 300 while ( 301 progressPlannerCountItems( type ) < 302 parseInt( 303 progressPlannerSuggestedTasks.maxItemsPerType[ type ] 304 ) && 305 progressPlannerGetNextItemFromType( type ) 306 ) { 307 progressPlannerInjectNextItem( type ); 308 } 309 } 310 311 const event = new CustomEvent( 'prplResizeAllGridItemsEvent' ); 272 312 document.dispatchEvent( event ); 273 313 } ); … … 422 462 ); 423 463 464 const prplGetRaviGaugeProps = () => { 465 const gauge = document.getElementById( 'prpl-gauge-ravi' ); 466 if ( ! gauge ) { 467 return; 468 } 469 470 return { 471 id: gauge.id, 472 background: gauge.getAttribute( 'background' ), 473 color: gauge.getAttribute( 'color' ), 474 max: gauge.getAttribute( 'data-max' ), 475 value: gauge.getAttribute( 'data-value' ), 476 badgeId: gauge.getAttribute( 'data-badge-id' ), 477 }; 478 }; 479 480 const prplUpdateRaviGauge = ( pointsDiff = 0 ) => { 481 if ( ! pointsDiff ) { 482 return; 483 } 484 485 const gaugeProps = prplGetRaviGaugeProps(); 486 487 if ( ! gaugeProps ) { 488 return; 489 } 490 491 let newValue = parseInt( gaugeProps.value ) + pointsDiff; 492 newValue = Math.round( newValue ); 493 newValue = Math.max( 0, newValue ); 494 newValue = Math.min( newValue, parseInt( gaugeProps.max ) ); 495 496 const Gauge = customElements.get( 'prpl-gauge' ); 497 const gauge = new Gauge( 498 { 499 max: parseInt( gaugeProps.max ), 500 value: parseFloat( newValue / parseInt( gaugeProps.max ) ), 501 background: gaugeProps.background, 502 color: gaugeProps.color, 503 maxDeg: '180deg', 504 start: '270deg', 505 cutout: '57%', 506 contentFontSize: 'var(--prpl-font-size-6xl)', 507 contentPadding: 508 'var(--prpl-padding) var(--prpl-padding) calc(var(--prpl-padding) * 2) var(--prpl-padding)', 509 marginBottom: 'var(--prpl-padding)', 510 }, 511 `<prpl-badge complete="true" badge-id="${ gaugeProps.badgeId }"></prpl-badge>` 512 ); 513 gauge.id = gaugeProps.id; 514 gauge.setAttribute( 'background', gaugeProps.background ); 515 gauge.setAttribute( 'color', gaugeProps.color ); 516 gauge.setAttribute( 'data-max', gaugeProps.max ); 517 gauge.setAttribute( 'data-value', newValue ); 518 gauge.setAttribute( 'data-badge-id', gaugeProps.badgeId ); 519 520 // Replace the old gauge with the new one. 521 const oldGauge = document.getElementById( gaugeProps.id ); 522 if ( oldGauge ) { 523 oldGauge.replaceWith( gauge ); 524 } 525 526 const oldCounter = document.getElementById( 527 'prpl-widget-content-ravi-points-number' 528 ); 529 if ( oldCounter ) { 530 oldCounter.textContent = newValue + 'pt'; 531 } 532 }; 533 534 // Listen for the event. 535 document.addEventListener( 536 'prplUpdateRaviGaugeEvent', 537 ( e ) => { 538 prplUpdateRaviGauge( e.detail.pointsDiff ); 539 }, 540 false 541 ); 542 424 543 // Listen for the event. 425 544 document.addEventListener( 426 545 'prplMaybeInjectSuggestedTaskEvent', 427 () => { 546 ( e ) => { 547 const type = e.detail.type; 548 549 if ( 'pending_celebration' === type ) { 550 return; 551 } 552 428 553 while ( 429 progressPlannerCountItems() < 430 parseInt( progressPlannerSuggestedTasks.maxItems ) && 431 progressPlannerGetNextItem() 554 progressPlannerCountItems( type ) < 555 parseInt( 556 progressPlannerSuggestedTasks.maxItemsPerType[ type ] 557 ) && 558 progressPlannerGetNextItemFromType( type ) 432 559 ) { 433 progressPlannerInjectNextItem( );560 progressPlannerInjectNextItem( type ); 434 561 } 435 562 -
progress-planner/trunk/classes/activities/class-suggested-task.php
r3210975 r3238385 59 59 60 60 $data = \progress_planner()->get_suggested_tasks()->get_local()->get_data_from_task_id( $this->data_id ); 61 if ( isset( $data['type'] ) && ( 'create-post' === $data['type'] || ' update-post' === $data['type'] ) && isset( $data['long'] ) && true === $data['long'] ) {61 if ( isset( $data['type'] ) && ( 'create-post' === $data['type'] || 'review-post' === $data['type'] ) && isset( $data['long'] ) && true === $data['long'] ) { 62 62 $points = 2; 63 63 } -
progress-planner/trunk/classes/admin/class-page-settings.php
r3226821 r3238385 160 160 161 161 // Skip if the ID is not set. 162 if ( 1 > (int) $page_args['id'] ) {162 if ( ! isset( $page_args['id'] ) || 1 > (int) $page_args['id'] ) { 163 163 continue; 164 164 } … … 171 171 } 172 172 173 $this->save_settings(); 173 174 $this->save_license(); 174 175 … … 176 177 177 178 \wp_send_json_success( \esc_html__( 'Options stored successfully', 'progress-planner' ) ); 179 } 180 181 /** 182 * Save the settings. 183 * 184 * @return void 185 */ 186 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 189 : false; 190 191 \update_user_meta( \get_current_user_id(), 'prpl_redirect_on_login', (bool) $redirect_on_login ); 178 192 } 179 193 … … 205 219 // Call the custom API. 206 220 $response = \wp_remote_post( 207 'https://progressplanner.com',221 \progress_planner()->get_remote_server_root_url(), 208 222 [ 209 223 'timeout' => 15, -
progress-planner/trunk/classes/admin/class-page.php
r3226821 r3238385 29 29 \add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] ); 30 30 \add_action( 'wp_ajax_progress_planner_save_cpt_settings', [ $this, 'save_cpt_settings' ] ); 31 \add_action( 'in_admin_header', [ $this, 'remove_admin_notices' ], PHP_INT_MAX ); 31 32 } 32 33 … … 34 35 * Get the widgets objects 35 36 * 36 * @return array<\Progress_Planner\Widget >37 * @return array<\Progress_Planner\Widgets\Widget> 37 38 */ 38 39 public function get_widgets() { 39 40 $widgets = [ 41 \progress_planner()->get_widgets__suggested_tasks(), 40 42 \progress_planner()->get_widgets__activity_scores(), 41 \progress_planner()->get_widgets__suggested_tasks(),42 43 \progress_planner()->get_widgets__todo(), 43 44 \progress_planner()->get_widgets__challenge(), … … 51 52 * Filter the widgets. 52 53 * 53 * @param array<\Progress_Planner\Widget > $widgets The widgets.54 * @param array<\Progress_Planner\Widgets\Widget> $widgets The widgets. 54 55 * 55 * @return array<\Progress_Planner\Widget >56 * @return array<\Progress_Planner\Widgets\Widget> 56 57 */ 57 58 return \apply_filters( 'progress_planner_admin_widgets', $widgets ); … … 63 64 * @param string $id The widget ID. 64 65 * 65 * @return \Progress_Planner\Widget |void66 * @return \Progress_Planner\Widgets\Widget|void 66 67 */ 67 68 public function get_widget( $id ) { … … 174 175 ); 175 176 } 177 178 $prpl_privacy_policy_accepted = \progress_planner()->is_privacy_policy_accepted(); 179 if ( ! $prpl_privacy_policy_accepted ) { 180 // Enqueue welcome styles. 181 \wp_enqueue_style( 182 'progress-planner-welcome', 183 PROGRESS_PLANNER_URL . '/assets/css/welcome.css', 184 [], 185 \progress_planner()->get_file_version( PROGRESS_PLANNER_DIR . '/assets/css/welcome.css' ) 186 ); 187 188 // Enqueue onboarding styles. 189 \wp_enqueue_style( 190 'progress-planner-onboard', 191 PROGRESS_PLANNER_URL . '/assets/css/onboard.css', 192 [], 193 \progress_planner()->get_file_version( PROGRESS_PLANNER_DIR . '/assets/css/onboard.css' ) 194 ); 195 } 176 196 } 177 197 … … 193 213 ); 194 214 } 215 216 /** 217 * Remove all admin notices when the user is on the Progress Planner page. 218 * 219 * @return void 220 */ 221 public function remove_admin_notices() { 222 $current_screen = \get_current_screen(); 223 if ( ! $current_screen ) { 224 return; 225 } 226 if ( ! \in_array( 227 $current_screen->id, 228 [ 229 'toplevel_page_progress-planner', 230 'progress-planner_page_progress-planner-settings', 231 ], 232 true 233 ) ) { 234 return; 235 } 236 237 \remove_all_actions( 'admin_notices' ); 238 } 195 239 } -
progress-planner/trunk/classes/badges/class-monthly.php
r3226821 r3238385 55 55 56 56 $activation_date = \progress_planner()->get_base()->get_activation_date(); 57 $start_date = $activation_date->modify( 'first day of this month' ); 57 if ( $activation_date < new \DateTime( 'first day of November 2024' ) ) { // When badges were introduced. 58 $start_date = $activation_date->modify( 'first day of November 2024' ); 59 } else { 60 $start_date = $activation_date->modify( 'first day of this month' ); 61 } 58 62 59 63 // Year when plugin was released. -
progress-planner/trunk/classes/class-base.php
r3226821 r3238385 99 99 $this->cached['badges'] = new Badges(); 100 100 101 // Dont add the widget if the privacy policy is not accepted.102 101 if ( true === $this->is_privacy_policy_accepted() ) { 103 102 $this->cached['settings_page'] = new Admin_Page_Settings(); 104 } 103 104 new Plugin_Deactivation(); 105 } 106 107 /** 108 * Redirect on login. 109 */ 110 \add_action( 'wp_login', [ $this, 'redirect_on_login' ], 10, 2 ); 105 111 } 106 112 … … 156 162 */ 157 163 public function get_placeholder_svg( $width = 1200, $height = 675 ) { 158 return 'data:image/svg+xml;base64,' . base64_encode( sprintf( '<svg width="%1$d" height="%2$d" xmlns="http://www.w3.org/2000/svg"><rect x="2" y="2" width="%3$d" height="%4$d" style="fill:#F6F5FB;stroke:#534786;stroke-width:2"/><text x="50%%" y="50%%" font-size="20" text-anchor="middle" alignment-baseline="middle" font-family="monospace" fill="#534786">progressplanner.com</text></svg>', $width, $height, ( $width - 4 ), ( $height - 4 ) ) ); 164 return 'data:image/svg+xml;base64,' . base64_encode( sprintf( '<svg width="%1$d" height="%2$d" xmlns="http://www.w3.org/2000/svg"><rect x="2" y="2" width="%3$d" height="%4$d" style="fill:#F6F5FB;stroke:#534786;stroke-width:2"/><text x="50%%" y="50%%" font-size="20" text-anchor="middle" alignment-baseline="middle" font-family="monospace" fill="#534786">progressplanner.com</text></svg>', $width, $height, ( $width - 4 ), ( $height - 4 ) ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode 159 165 } 160 166 … … 370 376 && 'valid' === \get_option( 'progress_planner_pro_license_status' ); 371 377 } 378 379 /** 380 * Redirect on login. 381 * 382 * @param string $user_login The user login. 383 * @param \WP_User $user The user object. 384 * 385 * @return void 386 */ 387 public function redirect_on_login( $user_login, $user ) { 388 // Check if the $user can `manage_options`. 389 if ( ! $user->has_cap( 'manage_options' ) ) { 390 return; 391 } 392 393 // Check if the user has the `prpl_redirect_on_login` meta. 394 if ( ! \get_user_meta( $user->ID, 'prpl_redirect_on_login', true ) ) { 395 return; 396 } 397 398 // Redirect to the Progress Planner dashboard. 399 \wp_safe_redirect( \admin_url( 'admin.php?page=progress-planner' ) ); 400 exit; 401 } 372 402 } 373 403 // phpcs:enable Generic.Commenting.Todo -
progress-planner/trunk/classes/class-playground.php
r3156816 r3238385 153 153 $this->create_random_post(); 154 154 } 155 for ( $i = 0; $i < 5; $i++ ) { 156 $this->create_random_post( true, 'page' ); 157 } 155 158 // One post for today. 156 159 $this->create_random_post( false ); … … 160 163 * Create a random post. 161 164 * 162 * @param bool $random_date Whether to use a random date or not. 165 * @param bool $random_date Whether to use a random date or not. 166 * @param string $post_type The post type to create. 163 167 * 164 168 * @return int Post ID. 165 169 */ 166 private function create_random_post( $random_date = true ) {170 private function create_random_post( $random_date = true, $post_type = 'post' ) { 167 171 $postarr = [ 168 172 'post_title' => str_replace( '.', '', $this->create_random_string( 5 ) ), 169 173 'post_content' => $this->create_random_string( wp_rand( 200, 500 ) ), 170 174 'post_status' => 'publish', 171 'post_type' => 'post',175 'post_type' => $post_type, 172 176 'post_date' => $this->get_random_date_last_12_months(), 173 177 ]; -
progress-planner/trunk/classes/class-rest-api-stats.php
r3217671 r3238385 167 167 $data['todo'] = $pending_todo_items; 168 168 169 $ravis_recommendations = \progress_planner()->get_suggested_tasks()->get_tasks(); 170 $data['recommendations'] = []; 171 foreach ( $ravis_recommendations as $recommendation ) { 172 $data['recommendations'][] = [ 173 'id' => $recommendation['task_id'], 174 'title' => $recommendation['title'], 175 'url' => isset( $recommendation['url'] ) ? $recommendation['url'] : '', 176 ]; 177 } 178 169 179 $data['plugin_url'] = \esc_url( \get_admin_url( null, 'admin.php?page=progress-planner' ) ); 170 180 … … 180 190 */ 181 191 public function validate_token( $token ) { 182 $token = str_replace( 'token/', '', $token ); 192 $token = str_replace( 'token/', '', $token ); 193 if ( \progress_planner()->is_pro_site() && $token === \get_option( 'progress_planner_pro_license_key' ) ) { 194 return true; 195 } 183 196 $license_key = \get_option( 'progress_planner_license_key', false ); 184 197 if ( ! $license_key || 'no-license' === $license_key ) { -
progress-planner/trunk/classes/class-suggested-tasks.php
r3210975 r3238385 11 11 use Progress_Planner\Suggested_Tasks\Remote_Tasks; 12 12 use Progress_Planner\Activities\Suggested_Task; 13 use Progress_Planner\Suggested_Tasks\Local_Tasks\Local_Task_Factory; 13 14 14 15 /** … … 52 53 \add_action( 'init', [ $this, 'init' ], 1 ); 53 54 } 55 56 // Add the automatic updates complete action. 57 \add_action( 'automatic_updates_complete', [ $this, 'on_automatic_updates_complete' ] ); 54 58 } 55 59 … … 67 71 68 72 foreach ( $completed_tasks as $task_id ) { 73 // Change the task status to pending celebration. 69 74 $this->mark_task_as_pending_celebration( $task_id ); 70 75 71 76 // Insert an activity. 72 $activity = new Suggested_Task(); 73 $activity->type = 'completed'; 74 $activity->data_id = (string) $task_id; 75 $activity->date = new \DateTime(); 76 $activity->user_id = \get_current_user_id(); 77 $activity->save(); 78 79 // Allow other classes to react to the completion of a suggested task. 80 do_action( 'progress_planner_suggested_task_completed', $task_id ); 77 $this->insert_activity( $task_id ); 78 } 79 } 80 81 /** 82 * Insert an activity. 83 * 84 * @param string $task_id The task ID. 85 * 86 * @return void 87 */ 88 public function insert_activity( $task_id ) { 89 // Insert an activity. 90 $activity = new Suggested_Task(); 91 $activity->type = 'completed'; 92 $activity->data_id = (string) $task_id; 93 $activity->date = new \DateTime(); 94 $activity->user_id = \get_current_user_id(); 95 $activity->save(); 96 97 // Allow other classes to react to the completion of a suggested task. 98 do_action( 'progress_planner_suggested_task_completed', $task_id ); 99 } 100 101 /** 102 * If done via automatic updates, the "core update" task should be marked as "completed" (and skip "pending celebration" status). 103 * 104 * @param array $update_results The update results. 105 * 106 * @return void 107 */ 108 public function on_automatic_updates_complete( $update_results ) { 109 110 $pending_tasks = $this->local->get_pending_tasks(); // @phpstan-ignore-line method.nonObject 111 112 if ( empty( $pending_tasks ) ) { 113 return; 114 } 115 116 // TODO: Get this from task provider. 117 $update_core_task_id = 'update-core'; 118 119 foreach ( $pending_tasks as $task_id ) { 120 $task_object = ( new Local_Task_Factory( $task_id ) )->get_task(); 121 $task_data = $task_object->get_data(); 122 123 if ( $task_data['type'] === $update_core_task_id && \gmdate( 'YW' ) === $task_data['year_week'] ) { 124 // Remove from local (pending tasks). 125 $this->local->remove_pending_task( $task_id ); // @phpstan-ignore-line method.nonObject 126 127 // Change the task status to completed. 128 $this->mark_task_as_completed( $task_id ); 129 130 // Insert an activity. 131 $this->insert_activity( $task_id ); 132 break; 133 } 81 134 } 82 135 } … … 470 523 471 524 /** 525 * Check if a task was completed. 526 * 527 * @param string $task_id The task ID. 528 * 529 * @return bool 530 */ 531 public function was_task_completed( $task_id ) { 532 return true === $this->check_task_condition( 533 [ 534 'type' => 'completed', 535 'task_id' => $task_id, 536 ] 537 ); 538 } 539 540 /** 472 541 * Handle the suggested task action. 473 542 * … … 489 558 switch ( $action ) { 490 559 case 'complete': 560 // It's local task, remove it from pending tasks. 561 if ( false === strpos( $task_id, 'remote-task' ) ) { 562 $this->local->remove_pending_task( $task_id ); // @phpstan-ignore-line method.nonObject 563 } 564 565 // Mark the task as completed. 491 566 $this->mark_task_as( 'completed', $task_id ); 567 568 // Insert an activity. 569 $this->insert_activity( $task_id ); 492 570 $updated = true; 493 571 break; … … 498 576 break; 499 577 500 case 'celebrated':501 // We dont need to do anything here, since the task is already marked as completed.502 $updated = true;503 break;504 505 578 default: 506 579 \wp_send_json_error( [ 'message' => \esc_html__( 'Invalid action.', 'progress-planner' ) ] ); -
progress-planner/trunk/classes/suggested-tasks/class-local-tasks-manager.php
r3217671 r3238385 10 10 use Progress_Planner\Suggested_Tasks\Local_Tasks\Local_Task_Factory; 11 11 use Progress_Planner\Suggested_Tasks\Local_Tasks\Providers\Content_Create; 12 use Progress_Planner\Suggested_Tasks\Local_Tasks\Providers\Content_ Update;12 use Progress_Planner\Suggested_Tasks\Local_Tasks\Providers\Content_Review; 13 13 use Progress_Planner\Suggested_Tasks\Local_Tasks\Providers\Core_Update; 14 use Progress_Planner\Suggested_Tasks\Local_Tasks\Providers\Core_Blogdescription; 14 15 use Progress_Planner\Suggested_Tasks\Local_Tasks\Providers\Settings_Saved; 15 16 use Progress_Planner\Suggested_Tasks\Local_Tasks\Providers\Debug_Display; 17 use Progress_Planner\Suggested_Tasks\Local_Tasks\Providers\Sample_Page; 18 use Progress_Planner\Suggested_Tasks\Local_Tasks\Providers\Hello_World; 19 use Progress_Planner\Suggested_Tasks\Local_Tasks\Providers\Core_Siteicon; 16 20 17 21 /** … … 45 49 $this->task_providers = [ 46 50 new Content_Create(), 47 new Content_ Update(),51 new Content_Review(), 48 52 new Core_Update(), 53 new Core_Blogdescription(), 49 54 new Settings_Saved(), 55 new Debug_Display(), 56 new Sample_Page(), 57 new Hello_World(), 58 new Core_Siteicon(), 50 59 ]; 51 60 … … 88 97 * Get a task provider by its type. 89 98 * 90 * @param string $provider_ type The provider type.99 * @param string $provider_id The provider ID. 91 100 * 92 101 * @return \Progress_Planner\Suggested_Tasks\Local_Tasks\Providers\Local_Tasks_Interface|null 93 102 */ 94 public function get_task_provider( $provider_ type) {103 public function get_task_provider( $provider_id ) { 95 104 foreach ( $this->task_providers as $provider_instance ) { 96 if ( $provider_instance->get_provider_ type() === $provider_type) {105 if ( $provider_instance->get_provider_id() === $provider_id ) { 97 106 return $provider_instance; 98 107 } … … 156 165 public function evaluate_task( $task_id ) { 157 166 $task_object = ( new Local_Task_Factory( $task_id ) )->get_task(); 158 $task_provider = $this->get_task_provider( $task_object->get_provider_ type() );167 $task_provider = $this->get_task_provider( $task_object->get_provider_id() ); 159 168 160 169 if ( ! $task_provider ) { … … 174 183 public function get_task_details( $task_id ) { 175 184 $task_object = ( new Local_Task_Factory( $task_id ) )->get_task(); 176 $task_provider = $this->get_task_provider( $task_object->get_provider_ type() );185 $task_provider = $this->get_task_provider( $task_object->get_provider_id() ); 177 186 178 187 if ( ! $task_provider ) { … … 262 271 $task_data = $task_object->get_data(); 263 272 273 // If the task was already completed, remove it. 274 if ( true === \progress_planner()->get_suggested_tasks()->was_task_completed( $task_data['task_id'] ) ) { 275 return false; 276 } 277 264 278 if ( isset( $task_data['year_week'] ) ) { 265 279 return \gmdate( 'YW' ) === $task_data['year_week']; 266 280 } 267 281 282 // We have changed type name, so we need to remove all tasks of the old type. 283 if ( isset( $task_data['type'] ) && 'update-post' === $task_data['type'] ) { 284 return false; 285 } 286 268 287 return true; 269 288 } -
progress-planner/trunk/classes/suggested-tasks/class-remote-tasks.php
r3226821 r3238385 41 41 continue; 42 42 } 43 44 // If the task with this id is completed, don't add a task. 45 if ( true === \progress_planner()->get_suggested_tasks()->was_task_completed( "remote-task-{$item['task_id']}" ) ) { 46 continue; 47 } 48 49 // TODO: Maybe skip task which don't have type defined (to not allow wrongly defined 3rd party tasks to override default type). 50 $item['type'] = 'remote-' . ( isset( $item['type'] ) ? $item['type'] : 'default' ); 43 51 $item['task_id'] = "remote-task-{$item['task_id']}"; 44 52 $items[] = $item; -
progress-planner/trunk/classes/suggested-tasks/local-tasks/class-local-task-factory.php
r3217671 r3238385 38 38 // Parse simple format, e.g. 'update-core-202449'. 39 39 if ( ! str_contains( $this->task_id, '|' ) ) { 40 40 41 $last_pos = strrpos( $this->task_id, '-' ); 41 if ( false === $last_pos ) { 42 return new Task_Local( [ 'task_id' => $this->task_id ] ); 42 43 // Check if the task ID ends with a '-12345' or not. 44 if ( $last_pos === false || ! preg_match( '/-\d+$/', $this->task_id ) ) { 45 return new Task_Local( 46 [ 47 'task_id' => $this->task_id, 48 'type' => $this->task_id, 49 ] 50 ); 43 51 } 44 52 … … 50 58 return new Task_Local( 51 59 [ 60 'task_id' => $this->task_id, 52 61 'type' => $type, 53 62 $task_suffix_key => $task_suffix, -
progress-planner/trunk/classes/suggested-tasks/local-tasks/class-task-local.php
r3210975 r3238385 48 48 /** 49 49 * Alias for get_type(). 50 * For compatibility with the old provider system. 50 51 * 51 52 * @return string 52 53 */ 53 public function get_provider_ type() {54 public function get_provider_id() { 54 55 return $this->get_type(); 55 56 } -
progress-planner/trunk/classes/suggested-tasks/local-tasks/providers/class-content-abstract.php
r3210975 r3238385 19 19 */ 20 20 protected $capability = 'edit_others_posts'; 21 22 /** 23 * The provider type. 24 * 25 * @var string 26 */ 27 const TYPE = 'writing'; 21 28 22 29 /** -
progress-planner/trunk/classes/suggested-tasks/local-tasks/providers/class-content-create.php
r3210975 r3238385 20 20 * @var string 21 21 */ 22 const TYPE = 'create-post'; 22 const ID = 'create-post'; 23 24 /** 25 * The provider type. 26 * 27 * @var string 28 */ 29 const TYPE = 'content-new'; 23 30 24 31 /** … … 28 35 */ 29 36 const ITEMS_TO_INJECT = 2; 30 31 /**32 * Get the provider ID.33 *34 * @return string35 */36 public function get_provider_type() {37 return self::TYPE;38 }39 37 40 38 /** … … 88 86 89 87 // If the task with this length and id is completed, don't add a task. 90 if ( true === \progress_planner()->get_suggested_tasks()->check_task_condition( 91 [ 92 'type' => 'completed', 93 'task_id' => $task_id, 94 ] 95 ) ) { 88 if ( true === \progress_planner()->get_suggested_tasks()->was_task_completed( $task_id ) ) { 96 89 return []; 97 90 } … … 157 150 * @return array 158 151 */ 159 public function get_task_details( $task_id ) { 152 public function get_task_details( $task_id = '' ) { 153 154 if ( ! $task_id ) { 155 return []; 156 } 160 157 161 158 $data = $this->get_data_from_task_id( $task_id ); … … 168 165 'parent' => 0, 169 166 'priority' => 'medium', 170 'type' => 'writing',167 'type' => $this->get_provider_type(), 171 168 'points' => isset( $data['long'] ) && $data['long'] ? 2 : 1, 172 169 'url' => \esc_url( \admin_url( 'post-new.php?post_type=post' ) ), -
progress-planner/trunk/classes/suggested-tasks/local-tasks/providers/class-core-update.php
r3217671 r3238385 12 12 * Add tasks for Core updates. 13 13 */ 14 class Core_Update extends Local_ Tasks_Abstract {14 class Core_Update extends Local_Repetitive_Tasks_Abstract { 15 15 16 16 /** … … 22 22 23 23 /** 24 * The provider type. 25 * 26 * @var string 27 */ 28 const TYPE = 'maintenance'; 29 30 /** 24 31 * The provider ID. 25 32 * 26 33 * @var string 27 34 */ 28 const TYPE = 'update-core'; 29 30 /** 31 * Get the provider ID. 32 * 33 * @return string 34 */ 35 public function get_provider_type() { 36 return self::TYPE; 37 } 35 const ID = 'update-core'; 38 36 39 37 /** … … 44 42 * @return bool|string 45 43 */ 46 public function evaluate_task( $task_id ) {47 44 48 // Early bail if the user does not have the capability to update the core, since \wp_get_update_data()['counts']['total'] will return 0. 49 if ( ! $this->capability_required() ) { 50 return false; 51 } 52 45 /** 46 * Check if the task condition is met. 47 * 48 * @return bool 49 */ 50 public function check_task_condition() { 53 51 // Without this \wp_get_update_data() might not return correct data for the core updates (depending on the timing). 54 52 if ( ! function_exists( 'get_core_updates' ) ) { … … 56 54 } 57 55 58 $task_object = ( new Local_Task_Factory( $task_id ) )->get_task(); 59 $task_data = $task_object->get_data(); 60 61 if ( $task_data['type'] === self::TYPE && \gmdate( 'YW' ) === $task_data['year_week'] && 0 === \wp_get_update_data()['counts']['total'] ) { 62 return $task_id; 63 } 64 return false; 65 } 66 67 /** 68 * Get an array of tasks to inject. 69 * 70 * @return array 71 */ 72 public function get_tasks_to_inject() { 73 74 // Early bail if the user does not have the capability to update the core or if the task is snoozed. 75 if ( true === $this->is_task_type_snoozed() || ! $this->capability_required() ) { 76 return []; 77 } 78 79 // Without this \wp_get_update_data() might not return correct data for the core updates (depending on the timing). 80 if ( ! function_exists( 'get_core_updates' ) ) { 81 require_once ABSPATH . 'wp-admin/includes/update.php'; // @phpstan-ignore requireOnce.fileNotFound 82 } 83 84 // If all updates are performed, do not add the task. 85 if ( 0 === \wp_get_update_data()['counts']['total'] ) { 86 return []; 87 } 88 89 return [ 90 $this->get_task_details( self::TYPE . '-' . \gmdate( 'YW' ) ), 91 ]; 56 return 0 === \wp_get_update_data()['counts']['total'] ? true : false; 92 57 } 93 58 … … 101 66 public function get_task_details( $task_id ) { 102 67 68 if ( ! $task_id ) { 69 $task_id = $this->get_provider_id() . '-' . \gmdate( 'YW' ); 70 } 71 103 72 return [ 104 73 'task_id' => $task_id, … … 106 75 'parent' => 0, 107 76 'priority' => 'high', 108 'type' => 'maintenance',77 'type' => $this->get_provider_type(), 109 78 'points' => 1, 110 79 'url' => $this->capability_required() ? \esc_url( \admin_url( 'update-core.php' ) ) : '', … … 112 81 ]; 113 82 } 114 115 /**116 * Get the data from a task-ID.117 *118 * @param string $task_id The task ID.119 *120 * @return array The data.121 */122 public function get_data_from_task_id( $task_id ) {123 $data = [124 'type' => self::TYPE,125 'id' => $task_id,126 ];127 128 return $data;129 }130 131 /**132 * Check if a task type is snoozed.133 *134 * @return bool135 */136 public function is_task_type_snoozed() {137 $snoozed = \progress_planner()->get_suggested_tasks()->get_snoozed_tasks();138 if ( ! \is_array( $snoozed ) || empty( $snoozed ) ) {139 return false;140 }141 142 foreach ( $snoozed as $task ) {143 $task_object = ( new Local_Task_Factory( $task['id'] ) )->get_task();144 $task_data = $task_object->get_data();145 if ( $task_data['type'] === self::TYPE ) {146 return true;147 }148 }149 150 return false;151 }152 83 } -
progress-planner/trunk/classes/suggested-tasks/local-tasks/providers/class-local-tasks-abstract.php
r3210975 r3238385 9 9 10 10 use Progress_Planner\Suggested_Tasks\Local_Tasks\Providers\Local_Tasks_Interface; 11 use Progress_Planner\Suggested_Tasks\Local_Tasks\Local_Task_Factory; 11 12 12 13 /** … … 16 17 17 18 /** 19 * The type of the task. 20 * 21 * @var string 22 */ 23 const TYPE = ''; 24 25 /** 26 * The ID of the task. 27 * 28 * @var string 29 */ 30 const ID = ''; 31 32 /** 18 33 * The capability required to perform the task. 19 34 * … … 21 36 */ 22 37 protected $capability = 'manage_options'; 38 39 /** 40 * Get the provider type. 41 * 42 * @return string 43 */ 44 public function get_provider_type() { 45 return static::TYPE; 46 } 47 48 /** 49 * Get the provider ID. 50 * 51 * @return string 52 */ 53 public function get_provider_id() { 54 return static::ID; 55 } 23 56 24 57 /** … … 32 65 : true; 33 66 } 67 68 /** 69 * Get the data from a task-ID. 70 * 71 * @param string $task_id The task ID (unused here). 72 * 73 * @return array The data. 74 */ 75 public function get_data_from_task_id( $task_id ) { 76 $data = [ 77 'type' => $this->get_provider_id(), 78 'id' => $task_id, 79 ]; 80 81 return $data; 82 } 83 84 /** 85 * Check if a task type is snoozed. 86 * 87 * @return bool 88 */ 89 public function is_task_type_snoozed() { 90 $snoozed = \progress_planner()->get_suggested_tasks()->get_snoozed_tasks(); 91 if ( ! \is_array( $snoozed ) || empty( $snoozed ) ) { 92 return false; 93 } 94 95 foreach ( $snoozed as $task ) { 96 $task_object = ( new Local_Task_Factory( $task['id'] ) )->get_task(); 97 $provider_id = $task_object->get_provider_id(); 98 99 if ( $provider_id === $this->get_provider_id() ) { 100 return true; 101 } 102 } 103 104 return false; 105 } 34 106 } -
progress-planner/trunk/classes/suggested-tasks/local-tasks/providers/class-local-tasks-interface.php
r3210975 r3238385 48 48 49 49 /** 50 * Get the provider type. 51 * 52 * @return string 53 */ 54 public function get_provider_type(); 55 56 /** 50 57 * Get the provider ID. 51 58 * 52 59 * @return string 53 60 */ 54 public function get_provider_ type();61 public function get_provider_id(); 55 62 56 63 /** -
progress-planner/trunk/classes/suggested-tasks/local-tasks/providers/class-settings-saved.php
r3210975 r3238385 11 11 * Add tasks for settings saved. 12 12 */ 13 class Settings_Saved extends Local_Tasks_Abstract { 13 class Settings_Saved extends Local_OneTime_Tasks_Abstract { 14 15 /** 16 * The provider type. 17 * 18 * @var string 19 */ 20 const TYPE = 'configuration'; 14 21 15 22 /** … … 18 25 * @var string 19 26 */ 20 const TYPE= 'settings-saved';27 const ID = 'settings-saved'; 21 28 22 29 /** 23 * Get the provider ID.30 * Check if the task condition is met. 24 31 * 25 * @return string32 * @return bool 26 33 */ 27 public function get_provider_type() { 28 return self::TYPE; 29 } 30 31 /** 32 * Evaluate a task. 33 * 34 * @param string $task_id The task ID. 35 * 36 * @return bool|string 37 */ 38 public function evaluate_task( $task_id ) { 39 40 // Early bail if the user does not have the capability to manage options. 41 if ( ! $this->capability_required() ) { 42 return false; 43 } 44 45 if ( 0 === strpos( $task_id, self::TYPE ) && false !== \get_option( 'progress_planner_pro_license_key', false ) ) { 46 return $task_id; 47 } 48 return false; 49 } 50 51 /** 52 * Get an array of tasks to inject. 53 * 54 * @return array 55 */ 56 public function get_tasks_to_inject() { 57 58 // Early bail if the user does not have the capability to manage options or if the task is snoozed. 59 if ( true === $this->is_task_type_snoozed() || ! $this->capability_required() ) { 60 return []; 61 } 62 34 public function check_task_condition() { 63 35 $prpl_pro_license_key = \get_option( 'progress_planner_pro_license_key', false ); 64 36 65 if ( false !== $prpl_pro_license_key ) { 66 return []; 67 } 68 69 $task_id = self::TYPE . '-' . \gmdate( 'YW' ); 70 71 // If the task with this id is completed, don't add a task. 72 if ( true === \progress_planner()->get_suggested_tasks()->check_task_condition( 73 [ 74 'type' => 'completed', 75 'task_id' => $task_id, 76 ] 77 ) ) { 78 return []; 79 } 80 81 return [ 82 $this->get_task_details( self::TYPE . '-' . \gmdate( 'YW' ) ), 83 ]; 37 return false !== $prpl_pro_license_key ? true : false; 84 38 } 85 39 … … 91 45 * @return array 92 46 */ 93 public function get_task_details( $task_id ) { 47 public function get_task_details( $task_id = '' ) { 48 49 if ( ! $task_id ) { 50 $task_id = $this->get_provider_id(); 51 } 94 52 95 53 return [ … … 98 56 'parent' => 0, 99 57 'priority' => 'high', 100 'type' => 'maintenance',58 'type' => $this->get_provider_type(), 101 59 'points' => 1, 102 'url' => \current_user_can( 'manage_options') ? \esc_url( \admin_url( 'admin.php?page=progress-planner-settings' ) ) : '',60 'url' => $this->capability_required() ? \esc_url( \admin_url( 'admin.php?page=progress-planner-settings' ) ) : '', 103 61 'description' => '<p>' . \esc_html__( 'Head over to the settings page and fill in the required information.', 'progress-planner' ) . '</p>', 104 62 ]; 105 63 } 106 107 /**108 * Get the data from a task-ID.109 *110 * @param string $task_id The task ID.111 *112 * @return array The data.113 */114 public function get_data_from_task_id( $task_id ) {115 $data = [116 'type' => self::TYPE,117 'id' => $task_id,118 ];119 120 return $data;121 }122 123 /**124 * Check if a task type is snoozed.125 *126 * @return bool127 */128 public function is_task_type_snoozed() {129 $snoozed = \progress_planner()->get_suggested_tasks()->get_snoozed_tasks();130 if ( ! \is_array( $snoozed ) || empty( $snoozed ) ) {131 return false;132 }133 134 foreach ( $snoozed as $task ) {135 if ( self::TYPE === $task['id'] ) {136 return true;137 }138 }139 140 return false;141 }142 64 } -
progress-planner/trunk/classes/widgets/class-activity-scores.php
r3226821 r3238385 8 8 namespace Progress_Planner\Widgets; 9 9 10 use Progress_Planner\Widget;11 10 use Progress_Planner\Goals\Goal_Recurring; 12 11 use Progress_Planner\Goals\Goal; -
progress-planner/trunk/classes/widgets/class-badge-streak.php
r3210975 r3238385 7 7 8 8 namespace Progress_Planner\Widgets; 9 10 use Progress_Planner\Widget;11 9 12 10 /** -
progress-planner/trunk/classes/widgets/class-challenge.php
r3226821 r3238385 11 11 * Challenge class. 12 12 */ 13 final class Challenge extends \Progress_Planner\Widget {13 final class Challenge extends Widget { 14 14 15 15 /** -
progress-planner/trunk/classes/widgets/class-latest-badge.php
r3210975 r3238385 7 7 8 8 namespace Progress_Planner\Widgets; 9 10 use Progress_Planner\Widget;11 9 12 10 /** -
progress-planner/trunk/classes/widgets/class-published-content.php
r3210975 r3238385 7 7 8 8 namespace Progress_Planner\Widgets; 9 10 use Progress_Planner\Widget;11 9 12 10 /** -
progress-planner/trunk/classes/widgets/class-suggested-tasks.php
r3217671 r3238385 8 8 namespace Progress_Planner\Widgets; 9 9 10 use Progress_Planner\Widget;11 10 use Progress_Planner\Badges\Monthly; 12 11 use Progress_Planner\Suggested_Tasks\Local_Tasks\Local_Task_Factory; … … 23 22 */ 24 23 protected $id = 'suggested-tasks'; 24 25 /** 26 * The tasks. 27 * 28 * @var array|null 29 */ 30 protected $pending_tasks = null; 25 31 26 32 /** … … 55 61 56 62 $pending_celebration = \progress_planner()->get_suggested_tasks()->get_pending_celebration(); 63 $pending_tasks = $this->get_pending_tasks(); 57 64 $deps = [ 58 65 'progress-planner-todo', … … 63 70 64 71 // Check if need to load confetti. 65 if ( ! empty( $pending_celebration ) ) {72 if ( isset( $pending_tasks['content-update'] ) || ! empty( $pending_celebration ) ) { 66 73 $deps[] = 'particles-confetti'; 67 74 } else { … … 90 97 $handle = 'progress-planner-' . $this->id; 91 98 92 $current_screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;93 94 99 // Enqueue the script. 95 100 \wp_enqueue_script( $handle ); … … 99 104 100 105 // Get pending tasks. 101 $tasks['details'] = \progress_planner()->get_suggested_tasks()->get_tasks();106 $tasks['details'] = $this->get_pending_tasks(); 102 107 103 108 // Insert the pending celebration tasks as high priority tasks, so they are shown always. … … 105 110 106 111 $task_object = ( new Local_Task_Factory( $task_id ) )->get_task(); 107 $task_provider = \progress_planner()->get_suggested_tasks()->get_local()->get_task_provider( $task_object->get_provider_ type() );108 109 if ( $task_provider ->capability_required() ) {112 $task_provider = \progress_planner()->get_suggested_tasks()->get_local()->get_task_provider( $task_object->get_provider_id() ); 113 114 if ( $task_provider && $task_provider->capability_required() ) { 110 115 $task_details = \progress_planner()->get_suggested_tasks()->get_local()->get_task_details( $task_id ); 111 116 … … 113 118 $task_details['priority'] = 'high'; // Celebrate tasks are always on top. 114 119 $task_details['action'] = 'celebrate'; 115 $tasks['details'][] = $task_details; 120 $task_details['type'] = 'pending_celebration'; 121 122 if ( ! isset( $tasks['details']['pending_celebration'] ) ) { 123 $tasks['details']['pending_celebration'] = []; 124 } 125 126 $tasks['details']['pending_celebration'][] = $task_details; 116 127 } 117 128 … … 119 130 \progress_planner()->get_suggested_tasks()->transition_task_status( $task_id, 'pending_celebration', 'completed' ); 120 131 } 132 } 133 134 $max_items_per_type = []; 135 foreach ( $tasks['details'] as $type => $items ) { 136 $max_items_per_type[ $type ] = $type === 'content-update' ? 2 : 1; 137 } 138 139 // We want all pending_celebration' tasks to be shown. 140 if ( isset( $max_items_per_type['pending_celebration'] ) ) { 141 $max_items_per_type['pending_celebration'] = 99; 142 } 143 144 // Check if current date is between Feb 12-16. 145 $confetti_options = []; 146 $year = \gmdate( 'Y' ); 147 $current_date = $year . '-' . \gmdate( 'm-d' ); 148 149 // TODO: GET params just for testing. 150 $start_date = $year . '-' . ( isset( $_GET['start_date'] ) ? \sanitize_text_field( \wp_unslash( $_GET['start_date'] ) ) : '02-12' ); 151 $end_date = $year . '-' . ( isset( $_GET['end_date'] ) ? \sanitize_text_field( \wp_unslash( $_GET['end_date'] ) ) : '02-16' ); 152 153 if ( $current_date >= $start_date && $current_date <= $end_date ) { 154 $confetti_options = [ 155 [ 156 'particleCount' => 50, 157 'scalar' => 2.2, 158 'shapes' => [ 'heart' ], 159 'colors' => [ 'FFC0CB', 'FF69B4', 'FF1493', 'C71585' ], 160 ], 161 [ 162 'particleCount' => 20, 163 'scalar' => 3.2, 164 'shapes' => [ 'heart' ], 165 'colors' => [ 'FFC0CB', 'FF69B4', 'FF1493', 'C71585' ], 166 ], 167 ]; 121 168 } 122 169 … … 126 173 'progressPlannerSuggestedTasks', 127 174 [ 128 'ajaxUrl' => \admin_url( 'admin-ajax.php' ), 129 'nonce' => \wp_create_nonce( 'progress_planner' ), 130 'tasks' => $tasks, 131 'maxItems' => $current_screen && 'dashboard' === $current_screen->id ? 3 : 5, 175 'ajaxUrl' => \admin_url( 'admin-ajax.php' ), 176 'nonce' => \wp_create_nonce( 'progress_planner' ), 177 'tasks' => $tasks, 178 'maxItemsPerType' => apply_filters( 'progress_planner_suggested_tasks_max_items_per_type', $max_items_per_type ), 179 'confettiOptions' => $confetti_options, 132 180 ] 133 181 ); 134 182 } 183 184 /** 185 * Get the tasks. 186 * 187 * @return array The tasks. 188 */ 189 public function get_pending_tasks() { 190 191 if ( null === $this->pending_tasks ) { 192 $tasks = []; 193 $pending_tasks = \progress_planner()->get_suggested_tasks()->get_tasks(); 194 195 // Sort them by type (channel). 196 foreach ( $pending_tasks as $task ) { 197 198 if ( ! isset( $tasks[ $task['type'] ] ) ) { 199 $tasks[ $task['type'] ] = []; 200 } 201 202 $tasks[ $task['type'] ][] = $task; 203 } 204 205 $this->pending_tasks = $tasks; 206 } 207 208 return $this->pending_tasks; 209 } 135 210 } -
progress-planner/trunk/classes/widgets/class-todo.php
r3210975 r3238385 7 7 8 8 namespace Progress_Planner\Widgets; 9 10 use Progress_Planner\Widget;11 9 12 10 /** -
progress-planner/trunk/classes/widgets/class-whats-new.php
r3210975 r3238385 8 8 namespace Progress_Planner\Widgets; 9 9 10 use Progress_Planner\Widget;11 10 use Progress_Planner\Cache; 12 11 -
progress-planner/trunk/progress-planner.php
r3226821 r3238385 10 10 * Requires at least: 6.3 11 11 * Requires PHP: 7.4 12 * Version: 1.0. 312 * Version: 1.0.4 13 13 * Author: Team Emilia Projects 14 14 * Author URI: https://prpl.fyi/about -
progress-planner/trunk/readme.txt
r3226821 r3238385 5 5 Tested up to: 6.7 6 6 Requires PHP: 7.4 7 Stable tag: 1.0. 37 Stable tag: 1.0.4 8 8 License: GPL3+ 9 9 License URI: https://www.gnu.org/licenses/gpl-3.0.en.html … … 78 78 79 79 == Changelog == 80 81 = 1.0.4 = 82 83 Enhancements: 84 85 * We've moved Ravi's recommendations to the top left of your Progress Planner dashboard. They're the most important thing on there, so we wanted to give it prime placement. 86 * We changed "Update post" to "Review post" / "Review page" and [wrote better instructions for reviewing old posts and pages](https://progressplanner.com/recommendations/review-post/). These tasks now prioritize the most important pages, like your About page, Privacy policy, Contact page and FAQ page. 87 * Added an option to redirect users to the Progress Planner dashboard after login. The WordPress dashboard isn't particularly useful in our eyes, this mind entice you to action more. 88 * Added a plugin-deactivation feedback form (we tell you, because you'll never see it, right? :) ). 89 * Removed the celebration for "Perform all updates" if it was done by WordPress's automatic update. We all love confetti, but when it comes all the time without you doing anything, it loses its value, right? Hence this fix. 90 91 We've added the following Recommendations from Ravi: 92 93 * [Setting site icon](https://progressplanner.com/recommendations/set-a-site-icon-aka-favicon/). 94 * [Setting the tagline](https://progressplanner.com/recommendations/set-tagline/). 95 * [Deactivating the display of PHP debug messages](https://progressplanner.com/recommendations/set-wp-debug/). 96 * [Removing the default WP "Hello world" post](https://progressplanner.com/recommendations/delete-the-default-wordpress-hello-world-post/). 97 * [Removing the default WP "Sample page" page](https://progressplanner.com/recommendations/delete-the-default-wordpress-sample-page-post/). 98 99 Under the hood: 100 101 * Improvements to the REST-API endpoint for getting stats. 102 * Removed admin notices on the Progress Planner page. 80 103 81 104 = 1.0.3 = -
progress-planner/trunk/views/admin-page-settings.php
r3226821 r3238385 34 34 35 35 <form id="prpl-settings"> 36 <div class="prpl-column"> 37 <div class="prpl-widget-wrapper"> 38 <h2 class="prpl-settings-section-title"> 39 <span class="icon"> 40 <?php \progress_planner()->the_asset( 'images/icon_pages.svg' ); ?> 41 </span> 42 <span> 43 <?php esc_html_e( 'Your pages', 'progress-planner' ); ?> 44 </span> 45 </h2> 46 <p> 47 <?php esc_html_e( 'Let us know if you have following pages.', 'progress-planner' ); ?> 48 </p> 49 <div class="prpl-pages-list"> 50 <?php 51 foreach ( \progress_planner()->get_admin__page_settings()->get_settings() as $prpl_setting ) { 52 \progress_planner()->the_view( "setting/{$prpl_setting['type']}.php", [ 'prpl_setting' => $prpl_setting ] ); 53 } 54 ?> 55 </div> 56 </div> 57 </div> 58 59 <div class="prpl-column"> 60 <div class="prpl-widget-wrapper"> 61 <h2 class="prpl-settings-section-license"> 62 <span> 63 <?php \esc_html_e( 'License', 'progress-planner' ); ?> 64 </span> 65 </h2> 66 <div class="prpl-license-keys-wrapper"> 67 <?php 68 $prpl_pro_license = \get_option( 'progress_planner_pro_license_key', '' ); 69 $prpl_pro_license_status = \get_option( 'progress_planner_pro_license_status', '' ); 70 ?> 71 <?php if ( empty( $prpl_pro_license ) || 'valid' !== $prpl_pro_license_status ) : ?> 72 <p> 73 <?php 74 printf( 75 // translators: %s is a link to the Pro page, with the text "Progress Planner Pro". 76 \esc_html__( 'Take part in interactive challenges to solve website problems like broken links and sharpen your skills with in-context mini courses. Upgrade to %s!', 'progress-planner' ), 77 '<a href="https://progressplanner.com/pro/" target="_blank">Progress Planner Pro</a>' 78 ); 79 ?> 80 </p> 81 <?php endif; ?> 82 <label for="prpl-setting-pro-license-key"> 83 <?php \esc_html_e( 'Progress Planner Pro license key', 'progress-planner' ); ?> 84 </label> 85 <div class="prpl-license-key-wrapper"> 86 <input 87 id="prpl-setting-pro-license-key" 88 name="prpl-pro-license-key" 89 type="text" 90 value="<?php echo \esc_attr( $prpl_pro_license ); ?>" 91 /> 92 <?php if ( ! empty( $prpl_pro_license ) ) : ?> 93 <span class="prpl-license-status prpl-license-status-<?php echo ( 'valid' === $prpl_pro_license_status ) ? 'valid' : 'invalid'; ?>"> 94 <?php if ( 'valid' === $prpl_pro_license_status ) : ?> 95 <span class="prpl-license-status-valid" title="<?php esc_attr_e( 'Valid', 'progress-planner' ); ?>"> 96 <?php \progress_planner()->the_asset( 'images/icon_check_circle.svg' ); ?> 97 </span> 98 <?php else : ?> 99 <span class="prpl-license-status-invalid" title="<?php esc_attr_e( 'Invalid', 'progress-planner' ); ?>"> 100 <?php \progress_planner()->the_asset( 'images/icon_exclamation_circle.svg' ); ?> 101 </span> 102 <?php endif; ?> 103 </span> 104 <?php endif; ?> 105 </div> 106 </div> 107 </div> 108 </div> 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' ); ?> 109 39 110 40 <?php wp_nonce_field( 'prpl-settings' ); ?> -
progress-planner/trunk/views/page-widgets/parts/monthly-badges.php
r3226821 r3238385 35 35 <?php if ( $prpl_badges ) : ?> 36 36 <?php 37 $prpl_badges_per_row = 3;38 $prpl_badges_count = count( $prpl_badges );39 $prpl_scroll_to_row = 1;37 $prpl_badges_per_row = 3; 38 $prpl_badges_count = count( $prpl_badges ); 39 $prpl_scroll_to_row = 1; 40 40 $prpl_current_month_badge_id = Monthly::get_badge_id_from_date( new \DateTime() ); 41 41 if ( 'popover' !== $prpl_location ) { -
progress-planner/trunk/views/page-widgets/suggested-tasks.php
r3226821 r3238385 15 15 $prpl_badge = \progress_planner()->get_badges()->get_badge( Monthly::get_badge_id_from_date( new \DateTime() ) ); 16 16 ?> 17 18 19 <div class="prpl-dashboard-widget-suggested-tasks"> 20 <h2 class="prpl-widget-title"> 21 <?php \esc_html_e( 'Ravi\'s Recommendations', 'progress-planner' ); ?> 22 </h2> 23 24 <ul style="display:none"></ul> 25 <ul class="prpl-suggested-tasks-list"></ul> 26 27 <hr> 28 </div> 29 17 30 <?php if ( $prpl_badge ) : ?> 18 31 <h2 class="prpl-widget-title"> … … 44 57 </h2> 45 58 46 <prpl-gauge background="var(--prpl-background-orange)" color="var(--prpl-color-accent-orange)"> 59 <prpl-gauge 60 id="prpl-gauge-ravi" 61 background="var(--prpl-background-orange)" 62 color="var(--prpl-color-accent-orange)" 63 data-max="<?php echo (int) Monthly::TARGET_POINTS; ?>" 64 data-value="<?php echo (float) $prpl_widget->get_score(); ?>" 65 data-badge-id="<?php echo esc_attr( $prpl_badge->get_id() ); ?>" 66 > 47 67 <progress max="<?php echo (int) Monthly::TARGET_POINTS; ?>" value="<?php echo (float) $prpl_widget->get_score(); ?>"> 48 68 <prpl-badge complete="true" badge-id="<?php echo esc_attr( $prpl_badge->get_id() ); ?>"></prpl-badge> … … 52 72 <div class="prpl-widget-content-points"> 53 73 <span><?php \esc_html_e( 'Progress monthly badge', 'progress-planner' ); ?></span> 54 <span class="prpl-widget-content-points-number">74 <span id="prpl-widget-content-ravi-points-number" class="prpl-widget-content-points-number"> 55 75 <?php echo (int) $prpl_widget->get_score(); ?>pt 56 76 </span> … … 59 79 <hr> 60 80 <?php endif; ?> 61 62 <div class="prpl-dashboard-widget-suggested-tasks">63 <h2 class="prpl-widget-title">64 <?php \esc_html_e( 'Ravi\'s Recommendations', 'progress-planner' ); ?>65 </h2>66 67 <ul style="display:none"></ul>68 <ul class="prpl-suggested-tasks-list"></ul>69 70 <hr>71 </div>72 81 73 82 <div class="prpl-widget-content">
Note: See TracChangeset
for help on using the changeset viewer.