diff --git a/.eslintrc.json b/.eslintrc.json index d8ee40e3..83237ed3 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -2,7 +2,10 @@ "globals": { "adminAutoshareForTwitter": "readonly", "jQuery": "readonly", - "wp": "readonly" + "wp": "readonly", + "FormData": "readonly", + "fetch": "readonly", + "ajaxurl": "readonly" }, "extends": "plugin:@wordpress/eslint-plugin/recommended" } diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..0acec209 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,8 @@ +# These owners will be the default owners for everything in the repo. Unless a later match takes precedence, @iamdharmesh, as primary maintainer will be requested for review when someone opens a Pull Request. +* @iamdharmesh + +# GitHub and WordPress.org specifics +/.github/ @jeffpaul +/.wordpress-org/ @jeffpaul +CODE_OF_CONDUCT.md @jeffpaul +LICENSE.md @jeffpaul diff --git a/.github/workflows/build-release-zip.yml b/.github/workflows/build-release-zip.yml index fb12f47c..afcc2cf6 100644 --- a/.github/workflows/build-release-zip.yml +++ b/.github/workflows/build-release-zip.yml @@ -2,6 +2,7 @@ name: Build release zip on: workflow_dispatch: + workflow_call: push: branches: - trunk @@ -31,7 +32,10 @@ jobs: env: cache-name: cache-node-modules with: - path: node_modules + path: | + node_modules + ~/.cache + ~/.npm key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} - name: Setup node version and npm cache diff --git a/.github/workflows/cypress.yml b/.github/workflows/cypress.yml index d9299824..09ee3b5a 100644 --- a/.github/workflows/cypress.yml +++ b/.github/workflows/cypress.yml @@ -8,9 +8,17 @@ on: pull_request: branches: - develop + paths: + - '**.js' + - '**.php' + - '**.css' jobs: + build: + uses: 10up/autoshare-for-twitter/.github/workflows/build-release-zip.yml@develop + cypress: + needs: build runs-on: ubuntu-latest strategy: @@ -22,26 +30,47 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - - name: Install dependencies - run: npm i && npm i -g cypress@9.0.0 + - name: Download build zip + uses: actions/download-artifact@v3 + with: + name: ${{ github.event.repository.name }} + path: ${{ github.event.repository.name }} + + - name: Display structure of downloaded files + run: ls -R + working-directory: ${{ github.event.repository.name }} - - name: Build asset - run: npm run build + - name: Cache node_modules + id: cache-node-modules + uses: actions/cache@v3 + env: + cache-name: cache-node-modules + with: + path: | + node_modules + ~/.cache + ~/.npm + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} - - name: Add dependencies - run: composer install -o --no-dev --ignore-platform-reqs + - name: Install dependencies + run: npm install - - name: Set the core version - run: ./tests/bin/set-core-version.js ${{ matrix.core.version }} + - name: Set the core version and plugins config + run: ./tests/bin/set-wp-config.js --core=${{ matrix.core.version }} --plugins=./${{ github.event.repository.name }},https://downloads.wordpress.org/plugin/classic-editor.1.6.1.zip - name: Set up WP environment run: npm run env:start - name: Test - run: cypress run --config-file tests/cypress/config.json --env TWITTER_API_KEY=${{ secrets.TWITTER_API_KEY }},TWITTER_API_SECRET=${{ secrets.TWITTER_API_SECRET }},TWITTER_ACCESS_TOKEN=${{ secrets.TWITTER_ACCESS_TOKEN }},TWITTER_ACCESS_SECRET=${{ secrets.TWITTER_ACCESS_SECRET }} - + run: npm run cypress:run + env: + CYPRESS_TWITTER_API_KEY: ${{ secrets.TWITTER_API_KEY }} + CYPRESS_TWITTER_API_SECRET: ${{ secrets.TWITTER_API_SECRET }} + CYPRESS_TWITTER_ACCESS_TOKEN: ${{ secrets.TWITTER_ACCESS_TOKEN }} + CYPRESS_TWITTER_ACCESS_SECRET: ${{ secrets.TWITTER_ACCESS_SECRET }} + - name: Upload artifacts uses: actions/upload-artifact@v2 if: failure() diff --git a/.github/workflows/eslint.yml b/.github/workflows/eslint.yml index 1493f8d4..91314fd1 100644 --- a/.github/workflows/eslint.yml +++ b/.github/workflows/eslint.yml @@ -8,6 +8,8 @@ on: pull_request: branches: - develop + paths: # Note: Update paths here will also require updating paths in ignore.eslint.yml. + - '**.js' jobs: eslint: diff --git a/.github/workflows/ignore-eslint.yml b/.github/workflows/ignore-eslint.yml new file mode 100644 index 00000000..9400f9f1 --- /dev/null +++ b/.github/workflows/ignore-eslint.yml @@ -0,0 +1,14 @@ +name: ESLint + +on: + pull_request: + branches: + - develop + paths-ignore: + - '**.js' + +jobs: + eslint: + runs-on: ubuntu-latest + steps: + - run: 'echo "No ESLint check required"' diff --git a/.github/workflows/ignore-phpcs.yml b/.github/workflows/ignore-phpcs.yml new file mode 100644 index 00000000..33aa4180 --- /dev/null +++ b/.github/workflows/ignore-phpcs.yml @@ -0,0 +1,14 @@ +name: PHPCS + +on: + pull_request: + branches: + - develop + paths-ignore: + - '**.php' + +jobs: + phpcs: + runs-on: ubuntu-latest + steps: + - run: 'echo "No PHPCS check required"' diff --git a/.github/workflows/ignore-phpunit.yml b/.github/workflows/ignore-phpunit.yml new file mode 100644 index 00000000..93b13956 --- /dev/null +++ b/.github/workflows/ignore-phpunit.yml @@ -0,0 +1,18 @@ +name: PHPUnit + +on: + pull_request: + branches: + - develop + paths-ignore: + - '**.php' + +jobs: + phpunit: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + php-versions: ['7.4', '8.0'] + steps: + - run: 'echo "No PHPUnit check required"' diff --git a/.github/workflows/php-compatibility.yml b/.github/workflows/php-compatibility.yml index b563cf6c..30692ede 100644 --- a/.github/workflows/php-compatibility.yml +++ b/.github/workflows/php-compatibility.yml @@ -8,6 +8,8 @@ on: pull_request: branches: - develop + paths: + - '**.php' jobs: php_compatibility: diff --git a/.github/workflows/phpcs.yml b/.github/workflows/phpcs.yml index a806311a..95c10242 100644 --- a/.github/workflows/phpcs.yml +++ b/.github/workflows/phpcs.yml @@ -8,6 +8,8 @@ on: pull_request: branches: - develop + paths: # Note: Update paths here will also require updating paths in ignore.phpcs.yml. + - '**.php' jobs: phpcs: diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml index 1e0f8d3f..6ae0c436 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/phpunit.yml @@ -8,6 +8,8 @@ on: pull_request: branches: - develop + paths: # Note: Update paths here will also require updating paths in ignore.phpunit.yml. + - '**.php' jobs: phpunit: diff --git a/.wordpress-org/screenshot-1.png b/.wordpress-org/screenshot-1.png new file mode 100644 index 00000000..06ee489a Binary files /dev/null and b/.wordpress-org/screenshot-1.png differ diff --git a/.wordpress-org/screenshot-2.png b/.wordpress-org/screenshot-2.png new file mode 100644 index 00000000..ecda3116 Binary files /dev/null and b/.wordpress-org/screenshot-2.png differ diff --git a/.wordpress-org/screenshot-3.png b/.wordpress-org/screenshot-3.png new file mode 100644 index 00000000..e28a5636 Binary files /dev/null and b/.wordpress-org/screenshot-3.png differ diff --git a/CHANGELOG.md b/CHANGELOG.md index 34db8072..d7d86429 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,28 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] - TBD -## [1.2.1] - 2022-12-05 +## [1.3.0] - 2023-01-19 +### Added +- "Tweet now" functionality (props [@Sidsector9](https://github.com/Sidsector9), [@iamdharmesh](https://github.com/iamdharmesh), [@cadic](https://github.com/cadic), [@jeffpaul](https://github.com/jeffpaul), [@linawiezkowiak](https://github.com/linawiezkowiak), [@oszkarnagy](https://github.com/oszkarnagy) via [#188](https://github.com/10up/autoshare-for-twitter/pull/188)). +- Toggle for adding/removing featured image from the tweet (props [@Sidsector9](https://github.com/Sidsector9), [@iamdharmesh](https://github.com/iamdharmesh), [@cadic](https://github.com/cadic), [@jeffpaul](https://github.com/jeffpaul), [@linawiezkowiak](https://github.com/linawiezkowiak), [@oszkarnagy](https://github.com/oszkarnagy) via [#188](https://github.com/10up/autoshare-for-twitter/pull/188)). +- Show Twitter status logs for the draft post if the post has been switched back to Draft from Published, and has already been Tweeted (props [@iamdharmesh](https://github.com/iamdharmesh), [@faisal-alvi](https://github.com/faisal-alvi), [@jeffpaul](https://github.com/jeffpaul), [@linawiezkowiak](https://github.com/linawiezkowiak), [@oszkarnagy](https://github.com/oszkarnagy) via [#215](https://github.com/10up/autoshare-for-twitter/pull/215)). +- Plugin screenshots to readme files (props [@iamdharmesh](https://github.com/iamdharmesh) via [#218](https://github.com/10up/autoshare-for-twitter/pull/218)). + +### Changed +- UI Improvements in Tweet status (props [@Sidsector9](https://github.com/Sidsector9), [@iamdharmesh](https://github.com/iamdharmesh), [@cadic](https://github.com/cadic), [@jeffpaul](https://github.com/jeffpaul), [@linawiezkowiak](https://github.com/linawiezkowiak), [@oszkarnagy](https://github.com/oszkarnagy) via [#188](https://github.com/10up/autoshare-for-twitter/pull/188)). +- UI Improvements in tweet message character count (props [@iamdharmesh](https://github.com/iamdharmesh), [@Sidsector9](https://github.com/Sidsector9), [@ravinderk](https://github.com/ravinderk), [@jeffpaul](https://github.com/jeffpaul), [@linawiezkowiak](https://github.com/linawiezkowiak), [@oszkarnagy](https://github.com/oszkarnagy) via [#214](https://github.com/10up/autoshare-for-twitter/pull/214)). +- Run GitHub Action workflows only when it required (props [@iamdharmesh](https://github.com/iamdharmesh), [@peterwilsoncc](https://github.com/peterwilsoncc) via [#204](https://github.com/10up/autoshare-for-twitter/pull/204)). +- Migrated Cypress from 9.0.0 to 11.2.0 (props [@iamdharmesh](https://github.com/iamdharmesh), [@Sidsector9](https://github.com/Sidsector9) via [#205](https://github.com/10up/autoshare-for-twitter/pull/205)). +- Run E2E tests on the zip generated by "Build release zip" action (props [@iamdharmesh](https://github.com/iamdharmesh), [@dkotter](https://github.com/dkotter), [@Sidsector9](https://github.com/Sidsector9) via [#206](https://github.com/10up/autoshare-for-twitter/pull/206)). + +### Fixed +- E2E tests fail in the CI with warm cache (props [@cadic](https://github.com/cadic), [@iamdharmesh](https://github.com/iamdharmesh) via [#212](https://github.com/10up/autoshare-for-twitter/pull/212)). + +### Security +- Bump `decode-uri-component` from 0.2.0 to 0.2.2 (props [@dependabot](https://github.com/apps/dependabot), [@iamdharmesh](https://github.com/iamdharmesh) via [#208](https://github.com/10up/autoshare-for-twitter/pull/208)). +- Bump `simple-git` from 3.14.1 to 3.15.1 (props [@dependabot](https://github.com/apps/dependabot), [@iamdharmesh](https://github.com/iamdharmesh) via [#210](https://github.com/10up/autoshare-for-twitter/pull/210)). + +## [1.2.1] - 2022-12-07 **Note that this release bumps the WordPress minimum from 5.3 to 5.7 and PHP minimum from 7.2 to 7.4.** ### Added @@ -159,6 +180,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - Initial closed source release (props [@scottlee](https://github.com/scottlee/)). [Unreleased]: https://github.com/10up/autoshare-for-twitter/compare/trunk...develop +[1.3.0]: https://github.com/10up/autoshare-for-twitter/compare/1.2.1...1.3.0 [1.2.1]: https://github.com/10up/autoshare-for-twitter/compare/1.2.0...1.2.1 [1.2.0]: https://github.com/10up/autoshare-for-twitter/compare/1.1.2...1.2.0 [1.1.2]: https://github.com/10up/autoshare-for-twitter/compare/1.1.1...1.1.2 diff --git a/CREDITS.md b/CREDITS.md index 7350e703..41ef1ca0 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -4,13 +4,13 @@ The following acknowledges the Maintainers for this repository, those who have C The following individuals are responsible for curating the list of issues, responding to pull requests, and ensuring regular releases happen. -[Jeffrey Paul (@jeffpaul)](https://github.com/jeffpaul), [Dharmesh Patel (@iamdharmesh)](https://github.com/iamdharmesh). +[Dharmesh Patel (@iamdharmesh)](https://github.com/iamdharmesh), [Jeffrey Paul (@jeffpaul)](https://github.com/jeffpaul). ## Contributors Thank you to all the people who have already contributed to this repository via bug reports, code, design, ideas, project management, translation, testing, etc. -[Adam Silverstein (@adamsilverstein)](https://github.com/adamsilverstein), [John Watkins (@johnwatkins0)](https://github.com/johnwatkins0), [Scott Lee (@scottlee)](https://github.com/scottlee), [Jeffrey Paul (@jeffpaul)](https://github.com/jeffpaul), [Stephanie Campbell (@sncampbell)](https://github.com/sncampbell), [Ryan Welcher (@ryanwelcher)](https://github.com/ryanwelcher), [Ricky Lee Whittemore (@rickalee)](https://github.com/rickalee), [Tung Du (@dinhtungdu)](https://github.com/dinhtungdu), [Lina Wiezkowiak (@linawiezkowiak)](https://github.com/linawiezkowiak), [Oszkar Nagy (@oszkarnagy)](https://github.com/oszkarnagy), [Helen Hou-Sandi (@helen)](https://github.com/helen), [Thrijith Thankachan (@thrijith)](https://github.com/thrijith), [Barney Jeffries (@barneyjeffries)](https://github.com/barneyjeffries), [Darin Kotter (@dkotter)](https://github.com/dkotter), [Siddharth Thevaril (@Sidsector9)](https://github.com/Sidsector9), [Dharmesh Patel (@iamdharmesh)](https://github.com/iamdharmesh), [Faisal Alvi (@faisal-alvi)](https://github.com/faisal-alvi), [Sudip Dadhaniya (@sudip-10up)](https://github.com/sudip-10up), [Peter Wilson (@peterwilsoncc)](https://github.com/peterwilsoncc), [Max Lyuchin (@cadic)](https://github.com/cadic), [Vikram Moparthy (@vikrampm1)](https://github.com/vikrampm1), [GitHub Dependabot (@dependabot)](https://github.com/apps/dependabot). +[Adam Silverstein (@adamsilverstein)](https://github.com/adamsilverstein), [John Watkins (@johnwatkins0)](https://github.com/johnwatkins0), [Scott Lee (@scottlee)](https://github.com/scottlee), [Jeffrey Paul (@jeffpaul)](https://github.com/jeffpaul), [Stephanie Campbell (@sncampbell)](https://github.com/sncampbell), [Ryan Welcher (@ryanwelcher)](https://github.com/ryanwelcher), [Ricky Lee Whittemore (@rickalee)](https://github.com/rickalee), [Tung Du (@dinhtungdu)](https://github.com/dinhtungdu), [Lina Wiezkowiak (@linawiezkowiak)](https://github.com/linawiezkowiak), [Oszkar Nagy (@oszkarnagy)](https://github.com/oszkarnagy), [Helen Hou-Sandi (@helen)](https://github.com/helen), [Thrijith Thankachan (@thrijith)](https://github.com/thrijith), [Barney Jeffries (@barneyjeffries)](https://github.com/barneyjeffries), [Darin Kotter (@dkotter)](https://github.com/dkotter), [Siddharth Thevaril (@Sidsector9)](https://github.com/Sidsector9), [Dharmesh Patel (@iamdharmesh)](https://github.com/iamdharmesh), [Faisal Alvi (@faisal-alvi)](https://github.com/faisal-alvi), [Sudip Dadhaniya (@sudip-10up)](https://github.com/sudip-10up), [Peter Wilson (@peterwilsoncc)](https://github.com/peterwilsoncc), [Max Lyuchin (@cadic)](https://github.com/cadic), [Vikram Moparthy (@vikrampm1)](https://github.com/vikrampm1), [GitHub Dependabot (@dependabot)](https://github.com/apps/dependabot), [Ravinder Kumar (@ravinderk)](https://github.com/ravinderk). ## Libraries diff --git a/README.md b/README.md index 4efc263f..10af0ffa 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,14 @@ add_filter( 'dt_blacklisted_meta', function( $blacklisted_metas ) { } ) ``` +| Autoshare for Twitter Settings, found under `Settings` > `Autoshare for Twitter`. | +| --------------- | +|[![Autoshare for Twitter Settings, found under `Settings` > `Autoshare for Twitter`.](.wordpress-org/screenshot-1.png)](.wordpress-org/screenshot-1.png)| + +| Create post screen with Autoshare for Twitter options | Published post screen with Autoshare for Twitter options. | +| -------------- | ---------------- | +| [![Create post screen with Autoshare for Twitter options](.wordpress-org/screenshot-2.png)](.wordpress-org/screenshot-2.png) | [![Published post screen with Autoshare for Twitter options.](.wordpress-org/screenshot-3.png)](.wordpress-org/screenshot-3.png) | + ## Requirements - PHP 7.4+ @@ -61,9 +69,9 @@ add_filter( 'dt_blacklisted_meta', function( $blacklisted_metas ) { ## Installation -1. Upload the entire `/autoshare-for-twitter` directory to the `/wp-content/plugins/` directory. -2. Activate the plugin -3. Register post type support for types that should be allowed to autoshare. `add_post_type_support( 'post', 'autoshare-for-twitter' );` +1. Install the plugin via the plugin installer, either by searching for it or uploading a .ZIP file. +2. Activate the plugin. +3. Save Twitter connection settings, found under `Settings` > `Autoshare for Twitter`. ## FAQs diff --git a/assets/css/admin-autoshare-for-twitter.css b/assets/css/admin-autoshare-for-twitter.css index 25106eaa..c86bf703 100644 --- a/assets/css/admin-autoshare-for-twitter.css +++ b/assets/css/admin-autoshare-for-twitter.css @@ -10,6 +10,7 @@ #autoshare_for_twitter_metabox pre { font-size: 11px; white-space: normal; + margin: 0px; } .autoshare-for-twitter-toggle-control { @@ -24,13 +25,12 @@ margin-top: 0.5rem; } -.autoshare-for-twitter-post-status { +.autoshare-for-twitter-post-status .autoshare-for-twitter-log{ font-style: italic; } .autoshare-for-twitter-post-status svg { - transform: translate3d(0, 3px, 0); - margin-right: 5px; + margin-right: 4px; } #autoshare-for-twitter-icon.pending, @@ -73,7 +73,6 @@ textarea#autoshare-for-twitter-text { width: 100% } span#autoshare-for-twitter-counter-wrap { - background: rgba(0, 0, 0, 0.07); font-family: Consolas, Monaco, monospace; } @@ -130,3 +129,90 @@ tbody .autoshare-for-twitter-status-logo--disabled::before { width: 24px; height: 24px; } + +.autoshare-for-twitter-log { + display: flex; + align-items: flex-start; + margin-bottom: 5px; +} + +.autoshare-for-twitter-log svg { + max-width: 20px; + height: auto; +} + + +#autoshare_for_twitter_metabox .autoshare-for-twitter-status-icon::before { + background-image: url('../images/twitter_default.svg'); + background-repeat: no-repeat; + background-size: 24px 24px; + content: ' '; + display: inline-block; + height: 24px; + width: 24px; + vertical-align: top; + margin-right: 5px; +} + +#autoshare_for_twitter_metabox .autoshare-for-twitter-status-icon--published::before { + background-image: url('../images/twitter_tweeted.svg'); +} + +#autoshare_for_twitter_metabox .autoshare-for-twitter-status-icon--error::before { + background-image: url('../images/twitter_failed.svg'); +} + +#autoshare_for_twitter_metabox .autoshare-for-twitter-status-icon--enabled::before { + background-image: url('../images/twitter_enabled.svg'); +} + +#autoshare_for_twitter_metabox .autoshare-for-twitter-status-icon--disabled::before { + background-image: url('../images/twitter_disabled.svg'); +} + +#autoshare_for_twitter_metabox .autoshare-for-twitter-status-log-data { + display: inline-block; + max-width: 85%; +} + +#autoshare_for_twitter_metabox .autoshare-for-twitter-status-wrap { + margin-bottom: 8px; +} + +#autoshare_for_twitter_metabox .tweet-now-button{ + text-decoration: none; + padding: 5px 12px 5px 0px; + font-weight: bold; + box-shadow: none; + outline: 0px; + background-color: #fff; +} + +#autoshare_for_twitter_metabox .tweet-now-button span.dashicons { + vertical-align: bottom; + font-size: 16px; +} + +#autoshare_for_twitter_metabox .autoshare-for-twitter-tweet-now-wrapper label.autoshare-for-twitter-enable-wrap { + display: none; +} + +#autoshare_for_twitter_metabox .autoshare-for-twitter-tweet-now-wrapper a.cancel-tweet-text { + display: none; +} + +#autoshare_for_twitter_metabox p#autoshare-for-twitter-error-message { + color: red; +} + +#autoshare_for_twitter_metabox .autoshare-for-twitter-tweet-now-wrapper span.spinner { + float: none; +} + +#autoshare_for_twitter_metabox span#autoshare-for-twitter-counter-wrap { + font-size: 12px; +} + +.autoshare-for-twitter-editor-panel .autoshare-for-twitter-tweet-text label { + width: 100%; +} diff --git a/assets/js/admin-autoshare-for-twitter.js b/assets/js/admin-autoshare-for-twitter.js index b46e95c0..cf38e319 100644 --- a/assets/js/admin-autoshare-for-twitter.js +++ b/assets/js/admin-autoshare-for-twitter.js @@ -12,9 +12,12 @@ $editLink = $('#autoshare-for-twitter-edit'), $editBody = $('#autoshare-for-twitter-override-body'), $hideLink = $('.cancel-tweet-text'), + $allowTweetImage = $('#autoshare-for-twitter-tweet-allow-image'), errorMessageContainer = document.getElementById('autoshare-for-twitter-error-message'), counterWrap = document.getElementById('autoshare-for-twitter-counter-wrap'), + allowTweetImageWrap = $('.autoshare-for-twitter-tweet-allow-image-wrap'), limit = 280; + const { __, sprintf } = wp.i18n; // Add enabled class if checked if ($tweetPost.prop('checked')) { @@ -24,6 +27,8 @@ // Event handlers. $tweetPost.on('click', handleRequest); $tweetText.change(handleRequest); + $tweetPost.change(toggleAllowImageVisibility); + $allowTweetImage.change(handleRequest); $editLink.on('click', function() { $editBody.slideToggle(); updateRemainingField(); @@ -43,6 +48,7 @@ if ('' === adminAutoshareForTwitter.currentStatus) { handleRequest(event, true); } + updateRemainingField(); }; /** @@ -71,6 +77,7 @@ var data = {}; data[adminAutoshareForTwitter.enableAutoshareKey] = status; data[adminAutoshareForTwitter.tweetBodyKey] = $tweetText.val(); + data[adminAutoshareForTwitter.allowTweetImageKey] = $allowTweetImage.prop('checked'); $('#submit').attr('disabled', true); wp.apiFetch({ @@ -91,13 +98,21 @@ $icon.removeClass('pending'); if (data.enabled) { - $icon.toggleClass('enabled'); + $icon.removeClass('disabled'); + $icon.addClass('enabled'); $tweetPost.prop('checked', true); } else { - $icon.toggleClass('disabled'); + $icon.removeClass('enabled'); + $icon.addClass('disabled'); $tweetPost.prop('checked', false); } + if (data.allowImage) { + $allowTweetImage.prop('checked', true); + } else { + $allowTweetImage.prop('checked', false); + } + $('#submit').attr('disabled', false); }) .catch(onRequestFail); @@ -107,18 +122,40 @@ * Updates the counter */ function updateRemainingField() { - var count = $tweetText.val().length; + let permalinkLength = 0; + if ( $('#sample-permalink').length ) { + permalinkLength = $('#sample-permalink').text().length + } + // +5 because of the space between body and URL and the ellipsis. + permalinkLength += 5; + + var count = $tweetText.val().length + permalinkLength; + $tweetText.attr('maxlength', limit - permalinkLength); $(counterWrap).text(count); // Toggle the .over-limit class. - if (limit < count) { + if (limit <= count) { + counterWrap.classList.remove('near-limit'); counterWrap.classList.add('over-limit'); - } else if (counterWrap.classList.contains('over-limit')) { + /* translators: %d is tweet message character count */ + $(counterWrap).text( sprintf( __( '%d - Too Long!', 'autoshare-for-twitter' ), count ) ); + } else if (240 <= count) { + counterWrap.classList.remove('over-limit'); + counterWrap.classList.add('near-limit'); + /* translators: %d is tweet message character count */ + $(counterWrap).text( sprintf( __( '%d - Getting Long!', 'autoshare-for-twitter' ), count ) ); + } else { + counterWrap.classList.remove('near-limit'); counterWrap.classList.remove('over-limit'); } } + // Update the counter when the permalink is changed. + $( '#titlediv' ).on( 'focus', '.edit-slug', function() { + updateRemainingField(); + }); + /** * Helper for toggling classes to indicate something is happening. */ @@ -127,4 +164,82 @@ $icon.removeClass('enabled'); $icon.removeClass('disabled'); } + + // Show/Hide "Use featured image in Tweet" checkbox. + if ( allowTweetImageWrap && wp.media.featuredImage ) { + toggleAllowImageVisibility(); + // Listen event for add/remove featured image. + wp.media.featuredImage.frame().on( 'select', toggleAllowImageVisibility ); + $('#postimagediv').on( 'click', '#remove-post-thumbnail', toggleAllowImageVisibility ); + } + + /** + * Show/Hide "Use featured image in Tweet" checkbox. + */ + function toggleAllowImageVisibility( event ) { + let hasMedia = wp.media.featuredImage.get(); + // Handle remove post thumbnail click + if( event && event.target && 'remove-post-thumbnail' === event.target.id && 'click' === event.type ) { + hasMedia = -1; + } + + const tweetNow = $('#tweet_now').length; + const autoshareEnabled = $tweetPost.prop('checked'); + // Autoshare is enabled and post has featured image. + if ( hasMedia > 0 && ( autoshareEnabled || tweetNow ) ) { + allowTweetImageWrap.show(); + } else { + allowTweetImageWrap.hide(); + } + } + + // Tweet Now functionality. + $('#tweet_now').on('click', function() { + $("#autoshare-for-twitter-error-message").html(''); + $(this).addClass("disabled"); + $(".autoshare-for-twitter-tweet-now-wrapper span.spinner").addClass("is-active"); + + const postId = $("#post_ID").val(); + const body = new FormData(); + body.append( 'action', adminAutoshareForTwitter.retweetAction ); + body.append( 'nonce', adminAutoshareForTwitter.nonce ); + body.append( 'post_id', postId ); + body.append( 'is_classic', 1 ); + + // Send request to Tweet now. + fetch( ajaxurl, { + method: 'POST', + body, + } ) + .then((response) => response.json()) + .then((response) => { + if ( + response && response.data && + ( ( response.success && response.data.message ) || ( false === response.success && false === response.data.is_retweeted) ) + ) { + $('.autoshare-for-twitter-status-logs-wrapper').html(response.data.message); + } else { + $("#autoshare-for-twitter-error-message").html(adminAutoshareForTwitter.unknownErrorText); + } + }) + .catch((error) => { + if(error.message){ + $("#autoshare-for-twitter-error-message").html(error.message); + } else { + $("#autoshare-for-twitter-error-message").html(adminAutoshareForTwitter.unknownErrorText); + } + }) + .finally(() => { + $(this).removeClass("disabled"); + $(".autoshare-for-twitter-tweet-now-wrapper span.spinner").removeClass("is-active"); + }); + }); + + // Toggle Tweet Now panel + jQuery("#autoshare_for_twitter_metabox .tweet-now-button").on("click", function(e){ + e.preventDefault(); + $editBody.show(); + jQuery(this).find('span').toggleClass('dashicons-arrow-up-alt2'); + jQuery(".autoshare-for-twitter-tweet-now-wrapper").slideToggle(); + }); })(jQuery); diff --git a/autoshare-for-twitter.php b/autoshare-for-twitter.php index 255eb06b..b6cd7b50 100644 --- a/autoshare-for-twitter.php +++ b/autoshare-for-twitter.php @@ -3,7 +3,7 @@ * Plugin Name: Autoshare for Twitter * Description: Automatically tweets the post title or custom message and a link to the post. * Disclaimer: TWITTER, TWEET, RETWEET and the Twitter logo are trademarks of Twitter, Inc. or its affiliates. - * Version: 1.2.1 + * Version: 1.3.0 * Requires at least: 5.7 * Requires PHP: 7.4 * Author: 10up @@ -20,7 +20,7 @@ } define( 'AUTOSHARE_FOR_TWITTER', __FILE__ ); -define( 'AUTOSHARE_FOR_TWITTER_VERSION', '1.2.1' ); +define( 'AUTOSHARE_FOR_TWITTER_VERSION', '1.3.0' ); define( 'AUTOSHARE_FOR_TWITTER_URL', plugin_dir_url( __FILE__ ) ); define( 'AUTOSHARE_FOR_TWITTER_PATH', plugin_dir_path( __FILE__ ) ); define( 'AUTOSHARE_FOR_TWITTER_INC', AUTOSHARE_FOR_TWITTER_PATH . 'includes/' ); diff --git a/composer.json b/composer.json index cb6db5d1..c6362de0 100644 --- a/composer.json +++ b/composer.json @@ -29,7 +29,7 @@ }, "minimum-stability": "dev", "config": { - "autoloader-suffix": "10upAutoshareForTwitterV121", + "autoloader-suffix": "10upAutoshareForTwitterV130", "allow-plugins": { "dealerdirect/phpcodesniffer-composer-installer": true } diff --git a/includes/admin/assets.php b/includes/admin/assets.php index f447aee6..dcb0aa15 100644 --- a/includes/admin/assets.php +++ b/includes/admin/assets.php @@ -12,10 +12,12 @@ use function TenUp\AutoshareForTwitter\Utils\opted_into_autoshare_for_twitter; use function TenUp\AutoshareForTwitter\REST\post_autoshare_for_twitter_meta_rest_route; use function TenUp\AutoshareForTwitter\Utils\autoshare_enabled; +use function TenUp\AutoshareForTwitter\Utils\tweet_image_allowed; use const TenUp\AutoshareForTwitter\Core\Post_Meta\ENABLE_AUTOSHARE_FOR_TWITTER_KEY; use const TenUp\AutoshareForTwitter\Core\Post_Meta\TWEET_BODY_KEY; use const TenUp\AutoshareForTwitter\Core\Post_Meta\TWITTER_STATUS_KEY; +use const TenUp\AutoshareForTwitter\Core\Post_Meta\TWEET_ALLOW_IMAGE; /** * The handle used in registering plugin assets. @@ -201,6 +203,9 @@ function localize_data( $handle = SCRIPT_HANDLE ) { 'status' => $status_meta && is_array( $status_meta ) ? $status_meta : null, 'unknownErrorText' => __( 'An unknown error occurred', 'autoshare-for-twitter' ), 'siteUrl' => home_url(), + 'allowTweetImage' => tweet_image_allowed( $post_id ), + 'allowTweetImageKey' => TWEET_ALLOW_IMAGE, + 'retweetAction' => 'tenup_autoshare_retweet', ]; wp_localize_script( $handle, 'adminAutoshareForTwitter', $localization ); diff --git a/includes/admin/post-meta.php b/includes/admin/post-meta.php index fec044a3..12c2fb39 100644 --- a/includes/admin/post-meta.php +++ b/includes/admin/post-meta.php @@ -14,6 +14,7 @@ use function TenUp\AutoshareForTwitter\Utils\autoshare_enabled; use function TenUp\AutoshareForTwitter\Utils\update_autoshare_for_twitter_meta; +use function TenUp\AutoshareForTwitter\Utils\tweet_image_allowed; use function TenUp\AutoshareForTwitter\Utils\delete_autoshare_for_twitter_meta; /** @@ -38,14 +39,16 @@ */ const TWITTER_STATUS_KEY = 'status'; +const TWEET_ALLOW_IMAGE = 'tweet-allow-image'; + /** * The setup function * * @return void */ function setup() { - add_action( 'post_submitbox_misc_actions', __NAMESPACE__ . '\tweet_submitbox_callback', 15 ); - add_action( 'autoshare_for_twitter_metabox', __NAMESPACE__ . '\render_tweet_submitbox', 10, 1 ); + // Add Autoshare for twitter meta box to classic editor. + add_action( 'add_meta_boxes', __NAMESPACE__ . '\autoshare_for_twitter_metabox', 10, 2 ); add_action( 'save_post', __NAMESPACE__ . '\save_tweet_meta', 10, 3 ); } @@ -114,6 +117,7 @@ function sanitize_autoshare_for_twitter_meta_data( $data ) { $filtered_data = []; foreach ( $data as $key => $value ) { switch ( $key ) { + case TWEET_ALLOW_IMAGE: case ENABLE_AUTOSHARE_FOR_TWITTER_KEY: $filtered_data[ $key ] = boolval( $value ); break; @@ -149,6 +153,16 @@ function save_autoshare_for_twitter_meta_data( $post_id, $data ) { } } + if ( ! array_key_exists( TWEET_ALLOW_IMAGE, $data ) ) { + // phpcs:ignore WordPress.Security.NonceVerification.Missing + if ( isset( $_POST['classic-editor'] ) ) { + // Handle unchecked "Tweet this post" checkbox for classic editor. + $data[ TWEET_ALLOW_IMAGE ] = 0; + } else { + $data[ TWEET_ALLOW_IMAGE ] = tweet_image_allowed( $post_id ) ? 1 : 0; + } + } + foreach ( $data as $key => $value ) { switch ( $key ) { case ENABLE_AUTOSHARE_FOR_TWITTER_KEY: @@ -161,19 +175,28 @@ function save_autoshare_for_twitter_meta_data( $post_id, $data ) { } else { delete_autoshare_for_twitter_meta( $post_id, TWEET_BODY_KEY ); } + + break; + + case TWEET_ALLOW_IMAGE: + update_autoshare_for_twitter_meta( $post_id, TWEET_ALLOW_IMAGE, $value ? 'yes' : 'no' ); + break; + + default: + break; } } } /** - * Callback for the Auto Tweet box in the Submit meta box. + * Add Autoshare for twitter metabox on post/post types. * - * @param \WP_Post $post The post being submitted. + * @param string $post_type Post Type. + * @param WP_Post $post WP_Post object. * - * @return void + * @since 1.3.0 */ -function tweet_submitbox_callback( $post ) { - +function autoshare_for_twitter_metabox( $post_type, $post ) { /** * Don't bother enqueuing assets if the post type hasn't opted into autosharing. */ @@ -181,11 +204,15 @@ function tweet_submitbox_callback( $post ) { return; } - ?> -
- -
- true ) + ); } /** @@ -201,33 +228,35 @@ function render_tweet_submitbox( $post ) { // If the post is already published the output varies slightly. if ( 'publish' === $post_status ) { - - $twitter_status = Utils\get_autoshare_for_twitter_meta( get_the_ID(), TWITTER_STATUS_KEY ); - $status = isset( $twitter_status['status'] ) ? $twitter_status['status'] : ''; - - switch ( $status ) { - - case 'published': - $output = markup_published( $twitter_status ); - break; - - case 'error': - $output = markup_error( $twitter_status ); - break; - - case 'unknown': - $output = markup_unknown( $twitter_status ); - break; - - default: - $output = __( 'This post was not tweeted.', 'autoshare-for-twitter' ); - break; - } - - echo wp_kses_post( "

$output

" ); - + // Display tweet status logs. + ?> +
+ +
+
+ + + +
+ +
+
+ $response_array ]; + } - $twitter_status = Utils\get_autoshare_for_twitter_meta( $post->ID, TWITTER_STATUS_KEY ); - $status = isset( $twitter_status['status'] ) ? $twitter_status['status'] : ''; + $tweet_metas = Utils\get_autoshare_for_twitter_meta( $post->ID, TWITTER_STATUS_KEY ); + + if ( empty( $tweet_metas ) || isset( $tweet_metas['twitter_id'] ) ) { + $tweet_metas = array( + array( + 'status' => isset( $tweet_metas['status'] ) ? $tweet_metas['status'] : '', + 'created_at' => isset( $tweet_metas['created_at'] ) ? $tweet_metas['created_at'] : '', + 'twitter_id' => isset( $tweet_metas['twitter_id'] ) ? $tweet_metas['twitter_id'] : '', + ), + ); + } elseif ( isset( $tweet_metas['status'] ) && ( 'error' === $tweet_metas['status'] || 'unknown' === $tweet_metas['status'] || 'other' === $tweet_metas['status'] ) ) { + $tweet_metas = array( + $tweet_metas, + ); + } + + foreach ( $tweet_metas as $tweet_meta ) { + $status = $tweet_meta['status']; + if ( 'publish' !== $post_status && empty( $status ) ) { + continue; + } switch ( $status ) { case 'published': - $date = Utils\date_from_twitter( $twitter_status['created_at'] ); - $twitter_url = Utils\link_from_twitter( $twitter_status['twitter_id'] ); + $date = Utils\date_from_twitter( $tweet_meta['created_at'] ); + $twitter_url = Utils\link_from_twitter( $tweet_meta['twitter_id'] ); - return [ + $response_array[] = [ // Translators: Placeholder is a date. 'message' => sprintf( __( 'Tweeted on %s', 'autoshare-for-twitter' ), $date ), 'url' => $twitter_url, 'status' => $status, ]; + break; + case 'error': - return [ - 'message' => __( 'Failed to tweet: ', 'autoshare-for-twitter' ) . $twitter_status['message'], + $response_array[] = [ + 'message' => __( 'Failed to tweet: ', 'autoshare-for-twitter' ) . $tweet_meta['message'], 'status' => $status, ]; + break; + case 'unknown': - return [ - 'message' => $twitter_status['message'], + $response_array[] = [ + 'message' => $tweet_meta['message'], 'status' => $status, ]; + break; + default: - return [ + $response_array[] = [ 'message' => __( 'This post was not tweeted.', 'autoshare-for-twitter' ), 'status' => $status, ]; } } - return [ 'message' => '' ]; + return [ 'message' => $response_array ]; } +/** + * Gets info on the post's Tweet status to display in classic editor metabox. + * + * @since 1.3.0 + * + * @param int|WP_Post $post The post we are rendering on. + * @return string markup containing the tweet status logs if the post was tweeted. + */ +function get_tweet_status_logs( $post ) { + $post = get_post( $post ); + $post_status = get_post_status( $post ); + $status_logs = ''; + + $tweet_metas = Utils\get_autoshare_for_twitter_meta( $post->ID, TWITTER_STATUS_KEY ); + + if ( empty( $tweet_metas ) || isset( $tweet_metas['twitter_id'] ) ) { + $tweet_metas = array( + array( + 'status' => isset( $tweet_metas['status'] ) ? $tweet_metas['status'] : '', + 'created_at' => isset( $tweet_metas['created_at'] ) ? $tweet_metas['created_at'] : '', + 'twitter_id' => isset( $tweet_metas['twitter_id'] ) ? $tweet_metas['twitter_id'] : '', + ), + ); + } elseif ( isset( $tweet_metas['status'] ) && ( 'error' === $tweet_metas['status'] || 'unknown' === $tweet_metas['status'] || 'other' === $tweet_metas['status'] ) ) { + $tweet_metas = array( + $tweet_metas, + ); + } + + foreach ( $tweet_metas as $twitter_meta ) { + $status = isset( $twitter_meta['status'] ) ? $twitter_meta['status'] : ''; + if ( 'publish' !== $post_status && empty( $status ) ) { + continue; + } + + switch ( $status ) { + + case 'published': + $output = markup_published( $twitter_meta ); + break; + + case 'error': + $output = markup_error( $twitter_meta ); + break; + + case 'unknown': + $output = markup_unknown( $twitter_meta ); + break; + + default: + $output = __( 'This post was not tweeted.', 'autoshare-for-twitter' ); + break; + } + + $status_logs .= "
$output
"; + } + + return wp_kses_post( $status_logs ); +} + /** * Outputs the markup and language to be used when a post has been successfully * sent to Twitter @@ -300,7 +417,7 @@ function markup_published( $status_meta ) { $twitter_url = Utils\link_from_twitter( $status_meta['twitter_id'] ); return sprintf( - '%s %s (%s)

', + '
%s
%s (%s)
', esc_html__( 'Tweeted on', 'autoshare-for-twitter' ), esc_html( $date ), esc_url( $twitter_url ), @@ -319,7 +436,7 @@ function markup_published( $status_meta ) { function markup_error( $status_meta ) { return sprintf( - '%s
%s

', + '
%s
%s
', esc_html__( 'Failed to tweet', 'autoshare-for-twitter' ), esc_html( $status_meta['message'] ) ); @@ -334,7 +451,10 @@ function markup_error( $status_meta ) { * @return string */ function markup_unknown( $status_meta ) { - return $status_meta['message']; + return sprintf( + '
%s
', + esc_html( $status_meta['message'] ) + ); } /** @@ -343,10 +463,11 @@ function markup_unknown( $status_meta ) { * @return string */ function _safe_markup_default() { - + $count = ( strlen( get_permalink( get_the_ID() ) ) + 5 ); + $max_length = 280 - $count; ob_start(); ?> -