Showing preview only (350K chars total). Download the full file or copy to clipboard to get everything.
Repository: 10up/simple-podcasting
Branch: develop
Commit: f4aed4e51587
Files: 100
Total size: 323.6 KB
Directory structure:
gitextract_alta88pg/
├── .distignore
├── .editorconfig
├── .eslintignore
├── .eslintrc
├── .github/
│ ├── CODEOWNERS
│ └── workflows/
│ ├── build-release-zip.yml
│ ├── close-stale-issues.yml
│ ├── cypress.yml
│ ├── dependency-review.yml
│ ├── php-compatibility.yml
│ ├── phpcs.yml
│ ├── phpunit.yml
│ ├── push-asset-readme-update.yml
│ ├── push-deploy.yml
│ ├── repo-automator.yml
│ └── wordpress-version-checker.yml
├── .gitignore
├── .husky/
│ ├── .gitignore
│ └── pre-commit
├── .nvmrc
├── .phpcs.xml.dist
├── .prettierrc
├── .wordpress-org/
│ └── blueprints/
│ └── blueprint.json
├── .wordpress-version-checker.json
├── .wp-env.json
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── CREDITS.md
├── LICENSE.md
├── README.md
├── assets/
│ ├── css/
│ │ ├── podcasting-edit-term.css
│ │ ├── podcasting-editor-screen.css
│ │ ├── podcasting-onboarding.scss
│ │ └── podcasting-transcript.css
│ └── js/
│ ├── blocks/
│ │ ├── latest-episode/
│ │ │ ├── index.js
│ │ │ └── index.scss
│ │ ├── podcast/
│ │ │ ├── index.js
│ │ │ └── index.scss
│ │ └── podcast-platforms/
│ │ ├── edit.js
│ │ ├── index.js
│ │ └── index.scss
│ ├── blocks.js
│ ├── create-podcast-show.js
│ ├── deprecated.js
│ ├── edit.js
│ ├── onboarding.js
│ ├── podcasting-edit-post.js
│ ├── podcasting-edit-term.js
│ └── transforms.js
├── composer.json
├── includes/
│ ├── admin/
│ │ ├── create-podcast-component.php
│ │ ├── onboarding.php
│ │ └── views/
│ │ ├── onboarding-header.php
│ │ ├── onboarding-page-one.php
│ │ └── onboarding-page-two.php
│ ├── block-patterns.php
│ ├── blocks/
│ │ ├── podcast/
│ │ │ └── markup.php
│ │ └── podcast-transcript/
│ │ ├── cite.js
│ │ ├── edit.js
│ │ ├── formats.js
│ │ ├── index.js
│ │ ├── markup.php
│ │ ├── styles.css
│ │ └── time.js
│ ├── blocks.php
│ ├── create-podcast.php
│ ├── customize-feed.php
│ ├── datatypes.php
│ ├── helpers.php
│ ├── post-meta-box.php
│ ├── rest-external-url.php
│ ├── transcripts.php
│ └── upgrade.php
├── package.json
├── phpunit.xml.dist
├── readme.txt
├── simple-podcasting.php
├── templates/
│ └── transcript.php
├── tests/
│ ├── bin/
│ │ └── set-wp-config.js
│ ├── cypress/
│ │ ├── .eslintrc
│ │ ├── config.config.js
│ │ ├── fixtures/
│ │ │ └── example.json
│ │ ├── integration/
│ │ │ ├── admin.test.js
│ │ │ ├── block.test.js
│ │ │ ├── onboarding.test.js
│ │ │ ├── podcast-setting-panel.test.js
│ │ │ └── taxonomy.test.js
│ │ ├── plugins/
│ │ │ └── index.js
│ │ ├── support/
│ │ │ ├── functions.js
│ │ │ └── index.js
│ │ └── tsconfig.json
│ └── unit/
│ ├── bootstrap.php
│ ├── test-blocks.php
│ ├── test-customize-feed.php
│ ├── test-datatypes.php
│ ├── test-helpers.php
│ ├── test-rest-external-url.php
│ └── test-transcript.php
└── webpack.config.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .distignore
================================================
# Directories
/.git
/.github
/.husky
/.wordpress-org
/assets
/gulp-tasks
/node_modules
/tests
/vendor
# Files
.*
/CHANGELOG.md
/CODE_OF_CONDUCT.md
/composer.json
/composer.lock
/CONTRIBUTING.md
/CREDITS.md
/gulpfile.babel.js
/LICENSE.md
/package.json
/package-lock.json
/phpunit.xml.dist
/README.md
/webpack.config.js
================================================
FILE: .editorconfig
================================================
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = tab
[{*.json,*.yml,.babelrc,.bowerrc,.postcssrc}]
indent_style = space
indent_size = 2
[*.txt,wp-config-sample.php]
end_of_line = crlf
================================================
FILE: .eslintignore
================================================
assets/js/frontend/vendor/*.js
================================================
FILE: .eslintrc
================================================
{
"extends": [ "plugin:@wordpress/eslint-plugin/recommended" ],
"ignorePatterns": ["**/vendor/**"]
}
================================================
FILE: .github/CODEOWNERS
================================================
# These owners will be the default owners for everything in the repo. Unless a later match takes precedence, @10up/open-source-practice, as primary maintainers will be requested for review when someone opens a Pull Request.
* @10up/open-source-practice
# GitHub and WordPress.org specifics
/.github/ @jeffpaul
/.wordpress-org/ @jeffpaul
CODE_OF_CONDUCT.md @jeffpaul
LICENSE.md @jeffpaul
================================================
FILE: .github/workflows/build-release-zip.yml
================================================
name: Build release zip
permissions:
contents: read
on:
workflow_dispatch:
workflow_call:
push:
branches:
- trunk
jobs:
build:
name: Build release zip
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Cache node_modules
id: cache-node-modules
uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2
env:
cache-name: cache-node-modules
with:
path: node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
- name: Setup node version and npm cache
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0
with:
node-version-file: '.nvmrc'
cache: 'npm'
- name: Install Node dependencies
if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: npm ci --no-optional
- name: Build plugin
run: npm run build
- name: Generate ZIP file
uses: 10up/action-wordpress-plugin-build-zip@b9e621e1261ccf51592b6f3943e4dc4518fca0d1 # v1.0.2
================================================
FILE: .github/workflows/close-stale-issues.yml
================================================
name: 'Close stale issues'
# **What it does**: Closes issues where the original author doesn't respond to a request for information.
# **Why we have it**: To remove the need for maintainers to remember to check back on issues periodically to see if contributors have responded.
on:
schedule:
# Schedule for every day at 1:30am UTC
- cron: '30 1 * * *'
permissions:
issues: write
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0
with:
days-before-stale: 7
days-before-close: 7
stale-issue-message: >
It has been 7 days since more information was requested from you in this issue and we have not heard back. This issue is now marked as stale and will be closed in 7 days, but if you have more information to add then please comment and the issue will stay open.
close-issue-message: >
This issue has been automatically closed because there has been no response
to our request for more information. With only the
information that is currently in the issue, we don't have enough information
to take action. Please reach out if you have or find the answers we need so
that we can investigate further. See [this blog post on bug reports and the
importance of repro steps](https://www.lee-dohm.com/2015/01/04/writing-good-bug-reports/)
for more information about the kind of information that may be helpful.
stale-issue-label: 'stale'
close-issue-reason: 'not_planned'
any-of-labels: 'needs:feedback'
remove-stale-when-updated: true
================================================
FILE: .github/workflows/cypress.yml
================================================
name: E2E Test
permissions:
contents: read
pull-requests: write
on:
push:
branches:
- trunk
- develop
pull_request:
branches:
- develop
jobs:
build:
uses: 10up/simple-podcasting/.github/workflows/build-release-zip.yml@develop
cypress:
needs: build
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
core:
- {name: 'WP latest', version: 'latest'}
- {name: 'WP trunk', version: 'WordPress/WordPress#master'}
- {name: 'WP minimum', version: 'WordPress/WordPress#6.6'}
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Download build zip
uses: actions/download-artifact@b14cf4c92620c250e1c074ab0a5800e37df86765 # v4.2.0
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: Cache node_modules
id: cache-node-modules
uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2
env:
cache-name: cache-node-modules
with:
path: |
node_modules
~/.cache
~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
- name: Install dependencies
run: npm install
- name: Set the core version and plugins config
run: ./tests/bin/set-wp-config.js --core=${{ matrix.core.version }} --plugins=./${{ github.event.repository.name }}
- name: Set up WP environment
run: npm run wp-env start
continue-on-error: ${{ matrix.core.name == 'WP trunk' }}
- name: Test
run: npm run cypress:run
continue-on-error: ${{ matrix.core.name == 'WP trunk' }}
- name: Update summary
if: always()
run: |
npx mochawesome-merge ./tests/cypress/reports/*.json -o tests/cypress/reports/mochawesome.json
rm -rf ./tests/cypress/reports/mochawesome-*.json
npx mochawesome-json-to-md -p ./tests/cypress/reports/mochawesome.json -o ./tests/cypress/reports/mochawesome.md
npx mochawesome-report-generator tests/cypress/reports/mochawesome.json -o tests/cypress/reports/
cat ./tests/cypress/reports/mochawesome.md >> $GITHUB_STEP_SUMMARY
- name: Make artifacts available
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
if: failure()
with:
name: cypress-artifact
retention-days: 2
path: |
${{ github.workspace }}/tests/cypress/screenshots/
${{ github.workspace }}/tests/cypress/videos/
${{ github.workspace }}/tests/cypress/logs/
${{ github.workspace }}/tests/cypress/reports/
================================================
FILE: .github/workflows/dependency-review.yml
================================================
# Dependency Review Action
#
# This Action will scan dependency manifest files that change as part of a Pull Reqest, surfacing known-vulnerable versions of the packages declared or updated in the PR. Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable packages will be blocked from merging.
#
# Source repository: https://github.com/actions/dependency-review-action
# Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement
name: 'Dependency Review'
on: [pull_request]
permissions:
contents: read
jobs:
dependency-review:
runs-on: ubuntu-latest
steps:
- name: 'Checkout Repository'
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Dependency Review
uses: actions/dependency-review-action@72eb03d02c7872a771aacd928f3123ac62ad6d3a # v4.3.3
with:
license-check: true
vulnerability-check: false
config-file: 10up/.github/.github/dependency-review-config.yml@trunk
================================================
FILE: .github/workflows/php-compatibility.yml
================================================
name: PHP Compatibility
permissions:
contents: read
env:
COMPOSER_VERSION: "2"
COMPOSER_CACHE: "${{ github.workspace }}/.composer-cache"
on:
push:
branches:
- develop
- trunk
pull_request:
branches:
- develop
jobs:
php_compatibility:
name: PHP ${{ matrix.php }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Set standard 10up cache directories
run: |
composer config -g cache-dir "${{ env.COMPOSER_CACHE }}"
- name: Prepare composer cache
uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2
with:
path: ${{ env.COMPOSER_CACHE }}
key: composer-${{ env.COMPOSER_VERSION }}-${{ hashFiles('**/composer.lock') }}
restore-keys: |
composer-${{ env.COMPOSER_VERSION }}-
- name: Set PHP version
uses: shivammathur/setup-php@9e72090525849c5e82e596468b86eb55e9cc5401 # v2.32.0
with:
php-version: '7.4'
coverage: none
tools: prestissimo, composer:v2
- name: Install dependencies
run: composer install
- name: Check PHP Compatibility
run: ./vendor/bin/phpcs -p simple-podcasting.php includes --standard=PHPCompatibilityWP --extensions=php --runtime-set testVersion 7.4-
================================================
FILE: .github/workflows/phpcs.yml
================================================
name: PHPCS
permissions:
contents: read
on:
push:
branches:
- develop
- trunk
paths:
- "**.php"
pull_request:
branches:
- develop
paths:
- "**.php"
jobs:
phpcs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Set PHP version
uses: shivammathur/setup-php@9e72090525849c5e82e596468b86eb55e9cc5401 # v2.32.0
with:
php-version: '7.4'
coverage: none
tools: composer:v2
- name: Install dependencies
run: composer install
- name: Test
run: ./vendor/bin/phpcs --runtime-set testVersion 7.4 .
================================================
FILE: .github/workflows/phpunit.yml
================================================
name: Unit Tests
permissions:
contents: read
env:
COMPOSER_VERSION: "2"
COMPOSER_CACHE: "${{ github.workspace }}/.composer-cache"
on:
push:
branches:
- develop
- trunk
pull_request:
branches:
- develop
jobs:
phpunit:
name: ${{ matrix.php.name }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
php:
- {name: 'PHP 7.4', version: '7.4'}
- {name: 'PHP 8.1', version: '8.1'}
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Set standard 10up cache directories
run: |
composer config -g cache-dir "${{ env.COMPOSER_CACHE }}"
- uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0
with:
node-version-file: '.nvmrc'
- name: Prepare composer cache
uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2
with:
path: ${{ env.COMPOSER_CACHE }}
key: composer-${{ env.COMPOSER_VERSION }}-${{ hashFiles('**/composer.lock') }}
restore-keys: |
composer-${{ env.COMPOSER_VERSION }}-
- name: Set PHP version
uses: shivammathur/setup-php@9e72090525849c5e82e596468b86eb55e9cc5401 # v2.32.0
with:
php-version: '${{ matrix.php.version }}'
coverage: none
tools: composer:v2
- name: Install dependencies
run: composer install && npm install
- name: Build
run: npm run build
- name: Test
run: ./vendor/bin/phpunit -v
================================================
FILE: .github/workflows/push-asset-readme-update.yml
================================================
name: Plugin asset/readme update
on:
push:
branches:
- trunk
permissions:
contents: read
jobs:
trunk:
name: Push to trunk
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: install node
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0
with:
node-version-file: .nvmrc
- name: Build
run: |
npm ci
npm run build
- name: WordPress.org plugin asset/readme update
uses: 10up/action-wordpress-plugin-asset-update@2480306f6f693672726d08b5917ea114cb2825f7 # v2.2.0
env:
SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }}
SVN_USERNAME: ${{ secrets.SVN_USERNAME }}
================================================
FILE: .github/workflows/push-deploy.yml
================================================
name: Deploy to WordPress.org
permissions:
contents: write
packages: read
actions: write
on:
release:
types: [published]
jobs:
tag:
name: New release
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: install node
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0
with:
node-version-file: .nvmrc
- name: Build
run: |
npm ci
npm run build
npm run makepot
- name: WordPress Plugin Deploy
id: deploy
uses: 10up/action-wordpress-plugin-deploy@54bd289b8525fd23a5c365ec369185f2966529c2 # v2.3.0
with:
generate-zip: true
env:
SVN_USERNAME: ${{ secrets.SVN_USERNAME }}
SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }}
- name: Upload release asset
uses: softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda # v2.2.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
files: ${{ github.workspace }}/${{ github.event.repository.name }}.zip
================================================
FILE: .github/workflows/repo-automator.yml
================================================
name: 'Repo Automator'
permissions:
contents: read
issues: write
on:
issues:
types:
- opened
push:
branches:
- develop
pull_request:
types:
- opened
- edited
- synchronize
- converted_to_draft
- ready_for_review
branches:
- develop
jobs:
Validate:
runs-on: ubuntu-latest
steps:
- uses: 10up/action-repo-automator@280f5dc0b4ed1b5c50c816e08623bdefce55cdce # v2.1.3
with:
fail-label: needs:feedback
pass-label: needs:code-review
conflict-label: needs:refresh
reviewers: |
team:open-source-practice
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
================================================
FILE: .github/workflows/wordpress-version-checker.yml
================================================
name: "WordPress version checker"
on:
push:
branches:
- develop
- trunk
pull_request:
branches:
- develop
schedule:
- cron: '0 0 * * 1'
permissions:
issues: write
jobs:
wordpress-version-checker:
runs-on: ubuntu-latest
steps:
- name: WordPress version checker
uses: skaut/wordpress-version-checker@9d247334f5b30202cb9c1f4aee74c52f37399f69 # v2.2.3
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
================================================
FILE: .gitignore
================================================
node_modules
bower_components
languages
release
vendor
phpunit.xml
.idea
.phpunit.result.cache
# Project Files
dist
ruleset.xml
# Editors
*.esproj
*.tmproj
*.tmproject
tmtags
.*.sw[a-z]
*.un~
Session.vim
*.swp
*.csv
# Mac OSX
.DS_Store
._*
.Spotlight-V100
.Trashes
# Windows
Thumbs.db
Desktop.ini
# E2E testing
.wp-env.override.json
artifacts
tests/cypress/downloads
tests/cypress/screenshots
tests/cypress/videos
tests/cypress/reports
================================================
FILE: .husky/.gitignore
================================================
_
================================================
FILE: .husky/pre-commit
================================================
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx lint-staged
================================================
FILE: .nvmrc
================================================
v20
================================================
FILE: .phpcs.xml.dist
================================================
<?xml version="1.0"?>
<ruleset name="Simple Podcasting">
<rule ref="10up-Default" />
<file>.</file>
<exclude-pattern>dist/</exclude-pattern>
<exclude-pattern>vendor/</exclude-pattern>
<exclude-pattern>tests/</exclude-pattern>
</ruleset>
================================================
FILE: .prettierrc
================================================
{
"useTabs": true,
"printWidth": 90,
"tabWidth": 4,
"singleQuote": true
}
================================================
FILE: .wordpress-org/blueprints/blueprint.json
================================================
{
"$schema": "https://playground.wordpress.net/blueprint-schema.json",
"landingPage": "\/wp-admin\/admin.php?page=simple-podcasting-onboarding&step=1",
"preferredVersions": {
"php": "7.4",
"wp": "latest"
},
"phpExtensionBundles": ["kitchen-sink"],
"features": {
"networking": true
},
"steps": [
{
"step": "login",
"username": "admin",
"password": "password"
},
{
"step": "installPlugin",
"pluginZipFile": {
"resource": "wordpress.org\/plugins",
"slug": "simple-podcasting"
},
"options": {
"activate": true
}
}
]
}
================================================
FILE: .wordpress-version-checker.json
================================================
{
"readme": "readme.txt",
"channel": "rc"
}
================================================
FILE: .wp-env.json
================================================
{
"plugins": ["."]
}
================================================
FILE: CHANGELOG.md
================================================
# Changelog
All notable changes to this project will be documented in this file, per [the Keep a Changelog standard](http://keepachangelog.com/).
## [Unreleased] - TBD
## [1.9.1] - 2025-05-19
**Note that this release bumps the WordPress minimum version from 6.5 to 6.6.**
### Added
- Screenshots for all new features (props [@gabriel-glo](https://github.com/gabriel-glo), [@jeffpaul](https://github.com/jeffpaul), [@Sidsector9](https://github.com/Sidsector9), [@dkotter](https://github.com/dkotter) via [#310](https://github.com/10up/simple-podcasting/pull/310)).
### Changed
- Bump WordPress "tested up to" version to 6.8 (props [@jeffpaul](https://github.com/jeffpaul) via [#335](https://github.com/10up/simple-podcasting/pull/335), [#336](https://github.com/10up/simple-podcasting/pull/336)).
- Bump WordPress minimum from 6.5 to 6.6 (props [@jeffpaul](https://github.com/jeffpaul) via [#335](https://github.com/10up/simple-podcasting/pull/335), [#336](https://github.com/10up/simple-podcasting/pull/336)).
### Fixed
- Issue where podcast feed title unexpectedly adding site title (props [@kirtangajjar](https://github.com/kirtangajjar), [@peterwilsoncc](https://github.com/peterwilsoncc), [@dabowman](https://github.com/dabowman) via [#295](https://github.com/10up/simple-podcasting/pull/295)).
### Security
- Bump `@wordpress/scripts` from 27.9.0 to 30.6.0 (props [@dependabot](https://github.com/apps/dependabot), [@Sidsector9](https://github.com/Sidsector9) via [#328](https://github.com/10up/simple-podcasting/pull/328)).
- Bump `cookie` from 0.4.2 to 0.7.1, `express` from 4.21.0 to 4.21.2, `@wordpress/e2e-test-utils-playwright` from 0.26.0 to 1.18.0, `serialize-javascript` from 6.0.0 to 6.0.2 and `mocha` from 10.4.0 to 11.1.0 (props [@dependabot](https://github.com/apps/dependabot), [@peterwilsoncc](https://github.com/peterwilsoncc) via [#332](https://github.com/10up/simple-podcasting/pull/332)).
- Bump `axios` from 1.7.4 to 1.9.0 and `http-proxy-middleware` from 2.0.6 to 2.0.9 (props [@dependabot](https://github.com/apps/dependabot), [@peterwilsoncc](https://github.com/peterwilsoncc) via [#338](https://github.com/10up/simple-podcasting/pull/338)).
### Developer
- Update all third-party actions our workflows rely on to use versions based on specific commit hashes (props [@jeffpaul](https://github.com/jeffpaul), [@dkotter](https://github.com/dkotter) via [#333](https://github.com/10up/simple-podcasting/pull/333)).
- Adjust `makepot` to only happen during deploy instead of during every prebuild (props [@jeffpaul](https://github.com/jeffpaul), [@dkotter](https://github.com/dkotter) via [#337](https://github.com/10up/simple-podcasting/pull/337)).
## [1.9.0] - 2024-11-18
**Note that this release bumps the WordPress minimum version from 5.7 to 6.5.**
### Added
- New options to the Podcast block to allow for more display customization (props [@barneyjeffries](https://github.com/barneyjeffries), [@Firestorm980](https://github.com/Firestorm980), [@mehidi258](https://github.com/mehidi258), [@jayedul](https://github.com/jayedul), [@Sidsector9](https://github.com/Sidsector9), [@peterwilsoncc](https://github.com/peterwilsoncc), [@faisal-alvi](https://github.com/faisal-alvi), [@gusaus](https://github.com/gusaus), [@jeffpaul](https://github.com/jeffpaul) via [#272](https://github.com/10up/simple-podcasting/pull/272)).
### Changed
- Update the rendering of the Podcast block to be more full featured and use all the newly added customization options (props [@barneyjeffries](https://github.com/barneyjeffries), [@Firestorm980](https://github.com/Firestorm980), [@mehidi258](https://github.com/mehidi258), [@jayedul](https://github.com/jayedul), [@Sidsector9](https://github.com/Sidsector9), [@peterwilsoncc](https://github.com/peterwilsoncc), [@faisal-alvi](https://github.com/faisal-alvi), [@gusaus](https://github.com/gusaus), [@sudar](https://github.com/sudar), [@iamdharmesh](https://github.com/iamdharmesh), [@jeffpaul](https://github.com/jeffpaul), [@dkotter](https://github.com/dkotter) via [#272](https://github.com/10up/simple-podcasting/pull/272), [#318](https://github.com/10up/simple-podcasting/pull/318), [#320](https://github.com/10up/simple-podcasting/pull/320), [#322](https://github.com/10up/simple-podcasting/pull/322)).
- Bump WordPress "tested up to" version to 6.7 (props [@qasumitbagthariya](https://github.com/qasumitbagthariya), [@jeffpaul](https://github.com/jeffpaul), [@dkotter](https://github.com/dkotter), [@sonali886](https://github.com/sonali886), [@godleman](https://github.com/godleman), [@mehul0810](https://github.com/mehul0810) via [#291](https://github.com/10up/simple-podcasting/pull/291), [#307](https://github.com/10up/simple-podcasting/pull/307), [#325](https://github.com/10up/simple-podcasting/pull/325), [#326](https://github.com/10up/simple-podcasting/pull/326)).
- Bump WordPress minimum from 5.7 to 6.5 (props [@qasumitbagthariya](https://github.com/qasumitbagthariya), [@jeffpaul](https://github.com/jeffpaul), [@dkotter](https://github.com/dkotter), [@sonali886](https://github.com/sonali886), [@godleman](https://github.com/godleman), [@mehul0810](https://github.com/mehul0810) via [#291](https://github.com/10up/simple-podcasting/pull/291), [#307](https://github.com/10up/simple-podcasting/pull/307), [#325](https://github.com/10up/simple-podcasting/pull/325), [#326](https://github.com/10up/simple-podcasting/pull/326)).
- Update how we import the `PluginDocumentSettingPanel` component to use the new `@wordpress/editor` package if it exists (props [@gabriel-glo](https://github.com/gabriel-glo), [@dkotter](https://github.com/dkotter) via [#309](https://github.com/10up/simple-podcasting/pull/309)).
### Security
- Bump `express` from 4.18.2 to 4.19.2, `follow-redirects` from 1.15.4 to 1.15.6, and `webpack-dev-middleware` from 5.3.3 to 5.3.4 (props [@dependabot](https://github.com/apps/dependabot), [@iamdharmesh](https://github.com/iamdharmesh) via [#290](https://github.com/10up/simple-podcasting/pull/290)).
- Bump `braces` from 3.0.2 to 3.0.3, `pac-resolver` from 7.0.0 to 7.0.1, `socks` from 2.7.1 to 2.8.3, `ws` from 7.5.9 to 7.5.10 and removes `ip` (props [@dependabot](https://github.com/apps/dependabot), [@Sidsector9](https://github.com/Sidsector9) via [#297](https://github.com/10up/simple-podcasting/pull/297), [#306](https://github.com/10up/simple-podcasting/pull/306)).
- Bump `axios` from 1.7.2 to 1.7.4 (props [@dependabot](https://github.com/apps/dependabot), [@Sidsector9](https://github.com/Sidsector9) via [#312](https://github.com/10up/simple-podcasting/pull/312)).
- Bump `webpack` from 5.91.0 to 5.94.0 (props [@dependabot](https://github.com/apps/dependabot), [@faisal-alvi](https://github.com/faisal-alvi) via [#315](https://github.com/10up/simple-podcasting/pull/315)).
- Bump `ws` from 7.5.10 to 8.18.0, `serve-static` from 1.15.0 to 1.16.2 and `express` from 4.19.2 to 4.21.0 (props [@dependabot](https://github.com/apps/dependabot), [@Sidsector9](https://github.com/Sidsector9) via [#319](https://github.com/10up/simple-podcasting/pull/319)).
### Developer
- Clean up NPM dependencies and update node to v20 (props [@Sidsector9](https://github.com/Sidsector9), [@jeffpaul](https://github.com/jeffpaul), [@peterwilsoncc](https://github.com/peterwilsoncc) via [#275](https://github.com/10up/simple-podcasting/pull/275)).
- Add "Testing" section to the `CONTRIBUTING.md` file (props [@kmgalanakis](https://github.com/kmgalanakis), [@jeffpaul](https://github.com/jeffpaul), [@iamdharmesh](https://github.com/iamdharmesh) via [#294](https://github.com/10up/simple-podcasting/pull/294)).
- Change support level from Active to Stable (props [@Sidsector9](https://github.com/Sidsector9), [@jeffpaul](https://github.com/jeffpaul), [@cadic](https://github.com/cadic) via [#217](https://github.com/10up/simple-podcasting/pull/217)).
- Switch from using `actions/upload-release-asset` to `softprops/action-gh-release` GitHub action (props [@Sidsector9](https://github.com/Sidsector9), [@jeffpaul](https://github.com/jeffpaul) via [#308](https://github.com/10up/simple-podcasting/pull/308)).
- Update repo badges, add WordPress Playground badge (props [@jeffpaul](https://github.com/jeffpaul), [@faisal-alvi](https://github.com/faisal-alvi), [@dkotter](https://github.com/dkotter) via [#311](https://github.com/10up/simple-podcasting/pull/311), [#317](https://github.com/10up/simple-podcasting/pull/317), [#321](https://github.com/10up/simple-podcasting/pull/321)).
## [1.8.0] - 2024-04-03
### Added
- "Latest Podcast Episode" query block variation (props [@jeffpaul](https://github.com/jeffpaul), [@cadic](https://github.com/cadic), [@barneyjeffries](https://github.com/barneyjeffries), [@faisal-alvi](https://github.com/faisal-alvi) via [#266](https://github.com/10up/simple-podcasting/pull/266)).
- Ability to add Unique Cover Art for Episodes (props [@jamesburgos](https://github.com/jamesburgos), [@jeffpaul](https://github.com/jeffpaul), [@zamanq](https://github.com/zamanq), [@iamdharmesh](https://github.com/iamdharmesh) via [#273](https://github.com/10up/simple-podcasting/pull/273)).
- `simple_podcasting_feed_title` filter hook to modify feed title (props [@martinburch](https://github.com/martinburch), [@psorensen](https://github.com/psorensen), [@dkotter](https://github.com/dkotter) via [#279](https://github.com/10up/simple-podcasting/pull/279)).
### Fixed
- Incorrect feed title (props [@martinburch](https://github.com/martinburch), [@psorensen](https://github.com/psorensen), [@dkotter](https://github.com/dkotter) via [#279](https://github.com/10up/simple-podcasting/pull/279)).
- Fatal error in WordPress 5.8 and earlier (props [@peterwilsoncc](https://github.com/peterwilsoncc), [@Sidsector9](https://github.com/Sidsector9) via [#277](https://github.com/10up/simple-podcasting/pull/277)).
### Changed
- Disabled auto sync pull requests with target branch (props [@iamdharmesh](https://github.com/iamdharmesh), [@jeffpaul](https://github.com/jeffpaul) via [#281](https://github.com/10up/simple-podcasting/pull/281)).
- Removed `PULL_REQUEST_TEMPLATE.md` template (props [@iamdharmesh](https://github.com/iamdharmesh), [@jeffpaul](https://github.com/jeffpaul) via [#286](https://github.com/10up/simple-podcasting/pull/286)).
- Replaced [lee-dohm/no-response](https://github.com/lee-dohm/no-response) with [actions/stale](https://github.com/actions/stale) to help with closing no-response/stale issues (props [@jeffpaul](https://github.com/jeffpaul), [@dkotter](https://github.com/dkotter) via [#287](https://github.com/10up/simple-podcasting/pull/287)).
- Upgrade the download-artifact from v3 to v4 (props [@iamdharmesh](https://github.com/iamdharmesh), [@jeffpaul](https://github.com/jeffpaul) via [#285](https://github.com/10up/simple-podcasting/pull/285)).
### Security
- Bumps `ip` from `1.1.8` to `1.1.9` (props [@dependabot](https://github.com/apps/dependabot), [@Sidsector9](https://github.com/Sidsector9) via [#278](https://github.com/10up/simple-podcasting/pull/278)).
## [1.7.0] - 2024-01-16
### Added
- Ability to add a transcript to a podcast episode by utilizing a new Podcast Transcript block. This block is added by clicking the `Add Transcript` button that will now show in the sidebar panel of the Podcast block (props [@nateconley](https://github.com/nateconley), [@peterwilsoncc](https://github.com/peterwilsoncc), [@sksaju](https://github.com/sksaju), [@kirtangajjar](https://github.com/kirtangajjar) via [#221](https://github.com/10up/simple-podcasting/pull/221)).
- Support for the WordPress.org plugin preview (props [@dkotter](https://github.com/dkotter), [@jeffpaul](https://github.com/jeffpaul) via [#265](https://github.com/10up/simple-podcasting/pull/265)).
### Fixed
- Ensure we show all Podcasting terms in the Block Editor sidebar (props [@dkotter](https://github.com/dkotter), [@channchetra](https://github.com/channchetra), [@Sidsector9](https://github.com/Sidsector9) via [#268](https://github.com/10up/simple-podcasting/pull/268)).
### Security
- Bump `axios` from 0.25.0 to 1.6.2 and `@wordpress/scripts` from 26.9.0 to 26.18.0 (props [@dependabot](https://github.com/apps/dependabot), [@Sidsector9](https://github.com/Sidsector9) via [#263](https://github.com/10up/simple-podcasting/pull/263)).
- Bump `follow-redirects` from 1.15.3 to 1.15.4 (props [@dependabot](https://github.com/apps/dependabot), [@dkotter](https://github.com/dkotter) via [#269](https://github.com/10up/simple-podcasting/pull/269)).
## [1.6.1] - 2023-11-21
### Added
- Repo Automator GitHub Action (props [@iamdharmesh](https://github.com/iamdharmesh), [@jeffpaul](https://github.com/jeffpaul) via [#253](https://github.com/10up/simple-podcasting/pull/253)).
### Changed
- Bump WordPress "tested up to" version to 6.4 (props [@qasumitbagthariya](https://github.com/qasumitbagthariya), [@jeffpaul](https://github.com/jeffpaul) via [#259](https://github.com/10up/simple-podcasting/pull/259), [#260](https://github.com/10up/simple-podcasting/pull/260)).
- Ensure end-to-end tests work on Cypress v13 and bump `cypress` from 11.2.0 to 13.2.0, `@10up/cypress-wp-utils` from 0.1.0 to 0.2.0, `@wordpress/env` from 5.4.0 to 8.7.0, `cypress-localstorage-commands` from 2.2.2 to 2.2.4 and `cypress-mochawesome-reporter` from 3.4.0 to 3.6.0 (props [@iamdharmesh](https://github.com/iamdharmesh), [@Sidsector9](https://github.com/Sidsector9) via [#254](https://github.com/10up/simple-podcasting/pull/254)).
### Security
- Bump `postcss` from 8.4.27 to 8.4.31 (props [@dependabot](https://github.com/apps/dependabot), [@faisal-alvi](https://github.com/faisal-alvi) via [#256](https://github.com/10up/simple-podcasting/pull/256)).
- Bump `@babel/traverse` from 7.22.8 to 7.23.2 (props [@dependabot](https://github.com/apps/dependabot), [@Sidsector9](https://github.com/Sidsector9) via [#257](https://github.com/10up/simple-podcasting/pull/257)).
## [1.6.0] - 2023-08-31
### Added
- Ability to create a Podcast from within the Block Editor (props [@Sidsector9](https://github.com/Sidsector9), [@iamdharmesh](https://github.com/iamdharmesh) via [#232](https://github.com/10up/simple-podcasting/pull/232)).
- New Podcast Platforms block that allows you to display icons and links to multiple podcast platforms (props [@Sidsector9](https://github.com/Sidsector9), [@iamdharmesh](https://github.com/iamdharmesh), [@jeffpaul](https://github.com/jeffpaul) via [#241](https://github.com/10up/simple-podcasting/pull/241)).
- Check for minimum required PHP version before loading the plugin (props [@kmgalanakis](https://github.com/kmgalanakis), [@dkotter](https://github.com/dkotter) via [#248](https://github.com/10up/simple-podcasting/pull/248)).
### Changed
- Rename `TAXONOMY_NAME` constant to `PODCASTING_TAXONOMY_NAME` (props [@jayedul](https://github.com/jayedul), [@peterwilsoncc](https://github.com/peterwilsoncc), [@dkotter](https://github.com/dkotter) via [#238](https://github.com/10up/simple-podcasting/pull/238)).
- Bump WordPress "tested up to" version to 6.3 (props [@dkotter](https://github.com/dkotter) via [#248](https://github.com/10up/simple-podcasting/pull/248)).
### Fixed
- Resolved a PHP warning when creating a new podcast (props [@kmgalanakis](https://github.com/kmgalanakis), [@iamdharmesh](https://github.com/iamdharmesh) via [#247](https://github.com/10up/simple-podcasting/pull/247)).
### Security
- Bump `word-wrap` from 1.2.3 to 1.2.4 (props [@dependabot](https://github.com/apps/dependabot), [@iamdharmesh](https://github.com/iamdharmesh) via [#243](https://github.com/10up/simple-podcasting/pull/243)).
## [1.5.0] - 2023-06-29
### Added
- Post Grid Block to display a grid of episode posts (props [@mehul0810](https://github.com/mehul0810), [@cadic](https://github.com/cadic), [@nateconley](https://github.com/nateconley), [@dkotter](https://github.com/dkotter), [@jeffpaul](https://github.com/jeffpaul), [@ajmaurya99](https://github.com/ajmaurya99), [@nickolas-kola](https://github.com/nickolas-kola), [@achchu93](https://github.com/achchu93) via [#214](https://github.com/10up/simple-podcasting/pull/214)).
- Mochawesome reporter added for Cypress end-to-end test report (props [@jayedul](https://github.com/jayedul), [@iamdharmesh](https://github.com/iamdharmesh) via [#236](https://github.com/10up/simple-podcasting/pull/236)).
### Changed
- Mark any required fields when adding/editing a podcast feed (props [@mehul0810](https://github.com/mehul0810), [@cadic](https://github.com/cadic), [@nateconley](https://github.com/nateconley), [@jeffpaul](https://github.com/jeffpaul), [@Spoygg](https://github.com/Spoygg), [@ggutenberg](https://github.com/ggutenberg), [@peterwilsoncc](https://github.com/peterwilsoncc), [@Sidsector9](https://github.com/Sidsector9), [@ravinderk](https://github.com/ravinderk), [@faisal-alvi](https://github.com/faisal-alvi), [@helen](https://github.com/helen) via [#216](https://github.com/10up/simple-podcasting/pull/216)).
- Bumped WordPress "tested up to" version 6.2 (props [@jayedul](https://github.com/jayedul), [@peterwilsoncc](https://github.com/peterwilsoncc), [@jeffpaul](https://github.com/jeffpaul) via [#230](https://github.com/10up/simple-podcasting/pull/230)).
- Run end-to-end tests on the zip generated by the "Build Release ZIP" GitHub Action (props [@jayedul](https://github.com/jayedul), [@Sidsector9](https://github.com/Sidsector9), [@iamdharmesh](https://github.com/iamdharmesh) via [#227](https://github.com/10up/simple-podcasting/pull/227)).
- GitHub Action `uses` updates (props [@Sidsector9](https://github.com/Sidsector9), [@iamdharmesh](https://github.com/iamdharmesh) via [#234](https://github.com/10up/simple-podcasting/pull/234)).
- Updated Dependency Review GitHub Action (props [@jeffpaul](https://github.com/jeffpaul), [@Sidsector9](https://github.com/Sidsector9) via [#237](https://github.com/10up/simple-podcasting/pull/237)).
### Removed
- Deprecated `<itunes:summary>` tag (props [@ggutenberg](https://github.com/ggutenberg), [@Sidsector9](https://github.com/Sidsector9), [@cadic](https://github.com/cadic), [@jeffpaul](https://github.com/jeffpaul) via [#223](https://github.com/10up/simple-podcasting/pull/223)).
- Unnecessary term meta registration on "init" (props [@kmgalanakis](https://github.com/kmgalanakis), [@faisal-alvi](https://github.com/faisal-alvi), [@cadic](https://github.com/cadic) via [#225](https://github.com/10up/simple-podcasting/pull/225)).
### Fixed
- Deprecation notices for `strpos` and `str_replace` on PHP >= 8.1 (props [@bmarshall511](https://github.com/bmarshall511), [@Sidsector9](https://github.com/Sidsector9), [@peterwilsoncc](https://github.com/peterwilsoncc) via [#239](https://github.com/10up/simple-podcasting/pull/239)).
### Security
- Bump `simple-git` from 3.15.1 to 3.16.0 (props [@dependabot](https://github.com/apps/dependabot), [@cadic](https://github.com/cadic) via [#215](https://github.com/10up/simple-podcasting/pull/215)).
- Bump `http-cache-semantics` from 4.1.0 to 4.1.1 (props [@dependabot](https://github.com/apps/dependabot), [@cadic](https://github.com/cadic) via [#219](https://github.com/10up/simple-podcasting/pull/219)).
- Bump `@sideway/formula` from 3.0.0 to 3.0.1 (props [@dependabot](https://github.com/apps/dependabot), [@cadic](https://github.com/cadic) via [#220](https://github.com/10up/simple-podcasting/pull/220)).
- Bump `webpack` from 5.75.0 to 5.76.1 (props [@dependabot](https://github.com/apps/dependabot), [@faisal-alvi](https://github.com/faisal-alvi) via [#222](https://github.com/10up/simple-podcasting/pull/222)).
## [1.4.0] - 2023-01-23
### Added
- New podcast onboarding flow (props [@Sidsector9](https://github.com/Sidsector9), [@cadic](https://github.com/cadic), [@iamdharmesh](https://github.com/iamdharmesh), [@helen](https://github.com/helen), [@jeffpaul](https://github.com/jeffpaul), [@Nicolas-knight](https://github.com/Nicolas-knight), [@jnetek](https://github.com/jnetek) via [#193](https://github.com/10up/simple-podcasting/pull/193)).
- Description field to RSS feed (props [@supersmo](https://github.com/supersmo), [@cadic](https://github.com/cadic) via [#204](https://github.com/10up/simple-podcasting/pull/204)).
- Build pre-release zip GitHub Action (props [@Sidsector9](https://github.com/Sidsector9), [@iamdharmesh](https://github.com/iamdharmesh), [@jeffpaul](https://github.com/jeffpaul), [@dkotter](https://github.com/dkotter), [@faisal-alvi](https://github.com/faisal-alvi), [@vikrampm1](https://github.com/vikrampm1) via [#199](https://github.com/10up/simple-podcasting/pull/199)).
### Changed
- Bump Wordpress "tested up to" to 6.1 (props [@jayedul](https://github.com/jayedul), [@dkotter](https://github.com/dkotter) via [#201](https://github.com/10up/simple-podcasting/pull/201)).
- Cypress integration migrated to 11+ (props [@jayedul](https://github.com/jayedul), [@cadic](https://github.com/cadic), [@jeffpaul](https://github.com/jeffpaul) via [#205](https://github.com/10up/simple-podcasting/pull/205)).
- Updated docs to add podcast feed to Pocket Casts (props [@jeffpaul](https://github.com/jeffpaul), [@Sidsector9](https://github.com/Sidsector9), [@cadic](https://github.com/cadic) via [#192](https://github.com/10up/simple-podcasting/pull/192)).
### Fixed
- Spotify not accepting feeds with empty `<description>` field (props [@supersmo](https://github.com/supersmo), [@cadic](https://github.com/cadic) via [#204](https://github.com/10up/simple-podcasting/pull/204)).
### Security
- Bump `json5` from 1.0.1 to 1.0.2 (props [@dependabot[bot]](https://github.com/apps/dependabot), [@cadic](https://github.com/cadic), [@jeffpaul](https://github.com/jeffpaul) via [#212](https://github.com/10up/simple-podcasting/pull/212)).
- Bump `loader-utils` from 2.0.2 to 2.0.4 (props [@dependabot[bot]](https://github.com/apps/dependabot), [@cadic](https://github.com/cadic), [@jeffpaul](https://github.com/jeffpaul) via [#195](https://github.com/10up/simple-podcasting/pull/195), [#198](https://github.com/10up/simple-podcasting/pull/198)).
- Bump `simple-git` from 3.14.1 to 3.15.1 (props [@dependabot[bot]](https://github.com/apps/dependabot), [@jeffpaul](https://github.com/jeffpaul) via [#202](https://github.com/10up/simple-podcasting/pull/202)).
## [1.3.0] - 2022-10-18
**Note that this version bumps the minimum PHP version from 7.0 to 7.4 and the minimum WordPress version from 4.6 to 5.7.**
### Added
- Podcasts Taxonomy term(s) added in block settings (props [@helen](https://github.com/helen), [@jeffpaul](https://github.com/jeffpaul), [@faisal-alvi](https://github.com/faisal-alvi), [@peterwilsoncc](https://github.com/peterwilsoncc), [@cadic](https://github.com/cadic) via [#183](https://github.com/10up/simple-podcasting/pull/183)).
- Type of show setting for the podcast (props [@cadic](https://github.com/cadic), [@faisal-alvi](https://github.com/faisal-alvi), [@jeffpaul](https://github.com/jeffpaul) via [#188](https://github.com/10up/simple-podcasting/pull/188)).
### Changed
- Podcasting Categories and Sub-Categories (props [@zamanq](https://github.com/zamanq), [@jeffpaul](https://github.com/jeffpaul), [@dkotter](https://github.com/dkotter), [@cadic](https://github.com/cadic), [@dchucks](https://github.com/dchucks) via [#179](https://github.com/10up/simple-podcasting/pull/179)).
- Bumped minimum PHP version required from 7.0 to 7.4 (props [@peterwilsoncc](https://github.com/peterwilsoncc), [@cadic](https://github.com/cadic), [@jeffpaul](https://github.com/jeffpaul), [@vikrampm1](https://github.com/vikrampm1) via [#184](https://github.com/10up/simple-podcasting/pull/184)).
- Bumped minimum WordPress version required from 4.6 to 5.7 (props [@peterwilsoncc](https://github.com/peterwilsoncc), [@cadic](https://github.com/cadic), [@jeffpaul](https://github.com/jeffpaul), [@vikrampm1](https://github.com/vikrampm1) via [#184](https://github.com/10up/simple-podcasting/pull/184)).
- Upgrade dependencies (props [@cadic](https://github.com/cadic), [@faisal-alvi](https://github.com/faisal-alvi) via [#187](https://github.com/10up/simple-podcasting/pull/187)).
### Fixed
- Saving podcast enclosure with Classic Editor (props [@cadic](https://github.com/cadic), [@faisal-alvi](https://github.com/faisal-alvi) via [#186](https://github.com/10up/simple-podcasting/pull/186)).
### Security
- Bump `got` from 10.7.0 to 11.8.5 (props [@faisal-alvi](https://github.com/faisal-alvi), [@iamdharmesh](https://github.com/iamdharmesh), [@jeffpaul](https://github.com/jeffpaul) via [#185](https://github.com/10up/simple-podcasting/pull/185)).
- Bump `@wordpress/env` from 4.5.0 to 5.2.0 (props [@faisal-alvi](https://github.com/faisal-alvi), [@iamdharmesh](https://github.com/iamdharmesh), [@jeffpaul](https://github.com/jeffpaul) via [#185](https://github.com/10up/simple-podcasting/pull/185)).
## [1.2.4] - 2022-07-27
### Added
- Season number, episode number and episode type attributes can now be stored with a Podcast (props [@zamanq](https://github.com/zamanq), [@dchucks](https://github.com/dchucks), [@cadic](https://github.com/cadic) via [#175](https://github.com/10up/simple-podcasting/pull/175)).
### Changed
- Bump WordPress version "tested up to" 6.0 (props [@cadic](https://github.com/cadic) via [#171](https://github.com/10up/simple-podcasting/issues/171)).
### Fixed
- Incorrect Language value in the Feed (props [@zamanq](https://github.com/zamanq), [@dchucks](https://github.com/dchucks), [@cadic](https://github.com/cadic) via [#176](https://github.com/10up/simple-podcasting/pull/176)).
### Security
- Bump `terser` from 5.12.1 to 5.14.2 (props [@dependabot](https://github.com/apps/dependabot) via [#180](https://github.com/10up/simple-podcasting/pull/180)).
## [1.2.3] - 2022-04-28
### Added
- Compatibility tests against PHP 7 and 8 (props [@cadic](https://github.com/cadic), [@dkotter](https://github.com/dkotter), [@jeffpaul](https://github.com/jeffpaul) via [#150](https://github.com/10up/simple-podcasting/pull/150)).
- Default Pull Request Reviewers via CODEOWNERS file (props [@jeffpaul](https://github.com/jeffpaul), [@cadic](https://github.com/cadic) via [#156](https://github.com/10up/simple-podcasting/pull/156)).
- Dependency security scanning (props [@jeffpaul](https://github.com/jeffpaul) via [#168](https://github.com/10up/simple-podcasting/pull/168)).
### Changed
- Unit tests against PHP 8 (props [@cadic](https://github.com/cadic), [@dkotter](https://github.com/dkotter), [@jeffpaul](https://github.com/jeffpaul) via [#150](https://github.com/10up/simple-podcasting/pull/150)).
- Bump required PHP 7.0 (props [@cadic](https://github.com/cadic), [@dkotter](https://github.com/dkotter), [@jeffpaul](https://github.com/jeffpaul) via [#150](https://github.com/10up/simple-podcasting/pull/150)).
- Replaced custom commands with @10up/cypress-wp-utils in end-to-end tests (props [@dinhtungdu](https://github.com/dinhtungdu), [@cadic](https://github.com/cadic), [@jeffpaul](https://github.com/jeffpaul) via [#162](https://github.com/10up/simple-podcasting/pull/162)).
### Fixed
- Missing `<enclosure>` in feed item (props [@davexpression](https://github.com/davexpression), [@cadic](https://github.com/cadic), [@jeffpaul](https://github.com/jeffpaul) via [#147](https://github.com/10up/simple-podcasting/pull/147)).
- Failing Cypress test on WP Minimum (props [@dinhtungdu](https://github.com/dinhtungdu), [@cadic](https://github.com/cadic), [@jeffpaul](https://github.com/jeffpaul) via [#164](https://github.com/10up/simple-podcasting/pull/164)).
- Updated badges in readme (props [@cadic](https://github.com/cadic), [@jeffpaul](https://github.com/jeffpaul) via [#167](https://github.com/10up/simple-podcasting/pull/167)).
### Security
- Upgraded node dependencies (props [@cadic](https://github.com/cadic), [@iamdharmesh](https://github.com/iamdharmesh), [@jeffpaul](https://github.com/jeffpaul) via [#158](https://github.com/10up/simple-podcasting/pull/158) and [#163](https://github.com/10up/simple-podcasting/pull/163)).
- Bump async from 2.6.3 to 2.6.4 (props [@dependabot](https://github.com/apps/dependabot) via [#166](https://github.com/10up/simple-podcasting/pull/166)).
- Bump node-forge from 1.2.1 to 1.3.0 (props [@dependabot](https://github.com/apps/dependabot) via [#160](https://github.com/10up/simple-podcasting/pull/160)).
- Bump minimist from 1.2.5 to 1.2.6 (props [@dependabot](https://github.com/apps/dependabot) via [#159](https://github.com/10up/simple-podcasting/pull/159)).
## [1.2.2] - 2022-03-01
### Added
- Filter `simple_podcasting_feed_item` to modify RSS feed item data before output (props [@cadic](https://github.com/cadic), [@iamdharmesh](https://github.com/iamdharmesh), [@jeffpaul](https://github.com/jeffpaul) via [#144](https://github.com/10up/simple-podcasting/pull/144)).
- Unit tests (props [@cadic](https://github.com/cadic) via [#142](https://github.com/10up/simple-podcasting/pull/142), [@dkotter](https://github.com/dkotter), [@jeffpaul](https://github.com/jeffpaul)).
- GitHub action job to run PHPCS (props [@cadic](https://github.com/cadic), [@dkotter](https://github.com/dkotter) via [#136](https://github.com/10up/simple-podcasting/pull/136)).
- Auto-create pot file in languages folder during the build process (props [@dkotter](https://github.com/dkotter), [@cadic](https://github.com/cadic) via [#131](https://github.com/10up/simple-podcasting/pull/131)).
### Changed
- Bump WordPress "tested up to" version 5.9 (props [@sudip-10up](https://github.com/sudip-10up), [@cadic](https://github.com/cadic), [@peterwilsoncc](https://github.com/peterwilsoncc) via [#140](https://github.com/10up/simple-podcasting/pull/140)).
### Fixed
- End-to-end tests with WordPress 5.9 element IDs (props[@cadic](https://github.com/cadic), [@felipeelia](https://github.com/felipeelia), [@dinhtungdu](https://github.com/dinhtungdu) via [#146](https://github.com/10up/simple-podcasting/pull/146)).
- Podcast feed link output on Edit Podcast screen (props [@mehidi258](https://github.com/mehidi258), [@jeffpaul](https://github.com/jeffpaul), [@cadic](https://github.com/cadic) via [#139](https://github.com/10up/simple-podcasting/pull/139)).
- Bug fix for `is_feed` being called too early (props [@tomjn](https://github.com/tomjn), [@jeffpaul](https://github.com/jeffpaul) via [#135](https://github.com/10up/simple-podcasting/pull/135)).
- Missing and incorrect text-domain (props [@dkotter](https://github.com/dkotter), [@cadic](https://github.com/cadic) via [#131](https://github.com/10up/simple-podcasting/pull/131)).
### Security
- Bump `nanoid` from 3.1.25 to 3.2.0 (props [@dependabot](https://github.com/apps/dependabot) via [#143](https://github.com/10up/simple-podcasting/pull/143)).
## [1.2.1] - 2021-12-16
### Added
- Filter `simple_podcasting_episodes_per_page` to override default of 250 episodes per podcast feed (props [@pabamato](https://github.com/pabamato), [@dinhtungdu](https://github.com/dinhtungdu), [@monomo111](https://github.com/monomo111), [@jeffpaul](https://github.com/jeffpaul), [@jakemgold](https://github.com/jakemgold) via [#109](https://github.com/10up/simple-podcasting/pull/109)).
- End-to-end testing using Cypress and `wp-env` (props [@dinhtungdu](https://github.com/dinhtungdu), [@markjaquith](https://github.com/markjaquith), [@youknowriad](https://github.com/youknowriad), [@helen](https://github.com/helen) via [#115](https://github.com/10up/simple-podcasting/pull/115), [#117](https://github.com/10up/simple-podcasting/pull/117)).
- Issue management automation via GitHub Actions (props [@jeffpaul](https://github.com/jeffpaul) via [#119](https://github.com/10up/simple-podcasting/pull/119)).
- Pull request template (props [@jeffpaul](https://github.com/jeffpaul), [@dinhtungdu](https://github.com/dinhtungdu) via [#125](https://github.com/10up/simple-podcasting/pull/125)).
### Changed
- Default number of episodes in RSS feeds increased from 10 to 250 (props [@pabamato](https://github.com/pabamato), [@dinhtungdu](https://github.com/dinhtungdu), [@monomo111](https://github.com/monomo111), [@jeffpaul](https://github.com/jeffpaul), [@jakemgold](https://github.com/jakemgold) via [#109](https://github.com/10up/simple-podcasting/pull/109)).
- Use `@wordpress/scripts` as the build tool (props [@dinhtungdu](https://github.com/dinhtungdu) via [#114](https://github.com/10up/simple-podcasting/pull/114)).
- Bump WordPress version “tested up to” 5.8.1 (props [David Chabbi](https://www.linkedin.com/in/david-chabbi-985719b4/), [@jeffpaul](https://github.com/jeffpaul), [@pabamato](https://github.com/pabamato) via [#106](https://github.com/10up/simple-podcasting/pull/106), [#110](https://github.com/10up/simple-podcasting/pull/110), [#124](https://github.com/10up/simple-podcasting/pull/124)).
- Documentation updates (props [@meszarosrob](https://github.com/meszarosrob), [@dinhtungdu](https://github.com/dinhtungdu) via [#101](https://github.com/10up/simple-podcasting/pull/101)).
### Fixed
- 'podcast' block core dependency (props [@pabamato](https://github.com/pabamato), [@dinhtungdu](https://github.com/dinhtungdu), [@monomo111](https://github.com/monomo111), [@jeffpaul](https://github.com/jeffpaul), [@jakemgold](https://github.com/jakemgold) via [#109](https://github.com/10up/simple-podcasting/pull/109)).
- Minimum WordPress version used by `wp-env` (props [@dinhtungdu](https://github.com/dinhtungdu) via [#122](https://github.com/10up/simple-podcasting/pull/122)).
## [1.2.0] - 2020-07-10
### Added
- Podcast image in the taxonomy list table view (props [@Firestorm980](https://github.com/Firestorm980), [@helen](https://github.com/helen) via [#87](https://github.com/10up/simple-podcasting/pull/87)).
- Ability for user to transform to/from the podcast and audio blocks (props [@Firestorm980](https://github.com/Firestorm980), [@helen](https://github.com/helen) via [#85](https://github.com/10up/simple-podcasting/pull/85)).
- Core `MediaReplaceFlow` to edit the podcast media (props [@Firestorm980](https://github.com/Firestorm980), [@helen](https://github.com/helen) via [#86](https://github.com/10up/simple-podcasting/pull/86)).
### Changed
- GitHub Actions from HCL to YAML workflow syntax (props [@helen](https://github.com/helen) via [#78](https://github.com/10up/simple-podcasting/pull/78)).
- Stop committing built files (props [@helen](https://github.com/helen) via [#95](https://github.com/10up/simple-podcasting/pull/95)).
- Documentation updates (props [@jeffpaul](https://github.com/jeffpaul), [@nhalstead](https://github.com/nhalstead) via [#76](https://github.com/10up/simple-podcasting/pull/76), [#79](https://github.com/10up/simple-podcasting/pull/79)).
### Fixed
- Using the upload or drag and drop instead of media library populates duration and mimetype (props [@Firestorm980](https://github.com/Firestorm980), [@helen](https://github.com/helen) via [#82](https://github.com/10up/simple-podcasting/pull/82)).
- Issue where it is possible to add non-audio files to the Podcast block (props [@mattheu](https://github.com/mattheu) via [#77](https://github.com/10up/simple-podcasting/pull/77)).
- Issue where React would throw an error relating to keys for list items (props [@Firestorm980](https://github.com/Firestorm980), [@helen](https://github.com/helen) via [#85](https://github.com/10up/simple-podcasting/pull/85)).
- Ensure podcast-related meta is deleted after block is removed. (props [@dinhtungdu](https://github.com/dinhtungdu) via [#96](https://github.com/10up/simple-podcasting/pull/96)).
## [1.1.1] - 2019-08-02
### Added
- GitHub Actions for WordPress.org plugin deploy (props [@helen](https://github.com/helen) via [#75](https://github.com/10up/simple-podcasting/pull/75)).
### Fixed
- Compatibility with WordPress 5.2 (props [@adamsilverstein](https://github.com/adamsilverstein) via [#68](https://github.com/10up/simple-podcasting/pull/68), [#70](https://github.com/10up/simple-podcasting/pull/70)).
- Corrected `10up/wp_mock` reference for Composer (props [@oscarssanchez](https://github.com/oscarssanchez) via [#69](https://github.com/10up/simple-podcasting/pull/69)).
## [1.1.0] - 2018-12-04
### Added
- Retrieve metadata for externally hosted audio files in the block editor.
- Specify email address for a given podcast.
- Set language for a given podcast.
- Developers: Add linting for coding standards.
### Changed
- Clearer language on the add new podcast form.
### Fixed
- Delete all associated meta when block is removed from a post.
- Restore all block editor functionality to align with Gutenberg/block changes.
- Fully clear add new form after creating a new podcast.
## [1.0.1] - 2018-07-02
### Fixed
- Properly output podcast categories and subcategories in the feed.
- Avoid a minified JS error when selecting a podcast image.
- Display podcast summary on edit form.
## [1.0.0] - 2018-06-29
- Initial plugin release.
[Unreleased]: https://github.com/10up/simple-podcasting/compare/trunk...develop
[1.9.1]: https://github.com/10up/simple-podcasting/compare/1.9.0...1.9.1
[1.9.0]: https://github.com/10up/simple-podcasting/compare/1.8.0...1.9.0
[1.8.0]: https://github.com/10up/simple-podcasting/compare/1.7.0...1.8.0
[1.7.0]: https://github.com/10up/simple-podcasting/compare/1.6.1...1.7.0
[1.6.1]: https://github.com/10up/simple-podcasting/compare/1.6.0...1.6.1
[1.6.0]: https://github.com/10up/simple-podcasting/compare/1.5.0...1.6.0
[1.5.0]: https://github.com/10up/simple-podcasting/compare/1.4.0...1.5.0
[1.4.0]: https://github.com/10up/simple-podcasting/compare/1.3.0...1.4.0
[1.3.0]: https://github.com/10up/simple-podcasting/compare/1.2.4...1.3.0
[1.2.4]: https://github.com/10up/simple-podcasting/compare/1.2.3-deploy...1.2.4
[1.2.3]: https://github.com/10up/simple-podcasting/compare/1.2.2...1.2.3-deploy
[1.2.2]: https://github.com/10up/simple-podcasting/compare/1.2.1...1.2.2
[1.2.1]: https://github.com/10up/simple-podcasting/compare/1.2.0...1.2.1
[1.2.0]: https://github.com/10up/simple-podcasting/compare/1.1.1...1.2.0
[1.1.1]: https://github.com/10up/simple-podcasting/compare/f8a958c...1.1.1
[1.1.0]: https://github.com/10up/simple-podcasting/compare/1.0.1...f8a958c
[1.0.1]: https://github.com/10up/simple-podcasting/compare/1.0.0...1.0.1
[1.0.0]: https://github.com/10up/simple-podcasting/releases/tag/1.0.0
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at opensource@10up.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing and Maintaining
First, thank you for taking the time to contribute!
The following is a set of guidelines for contributors as well as information and instructions around our maintenance process. The two are closely tied together in terms of how we all work together and set expectations, so while you may not need to know everything in here to submit an issue or pull request, it's best to keep them in the same document.
## Ways to contribute
Contributing isn't just writing code - it's anything that improves the project. All contributions for Simple Podcasting are managed right here on GitHub. Here are some ways you can help:
### Reporting bugs
If you're running into an issue with the plugin, please take a look through [existing issues](https://github.com/10up/simple-podcasting/issues) and [open a new one](https://github.com/10up/simple-podcasting/issues/new) if needed. If you're able, include steps to reproduce, environment information, and screenshots/screencasts as relevant.
### Suggesting enhancements
New features and enhancements are also managed via [issues](https://github.com/10up/simple-podcasting/issues). As project owners, 10up sets the direction and roadmap and may not prioritize or decide to implement if outside of the main goals of the plugin.
### Pull requests
Pull requests represent a proposed solution to a specified problem. They should always reference an issue that describes the problem and contains discussion about the problem itself. Discussion on pull requests should be limited to the pull request itself, i.e. code review.
For more on how 10up writes and manages code, check out our [10up Engineering Best Practices](https://10up.github.io/Engineering-Best-Practices/).
### Testing
Helping to test an open source project and provide feedback on success or failure of those tests is also a helpful contribution. You can find details on the Critical Flows and Test Cases in [this project's GitHub Wiki](https://github.com/10up/simple-podcasting/wiki/Critical-Flows-for-simple%E2%80%90podcasting) as well as details on our overall approach to [Critical Flows and Test Cases in our Open Source Best Practices](https://10up.github.io/Open-Source-Best-Practices/testing/#critial-flows). Submitting the results of testing via our Critical Flows as a comment on a Pull Request of a specific feature or as an Issue when testing the entire project is the best approach for providing testing results.
## Maintenance process
### Triage
Issues and WordPress.org forum posts should be reviewed weekly and triaged as necessary. Not all tasks have to be done at once or by the same person. Triage tasks include:
* Responding to new WordPress.org forum posts and GitHub issues/PRs with an acknolwedgment and following up on existing open/unresolved items that have had movement in the previous week.
* Marking forum posts as resolved when corresponding issues are fixed or as not a support issue if not relevant.
* Creating GitHub issues for WordPress.org forum posts as necessary or linking to them from existing related issues.
* Applying labels and milestones to GitHub issues.
#### Issue labels
All issues should be labeled as bugs (`type:bug`), enhancements/feature requests (`type:enhancement`), or questions/support (`type:question`). Each issue should only be of one "type".
Bugs and enhancements that are closed without a related change should be labeled as `declined`, `duplicate`, or `invalid`. Invalid issues would be where a problem is not reproducible or opened in the wrong repo and should be relatively uncommon. These labels are all prefixed with `closed:`.
There are two other labels that are GitHub defaults with more global meaning we've kept: `good first issue` and `help wanted`.
### Review against WordPress updates
During weekly triage, the tested up to version should be compared against the latest versions of WordPress, both the new and classic editors, and the standalone Gutenberg plugin. If there's a newer version of either, the plugin should be re-tested using any automated tests as well as any manual tests indicated below, and the tested up to version bumped and committed to both GitHub and the WordPress.org repository.
### Release cycle
New releases are targeted based on number and severity of changes along with human availability. When a release is targeted, a due date will be assigned to the appropriate milestone.
### Release instructions
1. Branch: Starting from `develop`, cut a release branch named `release/X.Y.Z` for your changes.
2. Version bump: Bump the version number in `simple-podcasting.php`, `package-lock.json`, `package.json`, and `readme.txt` if it does not already reflect the version being released. Update both the plugin "Version:" property and the plugin `PODCASTING_VERSION` constant in `simple-podcasting.php`.
3. Changelog: Add/update the changelog in both `CHANGELOG.md` and `readme.txt`.
4. Props: update `CREDITS.md` with any new contributors, confirm maintainers are accurate.
5. New files: Check to be sure any new files/paths that are unnecessary in the production version are included in `.distignore`.
6. Readme updates: Make any other readme changes as necessary. `README.md` is geared toward GitHub and `readme.txt` contains WordPress.org-specific content. The two are slightly different.
7. Merge: Make a non-fast-forward merge from your release branch to `develop` (or merge the pull request), then do the same for `develop` into `trunk`, ensuring you pull the most recent changes into `develop` first (`git checkout develop && git pull origin develop && git checkout trunk && git merge --no-ff develop`). `trunk` contains the latest stable release.
8. Push: Push your `trunk` branch to GitHub (e.g. `git push origin trunk`).
9. [Compare](https://github.com/10up/simple-podcasting/compare/trunk...develop) `trunk` to `develop` to ensure no additional changes were missed.
10. Test the pre-release ZIP locally by [downloading](https://github.com/10up/simple-podcasting/actions/workflows/build-release-zip.yml) it from the Build release zip action artifact and installing it locally. Ensure this zip has all the files we expect, that it installs and activates correctly and that all basic functionality is working.
11. Release: Create a [new release](https://github.com/10up/simple-podcasting/releases/new), naming the tag and the release with the new version number, and targeting the `trunk` branch. Paste the changelog from `CHANGELOG.md` into the body of the release and include a link to the [closed issues on the milestone](https://github.com/10up/simple-podcasting/milestone/#?closed=1).
12. SVN: Wait for the [GitHub Action](https://github.com/10up/simple-podcasting/actions) to finish deploying to the WordPress.org repository. If all goes well, users with SVN commit access for that plugin will receive an emailed diff of changes.
13. Check WordPress.org: Ensure that the changes are live on [WordPress.org](https://wordpress.org/plugins/simple-podcasting/). This may take a few minutes.
14. Close the milestone: Edit the [milestone](https://github.com/10up/simple-podcasting/milestone/#) with release date (in the `Due date (optional)` field) and link to GitHub release (in the `Description` field), then close the milestone.
15. Punt incomplete items: If any open issues or PRs which were milestoned for `X.Y.Z` do not make it into the release, update their milestone to `X.Y.Z+1`, `X.Y+1.0`, `X+1.0.0`, or `Future Release`.
================================================
FILE: CREDITS.md
================================================
The following acknowledges the Maintainers for this repository, those who have Contributed to this repository (via bug reports, code, design, ideas, project management, translation, testing, etc.), and any Libraries utilized.
## Maintainers
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).
## 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), [Helen Hou-Sandi (@helen)](https://github.com/helen), [Ryan Welcher (@ryanwelcher)](https://github.com/ryanwelcher), [David Chandra Purnama (@turtlepod)](https://github.com/turtlepod), [Oscar Sanchez S. (@oscarssanchez)](https://github.com/oscarssanchez), [Jon Christensen (@Firestorm980)](https://github.com/Firestorm980), [Jeffrey Paul (@jeffpaul)](https://github.com/jeffpaul), [Noah Halstead (@nhalstead)](https://github.com/nhalstead), [Matthew Haines-Young (@mattheu)](https://github.com/mattheu), [Tung Du (@dinhtungdu)](https://github.com/dinhtungdu), [David Chabbi](https://www.linkedin.com/in/david-chabbi-985719b4/), [Pablo Amato (@pabamato)](https://github.com/pabamato), [(@monomo111)](https://github.com/monomo111), [Jake Goldman (@jakemgold)](https://github.com/jakemgold), [Mark Jaquith (@markjaquith)](https://github.com/markjaquith), [Riad Benguella (@youknowriad)](https://github.com/youknowriad), [Mészáros Róbert (@meszarosrob)](https://github.com/meszarosrob), [Max Lyuchin (@cadic)](https://github.com/cadic), [Dharmesh Patel (@iamdharmesh)](https://github.com/iamdharmesh), [Darin Kotter (@dkotter)](https://github.com/dkotter), [Peter Wilson (@peterwilsoncc)](https://github.com/peterwilsoncc), [Felipe Elia (@felipeelia)](https://github.com/felipeelia), [Mehidi Hassan (@mehidi258)](https://github.com/mehidi258), [Tom J Nowell (@tomjn)](https://github.com/tomjn), [David Towoju (@davexpression)](https://github.com/davexpression), [Quamruz Zaman (@zamanq)](https://github.com/zamanq), [Debashish (@dchucks)](https://github.com/dchucks), [(@supersmo)](https://github.com/supersmo), [Jayedul Kabir (@jayedul)](https://github.com/jayedul), [Faisal Alvi (@faisal-alvi)](https://github.com/faisal-alvi), [Vikram Mopharty (@vikrampm1)](https://github.com/vikrampm1), [Siddharth Thevaril (@Sidsector9)](https://github.com/Sidsector9), [Nicolas Knight (@Nicolas-knight)](https://github.com/Nicolas-knight), [Jonathan Netek (@jnetek)](https://github.com/jnetek), [Mehul Gohil (@mehul0810)](https://github.com/mehul0810), [Nate Conley (@nateconley)](https://github.com/nateconley), [Ajay Maurya (@ajmaurya99)](https://github.com/ajmaurya99), [Nickolas Kola (@nickolas-kola)](https://github.com/nickolas-kola), [Ahamed Arshad Azmi (@achchu93)](https://github.com/achchu93), [Ivan Ivanić (@Spoygg)](https://github.com/Spoygg), [Garth Gutenberg (@ggutenberg)](https://github.com/ggutenberg), [Ravinder Kumar (@ravinderk)](https://github.com/ravinderk), [Konstantinos Galanakis (@kmgalanakis)](https://github.com/kmgalanakis), [Dependabot (@dependabot)](https://github.com/apps/dependabot), [Sumit Bagthariya (@qasumitbagthariya)](https://github.com/qasumitbagthariya), [Shazahan Kabir Saju (@sksaju)](https://github.com/sksaju), [Kirtan Gajjar (@kirtangajjar)](https://github.com/kirtangajjar), [Chetra Chann (@channchetra)](https://github.com/channchetra), [James Burgos (@jamesburgos)](https://github.com/jamesburgos), [Martin Burch (@martinburch)](https://github.com/martinburch), [Peter Sorensen (@psorensen)](https://github.com/psorensen), [Barney Jeffries (@barneyjeffries)](https://github.com/barneyjeffries), [Gus Austin (@gusaus)](https://github.com/gusaus), [Sonali Desai (@sonali886)](https://github.com/sonali886), [Gabriel Glogoški (@gabriel-glo)](https://github.com/gabriel-glo), [David Godleman (@godleman)](https://github.com/godleman), [Sudar Muthu (@sudar)](https://github.com/sudar), [David Bowman (@dabowman)](https://github.com/dabowman).
## Libraries
The following software libraries are utilized in this repository.
n/a.
================================================
FILE: LICENSE.md
================================================
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
================================================
FILE: README.md
================================================
# Simple Podcasting for WordPress

[](#support-level)    [](https://github.com/10up/simple-podcasting/blob/develop/LICENSE.md) [](https://github.com/10up/simple-podcasting/actions/workflows/dependency-review.yml) [](https://github.com/10up/simple-podcasting/actions/workflows/cypress.yml) [](https://github.com/10up/simple-podcasting/actions/workflows/phpunit.yml) [](https://github.com/10up/simple-podcasting/actions/workflows/phpcs.yml) [](https://github.com/10up/simple-podcasting/actions/workflows/php-compatibility.yml) [](https://github.com/10up/simple-podcasting/actions/workflows/github-code-scanning/codeql) [](https://playground.wordpress.net/?blueprint-url=https://raw.githubusercontent.com/10up/simple-podcasting/add/playground/.wordpress-org/blueprints/blueprint.json)
> Easily set up multiple podcast feeds using built-in WordPress posts. Includes a podcast block and podcast transcript block for the WordPress block editor (aka Gutenberg).
## Overview
Podcasting is a method to distribute audio messages through a feed to which listeners can subscribe. You can publish podcasts on your WordPress site and make them available for listeners in Apple Podcasts and through direct feed links for other podcasting apps by following these steps:

## Requirements
* PHP 7.4+
* [WordPress](http://wordpress.org) 6.6+
* RSS feeds must not be disabled
## Installation
1. Install the plugin via the plugin installer, either by searching for it or uploading a .zip file.
2. Activate the plugin.
3. Head to Posts → Podcasts and add at least one podcast.
4. Create a post and insert an audio embed (or a podcast block in the new WordPress editor) and select a Podcast feed to include it in.
## Create your podcast
From the WordPress Admin, go to Podcasts.
To create a podcast, complete all of the "Add New Podcast" fields and click "Add New Podcast".
* Name: this title appears in Apple Podcasts and any other podcast apps.
* Slug: this is the URL-friendly version of the Name field.
* Subtitle: the subtitle also appears in Apple Podcasts and any other podcast apps.
* Artist / Author name: the artist or producer of the work.
* Podcast email: a contact email address for your podcast.
* Summary: Apple Podcasts displays this summary when browsing through podcasts.
* Copyright / License information: copyright information viewable in Apple Podcasts or other podcast apps.
* Mark as explicit: mark Yes if podcast contains adult language or adult themes.
* Language: the main language spoken in the podcast.
* Cover image: add the URL for the cover art to appear in Apple Podcasts and other podcast apps. Click "Select Image" and choose an image from the Media Library. Note that podcast cover images must be between 1400 x 1400 and 3000 x 3000 pixels in JPG or PNG formats to work on Apple Podcasts.
* Keywords: add terms to help your podcast show up in search results on Apple Podcasts and other podcast apps.
* Categories: these allow your podcast to show up for those browsing Apple Podcasts or other podcast apps by category.
Repeat for each podcast you would like to create.
## Add content to your podcast
* Create a new post and assign it to one or more Podcasts using the panel labeled Podcasts.
* Upload or embed an audio file into this post using any of the usual WordPress methods. If using the new block-based WordPress editor (sometimes referred to as Gutenberg), insert a Podcast block. Only one Podcast block can be inserted per post.
* For more advanced settings, use the Podcasting meta box to mark explicit content or closed captioning available, season number, episode number, episode type, add a transcript and to optionally specify one media item in the post if you have more than one in your post. In the block-based editor, these are the block settings that appear in the sidebar when the podcast block is selected.
* Transcript: If desired, an optional transcript can be added from the settings of the Podcast block. This will add a Podcast Transcript block, allowing you to add a transcript consisting of time codes, citations, and paragrah text that can be embedded in the post, linked to an external plain HTML file, or linked in a special `<podcast:transcript>` XML element.
## Submit your podcast feed to Apple Podcasts
* Each podcast has a unique feed URL you can find on the Podcasts page. This is the URL you will submit to Apple.
* Ensure you test feeds before submitting them, see https://help.apple.com/itc/podcasts_connect/#/itcac471c970.
* Once the validator passes, submit your podcast. Podcasts submitted to Apple Podcasts do not become immediately available for subscription by others. They are submitted for review by Apple staff, see https://help.apple.com/itc/podcasts_connect/#/itcd88ea40b9
Podcast setup | Podcast in block editor | Podcast feed
------------- | ----------------- | ------------
[](.wordpress-org/screenshot-3.png) | [](.wordpress-org/screenshot-1.png) | [](.wordpress-org/screenshot-4.png)
Podcast Platforms block | Podcast Grid pattern | Podcast Transcript block
------------- | ----------------- | ------------
[](.wordpress-org/screenshot-2.png) | [](.wordpress-org/screenshot-5.png) | [](.wordpress-org/screenshot-6.png)
## Submit your podcast feed to Pocket Casts
* Validate your feeds at [Cast Feed Validator](https://www.castfeedvalidator.com/) before submitting them.
* Submit the podcast feed to https://pocketcasts.com/submit/
## Control how many episodes are listed on the feed
If you want to adjust the default number of episodes included in a podcast RSS feed, then utilize the following to do so...
```php
<?php
add_filter( 'simple_podcasting_episodes_per_page', 'podcasting_feed_episodes_per_page' );
/**
* Filter how many items are displayed on the feed
* Default is 250
*
* @param int $qty Items count.
* @return string
*/
function podcasting_feed_episodes_per_page( $qty ) {
return 300;
}
```
## Customize the RSS feed title
The `<title>` element of the RSS feed can be adjusted using the `simple_podcasting_feed_title` filter.
```php
<?php
add_filter( 'simple_podcasting_feed_title', 'podcasting_feed_update_feed_title', 10, 2 );
/**
* Filter the name of the of the feed channel
*
* @param $output Output to be modified.
* @param $term WP_Term object representing the podcast
* @return string
*/
function podcasting_feed_update_feed_title( $output, $term ) {
$term_name = $term->name;
return '10up Presents: ' . $term_name;
}
```
## Customize RSS feed
If you want to modify RSS feed items output, there is a filter for that:
```php
<?php
function podcasting_feed_item_filter( $feed_item = array(), $post_id = null, $term_id = null ) {
if ( 42 === $post_id ) {
$feed_item['keywords'] = 'one,two,three';
}
return $feed_item;
}
add_filter( 'simple_podcasting_feed_item', 'podcasting_feed_item_filter', 10, 3 );
```
## Frequently Asked Questions
### How do I get my podcast featured on Pocket Casts?
The Featured section of Pocket Casts is human-curated. To ensure that all podcasts have an equal opportunity at being featured, selections are made on the basis of merit.
If you’d like to suggest your podcast for a featured spot, reach out to curation@pocketcasts.com.
For more information, [read more](https://pocketcasts.com/podcast-producers/).
### How do I submit private and paid podcast feeds?
Follow this documentation to submit [private and paid podcast feeds](https://support.pocketcasts.com/article/password-protected-podcasts-2/)
### Where do I report security bugs found in this plugin?
Please report security bugs found in the source code of the Simple Podcasting plugin through the [Patchstack Vulnerability Disclosure Program](https://patchstack.com/database/vdp/0d49ba54-688e-484d-9411-4716696aa79b). The Patchstack team will assist you with verification, CVE assignment, and notify the developers of this plugin.
## Support Level
**Stable:** 10up is not planning to develop any new features for this, but will still respond to bug reports and security concerns. We welcome PRs, but any that include new features should be small and easy to integrate and should not include breaking changes. We otherwise intend to keep this tested up to the most recent version of WordPress.
## Changelog
A complete listing of all notable changes to Simple Podcasting for WordPress are documented in [CHANGELOG.md](https://github.com/10up/simple-podcasting/blob/develop/CHANGELOG.md).
## Contributing
Please read [CODE_OF_CONDUCT.md](https://github.com/10up/simple-podcasting/blob/develop/CODE_OF_CONDUCT.md) for details on our code of conduct, [CONTRIBUTING.md](https://github.com/10up/simple-podcasting/blob/develop/CONTRIBUTING.md) for details on the process for submitting pull requests to us, and [CREDITS.md](https://github.com/10up/simple-podcasting/blob/develop/CREDITS.md) for a listing of maintainers of, contributors to, and libraries used by Simple Podcasting for WordPress.
## Like what you see?
<a href="http://10up.com/contact/"><img src="https://github.com/10up/.github/blob/trunk/profile/10up-github-banner.jpg" width="850" alt="Work with the 10up WordPress Practice at Fueled"></a>
================================================
FILE: assets/css/podcasting-edit-term.css
================================================
.taxonomy-podcasting_podcasts .term-parent-wrap {
display: none;
}
.podcast-image-thumbnail {
max-width: 300px;
max-height: 200px;
}
.column-podcasting_image img {
height: auto;
max-width: 100%;
width: 75px;
}
.simple_podcasting__platforms img {
display: block;
max-width: 42px;
margin: 0 auto;
height: auto;
}
.simple_podcasting__platforms th {
padding: 15px 10px;
text-align: center;
}
.simple_podcasting__platforms td {
vertical-align: middle;
padding: 15px 10px;
}
.simple_podcasting__platforms-url {
min-width: 220px;
}
.simple_podcasting__platforms-icon {
transition: background-color 0.2s ease-out;
}
.simple_podcasting__platforms-icon--darken-bg {
background-color: rgb(28 52 59 / 22%);
transition: background-color 0.2s ease-in;
}
================================================
FILE: assets/css/podcasting-editor-screen.css
================================================
.components-input-control,
.components-base-control {
width: 100%;
}
.cover-art-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.cover-art-container button {
margin: 10px 0;
}
================================================
FILE: assets/css/podcasting-onboarding.scss
================================================
* {
box-sizing: border-box;
}
.admin_page_simple-podcasting-onboarding #wpcontent {
padding-left: 0;
}
#simple-podcasting {
&__onboarding-header {
width: 100%;
height: 79px;
padding: 0 1.75rem;
display: flex;
justify-content: space-between;
align-items: center;
background-color: #fff;
border-bottom: 1px solid #c0c0c1;
& > * {
flex-grow: 1;
flex-basis: 0;
}
}
&__branding {
display: flex;
align-items: center;
}
&__header-title {
text-align: center;
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
font-size: 17.8983px;
}
&__logo {
max-width: 56px;
height: auto;
img {
display: block;
width: 100%;
}
}
&__plugin-name {
margin-left: 11px;
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
font-size: 17.8983px;
line-height: 20px;
}
&__header-controls {
.simple-podcasting__btn {
float: right;
}
}
&__page-title {
margin-bottom: 30px;
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
font-size: 22px;
line-height: 24px;
}
&__upload-cover-image {
margin-top: 13px;
margin-bottom: 14px;
}
&__create-a-new-post-button {
padding: 10px 40px;
width: 240px;
display: block;
text-align: center;
text-decoration: none;
color: #fff !important;
}
&__cover-image-preview {
img {
display: block;
max-width: 256px;
}
}
}
.simple-podcasting {
// Body.
&__onboarding-body {
margin: 0 auto;
margin-top: 68px;
padding: 0 1.75rem;
&--step-1 {
max-width: 524px;
}
&--step-2 {
display: flex;
max-width: 1038px;
}
}
&__setting {
margin-bottom: 30px;
input,
textarea {
width: 100%;
padding: 11px 7px 10px 13px;
background: #FFFFFF;
border: 1px solid #828282;
border-radius: 5px;
}
select {
padding: 11px 7px 10px 13px;
width: 320px;
background: #FFFFFF;
border: 1px solid #828282;
border-radius: 5px;
}
}
&__setting-label {
display: block;
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
font-size: 16px;
line-height: 24px;
margin-bottom: 4px;
}
&__setting-description {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
font-size: 14px;
line-height: 24px;
color: #828282;
margin-top: 4px;
}
&__panel {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
font-size: 16px;
line-height: 24px;
flex-grow: 1;
flex-basis: 0;
p {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
font-size: 16px;
line-height: 24px;
}
&--left {
max-width: 483px;
a {
color: #000;
font-weight: 700;
}
}
}
&__podcast-block-preview {
width: 468px;
float: right;
box-shadow: 0px 0px 26px 8px rgba(0, 0, 0, 0.05);
img {
display: block;
width: 100%;
height: auto;
}
}
&__step-2-controls {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 64px;
a {
letter-spacing: 1px;
text-decoration-line: underline;
color: #4F4F4F !important;
}
}
}
.simple-podcasting__btn {
box-shadow: none;
border: 0;
outline: 0;
border-radius: 3px;
padding: 12px 40px;
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
font-size: 14px;
letter-spacing: 1px;
text-decoration: none;
cursor: pointer;
&--ghost {
border: 1px solid #000000;
background-color: rgba(0, 0, 0, 0);
color: #000;
}
&--black {
background: #4F4F4F;
border-radius: 3px;
color: #FFFFFF;
}
}
================================================
FILE: assets/css/podcasting-transcript.css
================================================
.wp-block-podcasting-podcast-transcript cite,
.wp-block-podcasting-podcast-transcript time {
display: block;
}
================================================
FILE: assets/js/blocks/latest-episode/index.js
================================================
import './index.scss';
================================================
FILE: assets/js/blocks/latest-episode/index.scss
================================================
.podcasting-latest-episode {
display: flex;
flex-direction: column;
justify-content: end;
min-height: 20rem;
overflow: hidden;
position: relative;
& .wp-block-post-featured-image {
height: 100%;
object-fit: fill;
object-position: center;
position: absolute;
width: 100%;
&::after {
background-color: rgb(0 0 0 / 75%);
content: "";
display: block;
height: 100%;
left: 0;
position: absolute;
top: 0;
width: 100%;
}
}
}
.podcasting-latest-episode__content {
color: #fff;
padding: 3rem;
position: relative;
z-index: 1;
@media (min-width: 768px) {
padding: 3rem;
}
& .wp-block-post-excerpt,
& .wp-block-post-excerpt__more-text {
margin-top: 0.25rem;
}
& .wp-block-post-excerpt__more-link {
color: #fff;
}
}
.editor-styles-wrapper .wp-block-post-content .podcasting-latest-episode__content .wp-block-post-excerpt__more-link:where(:not(.wp-element-button)) {
color: #fff;
}
================================================
FILE: assets/js/blocks/podcast/index.js
================================================
import './index.scss';
================================================
FILE: assets/js/blocks/podcast/index.scss
================================================
.wp-block-podcasting-podcast-outer {
border: 1px solid #707070;
border-radius: 4px;
padding: 20px;
}
.wp-block-podcasting-podcast__container {
margin-bottom: 10px;
@media (min-width: 768px) {
display: flex;
}
}
.wp-block-podcasting-podcast__show-art {
margin-bottom: 20px;
@media (min-width: 768px) {
flex-basis: 100px;
margin-bottom: 0;
margin-right: 20px;
}
}
.wp-block-podcasting-podcast__image {
aspect-ratio: 1/1;
height: auto;
position: relative;
& img {
display: block;
height: 100%;
object-fit: cover;
width: 100%;
}
}
.wp-block-podcasting-podcast__show-title {
margin: 0;
}
.wp-block-podcasting-podcast__show-details {
color: #575757;
font-size: 0.875rem;
text-transform: uppercase;
& span {
display: block;
margin-right: 6px;
@media (min-width: 768px) {
display: inline;
}
&::after {
@media (min-width: 768px) {
content: '/';
margin-left: 6px;
}
}
&:last-child {
margin-right: 0;
&::after {
display: none;
}
}
}
}
.wp-block-podcasting-podcast__caption {
margin-bottom: 10px;
}
.wp-block-podcasting-podcast {
margin: 0;
& audio {
display: block;
width: 100%;
}
}
================================================
FILE: assets/js/blocks/podcast-platforms/edit.js
================================================
import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
import { useState, useEffect } from '@wordpress/element';
import apiFetch from '@wordpress/api-fetch';
import { __ } from '@wordpress/i18n';
import { useDebounce } from 'use-debounce';
import {
Panel,
PanelBody,
PanelRow,
RangeControl,
SearchControl,
__experimentalItemGroup as ItemGroup,
__experimentalItem as Item,
BaseControl,
Button,
ButtonGroup,
Icon
} from '@wordpress/components';
function Edit( props ) {
const {
setAttributes,
isSelected,
attributes: {
showId,
iconSize,
align,
},
} = props;
/** State for the search text for the show name. Defaults to empty string. */
const [ searchText, setSearchText ] = useState( '' );
/** Debounced search text so that we don't trigger useEffect() for every character change. */
const [ debouncedSearchText ] = useDebounce( searchText, 300 );
/** Indicates when the ajax search for podcasts is completed. */
const [ isSearchCompleted, setIsSearchCompleted ] = useState( false );
/** State for search results matched by the search text. Defaults to array. */
const [ searchResults, setSearchResults ] = useState( [] );
/** State for the icon theme. Defaults to `color`. */
const [ iconTheme, setIconTheme ] = useState( 'color' );
/** State for platforms returned for a specific show. Defaults to array. */
const [ platforms, setPlatforms ] = useState( [] );
/**
* Hits the `/wp/v2/search` endpoint to search for
* podcast show by name.
*/
useEffect( () => {
const searchPodcastShow = async () => {
setIsSearchCompleted( false );
if ( ! searchText.length ) {
setSearchResults( [] );
return;
}
/** Query object required by `/wp/v2/search` to search for a term by name. */
const queryObject = {
search: searchText,
type: 'term',
subtype: 'podcasting_podcasts'
};
/** Converts an object to query-string. */
const queryString = new URLSearchParams( queryObject ).toString();
/** Returns the results of the search. */
const searchResults = await apiFetch( {
path: `/wp/v2/search?${ queryString }`,
} );
if ( ! searchResults.length ) {
setIsSearchCompleted( true );
}
setSearchResults( searchResults );
setIsSearchCompleted( true );
};
searchPodcastShow();
}, [ debouncedSearchText ] );
/**
* Fetches the podcasting platforms for a show whenever
* showId updates.
*/
useEffect( () => {
if ( ! showId ) {
return;
}
/**
* Responsible to fetch platforms for a show by show ID.
* @returns void
*/
const fetchPlatforms = async () => {
const result = await apiFetch( {
url: `${ ajaxurl }?show_id=${ showId }&action=get_podcast_platforms`,
} );
if ( ! result.success ) {
setPlatforms( [] );
return;
}
const {
data: { platforms, theme }
} = result;
setPlatforms( platforms );
setIconTheme( theme );
};
fetchPlatforms();
}, [ showId ] );
/**
* Handler to set the attribute showId.
*
* @param {Int} termId The show ID.
* @returns void
*/
const onShowSelect = ( termId ) => {
setAttributes( { showId: termId } );
setSearchResults( [] );
setIsSearchCompleted( false );
};
/**
* Handler to set size of the icon.
*
* @param {Int} size The icon size in `px`
*/
const setIconSize = ( size ) => {
setAttributes( { iconSize: size } );
};
/**
* Sets the HTML attributes for the root element.
*/
const blockProps = useBlockProps( {
className: isSelected ? 'simple-podcasting__podcast-platforms simple-podcasting__podcast-platforms--selected' : 'simple-podcasting__podcast-platforms',
} );
const platformSlugs = Object.keys( platforms );
return (
<>
<InspectorControls>
<Panel header={ __( 'Customization Controls', 'simple-podacsting' ) }>
<PanelBody>
<BaseControl label={ __( 'Icon size', 'simple-podcasting' ) }/>
<PanelRow>
<RangeControl
min={ 16 }
max={ 96 }
step={ 16 }
value={ iconSize }
onChange={ setIconSize }
/>
</PanelRow>
<BaseControl label={ __( 'Alignment', 'simple-podcasting' ) }/>
<PanelRow>
<ButtonGroup>
<Button
isPressed={ align === 'left' }
variant='ternary'
icon={ <Icon icon='align-left' /> }
onClick={ () => setAttributes( { align: 'left' } ) }
/>
<Button
isPressed={ align === 'center' }
variant='ternary'
icon={ <Icon icon='align-center' /> }
onClick={ () => setAttributes( { align: 'center' } ) }
/>
<Button
isPressed={ align === 'right' }
variant='ternary'
icon={ <Icon icon='align-right' /> }
onClick={ () => setAttributes( { align: 'right' } ) }
/>
</ButtonGroup>
</PanelRow>
</PanelBody>
</Panel>
</InspectorControls>
<div { ...blockProps }>
{
platformSlugs.length ? (
<div className={ `simple-podcasting__podcasting-platform-list simple-podcasting__podcasting-platform-list--${ align }` }>
{
platformSlugs.map( ( platform, index ) => {
return (
<span key={ index } className='simple-podcasting__podcasting-platform-list-item'>
<a href={ platforms[ platform ] } target="_blank">
<img className={ `simple-pocasting__icon-size--${ iconSize }` } src={ `${ podcastingPlatformVars.podcastingUrl }dist/images/icons/${ platform }/${ iconTheme }-100.png` } />
</a>
</span>
);
} )
}
</div>
) : (
<div className={ `simple-podcasting__podcasting-platform-list` }>
<p>{ __( 'No platforms are set for this podcast.', 'simple-podcasting' ) }</p>
</div>
)
}
{
isSelected || ! showId ? (
<div className='simple-podcasting__podcasting-search-controls'>
<SearchControl
placeholder={ __( 'Search a Podcast Show', 'simple-podcasting' ) }
onChange={ ( searchText ) => setSearchText( searchText ) }
value={ searchText }
/>
{
searchResults.length ?
(
<div className='simple-podcasting__podcasting-search-results'>
<ItemGroup
isSeparated
>
{
searchResults.map( ( result ) => (
<Item
key={ result.id }
className='simple-podcasting__podcast-search-results'
onClick={ () => onShowSelect( result.id ) }
>
{ result.title }
</Item>
) )
}
</ItemGroup>
</div>
) : (
! searchResults.length && isSearchCompleted ? (
<div className='simple-podcasting__podcasting-search-results'>
<ItemGroup
isSeparated
>
<Item>
{ __( 'No results found.' , 'simple-podcasting') }
</Item>
</ItemGroup>
</div>
) : null
)
}
</div>
) : null
}
</div>
</>
)
}
export default Edit;
================================================
FILE: assets/js/blocks/podcast-platforms/index.js
================================================
/**
* Internal block libraries
*/
import { __ } from '@wordpress/i18n';
import { registerBlockType } from '@wordpress/blocks';
import Edit from './edit';
import './index.scss';
/**
* Register Podcast Platforms block
*/
export default registerBlockType(
'podcasting/podcast-platforms',
{
title: __( 'Podcast Platforms', 'simple-podcasting' ),
description: __( 'Displays the list of platforms where the selected show is available.', 'simple-podcasting' ),
category: 'common',
icon: 'microphone',
supports: {
multiple: false,
},
attributes: {
showId: {
type: 'number',
default: 0,
},
iconSize: {
type: 'number',
default: 48,
},
align: {
type: 'string',
default: 'center',
}
},
edit: Edit,
save: () => null,
},
);
================================================
FILE: assets/js/blocks/podcast-platforms/index.scss
================================================
.wp-block {
.simple-podcasting__podcasting-platform-list {
margin-bottom: 3rem;
}
.simple-podcasting__podcast-platforms {
padding: 2.5rem 1rem 1rem 1rem;
}
}
.simple-podcasting {
&__podcast-platforms {
padding: 1rem 0 1rem 0;
}
&__podcast-search-results {
cursor: pointer;
}
&__select-show-popover {
max-width: 400px;
width: 100%;
.components-popover__content {
width: 100%;
padding: 1rem;
}
}
&__podcasting-platform-list {
display: flex;
flex-flow: row wrap;
&--left {
justify-content: start;
}
&--center {
justify-content: center;
}
&--right {
justify-content: end;
}
a {
text-decoration: none !important;
}
img {
display: block;
@for $i from 1 through 6 {
&.simple-pocasting__icon-size--#{$i * 16} {
max-width: #{$i * 16}px;
}
}
}
}
&__podcasting-platform-list-item {
display: flex;
align-self: center;
padding: 0.75rem;
@media screen and ( max-width: 480px ) {
justify-content: center;
width: 25%;
}
}
&__podcasting-search-controls {
position: relative;
input[type="search"] {
font-size: 1rem;
}
}
&__podcasting-search-results {
position: absolute;
border: 1px solid;
background-color: #fff;
min-width: 200px;
z-index: 10;
}
}
================================================
FILE: assets/js/blocks.js
================================================
/**
* Internal block libraries
*/
import { __ } from '@wordpress/i18n';
import { registerBlockType, registerBlockVariation } from '@wordpress/blocks';
// Split the Edit component out.
import Edit from './edit';
import transforms from './transforms';
import deprecated from './deprecated';
import '../css/podcasting-editor-screen.css';
/**
* Register example block
*/
export default registerBlockType(
'podcasting/podcast',
{
title: __( 'Podcast', 'simple-podcasting' ),
description: __( 'Insert a podcast episode into a post. To add it to a podcast feed, select a podcast in document settings.', 'simple-podcasting' ),
category: 'common',
icon: 'microphone',
supports: {
multiple: false,
},
attributes: {
id: {
type: 'number',
},
src: {
type: 'string',
source: 'attribute',
selector: 'audio',
attribute: 'src',
},
url: {
type: 'string',
source: 'meta',
meta: 'podcast_url',
},
filesize: {
type: 'number',
source: 'meta',
meta: 'podcast_filesize',
},
duration: {
type: 'string',
source: 'meta',
meta: 'podcast_duration',
},
mime: {
type: 'string',
source: 'meta',
meta: 'podcast_mime',
},
caption: {
type: 'array',
source: 'children',
selector: 'figcaption',
},
captioned: {
type: 'boolean',
source: 'meta',
meta: 'podcast_captioned',
default: false,
},
explicit: {
type: 'string',
source: 'meta',
meta: 'podcast_explicit',
},
enclosure: {
type: 'string',
source: 'meta',
meta: 'enclosure',
},
seasonNumber: {
type: 'string',
source: 'meta',
meta: 'podcast_season_number',
},
episodeNumber: {
type: 'string',
source: 'meta',
meta: 'podcast_episode_number',
},
episodeType: {
type: 'string',
source: 'meta',
meta: 'podcast_episode_type',
},
displayDuration: {
type: 'boolean',
default: false,
},
displayShowTitle: {
type: 'boolean',
default: false,
},
displayEpisodeTitle: {
type: 'boolean',
default: false,
},
displayArt: {
type: 'boolean',
default: false,
},
displayExplicitBadge: {
type: 'boolean',
default: false,
},
displaySeasonNumber: {
type: 'boolean',
default: false,
},
displayEpisodeNumber: {
type: 'boolean',
default: false,
},
displayEpisodeType: {
type: 'boolean',
default: false,
}
},
transforms,
edit: Edit,
save: props => {
const {
id,
src,
caption
} = props.attributes;
return (
<figure className={ id ? `podcast-${ id }` : null }>
{ caption && caption.length > 0 && <figcaption className="wp-block-podcasting-podcast__caption">{ caption }</figcaption> }
<audio controls="controls" src={ src } />
</figure>
);
},
deprecated,
},
);
const VARIATION_NAME = 'podcasting/latest-episode';
registerBlockVariation('core/query', {
name: VARIATION_NAME,
title: 'Latest Podcast Episode',
description: 'Displays the latest podcast episode.',
isActive: ['simple-podcasting'],
icon: 'microphone',
attributes: {
namespace: VARIATION_NAME,
query: {
postType: 'post',
podcastingQuery: 'not_empty',
},
},
allowedControls: [ ],
scope: [ 'inserter' ],
innerBlocks: [
[
'core/post-template',
{},
[ [
'core/group',
{ className: 'podcasting-latest-episode' },
[
[ 'core/post-featured-image' ],
[ 'core/group', { className: 'podcasting-latest-episode__content' }, [
[ 'core/post-title' ], [ 'core/post-date' ], [ 'core/post-excerpt' ]
] ],
]
] ],
],
[ 'core/query-no-results' ],
],
});
================================================
FILE: assets/js/create-podcast-show.js
================================================
import { registerPlugin } from "@wordpress/plugins";
import { store as editPostStore } from '@wordpress/edit-post';
import { __ } from "@wordpress/i18n";
import {
Button,
Modal,
TextControl,
SelectControl,
TextareaControl,
BaseControl,
Flex,
FlexItem,
CheckboxControl,
} from "@wordpress/components";
import { MediaUpload, MediaUploadCheck } from '@wordpress/block-editor';
import { useState, useEffect } from "@wordpress/element";
import { useSelect, dispatch } from "@wordpress/data";
// Once WordPress 6.6 becomes our minimum, change this back to `import { PluginDocumentSettingPanel } from '@wordpress/editor';`.
const PluginDocumentSettingPanel = wp.editor?.PluginDocumentSettingPanel ?? ( wp.editPost?.PluginDocumentSettingPanel ?? wp.editSite?.PluginDocumentSettingPanel );
// Due to unsupported versions of React, we're importing stores from the
// `wp` namespace instead of @wordpress NPM packages for the following.
const { store: editorStore } = wp.editor;
const { store: coreDataStore } = wp.coreData;
const DEFAULT_QUERY = {
per_page: -1,
orderby: 'name',
order: 'asc',
_fields: 'id,name,parent',
context: 'view',
};
const CreatePodcastShowModal = ( { isModalOpen, closeModal } ) => {
const [ showName, setShowName ] = useState( '' );
const [ artistName, setArtistName ] = useState( '' );
const [ showCategory, setShowCategory ] = useState( '' );
const [ summary, setSummary ] = useState( '' );
const [ coverId, setCoverId ] = useState( 0 );
const [ coverUrl, setCoverUrl ] = useState( '' );
const [ ajaxInprogress, setAjaxInProgress ] = useState( false );
const [ isPodcastCreated, setIsPodcastCreated ] = useState( false );
const modalStyle = {
maxWidth: '645px',
width: '100%'
};
const fieldStyle = {
marginBottom: '26px',
};
const createShow = async () => {
setAjaxInProgress( true );
try {
const podcast = await wp.data.dispatch( coreDataStore ).saveEntityRecord(
'taxonomy',
'podcasting_podcasts',
{
name: showName,
meta: {
podcasting_summary: summary,
podcasting_category_1: showCategory,
podcasting_image: coverId,
podcasting_image_url: coverUrl,
podcasting_talent_name: artistName,
}
}
);
if ( podcast ) {
setIsPodcastCreated( true );
}
} catch ( error ) {
setAjaxInProgress( false );
}
setAjaxInProgress( false );
};
if ( ! isModalOpen ) {
return false;
}
const categoriesOptions = Object.keys( podcastingShowPluginVars.categories ).map( key => ( { value: key, label: podcastingShowPluginVars.categories[key] } ) );
return (
<Modal
title={ isPodcastCreated ? __( 'Podcast created!', 'simple-podcasting' ) : __( 'Add New Podcast', 'simple-podcasting' ) }
style={ modalStyle }
onRequestClose={ ( event ) => {
const selectImageBtn = event.target.closest( '.podcasting__select-image-btn' );
if ( selectImageBtn ) {
return;
}
closeModal();
} }
>
{
isPodcastCreated ? (
<>
<Button
variant="link"
text={ __( 'Add another Podcast', 'simple-podcasting' ) }
onClick={ () => {
setIsPodcastCreated( false );
setShowName( '' );
setShowCategory( '' );
setSummary( '' );
setCoverId( 0 );
setCoverUrl( '' );
} }
/>
</>
) : (
<>
<div className="podcasting__modal-field-row" style={ fieldStyle }>
<TextControl
className="podcasting__modal-name-field"
label={ __( 'Podcast name*', 'simple-podcasting' ) }
help={ __( 'This is the name that listeners will see when searching or subscribing.', 'simple-podcasting' ) }
value={ showName }
onChange={ ( val ) => setShowName( val ) }
required
/>
</div>
<div className="podcasting__modal-field-row" style={ fieldStyle }>
<TextControl
className="podcasting__modal-artist-field"
label={ __( 'Artist name*', 'simple-podcasting' ) }
help={ __( 'Who’s the artist or author of your podcast show that listeners will see?', 'simple-podcasting' ) }
value={ artistName }
onChange={ ( val ) => setArtistName( val ) }
required
/>
</div>
<div className="podcasting__modal-field-row" style={ fieldStyle }>
<SelectControl
className="podcasting__modal-category-field"
label={ __( 'Category*', 'simple-podcasting' ) }
help={ __( 'Select the category listeners will use to discover your show when browsing podcatchers. You can also add subcategories later.', 'simple-podcasting' ) }
options={ categoriesOptions }
value={ showCategory }
onChange={ ( val ) => setShowCategory( val ) }
required
/>
</div>
<div className="podcasting__modal-field-row" style={ fieldStyle }>
<TextareaControl
className="podcasting__modal-summary-field"
label={ __( 'Summary*', 'simple-podcasting' ) }
help={ __( 'Briefly describe to your listeners what your show is about. (No HTML please.)', 'simple-podcasting' ) }
rows={ 6 }
value={ summary }
onChange={ ( val ) => setSummary( val ) }
required
/>
</div>
<div className="podcasting__modal-field-row" style={ fieldStyle }>
<MediaUploadCheck>
<MediaUpload
onSelect={ ( media ) => {
setCoverId( media.id );
setCoverUrl( media.url );
} }
allowedTypes={ [ 'image' ] }
value={ coverId }
render={ ( { open } ) => (
<>
<BaseControl label={ __( 'Cover Image*', 'simple-podcasting' ) } />
<Flex justify="normal">
<FlexItem>
<Button
className="podcasting__select-image-btn"
variant="secondary"
text={ coverId ? __( 'Replace Image', 'simple-podcasting' ) : __( 'Select Image', 'simple-podcasting' ) }
onClick={ open }
/>
</FlexItem>
{
coverId ? (
<FlexItem>
<Button
className="podcasting__remove-image-btn"
variant="secondary"
text={ __( 'Remove', 'simple-podcasting' ) }
isDestructive
onClick={ () => {
setCoverId( 0 );
setCoverUrl( '' );
} }
/>
</FlexItem>
) : null
}
</Flex>
{
coverId ? (
<div className="podcasting-cover-preview" style={ {
maxWidth: '256px',
marginTop: '1rem',
} }>
<img src={ coverUrl } style={ { width: '100%' } } />
</div>
) : null
}
<BaseControl help={ __( 'Square images are required to properly display within podcatcher apps.Minimum size: 1400 px x 1400 px. Maximum size: 2048 px x 2048 px.', 'simple-podcasting' ) } />
</>
) }
/>
</MediaUploadCheck>
</div>
<Flex justify="normal" gap={ 9 }>
<FlexItem>
<Button
className="podcasting__create-podcast-btn"
variant="primary"
text={ __( 'Create Podcast', 'simple-podcasting' ) }
disabled={ ! showName || '' === showCategory || ! artistName || ! summary || ! coverId }
onClick={ createShow }
isBusy={ ajaxInprogress }
/>
</FlexItem>
<FlexItem>
<Button
variant="link"
text={ __( 'Cancel', 'simple-podcasting' ) }
onClick={ closeModal }
/>
</FlexItem>
</Flex>
</>
)
}
</Modal>
);
};
const CreatePodcastShowPlugin = () => {
const { allPodcasts, attachedPodcasts, currentPostId } = useSelect( ( select ) => {
const { getEntityRecords } = select( coreDataStore );
const { getCurrentPostId } = select( editorStore );
return {
allPodcasts: getEntityRecords( 'taxonomy', 'podcasting_podcasts', DEFAULT_QUERY ) || [],
attachedPodcasts: getEntityRecords( 'taxonomy', 'podcasting_podcasts', { post: getCurrentPostId() } ) || [],
currentPostId: getCurrentPostId(),
}
} );
// Remove the default 'Podcast' taxonomy panel.
useEffect( () => {
dispatch( editPostStore ).removeEditorPanel( 'taxonomy-panel-podcasting_podcasts' );
}, [] );
const [ isModalOpen, setIsModalOpen ] = useState( false );
const openModal = () => setIsModalOpen( true );
const closeModal = () => setIsModalOpen( false );
const attachedPodcastIds = useSelect( ( select ) => {
return select( 'core/editor' ).getEditedPostAttribute( 'podcasting_podcasts' );
} );
/**
* Attaches the podcast term to the current post if selected.
*
* @param {Boolean} isChecked If the podcast term checkbox is checked.
* @param {Integer} podcastId The podcast term ID.
*/
function attachPodcastToPost( isChecked, podcastId ) {
let updatedAttachedPodcastIds = [ ...attachedPodcastIds, podcastId ];
if ( isChecked ) {
updatedAttachedPodcastIds = [ ...attachedPodcastIds, podcastId ];
} else {
updatedAttachedPodcastIds = attachedPodcastIds.filter( ( currentPodcastId ) => currentPodcastId !== podcastId );
}
dispatch( coreDataStore ).editEntityRecord(
'postType',
'post',
currentPostId,
{
podcasting_podcasts: updatedAttachedPodcastIds,
}
)
}
return (
<>
<PluginDocumentSettingPanel
title={ __( 'Podcasts', 'simple-podcasting' ) }
className='podcasting__podcast-list'
>
{
allPodcasts.map( ( item, index ) => {
return (
<CheckboxControl
className="podcasting__podcast-list-item"
key={ index }
label={ item.name }
onChange={ ( isChecked ) => attachPodcastToPost( isChecked, item.id ) }
checked={ attachedPodcastIds.includes( item.id ) }
/>
)
} )
}
<Button
variant="link"
text={ __( 'Add New Podcast', 'simple-podcasting' ) }
onClick={ openModal }
style={ { marginTop: '12px' } }
className="podcasting__add-new-podcast"
/>
</PluginDocumentSettingPanel>
<CreatePodcastShowModal
isModalOpen={ isModalOpen }
openModal={ openModal }
closeModal={ closeModal }
/>
</>
)
};
registerPlugin( 'podcasting-create-podcast-show', {
render: CreatePodcastShowPlugin
} );
================================================
FILE: assets/js/deprecated.js
================================================
export default [
{
attributes: {
id: {
type: 'number',
},
src: {
type: 'string',
source: 'attribute',
selector: 'audio',
attribute: 'src',
},
url: {
type: 'string',
source: 'meta',
meta: 'podcast_url',
},
filesize: {
type: 'number',
source: 'meta',
meta: 'podcast_filesize',
},
duration: {
type: 'string',
source: 'meta',
meta: 'podcast_duration',
},
mime: {
type: 'string',
source: 'meta',
meta: 'podcast_mime',
},
caption: {
type: 'array',
source: 'children',
selector: 'figcaption',
},
captioned: {
type: 'boolean',
source: 'meta',
meta: 'podcast_captioned',
default: false,
},
explicit: {
type: 'string',
source: 'meta',
meta: 'podcast_explicit',
default: 'no',
},
enclosure: {
type: 'string',
source: 'meta',
meta: 'enclosure',
},
seasonNumber: {
type: 'string',
source: 'meta',
meta: 'podcast_season_number',
},
episodeNumber: {
type: 'string',
source: 'meta',
meta: 'podcast_episode_number',
},
episodeType: {
type: 'string',
source: 'meta',
meta: 'podcast_episode_type',
}
},
supports: {
multiple: false,
},
save: props => {
const {
id,
src,
caption
} = props.attributes;
return (
<figure className={ id ? `podcast-${ id }` : null }>
<audio controls="controls" src={ src } />
{ caption && caption.length > 0 && <figcaption>{ caption }</figcaption> }
</figure>
);
},
},
];
================================================
FILE: assets/js/edit.js
================================================
const { __ } = wp.i18n;
const {
BlockControls,
InspectorControls,
MediaPlaceholder,
MediaReplaceFlow,
MediaUpload,
MediaUploadCheck,
RichText,
} = wp.blockEditor;
const {
ToggleControl,
PanelBody,
PanelRow,
SelectControl,
TextControl,
RadioControl,
} = wp.components;
const { Fragment } = wp.element;
const { apiFetch } = wp;
const ALLOWED_MEDIA_TYPES = ['audio'];
const { select } = wp.data;
import { Button } from '@wordpress/components';
import { useState, useEffect } from '@wordpress/element';
import { dispatch, useSelect, useDispatch } from '@wordpress/data';
import { createBlock } from '@wordpress/blocks';
function useFeaturedImage() {
const featuredImageId = useSelect((select) => select('core/editor').getEditedPostAttribute('featured_media'), []);
const { editPost } = useDispatch('core/editor');
const featuredImageUrl = useSelect((select) => {
const { getMedia } = select('core');
const image = getMedia(featuredImageId);
return image?.source_url;
}, [featuredImageId]);
const setFeaturedImage = (imageId) => {
editPost({ featured_media: imageId });
};
const removeFeaturedImage = () => {
editPost({ featured_media: 0 });
};
return { featuredImageUrl, setFeaturedImage, removeFeaturedImage, featuredImageId };
}
function Edit( props ) {
const {
className,
setAttributes,
isSelected,
attributes,
featuredImageUrl,
setFeaturedImage,
removeFeaturedImage,
featuredImageId
} = props;
const {
caption,
explicit,
displayDuration,
displayShowTitle,
displayEpisodeTitle,
displayArt,
displayExplicitBadge,
displaySeasonNumber,
displayEpisodeNumber,
displayEpisodeType
} = attributes;
const duration = attributes.duration || '';
const captioned = attributes.captioned || '';
const seasonNumber = attributes.seasonNumber || '';
const episodeNumber = attributes.episodeNumber || '';
const episodeType = attributes.episodeType || '';
const [ src, setSrc ] = useState( props.attributes.src );
const postTitle = useSelect( ( select ) => select( 'core/editor' ).getEditedPostAttribute( 'title' ) );
const onSelectAttachment = (attachment) => {
// Upload and Media Library return different attachment objects.
// Therefore, we need to check the existence of some entries.
let mime, filesize, duration;
if (attachment.mime) {
mime = attachment.mime;
} else if (attachment.mime_type) {
mime = attachment.mime_type;
}
if (attachment.filesizeInBytes) {
filesize = attachment.filesizeInBytes;
} else if (
attachment.media_details &&
attachment.media_details.filesize
) {
filesize = attachment.media_details.filesize;
}
if (attachment.fileLength) {
duration = attachment.fileLength;
} else if (
attachment.media_details &&
attachment.media_details.length_formatted
) {
duration = attachment.media_details.length_formatted;
}
setAttributes({
id: attachment.id,
src: attachment.url,
url: attachment.url,
mime,
filesize,
duration,
caption: attachment.title,
enclosure: attachment.url + '\n' + filesize + '\n' + mime,
});
setSrc( attachment.url );
};
const onSelectURL = (newSrc) => {
if (newSrc !== src) {
apiFetch({
path: `simple-podcasting/v1/external-url/?url=${newSrc}`,
})
.then((res) => {
if (res.success) {
const { mime, filesize, duration } = res.data;
setAttributes({
src: newSrc,
url: newSrc,
id: null,
mime,
filesize,
duration,
displayDurationValue: duration,
caption: '',
});
}
})
.catch((err) => {
// eslint-disable-next-line no-console
console.error(err);
});
setSrc( newSrc );
}
};
const controls = (
<BlockControls key="controls">
{src ? (
<MediaReplaceFlow
mediaURL={attributes.src}
allowedTypes={ALLOWED_MEDIA_TYPES}
accept="audio/*"
onSelect={onSelectAttachment}
onSelectURL={onSelectURL}
/>
) : null}
</BlockControls>
);
const showId = useSelect( ( __select ) => {
const attachedPodcastIds = __select( 'core/editor' ).getEditedPostAttribute( 'podcasting_podcasts' );
return attachedPodcastIds ? attachedPodcastIds[0] : null;
} );
const show = useSelect( ( __select ) => {
return __select('core').getEntityRecords('taxonomy', 'podcasting_podcasts', {
include: [ showId ],
});
} );
const showName = show && show[0] ? show[0]?.name : null;
const showImage = show && show[0] ? show[0]?.meta?.podcasting_image_url : null;
const onUpdateImage = (image) => {
setFeaturedImage(image.id);
};
return (
<Fragment>
{controls}
<InspectorControls>
<PanelBody
title={__('Podcast Settings', 'simple-podcasting')}
className="simple-podcast-settings"
>
<PanelRow>
<ToggleControl
id="podcast-captioned-form-toggle"
label={__(
'Closed Captioned',
'simple-podcasting'
)}
checked={captioned}
onChange={() => setAttributes({ captioned: !captioned})}
/>
</PanelRow>
<PanelRow>
<ToggleControl
label={__(
'Display Listen Time',
'simple-podcasting'
)}
checked={displayDuration}
onChange={() => setAttributes({ displayDuration: !displayDuration})}
/>
</PanelRow>
<PanelRow>
<ToggleControl
label={__(
'Display Show Title',
'simple-podcasting'
)}
checked={displayShowTitle}
onChange={() => setAttributes({ displayShowTitle: !displayShowTitle})}
/>
</PanelRow>
<PanelRow>
<ToggleControl
label={__(
'Display Episode Title',
'simple-podcasting'
)}
checked={displayEpisodeTitle}
onChange={() => setAttributes({ displayEpisodeTitle: !displayEpisodeTitle})}
/>
</PanelRow>
<PanelRow>
<ToggleControl
label={__(
'Display Show Art',
'simple-podcasting'
)}
checked={displayArt}
onChange={() => setAttributes({ displayArt: !displayArt})}
/>
</PanelRow>
<PanelRow>
<ToggleControl
label={__(
'Display Explicit Badge',
'simple-podcasting'
)}
checked={displayExplicitBadge}
onChange={() => setAttributes({ displayExplicitBadge: !displayExplicitBadge})}
/>
</PanelRow>
<PanelRow>
<ToggleControl
label={__(
'Display Season Number',
'simple-podcasting'
)}
checked={displaySeasonNumber}
onChange={() => setAttributes({ displaySeasonNumber: !displaySeasonNumber})}
/>
</PanelRow>
<PanelRow>
<ToggleControl
label={__(
'Display Episode Number',
'simple-podcasting'
)}
checked={displayEpisodeNumber}
onChange={() => setAttributes({ displayEpisodeNumber: !displayEpisodeNumber})}
help={
! displayEpisodeTitle
&& __( 'The "Display Episode Title" setting should be enabled for this to display the episode number.', 'simple-podcasting' )
}
/>
</PanelRow>
<PanelRow>
<ToggleControl
label={__(
'Display Episode Type',
'simple-podcasting'
)}
checked={displayEpisodeType}
onChange={() => setAttributes({ displayEpisodeType: !displayEpisodeType})}
/>
</PanelRow>
<PanelRow>
<SelectControl
label={__(
'Explicit Content',
'simple-podcasting'
)}
value={explicit}
options={[
{
value: 'no',
label: __('No', 'simple-podcasting'),
},
{
value: 'yes',
label: __('Yes', 'simple-podcasting'),
},
{
value: 'clean',
label: __('Clean', 'simple-podcasting'),
},
]}
onChange={(explicit) =>
setAttributes({ explicit })
}
/>
</PanelRow>
<PanelRow>
<TextControl
label={__(
'Length (MM:SS)',
'simple-podcasting'
)}
value={duration}
onChange={(duration) =>
setAttributes({ duration })
}
/>
</PanelRow>
<PanelRow>
<TextControl
label={__('Season Number', 'simple-podcasting')}
value={seasonNumber}
onChange={(seasonNumber) =>
setAttributes({ seasonNumber })
}
/>
</PanelRow>
<PanelRow>
<TextControl
label={__(
'Episode Number',
'simple-podcasting'
)}
value={episodeNumber}
onChange={(episodeNumber) =>
setAttributes({ episodeNumber })
}
/>
</PanelRow>
<PanelRow>
<RadioControl
label={__('Episode Type', 'simple-podcasting')}
selected={episodeType}
options={[
{
label: __('None', 'simple-podcasting'),
value: 'none',
},
{
label: __('Full', 'simple-podcasting'),
value: 'full',
},
{
label: __(
'Trailer',
'simple-podcasting'
),
value: 'trailer',
},
{
label: __('Bonus', 'simple-podcasting'),
value: 'bonus',
},
]}
onChange={(episodeType) =>
setAttributes({ episodeType })
}
/>
</PanelRow>
<PanelRow>
<Button
variant="secondary"
onClick={() =>
dispatch('core/block-editor').insertBlocks(
createBlock(
'podcasting/podcast-transcript'
)
)
}
>
{__('Add Transcript', 'simple-podcasting')}
</Button>
</PanelRow>
<h3 style={{marginTop: '20px'}}>{__('Cover Image', 'simple-podcasting')}</h3>
<p>{__('The featured image of the current post is used as the episode cover art. Please select a featured image to set it.', 'simple-podcasting')}</p>
<PanelRow className="cover-art-container">
{featuredImageUrl && (
<img src={featuredImageUrl} alt="Cover Image" />
)}
<MediaUploadCheck>
<MediaUpload
onSelect={onUpdateImage}
allowedTypes={['image']}
render={({ open }) => (
<Button isSecondary onClick={open}>
{featuredImageUrl ?
__('Replace Cover Art', 'simple-podcasting')
:
__('Select Cover Art', 'simple-podcasting')
}
</Button>
)}
value={featuredImageId}
/>
</MediaUploadCheck>
{featuredImageUrl && (
<Button isLink isDestructive onClick={removeFeaturedImage}>{__('Delete Cover Art', 'simple-podcasting')}</Button>
)}
</PanelRow>
</PanelBody>
</InspectorControls>
<div className="wp-block-podcasting-podcast-outer">
{src ? (
<>
<div className="wp-block-podcasting-podcast__container">
{displayArt && (featuredImageUrl || showImage) && (
<div className="wp-block-podcasting-podcast__show-art">
<div className="wp-block-podcasting-podcast__image">
<img
src={featuredImageUrl ? featuredImageUrl : showImage}
alt={showName}
/>
</div>
</div>
)}
<div className="wp-block-podcasting-podcast__details">
{displayEpisodeTitle && postTitle && (
<h3 className="wp-block-podcasting-podcast__show-title">
{displayEpisodeNumber && episodeNumber && (
<span>
{episodeNumber}.
</span>
)}
{postTitle}
</h3>
)}
<div className="wp-block-podcasting-podcast__show-details">
{displayShowTitle && (
<span className="wp-block-podcasting-podcast__title">
{showName}
</span>
)}
{displaySeasonNumber && seasonNumber && (
<span className="wp-block-podcasting-podcast__season">
{__(
'Season: ',
'simple-podcasting'
)}
{seasonNumber}
</span>
)}
{displayEpisodeNumber && episodeNumber && (
<span className="wp-block-podcasting-podcast__episode">
{__('Episode: ', 'simple-podcasting')}
{episodeNumber}
</span>
)}
</div>
<div className="wp-block-podcasting-podcast__show-details">
{displayDuration && duration && (
<span className="wp-block-podcasting-podcast__duration">
{__('Listen Time: ', 'simple-podcasting')}
{duration}
</span>
)}
{displayEpisodeType && (episodeType !== 'none') && (
<span className="wp-block-podcasting-podcast__episode-type">
{__(
'Episode type: ',
'simple-podcasting'
)}
{episodeType}
</span>
)}
{displayExplicitBadge && (
<span className="wp-block-podcasting-podcast__explicit-badge">
{__(
'Explicit: ',
'simple-podcasting'
)}
{explicit}
</span>
)}
</div>
</div>
</div>
<figure key="audio" className={className}>
{((caption && caption.length) || !!isSelected) && (
<RichText
tagName="figcaption"
placeholder={__(
'Write caption…',
'simple-podcasting'
)}
className="wp-block-podcasting-podcast__caption"
value={caption}
onChange={(value) =>
setAttributes({ caption: value })
}
isSelected={isSelected}
/>
)}
<audio controls="controls" src={src} />
</figure>
</>
) : (
<MediaPlaceholder
icon="microphone"
labels={{
title: __('Podcast', 'simple-podcasting'),
name: __(
'a podcast episode',
'simple-podcasting'
),
}}
className={className}
onSelect={onSelectAttachment}
onSelectURL={onSelectURL}
accept="audio/*"
allowedTypes={ALLOWED_MEDIA_TYPES}
value={attributes}
/>
)}
</div>
</Fragment>
);
}
function PodcastBlockWithHooks(props) {
const featuredImageProp = useFeaturedImage();
return <Edit {...props} {...featuredImageProp} />;
}
export default PodcastBlockWithHooks;
================================================
FILE: assets/js/onboarding.js
================================================
import '../css/podcasting-onboarding.scss';
( function( $ ) {
$( function() {
const selectImageBtn = $( '#simple-podcasting__upload-cover-image' );
const coverImage = $( 'input[name="podcast-cover-image-id"]' );
const coverImagePreview = $( '#simple-podcasting__cover-image-preview' );
let uploader_frame = null;
/** Upload image button handler */
selectImageBtn.on( 'click', function() {
uploader_frame = wp.media( {
multiple: false,
library: {
type: 'image'
}
} ).on( 'select', function() {
const { id, url } = uploader_frame.state().get( 'selection' ).first().toJSON();
coverImagePreview.html( `<img src="${ url }" />` )
coverImage.val( id );
} );
uploader_frame.open();
} );
} )
} )( jQuery )
================================================
FILE: assets/js/podcasting-edit-post.js
================================================
/*global jQuery */
jQuery( document ).ready( function( $ ) {
$( '#podcasting-enclosure-button' ).click( function( e ) {
e.preventDefault();
var $this = $( this ),
$input = $( 'input#podcasting-enclosure-url' ),
mediaUploader;
// If the uploader object has already been created, reopen the dialog.
if ( mediaUploader ) {
mediaUploader.open();
return;
}
// eslint-disable-next-line camelcase
mediaUploader = wp.media.frames.file_frame = wp.media( {
title: $this.data( 'modalTitle' ),
button: {
text: $this.data( 'modalButton' )
},
library: {
type: 'audio'
},
multiple: false
});
mediaUploader.off( 'select' );
mediaUploader.on( 'select', function() {
var attachment = mediaUploader.state().get('selection').first();
$input.val( attachment.get('url') );
});
mediaUploader.open();
} );
} );
================================================
FILE: assets/js/podcasting-edit-term.js
================================================
/*global jQuery, validateForm*/
import '../css/podcasting-edit-term.css';
jQuery( document ).ready( function( $ ) {
// Clear Image Field.
function clearImageField( el ) {
var $link = $( el ),
$wrapper = $link.parents( '.media-wrapper' ),
$button = $wrapper.find( '.podcasting-media-button' ),
$hidden = $( document.getElementById( $button.data( 'slug' ) ) ),
$existing = $wrapper.find( '.podasting-existing-image' ),
$upload = $wrapper.find( '.podcasting-upload-image' );
// Update the display.
$upload.removeClass('hidden');
$existing.addClass('hidden');
$hidden.val( '' );
}
// When the term add button is clicked, reset the dropdown fields.
$( '#submit' ).click( function() {
var $form = $( 'form#addtag' );
if ( ! validateForm( $form ) ) {
return;
}
// Add a brief delay to allow the form to submit.
setTimeout( function() {
$( '.fm-select select' ).val( 'None' );
clearImageField( '.podcast-media-remove' );
$( '#podcasting_category_1,#podcasting_category_2,#podcasting_category_3' ).val( '' );
window.scrollTo(0,0);
}, 500 );
} );
var mediaUploader;
// Handle media upload buttons.
$( 'input.podcasting-media-button' ).on( 'click', function( e ) {
e.preventDefault();
var $button = $( e.currentTarget ),
$hidden = $( document.getElementById( $button.data( 'slug' ) ) ),
$wrapper = $button.parents( '.media-wrapper' ),
$image = $wrapper.find( 'img' ),
$existing = $wrapper.find( '.podasting-existing-image' ),
$upload = $wrapper.find( '.podcasting-upload-image' );
// If the uploader object has already been created, reopen the dialog.
if (mediaUploader) {
mediaUploader.open();
return;
}
// Extend the wp.media object.
// eslint-disable-next-line camelcase
mediaUploader = wp.media.frames.file_frame = wp.media( {
title: $button.data( 'choose' ),
button: {
text: $button.data( 'update' )
},
multiple: false
});
// When a file is selected, grab the URL and set it as the text field's value.
mediaUploader.off( 'select' );
mediaUploader.on( 'select', function() {
var attachment = mediaUploader.state().get('selection').first();
// Set the hidden field value.
$hidden.val( attachment.get('id') );
// Update the display.
$upload.addClass('hidden');
$existing.removeClass('hidden');
$image.attr( 'src', attachment.get('url') );
});
// Open the uploader dialog
mediaUploader.open();
});
// Handle media remove buttons.
$( '.podcast-media-remove' ).on( 'click', function( e ) {
e.preventDefault();
clearImageField( e.currentTarget );
} );
const iconThemeRadioEl = $( 'input[name="podcasting_icon_theme"]' );
const iconWrappers = $( '.simple_podcasting__platforms-icon' );
iconThemeRadioEl.on( 'change', function() {
const current = $( this );
const selected = current.val();
if ( 'white' === selected ) {
iconWrappers.addClass( 'simple_podcasting__platforms-icon--darken-bg' );
} else {
iconWrappers.removeClass( 'simple_podcasting__platforms-icon--darken-bg' );
}
iconWrappers.each( ( index, icon ) => {
const imgEl = $( icon ).find( 'img' );
const platform = imgEl.data( 'platform' );
imgEl.attr( 'src', `${ podcastingEditPostVars.iconUrl }/${ platform }/${ selected }-100.png` );
});
} );
} );
================================================
FILE: assets/js/transforms.js
================================================
/**
* WordPress dependencies
*/
const { select } = wp.data;
const { createBlock } = wp.blocks;
/**
* Transforms
*/
const transforms = {
from: [
{
type: 'block',
blocks: [ 'core/audio' ],
transform: ( attributes ) => {
return createBlock( 'podcasting/podcast', {
id: attributes.id,
src: attributes.src
} );
},
},
],
to: [
{
type: 'block',
blocks: [ 'core/audio' ],
isMatch: ( { id } ) => {
if ( ! id ) {
return false;
}
const { getMedia } = select( 'core' );
const media = getMedia( id );
return !! media && media.mime_type.includes( 'audio' );
},
transform: ( attributes ) => {
return createBlock( 'core/audio', {
src: attributes.src,
id: attributes.id
} );
},
},
],
};
export default transforms;
================================================
FILE: composer.json
================================================
{
"name": "10up/simple-podcasting",
"description": "A simple podcasting solution for WordPress. ",
"homepage": "https://github.com/10up/simple-podcasting",
"license": "GPL-2.0-or-later",
"authors": [
{
"name": "10up",
"email": "opensource@10up.com",
"homepage": "https://10up.com"
}
],
"support": {
"issues": "https://github.com/10up/simple-podcasting/issues"
},
"require": {
"php": ">=7.3"
},
"require-dev": {
"10up/phpcs-composer": "^3.0",
"10up/wp_mock": "^0.4.2",
"phpunit/phpunit": "^9.5",
"phpcompatibility/php-compatibility": "dev-develop as 9.99.99"
},
"config": {
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": true
}
}
}
================================================
FILE: includes/admin/create-podcast-component.php
================================================
<?php
/**
* Defines class to handle creation of a podcast
* from with the Post editor.
*
* @package tenup_podcasting
*/
namespace tenup_podcasting\admin;
/**
* Adds methods to create a podcast using the Gutenberg inspector component.
*/
class Create_Podcast_Component {
/**
* Constructor method.
*/
public function __construct() {
add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
}
/**
* Admin enqueue scripts.
*/
public function admin_enqueue_scripts() {
$screen = get_current_screen();
if ( ! ( $screen && 'post' === $screen->id ) ) {
return;
}
wp_enqueue_script(
'podcasting_create_podcast_show_plugin',
PODCASTING_URL . 'dist/create-podcast-show.js',
array(),
PODCASTING_VERSION,
true
);
wp_localize_script(
'podcasting_create_podcast_show_plugin',
'podcastingShowPluginVars',
array(
'categories' => \tenup_podcasting\get_podcasting_categories_options(),
)
);
}
}
new Create_Podcast_Component();
================================================
FILE: includes/admin/onboarding.php
================================================
<?php
/**
* Registers and renders the onboarding setup wizard.
*
* @package tenup_podcasting
*/
namespace tenup_podcasting\admin;
/**
* Adds methods required for handling the onboarding wizard.
*/
class Onboarding {
/**
* Indicates onboarding is in progress.
*
* @var string
*/
const STATUS_IN_PROGRESS = 'in-progress';
/**
* Indicates onboarding is complete.
*
* @var string
*/
const STATUS_COMPLETED = 'completed';
/**
* Holds the object for Create_Podcast.
*
* @var \tenup_podcasting\Create_Podcast
*/
protected $create_podcast;
/**
* Constructor
*/
public function __construct() {
$this->create_podcast = new \tenup_podcasting\Create_Podcast();
add_action( 'admin_menu', array( $this, 'register_onoarding_page' ) );
add_action( 'admin_init', array( $this, 'onboarding_action_handler' ) );
}
/**
* Registers a hidden sub menu page for the onboarding wizard.
*/
public function register_onoarding_page() {
add_submenu_page(
'admin.php',
esc_html__( 'Simple Podcasting Onboarding' ),
'',
'manage_options',
'simple-podcasting-onboarding',
array( $this, 'render_page_contents' )
);
if ( 'no' === get_option( 'simple_podcasting_onboarding', '' ) ) {
update_option( 'simple_podcasting_onboarding', self::STATUS_IN_PROGRESS );
wp_safe_redirect( admin_url( 'admin.php?page=simple-podcasting-onboarding&step=1' ) );
die();
}
}
/**
* Renders the page content for the onboarding wizard.
*/
public function render_page_contents() {
$step = filter_input( INPUT_GET, 'step', FILTER_VALIDATE_INT );
if ( ! $step ) {
$step = 1;
}
require_once 'views/onboarding-header.php';
switch ( $step ) {
case 1:
require_once 'views/onboarding-page-one.php';
break;
case 2:
require_once 'views/onboarding-page-two.php';
break;
default:
break;
}
}
/**
* Onboarding data saving handler.
*/
public function onboarding_action_handler() {
if ( ! $this->create_podcast->verify_nonce() ) {
return;
}
$this->create_podcast->sanitize_podcast_fields();
$is_sanitized = $this->create_podcast->save_podcast_fields();
if ( is_wp_error( $is_sanitized ) ) {
$error_message = $is_sanitized->get_error_message();
add_action(
'admin_notices',
function () use ( $error_message ) {
if ( empty( $error_message ) ) {
return;
}
?>
<div class="notice notice-error is-dismissible">
<p><?php echo wp_kses_post( $error_message ); ?></p>
</div>
<?php
}
);
return;
}
if ( self::STATUS_IN_PROGRESS === get_option( 'simple_podcasting_onboarding', '' ) ) {
wp_safe_redirect( admin_url( 'admin.php?page=simple-podcasting-onboarding&step=2' ) );
die;
}
}
}
$onbarding_status = get_option( 'simple_podcasting_onboarding', '' );
if ( Onboarding::STATUS_COMPLETED !== $onbarding_status ) {
new Onboarding();
}
================================================
FILE: includes/admin/views/onboarding-header.php
================================================
<?php
/**
* Header template for onboarding: Step - 1.
*
* @package tenup_podcasting
*/
?>
<div id="simple-podcasting__onboarding-header">
<div id="simple-podcasting__branding">
<div id="simple-podcasting__logo">
<img src="https://ps.w.org/simple-podcasting/assets/icon-128x128.png" />
</div>
<div id="simple-podcasting__plugin-name">
<?php esc_html_e( 'Simple Podcasting', 'simple-podcasting' ); ?>
</div>
</div>
<div id="simple-podcasting__header-title">
<?php esc_html_e( 'Get Started With Podcasting', 'simple-podcasting' ); ?>
</div>
<div id="simple-podcasting__header-controls">
<a href="<?php echo esc_url( admin_url( 'plugins.php' ) ); ?>" class="simple-podcasting__btn simple-podcasting__btn--ghost" id="simple-podcasting-btn__skip-onboarding"><?php esc_html_e( 'Skip Setup', 'simple-podcasting' ); ?></a>
</div>
</div>
================================================
FILE: includes/admin/views/onboarding-page-one.php
================================================
<?php
/**
* Body template for onboarding: Step - 1.
*
* @package tenup_podcasting
*/
?>
<form method="POST" action="">
<div class="simple-podcasting__onboarding-body simple-podcasting__onboarding-body--step-1">
<div id="simple-podcasting__page-title">
<?php esc_html_e( 'Create your first podcast show', 'simple-podcasting' ); ?>
</div>
<!-- Podcast name -->
<div class="simple-podcasting__setting">
<label class="simple-podcasting__setting-label" for="simple-podcasting-podcast-name"><?php esc_html_e( 'Podcast name*', 'simple-podcasting' ); ?></label>
<div class="simple-podcasting__setting-input"><input name="podcast-name" id="simple-podcasting-podcast-name" aria-describedby="simple-podcasting__podcast-name-description" required type="text" /></div>
<div class="simple-podcasting__setting-description" id="simple-podcasting__podcast-name-description"><?php esc_html_e( 'What’s the title of your podcast show that listeners will see?', 'simple-podcasting' ); ?></div>
</div>
<!-- Podcast artist/author -->
<div class="simple-podcasting__setting">
<label class="simple-podcasting__setting-label" for="simple-podcasting-podcast-artist"><?php esc_html_e( 'Podcast Artist / Author name*', 'simple-podcasting' ); ?></label>
<div class="simple-podcasting__setting-input"><input name="podcast-artist" id="simple-podcasting-podcast-artist" aria-describedby="simple-podcasting__podcast-artist-description" required type="text" /></div>
<div class="simple-podcasting__setting-description" id="simple-podcasting__podcast-artist-description"><?php esc_html_e( 'Who’s the artist or author of your podcast show that listeners will see?', 'simple-podcasting' ); ?></div>
</div>
<!-- Podcast description -->
<div class="simple-podcasting__setting">
<label class="simple-podcasting__setting-label" for="simple-podcasting-podcast-description"><?php esc_html_e( 'Podcast summary*', 'simple-podcasting' ); ?></label>
<div class="simple-podcasting__setting-input"><textarea name="podcast-description" id="simple-podcasting-podcast-description" aria-describedby="simple-podcasting__podcast-description-description" rows="5" required></textarea></div>
<div class="simple-podcasting__setting-description" id="simple-podcasting__podcast-description-description"><?php esc_html_e( 'Briefly describe to your listeners what your podcast is about. (No HTML please)', 'simple-podcasting' ); ?></div>
</div>
<!-- Cover image -->
<div class="simple-podcasting__setting">
<input type="hidden" name="podcast-cover-image-id" id="simple-podcasting-podcast-cover-image-id" aria-describedby="simple-podcasting__cover-image-description" value="" required>
<label class="simple-podcasting__setting-label" for="simple-podcasting-podcast-cover-image-id"><?php esc_html_e( 'Cover image*', 'simple-podcasting' ); ?></label>
<div id="simple-podcasting__cover-image-preview"></div>
<button type="button" class="simple-podcasting__btn simple-podcasting__btn--ghost" id="simple-podcasting__upload-cover-image"><?php esc_html_e( 'Select image', 'simple-podcasting' ); ?></button>
<div class="simple-podcasting__setting-description" id="simple-podcasting__cover-image-description"><?php esc_html_e( 'Minimum size: 1400px x 1400 px — maximum size: 2048px x 2048px. Make sure the image is square so it will properly display within podcatcher apps.', 'simple-podcasting' ); ?></div>
</div>
<!-- Podcast category -->
<div class="simple-podcasting__setting">
<label class="simple-podcasting__setting-label" for="simple-podcasting-podcast-category"><?php esc_html_e( 'Podcast category*', 'simple-podcasting' ); ?></label>
<select name="podcast-category" id="simple-podcasting-podcast-category" aria-describedby="simple-podcasting__podcast-category-description" required>
<?php foreach ( \tenup_podcasting\get_podcasting_categories_options() as $option_value => $option_label ) : ?>
<option value="<?php echo esc_attr( $option_value ); ?>"><?php echo esc_html( $option_label ); ?></option>
<?php endforeach; ?>
</select>
<div class="simple-podcasting__setting-description" id="simple-podcasting__podcast-category-description"><?php esc_html_e( 'What’s the category the listeners will use to discover your podcast under when browsing podcatchers?', 'simple-podcasting' ); ?></div>
</div>
<!-- Create button -->
<button class="simple-podcasting__btn simple-podcasting__btn--black" id="simple-podcasting__create-podcast-button"><?php esc_html_e( 'Create', 'simple-podcasting' ); ?></button>
<?php wp_nonce_field( 'simple-podcasting-create-show-action', 'simple-podcasting-create-show-nonce-field' ); ?>
</div>
</form>
================================================
FILE: includes/admin/views/onboarding-page-two.php
================================================
<?php
/**
* Body template for onboarding: Step - 2.
*
* @package tenup_podcasting
*/
use tenup_podcasting\admin\Onboarding;
?>
<div class="simple-podcasting__onboarding-body simple-podcasting__onboarding-body--step-2">
<div class="simple-podcasting__panel simple-podcasting__panel--left">
<div id="simple-podcasting__page-title">
<?php esc_html_e( 'Well done!', 'simple-podcasting' ); ?>
</div>
<p>
<?php
printf(
/* translators: %s podcast term edit page. */
__( 'You can always edit show details <a href="%s">here</a>.', 'simple-podcasting' ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
esc_url( admin_url( 'edit-tags.php?taxonomy=podcasting_podcasts&podcasts=true' ) )
);
?>
</p>
<p><?php esc_html_e( 'Now let’s create your show’s first episode:', 'simple-podcasting' ); ?></p>
<ol>
<li><?php esc_html_e( 'Create a new post', 'simple-podcasting' ); ?></li>
<li><?php esc_html_e( 'Assign the post to a show', 'simple-podcasting' ); ?></li>
<li><?php esc_html_e( 'Insert a podcast block with an audio file into the content', 'simple-podcasting' ); ?></li>
</ol>
<p><?php esc_html_e( 'You can then submit the feed URL to podcatchers. The feed will automatically update each time you add a new episode.', 'simple-podcasting' ); ?></p>
<p><?php esc_html_e( "Let's get started!", 'simple-podcasting' ); ?></p>
<div class="simple-podcasting__step-2-controls">
<a href="<?php echo esc_url( admin_url( 'post-new.php' ) ); ?>" class="simple-podcasting__btn simple-podcasting__btn--black" id="simple-podcasting__create-a-new-post-button"><?php esc_html_e( 'Create a new Post', 'simple-podcasting' ); ?></a>
<a href="<?php echo esc_url( admin_url( 'edit-tags.php?taxonomy=podcasting_podcasts&podcasts=true' ) ); ?>"><?php esc_html_e( 'Create another Show', 'simple-podcasting' ); ?></a>
</div>
</div>
<div class="simple-podcasting__panel simple-podcasting__panel--right">
<div class="simple-podcasting__podcast-block-preview">
<img
srcset="<?php echo esc_url( PODCASTING_URL . 'dist/assets/images/podcast-block-preview.png' ); ?>, <?php echo esc_url( PODCASTING_URL . 'dist/assets/images/podcast-block-preview@2x.png' ); ?> 2x"
src="<?php echo esc_url( PODCASTING_URL . 'dist/assets/images/podcast-block-preview.png' ); ?>"
/>
</div>
</div>
<?php update_option( 'simple_podcasting_onboarding', Onboarding::STATUS_COMPLETED ); ?>
</div>
================================================
FILE: includes/block-patterns.php
================================================
<?php
/**
* Register and enqueue all things block patterns.
*
* @package tenup_podcasting
*/
namespace tenup_podcasting\block_patterns;
/**
* Register block and its assets.
*/
function init() {
$podcast_terms = get_terms(
[
'taxonomy' => PODCASTING_TAXONOMY_NAME,
'fields' => 'ids',
]
);
if ( empty( $podcast_terms ) ) {
return;
}
register_block_pattern(
'podcasting/podcast-grid',
array(
'title' => __( 'Podcast Grid', 'simple-podcasting' ),
'description' => _x( 'Podcast Grid', 'This block pattern is used to display podcast in a grid structure.', 'simple-podcasting' ),
'categories' => [ 'query' ],
'content' => '<!-- wp:query {"query":{"perPage":3,"taxQuery":{"podcasting_podcasts":[' . implode( ',', $podcast_terms ) . ']},"pages":0,"offset":0,"postType":"post","order":"desc","orderBy":"date","author":"","search":"","exclude":[],"sticky":"exclude","inherit":false},"displayLayout":{"type":"flex","columns":3}} -->
<div class="wp-block-query">
<!-- wp:post-template -->
<!-- wp:cover {"useFeaturedImage":true,"dimRatio":50,"className":"alignfull"} -->
<div class="wp-block-cover alignfull">
<span aria-hidden="true" class="wp-block-cover__background has-background-dim"></span>
<div class="wp-block-cover__inner-container">
<!-- wp:post-title {"isLink":true,"style":{"typography":{"fontSize":"1.6rem"},"elements":{"link":{"color":{"text":"var:preset|color|white"}}}},"textColor":"white"} /-->
<!-- wp:post-terms {"term":"' . esc_js( PODCASTING_TAXONOMY_NAME ) . '","style":{"typography":{"fontSize":"2rem"},"elements":{"link":{"color":{"text":"var:preset|color|white"}}}},"textColor":"white"} /-->
<!-- wp:post-date {"displayType":"modified","style":{"typography":{"fontSize":"0.8rem"},"spacing":{"margin":{"top":"0px","right":"0px","bottom":"0px","left":"0px"}},"elements":{"link":{"color":{"text":"var:preset|color|white"}}}},"textColor":"white"} /-->
</div>
</div>
<!-- /wp:cover -->
<!-- /wp:post-template -->
</div>
<!-- /wp:query -->',
)
);
}
add_action( 'init', __NAMESPACE__ . '\init' );
================================================
FILE: includes/blocks/podcast/markup.php
================================================
<?php
/**
* Podcast markup
*
* @package tenup_podcasting
*
* @var array $attributes Block attributes.
* @var string $content Block content.
* @var WP_Block $block Block instance.
* @var array $context Block context.
*/
$attributes = wp_parse_args(
$attributes ?? [],
[
'id' => null,
'caption' => '',
'displayDuration' => false,
'displayShowTitle' => false,
'displayEpisodeTitle' => false,
'displayArt' => false,
'displayExplicitBadge' => false,
'displaySeasonNumber' => false,
'displayEpisodeNumber' => false,
'displayEpisodeType' => false,
]
);
if ( ! $attributes['id'] ) {
return;
}
$post_id = get_the_id();
$podcast_shows = get_the_terms( $post_id, 'podcasting_podcasts' );
$podcast_show = $podcast_shows ? $podcast_shows[0] : '';
$show_name = $podcast_show ? $podcast_show->name : '';
$src = get_post_meta( $post_id, 'src', true );
$duration = get_post_meta( $post_id, 'podcast_duration', true );
$explicit = get_post_meta( $post_id, 'podcast_explicit', true );
$episode_type = get_post_meta( $post_id, 'podcast_episode_type', true );
$episode_number = get_post_meta( $post_id, 'podcast_episode_number', true );
$season_number = get_post_meta( $post_id, 'podcast_season_number', true );
if ( is_a( $podcast_show, 'WP_Term' ) ) {
$term_image_id = get_term_meta( $podcast_show->term_id, 'podcasting_image', true );
} else {
$term_image_id = '';
}
?>
<div class="wp-block-podcasting-podcast-outer">
<div class="wp-block-podcasting-podcast__container">
<?php if ( $attributes['displayArt'] && ( has_post_thumbnail() || ! empty( $term_image_id ) ) ) : ?>
<div class="wp-block-podcasting-podcast__show-art">
<div class="wp-block-podcasting-podcast__image">
<?php
if ( has_post_thumbnail() ) {
the_post_thumbnail( 'medium' );
} elseif ( $podcast_show instanceof \WP_Term ) {
echo wp_get_attachment_image( $term_image_id, 'medium' );
}
?>
</div>
</div>
<?php endif; ?>
<div class="wp-block-podcasting-podcast__details">
<?php if ( $attributes['displayEpisodeTitle'] ) : ?>
<h3 class="wp-block-podcasting-podcast__show-title">
<?php if ( $attributes['displayEpisodeNumber'] && ! empty( $episode_number ) ) : ?>
<span>
<?php echo esc_html( $episode_number ); ?>.
</span>
<?php endif; ?>
<?php the_title(); ?>
</h3>
<?php endif; ?>
<div class="wp-block-podcasting-podcast__show-details">
<?php if ( $attributes['displayShowTitle'] && ! empty( $show_name ) ) : ?>
<span class="wp-block-podcasting-podcast__title">
<?php echo esc_html( $show_name ); ?>
</span>
<?php endif; ?>
<?php if ( $attributes['displaySeasonNumber'] && ! empty( $season_number ) ) : ?>
<span class="wp-block-podcasting-podcast__season">
<?php esc_html_e( 'Season: ', 'simple-podcasting' ); ?>
<?php echo esc_html( $season_number ); ?>
</span>
<?php endif; ?>
<?php if ( $attributes['displayEpisodeNumber'] && ! empty( $episode_number ) ) : ?>
<span class="wp-block-podcasting-podcast__episode">
<?php esc_html_e( 'Episode: ', 'simple-podcasting' ); ?>
<?php echo esc_html( $episode_number ); ?>
</span>
<?php endif; ?>
</div>
<div class="wp-block-podcasting-podcast__show-details">
<?php if ( $attributes['displayDuration'] && ! empty( $duration ) ) : ?>
<span class="wp-block-podcasting-podcast__duration">
<?php esc_html_e( 'Listen Time: ', 'simple-podcasting' ); ?>
<?php echo esc_html( $duration ); ?>
</span>
<?php endif; ?>
<?php if ( $attributes['displayEpisodeType'] && ! empty( $episode_type ) && 'none' !== $episode_type ) : ?>
<span class="wp-block-podcasting-podcast__episode-type">
<?php esc_html_e( 'Episode type: ', 'simple-podcasting' ); ?>
<?php echo esc_html( $episode_type ); ?>
</span>
<?php endif; ?>
<?php if ( $attributes['displayExplicitBadge'] && ! empty( $explicit ) ) : ?>
<span class="wp-block-podcasting-podcast__explicit-badge">
<?php esc_html_e( 'Explicit: ', 'simple-podcasting' ); ?>
<?php echo esc_html( $explicit ); ?>
</span>
<?php endif; ?>
</div>
</div>
</div>
<?php echo wp_kses_post( $content ); ?>
</div>
================================================
FILE: includes/blocks/podcast-transcript/cite.js
================================================
import { registerBlockType, createBlock } from '@wordpress/blocks';
import { useBlockProps, RichText } from '@wordpress/block-editor';
const Edit = ({
attributes,
attributes: { text },
setAttributes,
clientId,
onReplace,
}) => {
const blockProps = useBlockProps();
return (
<RichText
tagName="cite"
value={text}
onChange={(content) => setAttributes({ text: content })}
allowedFormats={[]}
withoutInteractiveFormatting
onSplit={(value, isOriginal) => {
let block;
if (isOriginal || value) {
block = createBlock('podcasting/podcast-transcript-cite', {
...attributes,
content: value,
});
} else {
block = createBlock('core/paragraph');
}
if (isOriginal) {
block.clientId = clientId;
}
return block;
}}
onReplace={onReplace}
{...blockProps}
/>
);
};
registerBlockType('podcasting/podcast-transcript-cite', {
edit: Edit,
save: ({ attributes: { text } }) => <cite>{text}</cite>,
});
================================================
FILE: includes/blocks/podcast-transcript/edit.js
================================================
import { useBlockProps, RichText, InnerBlocks } from '@wordpress/block-editor';
import { __ } from '@wordpress/i18n';
import {
RadioControl,
Card,
CardBody,
Placeholder,
} from '@wordpress/components';
import { useSelect, withSelect } from '@wordpress/data';
import { useEffect } from '@wordpress/element';
import { useEntityProp } from '@wordpress/core-data';
import { serialize } from '@wordpress/blocks';
const Edit = withSelect((select, { clientId }) => {
return {
innerBlocks: select('core/block-editor').getBlocksByClientId(clientId),
};
})(({ attributes, setAttributes, isSelected, innerBlocks, clientId }) => {
const blockProps = useBlockProps({});
const postType = useSelect(
(select) => select('core/editor').getCurrentPostType(),
[]
);
const [meta, setMeta] = useEntityProp('postType', postType, 'meta');
useEffect(() => {
if (innerBlocks.length) {
setMeta({
...meta,
podcast_transcript: serialize(innerBlocks[0].innerBlocks),
});
}
}, [innerBlocks]);
const isInnerBlockSelected = useSelect((select) =>
select('core/block-editor').hasSelectedInnerBlock(clientId)
);
console.log(isInnerBlockSelected);
const { display, linkText } = attributes;
return (
<section {...blockProps}>
{(isSelected || isInnerBlockSelected) && (
<>
<Card>
<CardBody>
<RadioControl
label={__(
'Transcript Display',
'simple-podcasting'
)}
selected={display}
options={[
{
label: __(
'Display Transcript on Post',
'simple-podcasting'
),
value: 'post',
},
{
label: __(
'Display Link to Transcript',
'simple-podcasting'
),
value: 'link',
},
{
label: __(
'Do not display - only show link in RSS feed',
'simple-podcasting'
),
value: 'none',
},
]}
onChange={(value) =>
setAttributes({ display: value })
}
/>
</CardBody>
</Card>
<br />
</>
)}
{display === 'none' && !isSelected && (
<Placeholder
icon="microphone"
label={__('Podcast Transcript', 'simple-podcasting')}
/>
)}
{display === 'link' && (
<RichText
tagName="a"
value={linkText}
onChange={(content) => setAttributes({ linkText: content })}
placeholder={__('Transcript Link', 'simple-podcasting')}
allowedFormats={[]}
/>
)}
{(isSelected || isInnerBlockSelected || display === 'post') && (
<>
<section>
<InnerBlocks
allowedBlocks={[
'core/paragraph',
'podcasting/podcast-transcript-cite',
'podcasting/podcast-transcript-time',
]}
/>
</section>
</>
)}
</section>
);
});
export default Edit;
================================================
FILE: includes/blocks/podcast-transcript/formats.js
================================================
import { registerFormatType, toggleFormat } from '@wordpress/rich-text';
import { BlockControls } from '@wordpress/block-editor';
import { ToolbarGroup, ToolbarButton } from '@wordpress/components';
import { useSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
const Cite = ({ isActive, onChange, value }) => {
const selectedBlock = useSelect((select) => {
return select('core/block-editor').getSelectedBlock();
}, []);
if (
selectedBlock &&
selectedBlock.name !== 'podcasting/podcast-transcript'
) {
return null;
}
return (
<BlockControls>
<ToolbarGroup>
<ToolbarButton
icon="admin-users"
title={__('Speaker Citation', 'simple-podcasting')}
onClick={() => {
onChange(
toggleFormat(value, {
type: 'podcasting/transcript-cite',
})
);
}}
isActive={isActive}
/>
</ToolbarGroup>
</BlockControls>
);
};
registerFormatType('podcasting/transcript-cite', {
title: __('Cite', 'simple-podcasting'),
tagName: 'cite',
className: null,
edit: Cite,
});
const Time = ({ isActive, onChange, value }) => {
const selectedBlock = useSelect((select) => {
return select('core/block-editor').getSelectedBlock();
}, []);
if (
selectedBlock &&
selectedBlock.name !== 'podcasting/podcast-transcript'
) {
return null;
}
return (
<BlockControls>
<ToolbarGroup>
<ToolbarButton
icon="clock"
title={__('Timestamp', 'simple-podcasting')}
onClick={() => {
onChange(
toggleFormat(value, {
type: 'podcasting/transcript-time',
})
);
}}
isActive={isActive}
/>
</ToolbarGroup>
</BlockControls>
);
};
registerFormatType('podcasting/transcript-time', {
title: __('Time', 'simple-podcasting'),
tagName: 'time',
className: null,
edit: Time,
});
================================================
FILE: includes/blocks/podcast-transcript/index.js
================================================
import { registerBlockType } from '@wordpress/blocks';
import { InnerBlocks } from '@wordpress/block-editor';
import './styles.css';
import Edit from './edit';
import './cite';
import './time';
registerBlockType('podcasting/podcast-transcript', {
edit: Edit,
save: () => <InnerBlocks.Content />,
});
================================================
FILE: includes/blocks/podcast-transcript/markup.php
================================================
<?php
/**
* Podcast Transcript markup
*
* @package tenup_podcasting
*
* @var array $attributes Block attributes.
* @var string $content Block content.
* @var WP_Block $block Block instance.
* @var array $context Block context.
*/
use function tenup_podcasting\transcripts\get_transcript_link_from_post;
if ( 'none' !== $attributes['display'] ) : ?>
<div <?php echo get_block_wrapper_attributes(); // phpcs:ignore ?>>
<?php
switch ( $attributes['display'] ) {
case 'post':
echo wp_kses_post(
do_blocks(
get_post_meta( get_the_ID(), 'podcast_transcript', true )
)
);
break;
case 'link':
printf(
'<p><a href="%s">%s</a></p>',
esc_url( get_transcript_link_from_post( get_post() ) ),
esc_html( $attributes['linkText'] )
);
break;
}
?>
</div>
<?php endif; ?>
================================================
FILE: includes/blocks/podcast-transcript/styles.css
================================================
.wp-block-podcasting-podcast-transcript cite,
.wp-block-podcasting-podcast-transcript time {
display: block;
}
================================================
FILE: includes/blocks/podcast-transcript/time.js
================================================
import { registerBlockType, createBlock } from '@wordpress/blocks';
import { useBlockProps, RichText } from '@wordpress/block-editor';
const Edit = ({
attributes,
attributes: { text },
setAttributes,
clientId,
onReplace,
}) => {
const blockProps = useBlockProps();
return (
<RichText
tagName="time"
value={text}
onChange={(content) => setAttributes({ text: content })}
allowedFormats={[]}
withoutInteractiveFormatting
onSplit={(value, isOriginal) => {
let block;
if (isOriginal || value) {
block = createBlock('podcasting/podcast-transcript-time', {
...attributes,
content: value,
});
} else {
block = createBlock('core/paragraph');
}
if (isOriginal) {
block.clientId = clientId;
}
return block;
}}
onReplace={onReplace}
{...blockProps}
/>
);
};
registerBlockType('podcasting/podcast-transcript-time', {
edit: Edit,
save: ({ attributes: { text } }) => <time>{text}</time>,
});
================================================
FILE: includes/blocks.php
================================================
<?php
/**
* Register and enqueue all things block-related.
*
* @package tenup_podcasting
*/
namespace tenup_podcasting\block;
/**
* Register block and its assets.
*/
function init() {
$block_asset = require PODCASTING_PATH . 'dist/blocks.asset.php';
wp_register_script(
'podcasting-block-editor',
PODCASTING_URL . 'dist/blocks.js',
$block_asset['dependencies'],
$block_asset['version'],
true
);
wp_register_style(
'podcasting-block-editor',
PODCASTING_URL . 'dist/blocks.css',
array(),
$block_asset['version'],
'all'
);
register_block_type(
'podcasting/podcast',
array(
'editor_script' => 'podcasting-block-editor',
'editor_style' => 'podcasting-block-editor',
'render_callback' => __NAMESPACE__ . '\render',
)
);
}
add_action( 'init', __NAMESPACE__ . '\init' );
/**
* Render the block.
*
* @param array $attributes Block attributes.
* @param string $content Block content.
* @param WP_Block $block Block instance.
* @return string HTML output
*/
function render( $attributes, $content, $block ) {
ob_start();
include PODCASTING_PATH . 'includes/blocks/podcast/markup.php';
return ob_get_clean();
}
/**
* Register block and its assets.
*/
function init_transcript() {
$podcast_transcript_block_asset = require PODCASTING_PATH . 'dist/podcasting-transcript.asset.php';
wp_register_script(
'podcasting-transcript',
PODCASTING_URL . 'dist/podcasting-transcript.js',
$podcast_transcript_block_asset['dependencies'],
$podcast_transcript_block_asset['version'],
true
);
wp_register_style(
'podcasting-transcript',
PODCASTING_URL . 'dist/podcasting-transcript.css',
array(),
$podcast_transcript_block_asset['version'],
'all'
);
$transcript_block_args = array(
'editor_script' => 'podcasting-transcript',
'style_handles' => array( 'podcasting-transcript' ),
'title' => __( 'Podcast Transcript', 'simple-podcasting' ),
'description' => '',
'textdomain' => 'simple-podcasting',
'name' => 'podcasting/podcast-transcript',
'icon' => 'format-quote',
'api_version' => 2,
'category' => 'common',
'attributes' => array(
'transcript' => array(
'type' => 'string',
),
'display' => array(
'type' => 'string',
'default' => 'post',
),
'linkText' => array(
'type' => 'string',
'default' => __( 'Transcript Link', 'simple-podcastin' ),
),
),
'example' => array(),
'supports' => array(
'multiple' => false,
'inserter' => false,
),
);
$transcript_block_args['render_callback'] = function ( $attributes, $content, $block ) {
ob_start();
include PODCASTING_PATH . 'includes/blocks/podcast-transcript/markup.php';
return ob_get_clean();
};
register_block_type(
'podcasting/podcast-transcript',
$transcript_block_args
);
/**
* Simple cite block.
*/
register_block_type(
'podcasting/podcast-transcript-cite',
array(
'editor_script' => 'podcasting-transcript',
'title' => __( 'Cite', 'simple-podcasting' ),
'description' => '',
'textdomain' => 'simple-podcasting',
'name' => 'podcasting/podcast-transcript-cite',
'icon' => 'admin-users',
'api_version' => 2,
'category' => 'text',
'attributes' => array(
'text' => array(
'type' => 'string',
),
),
'supports' => array(
'html' => false,
'reusable' => false,
),
'parent' => [ 'podcasting/podcast-transcript' ],
)
);
/**
* Simple time block.
*/
register_block_type(
'podcasting/podcast-transcript-time',
array(
'editor_script' => 'podcasting-transcript',
'title' => __( 'Time', 'simple-podcasting' ),
'description' => '',
'textdomain' => 'simple-podcasting',
'name' => 'podcasting/podcast-transcript-time',
'icon' => 'clock',
'api_version' => 2,
'category' => 'text',
'attributes' => array(
'text' => array(
'type' => 'string',
),
),
'supports' => array(
'html' => false,
'reusable' => false,
),
'parent' => [ 'podcasting/podcast-transcript' ],
)
);
}
add_action( 'init', __NAMESPACE__ . '\init_transcript' );
/**
* Registers block for Podcast Platforms.
*/
function register_podcast_platforms_block() {
if ( ! file_exists( PODCASTING_PATH . 'dist/podcast-platforms-block.asset.php' ) ) {
return;
}
$block_asset = require PODCASTING_PATH . 'dist/podcast-platforms-block.asset.php';
wp_register_script(
'podcast-platforms-block-editor',
PODCASTING_URL . 'dist/podcast-platforms-block.js',
$block_asset['dependencies'],
$block_asset['version'],
true
);
wp_localize_script(
'podcast-platforms-block-editor',
'podcastingPlatformVars',
array(
'podcastingUrl' => PODCASTING_URL,
)
);
wp_register_style(
'podcast-platforms-block-editor',
PODCASTING_URL . 'dist/podcast-platforms-block.css',
array(),
$block_asset['version'],
'all'
);
register_block_type(
'podcasting/podcast-platforms',
array(
'editor_script' => 'podcast-platforms-block-editor',
'editor_style' => 'podcast-platforms-block-editor',
'style' => 'podcast-platforms-block-editor',
'render_callback' => __NAMESPACE__ . '\render_podcasting_platforms',
)
);
}
add_action( 'init', __NAMESPACE__ . '\register_podcast_platforms_block' );
/**
* Renders the block `podcasting/podcast-platforms`.
*
* @param array $attrs Block attributes.
* @return string
*/
function render_podcasting_platforms( $attrs ) {
if ( ! ( is_array( $attrs ) && isset( $attrs['showId'] ) ) ) {
return '';
}
$show_id = isset( $attrs['showId'] ) ? $attrs['showId'] : 0;
$icon_size = isset( $attrs['iconSize'] ) ? $attrs['iconSize'] : 48;
$align = isset( $attrs['align'] ) ? $attrs['align'] : 'center';
if ( 0 === $show_id ) {
return '';
}
$supported_platforms = \tenup_podcasting\get_supported_platforms();
$platforms = get_term_meta( $show_id, 'podcasting_platforms', true );
$theme = get_term_meta( $show_id, 'podcasting_icon_theme', true );
$theme = empty( $theme ) ? 'color' : $theme;
if ( ! is_array( $platforms ) || empty( $platforms ) ) {
return '';
}
ob_start();
?>
<div class="simple-podcasting__podcast-platforms">
<div class='simple-podcasting__podcasting-platform-list <?php echo esc_attr( 'simple-podcasting__podcasting-platform-list--' . $align ); ?>'>
<?php foreach ( $platforms as $slug => $url ) : ?>
<?php
if ( empty( $url ) ) {
continue;
}
$podcast_title = $supported_platforms[ $slug ]['title'];
?>
<span class='simple-podcasting__podcasting-platform-list-item'>
<a href="<?php echo esc_url( $url ); ?>" target="_blank" title="<?php echo esc_attr( $podcast_title ); ?>" aria-label="<?php echo esc_attr( $podcast_title ); ?>">
<img
class="simple-pocasting__icon-size--<?php echo esc_attr( $icon_size ); ?>"
src="<?php printf( '%sdist/images/icons/%s/%s-100.png', esc_url( PODCASTING_URL ), esc_attr( $slug ), esc_attr( $theme ) ); ?>"
/>
</a>
</span>
<?php endforeach; ?>
</div>
</div>
<?php
return ob_get_clean();
}
/**
* Register JS block-specific strings.
*
* These need to be available in PHP for .pot creation but don't need to do anything.
*
* @return void
*/
function register_js_strings() {
__( 'Insert a podcast episode into a post. To add it to a podcast feed, select a podcast in document settings.', 'simple-podcasting' );
__( 'Podcast Settings', 'simple-podcasting' );
__( 'Length (MM:SS)', 'simple-podcasting' );
__( 'a podcast episode', 'simple-podcasting' );
__( 'Season Number', 'simple-podcasting' );
__( 'Episode Number', 'simple-podcasting' );
__( 'Episode Type', 'simple-podcasting' );
}
add_action( 'init', __NAMESPACE__ . '\register_js_strings' );
/**
* Register and load block editor translations.
*
* In an ideal world, this would only load the translations absolutely necessary in a JS context.
* Since this is a small plugin it's still okay for now.
*
* @return void
*/
function load_translations() {
if ( function_exists( 'wp_set_script_translations' ) ) {
wp_set_script_translations( 'podcasting-block-editor', 'simple-podcasting' );
} elseif ( function_exists( 'gutenberg_get_jed_locale_data' ) ) {
$data = wp_json_encode( gutenberg_get_jed_locale_data( 'simple-podcasting' ) );
wp_add_inline_script(
'wp-i18n',
'wp.i18n.setLocaleData( ' . $data . ', "simple-podcasting" );'
);
}
}
add_action( 'enqueue_block_editor_assets', __NAMESPACE__ . '\load_translations' );
/**
* Delete left over post meta after deleting podcast block.
*
* @param WP_Post $post Inserted or updated post object.
* @param WP_REST_Request $request Request object.
* @param bool $creating True when creating a post, false when updating.
* @return void
*/
function block_editor_meta_cleanup( $post, $request, $creating ) {
if ( $creating ) {
return;
}
if ( has_block( 'podcasting/podcast', $post->ID ) ) {
return;
}
\tenup_podcasting\helpers\delete_all_podcast_meta( $post->ID );
}
add_action( 'rest_after_insert_post', __NAMESPACE__ . '\block_editor_meta_cleanup', 10, 3 );
/**
* Returns podcast platforms meta.
*/
function ajax_get_podcast_platforms() {
$term_id = filter_input( INPUT_GET, 'show_id', FILTER_VALIDATE_INT );
if ( ! $term_id ) {
wp_send_json_error( esc_html__( 'Term ID not valid', 'simple-podcasting' ) );
}
$platforms = get_term_meta( $term_id, 'podcasting_platforms', true );
if ( ! is_array( $platforms ) ) {
wp_send_json_error( esc_html__( 'No shows found', 'simple-podcasting' ) );
}
$platforms = array_filter(
$platforms,
function ( $platform ) {
return ! empty( $platform );
}
);
$theme = get_term_meta( $term_id, 'podcasting_icon_theme', true );
if ( empty( $theme ) ) {
$theme = 'color';
}
$result = array(
'platforms' => $platforms,
'theme' => $theme,
);
wp_send_json_success( $result );
}
add_action( 'wp_ajax_get_podcast_platforms', __NAMESPACE__ . '\ajax_get_podcast_platforms' );
/**
* Latest podcast query for front-end.
*
* @param Object $query query object.
*/
function latest_episode_query_loop( $query ) {
// update query to only return posts that have a podcast selected
return [
'post_type' => 'post',
'posts_per_page' => 1,
'orderby' => 'date',
'order' => 'DESC',
'tax_query' => [
[
'taxonomy' => 'podcasting_podcasts',
'field' => 'term_id',
'operator' => 'EXISTS',
],
],
];
}
/**
* Latest podcast check.
*
* @param String $pre_render pre render object.
* @param Array $parsed_block parsed block object.
*/
function latest_episode_check( $pre_render, $parsed_block ) {
if ( isset( $parsed_block['attrs']['namespace'] ) && 'podcasting/latest-episode' === $parsed_block['attrs']['namespace'] ) {
add_action( 'query_loop_block_query_vars', __NAMESPACE__ . '\latest_episode_query_loop' );
}
}
add_filter( 'pre_render_block', __NAMESPACE__ . '\latest_episode_check', 10, 2 );
/**
* Latest podcast query in editor.
*
* @param Array $args query args.
* @param WP_REST_Request $request request object.
*/
function latest_episode_query_api( $args, $request ) {
$podcasting_podcasts = $request->get_param( 'podcastingQuery' );
if ( 'not_empty' === $podcasting_podcasts ) {
$args = [
'post_type' => 'post',
'posts_per_page' => 1,
'orderby' => 'date',
'order' => 'DESC',
'tax_query' => [
[
'taxonomy' => 'podcasting_podcasts',
'field' => 'term_id',
'operator' => 'EXISTS',
],
],
];
}
return $args;
}
add_filter( 'rest_post_query', __NAMESPACE__ . '\latest_episode_query_api', 10, 2 );
================================================
FILE: includes/create-podcast.php
================================================
<?php
/**
* Defines class to handle the validation, sanitization
* and creation of a podcast.
*
* @package tenup_podcasting
*/
namespace tenup_podcasting;
/**
* Provides methods to create a podcast.
*/
class Create_Podcast {
/**
* Name of the podcast.
*
* @var string
*/
protected $podcast_name = '';
/**
* Name of the podcast artist.
*
* @var string
*/
protected $podcast_talent_name = '';
/**
* Summary of the podcast.
*
* @var string
*/
protected $podcast_description = '';
/**
* Podcast's primary category.
*
* @var string
*/
protected $podcast_category = '';
/**
* ID of the podcast cover.
*
* @var int
*/
protected $podcast_cover_id = 0;
/**
* Verifies nonce needed for the creation of a podcast.
*
* @return boolean|WP_Error
*/
public function verify_nonce() {
$is_nonce_set = isset( $_POST['simple-podcasting-create-show-nonce-field'] );
if ( ! $is_nonce_set ) {
return false;
}
$is_nonce_verified = wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['simple-podcasting-create-show-nonce-field'] ) ), 'simple-podcasting-create-show-action' );
if ( ! $is_nonce_verified ) {
return new \WP_Error(
'simple_podcasting_nonce_verification_failed',
esc_html__( 'Nonce verification failed.' )
);
}
ret
gitextract_alta88pg/ ├── .distignore ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .github/ │ ├── CODEOWNERS │ └── workflows/ │ ├── build-release-zip.yml │ ├── close-stale-issues.yml │ ├── cypress.yml │ ├── dependency-review.yml │ ├── php-compatibility.yml │ ├── phpcs.yml │ ├── phpunit.yml │ ├── push-asset-readme-update.yml │ ├── push-deploy.yml │ ├── repo-automator.yml │ └── wordpress-version-checker.yml ├── .gitignore ├── .husky/ │ ├── .gitignore │ └── pre-commit ├── .nvmrc ├── .phpcs.xml.dist ├── .prettierrc ├── .wordpress-org/ │ └── blueprints/ │ └── blueprint.json ├── .wordpress-version-checker.json ├── .wp-env.json ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── CREDITS.md ├── LICENSE.md ├── README.md ├── assets/ │ ├── css/ │ │ ├── podcasting-edit-term.css │ │ ├── podcasting-editor-screen.css │ │ ├── podcasting-onboarding.scss │ │ └── podcasting-transcript.css │ └── js/ │ ├── blocks/ │ │ ├── latest-episode/ │ │ │ ├── index.js │ │ │ └── index.scss │ │ ├── podcast/ │ │ │ ├── index.js │ │ │ └── index.scss │ │ └── podcast-platforms/ │ │ ├── edit.js │ │ ├── index.js │ │ └── index.scss │ ├── blocks.js │ ├── create-podcast-show.js │ ├── deprecated.js │ ├── edit.js │ ├── onboarding.js │ ├── podcasting-edit-post.js │ ├── podcasting-edit-term.js │ └── transforms.js ├── composer.json ├── includes/ │ ├── admin/ │ │ ├── create-podcast-component.php │ │ ├── onboarding.php │ │ └── views/ │ │ ├── onboarding-header.php │ │ ├── onboarding-page-one.php │ │ └── onboarding-page-two.php │ ├── block-patterns.php │ ├── blocks/ │ │ ├── podcast/ │ │ │ └── markup.php │ │ └── podcast-transcript/ │ │ ├── cite.js │ │ ├── edit.js │ │ ├── formats.js │ │ ├── index.js │ │ ├── markup.php │ │ ├── styles.css │ │ └── time.js │ ├── blocks.php │ ├── create-podcast.php │ ├── customize-feed.php │ ├── datatypes.php │ ├── helpers.php │ ├── post-meta-box.php │ ├── rest-external-url.php │ ├── transcripts.php │ └── upgrade.php ├── package.json ├── phpunit.xml.dist ├── readme.txt ├── simple-podcasting.php ├── templates/ │ └── transcript.php ├── tests/ │ ├── bin/ │ │ └── set-wp-config.js │ ├── cypress/ │ │ ├── .eslintrc │ │ ├── config.config.js │ │ ├── fixtures/ │ │ │ └── example.json │ │ ├── integration/ │ │ │ ├── admin.test.js │ │ │ ├── block.test.js │ │ │ ├── onboarding.test.js │ │ │ ├── podcast-setting-panel.test.js │ │ │ └── taxonomy.test.js │ │ ├── plugins/ │ │ │ └── index.js │ │ ├── support/ │ │ │ ├── functions.js │ │ │ └── index.js │ │ └── tsconfig.json │ └── unit/ │ ├── bootstrap.php │ ├── test-blocks.php │ ├── test-customize-feed.php │ ├── test-datatypes.php │ ├── test-helpers.php │ ├── test-rest-external-url.php │ └── test-transcript.php └── webpack.config.js
SYMBOL INDEX (130 symbols across 26 files)
FILE: assets/js/blocks.js
constant VARIATION_NAME (line 147) | const VARIATION_NAME = 'podcasting/latest-episode';
FILE: assets/js/blocks/podcast-platforms/edit.js
function Edit (line 21) | function Edit( props ) {
FILE: assets/js/create-podcast-show.js
constant DEFAULT_QUERY (line 26) | const DEFAULT_QUERY = {
function attachPodcastToPost (line 276) | function attachPodcastToPost( isChecked, podcastId ) {
FILE: assets/js/edit.js
constant ALLOWED_MEDIA_TYPES (line 22) | const ALLOWED_MEDIA_TYPES = ['audio'];
function useFeaturedImage (line 30) | function useFeaturedImage() {
function Edit (line 51) | function Edit( props ) {
function PodcastBlockWithHooks (line 545) | function PodcastBlockWithHooks(props) {
FILE: assets/js/podcasting-edit-term.js
function clearImageField (line 7) | function clearImageField( el ) {
FILE: includes/admin/create-podcast-component.php
class Create_Podcast_Component (line 14) | class Create_Podcast_Component {
method __construct (line 18) | public function __construct() {
method admin_enqueue_scripts (line 25) | public function admin_enqueue_scripts() {
FILE: includes/admin/onboarding.php
class Onboarding (line 13) | class Onboarding {
method __construct (line 38) | public function __construct() {
method register_onoarding_page (line 48) | public function register_onoarding_page() {
method render_page_contents (line 68) | public function render_page_contents() {
method onboarding_action_handler (line 94) | public function onboarding_action_handler() {
FILE: includes/block-patterns.php
function init (line 13) | function init() {
FILE: includes/blocks.php
function init (line 13) | function init() {
function render (line 50) | function render( $attributes, $content, $block ) {
function init_transcript (line 59) | function init_transcript() {
function register_podcast_platforms_block (line 178) | function register_podcast_platforms_block() {
function render_podcasting_platforms (line 227) | function render_podcasting_platforms( $attrs ) {
function register_js_strings (line 288) | function register_js_strings() {
function load_translations (line 307) | function load_translations() {
function block_editor_meta_cleanup (line 328) | function block_editor_meta_cleanup( $post, $request, $creating ) {
function ajax_get_podcast_platforms (line 344) | function ajax_get_podcast_platforms() {
function latest_episode_query_loop (line 384) | function latest_episode_query_loop( $query ) {
function latest_episode_check (line 408) | function latest_episode_check( $pre_render, $parsed_block ) {
function latest_episode_query_api (line 422) | function latest_episode_query_api( $args, $request ) {
FILE: includes/create-podcast.php
class Create_Podcast (line 14) | class Create_Podcast {
method verify_nonce (line 55) | public function verify_nonce() {
method sanitize_podcast_fields (line 77) | public function sanitize_podcast_fields() {
method save_podcast_fields (line 90) | public function save_podcast_fields() {
FILE: includes/customize-feed.php
function xmlns (line 15) | function xmlns() {
function get_the_term (line 25) | function get_the_term() {
function bloginfo_rss_name (line 40) | function bloginfo_rss_name( $output ) {
function bloginfo_rss (line 61) | function bloginfo_rss( $output, $requested ) {
function feed_head (line 92) | function feed_head() {
function feed_item (line 169) | function feed_item() {
function display_rss_enclosure (line 291) | function display_rss_enclosure( $post ) {
function generate_categories (line 321) | function generate_categories() {
function empty_rss_excerpt (line 379) | function empty_rss_excerpt( $output ) {
function pre_get_posts (line 398) | function pre_get_posts( $query ) {
FILE: includes/datatypes.php
function register_meta (line 13) | function register_meta() {
function podcasting_term_auth_callback (line 206) | function podcasting_term_auth_callback() {
function create_podcasts_taxonomy (line 217) | function create_podcasts_taxonomy() {
function filter_parent_file (line 262) | function filter_parent_file( $file ) {
function add_top_level_menu (line 278) | function add_top_level_menu() {
function add_podcasting_taxonomy_help_text (line 295) | function add_podcasting_taxonomy_help_text() {
function get_supported_platforms (line 307) | function get_supported_platforms() {
function render_platform_fields (line 361) | function render_platform_fields( $field, $value, $term_id ) {
function add_podcasting_term_add_meta_fields (line 421) | function add_podcasting_term_add_meta_fields( $term ) {
function the_field (line 440) | function the_field( $field, $value = '', $term_id = false ) {
function save_podcasting_term_meta (line 580) | function save_podcasting_term_meta( $term_id ) {
function add_podcasting_term_edit_meta_fields (line 634) | function add_podcasting_term_edit_meta_fields( $term ) {
function add_podcasting_term_meta_nonce (line 669) | function add_podcasting_term_meta_nonce( $term, $taxonomy = false ) {
function add_podcasting_term_feed_link_column (line 699) | function add_podcasting_term_feed_link_column( $content, $column_name, $...
function add_podcasting_term_podcasting_image_column (line 718) | function add_podcasting_term_podcasting_image_column( $content, $column_...
function add_custom_term_columns (line 736) | function add_custom_term_columns( $columns ) {
function get_meta_fields (line 755) | function get_meta_fields() {
function get_podcasting_categories (line 872) | function get_podcasting_categories() {
function get_podcasting_categories_options (line 1070) | function get_podcasting_categories_options() {
function get_podcasting_language_options (line 1094) | function get_podcasting_language_options() {
FILE: includes/helpers.php
function get_podcast_meta_from_url (line 17) | function get_podcast_meta_from_url( $url ) {
function delete_all_podcast_meta (line 71) | function delete_all_podcast_meta( $post_id ) {
FILE: includes/post-meta-box.php
function add_podcasting_meta_box (line 13) | function add_podcasting_meta_box() {
function meta_box_html (line 33) | function meta_box_html( $post ) {
function save_meta_box (line 110) | function save_meta_box( $post_id ) {
function edit_post_enqueues (line 193) | function edit_post_enqueues( $hook_suffix ) {
FILE: includes/rest-external-url.php
function setup (line 19) | function setup() {
function define_endpoint_for_external_files_meta_check (line 30) | function define_endpoint_for_external_files_meta_check() {
function handle_request (line 58) | function handle_request( \WP_REST_Request $request ) {
FILE: includes/transcripts.php
function query_vars (line 21) | function query_vars( $vars ) {
function template (line 35) | function template( $template ) {
function rewrite_rules (line 50) | function rewrite_rules( $rules ) {
function get_transcript_link_from_post (line 63) | function get_transcript_link_from_post( $post = null ) {
function allow_time_element (line 82) | function allow_time_element( $html, $context ) {
FILE: includes/upgrade.php
function maybe_flush_rewrite (line 15) | function maybe_flush_rewrite() {
FILE: simple-podcasting.php
function minimum_php_requirement (line 27) | function minimum_php_requirement() {
function site_meets_php_requirements (line 38) | function site_meets_php_requirements() {
function activate_plugin (line 94) | function activate_plugin() {
function podcasting_is_enabled (line 133) | function podcasting_is_enabled() {
function podcasting_edit_term_enqueues (line 151) | function podcasting_edit_term_enqueues( $hook_suffix ) {
function custom_feed (line 220) | function custom_feed( \WP_Query $query ) {
function setup_edit_screen (line 248) | function setup_edit_screen() {
function register_podcast_block_assets (line 258) | function register_podcast_block_assets() {
function register_podcast_block_assets_admin (line 282) | function register_podcast_block_assets_admin() {
function register_latest_episode_assets (line 306) | function register_latest_episode_assets() {
function register_latest_episode_assets_admin (line 328) | function register_latest_episode_assets_admin() {
FILE: tests/cypress/config.config.js
method setupNodeEvents (line 24) | setupNodeEvents(on, config) {
FILE: tests/cypress/integration/podcast-setting-panel.test.js
function closeWelcomeGuide (line 29) | function closeWelcomeGuide() {
function openEditorSidebar (line 43) | function openEditorSidebar() {
function openComplementaryArea (line 56) | function openComplementaryArea() {
FILE: tests/unit/test-blocks.php
class BlockTests (line 8) | class BlockTests extends TestCase {
method setUp (line 14) | public function setUp() : void {
method tearDown (line 22) | public function tearDown() : void {
method test_init (line 26) | public function test_init() {
method test_register_js_strings (line 77) | public function test_register_js_strings() {
method test_block_editor_meta_cleanup (line 87) | public function test_block_editor_meta_cleanup( $creating, $has_block,...
method data_provider_for_test_block_editor_meta_cleanup (line 108) | public function data_provider_for_test_block_editor_meta_cleanup() {
FILE: tests/unit/test-customize-feed.php
class CustomizeFeedTests (line 8) | class CustomizeFeedTests extends TestCase {
method setUp (line 14) | public function setUp() : void {
method tearDown (line 22) | public function tearDown() : void {
method test_get_the_term (line 26) | public function test_get_the_term() {
method test_empty_rss_excerpt (line 58) | public function test_empty_rss_excerpt() {
method test_pre_get_posts_no_feed (line 79) | public function test_pre_get_posts_no_feed() {
method test_pre_get_posts_feed (line 90) | public function test_pre_get_posts_feed() {
method test_feed_item (line 111) | public function test_feed_item( $talent_option, $post_data, $term, $po...
method test_rss_title_can_be_filtered (line 179) | public function test_rss_title_can_be_filtered() {
method data_provider_for_test_feed_item (line 204) | public function data_provider_for_test_feed_item() {
FILE: tests/unit/test-datatypes.php
class DatatypesTests (line 8) | class DatatypesTests extends TestCase {
method setUp (line 14) | public function setUp() : void {
method tearDown (line 22) | public function tearDown() : void {
method test_filter_parent_file (line 29) | public function test_filter_parent_file( $screen, $original_file, $exp...
method data_provider_for_test_filter_parent_file (line 38) | public function data_provider_for_test_filter_parent_file()
FILE: tests/unit/test-helpers.php
class HelpersTests (line 8) | class HelpersTests extends TestCase {
method setUp (line 14) | public function setUp() : void {
method tearDown (line 22) | public function tearDown() : void {
method test_delete_all_podcast_meta (line 26) | public function test_delete_all_podcast_meta() {
method test_get_podcast_meta_from_url (line 48) | public function test_get_podcast_meta_from_url( $url, $redirect, $head...
method data_provider_for_test_get_podcast_meta_from_url (line 71) | public function data_provider_for_test_get_podcast_meta_from_url() {
FILE: tests/unit/test-rest-external-url.php
class RestExternalUrlTests (line 8) | class RestExternalUrlTests extends TestCase {
method setUp (line 14) | public function setUp() : void {
method tearDown (line 22) | public function tearDown() : void {
method test_handle_request_cached (line 26) | public function test_handle_request_cached() {
FILE: tests/unit/test-transcript.php
class TranscriptTests (line 8) | class TranscriptTests extends TestCase {
method setUp (line 14) | public function setUp() : void {
method tearDown (line 22) | public function tearDown() : void {
method test_get_transcript_from_post (line 26) | public function test_get_transcript_from_post() {
Condensed preview — 100 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (366K chars).
[
{
"path": ".distignore",
"chars": 319,
"preview": "# Directories\n/.git\n/.github\n/.husky\n/.wordpress-org\n/assets\n/gulp-tasks\n/node_modules\n/tests\n/vendor\n\n# Files\n.*\n/CHANG"
},
{
"path": ".editorconfig",
"chars": 262,
"preview": "root = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\ntrim_trailing_whitespace = true\nindent_sty"
},
{
"path": ".eslintignore",
"chars": 31,
"preview": "assets/js/frontend/vendor/*.js\n"
},
{
"path": ".eslintrc",
"chars": 103,
"preview": "{\n\t\"extends\": [ \"plugin:@wordpress/eslint-plugin/recommended\" ],\n\t\"ignorePatterns\": [\"**/vendor/**\"]\n}\n"
},
{
"path": ".github/CODEOWNERS",
"chars": 429,
"preview": "# These owners will be the default owners for everything in the repo. Unless a later match takes precedence, @10up/open-"
},
{
"path": ".github/workflows/build-release-zip.yml",
"chars": 1196,
"preview": "name: Build release zip\n\npermissions:\n contents: read\n\non:\n workflow_dispatch:\n workflow_call:\n push:\n branches:\n "
},
{
"path": ".github/workflows/close-stale-issues.yml",
"chars": 1708,
"preview": "name: 'Close stale issues'\n\n# **What it does**: Closes issues where the original author doesn't respond to a request for"
},
{
"path": ".github/workflows/cypress.yml",
"chars": 2899,
"preview": "name: E2E Test\n\npermissions:\n contents: read\n pull-requests: write\n\non:\n push:\n branches:\n - trunk\n - de"
},
{
"path": ".github/workflows/dependency-review.yml",
"chars": 1137,
"preview": "# Dependency Review Action\n#\n# This Action will scan dependency manifest files that change as part of a Pull Reqest, sur"
},
{
"path": ".github/workflows/php-compatibility.yml",
"chars": 1355,
"preview": "name: PHP Compatibility\n\npermissions:\n contents: read\n\nenv:\n COMPOSER_VERSION: \"2\"\n COMPOSER_CACHE: \"${{ github.works"
},
{
"path": ".github/workflows/phpcs.yml",
"chars": 703,
"preview": "name: PHPCS\n\npermissions:\n contents: read\n\non:\n push:\n branches:\n - develop\n - trunk\n paths:\n - \""
},
{
"path": ".github/workflows/phpunit.yml",
"chars": 1568,
"preview": "name: Unit Tests\n\npermissions:\n contents: read\n\nenv:\n COMPOSER_VERSION: \"2\"\n COMPOSER_CACHE: \"${{ github.workspace }}"
},
{
"path": ".github/workflows/push-asset-readme-update.yml",
"chars": 778,
"preview": "name: Plugin asset/readme update\n\non:\n push:\n branches:\n - trunk\n\npermissions:\n contents: read\n\njobs:\n trunk:\n "
},
{
"path": ".github/workflows/push-deploy.yml",
"chars": 1139,
"preview": "name: Deploy to WordPress.org\n\npermissions:\n contents: write\n packages: read\n actions: write\n\non:\n release:\n type"
},
{
"path": ".github/workflows/repo-automator.yml",
"chars": 713,
"preview": "name: 'Repo Automator'\n\npermissions:\n contents: read\n issues: write\n\non:\n issues:\n types:\n - opened\n push:\n "
},
{
"path": ".github/workflows/wordpress-version-checker.yml",
"chars": 477,
"preview": "name: \"WordPress version checker\"\n\non:\n push:\n branches:\n - develop\n - trunk\n pull_request:\n branches:"
},
{
"path": ".gitignore",
"chars": 441,
"preview": "node_modules\nbower_components\nlanguages\nrelease\nvendor\nphpunit.xml\n.idea\n.phpunit.result.cache\n\n# Project Files\ndist\nrul"
},
{
"path": ".husky/.gitignore",
"chars": 2,
"preview": "_\n"
},
{
"path": ".husky/pre-commit",
"chars": 58,
"preview": "#!/bin/sh\n. \"$(dirname \"$0\")/_/husky.sh\"\n\nnpx lint-staged\n"
},
{
"path": ".nvmrc",
"chars": 4,
"preview": "v20\n"
},
{
"path": ".phpcs.xml.dist",
"chars": 242,
"preview": "<?xml version=\"1.0\"?>\n<ruleset name=\"Simple Podcasting\">\n\t<rule ref=\"10up-Default\" />\n\t<file>.</file>\n\t<exclude-pattern>"
},
{
"path": ".prettierrc",
"chars": 78,
"preview": "{\n\t\"useTabs\": true,\n\t\"printWidth\": 90,\n\t\"tabWidth\": 4,\n\t\"singleQuote\": true\n}\n"
},
{
"path": ".wordpress-org/blueprints/blueprint.json",
"chars": 573,
"preview": "{\n\t\"$schema\": \"https://playground.wordpress.net/blueprint-schema.json\",\n\t\"landingPage\": \"\\/wp-admin\\/admin.php?page=simp"
},
{
"path": ".wordpress-version-checker.json",
"chars": 52,
"preview": "{\n \"readme\": \"readme.txt\",\n \"channel\": \"rc\"\n}\n"
},
{
"path": ".wp-env.json",
"chars": 23,
"preview": "{\n \"plugins\": [\".\"]\n}\n"
},
{
"path": "CHANGELOG.md",
"chars": 38193,
"preview": "# Changelog\n\nAll notable changes to this project will be documented in this file, per [the Keep a Changelog standard](ht"
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 3351,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
},
{
"path": "CONTRIBUTING.md",
"chars": 7487,
"preview": "# Contributing and Maintaining\n\nFirst, thank you for taking the time to contribute!\n\nThe following is a set of guideline"
},
{
"path": "CREDITS.md",
"chars": 4259,
"preview": "The following acknowledges the Maintainers for this repository, those who have Contributed to this repository (via bug r"
},
{
"path": "LICENSE.md",
"chars": 18092,
"preview": " GNU GENERAL PUBLIC LICENSE\n Version 2, June 1991\n\n Copyright (C) 1989, 1991 Fr"
},
{
"path": "README.md",
"chars": 11015,
"preview": "# Simple Podcasting for WordPress\n\n {\n\t$( function() {\n\t\tconst selectImageBtn = $( '#simple-pod"
},
{
"path": "assets/js/podcasting-edit-post.js",
"chars": 863,
"preview": "/*global jQuery */\njQuery( document ).ready( function( $ ) {\n\t$( '#podcasting-enclosure-button' ).click( function( e ) {"
},
{
"path": "assets/js/podcasting-edit-term.js",
"chars": 3328,
"preview": "/*global jQuery, validateForm*/\nimport '../css/podcasting-edit-term.css';\n\njQuery( document ).ready( function( $ ) {\n\n\t/"
},
{
"path": "assets/js/transforms.js",
"chars": 807,
"preview": "/**\n * WordPress dependencies\n */\nconst { select } = wp.data;\nconst { createBlock } = wp.blocks;\n\n/**\n * Transforms\n */\n"
},
{
"path": "composer.json",
"chars": 745,
"preview": "{\n \"name\": \"10up/simple-podcasting\",\n \"description\": \"A simple podcasting solution for WordPress. \",\n \"homepage\": \"ht"
},
{
"path": "includes/admin/create-podcast-component.php",
"chars": 1009,
"preview": "<?php\n/**\n * Defines class to handle creation of a podcast\n * from with the Post editor.\n *\n * @package tenup_podcasting"
},
{
"path": "includes/admin/onboarding.php",
"chars": 2912,
"preview": "<?php\n/**\n * Registers and renders the onboarding setup wizard.\n *\n * @package tenup_podcasting\n */\n\nnamespace tenup_pod"
},
{
"path": "includes/admin/views/onboarding-header.php",
"chars": 859,
"preview": "<?php\n/**\n * Header template for onboarding: Step - 1.\n *\n * @package tenup_podcasting\n */\n\n?>\n<div id=\"simple-podcastin"
},
{
"path": "includes/admin/views/onboarding-page-one.php",
"chars": 4687,
"preview": "<?php\n/**\n * Body template for onboarding: Step - 1.\n *\n * @package tenup_podcasting\n */\n\n?>\n<form method=\"POST\" action="
},
{
"path": "includes/admin/views/onboarding-page-two.php",
"chars": 2445,
"preview": "<?php\n/**\n * Body template for onboarding: Step - 2.\n *\n * @package tenup_podcasting\n */\n\nuse tenup_podcasting\\admin\\Onb"
},
{
"path": "includes/block-patterns.php",
"chars": 2159,
"preview": "<?php\n/**\n * Register and enqueue all things block patterns.\n *\n * @package tenup_podcasting\n */\n\nnamespace tenup_podcas"
},
{
"path": "includes/blocks/podcast/markup.php",
"chars": 4360,
"preview": "<?php\n/**\n * Podcast markup\n *\n * @package tenup_podcasting\n *\n * @var array $attributes Block attributes.\n * @var st"
},
{
"path": "includes/blocks/podcast-transcript/cite.js",
"chars": 983,
"preview": "import { registerBlockType, createBlock } from '@wordpress/blocks';\nimport { useBlockProps, RichText } from '@wordpress/"
},
{
"path": "includes/blocks/podcast-transcript/edit.js",
"chars": 2867,
"preview": "import { useBlockProps, RichText, InnerBlocks } from '@wordpress/block-editor';\nimport { __ } from '@wordpress/i18n';\nim"
},
{
"path": "includes/blocks/podcast-transcript/formats.js",
"chars": 1825,
"preview": "import { registerFormatType, toggleFormat } from '@wordpress/rich-text';\nimport { BlockControls } from '@wordpress/block"
},
{
"path": "includes/blocks/podcast-transcript/index.js",
"chars": 305,
"preview": "import { registerBlockType } from '@wordpress/blocks';\nimport { InnerBlocks } from '@wordpress/block-editor';\nimport './"
},
{
"path": "includes/blocks/podcast-transcript/markup.php",
"chars": 834,
"preview": "<?php\n/**\n * Podcast Transcript markup\n *\n * @package tenup_podcasting\n *\n * @var array $attributes Block attributes."
},
{
"path": "includes/blocks/podcast-transcript/styles.css",
"chars": 112,
"preview": ".wp-block-podcasting-podcast-transcript cite,\n.wp-block-podcasting-podcast-transcript time {\n\tdisplay: block;\n}\n"
},
{
"path": "includes/blocks/podcast-transcript/time.js",
"chars": 983,
"preview": "import { registerBlockType, createBlock } from '@wordpress/blocks';\nimport { useBlockProps, RichText } from '@wordpress/"
},
{
"path": "includes/blocks.php",
"chars": 11849,
"preview": "<?php\n/**\n * Register and enqueue all things block-related.\n *\n * @package tenup_podcasting\n */\n\nnamespace tenup_podcast"
},
{
"path": "includes/create-podcast.php",
"chars": 4156,
"preview": "<?php\n/**\n * Defines class to handle the validation, sanitization\n * and creation of a podcast.\n *\n * @package tenup_pod"
},
{
"path": "includes/customize-feed.php",
"chars": 12127,
"preview": "<?php\n/**\n * Customize the feed for a specific podcast. Insert the podcast data stored in term meta.\n *\n * @package tenu"
},
{
"path": "includes/datatypes.php",
"chars": 31214,
"preview": "<?php\n/**\n * Register the data types\n *\n * @package tenup_podcasting\n */\n\nnamespace tenup_podcasting;\n\n/**\n * Register t"
},
{
"path": "includes/helpers.php",
"chars": 2647,
"preview": "<?php\n/**\n * Common helper functions\n *\n * @package tenup_podcasting\\helpers\n */\n\nnamespace tenup_podcasting\\helpers;\n\n/"
},
{
"path": "includes/post-meta-box.php",
"chars": 8929,
"preview": "<?php\n/**\n * Add a meta box to the post edit screen, plus handlers for saving.\n *\n * @package tenup_podcasting;\n */\n\nnam"
},
{
"path": "includes/rest-external-url.php",
"chars": 2036,
"preview": "<?php\n/**\n * Endpoint definitions.\n *\n * @package tenup_podcasting\\endpoints\n */\n\nnamespace tenup_podcasting\\endpoints\\e"
},
{
"path": "includes/transcripts.php",
"chars": 2165,
"preview": "<?php\n/**\n * Adds an endpoint for viewing transcripts.\n *\n * @package tenup_podcasting\\transcripts;\n */\n\nnamespace tenup"
},
{
"path": "includes/upgrade.php",
"chars": 527,
"preview": "<?php\n/**\n * Upgrade routines using options.\n *\n * @package tenup_podcasting\\upgrade;\n */\n\nnamespace tenup_podcasting\\up"
},
{
"path": "package.json",
"chars": 2088,
"preview": "{\n \"name\": \"@10up/simple-podcasting\",\n \"version\": \"1.9.1\",\n \"description\": \"A simple podcasting solution for WordPres"
},
{
"path": "phpunit.xml.dist",
"chars": 543,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit\n\t\tbootstrap=\"tests/unit/bootstrap.php\"\n\t\tbackupGlobals=\"false\"\n\t\tcolors="
},
{
"path": "readme.txt",
"chars": 20883,
"preview": "=== Simple Podcasting ===\nContributors: 10up, helen, adamsilverstein, jakemgold, jeffpaul, cadic\nTags: podcastin"
},
{
"path": "simple-podcasting.php",
"chars": 9058,
"preview": "<?php\n/**\n * Plugin Name: Simple Podcasting\n * Plugin URI: https://github.com/10up/simple-podcasting\n * Des"
},
{
"path": "templates/transcript.php",
"chars": 1119,
"preview": "<?php\n/**\n * Template for transcripts.\n * Intentionally barebones with the minimum html for use by tools.\n *\n * @package"
},
{
"path": "tests/bin/set-wp-config.js",
"chars": 817,
"preview": "#!/usr/bin/env node\n\nconst fs = require('fs');\n\nconst path = `${process.cwd()}/.wp-env.override.json`;\n\nlet config = fs."
},
{
"path": "tests/cypress/.eslintrc",
"chars": 92,
"preview": "{\n\t\"extends\": \"plugin:cypress/recommended\",\n\t\"rules\": {\n\t\t\"jest/expect-expect\": \"off\",\n\t}\n}\n"
},
{
"path": "tests/cypress/config.config.js",
"chars": 1053,
"preview": "const { defineConfig } = require('cypress');\n\nmodule.exports = defineConfig({\n\tfixturesFolder: __dirname + '/fixtures',\n"
},
{
"path": "tests/cypress/fixtures/example.json",
"chars": 155,
"preview": "{\n \"name\": \"Using fixtures to represent data\",\n \"email\": \"hello@cypress.io\",\n \"body\": \"Fixtures are a great way to mo"
},
{
"path": "tests/cypress/integration/admin.test.js",
"chars": 261,
"preview": "describe('Admin can login and make sure plugin is activated', () => {\n\tbeforeEach(() => {\n\t\tcy.login();\n\t});\n\n\tit('Can a"
},
{
"path": "tests/cypress/integration/block.test.js",
"chars": 2578,
"preview": "import 'cypress-localstorage-commands';\nconst { populatePodcast } = require('../support/functions');\n\ndescribe('Admin ca"
},
{
"path": "tests/cypress/integration/onboarding.test.js",
"chars": 1887,
"preview": "const {\n\trandomName,\n\tpopulatePodcast,\n\tdeleteAllTerms,\n} = require('../support/functions');\n\ndescribe('Onboarding tests"
},
{
"path": "tests/cypress/integration/podcast-setting-panel.test.js",
"chars": 5233,
"preview": "describe('Create podcast setting panel', () => {\n\tbefore(() => {\n\t\tcy.login();\n\t\tcy.visit(\n\t\t\t'/wp-admin/edit-tags.php?t"
},
{
"path": "tests/cypress/integration/taxonomy.test.js",
"chars": 3171,
"preview": "const {\n\trandomName,\n\tpopulatePodcast,\n\tdeleteAllTerms,\n} = require('../support/functions');\n\ndescribe('Admin can create"
},
{
"path": "tests/cypress/plugins/index.js",
"chars": 1388,
"preview": "/// <reference types=\"cypress\" />\n// ***********************************************************\n// This example plugins"
},
{
"path": "tests/cypress/support/functions.js",
"chars": 1897,
"preview": "export const randomName = () => Math.random().toString(16).substring(7);\n\nexport const getFirstImage = () => {\n\tcy.get('"
},
{
"path": "tests/cypress/support/index.js",
"chars": 598,
"preview": "// ***********************************************************\n// This example support/index.js is processed and\n// load"
},
{
"path": "tests/cypress/tsconfig.json",
"chars": 102,
"preview": "{\n \"compilerOptions\": {\n \"allowJs\": true,\n \"types\": [\"cypress\"]\n },\n \"include\": [\"**/*.*\"]\n}\n"
},
{
"path": "tests/unit/bootstrap.php",
"chars": 1075,
"preview": "<?php\n/**\n * The bootstrap file for PHPUnit tests for the Simple Podcasting plugin.\n * Starts up WP_Mock and requires th"
},
{
"path": "tests/unit/test-blocks.php",
"chars": 3514,
"preview": "<?php\n\nuse PHPUnit\\Framework\\TestCase;\n\n/**\n * The AddressTests class tests the functions associated with an address ass"
},
{
"path": "tests/unit/test-customize-feed.php",
"chars": 10789,
"preview": "<?php\n\nuse PHPUnit\\Framework\\TestCase;\n\n/**\n * The AddressTests class tests the functions associated with an address ass"
},
{
"path": "tests/unit/test-datatypes.php",
"chars": 2200,
"preview": "<?php\n\nuse PHPUnit\\Framework\\TestCase;\n\n/**\n * The AddressTests class tests the functions associated with an address ass"
},
{
"path": "tests/unit/test-helpers.php",
"chars": 3498,
"preview": "<?php\n\nuse PHPUnit\\Framework\\TestCase;\n\n/**\n * The AddressTests class tests the functions associated with an address ass"
},
{
"path": "tests/unit/test-rest-external-url.php",
"chars": 1315,
"preview": "<?php\n\nuse PHPUnit\\Framework\\TestCase;\n\n/**\n * The AddressTests class tests the functions associated with an address ass"
},
{
"path": "tests/unit/test-transcript.php",
"chars": 1271,
"preview": "<?php\n\nuse PHPUnit\\Framework\\TestCase;\n\n/**\n * The TranscriptTests class tests the functions associated with a transcrip"
},
{
"path": "webpack.config.js",
"chars": 1421,
"preview": "const defaultConfig = require('@wordpress/scripts/config/webpack.config');\nconst CopyPlugin = require('copy-webpack-plug"
}
]
About this extraction
This page contains the full source code of the 10up/simple-podcasting GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 100 files (323.6 KB), approximately 97.0k tokens, and a symbol index with 130 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.