diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml new file mode 100644 index 00000000..e8c73690 --- /dev/null +++ b/.github/workflows/run_tests.yml @@ -0,0 +1,105 @@ +name: CI + +on: + pull_request: + branches: + - develop + - master + +permissions: + contents: read + +jobs: + run_tests: + name: Run Unit & Acceptance Tests + runs-on: ubuntu-latest + services: + mysql: + image: mysql:8 + env: + MYSQL_ROOT_PASSWORD: wordpress + MYSQL_USER: wordpress + MYSQL_PASSWORD: wordpress + MYSQL_DATABASE: wordpress_test + ports: + - 3306:3306 + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Set up PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 8.1 + coverage: none + tools: wp-cli + + - name: Install Composer dependencies + run: composer install + + - name: Setup Node + uses: actions/setup-node@v2 + with: + node-version: '17' + + - name: Prepare testing environment + run: | + # Move the plugin files to the separate folder + mkdir ssp && mv `ls -A | grep -v ssp` ssp + + # Download WordPress + wp core download --allow-root + + # Set up .env + echo "TEST_WP_ROOT=$(pwd)" > .env.testing + echo "TEST_DB_HOST=127.0.0.1" >> .env.testing + echo "TEST_DB_NAME=wordpress_test" >> .env.testing + echo "TEST_DB_USER=wordpress" >> .env.testing + echo "TEST_DB_PASSWORD=wordpress" >> .env.testing + echo "TEST_DOMAIN=localhost" >> .env.testing + mv .env.testing ssp/ + + # Move the plugin to the plugins folder + mv ssp ./wp-content/plugins/seriously-simple-podcasting + cd wp-content/plugins/seriously-simple-podcasting + npm install -g grunt-cli + npm install + npm rebuild node-sass + grunt uglify && grunt cssmin && npm run build + + - name: Run Unit tests + run: cd wp-content/plugins/seriously-simple-podcasting && vendor/bin/codecept run wpunit + + - name: Run Acceptance tests + run: | + wp config create --dbname=wordpress_test --dbuser=wordpress --dbpass=wordpress --dbhost=127.0.0.1 --dbprefix=wsy_ --allow-root + wp config set WP_SITEURL http://localhost:8000 --type=constant --allow-root + wp config set WP_HOME http://localhost:8000 --type=constant --allow-root + nohup php -S localhost:8000 & + cd wp-content/plugins/seriously-simple-podcasting + echo "SITE_URL=http://localhost:8000" >> .env.testing + echo "SITE_USER=autotest" >> .env.testing + echo "SITE_USER_PASS=password" >> .env.testing + echo "PODCAST_GUID=dd94465b-580a-501c-a892-caf224d23d7a" >> .env.testing + sudo apt-get update -qq + sudo env DEBIAN_FRONTEND=noninteractive apt-get install -qq mysql-client default-libmysqlclient-dev + gzip -d tests/seed.sql.gz && wp db import tests/seed.sql --allow-root + wp core update-db --allow-root + wp theme activate twentytwentyfour --allow-root + wp plugin install classic-editor --allow-root + vendor/bin/codecept run acceptance --steps + + - name: Upload codeception output on failure + if: failure() + uses: actions/upload-artifact@v4 + with: + name: acceptance-tests + path: wp-content/plugins/seriously-simple-podcasting/tests/_output + + - name: Upload PHP server logs on failure + if: failure() + uses: actions/upload-artifact@v4 + with: + name: acceptance-tests + path: nohup.out diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9caf71f3..c9b187e4 100755 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -46,7 +46,6 @@ unit_tests: # Prepare testing environment file - touch .env.testing - - echo "TEST_WP_ROOT=$(pwd)" - echo "TEST_WP_ROOT=$(pwd)" >> .env.testing - echo "TEST_DB_HOST=db" >> .env.testing - echo "TEST_DB_NAME=$MYSQL_DATABASE" >> .env.testing @@ -151,6 +150,7 @@ acceptance_tests: - echo "SITE_URL=$SSH_USER" >> .env.testing - echo "SITE_USER=$SITE_USER" >> .env.testing - echo "SITE_USER_PASS=$SITE_USER_PASS" >> .env.testing + - echo "PODCAST_GUID=115e423a-72d2-531e-9d3c-ece7dd4b74fe" >> .env.testing # Run Acceptance tests - vendor/bin/codecept run acceptance --steps diff --git a/assets/admin/css/admin.css b/assets/admin/css/admin.css index e2597b1f..ca9cbd7c 100644 --- a/assets/admin/css/admin.css +++ b/assets/admin/css/admin.css @@ -204,6 +204,16 @@ textarea#episode_embed_code { font-size: 16px; top: -3px; } +#podmotor_account_api_token { + width: 100%; + min-width: 100%; + display: block; + margin-bottom: 8px; } + +.ssp-settings-integrations table label { + margin-top: 10px; + display: block; } + .ssp-settings:not(.ssp-settings-extensions, .ssp-settings-integrations) { background: #FFFFFF; margin-top: 28px; @@ -219,8 +229,6 @@ textarea#episode_embed_code { .ssp-settings table input.regular-text { max-width: 90%; background: #F1F5F9; } - .ssp-settings .validate-api-credentials-message { - margin-left: 30px; } .ssp-settings .loader { position: relative; } .ssp-settings .loader:after { @@ -243,6 +251,32 @@ textarea#episode_embed_code { .ssp-settings .ssp-sync-msg.success .sync-overview { color: #4caf50; } +.ssp-main-settings .error { + color: #DF4E4F; } + +.ssp-main-settings .hidden { + display: none; } + +.ssp-main-settings .disconnect-castos { + margin-right: 8px; + vertical-align: middle; } + +.ssp-main-settings.tab-castos-hosting .loader { + position: relative; } + .ssp-main-settings.tab-castos-hosting .loader:after { + content: ''; + display: block; + background: url(../img/loader2.svg) no-repeat; + position: absolute; + right: -50px; + top: -9px; + background-size: contain; + width: 44px; + height: 44px; } + +.connect-castos-message { + margin-left: 20px; } + .ssp-sync-podcast { display: flex; justify-content: space-between; @@ -467,6 +501,8 @@ textarea#episode_embed_code { right: 350px; } .ssp-onboarding-step-4 .ssp-onboarding__steps::after { right: 180px; } + .ssp-onboarding-step-4 .ssp-onboarding__settings-body { + padding: 24px 24px 36px; } .ssp-onboarding-step-4__info { display: flex; flex-direction: column; @@ -659,7 +695,7 @@ textarea#episode_embed_code { z-index: 0; visibility: hidden; transition: .6s; - margin-top: -444px; } + margin-top: -345px; } .ssp-onboarding-step-4__form--opened { visibility: visible; margin-top: 0; } @@ -749,6 +785,10 @@ textarea#episode_embed_code { text-align: right; margin-top: 25px; position: relative; } + .ssp-onboarding__submit span.connect-castos-message { + display: block; + text-align: left; + margin: 20px 0 0; } .ssp-onboarding__submit button[type=submit], .ssp-onboarding__submit .button { position: relative; height: 50px; @@ -794,39 +834,56 @@ textarea#episode_embed_code { background: #516178; } .ssp-onboarding__submit .button span { line-height: 50px; } - .ssp-onboarding__submit .validate-token { + .ssp-onboarding__submit .castos-connect { float: left; position: relative; padding: 0 20px 0 45px; } - .ssp-onboarding__submit .validate-token:after { + .ssp-onboarding__submit .castos-connect:after { content: ''; display: block; position: absolute; - background: url(../img/validate.svg) no-repeat; - background-size: cover; - left: 19px; - top: 17px; - width: 14px; - height: 18px; - filter: none; } - .ssp-onboarding__submit .validate-token.validating { - background: #374151; } - .ssp-onboarding__submit .validate-token.validating:hover, .ssp-onboarding__submit .validate-token.validating:active, .ssp-onboarding__submit .validate-token.validating:focus { - background: #374151; } - .ssp-onboarding__submit .validate-token.validating:after { + background: url(../img/connect.svg) no-repeat; + left: 13px; + top: 13px; + width: 25px; + height: 23px; + filter: invert(100%) sepia(100%) saturate(0%) hue-rotate(198deg) brightness(101%) contrast(102%); + background-size: contain; } + .ssp-onboarding__submit .castos-connect:disabled { + background: #9CA3AF !important; + color: #D1D5DB !important; } + .ssp-onboarding__submit .castos-connect:disabled:after { + filter: invert(94%) sepia(8%) saturate(150%) hue-rotate(177deg) brightness(90%) contrast(93%); + transition: none; } + .ssp-onboarding__submit .castos-connect.connecting { + background: #DE7373 !important; + color: #FFFFFF !important; } + .ssp-onboarding__submit .castos-connect.connecting:hover, .ssp-onboarding__submit .castos-connect.connecting:active, .ssp-onboarding__submit .castos-connect.connecting:focus, .ssp-onboarding__submit .castos-connect.connecting:disabled { + background: #DE7373; } + .ssp-onboarding__submit .castos-connect.connecting:after { width: 17px; height: 18px; - background-image: url(../img/validating.svg); } - .ssp-onboarding__submit .validate-token.valid { - background: #10B981; - padding: 0 16px 0 45px; } - .ssp-onboarding__submit .validate-token.valid:hover, .ssp-onboarding__submit .validate-token.valid:active, .ssp-onboarding__submit .validate-token.valid:focus { + background-image: url(../img/connecting.svg); + animation: rotation 2s infinite linear; + filter: none; + transition: none; + top: 15px; + left: 15px; } + .ssp-onboarding__submit .castos-connect.connected { + background: #10B981 !important; + color: #FFFFFF !important; + padding: 0 16px 0 45px; + opacity: .7; } + .ssp-onboarding__submit .castos-connect.connected:hover, .ssp-onboarding__submit .castos-connect.connected:active, .ssp-onboarding__submit .castos-connect.connected:focus { background: #10B981; } - .ssp-onboarding__submit .validate-token.valid:after { - width: 16px; - height: 12px; - top: 19px; - background-image: url(../img/checkbox.svg); } + .ssp-onboarding__submit .castos-connect.connected:after { + background-image: url(../img/connect.svg); + left: 13px; + top: 13px; + width: 25px; + height: 23px; + filter: invert(100%) sepia(100%) saturate(0%) hue-rotate(198deg) brightness(101%) contrast(102%); + background-size: contain; } .ssp-onboarding__image-info { position: absolute; display: flex; @@ -864,10 +921,6 @@ textarea#episode_embed_code { transform: rotate(-45deg); } .ssp-onboarding__delete-image:hover:before, .ssp-onboarding__delete-image:hover:after { background: #fff; } - .ssp-onboarding .validate-api-credentials-message { - position: absolute; - left: 0; - bottom: -18px; } .ssp-onboarding__links { display: flex; text-align: left; } @@ -977,17 +1030,17 @@ textarea#episode_embed_code { font-family: dashicons, 'Inter', sans-serif; } /* Hide the WP bars */ -.admin_page_ssp-onboarding-1 #adminmenumain, .admin_page_ssp-onboarding-1 #wpadminbar, .admin_page_ssp-onboarding-1 #wpfooter, .admin_page_ssp-onboarding-2 #adminmenumain, .admin_page_ssp-onboarding-2 #wpadminbar, .admin_page_ssp-onboarding-2 #wpfooter, .admin_page_ssp-onboarding-3 #adminmenumain, .admin_page_ssp-onboarding-3 #wpadminbar, .admin_page_ssp-onboarding-3 #wpfooter, .admin_page_ssp-onboarding-4 #adminmenumain, .admin_page_ssp-onboarding-4 #wpadminbar, .admin_page_ssp-onboarding-4 #wpfooter, .admin_page_ssp-onboarding-5 #adminmenumain, .admin_page_ssp-onboarding-5 #wpadminbar, .admin_page_ssp-onboarding-5 #wpfooter { +.podcast_page_ssp-onboarding-1 #adminmenumain, .podcast_page_ssp-onboarding-1 #wpadminbar, .podcast_page_ssp-onboarding-1 #wpfooter, .admin_page_ssp-onboarding-1 #adminmenumain, .admin_page_ssp-onboarding-1 #wpadminbar, .admin_page_ssp-onboarding-1 #wpfooter, .admin_page_ssp-onboarding-2 #adminmenumain, .admin_page_ssp-onboarding-2 #wpadminbar, .admin_page_ssp-onboarding-2 #wpfooter, .admin_page_ssp-onboarding-3 #adminmenumain, .admin_page_ssp-onboarding-3 #wpadminbar, .admin_page_ssp-onboarding-3 #wpfooter, .admin_page_ssp-onboarding-4 #adminmenumain, .admin_page_ssp-onboarding-4 #wpadminbar, .admin_page_ssp-onboarding-4 #wpfooter, .admin_page_ssp-onboarding-5 #adminmenumain, .admin_page_ssp-onboarding-5 #wpadminbar, .admin_page_ssp-onboarding-5 #wpfooter { display: none; } -.admin_page_ssp-onboarding-1 #wpcontent, .admin_page_ssp-onboarding-2 #wpcontent, .admin_page_ssp-onboarding-3 #wpcontent, .admin_page_ssp-onboarding-4 #wpcontent, .admin_page_ssp-onboarding-5 #wpcontent { +.podcast_page_ssp-onboarding-1 #wpcontent, .admin_page_ssp-onboarding-1 #wpcontent, .admin_page_ssp-onboarding-2 #wpcontent, .admin_page_ssp-onboarding-3 #wpcontent, .admin_page_ssp-onboarding-4 #wpcontent, .admin_page_ssp-onboarding-5 #wpcontent { margin-left: 0; padding-left: 0; } -.admin_page_ssp-onboarding-1 #wpbody-content, .admin_page_ssp-onboarding-2 #wpbody-content, .admin_page_ssp-onboarding-3 #wpbody-content, .admin_page_ssp-onboarding-4 #wpbody-content, .admin_page_ssp-onboarding-5 #wpbody-content { +.podcast_page_ssp-onboarding-1 #wpbody-content, .admin_page_ssp-onboarding-1 #wpbody-content, .admin_page_ssp-onboarding-2 #wpbody-content, .admin_page_ssp-onboarding-3 #wpbody-content, .admin_page_ssp-onboarding-4 #wpbody-content, .admin_page_ssp-onboarding-5 #wpbody-content { padding-bottom: 30px; } -.admin_page_ssp-onboarding-1 .notice, .admin_page_ssp-onboarding-1 .error, .admin_page_ssp-onboarding-2 .notice, .admin_page_ssp-onboarding-2 .error, .admin_page_ssp-onboarding-3 .notice, .admin_page_ssp-onboarding-3 .error, .admin_page_ssp-onboarding-4 .notice, .admin_page_ssp-onboarding-4 .error, .admin_page_ssp-onboarding-5 .notice, .admin_page_ssp-onboarding-5 .error { +.podcast_page_ssp-onboarding-1 .notice, .podcast_page_ssp-onboarding-1 .error, .admin_page_ssp-onboarding-1 .notice, .admin_page_ssp-onboarding-1 .error, .admin_page_ssp-onboarding-2 .notice, .admin_page_ssp-onboarding-2 .error, .admin_page_ssp-onboarding-3 .notice, .admin_page_ssp-onboarding-3 .error, .admin_page_ssp-onboarding-4 .notice, .admin_page_ssp-onboarding-4 .error, .admin_page_ssp-onboarding-5 .notice, .admin_page_ssp-onboarding-5 .error { display: none; } .ssp-review-notice { diff --git a/assets/admin/img/connect.svg b/assets/admin/img/connect.svg new file mode 100644 index 00000000..463acfb4 --- /dev/null +++ b/assets/admin/img/connect.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/admin/img/connecting.svg b/assets/admin/img/connecting.svg new file mode 100644 index 00000000..23295e5b --- /dev/null +++ b/assets/admin/img/connecting.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/admin/img/loader2.svg b/assets/admin/img/loader2.svg new file mode 100644 index 00000000..25f1ca19 --- /dev/null +++ b/assets/admin/img/loader2.svg @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/admin/js/onboarding.js b/assets/admin/js/onboarding.js index 9c46ad01..d4866290 100644 --- a/assets/admin/js/onboarding.js +++ b/assets/admin/js/onboarding.js @@ -5,7 +5,7 @@ jQuery(document).ready(function($) { $imgName = $imgInfo.find('.js-onboarding-img-name'), $fields = $('.js-onboarding-field'), $btn = $('.js-onboarding-btn'), - $validateTokenBtn = $('.js-onboarding-validate-token'), + $connectCastosBtn = $('.js-onboarding-castos-connect'), $hostingStep2 = $('.js-hosting-form'), $accordion = $('.js-accordion'), $dragable = $('.js-onboarding-dragable'), @@ -54,29 +54,33 @@ jQuery(document).ready(function($) { }); }, initTokenValidation = function(){ - $validateTokenBtn.on('validated', function () { - //don't use $btn since it has custom validation - var $form = $validateTokenBtn.closest('form'), - $nextButton = $form.find('button[type=submit]'); - $(this).removeClass('validating'); - if ($validateTokenBtn.hasClass('valid')) { + $connectCastosBtn.on('connected', function (e, response) { + var $form = $connectCastosBtn.closest('form'), + $nextButton = $form.find('button[type=submit]'), + $me = $(this), + $msg = $form.find('.connect-castos-message'), + $field = $('.js-onboarding-castos-connect-field'); + $me.removeClass('connecting'); + $msg.show(); + + if ("success" === response.status) { + $me.html($me.data('connected-txt')); + $field.attr('disabled', 'disabled'); $nextButton.removeAttr('disabled'); - $form.find('.validate-api-credentials-message').html(''); - $(this).html($(this).data('valid-txt')); } else { - $(this).html($(this).data('initial-txt')); + $me.html($me.data('initial-txt')); $nextButton.attr('disabled', 'disabled'); } }); - $validateTokenBtn.on('click', function(){ - $(this).addClass('validating').html($(this).data('validating-txt')); + $connectCastosBtn.on('connecting', function(){ + $(this).addClass('connecting').html($(this).data('connecting-txt')); }); - $('.js-onboarding-validate-token-field').on('change paste keyup', function(){ - var $nextButton = $validateTokenBtn.closest('form').find('button[type=submit]'); - $validateTokenBtn.html($validateTokenBtn.data('initial-txt')); - $validateTokenBtn.removeClass('valid'); + $('.js-onboarding-castos-connect-field').on('change paste keyup', function(){ + var $nextButton = $connectCastosBtn.closest('form').find('button[type=submit]'); + $connectCastosBtn.html($connectCastosBtn.data('initial-txt')); + $connectCastosBtn.removeClass('valid'); $nextButton.attr('disabled', 'disabled'); }); }, diff --git a/assets/admin/scss/_admin.scss b/assets/admin/scss/_admin.scss index ce7728fd..e85ff61b 100644 --- a/assets/admin/scss/_admin.scss +++ b/assets/admin/scss/_admin.scss @@ -271,6 +271,19 @@ textarea#episode_embed_code { } } +#podmotor_account_api_token { + width: 100%; + min-width: 100%; + display: block; + margin-bottom: 8px; +} + +.ssp-settings-integrations { + table label { + margin-top: 10px; + display: block; + } +} .ssp-settings:not(.ssp-settings-extensions,.ssp-settings-integrations) { background: $clr_white; @@ -298,10 +311,6 @@ textarea#episode_embed_code { } } - .validate-api-credentials-message { - margin-left: 30px; - } - .loader { position: relative; &:after { @@ -334,6 +343,42 @@ textarea#episode_embed_code { } } +.ssp-main-settings { + .error { + color: $clr_red_400; + } + + .hidden { + display: none; + } + + .disconnect-castos { + margin-right: 8px; + vertical-align: middle; + } + + &.tab-castos-hosting { + .loader { + position: relative; + &:after { + content: ''; + display: block; + background: url(../img/loader2.svg) no-repeat; + position: absolute; + right: -50px; + top: -9px; + background-size: contain; + width: 44px; + height: 44px; + } + } + } +} + +.connect-castos-message { + margin-left: 20px; +} + .ssp-sync-podcast { display: flex; justify-content: space-between; diff --git a/assets/admin/scss/_onboarding.scss b/assets/admin/scss/_onboarding.scss index a9bda3c3..7eb298af 100644 --- a/assets/admin/scss/_onboarding.scss +++ b/assets/admin/scss/_onboarding.scss @@ -156,7 +156,7 @@ &-step-2, &-step-3, &-step-4, &-step-5 { .ssp-onboarding__steps::after { - background: #DF4E4F; + background: $clr_red_400; width: initial; } } @@ -191,6 +191,10 @@ right: 180px; } + .ssp-onboarding__settings-body { + padding: 24px 24px 36px; + } + &__info { display: flex; flex-direction: column; @@ -317,7 +321,7 @@ li { &:hover { svg path, svg rect { - fill: #DF4E4F; + fill: $clr_red_400; } } } @@ -452,7 +456,7 @@ z-index: 0; visibility: hidden; transition: .6s; - margin-top: -444px; + margin-top: -345px; &--opened { visibility: visible; @@ -585,13 +589,19 @@ margin-top: 25px; position: relative; + span.connect-castos-message { + display: block; + text-align: left; + margin: 20px 0 0; + } + button[type=submit], .button { position: relative; height: 50px; border-radius: 6px; border: 0; color: #FFFFFF; - background: #DF4E4F; + background: $clr_red_400; font-size: 18px; padding: 0 55px 0 33px; transition: .3s; @@ -650,7 +660,7 @@ } } - .validate-token { + .castos-connect { float: left; position: relative; padding: 0 20px 0 45px; @@ -659,42 +669,62 @@ content: ''; display: block; position: absolute; - background: url(../img/validate.svg) no-repeat; - background-size: cover; - left: 19px; - top: 17px; - width: 14px; - height: 18px; - filter: none; + background: url(../img/connect.svg) no-repeat; + left: 13px; + top: 13px; + width: 25px; + height: 23px; + filter: invert(100%) sepia(100%) saturate(0%) hue-rotate(198deg) brightness(101%) contrast(102%); + background-size: contain; + } + + &:disabled { + background: #9CA3AF !important; + color: #D1D5DB !important; + &:after { + //filter: invert(92%) sepia(17%) saturate(74%) hue-rotate(176deg) brightness(92%) contrast(88%); + filter: invert(94%) sepia(8%) saturate(150%) hue-rotate(177deg) brightness(90%) contrast(93%); + transition: none; + } } - &.validating { - background: #374151; + &.connecting { + background: #DE7373 !important; + color: #FFFFFF !important; - &:hover, &:active, &:focus { - background: #374151; + &:hover, &:active, &:focus, &:disabled { + background: #DE7373; } &:after { width: 17px; height: 18px; - background-image: url(../img/validating.svg); + background-image: url(../img/connecting.svg); + animation: rotation 2s infinite linear; + filter: none; + transition: none; + top: 15px; + left: 15px; } } - &.valid { - background: #10B981; + &.connected { + background: #10B981 !important; + color: $clr_white !important; padding: 0 16px 0 45px; - &:hover, &:active, &:focus { background: #10B981; } + opacity: .7; &:after { - width: 16px; - height: 12px; - top: 19px; - background-image: url(../img/checkbox.svg); + background-image: url(../img/connect.svg); + left: 13px; + top: 13px; + width: 25px; + height: 23px; + filter: invert(100%) sepia(100%) saturate(0%) hue-rotate(198deg) brightness(101%) contrast(102%); + background-size: contain; } } } @@ -732,7 +762,7 @@ display: block; height: 1.5px; width: 14px; - background: #DF4E4F; + background: $clr_red_400; position: absolute; left: 7px; top: 46%; @@ -752,12 +782,6 @@ } } - .validate-api-credentials-message { - position: absolute; - left: 0; - bottom: -18px; - } - &__links { display: flex; text-align: left; @@ -822,7 +846,7 @@ width: 30px; height: 30px; background: #FFF; - color: #DF4E4F; + color: $clr_red_400; font-size: 20px; font-weight: 600; border-radius: 50%; @@ -907,7 +931,7 @@ } /* Hide the WP bars */ -.admin_page_ssp-onboarding-1, .admin_page_ssp-onboarding-2, .admin_page_ssp-onboarding-3, .admin_page_ssp-onboarding-4, .admin_page_ssp-onboarding-5 { +.podcast_page_ssp-onboarding-1, .admin_page_ssp-onboarding-1, .admin_page_ssp-onboarding-2, .admin_page_ssp-onboarding-3, .admin_page_ssp-onboarding-4, .admin_page_ssp-onboarding-5 { #adminmenumain, #wpadminbar, #wpfooter { display: none; } diff --git a/assets/css/settings.css b/assets/css/settings.css index 95cb20c5..de8323bb 100644 --- a/assets/css/settings.css +++ b/assets/css/settings.css @@ -18,7 +18,7 @@ height: auto; } -#ssp-main-settings { +#ssp-main-settings.castos-disconnected { width: 75%; float: left; } @@ -36,11 +36,6 @@ overflow: hidden; } -#ssp-sidebar .sidebar-content.castos-connected { - border: none; - background: none; -} - #ssp-sidebar .sidebar-content h3 { color: #6c25d0; margin: -20px -20px 0 -20px; diff --git a/assets/js/admin-menu.js b/assets/js/admin-menu.js new file mode 100644 index 00000000..e75ddc8f --- /dev/null +++ b/assets/js/admin-menu.js @@ -0,0 +1,11 @@ +jQuery( document ).ready( function( $ ) { + var initOnboardingMenu = function() { + var $firstMenuItem = $( '#menu-posts-podcast' ).find( '.wp-submenu a.wp-first-item' ); + + if ( $firstMenuItem.length && $firstMenuItem.attr( 'href' ).includes( 'page=ssp-onboarding-1' ) ) { + $firstMenuItem.closest('ul').find('li').not('.wp-first-item').hide(); + } + } + + initOnboardingMenu(); +} ); diff --git a/assets/js/admin.js b/assets/js/admin.js index 65937b54..90888b60 100644 --- a/assets/js/admin.js +++ b/assets/js/admin.js @@ -257,5 +257,19 @@ jQuery(document).ready(function($) { }); } + var initNotifications = function () { + $('.notice.is-constant').on( 'click', '.notice-dismiss', function () { + var $notice = $(this).closest('.notice'), + data = { + 'action': 'remove_constant_notice', + 'id': $notice.data('id'), + 'nonce': $notice.data('nonce'), + }; + + $.post(ajaxurl, data); + }); + }; + initDynamoBtn(); + initNotifications(); }); diff --git a/assets/js/settings.js b/assets/js/settings.js index d6bad924..b26e80ad 100644 --- a/assets/js/settings.js +++ b/assets/js/settings.js @@ -5,11 +5,8 @@ */ jQuery(document).ready(function($) { - - var $podmotorAccountEmail = $("#podmotor_account_email"), - $podmotorAccountAPIToken = $("#podmotor_account_api_token"), - $parentCategories = $('.js-parent-category'), - $validateBtn = $("#validate_api_credentials"); + var $podmotorAccountAPIToken = $("#podmotor_account_api_token"), + $parentCategories = $('.js-parent-category'); const { __ } = wp.i18n; @@ -35,103 +32,106 @@ jQuery(document).ready(function($) { } function initCastosAPICredentials() { - var disableSubmitButton = function () { - /** - * If either API field is empty, disable the submit button - */ - if ($podmotorAccountEmail.val() === '' || $podmotorAccountAPIToken.val() === '') { - $("#ssp-settings-submit").prop("disabled", "disabled"); - } - - /** - * If the user changes the email, disable the submit button - */ - $podmotorAccountEmail.on("change paste keydown keyup", function () { - $("#ssp-settings-submit").prop("disabled", "disabled"); - }); - - /** - * If the user changes the account api key, disable the submit button - */ - $podmotorAccountAPIToken.on("change paste keydown keyup", function () { - $("#ssp-settings-submit").prop("disabled", "disabled"); - }); - }, - /** - * Validate the api credentials - */ - validateAPICredentials = function () { - $validateBtn.on("click", function () { - - var podmotor_account_email = $("#podmotor_account_email").val(), - podmotor_account_api_token = $("#podmotor_account_api_token").val(), - nonce = $("#podcast_settings_tab_nonce").val(), - $msg = $('.validate-api-credentials-message'); - - if (!$msg.length) { - $msg = $(''); - $validateBtn.parent().append($msg); - } - - $msg.html("Validating API credentials..."); - - $validateBtn.addClass('loader'); - - $.ajax({ - method: "GET", - url: ajaxurl, - data: { - action: "validate_castos_credentials", - api_token: podmotor_account_api_token, - email: podmotor_account_email, - nonce: nonce - } - }) - .done(function (response) { - $validateBtn.removeClass('loader'); - if (response.status === 'success') { - $(".validate-api-credentials-message").html("Credentials Valid. Please click 'Save Settings' to save Credentials."); - $("#ssp-settings-submit").prop("disabled", ""); - $validateBtn.val('Valid Credentials'); - $validateBtn.addClass('valid'); - } else { - $validateBtn.addClass('invalid'); - $(".validate-api-credentials-message").html(response.message); - } - $validateBtn.trigger('validated'); - }); - }); - }, - /** - * Disconnect Castos checkbox on change, renders a confirmation message to the user. - */ - disconnectCastos = function () { - $('#podmotor_disconnect').on('change', function (event) { - var $checkbox = $(this); - - // if the change is to uncheck the checkbox - if (!$checkbox.is(':checked')) { - return; - } - - var $message = 'If you disconnect from Castos hosting you will no longer be able to upload media files to the Castos hosting platform. If you’re no longer a Castos customer your media files may no longer be available to your listeners.'; - var user_input = confirm($message); - if (user_input !== true) { - // Ensures this code runs AFTER the browser handles click however it wants. - setTimeout(function () { - $checkbox.removeAttr('checked'); - }, 0); - event.preventDefault(); - event.stopPropagation(); - } - }); - } - - if ($podmotorAccountEmail.length > 0 && $podmotorAccountAPIToken.length > 0) { - disableSubmitButton(); - validateAPICredentials(); - disconnectCastos(); + var $connectBtn = $(".castos-connect"), + disableConnectButton = function(){ + $connectBtn.prop("disabled", "disabled"); + }, + enableConnectButton = function(){ + $connectBtn.prop("disabled", "").removeClass('disabled'); + }, + connectButtonStates = function () { + if ($podmotorAccountAPIToken.length) { + $connectBtn.show(); + } + $podmotorAccountAPIToken.on("focus change paste keydown keyup", function () { + $podmotorAccountAPIToken.val() ? enableConnectButton() : disableConnectButton(); + }); + + $podmotorAccountAPIToken.on("focus", function(){ + $('.connect-castos-message').html('').removeClass('error'); + }); + }, + /** + * Validate the api credentials + */ + initConnect = function () { + $connectBtn.on('click', function (e) { + e.preventDefault(); + e.stopPropagation(); + $connectBtn.prop('disabled', 'disabled'); + $connectBtn.trigger('connecting'); + + var podmotor_account_api_token = $('#podmotor_account_api_token').val(), + nonce = $('#podcast_settings_tab_nonce').val(), + $msg = $('.connect-castos-message'); + + if ($msg.length) { + $msg.html('').removeClass('error'); + } else { + $msg = $(''); + $connectBtn.parent().append($msg); + } + + $connectBtn.addClass('loader'); + + $.ajax({ + method: 'GET', + url: ajaxurl, + data: { + action: 'connect_castos', + api_token: podmotor_account_api_token, + nonce: nonce, + }, + }) + .done(function (response) { + $connectBtn.trigger('connected', response); + if (response.status === 'success') { + $connectBtn.addClass('connected'); + if ( ! $connectBtn.data( 'no-reload' ) ) { + window.location.reload(); + } else { + $msg.html( response.message ); + } + } else { + $connectBtn.removeClass('loader'); + $msg.addClass('error'); + $msg.html(response.message); + } + }) + }) + }, + /** + * Disconnect Castos checkbox on change, renders a confirmation message to the user. + */ + initDisconnect = function () { + var $disconnect = $('#disconnect_castos'); + $disconnect.on('click', function (event) { + var $message = 'If you disconnect from Castos hosting you will no longer be able to upload media files to the Castos hosting platform. If you’re no longer a Castos customer your media files may no longer be available to your listeners.'; + var user_input = confirm($message); + if (user_input === true) { + $disconnect.addClass('loader'); + $disconnect.parent().find('label').remove(); + $.ajax({ + method: 'GET', + url: ajaxurl, + data: { + action: 'disconnect_castos', + nonce: $('#podcast_settings_tab_nonce').val(), + }, + }) + .done(function (response) { + window.location.reload(); + }) + } + }); + } + + if ($podmotorAccountAPIToken.length > 0) { + connectButtonStates(); + initConnect(); } + + initDisconnect(); } function initSubcategoryFiltration(){ diff --git a/package.json b/package.json index 86cfe793..73ec5df8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "seriously-simple-podcasting", - "version": "3.2.0", + "version": "3.5.0", "main": "build/index.js", "author": "CastosHQ", "devDependencies": { diff --git a/php/classes/controllers/class-ads-controller.php b/php/classes/controllers/class-ads-controller.php index 61b0a0e5..29f55e43 100644 --- a/php/classes/controllers/class-ads-controller.php +++ b/php/classes/controllers/class-ads-controller.php @@ -113,7 +113,7 @@ protected function is_ads_enabled_in_castos() { $series_id = $this->get_current_feed_series_id(); - if ( empty( $podcasts['data']['podcast_list'] ) ) { + if ( ! is_array( $podcasts ) || empty( $podcasts['data']['podcast_list'] ) ) { return false; } diff --git a/php/classes/controllers/class-app-controller.php b/php/classes/controllers/class-app-controller.php index bafc448a..a2dce628 100644 --- a/php/classes/controllers/class-app-controller.php +++ b/php/classes/controllers/class-app-controller.php @@ -259,7 +259,9 @@ protected function bootstrap() { $this->episode_repository = new Episode_Repository( $this->feed_handler ); - $this->castos_handler = new Castos_Handler( $this->feed_handler, $this->logger ); + $this->admin_notices_handler = new Admin_Notifications_Handler(); + + $this->castos_handler = new Castos_Handler( $this->feed_handler, $this->logger, $this->admin_notices_handler ); $this->onboarding_controller = new Onboarding_Controller( $this->renderer, $this->settings_handler ); @@ -273,15 +275,13 @@ protected function bootstrap() { $this->widgets_controller = new Widgets_Controller( $this->file, $this->version ); - $this->ajax_handler = new Ajax_Handler( $this->castos_handler ); + $this->ajax_handler = new Ajax_Handler( $this->castos_handler, $this->admin_notices_handler ); $this->podping_handler = new Podping_Handler( $this->logger ); - $this->admin_notices_handler = new Admin_Notifications_Handler( $this->token ); - $this->assets_controller = new Assets_Controller(); - $this->series_handler = new Series_Handler( $this->admin_notices_handler, $this->roles_handler, $this->castos_handler, $this->settings_handler, $this->episode_repository ); + $this->series_handler = new Series_Handler( $this->admin_notices_handler, $this->roles_handler, $this->castos_handler, $this->settings_handler, $this->episode_repository ); $this->upgrade_handler = new Upgrade_Handler( $this->episode_repository, $this->castos_handler, $this->series_handler ); @@ -331,7 +331,7 @@ protected function bootstrap() { $this->load_plugin_textdomain(); } - protected function init_integrations(){ + protected function init_integrations() { /* * Gutenberg integration. * Only load Blocks if the WordPress version is newer than 5.0. @@ -393,7 +393,7 @@ public function get_available_services() { * * @return string * */ - protected function get_wp_version(){ + protected function get_wp_version() { global $wp_version; return $wp_version; @@ -402,7 +402,7 @@ protected function get_wp_version(){ /** * Init REST API */ - protected function init_rest_api(){ + protected function init_rest_api() { global $wp_version; // Only load WP REST API Endpoints if the WordPress version is newer than 4.7. @@ -494,7 +494,6 @@ public function get_settings_handler() { } - /** * Register the Castos Blog dashboard widget * Hooks into the wp_dashboard_setup action hook @@ -547,9 +546,9 @@ public function ssp_castos_dashboard_output( $widget_id, $feeds ) { /** * Check if there is a cached version of the RSS Feed and output it */ - $locale = get_user_locale(); - $cache_key = 'ssp_dash_v2_' . md5( $widget_id . '_' . $locale ); - $rss_output = get_transient( $cache_key ); + $locale = get_user_locale(); + $cache_key = 'ssp_dash_v2_' . md5( $widget_id . '_' . $locale ); + $rss_output = get_transient( $cache_key ); if ( false !== $rss_output ) { return $rss_output; } @@ -568,13 +567,14 @@ public function ssp_castos_dashboard_output( $widget_id, $feeds ) { * Set up the cached version to expire in 12 hours and output the content */ set_transient( $cache_key, $rss_output, 12 * HOUR_IN_SECONDS ); + return $rss_output; } /** * Adding podcast episodes to 'At a glance' dashboard widget * - * @param array $items Existing items + * @param array $items Existing items * * @return array Updated items */ @@ -583,8 +583,8 @@ public function glance_items( $items = array() ) { $num_posts = count( ssp_episodes( - 1, '', false, 'glance' ) ); $post_type_object = get_post_type_object( $this->token ); - $text = _n( '%s Episode', '%s Episodes', $num_posts, 'seriously-simple-podcasting' ); - $text = sprintf( $text, number_format_i18n( $num_posts ) ); + $text = _n( '%s Episode', '%s Episodes', $num_posts, 'seriously-simple-podcasting' ); + $text = sprintf( $text, number_format_i18n( $num_posts ) ); if ( $post_type_object && current_user_can( $post_type_object->cap->edit_posts ) ) { $items[] = sprintf( '%2$s', $this->token, $text ) . "\n"; @@ -598,10 +598,10 @@ public function glance_items( $items = array() ) { /** * Adding appreciation links to the SSP record in the plugin list table * - * @param array $plugin_meta Default plugin meta links - * @param string $plugin_file Plugin file - * @param array $plugin_data Array of plugin data - * @param string $status Plugin status + * @param array $plugin_meta Default plugin meta links + * @param string $plugin_file Plugin file + * @param array $plugin_data Array of plugin data + * @param string $status Plugin status * * @return array Modified plugin meta links */ @@ -613,6 +613,7 @@ public function plugin_row_meta( $plugin_meta = array(), $plugin_file = '', $plu $plugin_meta['docs'] = '' . __( 'Documentation', 'seriously-simple-podcasting' ) . ''; $plugin_meta['addons'] = '' . __( 'Add-ons', 'seriously-simple-podcasting' ) . ''; $plugin_meta['review'] = '' . __( 'Write a review', 'seriously-simple-podcasting' ) . ''; + return $plugin_meta; } @@ -650,7 +651,7 @@ public function load_plugin_textdomain() { /** * Hide RSS footer created by WordPress SEO from podcast RSS feed * - * @param boolean $include_footer Default inclusion value + * @param boolean $include_footer Default inclusion value * * @return boolean Modified inclusion value */ @@ -688,6 +689,7 @@ public function activate() { public function deactivate() { flush_rewrite_rules(); $this->roles_handler->remove_custom_roles(); + $this->castos_handler->remove_api_credentials(); } /** @@ -710,7 +712,7 @@ public function maybe_run_plugin_updates() { /** * Add rating link to admin footer on SSP settings pages * - * @param string $footer_text Default footer text + * @param string $footer_text Default footer text * * @return string Modified footer text */ @@ -722,7 +724,7 @@ public function admin_footer_text( $footer_text ) { // Change the footer text if ( ! get_option( 'ssp_admin_footer_text_rated' ) ) { $footer_text = sprintf( __( 'If you like %1$sSeriously Simple Podcasting%2$s please leave a %3$s★★★★★%4$s rating. A huge thank you in advance!', 'seriously-simple-podcasting' ), '', '', '', '' ); - $footer_text .= sprintf("