Plugin Directory

Changeset 3306880


Ignore:
Timestamp:
06/05/2025 08:05:29 AM (9 months ago)
Author:
progressplanner
Message:

Update to version 1.5.0 from GitHub

Location:
progress-planner
Files:
14 added
42 edited
1 copied

Legend:

Unmodified
Added
Removed
  • progress-planner/tags/1.5.0/CHANGELOG.md

    r3290442 r3306880  
     1= 1.5.0 =
     2
     3Added these recommendations from Ravi:
     4
     5* [Test if your website can send emails correctly](https://prpl.fyi/troubleshoot-smtp).
     6
     7Bugs we fixed:
     8
     9* Don't redirect user to Progress Planner dashboard if 'redirect_to' GET or POST parameter is set.
     10* Removed the Onboard tour steps for the Settings popover which was removed in 1.3.0.
     11* Fixed detecting creation of new valuable content posts.
     12* Don't award point for all Todo tasks, only for golden.
     13* Fix Todo task title not being editable.
     14
    115= 1.4.2 =
    216
  • progress-planner/tags/1.5.0/assets/css/admin.css

    r3283338 r3306880  
    363363.prpl-wrap input[type="search"] {
    364364    height: 40px;
    365     box-shadow: 1px 2px 4px 0 rgba(0, 0, 0, 0.25);
     365    box-shadow: 1px 2px 4px 0 rgba(0, 0, 0, 0.05);
    366366}
    367367
     
    473473    }
    474474}
     475
     476/*------------------------------------*\
     477    Layout for columns.
     478\*------------------------------------*/
     479.prpl-columns-wrapper {
     480    display: grid;
     481    grid-template-columns: repeat(2, 1fr);
     482    gap: var(--prpl-padding);
     483}
  • progress-planner/tags/1.5.0/assets/css/page-widgets/suggested-tasks.css

    r3283338 r3306880  
    145145        h3 {
    146146            font-style: italic;
     147        }
     148    }
     149
     150    .prpl-suggested-task {
     151
     152        .prpl-suggested-task-checkbox {
     153            flex-shrink: 0; /* Prevent shrinking on mobile */
    147154        }
    148155    }
     
    267274}
    268275
    269 
    270276#prpl-popover-monthly-badges-trigger {
    271277    font-size: var(--prpl-font-size-base);
     
    274280    margin-top: 0.75rem;
    275281}
     282
     283/*------------------------------------*\
     284    Interactive tasks, popover.
     285\*------------------------------------*/
     286.prpl-popover.prpl-popover-interactive {
     287    padding: 24px 24px 14px 24px; /* 14px is needed for the "next" button hover state. */
     288    box-sizing: border-box;
     289
     290    * {
     291        max-width: calc(100% - 1px);
     292    }
     293
     294    .prpl-columns-wrapper-flex {
     295        display: flex;
     296        flex-wrap: wrap;
     297        gap: 40px;
     298        overflow: hidden;
     299        padding-bottom: 10px; /* Needed for the "next" button hover state. */
     300
     301        > * {
     302            flex-grow: 1;
     303            flex-basis: 300px;
     304            position: relative;
     305
     306            &:not(:first-child) {
     307
     308                &::before,
     309                &::after {
     310                    content: "";
     311                    display: block;
     312                    position: absolute;
     313                    top: 0;
     314                    left: -20px;
     315                    width: 1px;
     316                    height: 100%;
     317                    background-color: var(--prpl-color-gray-2);
     318                }
     319
     320                &::after {
     321                    top: -20px;
     322                    left: 0;
     323                    width: 100%;
     324                    height: 1px;
     325                }
     326            }
     327        }
     328    }
     329
     330    .prpl-column {
     331
     332        /* Set margin for headings and paragraphs. */
     333        h1,
     334        h2,
     335        h3,
     336        h4,
     337        h5,
     338        h6 {
     339
     340            &:first-child {
     341                margin-top: 0;
     342            }
     343        }
     344
     345        p {
     346            margin-bottom: 1rem;
     347
     348            &:first-child {
     349                margin-top: 0;
     350            }
     351
     352            &:last-child {
     353                margin-bottom: 0;
     354            }
     355        }
     356
     357        .prpl-interactive-task-title {
     358            font-size: 18px;
     359            line-height: 22px;
     360
     361            & + p {
     362                margin-top: 4px;
     363            }
     364        }
     365
     366        /* Set padding and background color for content column (description text). */
     367        &.prpl-column-content {
     368            padding: 20px;
     369            border-radius: var(--prpl-border-radius-big);
     370            background-color: var(--prpl-background-purple);
     371        }
     372
     373        .prpl-note {
     374            margin-bottom: 1rem;
     375            display: flex;
     376            align-items: flex-start;
     377            gap: 0.5rem;
     378            padding: 0.75rem;
     379            color: #854d0e;
     380            font-size: var(--prpl-font-size-small);
     381            border-radius: 6px;
     382            background-color: #fefce8;
     383
     384            .prpl-note-icon {
     385                display: flex;
     386                flex-shrink: 0;
     387                align-items: center;
     388                justify-content: center;
     389                width: 20px;
     390                height: 20px;
     391                color: #eab308;
     392
     393                svg {
     394                    width: 100%;
     395                    height: 100%;
     396                }
     397            }
     398
     399            &.prpl-note-error {
     400                color: #9f0712;
     401                background-color: var(--prpl-background-red);
     402
     403                .prpl-note-icon {
     404                    color: var(--prpl-color-notification-red);
     405                }
     406
     407            }
     408        }
     409
     410        /* To align the buttons to the bottom of the column. */
     411        &:not(.prpl-column-content) {
     412            display: flex;
     413            flex-direction: column;
     414        }
     415
     416        /* Inputs. */
     417        input[type="text"],
     418        input[type="email"],
     419        input[type="number"],
     420        input[type="url"],
     421        input[type="tel"],
     422        input[type="search"] {
     423            height: 44px;
     424            padding: 1rem; /* WIP */
     425            width: 100%;
     426            min-width: 300px; /* WIP */
     427            border-radius: 6px;
     428            border: 1px solid var(--prpl-color-gray-2);
     429        }
     430
     431        .prpl-button {
     432            padding: 0.75rem 1.25rem;
     433            border: none;
     434            color: var(--prpl-color-gray-6);
     435            font-weight: 600;
     436            border-radius: var(--prpl-border-radius);
     437            background-color: var(--prpl-color-400-orange); /* WIP: pick exact color */
     438            transition: all 0.25s ease-in-out;
     439            position: relative;
     440
     441            &::after {
     442                content: "";
     443                display: block;
     444                width: 100%;
     445                height: 100%;
     446                background: var(--prpl-color-400-orange); /* WIP: pick exact color */
     447                position: absolute;
     448                top: 0;
     449                left: 0;
     450                z-index: -1;
     451                border-radius: 6px;
     452                transition: all 0.25s ease-in-out;
     453            }
     454
     455            &:hover,
     456            &:focus {
     457                background: var(--prpl-color-400-orange); /* WIP: pick exact color */
     458
     459                &::after {
     460                    background: var(--prpl-color-400-orange); /* WIP: pick exact color */
     461                    box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.15);
     462                    width: calc(100% + 4px);
     463                    height: calc(100% + 4px);
     464                    margin: -2px;
     465                }
     466            }
     467        }
     468
     469        textarea {
     470            width: 100%;
     471            min-height: 100px;
     472            border-radius: 6px;
     473            box-shadow: 1px 2px 4px 0 rgba(0, 0, 0, 0.05);
     474            border: 1px solid var(--prpl-color-gray-2);
     475        }
     476
     477        /* Used for radio and checkbox inputs. */
     478        .radios {
     479            padding-left: 3px; /* To prevent custom radio and checkbox from being cut off. */
     480            display: flex;
     481            flex-direction: column;
     482            gap: 0.5rem;
     483
     484            .prpl-radio-wrapper {
     485                display: flex;
     486                align-items: center;
     487            }
     488
     489            --prpl-input-green: #3bb3a6;
     490            --prpl-input-gray: #8b99a6;
     491
     492            /* Hide the default input, because WP has it's own styles (which include pseudo-elements). */
     493            .prpl-custom-checkbox input[type="checkbox"],
     494            .prpl-custom-radio input[type="radio"] {
     495                position: absolute;
     496                opacity: 0;
     497                width: 0;
     498                height: 0;
     499            }
     500
     501            /* Shared styles for the custom control */
     502            .prpl-custom-control {
     503                display: inline-block;
     504                vertical-align: middle;
     505                margin-right: 12px;
     506                width: 20px;
     507                height: 20px;
     508                box-sizing: border-box;
     509                position: relative;
     510                transition: border-color 0.2s, background 0.2s;
     511            }
     512
     513            /* Label text styling */
     514            .prpl-custom-checkbox,
     515            .prpl-custom-radio {
     516                display: flex;
     517                align-items: center;
     518                margin-bottom: 0.5rem;
     519                cursor: pointer;
     520                user-select: none;
     521            }
     522
     523            /* Checkbox styles */
     524            .prpl-custom-checkbox {
     525
     526                .prpl-custom-control {
     527                    border: 1px solid var(--prpl-input-gray);
     528                    border-radius: 6px;
     529                    background: #fff;
     530                }
     531
     532                input[type="checkbox"] {
     533
     534                    /* Checkbox hover (off) */
     535                    &:hover + .prpl-custom-control {
     536                        box-shadow: 0 0 0 2px #f7f8fa, 0 0 0 3px var(--prpl-input-green);
     537                    }
     538
     539                    /* Checkbox checked (on) */
     540                    &:checked + .prpl-custom-control {
     541                        background: var(--prpl-input-green);
     542                        border-color: var(--prpl-input-green);
     543                        box-shadow: 0 0 0 2px #f7f8fa, 0 0 0 3px var(--prpl-input-green);
     544                    }
     545                }
     546
     547
     548                /* Checkmark */
     549                .prpl-custom-control::after {
     550                    content: "";
     551                    position: absolute;
     552                    left: 6px;
     553                    top: 2px;
     554                    width: 4px;
     555                    height: 9px;
     556                    border: solid #fff;
     557                    border-width: 0 2px 2px 0;
     558                    opacity: 0;
     559                    transform: scale(0.8) rotate(45deg);
     560                    transition: opacity 0.2s, transform 0.2s;
     561                }
     562
     563                input[type="checkbox"]:checked + .prpl-custom-control::after {
     564                    opacity: 1;
     565                    transform: scale(1) rotate(45deg);
     566                }
     567            }
     568
     569            /* Radio styles */
     570            .prpl-custom-radio {
     571
     572                .prpl-custom-control {
     573                    border: 1px solid var(--prpl-input-gray);
     574                    border-radius: 50%;
     575                    background: #fff;
     576                }
     577
     578                /* Radio hover (off) */
     579                input[type="radio"] {
     580
     581                    &:hover + .prpl-custom-control {
     582                        box-shadow: 0 0 0 2px #f7f8fa, 0 0 0 3px var(--prpl-input-green);
     583                    }
     584
     585                    /* Radio checked (on) */
     586                    &:checked + .prpl-custom-control {
     587                        background: var(--prpl-input-green);
     588                        border-color: var(--prpl-input-green);
     589                        box-shadow: 0 0 0 2px #f7f8fa, 0 0 0 3px var(--prpl-input-green);
     590                    }
     591                }
     592
     593                /* Radio dot */
     594                .prpl-custom-control::after {
     595                    content: "";
     596                    position: absolute;
     597                    top: 5px;
     598                    left: 5px;
     599                    width: 8px;
     600                    height: 8px;
     601                    background: #fff;
     602                    border-radius: 50%;
     603                    opacity: 0;
     604                    transition: opacity 0.2s;
     605                }
     606
     607                input[type="radio"]:checked + .prpl-custom-control::after {
     608                    opacity: 1;
     609                    background: #fff;
     610                }
     611            }
     612        }
     613
     614        /* Used for next step button. */
     615        .prpl-steps-nav-wrapper {
     616            margin-top: auto;
     617            padding-top: 1rem;
     618            display: flex;
     619            justify-content: flex-end;
     620            gap: 1rem;
     621            align-self: flex-end;
     622            width: 100%;
     623
     624            .prpl-button {
     625                cursor: pointer;
     626
     627                /* If the button has empty data-action attribute disable it. */
     628                &[data-action=""] {
     629                    pointer-events: none;
     630                    opacity: 0.5;
     631                }
     632            }
     633
     634        }
     635    }
     636}
  • progress-planner/tags/1.5.0/assets/css/settings-page.css

    r3283338 r3306880  
    294294            max-width: calc(100% - 2rem);
    295295            border: 1px solid var(--prpl-color-gray-2);
    296             box-shadow: 1px 2px 4px 0 rgba(0, 0, 0, 0.05);
    297296        }
    298297    }
  • progress-planner/tags/1.5.0/assets/js/external-link-accessibility-helper.js

    r3289779 r3306880  
    4343    // Private: check and decorate one link
    4444    _processLink( link ) {
    45         if ( link.dataset.prpl_accessibility_enhanced === 'true' ) return;
     45        if ( link.dataset.prpl_accessibility_enhanced === 'true' ) {
     46            return;
     47        }
    4648
    4749        const url = new URL( link.href, window.location.href );
  • progress-planner/tags/1.5.0/assets/js/tour.js

    r3264985 r3306880  
    3838        { config, state } // eslint-disable-line no-unused-vars
    3939    ) => {
    40         const settingsPopover = document.getElementById(
    41             'prpl-popover-settings'
    42         );
    4340        const monthlyBadgesPopover = document.getElementById(
    4441            'prpl-popover-monthly-badges'
    4542        );
    46 
    47         if ( state.activeIndex === 4 ) {
    48             prplTourShowPopover( settingsPopover );
    49         }
    50 
    51         if ( state.activeIndex === 7 ) {
     43        if ( state.activeIndex === 5 ) {
    5244            prplTourShowPopover( monthlyBadgesPopover );
    5345        }
     
    8678// eslint-disable-next-line no-unused-vars -- This is called on a few buttons.
    8779function prplStartTour() {
    88     const settingsPopover = document.getElementById( 'prpl-popover-settings' );
    8980    const monthlyBadgesPopover = document.getElementById(
    9081        'prpl-popover-monthly-badges'
    9182    );
    9283    const progressPlannerTourSteps = progressPlannerTour.steps;
    93     progressPlannerTourSteps[ 3 ].popover.onNextClick = function () {
    94         prplTourShowPopover( settingsPopover );
    95         prplDriverObj.moveNext();
    96     };
     84
    9785    progressPlannerTourSteps[ 4 ].popover.onNextClick = function () {
    98         prplTourHidePopover( settingsPopover );
    99         prplDriverObj.moveNext();
    100     };
    101 
    102     progressPlannerTourSteps[ 6 ].popover.onNextClick = function () {
    10386        prplTourShowPopover( monthlyBadgesPopover );
    10487        prplDriverObj.moveNext();
    10588    };
    106     progressPlannerTourSteps[ 7 ].popover.onNextClick = function () {
     89    progressPlannerTourSteps[ 5 ].popover.onNextClick = function () {
    10790        prplTourHidePopover( monthlyBadgesPopover );
    10891        prplDriverObj.moveNext();
  • progress-planner/tags/1.5.0/assets/js/web-components/prpl-suggested-task.js

    r3289779 r3306880  
    3131            useCheckbox = true,
    3232            taskList = '', // prplSuggestedTasks or progressPlannerTodo.
     33            popover_id,
    3334        } ) {
    3435            // Get parent class properties
     
    4041            if ( url ) {
    4142                taskHeading = `<a href="${ url }" target="${ url_target }">${ title }</a>`;
     43            }
     44
     45            if ( popover_id ) {
     46                taskHeading = `<a href="#" role="button" onclick="document.getElementById('${ popover_id }')?.showPopover()">${ title }</a>`;
    4247            }
    4348
     
    213218                    }
    214219
    215                     output += `<input
     220                    output += `<label><input
    216221                        type="checkbox"
    217                         id="prpl-suggested-task-checkbox-${ task_id }"
    218222                        class="prpl-suggested-task-checkbox"
    219223                        style="${ checkboxStyle }"
    220224                        ${ ! dismissable ? 'disabled' : '' }
    221225                        ${ getTaskStatus() === 'completed' ? 'checked' : '' }
    222                     >`;
     226                    ><span class="screen-reader-text">${ taskHeading }: ${ prplL10n(
     227                        'markAsComplete'
     228                    ) }</span></label>`;
    223229
    224230                    if ( ! dismissable ) {
     
    256262                ${ actionButtons.completeCheckbox }
    257263                <h3 style="width: 100%;">
    258                     ${
    259                         useCheckbox
    260                             ? `<label for="prpl-suggested-task-checkbox-${ task_id }">`
    261                             : ''
    262                     }
    263264                    <span${
    264265                        'user' === category
     
    266267                            : ''
    267268                    }>${ taskHeading }</span>
    268                     ${ useCheckbox && dismissable ? `</label>` : '' }
    269269                </h3>
    270270                <div class="prpl-suggested-task-actions">
     
    478478                                } )
    479479                            );
     480
     481                            h3Span
     482                                .closest( '.prpl-suggested-task' )
     483                                .querySelector(
     484                                    'label:has(.prpl-suggested-task-checkbox) .screen-reader-text'
     485                                ).innerHTML = title;
    480486                        } );
    481487                }, 300 );
  • progress-planner/tags/1.5.0/classes/activities/class-suggested-task.php

    r3289779 r3306880  
    7070
    7171        if ( ! empty( $tasks ) && isset( $tasks[0]['provider_id'] ) ) {
    72             $task_provider = \progress_planner()->get_suggested_tasks()->get_tasks_manager()->get_task_provider( $tasks[0]['provider_id'] );
     72            if ( 'user' === $tasks[0]['provider_id'] ) {
     73                $points = isset( $tasks[0]['points'] ) ? (int) $tasks[0]['points'] : 0;
     74            } else {
     75                $task_provider = \progress_planner()->get_suggested_tasks()->get_tasks_manager()->get_task_provider( $tasks[0]['provider_id'] );
    7376
    74             if ( $task_provider ) {
    75                 // Create post task provider had a different points system, this is for backwards compatibility.
    76                 $points = $task_provider instanceof Content_Create ? $task_provider->get_points( $this->data_id ) : $task_provider->get_points();
     77                if ( $task_provider ) {
     78                    // Create post task provider had a different points system, this is for backwards compatibility.
     79                    $points = $task_provider instanceof Content_Create ? $task_provider->get_points( $this->data_id ) : $task_provider->get_points();
     80                }
    7781            }
    7882        }
  • progress-planner/tags/1.5.0/classes/admin/class-tour.php

    r3264985 r3306880  
    5050                    'title'       => \esc_html__( 'Longterm activity score', 'progress-planner' ),
    5151                    'description' => \esc_html__( "Here, we show you your longterm activity score. This shows whether you've been active on your website over a longer period of time.", 'progress-planner' ),
    52                     'side'        => 'top',
    53                     'align'       => 'center',
    54                 ],
    55             ],
    56             [
    57                 'element' => '#prpl-popover-settings-trigger',
    58                 'popover' => [
    59                     'title'       => \esc_html__( 'Settings', 'progress-planner' ),
    60                     'description' => \esc_html__( 'With this button you can open the settings. Here you can determine which post types you want to include in your activity score.', 'progress-planner' ),
    61                     'side'        => 'top',
    62                     'align'       => 'center',
    63                 ],
    64             ],
    65             [
    66                 'element' => '#prpl-popover-settings',
    67                 'popover' => [
    68                     'title'       => \esc_html__( 'Post types', 'progress-planner' ),
    69                     'description' => \esc_html__( 'Simply select the post types you want to include in your activity score and hit save.', 'progress-planner' ),
    7052                    'side'        => 'top',
    7153                    'align'       => 'center',
  • progress-planner/tags/1.5.0/classes/class-base.php

    r3283338 r3306880  
    466466        }
    467467
     468        if ( isset( $_REQUEST['redirect_to'] ) && '' !== $_REQUEST['redirect_to'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Missing -- We're not processing any data.
     469            return;
     470        }
     471
    468472        // Redirect to the Progress Planner dashboard.
    469473        \wp_safe_redirect( \admin_url( 'admin.php?page=progress-planner' ) );
  • progress-planner/tags/1.5.0/classes/class-plugin-upgrade-tasks.php

    r3289779 r3306880  
    2525
    2626        // Check if the plugin was upgraded or new plugin was activated.
    27         \add_action( 'init', [ $this, 'handle_activation_or_upgrade' ], 100 ); // We need to run this after the Tasks_Manager::init() is called.
     27        \add_action( 'init', [ $this, 'handle_activation_or_upgrade' ], 100 ); // We need to run this after the Local_Tasks_Manager::init() is called.
     28
     29        // Add the action to add the upgrade tasks popover.
     30        \add_action( 'progress_planner_admin_page_after_widgets', [ $this, 'add_upgrade_tasks_popover' ] );
    2831    }
    2932
     
    148151        \delete_option( 'progress_planner_upgrade_popover_task_provider_ids' );
    149152    }
     153
     154    /**
     155     * Add the upgrade tasks popover.
     156     *
     157     * @return void
     158     */
     159    public function add_upgrade_tasks_popover() {
     160        if ( $this->should_show_upgrade_popover() ) {
     161            \progress_planner()->get_ui__popover()->the_popover( 'upgrade-tasks' )->render();
     162        }
     163    }
    150164}
  • progress-planner/tags/1.5.0/classes/class-suggested-tasks.php

    r3289779 r3306880  
    3636        if ( \is_admin() ) {
    3737            \add_action( 'init', [ $this, 'init' ], 100 ); // Wait for the post types to be initialized.
     38
     39            // Check GET parameter and maybe set task as pending celebration.
     40            \add_action( 'init', [ $this, 'maybe_complete_task' ] );
    3841        }
    3942
     
    532535
    533536    /**
     537     * Maybe complete a task.
     538     * Primarly this is used for deeplinking, ie user is testing if the emails are working
     539     * He gets an email with a link which automatically completes the task.
     540     *
     541     * @return void
     542     */
     543    public function maybe_complete_task() {
     544        if ( ! \progress_planner()->is_on_progress_planner_dashboard_page() || ! isset( $_GET['prpl_complete_task'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
     545            return;
     546        }
     547
     548        $task_id = \sanitize_text_field( \wp_unslash( $_GET['prpl_complete_task'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
     549        if ( ! $task_id ) {
     550            return;
     551        }
     552
     553        if ( ! $this->was_task_completed( $task_id ) ) {
     554            $this->mark_task_as( 'pending_celebration', $task_id );
     555
     556            // Insert an activity.
     557            $this->insert_activity( $task_id );
     558        }
     559    }
     560
     561    /**
    534562     * Handle the suggested task action.
    535563     *
     
    556584                // Insert an activity.
    557585                $this->insert_activity( $task_id );
     586
    558587                $updated = true;
    559588                break;
  • progress-planner/tags/1.5.0/classes/suggested-tasks/class-tasks-manager.php

    r3289779 r3306880  
    2828use Progress_Planner\Suggested_Tasks\Providers\Integrations\Yoast\Add_Yoast_Providers;
    2929use Progress_Planner\Suggested_Tasks\Providers\User as User_Tasks;
     30use Progress_Planner\Suggested_Tasks\Providers\Interactive\Email_Sending;
    3031use Progress_Planner\Suggested_Tasks\Providers\Set_Valuable_Post_Types;
    3132use Progress_Planner\Suggested_Tasks\Providers\Fewer_Tags;
     
    6869            new Search_Engine_Visibility(),
    6970            new User_Tasks(),
     71            new Email_Sending(),
    7072            new Set_Valuable_Post_Types(),
    7173            new Remove_Terms_Without_Posts(),
  • progress-planner/tags/1.5.0/classes/suggested-tasks/data-collector/class-last-published-post.php

    r3283338 r3306880  
    3535     */
    3636    public function init() {
    37         \add_action( 'init', [ $this, 'set_include_post_types' ], 99 ); // Wait for all CPTs to be registered.
     37        \add_action( 'init', [ $this, 'set_include_post_types' ], 100 ); // Wait for all CPTs to be registered and collector manager to trigger it's init method (which is done on priority 99).
    3838        \add_action( 'transition_post_status', [ $this, 'update_last_published_post_cache' ], 10, 3 );
    3939    }
  • progress-planner/tags/1.5.0/classes/suggested-tasks/providers/integrations/yoast/class-cornerstone-workout.php

    r3290442 r3306880  
    88namespace Progress_Planner\Suggested_Tasks\Providers\Integrations\Yoast;
    99
    10 use Progress_Planner\Suggested_Tasks\Providers\Tasks;
     10use Progress_Planner\Suggested_Tasks\Providers\Integrations\Yoast\Yoast_Provider;
    1111use Progress_Planner\Suggested_Tasks\Providers\Traits\Dismissable_Task;
    1212
     
    1414 * Add tasks for Yoast SEO cornerstone content.
    1515 */
    16 class Cornerstone_Workout extends Tasks {
     16class Cornerstone_Workout extends Yoast_Provider {
    1717    use Dismissable_Task;
    1818
     
    3030     */
    3131    protected const PROVIDER_ID = 'yoast-cornerstone-workout';
    32 
    33     /**
    34      * The provider category.
    35      *
    36      * @var string
    37      */
    38     protected const CATEGORY = 'configuration';
    3932
    4033    /**
  • progress-planner/tags/1.5.0/classes/suggested-tasks/providers/integrations/yoast/class-orphaned-content-workout.php

    r3290442 r3306880  
    88namespace Progress_Planner\Suggested_Tasks\Providers\Integrations\Yoast;
    99
    10 use Progress_Planner\Suggested_Tasks\Providers\Tasks;
     10use Progress_Planner\Suggested_Tasks\Providers\Integrations\Yoast\Yoast_Provider;
    1111use Progress_Planner\Suggested_Tasks\Providers\Traits\Dismissable_Task;
    1212
     
    1414 * Add tasks for Yoast SEO cornerstone content.
    1515 */
    16 class Orphaned_Content_Workout extends Tasks {
     16class Orphaned_Content_Workout extends Yoast_Provider {
    1717    use Dismissable_Task;
    1818
     
    3030     */
    3131    protected const PROVIDER_ID = 'yoast-orphaned-content-workout';
    32 
    33     /**
    34      * The provider category.
    35      *
    36      * @var string
    37      */
    38     protected const CATEGORY = 'configuration';
    3932
    4033    /**
  • progress-planner/tags/1.5.0/classes/utils/class-debug-tools.php

    r3289779 r3306880  
    4747        \add_action( 'init', [ $this, 'check_delete_badges' ] );
    4848        \add_action( 'init', [ $this, 'check_toggle_migrations' ] );
     49        \add_action( 'init', [ $this, 'check_delete_single_task' ] );
    4950
    5051        // Add filter to modify the maximum number of suggested tasks to display.
     
    252253                }
    253254
     255                // Add delete button.
     256                $delete_url = add_query_arg(
     257                    [
     258                        'prpl_delete_single_task' => $task['task_id'],
     259                        '_wpnonce'                => wp_create_nonce( 'prpl_debug_tools' ),
     260                    ],
     261                    $this->current_url
     262                );
     263
    254264                $admin_bar->add_node(
    255265                    [
    256266                        'id'     => 'prpl-suggested-' . $key . '-' . $title,
    257267                        'parent' => 'prpl-suggested-' . $key,
    258                         'title'  => $title,
     268                        'title'  => $title . ' <a href="' . esc_url( $delete_url ) . '" style="color: #dc3232; display: inline-block; margin-left: 5px; text-decoration: none;">×</a>',
    259269                    ]
    260270                );
     
    480490        }
    481491
     492        // Plugin activation date.
     493        $progress_planner_settings = \get_option( \Progress_Planner\Settings::OPTION_NAME, [] );
     494        $admin_bar->add_node(
     495            [
     496                'id'     => 'prpl-plugin-activation-date',
     497                'parent' => 'prpl-more-info',
     498                'title'  => 'Plugin Activation Date: ' . ( isset( $progress_planner_settings['activation_date'] ) ? $progress_planner_settings['activation_date'] : 'Unknown' ),
     499            ]
     500        );
     501
    482502        // Free license info.
    483503        $prpl_free_license_key = \get_option( 'progress_planner_license_key', false );
     
    606626        }
    607627    }
     628
     629    /**
     630     * Check and process the delete single task action.
     631     *
     632     * Deletes a single task if the appropriate query parameter is set
     633     * and user has required capabilities.
     634     *
     635     * @return void
     636     */
     637    public function check_delete_single_task() {
     638        if (
     639            ! isset( $_GET['prpl_delete_single_task'] ) || // phpcs:ignore WordPress.Security.NonceVerification.Recommended
     640            ! current_user_can( 'manage_options' )
     641        ) {
     642            return;
     643        }
     644
     645        // Verify nonce for security.
     646        $this->verify_nonce();
     647
     648        $task_id = sanitize_text_field( wp_unslash( $_GET['prpl_delete_single_task'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
     649
     650        // Delete the task.
     651        \progress_planner()->get_suggested_tasks()->delete_task( $task_id );
     652
     653        // Redirect to the same page without the parameter.
     654        wp_safe_redirect( remove_query_arg( [ 'prpl_delete_single_task', '_wpnonce' ] ) );
     655        exit;
     656    }
    608657}
  • progress-planner/tags/1.5.0/playwright.config.js

    r3283338 r3306880  
    2626            name: 'parallel',
    2727            use: { ...devices[ 'Desktop Chrome' ] },
    28             testIgnore: [
    29                 'onboarding.spec.js',
    30                 'task-tagline.spec.js',
    31                 'todo.spec.js',
    32                 'todo-reorder.spec.js',
    33                 'todo-complete.spec.js',
    34                 'sequential.spec.js',
    35             ],
     28            testIgnore: [ 'sequential.spec.js', '**/sequential/**' ],
    3629            fullyParallel: true,
    3730            workers: 4,
  • progress-planner/tags/1.5.0/progress-planner.php

    r3290442 r3306880  
    1010 * Requires at least: 6.3
    1111 * Requires PHP:      7.4
    12  * Version:           1.4.2
     12 * Version:           1.5.0
    1313 * Author:            Team Emilia Projects
    1414 * Author URI:        https://prpl.fyi/about
  • progress-planner/tags/1.5.0/readme.txt

    r3290442 r3306880  
    55Tested up to: 6.8
    66Requires PHP: 7.4
    7 Stable tag: 1.4.2
     7Stable tag: 1.5.0
    88License: GPL3+
    99License URI: https://www.gnu.org/licenses/gpl-3.0.en.html
     
    110110
    111111== Changelog ==
     112
     113= 1.5.0 =
     114
     115Added these recommendations from Ravi:
     116
     117* [Test if your website can send emails correctly](https://prpl.fyi/troubleshoot-smtp).
     118
     119Bugs we fixed:
     120
     121* Don't redirect user to Progress Planner dashboard if 'redirect_to' GET or POST parameter is set.
     122* Removed the Onboard tour steps for the Settings popover which was removed in 1.3.0.
     123* Fixed detecting creation of new valuable content posts.
     124* Don't award point for all Todo tasks, only for golden.
     125* Fix Todo task title not being editable.
    112126
    113127= 1.4.2 =
  • progress-planner/tags/1.5.0/views/admin-page.php

    r3268602 r3306880  
    2929        </div>
    3030
    31         <?php // Display the upgrade tasks popover if needed. ?>
    32         <?php if ( \progress_planner()->get_plugin_upgrade_tasks()->should_show_upgrade_popover() ) : ?>
    33             <?php \progress_planner()->get_ui__popover()->the_popover( 'upgrade-tasks' )->render(); ?>
    34         <?php endif; ?>
     31        <?php
     32            /**
     33             * Fires after the widgets are rendered.
     34             * Nice place to add custom content since our styling is in general applied inside .prpl-wrap .
     35             *
     36             * @since 1.1.1
     37             */
     38            do_action( 'progress_planner_admin_page_after_widgets' );
     39        ?>
    3540    <?php else : ?>
    3641        <?php \progress_planner()->the_view( 'welcome.php' ); ?>
  • progress-planner/trunk/CHANGELOG.md

    r3290442 r3306880  
     1= 1.5.0 =
     2
     3Added these recommendations from Ravi:
     4
     5* [Test if your website can send emails correctly](https://prpl.fyi/troubleshoot-smtp).
     6
     7Bugs we fixed:
     8
     9* Don't redirect user to Progress Planner dashboard if 'redirect_to' GET or POST parameter is set.
     10* Removed the Onboard tour steps for the Settings popover which was removed in 1.3.0.
     11* Fixed detecting creation of new valuable content posts.
     12* Don't award point for all Todo tasks, only for golden.
     13* Fix Todo task title not being editable.
     14
    115= 1.4.2 =
    216
  • progress-planner/trunk/assets/css/admin.css

    r3283338 r3306880  
    363363.prpl-wrap input[type="search"] {
    364364    height: 40px;
    365     box-shadow: 1px 2px 4px 0 rgba(0, 0, 0, 0.25);
     365    box-shadow: 1px 2px 4px 0 rgba(0, 0, 0, 0.05);
    366366}
    367367
     
    473473    }
    474474}
     475
     476/*------------------------------------*\
     477    Layout for columns.
     478\*------------------------------------*/
     479.prpl-columns-wrapper {
     480    display: grid;
     481    grid-template-columns: repeat(2, 1fr);
     482    gap: var(--prpl-padding);
     483}
  • progress-planner/trunk/assets/css/page-widgets/suggested-tasks.css

    r3283338 r3306880  
    145145        h3 {
    146146            font-style: italic;
     147        }
     148    }
     149
     150    .prpl-suggested-task {
     151
     152        .prpl-suggested-task-checkbox {
     153            flex-shrink: 0; /* Prevent shrinking on mobile */
    147154        }
    148155    }
     
    267274}
    268275
    269 
    270276#prpl-popover-monthly-badges-trigger {
    271277    font-size: var(--prpl-font-size-base);
     
    274280    margin-top: 0.75rem;
    275281}
     282
     283/*------------------------------------*\
     284    Interactive tasks, popover.
     285\*------------------------------------*/
     286.prpl-popover.prpl-popover-interactive {
     287    padding: 24px 24px 14px 24px; /* 14px is needed for the "next" button hover state. */
     288    box-sizing: border-box;
     289
     290    * {
     291        max-width: calc(100% - 1px);
     292    }
     293
     294    .prpl-columns-wrapper-flex {
     295        display: flex;
     296        flex-wrap: wrap;
     297        gap: 40px;
     298        overflow: hidden;
     299        padding-bottom: 10px; /* Needed for the "next" button hover state. */
     300
     301        > * {
     302            flex-grow: 1;
     303            flex-basis: 300px;
     304            position: relative;
     305
     306            &:not(:first-child) {
     307
     308                &::before,
     309                &::after {
     310                    content: "";
     311                    display: block;
     312                    position: absolute;
     313                    top: 0;
     314                    left: -20px;
     315                    width: 1px;
     316                    height: 100%;
     317                    background-color: var(--prpl-color-gray-2);
     318                }
     319
     320                &::after {
     321                    top: -20px;
     322                    left: 0;
     323                    width: 100%;
     324                    height: 1px;
     325                }
     326            }
     327        }
     328    }
     329
     330    .prpl-column {
     331
     332        /* Set margin for headings and paragraphs. */
     333        h1,
     334        h2,
     335        h3,
     336        h4,
     337        h5,
     338        h6 {
     339
     340            &:first-child {
     341                margin-top: 0;
     342            }
     343        }
     344
     345        p {
     346            margin-bottom: 1rem;
     347
     348            &:first-child {
     349                margin-top: 0;
     350            }
     351
     352            &:last-child {
     353                margin-bottom: 0;
     354            }
     355        }
     356
     357        .prpl-interactive-task-title {
     358            font-size: 18px;
     359            line-height: 22px;
     360
     361            & + p {
     362                margin-top: 4px;
     363            }
     364        }
     365
     366        /* Set padding and background color for content column (description text). */
     367        &.prpl-column-content {
     368            padding: 20px;
     369            border-radius: var(--prpl-border-radius-big);
     370            background-color: var(--prpl-background-purple);
     371        }
     372
     373        .prpl-note {
     374            margin-bottom: 1rem;
     375            display: flex;
     376            align-items: flex-start;
     377            gap: 0.5rem;
     378            padding: 0.75rem;
     379            color: #854d0e;
     380            font-size: var(--prpl-font-size-small);
     381            border-radius: 6px;
     382            background-color: #fefce8;
     383
     384            .prpl-note-icon {
     385                display: flex;
     386                flex-shrink: 0;
     387                align-items: center;
     388                justify-content: center;
     389                width: 20px;
     390                height: 20px;
     391                color: #eab308;
     392
     393                svg {
     394                    width: 100%;
     395                    height: 100%;
     396                }
     397            }
     398
     399            &.prpl-note-error {
     400                color: #9f0712;
     401                background-color: var(--prpl-background-red);
     402
     403                .prpl-note-icon {
     404                    color: var(--prpl-color-notification-red);
     405                }
     406
     407            }
     408        }
     409
     410        /* To align the buttons to the bottom of the column. */
     411        &:not(.prpl-column-content) {
     412            display: flex;
     413            flex-direction: column;
     414        }
     415
     416        /* Inputs. */
     417        input[type="text"],
     418        input[type="email"],
     419        input[type="number"],
     420        input[type="url"],
     421        input[type="tel"],
     422        input[type="search"] {
     423            height: 44px;
     424            padding: 1rem; /* WIP */
     425            width: 100%;
     426            min-width: 300px; /* WIP */
     427            border-radius: 6px;
     428            border: 1px solid var(--prpl-color-gray-2);
     429        }
     430
     431        .prpl-button {
     432            padding: 0.75rem 1.25rem;
     433            border: none;
     434            color: var(--prpl-color-gray-6);
     435            font-weight: 600;
     436            border-radius: var(--prpl-border-radius);
     437            background-color: var(--prpl-color-400-orange); /* WIP: pick exact color */
     438            transition: all 0.25s ease-in-out;
     439            position: relative;
     440
     441            &::after {
     442                content: "";
     443                display: block;
     444                width: 100%;
     445                height: 100%;
     446                background: var(--prpl-color-400-orange); /* WIP: pick exact color */
     447                position: absolute;
     448                top: 0;
     449                left: 0;
     450                z-index: -1;
     451                border-radius: 6px;
     452                transition: all 0.25s ease-in-out;
     453            }
     454
     455            &:hover,
     456            &:focus {
     457                background: var(--prpl-color-400-orange); /* WIP: pick exact color */
     458
     459                &::after {
     460                    background: var(--prpl-color-400-orange); /* WIP: pick exact color */
     461                    box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.15);
     462                    width: calc(100% + 4px);
     463                    height: calc(100% + 4px);
     464                    margin: -2px;
     465                }
     466            }
     467        }
     468
     469        textarea {
     470            width: 100%;
     471            min-height: 100px;
     472            border-radius: 6px;
     473            box-shadow: 1px 2px 4px 0 rgba(0, 0, 0, 0.05);
     474            border: 1px solid var(--prpl-color-gray-2);
     475        }
     476
     477        /* Used for radio and checkbox inputs. */
     478        .radios {
     479            padding-left: 3px; /* To prevent custom radio and checkbox from being cut off. */
     480            display: flex;
     481            flex-direction: column;
     482            gap: 0.5rem;
     483
     484            .prpl-radio-wrapper {
     485                display: flex;
     486                align-items: center;
     487            }
     488
     489            --prpl-input-green: #3bb3a6;
     490            --prpl-input-gray: #8b99a6;
     491
     492            /* Hide the default input, because WP has it's own styles (which include pseudo-elements). */
     493            .prpl-custom-checkbox input[type="checkbox"],
     494            .prpl-custom-radio input[type="radio"] {
     495                position: absolute;
     496                opacity: 0;
     497                width: 0;
     498                height: 0;
     499            }
     500
     501            /* Shared styles for the custom control */
     502            .prpl-custom-control {
     503                display: inline-block;
     504                vertical-align: middle;
     505                margin-right: 12px;
     506                width: 20px;
     507                height: 20px;
     508                box-sizing: border-box;
     509                position: relative;
     510                transition: border-color 0.2s, background 0.2s;
     511            }
     512
     513            /* Label text styling */
     514            .prpl-custom-checkbox,
     515            .prpl-custom-radio {
     516                display: flex;
     517                align-items: center;
     518                margin-bottom: 0.5rem;
     519                cursor: pointer;
     520                user-select: none;
     521            }
     522
     523            /* Checkbox styles */
     524            .prpl-custom-checkbox {
     525
     526                .prpl-custom-control {
     527                    border: 1px solid var(--prpl-input-gray);
     528                    border-radius: 6px;
     529                    background: #fff;
     530                }
     531
     532                input[type="checkbox"] {
     533
     534                    /* Checkbox hover (off) */
     535                    &:hover + .prpl-custom-control {
     536                        box-shadow: 0 0 0 2px #f7f8fa, 0 0 0 3px var(--prpl-input-green);
     537                    }
     538
     539                    /* Checkbox checked (on) */
     540                    &:checked + .prpl-custom-control {
     541                        background: var(--prpl-input-green);
     542                        border-color: var(--prpl-input-green);
     543                        box-shadow: 0 0 0 2px #f7f8fa, 0 0 0 3px var(--prpl-input-green);
     544                    }
     545                }
     546
     547
     548                /* Checkmark */
     549                .prpl-custom-control::after {
     550                    content: "";
     551                    position: absolute;
     552                    left: 6px;
     553                    top: 2px;
     554                    width: 4px;
     555                    height: 9px;
     556                    border: solid #fff;
     557                    border-width: 0 2px 2px 0;
     558                    opacity: 0;
     559                    transform: scale(0.8) rotate(45deg);
     560                    transition: opacity 0.2s, transform 0.2s;
     561                }
     562
     563                input[type="checkbox"]:checked + .prpl-custom-control::after {
     564                    opacity: 1;
     565                    transform: scale(1) rotate(45deg);
     566                }
     567            }
     568
     569            /* Radio styles */
     570            .prpl-custom-radio {
     571
     572                .prpl-custom-control {
     573                    border: 1px solid var(--prpl-input-gray);
     574                    border-radius: 50%;
     575                    background: #fff;
     576                }
     577
     578                /* Radio hover (off) */
     579                input[type="radio"] {
     580
     581                    &:hover + .prpl-custom-control {
     582                        box-shadow: 0 0 0 2px #f7f8fa, 0 0 0 3px var(--prpl-input-green);
     583                    }
     584
     585                    /* Radio checked (on) */
     586                    &:checked + .prpl-custom-control {
     587                        background: var(--prpl-input-green);
     588                        border-color: var(--prpl-input-green);
     589                        box-shadow: 0 0 0 2px #f7f8fa, 0 0 0 3px var(--prpl-input-green);
     590                    }
     591                }
     592
     593                /* Radio dot */
     594                .prpl-custom-control::after {
     595                    content: "";
     596                    position: absolute;
     597                    top: 5px;
     598                    left: 5px;
     599                    width: 8px;
     600                    height: 8px;
     601                    background: #fff;
     602                    border-radius: 50%;
     603                    opacity: 0;
     604                    transition: opacity 0.2s;
     605                }
     606
     607                input[type="radio"]:checked + .prpl-custom-control::after {
     608                    opacity: 1;
     609                    background: #fff;
     610                }
     611            }
     612        }
     613
     614        /* Used for next step button. */
     615        .prpl-steps-nav-wrapper {
     616            margin-top: auto;
     617            padding-top: 1rem;
     618            display: flex;
     619            justify-content: flex-end;
     620            gap: 1rem;
     621            align-self: flex-end;
     622            width: 100%;
     623
     624            .prpl-button {
     625                cursor: pointer;
     626
     627                /* If the button has empty data-action attribute disable it. */
     628                &[data-action=""] {
     629                    pointer-events: none;
     630                    opacity: 0.5;
     631                }
     632            }
     633
     634        }
     635    }
     636}
  • progress-planner/trunk/assets/css/settings-page.css

    r3283338 r3306880  
    294294            max-width: calc(100% - 2rem);
    295295            border: 1px solid var(--prpl-color-gray-2);
    296             box-shadow: 1px 2px 4px 0 rgba(0, 0, 0, 0.05);
    297296        }
    298297    }
  • progress-planner/trunk/assets/js/external-link-accessibility-helper.js

    r3289779 r3306880  
    4343    // Private: check and decorate one link
    4444    _processLink( link ) {
    45         if ( link.dataset.prpl_accessibility_enhanced === 'true' ) return;
     45        if ( link.dataset.prpl_accessibility_enhanced === 'true' ) {
     46            return;
     47        }
    4648
    4749        const url = new URL( link.href, window.location.href );
  • progress-planner/trunk/assets/js/tour.js

    r3264985 r3306880  
    3838        { config, state } // eslint-disable-line no-unused-vars
    3939    ) => {
    40         const settingsPopover = document.getElementById(
    41             'prpl-popover-settings'
    42         );
    4340        const monthlyBadgesPopover = document.getElementById(
    4441            'prpl-popover-monthly-badges'
    4542        );
    46 
    47         if ( state.activeIndex === 4 ) {
    48             prplTourShowPopover( settingsPopover );
    49         }
    50 
    51         if ( state.activeIndex === 7 ) {
     43        if ( state.activeIndex === 5 ) {
    5244            prplTourShowPopover( monthlyBadgesPopover );
    5345        }
     
    8678// eslint-disable-next-line no-unused-vars -- This is called on a few buttons.
    8779function prplStartTour() {
    88     const settingsPopover = document.getElementById( 'prpl-popover-settings' );
    8980    const monthlyBadgesPopover = document.getElementById(
    9081        'prpl-popover-monthly-badges'
    9182    );
    9283    const progressPlannerTourSteps = progressPlannerTour.steps;
    93     progressPlannerTourSteps[ 3 ].popover.onNextClick = function () {
    94         prplTourShowPopover( settingsPopover );
    95         prplDriverObj.moveNext();
    96     };
     84
    9785    progressPlannerTourSteps[ 4 ].popover.onNextClick = function () {
    98         prplTourHidePopover( settingsPopover );
    99         prplDriverObj.moveNext();
    100     };
    101 
    102     progressPlannerTourSteps[ 6 ].popover.onNextClick = function () {
    10386        prplTourShowPopover( monthlyBadgesPopover );
    10487        prplDriverObj.moveNext();
    10588    };
    106     progressPlannerTourSteps[ 7 ].popover.onNextClick = function () {
     89    progressPlannerTourSteps[ 5 ].popover.onNextClick = function () {
    10790        prplTourHidePopover( monthlyBadgesPopover );
    10891        prplDriverObj.moveNext();
  • progress-planner/trunk/assets/js/web-components/prpl-suggested-task.js

    r3289779 r3306880  
    3131            useCheckbox = true,
    3232            taskList = '', // prplSuggestedTasks or progressPlannerTodo.
     33            popover_id,
    3334        } ) {
    3435            // Get parent class properties
     
    4041            if ( url ) {
    4142                taskHeading = `<a href="${ url }" target="${ url_target }">${ title }</a>`;
     43            }
     44
     45            if ( popover_id ) {
     46                taskHeading = `<a href="#" role="button" onclick="document.getElementById('${ popover_id }')?.showPopover()">${ title }</a>`;
    4247            }
    4348
     
    213218                    }
    214219
    215                     output += `<input
     220                    output += `<label><input
    216221                        type="checkbox"
    217                         id="prpl-suggested-task-checkbox-${ task_id }"
    218222                        class="prpl-suggested-task-checkbox"
    219223                        style="${ checkboxStyle }"
    220224                        ${ ! dismissable ? 'disabled' : '' }
    221225                        ${ getTaskStatus() === 'completed' ? 'checked' : '' }
    222                     >`;
     226                    ><span class="screen-reader-text">${ taskHeading }: ${ prplL10n(
     227                        'markAsComplete'
     228                    ) }</span></label>`;
    223229
    224230                    if ( ! dismissable ) {
     
    256262                ${ actionButtons.completeCheckbox }
    257263                <h3 style="width: 100%;">
    258                     ${
    259                         useCheckbox
    260                             ? `<label for="prpl-suggested-task-checkbox-${ task_id }">`
    261                             : ''
    262                     }
    263264                    <span${
    264265                        'user' === category
     
    266267                            : ''
    267268                    }>${ taskHeading }</span>
    268                     ${ useCheckbox && dismissable ? `</label>` : '' }
    269269                </h3>
    270270                <div class="prpl-suggested-task-actions">
     
    478478                                } )
    479479                            );
     480
     481                            h3Span
     482                                .closest( '.prpl-suggested-task' )
     483                                .querySelector(
     484                                    'label:has(.prpl-suggested-task-checkbox) .screen-reader-text'
     485                                ).innerHTML = title;
    480486                        } );
    481487                }, 300 );
  • progress-planner/trunk/classes/activities/class-suggested-task.php

    r3289779 r3306880  
    7070
    7171        if ( ! empty( $tasks ) && isset( $tasks[0]['provider_id'] ) ) {
    72             $task_provider = \progress_planner()->get_suggested_tasks()->get_tasks_manager()->get_task_provider( $tasks[0]['provider_id'] );
     72            if ( 'user' === $tasks[0]['provider_id'] ) {
     73                $points = isset( $tasks[0]['points'] ) ? (int) $tasks[0]['points'] : 0;
     74            } else {
     75                $task_provider = \progress_planner()->get_suggested_tasks()->get_tasks_manager()->get_task_provider( $tasks[0]['provider_id'] );
    7376
    74             if ( $task_provider ) {
    75                 // Create post task provider had a different points system, this is for backwards compatibility.
    76                 $points = $task_provider instanceof Content_Create ? $task_provider->get_points( $this->data_id ) : $task_provider->get_points();
     77                if ( $task_provider ) {
     78                    // Create post task provider had a different points system, this is for backwards compatibility.
     79                    $points = $task_provider instanceof Content_Create ? $task_provider->get_points( $this->data_id ) : $task_provider->get_points();
     80                }
    7781            }
    7882        }
  • progress-planner/trunk/classes/admin/class-tour.php

    r3264985 r3306880  
    5050                    'title'       => \esc_html__( 'Longterm activity score', 'progress-planner' ),
    5151                    'description' => \esc_html__( "Here, we show you your longterm activity score. This shows whether you've been active on your website over a longer period of time.", 'progress-planner' ),
    52                     'side'        => 'top',
    53                     'align'       => 'center',
    54                 ],
    55             ],
    56             [
    57                 'element' => '#prpl-popover-settings-trigger',
    58                 'popover' => [
    59                     'title'       => \esc_html__( 'Settings', 'progress-planner' ),
    60                     'description' => \esc_html__( 'With this button you can open the settings. Here you can determine which post types you want to include in your activity score.', 'progress-planner' ),
    61                     'side'        => 'top',
    62                     'align'       => 'center',
    63                 ],
    64             ],
    65             [
    66                 'element' => '#prpl-popover-settings',
    67                 'popover' => [
    68                     'title'       => \esc_html__( 'Post types', 'progress-planner' ),
    69                     'description' => \esc_html__( 'Simply select the post types you want to include in your activity score and hit save.', 'progress-planner' ),
    7052                    'side'        => 'top',
    7153                    'align'       => 'center',
  • progress-planner/trunk/classes/class-base.php

    r3283338 r3306880  
    466466        }
    467467
     468        if ( isset( $_REQUEST['redirect_to'] ) && '' !== $_REQUEST['redirect_to'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Missing -- We're not processing any data.
     469            return;
     470        }
     471
    468472        // Redirect to the Progress Planner dashboard.
    469473        \wp_safe_redirect( \admin_url( 'admin.php?page=progress-planner' ) );
  • progress-planner/trunk/classes/class-plugin-upgrade-tasks.php

    r3289779 r3306880  
    2525
    2626        // Check if the plugin was upgraded or new plugin was activated.
    27         \add_action( 'init', [ $this, 'handle_activation_or_upgrade' ], 100 ); // We need to run this after the Tasks_Manager::init() is called.
     27        \add_action( 'init', [ $this, 'handle_activation_or_upgrade' ], 100 ); // We need to run this after the Local_Tasks_Manager::init() is called.
     28
     29        // Add the action to add the upgrade tasks popover.
     30        \add_action( 'progress_planner_admin_page_after_widgets', [ $this, 'add_upgrade_tasks_popover' ] );
    2831    }
    2932
     
    148151        \delete_option( 'progress_planner_upgrade_popover_task_provider_ids' );
    149152    }
     153
     154    /**
     155     * Add the upgrade tasks popover.
     156     *
     157     * @return void
     158     */
     159    public function add_upgrade_tasks_popover() {
     160        if ( $this->should_show_upgrade_popover() ) {
     161            \progress_planner()->get_ui__popover()->the_popover( 'upgrade-tasks' )->render();
     162        }
     163    }
    150164}
  • progress-planner/trunk/classes/class-suggested-tasks.php

    r3289779 r3306880  
    3636        if ( \is_admin() ) {
    3737            \add_action( 'init', [ $this, 'init' ], 100 ); // Wait for the post types to be initialized.
     38
     39            // Check GET parameter and maybe set task as pending celebration.
     40            \add_action( 'init', [ $this, 'maybe_complete_task' ] );
    3841        }
    3942
     
    532535
    533536    /**
     537     * Maybe complete a task.
     538     * Primarly this is used for deeplinking, ie user is testing if the emails are working
     539     * He gets an email with a link which automatically completes the task.
     540     *
     541     * @return void
     542     */
     543    public function maybe_complete_task() {
     544        if ( ! \progress_planner()->is_on_progress_planner_dashboard_page() || ! isset( $_GET['prpl_complete_task'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
     545            return;
     546        }
     547
     548        $task_id = \sanitize_text_field( \wp_unslash( $_GET['prpl_complete_task'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
     549        if ( ! $task_id ) {
     550            return;
     551        }
     552
     553        if ( ! $this->was_task_completed( $task_id ) ) {
     554            $this->mark_task_as( 'pending_celebration', $task_id );
     555
     556            // Insert an activity.
     557            $this->insert_activity( $task_id );
     558        }
     559    }
     560
     561    /**
    534562     * Handle the suggested task action.
    535563     *
     
    556584                // Insert an activity.
    557585                $this->insert_activity( $task_id );
     586
    558587                $updated = true;
    559588                break;
  • progress-planner/trunk/classes/suggested-tasks/class-tasks-manager.php

    r3289779 r3306880  
    2828use Progress_Planner\Suggested_Tasks\Providers\Integrations\Yoast\Add_Yoast_Providers;
    2929use Progress_Planner\Suggested_Tasks\Providers\User as User_Tasks;
     30use Progress_Planner\Suggested_Tasks\Providers\Interactive\Email_Sending;
    3031use Progress_Planner\Suggested_Tasks\Providers\Set_Valuable_Post_Types;
    3132use Progress_Planner\Suggested_Tasks\Providers\Fewer_Tags;
     
    6869            new Search_Engine_Visibility(),
    6970            new User_Tasks(),
     71            new Email_Sending(),
    7072            new Set_Valuable_Post_Types(),
    7173            new Remove_Terms_Without_Posts(),
  • progress-planner/trunk/classes/suggested-tasks/data-collector/class-last-published-post.php

    r3283338 r3306880  
    3535     */
    3636    public function init() {
    37         \add_action( 'init', [ $this, 'set_include_post_types' ], 99 ); // Wait for all CPTs to be registered.
     37        \add_action( 'init', [ $this, 'set_include_post_types' ], 100 ); // Wait for all CPTs to be registered and collector manager to trigger it's init method (which is done on priority 99).
    3838        \add_action( 'transition_post_status', [ $this, 'update_last_published_post_cache' ], 10, 3 );
    3939    }
  • progress-planner/trunk/classes/suggested-tasks/providers/integrations/yoast/class-cornerstone-workout.php

    r3290442 r3306880  
    88namespace Progress_Planner\Suggested_Tasks\Providers\Integrations\Yoast;
    99
    10 use Progress_Planner\Suggested_Tasks\Providers\Tasks;
     10use Progress_Planner\Suggested_Tasks\Providers\Integrations\Yoast\Yoast_Provider;
    1111use Progress_Planner\Suggested_Tasks\Providers\Traits\Dismissable_Task;
    1212
     
    1414 * Add tasks for Yoast SEO cornerstone content.
    1515 */
    16 class Cornerstone_Workout extends Tasks {
     16class Cornerstone_Workout extends Yoast_Provider {
    1717    use Dismissable_Task;
    1818
     
    3030     */
    3131    protected const PROVIDER_ID = 'yoast-cornerstone-workout';
    32 
    33     /**
    34      * The provider category.
    35      *
    36      * @var string
    37      */
    38     protected const CATEGORY = 'configuration';
    3932
    4033    /**
  • progress-planner/trunk/classes/suggested-tasks/providers/integrations/yoast/class-orphaned-content-workout.php

    r3290442 r3306880  
    88namespace Progress_Planner\Suggested_Tasks\Providers\Integrations\Yoast;
    99
    10 use Progress_Planner\Suggested_Tasks\Providers\Tasks;
     10use Progress_Planner\Suggested_Tasks\Providers\Integrations\Yoast\Yoast_Provider;
    1111use Progress_Planner\Suggested_Tasks\Providers\Traits\Dismissable_Task;
    1212
     
    1414 * Add tasks for Yoast SEO cornerstone content.
    1515 */
    16 class Orphaned_Content_Workout extends Tasks {
     16class Orphaned_Content_Workout extends Yoast_Provider {
    1717    use Dismissable_Task;
    1818
     
    3030     */
    3131    protected const PROVIDER_ID = 'yoast-orphaned-content-workout';
    32 
    33     /**
    34      * The provider category.
    35      *
    36      * @var string
    37      */
    38     protected const CATEGORY = 'configuration';
    3932
    4033    /**
  • progress-planner/trunk/classes/utils/class-debug-tools.php

    r3289779 r3306880  
    4747        \add_action( 'init', [ $this, 'check_delete_badges' ] );
    4848        \add_action( 'init', [ $this, 'check_toggle_migrations' ] );
     49        \add_action( 'init', [ $this, 'check_delete_single_task' ] );
    4950
    5051        // Add filter to modify the maximum number of suggested tasks to display.
     
    252253                }
    253254
     255                // Add delete button.
     256                $delete_url = add_query_arg(
     257                    [
     258                        'prpl_delete_single_task' => $task['task_id'],
     259                        '_wpnonce'                => wp_create_nonce( 'prpl_debug_tools' ),
     260                    ],
     261                    $this->current_url
     262                );
     263
    254264                $admin_bar->add_node(
    255265                    [
    256266                        'id'     => 'prpl-suggested-' . $key . '-' . $title,
    257267                        'parent' => 'prpl-suggested-' . $key,
    258                         'title'  => $title,
     268                        'title'  => $title . ' <a href="' . esc_url( $delete_url ) . '" style="color: #dc3232; display: inline-block; margin-left: 5px; text-decoration: none;">×</a>',
    259269                    ]
    260270                );
     
    480490        }
    481491
     492        // Plugin activation date.
     493        $progress_planner_settings = \get_option( \Progress_Planner\Settings::OPTION_NAME, [] );
     494        $admin_bar->add_node(
     495            [
     496                'id'     => 'prpl-plugin-activation-date',
     497                'parent' => 'prpl-more-info',
     498                'title'  => 'Plugin Activation Date: ' . ( isset( $progress_planner_settings['activation_date'] ) ? $progress_planner_settings['activation_date'] : 'Unknown' ),
     499            ]
     500        );
     501
    482502        // Free license info.
    483503        $prpl_free_license_key = \get_option( 'progress_planner_license_key', false );
     
    606626        }
    607627    }
     628
     629    /**
     630     * Check and process the delete single task action.
     631     *
     632     * Deletes a single task if the appropriate query parameter is set
     633     * and user has required capabilities.
     634     *
     635     * @return void
     636     */
     637    public function check_delete_single_task() {
     638        if (
     639            ! isset( $_GET['prpl_delete_single_task'] ) || // phpcs:ignore WordPress.Security.NonceVerification.Recommended
     640            ! current_user_can( 'manage_options' )
     641        ) {
     642            return;
     643        }
     644
     645        // Verify nonce for security.
     646        $this->verify_nonce();
     647
     648        $task_id = sanitize_text_field( wp_unslash( $_GET['prpl_delete_single_task'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
     649
     650        // Delete the task.
     651        \progress_planner()->get_suggested_tasks()->delete_task( $task_id );
     652
     653        // Redirect to the same page without the parameter.
     654        wp_safe_redirect( remove_query_arg( [ 'prpl_delete_single_task', '_wpnonce' ] ) );
     655        exit;
     656    }
    608657}
  • progress-planner/trunk/playwright.config.js

    r3283338 r3306880  
    2626            name: 'parallel',
    2727            use: { ...devices[ 'Desktop Chrome' ] },
    28             testIgnore: [
    29                 'onboarding.spec.js',
    30                 'task-tagline.spec.js',
    31                 'todo.spec.js',
    32                 'todo-reorder.spec.js',
    33                 'todo-complete.spec.js',
    34                 'sequential.spec.js',
    35             ],
     28            testIgnore: [ 'sequential.spec.js', '**/sequential/**' ],
    3629            fullyParallel: true,
    3730            workers: 4,
  • progress-planner/trunk/progress-planner.php

    r3290442 r3306880  
    1010 * Requires at least: 6.3
    1111 * Requires PHP:      7.4
    12  * Version:           1.4.2
     12 * Version:           1.5.0
    1313 * Author:            Team Emilia Projects
    1414 * Author URI:        https://prpl.fyi/about
  • progress-planner/trunk/readme.txt

    r3290442 r3306880  
    55Tested up to: 6.8
    66Requires PHP: 7.4
    7 Stable tag: 1.4.2
     7Stable tag: 1.5.0
    88License: GPL3+
    99License URI: https://www.gnu.org/licenses/gpl-3.0.en.html
     
    110110
    111111== Changelog ==
     112
     113= 1.5.0 =
     114
     115Added these recommendations from Ravi:
     116
     117* [Test if your website can send emails correctly](https://prpl.fyi/troubleshoot-smtp).
     118
     119Bugs we fixed:
     120
     121* Don't redirect user to Progress Planner dashboard if 'redirect_to' GET or POST parameter is set.
     122* Removed the Onboard tour steps for the Settings popover which was removed in 1.3.0.
     123* Fixed detecting creation of new valuable content posts.
     124* Don't award point for all Todo tasks, only for golden.
     125* Fix Todo task title not being editable.
    112126
    113127= 1.4.2 =
  • progress-planner/trunk/views/admin-page.php

    r3268602 r3306880  
    2929        </div>
    3030
    31         <?php // Display the upgrade tasks popover if needed. ?>
    32         <?php if ( \progress_planner()->get_plugin_upgrade_tasks()->should_show_upgrade_popover() ) : ?>
    33             <?php \progress_planner()->get_ui__popover()->the_popover( 'upgrade-tasks' )->render(); ?>
    34         <?php endif; ?>
     31        <?php
     32            /**
     33             * Fires after the widgets are rendered.
     34             * Nice place to add custom content since our styling is in general applied inside .prpl-wrap .
     35             *
     36             * @since 1.1.1
     37             */
     38            do_action( 'progress_planner_admin_page_after_widgets' );
     39        ?>
    3540    <?php else : ?>
    3641        <?php \progress_planner()->the_view( 'welcome.php' ); ?>
Note: See TracChangeset for help on using the changeset viewer.