[
  {
    "path": ".distignore",
    "content": "# 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/CHANGELOG.md\n/CODE_OF_CONDUCT.md\n/composer.json\n/composer.lock\n/CONTRIBUTING.md\n/CREDITS.md\n/gulpfile.babel.js\n/LICENSE.md\n/package.json\n/package-lock.json\n/phpunit.xml.dist\n/README.md\n/webpack.config.js\n"
  },
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\ntrim_trailing_whitespace = true\nindent_style = tab\n\n[{*.json,*.yml,.babelrc,.bowerrc,.postcssrc}]\nindent_style = space\nindent_size = 2\n\n[*.txt,wp-config-sample.php]\nend_of_line = crlf\n"
  },
  {
    "path": ".eslintignore",
    "content": "assets/js/frontend/vendor/*.js\n"
  },
  {
    "path": ".eslintrc",
    "content": "{\n\t\"extends\": [ \"plugin:@wordpress/eslint-plugin/recommended\" ],\n\t\"ignorePatterns\": [\"**/vendor/**\"]\n}\n"
  },
  {
    "path": ".github/CODEOWNERS",
    "content": "# 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.\n*                   @10up/open-source-practice\n\n# GitHub and WordPress.org specifics\n/.github/           @jeffpaul\n/.wordpress-org/    @jeffpaul\nCODE_OF_CONDUCT.md  @jeffpaul\nLICENSE.md          @jeffpaul\n"
  },
  {
    "path": ".github/workflows/build-release-zip.yml",
    "content": "name: Build release zip\n\npermissions:\n  contents: read\n\non:\n  workflow_dispatch:\n  workflow_call:\n  push:\n   branches:\n    - trunk\n\njobs:\n  build:\n    name: Build release zip\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2\n\n      - name: Cache node_modules\n        id: cache-node-modules\n        uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2\n        env:\n          cache-name: cache-node-modules\n        with:\n          path: node_modules\n          key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}\n\n      - name: Setup node version and npm cache\n        uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0\n        with:\n          node-version-file: '.nvmrc'\n          cache: 'npm'\n\n      - name: Install Node dependencies\n        if: steps.cache-node-modules.outputs.cache-hit != 'true'\n        run: npm ci --no-optional\n\n      - name: Build plugin\n        run: npm run build\n\n      - name: Generate ZIP file\n        uses: 10up/action-wordpress-plugin-build-zip@b9e621e1261ccf51592b6f3943e4dc4518fca0d1 # v1.0.2\n"
  },
  {
    "path": ".github/workflows/close-stale-issues.yml",
    "content": "name: 'Close stale issues'\n\n# **What it does**: Closes issues where the original author doesn't respond to a request for information.\n# **Why we have it**: To remove the need for maintainers to remember to check back on issues periodically to see if contributors have responded.\n\non:\n  schedule:\n    # Schedule for every day at 1:30am UTC\n    - cron: '30 1 * * *'\n\npermissions:\n  issues: write\n\njobs:\n  stale:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0\n        with:\n          days-before-stale: 7\n          days-before-close: 7\n          stale-issue-message: >\n            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.\n          close-issue-message: >\n            This issue has been automatically closed because there has been no response\n            to our request for more information. With only the\n            information that is currently in the issue, we don't have enough information\n            to take action. Please reach out if you have or find the answers we need so\n            that we can investigate further. See [this blog post on bug reports and the\n            importance of repro steps](https://www.lee-dohm.com/2015/01/04/writing-good-bug-reports/)\n            for more information about the kind of information that may be helpful.\n          stale-issue-label: 'stale'\n          close-issue-reason: 'not_planned'\n          any-of-labels: 'needs:feedback'\n          remove-stale-when-updated: true\n"
  },
  {
    "path": ".github/workflows/cypress.yml",
    "content": "name: E2E Test\n\npermissions:\n  contents: read\n  pull-requests: write\n\non:\n  push:\n    branches:\n      - trunk\n      - develop\n  pull_request:\n    branches:\n      - develop\n\njobs:\n  build:\n    uses: 10up/simple-podcasting/.github/workflows/build-release-zip.yml@develop\n\n  cypress:\n    needs: build\n    runs-on: ubuntu-latest\n\n    strategy:\n      fail-fast: false\n      matrix:\n        core:\n          - {name: 'WP latest', version: 'latest'}\n          - {name: 'WP trunk', version: 'WordPress/WordPress#master'}\n          - {name: 'WP minimum', version: 'WordPress/WordPress#6.6'}\n\n    steps:\n    - name: Checkout\n      uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2\n\n    - name: Download build zip\n      uses: actions/download-artifact@b14cf4c92620c250e1c074ab0a5800e37df86765 # v4.2.0\n      with:\n        name: ${{ github.event.repository.name }}\n        path: ${{ github.event.repository.name }}\n\n    - name: Display structure of downloaded files\n      run: ls -R\n      working-directory: ${{ github.event.repository.name }}\n\n    - name: Cache node_modules\n      id: cache-node-modules\n      uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2\n      env:\n        cache-name: cache-node-modules\n      with:\n        path: |\n          node_modules\n          ~/.cache\n          ~/.npm\n        key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}\n\n    - name: Install dependencies\n      run: npm install\n\n    - name: Set the core version and plugins config\n      run: ./tests/bin/set-wp-config.js --core=${{ matrix.core.version }} --plugins=./${{ github.event.repository.name }}\n\n    - name: Set up WP environment\n      run: npm run wp-env start\n      continue-on-error: ${{ matrix.core.name == 'WP trunk' }}\n\n    - name: Test\n      run: npm run cypress:run\n      continue-on-error: ${{ matrix.core.name == 'WP trunk' }}\n\n    - name: Update summary\n      if: always()\n      run: |\n          npx mochawesome-merge ./tests/cypress/reports/*.json -o tests/cypress/reports/mochawesome.json\n          rm -rf ./tests/cypress/reports/mochawesome-*.json\n          npx mochawesome-json-to-md -p ./tests/cypress/reports/mochawesome.json -o ./tests/cypress/reports/mochawesome.md\n          npx mochawesome-report-generator tests/cypress/reports/mochawesome.json -o tests/cypress/reports/\n          cat ./tests/cypress/reports/mochawesome.md >> $GITHUB_STEP_SUMMARY\n\n    - name: Make artifacts available\n      uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1\n      if: failure()\n      with:\n        name: cypress-artifact\n        retention-days: 2\n        path: |\n            ${{ github.workspace }}/tests/cypress/screenshots/\n            ${{ github.workspace }}/tests/cypress/videos/\n            ${{ github.workspace }}/tests/cypress/logs/\n            ${{ github.workspace }}/tests/cypress/reports/\n"
  },
  {
    "path": ".github/workflows/dependency-review.yml",
    "content": "# Dependency Review Action\n#\n# 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.\n#\n# Source repository: https://github.com/actions/dependency-review-action\n# Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement\nname: 'Dependency Review'\non: [pull_request]\n\npermissions:\n  contents: read\n\njobs:\n  dependency-review:\n    runs-on: ubuntu-latest\n    steps:\n      - name: 'Checkout Repository'\n        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2\n\n      - name: Dependency Review\n        uses: actions/dependency-review-action@72eb03d02c7872a771aacd928f3123ac62ad6d3a # v4.3.3\n        with:\n          license-check: true\n          vulnerability-check: false\n          config-file: 10up/.github/.github/dependency-review-config.yml@trunk\n"
  },
  {
    "path": ".github/workflows/php-compatibility.yml",
    "content": "name: PHP Compatibility\n\npermissions:\n  contents: read\n\nenv:\n  COMPOSER_VERSION: \"2\"\n  COMPOSER_CACHE: \"${{ github.workspace }}/.composer-cache\"\n\non:\n  push:\n    branches:\n      - develop\n      - trunk\n  pull_request:\n    branches:\n      - develop\n\njobs:\n  php_compatibility:\n    name: PHP ${{ matrix.php }}\n    runs-on: ubuntu-latest\n\n    steps:\n    - name: Checkout\n      uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2\n\n    - name: Set standard 10up cache directories\n      run: |\n        composer config -g cache-dir \"${{ env.COMPOSER_CACHE }}\"\n\n    - name: Prepare composer cache\n      uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2\n      with:\n        path: ${{ env.COMPOSER_CACHE }}\n        key: composer-${{ env.COMPOSER_VERSION }}-${{ hashFiles('**/composer.lock') }}\n        restore-keys: |\n          composer-${{ env.COMPOSER_VERSION }}-\n\n    - name: Set PHP version\n      uses: shivammathur/setup-php@9e72090525849c5e82e596468b86eb55e9cc5401 # v2.32.0\n      with:\n        php-version: '7.4'\n        coverage: none\n        tools: prestissimo, composer:v2\n\n    - name: Install dependencies\n      run: composer install\n\n    - name: Check PHP Compatibility\n      run: ./vendor/bin/phpcs -p simple-podcasting.php includes --standard=PHPCompatibilityWP --extensions=php --runtime-set testVersion 7.4-\n"
  },
  {
    "path": ".github/workflows/phpcs.yml",
    "content": "name: PHPCS\n\npermissions:\n  contents: read\n\non:\n  push:\n    branches:\n      - develop\n      - trunk\n    paths:\n      - \"**.php\"\n  pull_request:\n    branches:\n      - develop\n    paths:\n      - \"**.php\"\n\njobs:\n  phpcs:\n    runs-on: ubuntu-latest\n\n    steps:\n    - name: Checkout\n      uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2\n\n    - name: Set PHP version\n      uses: shivammathur/setup-php@9e72090525849c5e82e596468b86eb55e9cc5401 # v2.32.0\n      with:\n        php-version: '7.4'\n        coverage: none\n        tools: composer:v2\n\n    - name: Install dependencies\n      run: composer install\n\n    - name: Test\n      run: ./vendor/bin/phpcs --runtime-set testVersion 7.4 .\n"
  },
  {
    "path": ".github/workflows/phpunit.yml",
    "content": "name: Unit Tests\n\npermissions:\n  contents: read\n\nenv:\n  COMPOSER_VERSION: \"2\"\n  COMPOSER_CACHE: \"${{ github.workspace }}/.composer-cache\"\n\non:\n  push:\n    branches:\n      - develop\n      - trunk\n  pull_request:\n    branches:\n      - develop\n\njobs:\n  phpunit:\n    name: ${{ matrix.php.name }}\n    runs-on: ubuntu-latest\n\n    strategy:\n      fail-fast: false\n      matrix:\n        php:\n          - {name: 'PHP 7.4', version: '7.4'}\n          - {name: 'PHP 8.1', version: '8.1'}\n\n    steps:\n    - name: Checkout\n      uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2\n\n    - name: Set standard 10up cache directories\n      run: |\n        composer config -g cache-dir \"${{ env.COMPOSER_CACHE }}\"\n\n    - uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0\n      with:\n        node-version-file: '.nvmrc'\n\n    - name: Prepare composer cache\n      uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2\n      with:\n        path: ${{ env.COMPOSER_CACHE }}\n        key: composer-${{ env.COMPOSER_VERSION }}-${{ hashFiles('**/composer.lock') }}\n        restore-keys: |\n          composer-${{ env.COMPOSER_VERSION }}-\n\n    - name: Set PHP version\n      uses: shivammathur/setup-php@9e72090525849c5e82e596468b86eb55e9cc5401 # v2.32.0\n      with:\n        php-version: '${{ matrix.php.version }}'\n        coverage: none\n        tools: composer:v2\n\n    - name: Install dependencies\n      run: composer install && npm install\n\n    - name: Build\n      run: npm run build\n\n    - name: Test\n      run: ./vendor/bin/phpunit -v\n"
  },
  {
    "path": ".github/workflows/push-asset-readme-update.yml",
    "content": "name: Plugin asset/readme update\n\non:\n  push:\n    branches:\n    - trunk\n\npermissions:\n  contents: read\n\njobs:\n  trunk:\n    name: Push to trunk\n    runs-on: ubuntu-latest\n\n    steps:\n    - name: Checkout code\n      uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2\n\n    - name: install node\n      uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0\n      with:\n        node-version-file: .nvmrc\n\n    - name: Build\n      run: |\n        npm ci\n        npm run build\n\n    - name: WordPress.org plugin asset/readme update\n      uses: 10up/action-wordpress-plugin-asset-update@2480306f6f693672726d08b5917ea114cb2825f7 # v2.2.0\n      env:\n        SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }}\n        SVN_USERNAME: ${{ secrets.SVN_USERNAME }}\n"
  },
  {
    "path": ".github/workflows/push-deploy.yml",
    "content": "name: Deploy to WordPress.org\n\npermissions:\n  contents: write\n  packages: read\n  actions: write\n\non:\n  release:\n    types: [published]\n\njobs:\n  tag:\n    name: New release\n    runs-on: ubuntu-latest\n\n    steps:\n    - name: Checkout code\n      uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2\n\n    - name: install node\n      uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0\n      with:\n        node-version-file: .nvmrc\n\n    - name: Build\n      run: |\n        npm ci\n        npm run build\n        npm run makepot\n\n    - name: WordPress Plugin Deploy\n      id: deploy\n      uses: 10up/action-wordpress-plugin-deploy@54bd289b8525fd23a5c365ec369185f2966529c2 # v2.3.0\n      with:\n        generate-zip: true\n      env:\n        SVN_USERNAME: ${{ secrets.SVN_USERNAME }}\n        SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }}\n\n    - name: Upload release asset\n      uses: softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda # v2.2.1\n      env:\n        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n      with:\n        files: ${{ github.workspace }}/${{ github.event.repository.name }}.zip\n"
  },
  {
    "path": ".github/workflows/repo-automator.yml",
    "content": "name: 'Repo Automator'\n\npermissions:\n  contents: read\n  issues: write\n\non:\n  issues:\n    types:\n      - opened\n  push:\n    branches:\n      - develop\n  pull_request:\n    types:\n      - opened\n      - edited\n      - synchronize\n      - converted_to_draft\n      - ready_for_review\n    branches:\n      - develop\n\njobs:\n  Validate:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: 10up/action-repo-automator@280f5dc0b4ed1b5c50c816e08623bdefce55cdce # v2.1.3\n        with:\n          fail-label: needs:feedback\n          pass-label: needs:code-review\n          conflict-label: needs:refresh\n          reviewers: |\n            team:open-source-practice\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/wordpress-version-checker.yml",
    "content": "name: \"WordPress version checker\"\n\non:\n  push:\n    branches:\n      - develop\n      - trunk\n  pull_request:\n    branches:\n      - develop\n  schedule:\n    - cron: '0 0 * * 1'\n\npermissions:\n  issues: write\n\njobs:\n  wordpress-version-checker:\n    runs-on: ubuntu-latest\n    steps:\n      - name: WordPress version checker\n        uses: skaut/wordpress-version-checker@9d247334f5b30202cb9c1f4aee74c52f37399f69 # v2.2.3\n        with:\n          repo-token: ${{ secrets.GITHUB_TOKEN }}\n"
  },
  {
    "path": ".gitignore",
    "content": "node_modules\nbower_components\nlanguages\nrelease\nvendor\nphpunit.xml\n.idea\n.phpunit.result.cache\n\n# Project Files\ndist\nruleset.xml\n\n# Editors\n*.esproj\n*.tmproj\n*.tmproject\ntmtags\n.*.sw[a-z]\n*.un~\nSession.vim\n*.swp\n*.csv\n\n# Mac OSX\n.DS_Store\n._*\n.Spotlight-V100\n.Trashes\n\n# Windows\nThumbs.db\nDesktop.ini\n\n# E2E testing\n.wp-env.override.json\nartifacts\ntests/cypress/downloads\ntests/cypress/screenshots\ntests/cypress/videos\ntests/cypress/reports\n"
  },
  {
    "path": ".husky/.gitignore",
    "content": "_\n"
  },
  {
    "path": ".husky/pre-commit",
    "content": "#!/bin/sh\n. \"$(dirname \"$0\")/_/husky.sh\"\n\nnpx lint-staged\n"
  },
  {
    "path": ".nvmrc",
    "content": "v20\n"
  },
  {
    "path": ".phpcs.xml.dist",
    "content": "<?xml version=\"1.0\"?>\n<ruleset name=\"Simple Podcasting\">\n\t<rule ref=\"10up-Default\" />\n\t<file>.</file>\n\t<exclude-pattern>dist/</exclude-pattern>\n\t<exclude-pattern>vendor/</exclude-pattern>\n\t<exclude-pattern>tests/</exclude-pattern>\n</ruleset>\n"
  },
  {
    "path": ".prettierrc",
    "content": "{\n\t\"useTabs\": true,\n\t\"printWidth\": 90,\n\t\"tabWidth\": 4,\n\t\"singleQuote\": true\n}\n"
  },
  {
    "path": ".wordpress-org/blueprints/blueprint.json",
    "content": "{\n\t\"$schema\": \"https://playground.wordpress.net/blueprint-schema.json\",\n\t\"landingPage\": \"\\/wp-admin\\/admin.php?page=simple-podcasting-onboarding&step=1\",\n\t\"preferredVersions\": {\n\t\t\"php\": \"7.4\",\n\t\t\"wp\": \"latest\"\n\t},\n\t\"phpExtensionBundles\": [\"kitchen-sink\"],\n\t\"features\": {\n\t\t\"networking\": true\n\t},\n\t\"steps\": [\n\t\t{\n\t\t\t\"step\": \"login\",\n\t\t\t\"username\": \"admin\",\n\t\t\t\"password\": \"password\"\n\t\t},\n\t\t{\n\t\t\t\"step\": \"installPlugin\",\n\t\t\t\"pluginZipFile\": {\n\t\t\t\t\"resource\": \"wordpress.org\\/plugins\",\n\t\t\t\t\"slug\": \"simple-podcasting\"\n\t\t\t},\n\t\t\t\"options\": {\n\t\t\t\t\"activate\": true\n\t\t\t}\n\t\t}\n\t]\n}\n"
  },
  {
    "path": ".wordpress-version-checker.json",
    "content": "{\n    \"readme\": \"readme.txt\",\n    \"channel\": \"rc\"\n}\n"
  },
  {
    "path": ".wp-env.json",
    "content": "{\n  \"plugins\": [\".\"]\n}\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file, per [the Keep a Changelog standard](http://keepachangelog.com/).\n\n## [Unreleased] - TBD\n\n## [1.9.1] - 2025-05-19\n**Note that this release bumps the WordPress minimum version from 6.5 to 6.6.**\n\n### Added\n- 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)).\n\n### Changed\n- 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)).\n- 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)).\n\n### Fixed\n- 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)).\n\n### Security\n- 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)).\n- 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)).\n- 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)).\n\n### Developer\n- 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)).\n- 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)).\n\n## [1.9.0] - 2024-11-18\n**Note that this release bumps the WordPress minimum version from 5.7 to 6.5.**\n\n### Added\n- 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)).\n\n### Changed\n- 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)).\n- 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)).\n- 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)).\n- 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)).\n\n### Security\n- 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)).\n- 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)).\n- 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)).\n- 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)).\n- 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)).\n\n### Developer\n- 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)).\n- 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)).\n- 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)).\n- 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)).\n- 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)).\n\n## [1.8.0] - 2024-04-03\n### Added\n- \"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)).\n- 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)).\n- `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)).\n\n### Fixed\n- 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)).\n- 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)).\n\n### Changed\n- 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)).\n- 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)).\n- 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)).\n- 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)).\n\n### Security\n- 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)).\n\n## [1.7.0] - 2024-01-16\n### Added\n- 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)).\n- 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)).\n\n### Fixed\n- 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)).\n\n### Security\n- 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)).\n- 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)).\n\n## [1.6.1] - 2023-11-21\n### Added\n- 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)).\n\n### Changed\n- 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)).\n- 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)).\n\n### Security\n- 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)).\n- 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)).\n\n## [1.6.0] - 2023-08-31\n### Added\n- 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)).\n- 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)).\n- 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)).\n\n### Changed\n- 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)).\n- 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)).\n\n### Fixed\n- 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)).\n\n### Security\n- 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)).\n\n## [1.5.0] - 2023-06-29\n### Added\n- 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)).\n- 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)).\n\n### Changed\n- 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)).\n- 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)).\n- 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)).\n- 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)).\n- 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)).\n\n### Removed\n- 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)).\n- 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)).\n\n### Fixed\n- 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)).\n\n### Security\n- 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)).\n- 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)).\n- 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)).\n- 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)).\n\n## [1.4.0] - 2023-01-23\n### Added\n- 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)).\n- 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)).\n- 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)).\n\n### Changed\n- 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)).\n- 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)).\n- 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)).\n\n### Fixed\n- 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)).\n\n### Security\n- 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)).\n- 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)).\n- 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)).\n\n## [1.3.0] - 2022-10-18\n**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.**\n\n### Added\n- 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)).\n- 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)).\n\n### Changed\n- 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)).\n- 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)).\n- 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)).\n- 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)).\n\n### Fixed\n- 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)).\n\n### Security\n- 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)).\n- 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)).\n\n## [1.2.4] - 2022-07-27\n### Added\n- 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)).\n\n### Changed\n- Bump WordPress version \"tested up to\" 6.0 (props [@cadic](https://github.com/cadic) via [#171](https://github.com/10up/simple-podcasting/issues/171)).\n\n### Fixed\n- 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)).\n\n### Security\n- 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)).\n\n## [1.2.3] - 2022-04-28\n### Added\n- 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)).\n- 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)).\n- Dependency security scanning (props [@jeffpaul](https://github.com/jeffpaul) via [#168](https://github.com/10up/simple-podcasting/pull/168)).\n\n### Changed\n- 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)).\n- 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)).\n- 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)).\n\n### Fixed\n- 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)).\n- 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)).\n- 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)).\n\n### Security\n- 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)).\n- 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)).\n- 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)).\n- 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)).\n\n## [1.2.2] - 2022-03-01\n### Added\n- 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)).\n- 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)).\n- 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)).\n- 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)).\n\n### Changed\n- 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)).\n\n### Fixed\n- 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)).\n- 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)).\n- 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)).\n- 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)).\n\n### Security\n- 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)).\n\n## [1.2.1] - 2021-12-16\n### Added\n- 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)).\n- 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)).\n- Issue management automation via GitHub Actions (props [@jeffpaul](https://github.com/jeffpaul) via [#119](https://github.com/10up/simple-podcasting/pull/119)).\n- Pull request template (props [@jeffpaul](https://github.com/jeffpaul), [@dinhtungdu](https://github.com/dinhtungdu) via [#125](https://github.com/10up/simple-podcasting/pull/125)).\n\n### Changed\n- 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)).\n- Use `@wordpress/scripts` as the build tool (props [@dinhtungdu](https://github.com/dinhtungdu) via [#114](https://github.com/10up/simple-podcasting/pull/114)).\n- 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)).\n- Documentation updates (props [@meszarosrob](https://github.com/meszarosrob), [@dinhtungdu](https://github.com/dinhtungdu) via [#101](https://github.com/10up/simple-podcasting/pull/101)).\n\n### Fixed\n- '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)).\n- Minimum WordPress version used by `wp-env` (props [@dinhtungdu](https://github.com/dinhtungdu) via [#122](https://github.com/10up/simple-podcasting/pull/122)).\n\n## [1.2.0] - 2020-07-10\n### Added\n- 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)).\n- 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)).\n- 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)).\n\n### Changed\n- GitHub Actions from HCL to YAML workflow syntax (props [@helen](https://github.com/helen) via [#78](https://github.com/10up/simple-podcasting/pull/78)).\n- Stop committing built files (props [@helen](https://github.com/helen) via [#95](https://github.com/10up/simple-podcasting/pull/95)).\n- 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)).\n\n### Fixed\n- 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)).\n- 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)).\n- 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)).\n- 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)).\n\n## [1.1.1] - 2019-08-02\n### Added\n- GitHub Actions for WordPress.org plugin deploy (props [@helen](https://github.com/helen) via [#75](https://github.com/10up/simple-podcasting/pull/75)).\n\n### Fixed\n- 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)).\n- Corrected `10up/wp_mock` reference for Composer (props [@oscarssanchez](https://github.com/oscarssanchez) via [#69](https://github.com/10up/simple-podcasting/pull/69)).\n\n## [1.1.0] - 2018-12-04\n### Added\n- Retrieve metadata for externally hosted audio files in the block editor.\n- Specify email address for a given podcast.\n- Set language for a given podcast.\n- Developers: Add linting for coding standards.\n\n### Changed\n- Clearer language on the add new podcast form.\n\n### Fixed\n- Delete all associated meta when block is removed from a post.\n- Restore all block editor functionality to align with Gutenberg/block changes.\n- Fully clear add new form after creating a new podcast.\n\n## [1.0.1] - 2018-07-02\n### Fixed\n- Properly output podcast categories and subcategories in the feed.\n- Avoid a minified JS error when selecting a podcast image.\n- Display podcast summary on edit form.\n\n## [1.0.0] - 2018-06-29\n- Initial plugin release.\n\n[Unreleased]: https://github.com/10up/simple-podcasting/compare/trunk...develop\n[1.9.1]: https://github.com/10up/simple-podcasting/compare/1.9.0...1.9.1\n[1.9.0]: https://github.com/10up/simple-podcasting/compare/1.8.0...1.9.0\n[1.8.0]: https://github.com/10up/simple-podcasting/compare/1.7.0...1.8.0\n[1.7.0]: https://github.com/10up/simple-podcasting/compare/1.6.1...1.7.0\n[1.6.1]: https://github.com/10up/simple-podcasting/compare/1.6.0...1.6.1\n[1.6.0]: https://github.com/10up/simple-podcasting/compare/1.5.0...1.6.0\n[1.5.0]: https://github.com/10up/simple-podcasting/compare/1.4.0...1.5.0\n[1.4.0]: https://github.com/10up/simple-podcasting/compare/1.3.0...1.4.0\n[1.3.0]: https://github.com/10up/simple-podcasting/compare/1.2.4...1.3.0\n[1.2.4]: https://github.com/10up/simple-podcasting/compare/1.2.3-deploy...1.2.4\n[1.2.3]: https://github.com/10up/simple-podcasting/compare/1.2.2...1.2.3-deploy\n[1.2.2]: https://github.com/10up/simple-podcasting/compare/1.2.1...1.2.2\n[1.2.1]: https://github.com/10up/simple-podcasting/compare/1.2.0...1.2.1\n[1.2.0]: https://github.com/10up/simple-podcasting/compare/1.1.1...1.2.0\n[1.1.1]: https://github.com/10up/simple-podcasting/compare/f8a958c...1.1.1\n[1.1.0]: https://github.com/10up/simple-podcasting/compare/1.0.1...f8a958c\n[1.0.1]: https://github.com/10up/simple-podcasting/compare/1.0.0...1.0.1\n[1.0.0]: https://github.com/10up/simple-podcasting/releases/tag/1.0.0\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as\ncontributors and maintainers pledge to making participation in our project and\nour community a harassment-free experience for everyone, regardless of age, body\nsize, disability, ethnicity, sex characteristics, gender identity and expression,\nlevel of experience, education, socio-economic status, nationality, personal\nappearance, race, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or\n advances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic\n address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable\nbehavior and are expected to take appropriate and fair corrective action in\nresponse to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or\nreject comments, commits, code, wiki edits, issues, and other contributions\nthat are not aligned to this Code of Conduct, or to ban temporarily or\npermanently any contributor for other behaviors that they deem inappropriate,\nthreatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces\nwhen an individual is representing the project or its community. Examples of\nrepresenting a project or community include using an official project e-mail\naddress, posting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event. Representation of a project may be\nfurther defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported by contacting the project team at opensource@10up.com. All\ncomplaints will be reviewed and investigated and will result in a response that\nis deemed necessary and appropriate to the circumstances. The project team is\nobligated to maintain confidentiality with regard to the reporter of an incident.\nFurther details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good\nfaith may face temporary or permanent repercussions as determined by other\nmembers of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,\navailable at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see\nhttps://www.contributor-covenant.org/faq\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing and Maintaining\n\nFirst, thank you for taking the time to contribute!\n\nThe 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.\n\n## Ways to contribute\n\nContributing 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:\n\n### Reporting bugs\n\nIf 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.\n\n### Suggesting enhancements\n\nNew 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.\n\n### Pull requests\n\nPull 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.\n\nFor more on how 10up writes and manages code, check out our [10up Engineering Best Practices](https://10up.github.io/Engineering-Best-Practices/).\n\n### Testing\n\nHelping 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.\n\n## Maintenance process\n\n### Triage\n\nIssues 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:\n\n* 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.\n* Marking forum posts as resolved when corresponding issues are fixed or as not a support issue if not relevant.\n* Creating GitHub issues for WordPress.org forum posts as necessary or linking to them from existing related issues.\n* Applying labels and milestones to GitHub issues.\n\n#### Issue labels\n\nAll 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\".\n\nBugs 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:`.\n\nThere are two other labels that are GitHub defaults with more global meaning we've kept: `good first issue` and `help wanted`.\n\n### Review against WordPress updates\n\nDuring 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.\n\n### Release cycle\n\nNew 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.\n\n### Release instructions\n\n1. Branch: Starting from `develop`, cut a release branch named `release/X.Y.Z` for your changes.\n2. 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`.\n3. Changelog: Add/update the changelog in both `CHANGELOG.md` and `readme.txt`.\n4. Props: update `CREDITS.md` with any new contributors, confirm maintainers are accurate.\n5. New files: Check to be sure any new files/paths that are unnecessary in the production version are included in `.distignore`.\n6. 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.\n7. 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.\n8. Push: Push your `trunk` branch to GitHub (e.g. `git push origin trunk`).\n9. [Compare](https://github.com/10up/simple-podcasting/compare/trunk...develop) `trunk` to `develop` to ensure no additional changes were missed.\n10. 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.\n11. 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).\n12. 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.\n13. Check WordPress.org: Ensure that the changes are live on [WordPress.org](https://wordpress.org/plugins/simple-podcasting/). This may take a few minutes.\n14. 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.\n15. 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`.\n"
  },
  {
    "path": "CREDITS.md",
    "content": "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.\n\n## Maintainers\n\nThe following individuals are responsible for curating the list of issues, responding to pull requests, and ensuring regular releases happen.\n\n[Jeffrey Paul (@jeffpaul)](https://github.com/jeffpaul).\n\n## Contributors\n\nThank you to all the people who have already contributed to this repository via bug reports, code, design, ideas, project management, translation, testing, etc.\n\n[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).\n\n## Libraries\n\nThe following software libraries are utilized in this repository.\n\nn/a.\n"
  },
  {
    "path": "LICENSE.md",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Free Software Foundation, Inc.,\n 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The licenses for most software are designed to take away your\nfreedom to share and change it.  By contrast, the GNU General Public\nLicense is intended to guarantee your freedom to share and change free\nsoftware--to make sure the software is free for all its users.  This\nGeneral Public License applies to most of the Free Software\nFoundation's software and to any other program whose authors commit to\nusing it.  (Some other Free Software Foundation software is covered by\nthe GNU Lesser General Public License instead.)  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthis service if you wish), that you receive source code or can get it\nif you want it, that you can change the software or use pieces of it\nin new free programs; and that you know you can do these things.\n\n  To protect your rights, we need to make restrictions that forbid\nanyone to deny you these rights or to ask you to surrender the rights.\nThese restrictions translate to certain responsibilities for you if you\ndistribute copies of the software, or if you modify it.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must give the recipients all the rights that\nyou have.  You must make sure that they, too, receive or can get the\nsource code.  And you must show them these terms so they know their\nrights.\n\n  We protect your rights with two steps: (1) copyright the software, and\n(2) offer you this license which gives you legal permission to copy,\ndistribute and/or modify the software.\n\n  Also, for each author's protection and ours, we want to make certain\nthat everyone understands that there is no warranty for this free\nsoftware.  If the software is modified by someone else and passed on, we\nwant its recipients to know that what they have is not the original, so\nthat any problems introduced by others will not reflect on the original\nauthors' reputations.\n\n  Finally, any free program is threatened constantly by software\npatents.  We wish to avoid the danger that redistributors of a free\nprogram will individually obtain patent licenses, in effect making the\nprogram proprietary.  To prevent this, we have made it clear that any\npatent must be licensed for everyone's free use or not licensed at all.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                    GNU GENERAL PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. This License applies to any program or other work which contains\na notice placed by the copyright holder saying it may be distributed\nunder the terms of this General Public License.  The \"Program\", below,\nrefers to any such program or work, and a \"work based on the Program\"\nmeans either the Program or any derivative work under copyright law:\nthat is to say, a work containing the Program or a portion of it,\neither verbatim or with modifications and/or translated into another\nlanguage.  (Hereinafter, translation is included without limitation in\nthe term \"modification\".)  Each licensee is addressed as \"you\".\n\nActivities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope.  The act of\nrunning the Program is not restricted, and the output from the Program\nis covered only if its contents constitute a work based on the\nProgram (independent of having been made by running the Program).\nWhether that is true depends on what the Program does.\n\n  1. You may copy and distribute verbatim copies of the Program's\nsource code as you receive it, in any medium, provided that you\nconspicuously and appropriately publish on each copy an appropriate\ncopyright notice and disclaimer of warranty; keep intact all the\nnotices that refer to this License and to the absence of any warranty;\nand give any other recipients of the Program a copy of this License\nalong with the Program.\n\nYou may charge a fee for the physical act of transferring a copy, and\nyou may at your option offer warranty protection in exchange for a fee.\n\n  2. You may modify your copy or copies of the Program or any portion\nof it, thus forming a work based on the Program, and copy and\ndistribute such modifications or work under the terms of Section 1\nabove, provided that you also meet all of these conditions:\n\n    a) You must cause the modified files to carry prominent notices\n    stating that you changed the files and the date of any change.\n\n    b) You must cause any work that you distribute or publish, that in\n    whole or in part contains or is derived from the Program or any\n    part thereof, to be licensed as a whole at no charge to all third\n    parties under the terms of this License.\n\n    c) If the modified program normally reads commands interactively\n    when run, you must cause it, when started running for such\n    interactive use in the most ordinary way, to print or display an\n    announcement including an appropriate copyright notice and a\n    notice that there is no warranty (or else, saying that you provide\n    a warranty) and that users may redistribute the program under\n    these conditions, and telling the user how to view a copy of this\n    License.  (Exception: if the Program itself is interactive but\n    does not normally print such an announcement, your work based on\n    the Program is not required to print an announcement.)\n\nThese requirements apply to the modified work as a whole.  If\nidentifiable sections of that work are not derived from the Program,\nand can be reasonably considered independent and separate works in\nthemselves, then this License, and its terms, do not apply to those\nsections when you distribute them as separate works.  But when you\ndistribute the same sections as part of a whole which is a work based\non the Program, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the\nentire whole, and thus to each and every part regardless of who wrote it.\n\nThus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to\nexercise the right to control the distribution of derivative or\ncollective works based on the Program.\n\nIn addition, mere aggregation of another work not based on the Program\nwith the Program (or with a work based on the Program) on a volume of\na storage or distribution medium does not bring the other work under\nthe scope of this License.\n\n  3. You may copy and distribute the Program (or a work based on it,\nunder Section 2) in object code or executable form under the terms of\nSections 1 and 2 above provided that you also do one of the following:\n\n    a) Accompany it with the complete corresponding machine-readable\n    source code, which must be distributed under the terms of Sections\n    1 and 2 above on a medium customarily used for software interchange; or,\n\n    b) Accompany it with a written offer, valid for at least three\n    years, to give any third party, for a charge no more than your\n    cost of physically performing source distribution, a complete\n    machine-readable copy of the corresponding source code, to be\n    distributed under the terms of Sections 1 and 2 above on a medium\n    customarily used for software interchange; or,\n\n    c) Accompany it with the information you received as to the offer\n    to distribute corresponding source code.  (This alternative is\n    allowed only for noncommercial distribution and only if you\n    received the program in object code or executable form with such\n    an offer, in accord with Subsection b above.)\n\nThe source code for a work means the preferred form of the work for\nmaking modifications to it.  For an executable work, complete source\ncode means all the source code for all modules it contains, plus any\nassociated interface definition files, plus the scripts used to\ncontrol compilation and installation of the executable.  However, as a\nspecial exception, the source code distributed need not include\nanything that is normally distributed (in either source or binary\nform) with the major components (compiler, kernel, and so on) of the\noperating system on which the executable runs, unless that component\nitself accompanies the executable.\n\nIf distribution of executable or object code is made by offering\naccess to copy from a designated place, then offering equivalent\naccess to copy the source code from the same place counts as\ndistribution of the source code, even though third parties are not\ncompelled to copy the source along with the object code.\n\n  4. You may not copy, modify, sublicense, or distribute the Program\nexcept as expressly provided under this License.  Any attempt\notherwise to copy, modify, sublicense or distribute the Program is\nvoid, and will automatically terminate your rights under this License.\nHowever, parties who have received copies, or rights, from you under\nthis License will not have their licenses terminated so long as such\nparties remain in full compliance.\n\n  5. You are not required to accept this License, since you have not\nsigned it.  However, nothing else grants you permission to modify or\ndistribute the Program or its derivative works.  These actions are\nprohibited by law if you do not accept this License.  Therefore, by\nmodifying or distributing the Program (or any work based on the\nProgram), you indicate your acceptance of this License to do so, and\nall its terms and conditions for copying, distributing or modifying\nthe Program or works based on it.\n\n  6. Each time you redistribute the Program (or any work based on the\nProgram), the recipient automatically receives a license from the\noriginal licensor to copy, distribute or modify the Program subject to\nthese terms and conditions.  You may not impose any further\nrestrictions on the recipients' exercise of the rights granted herein.\nYou are not responsible for enforcing compliance by third parties to\nthis License.\n\n  7. If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues),\nconditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot\ndistribute so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you\nmay not distribute the Program at all.  For example, if a patent\nlicense would not permit royalty-free redistribution of the Program by\nall those who receive copies directly or indirectly through you, then\nthe only way you could satisfy both it and this License would be to\nrefrain entirely from distribution of the Program.\n\nIf any portion of this section is held invalid or unenforceable under\nany particular circumstance, the balance of the section is intended to\napply and the section as a whole is intended to apply in other\ncircumstances.\n\nIt is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any\nsuch claims; this section has the sole purpose of protecting the\nintegrity of the free software distribution system, which is\nimplemented by public license practices.  Many people have made\ngenerous contributions to the wide range of software distributed\nthrough that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing\nto distribute software through any other system and a licensee cannot\nimpose that choice.\n\nThis section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n\n  8. If the distribution and/or use of the Program is restricted in\ncertain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Program under this License\nmay add an explicit geographical distribution limitation excluding\nthose countries, so that distribution is permitted only in or among\ncountries not thus excluded.  In such case, this License incorporates\nthe limitation as if written in the body of this License.\n\n  9. The Free Software Foundation may publish revised and/or new versions\nof the General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\nEach version is given a distinguishing version number.  If the Program\nspecifies a version number of this License which applies to it and \"any\nlater version\", you have the option of following the terms and conditions\neither of that version or of any later version published by the Free\nSoftware Foundation.  If the Program does not specify a version number of\nthis License, you may choose any version ever published by the Free Software\nFoundation.\n\n  10. If you wish to incorporate parts of the Program into other free\nprograms whose distribution conditions are different, write to the author\nto ask for permission.  For software which is copyrighted by the Free\nSoftware Foundation, write to the Free Software Foundation; we sometimes\nmake exceptions for this.  Our decision will be guided by the two goals\nof preserving the free status of all derivatives of our free software and\nof promoting the sharing and reuse of software generally.\n\n                            NO WARRANTY\n\n  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\nFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\nOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\nPROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\nOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\nTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\nPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\nREPAIR OR CORRECTION.\n\n  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\nREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\nINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\nOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\nTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\nYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\nPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGES.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nconvey the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf the program is interactive, make it output a short notice like this\nwhen it starts in an interactive mode:\n\n    Gnomovision version 69, Copyright (C) year name of author\n    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, the commands you use may\nbe called something other than `show w' and `show c'; they could even be\nmouse-clicks or menu items--whatever suits your program.\n\nYou should also get your employer (if you work as a programmer) or your\nschool, if any, to sign a \"copyright disclaimer\" for the program, if\nnecessary.  Here is a sample; alter the names:\n\n  Yoyodyne, Inc., hereby disclaims all copyright interest in the program\n  `Gnomovision' (which makes passes at compilers) written by James Hacker.\n\n  <signature of Ty Coon>, 1 April 1989\n  Ty Coon, President of Vice\n\nThis General Public License does not permit incorporating your program into\nproprietary programs.  If your program is a subroutine library, you may\nconsider it more useful to permit linking proprietary applications with the\nlibrary.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.\n"
  },
  {
    "path": "README.md",
    "content": "# Simple Podcasting for WordPress\n\n![Simple Podcasting](https://github.com/10up/simple-podcasting/blob/develop/.wordpress-org/banner-1544x500.png)\n\n[![Support Level](https://img.shields.io/badge/support-stable-blue.svg)](#support-level) ![Required PHP Version](https://img.shields.io/wordpress/plugin/required-php/simple-podcasting?label=Requires%20PHP) ![Required WP Version](https://img.shields.io/wordpress/plugin/wp-version/simple-podcasting?label=Requires%20WordPress) ![WordPress tested up to version](https://img.shields.io/wordpress/plugin/tested/simple-podcasting?label=WordPress) [![GPLv2 License](https://img.shields.io/github/license/10up/simple-podcasting.svg)](https://github.com/10up/simple-podcasting/blob/develop/LICENSE.md) [![Dependency Review](https://github.com/10up/simple-podcasting/actions/workflows/dependency-review.yml/badge.svg)](https://github.com/10up/simple-podcasting/actions/workflows/dependency-review.yml) [![E2E Test](https://github.com/10up/simple-podcasting/actions/workflows/cypress.yml/badge.svg)](https://github.com/10up/simple-podcasting/actions/workflows/cypress.yml) [![Unit Tests](https://github.com/10up/simple-podcasting/actions/workflows/phpunit.yml/badge.svg)](https://github.com/10up/simple-podcasting/actions/workflows/phpunit.yml) [![PHPCS](https://github.com/10up/simple-podcasting/actions/workflows/phpcs.yml/badge.svg)](https://github.com/10up/simple-podcasting/actions/workflows/phpcs.yml) [![PHP Compatibility](https://github.com/10up/simple-podcasting/actions/workflows/php-compatibility.yml/badge.svg)](https://github.com/10up/simple-podcasting/actions/workflows/php-compatibility.yml) [![CodeQL](https://github.com/10up/simple-podcasting/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/10up/simple-podcasting/actions/workflows/github-code-scanning/codeql) [![WordPress Playground Demo](https://img.shields.io/wordpress/plugin/v/simple-podcasting?logo=wordpress&logoColor=FFFFFF&label=Playground%20Demo&labelColor=3858E9&color=3858E9)](https://playground.wordpress.net/?blueprint-url=https://raw.githubusercontent.com/10up/simple-podcasting/add/playground/.wordpress-org/blueprints/blueprint.json)\n\n> 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).\n\n## Overview\n\nPodcasting 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:\n\n![Screenshot of podcast block](.wordpress-org/screenshot-1.png \"Example of a podcast block in the new WordPress editor\")\n\n## Requirements\n\n* PHP 7.4+\n* [WordPress](http://wordpress.org) 6.6+\n* RSS feeds must not be disabled\n\n## Installation\n\n1. Install the plugin via the plugin installer, either by searching for it or uploading a .zip file.\n2. Activate the plugin.\n3. Head to Posts → Podcasts and add at least one podcast.\n4. 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.\n\n## Create your podcast\n\nFrom the WordPress Admin, go to Podcasts.\nTo create a podcast, complete all of the \"Add New Podcast\" fields and click \"Add New Podcast\".\n\n * Name: this title appears in Apple Podcasts and any other podcast apps.\n * Slug: this is the URL-friendly version of the Name field.\n * Subtitle: the subtitle also appears in Apple Podcasts and any other podcast apps.\n * Artist / Author name: the artist or producer of the work.\n * Podcast email: a contact email address for your podcast.\n * Summary: Apple Podcasts displays this summary when browsing through podcasts.\n * Copyright / License information: copyright information viewable in Apple Podcasts or other podcast apps.\n * Mark as explicit: mark Yes if podcast contains adult language or adult themes.\n * Language: the main language spoken in the podcast.\n * 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.\n * Keywords: add terms to help your podcast show up in search results on Apple Podcasts and other podcast apps.\n * Categories: these allow your podcast to show up for those browsing Apple Podcasts or other podcast apps by category.\n\nRepeat for each podcast you would like to create.\n\n## Add content to your podcast\n\n * Create a new post and assign it to one or more Podcasts using the panel labeled Podcasts.\n * 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.\n * 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.\n * 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.\n\n## Submit your podcast feed to Apple Podcasts\n\n* Each podcast has a unique feed URL you can find on the Podcasts page. This is the URL you will submit to Apple.\n* Ensure you test feeds before submitting them, see https://help.apple.com/itc/podcasts_connect/#/itcac471c970.\n* 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\n\nPodcast setup | Podcast in block editor | Podcast feed\n------------- | ----------------- | ------------\n[![Podcast setup](.wordpress-org/screenshot-3.png)](.wordpress-org/screenshot-3.png) | [![Podcast in editor](.wordpress-org/screenshot-1.png)](.wordpress-org/screenshot-1.png) | [![Podcast feed](.wordpress-org/screenshot-4.png)](.wordpress-org/screenshot-4.png)\n\nPodcast Platforms block | Podcast Grid pattern | Podcast Transcript block\n------------- | ----------------- | ------------\n[![Podcast Platforms block](.wordpress-org/screenshot-2.png)](.wordpress-org/screenshot-2.png) | [![Podcast Grid pattern](.wordpress-org/screenshot-5.png)](.wordpress-org/screenshot-5.png) | [![Podcast Transcript block](.wordpress-org/screenshot-6.png)](.wordpress-org/screenshot-6.png)\n\n## Submit your podcast feed to Pocket Casts\n\n* Validate your feeds at [Cast Feed Validator](https://www.castfeedvalidator.com/) before submitting them.\n* Submit the podcast feed to https://pocketcasts.com/submit/\n\n## Control how many episodes are listed on the feed\n\nIf you want to adjust the default number of episodes included in a podcast RSS feed, then utilize the following to do so...\n\n```php\n<?php\n\nadd_filter( 'simple_podcasting_episodes_per_page', 'podcasting_feed_episodes_per_page' );\n\n/**\n * Filter how many items are displayed on the feed\n * Default is 250\n *\n * @param int $qty Items count.\n * @return string\n */\nfunction podcasting_feed_episodes_per_page( $qty ) {\n\treturn 300;\n}\n```\n\n## Customize the RSS feed title\n\nThe `<title>` element of the RSS feed can be adjusted using the `simple_podcasting_feed_title` filter.\n\n```php\n<?php\n\nadd_filter( 'simple_podcasting_feed_title', 'podcasting_feed_update_feed_title', 10, 2 );\n\n/**\n * Filter the name of the of the feed channel\n *\n * @param $output Output to be modified.\n * @param $term WP_Term object representing the podcast\n * @return string\n */\nfunction podcasting_feed_update_feed_title( $output, $term ) {\n\t$term_name = $term->name;\n\n\treturn '10up Presents: ' . $term_name;\n}\n```\n\n## Customize RSS feed\n\nIf you want to modify RSS feed items output, there is a filter for that:\n\n```php\n<?php\n\nfunction podcasting_feed_item_filter( $feed_item = array(), $post_id = null, $term_id = null ) {\n\tif ( 42 === $post_id ) {\n\t\t$feed_item['keywords'] = 'one,two,three';\n\t}\n\treturn $feed_item;\n}\nadd_filter( 'simple_podcasting_feed_item', 'podcasting_feed_item_filter', 10, 3 );\n```\n\n## Frequently Asked Questions\n\n### How do I get my podcast featured on Pocket Casts?\n\nThe 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.\n\nIf you’d like to suggest your podcast for a featured spot, reach out to curation@pocketcasts.com.\n\nFor more information, [read more](https://pocketcasts.com/podcast-producers/).\n\n### How do I submit private and paid podcast feeds?\n\nFollow this documentation to submit [private and paid podcast feeds](https://support.pocketcasts.com/article/password-protected-podcasts-2/)\n\n### Where do I report security bugs found in this plugin?\n\nPlease 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.\n\n## Support Level\n\n**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.\n\n## Changelog\n\nA 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).\n\n## Contributing\n\nPlease 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.\n\n## Like what you see?\n\n<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>\n"
  },
  {
    "path": "assets/css/podcasting-edit-term.css",
    "content": ".taxonomy-podcasting_podcasts .term-parent-wrap {\n\tdisplay: none;\n}\n.podcast-image-thumbnail {\n\tmax-width: 300px;\n\tmax-height: 200px;\n}\n\n.column-podcasting_image img {\n\theight: auto;\n\tmax-width: 100%;\n\twidth: 75px;\n}\n\n.simple_podcasting__platforms img {\n\tdisplay: block;\n\tmax-width: 42px;\n\tmargin: 0 auto;\n\theight: auto;\n}\n\n.simple_podcasting__platforms th {\n\tpadding: 15px 10px;\n\ttext-align: center;\n}\n\n.simple_podcasting__platforms td {\n\tvertical-align: middle;\n\tpadding: 15px 10px;\n}\n\n.simple_podcasting__platforms-url {\n\tmin-width: 220px;\n}\n\n.simple_podcasting__platforms-icon {\n\ttransition: background-color 0.2s ease-out;\n}\n\n.simple_podcasting__platforms-icon--darken-bg {\n\tbackground-color: rgb(28 52 59 / 22%);\n\ttransition: background-color 0.2s ease-in;\n}\n"
  },
  {
    "path": "assets/css/podcasting-editor-screen.css",
    "content": ".components-input-control,\n.components-base-control {\n\twidth: 100%;\n}\n.cover-art-container {\n\tdisplay: flex;\n\tflex-direction: column;\n\tjustify-content: center;\n\talign-items: center;\n}\n.cover-art-container button {\n\tmargin: 10px 0;\n}\n"
  },
  {
    "path": "assets/css/podcasting-onboarding.scss",
    "content": "* {\n\tbox-sizing: border-box;\n}\n\n.admin_page_simple-podcasting-onboarding #wpcontent {\n\tpadding-left: 0;\n}\n\n#simple-podcasting {\n\t&__onboarding-header {\n\t\twidth: 100%;\n\t\theight: 79px;\n\t\tpadding: 0 1.75rem;\n\n\t\tdisplay: flex;\n\t\tjustify-content: space-between;\n\t\talign-items: center;\n\n\t\tbackground-color: #fff;\n\t\tborder-bottom: 1px solid #c0c0c1;\n\n\t\t& > * {\n\t\t\tflex-grow: 1;\n\t\t\tflex-basis: 0;\n\t\t}\n\t}\n\n\t&__branding {\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t}\n\n\t&__header-title {\n\t\ttext-align: center;\n\t\tfont-family: 'Roboto';\n\t\tfont-style: normal;\n\t\tfont-weight: 700;\n\t\tfont-size: 17.8983px;\n\t}\n\n\t&__logo {\n\t\tmax-width: 56px;\n\t\theight: auto;\n\n\t\timg {\n\t\t\tdisplay: block;\n\t\t\twidth: 100%;\n\t\t}\n\t}\n\n\t&__plugin-name {\n\t\tmargin-left: 11px;\n\t\tfont-family: 'Roboto';\n\t\tfont-style: normal;\n\t\tfont-weight: 700;\n\t\tfont-size: 17.8983px;\n\t\tline-height: 20px;\n\t}\n\n\t&__header-controls {\n\t\t.simple-podcasting__btn {\n\t\t\tfloat: right;\n\t\t}\n\t}\n\n\t&__page-title {\n\t\tmargin-bottom: 30px;\n\t\tfont-family: 'Roboto';\n\t\tfont-style: normal;\n\t\tfont-weight: 700;\n\t\tfont-size: 22px;\n\t\tline-height: 24px;\n\t}\n\n\t&__upload-cover-image {\n\t\tmargin-top: 13px;\n\t\tmargin-bottom: 14px;\n\t}\n\n\t&__create-a-new-post-button {\n\t\tpadding: 10px 40px;\n\t\twidth: 240px;\n\n\t\tdisplay: block;\n\t\ttext-align: center;\n\t\ttext-decoration: none;\n\n\t\tcolor: #fff !important;\n\t}\n\n\t&__cover-image-preview {\n\t\timg {\n\t\t\tdisplay: block;\n\t\t\tmax-width: 256px;\n\t\t}\n\t}\n}\n\n.simple-podcasting {\n\t// Body.\n\t&__onboarding-body {\n\t\tmargin: 0 auto;\n\t\tmargin-top: 68px;\n\t\tpadding: 0 1.75rem;\n\n\t\t&--step-1 {\n\t\t\tmax-width: 524px;\n\t\t}\n\n\t\t&--step-2 {\n\t\t\tdisplay: flex;\n\t\t\tmax-width: 1038px;\n\t\t}\n\t}\n\n\t&__setting {\n\t\tmargin-bottom: 30px;\n\n\t\tinput,\n\t\ttextarea {\n\t\t\twidth: 100%;\n\t\t\tpadding: 11px 7px 10px 13px;\n\t\t\tbackground: #FFFFFF;\n\t\t\tborder: 1px solid #828282;\n\t\t\tborder-radius: 5px;\n\t\t}\n\n\t\tselect {\n\t\t\tpadding: 11px 7px 10px 13px;\n\t\t\twidth: 320px;\n\t\t\tbackground: #FFFFFF;\n\t\t\tborder: 1px solid #828282;\n\t\t\tborder-radius: 5px;\n\t\t}\n\t}\n\n\t&__setting-label {\n\t\tdisplay: block;\n\t\tfont-family: 'Roboto';\n\t\tfont-style: normal;\n\t\tfont-weight: 700;\n\t\tfont-size: 16px;\n\t\tline-height: 24px;\n\t\tmargin-bottom: 4px;\n\t}\n\n\t&__setting-description {\n\t\tfont-family: 'Roboto';\n\t\tfont-style: normal;\n\t\tfont-weight: 400;\n\t\tfont-size: 14px;\n\t\tline-height: 24px;\n\t\tcolor: #828282;\n\t\tmargin-top: 4px;\n\t}\n\n\t&__panel {\n\t\tfont-family: 'Roboto';\n\t\tfont-style: normal;\n\t\tfont-weight: 400;\n\t\tfont-size: 16px;\n\t\tline-height: 24px;\n\t\tflex-grow: 1;\n\t\tflex-basis: 0;\n\n\t\tp {\n\t\t\tfont-family: 'Roboto';\n\t\t\tfont-style: normal;\n\t\t\tfont-weight: 400;\n\t\t\tfont-size: 16px;\n\t\t\tline-height: 24px;\n\t\t}\n\n\t\t&--left {\n\t\t\tmax-width: 483px;\n\n\t\t\ta {\n\t\t\t\tcolor: #000;\n\t\t\t\tfont-weight: 700;\n\t\t\t}\n\t\t}\n\t}\n\n\t&__podcast-block-preview {\n\t\twidth: 468px;\n\t\tfloat: right;\n\t\tbox-shadow: 0px 0px 26px 8px rgba(0, 0, 0, 0.05);\n\n\t\timg {\n\t\t\tdisplay: block;\n\t\t\twidth: 100%;\n\t\t\theight: auto;\n\t\t}\n\t}\n\n\t&__step-2-controls {\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tjustify-content: space-between;\n\n\t\tmargin-top: 64px;\n\n\t\ta {\n\t\t\tletter-spacing: 1px;\n\t\t\ttext-decoration-line: underline;\n\t\t\tcolor: #4F4F4F !important;\n\t\t}\n\t}\n}\n\n.simple-podcasting__btn {\n\tbox-shadow: none;\n\tborder: 0;\n\toutline: 0;\n\tborder-radius: 3px;\n\tpadding: 12px 40px;\n\n\tfont-family: 'Roboto';\n\tfont-style: normal;\n\tfont-weight: 700;\n\tfont-size: 14px;\n\tletter-spacing: 1px;\n\n\ttext-decoration: none;\n\n\tcursor: pointer;\n\n\t&--ghost {\n\t\tborder: 1px solid #000000;\n\t\tbackground-color: rgba(0, 0, 0, 0);\n\t\tcolor: #000;\n\t}\n\n\t&--black {\n\t\tbackground: #4F4F4F;\n\t\tborder-radius: 3px;\n\t\tcolor: #FFFFFF;\n\t}\n}\n"
  },
  {
    "path": "assets/css/podcasting-transcript.css",
    "content": ".wp-block-podcasting-podcast-transcript cite,\n.wp-block-podcasting-podcast-transcript time {\n\tdisplay: block;\n}\n"
  },
  {
    "path": "assets/js/blocks/latest-episode/index.js",
    "content": "import './index.scss';\n"
  },
  {
    "path": "assets/js/blocks/latest-episode/index.scss",
    "content": ".podcasting-latest-episode {\n\tdisplay: flex;\n\tflex-direction: column;\n\tjustify-content: end;\n\tmin-height: 20rem;\n\toverflow: hidden;\n\tposition: relative;\n\n\t& .wp-block-post-featured-image {\n\t\theight: 100%;\n\t\tobject-fit: fill;\n\t\tobject-position: center;\n\t\tposition: absolute;\n\t\twidth: 100%;\n\n\t\t&::after {\n\t\t\tbackground-color: rgb(0 0 0 / 75%);\n\t\t\tcontent: \"\";\n\t\t\tdisplay: block;\n\t\t\theight: 100%;\n\t\t\tleft: 0;\n\t\t\tposition: absolute;\n\t\t\ttop: 0;\n\t\t\twidth: 100%;\n\t\t}\n\t}\n}\n\n.podcasting-latest-episode__content {\n\tcolor: #fff;\n\tpadding: 3rem;\n\tposition: relative;\n\tz-index: 1;\n\n\t@media (min-width: 768px) {\n\t\tpadding: 3rem;\n\t}\n\n\t& .wp-block-post-excerpt,\n\t& .wp-block-post-excerpt__more-text {\n\t\tmargin-top: 0.25rem;\n\t}\n\n\t& .wp-block-post-excerpt__more-link {\n\t\tcolor: #fff;\n\t}\n}\n\n.editor-styles-wrapper .wp-block-post-content .podcasting-latest-episode__content .wp-block-post-excerpt__more-link:where(:not(.wp-element-button)) {\n\tcolor: #fff;\n}\n"
  },
  {
    "path": "assets/js/blocks/podcast/index.js",
    "content": "import './index.scss';\n"
  },
  {
    "path": "assets/js/blocks/podcast/index.scss",
    "content": ".wp-block-podcasting-podcast-outer {\n    border: 1px solid #707070;\n    border-radius: 4px;\n    padding: 20px;\n}\n\n.wp-block-podcasting-podcast__container {\n    margin-bottom: 10px;\n\n    @media (min-width: 768px) {\n        display: flex;\n    }\n}\n\n.wp-block-podcasting-podcast__show-art {\n    margin-bottom: 20px;\n\n    @media (min-width: 768px) {\n        flex-basis: 100px;\n        margin-bottom: 0;\n        margin-right: 20px;\n    }\n}\n\n.wp-block-podcasting-podcast__image {\n    aspect-ratio: 1/1;\n    height: auto;\n    position: relative;\n\n    & img {\n        display: block;\n        height: 100%;\n        object-fit: cover;\n        width: 100%;\n    }\n}\n\n.wp-block-podcasting-podcast__show-title {\n    margin: 0;\n}\n\n.wp-block-podcasting-podcast__show-details {\n    color: #575757;\n    font-size: 0.875rem;\n    text-transform: uppercase;\n\n    & span {\n        display: block;\n        margin-right: 6px;\n\n        @media (min-width: 768px) {\n            display: inline;\n        }\n\n        &::after {\n\n            @media (min-width: 768px) {\n                content: '/';\n                margin-left: 6px;\n            }\n        }\n\n        &:last-child {\n            margin-right: 0;\n\n            &::after {\n                display: none;\n            }\n        }\n    }\n}\n\n.wp-block-podcasting-podcast__caption {\n    margin-bottom: 10px;\n}\n\n.wp-block-podcasting-podcast {\n    margin: 0;\n\n    & audio {\n        display: block;\n        width: 100%;\n    }\n}\n"
  },
  {
    "path": "assets/js/blocks/podcast-platforms/edit.js",
    "content": "import { useBlockProps, InspectorControls } from '@wordpress/block-editor';\nimport { useState, useEffect } from '@wordpress/element';\nimport apiFetch from '@wordpress/api-fetch';\nimport { __ } from '@wordpress/i18n';\nimport { useDebounce } from 'use-debounce';\nimport {\n\tPanel,\n\tPanelBody,\n\tPanelRow,\n\tRangeControl,\n\tSearchControl,\n\t__experimentalItemGroup as ItemGroup,\n\t__experimentalItem as Item,\n\tBaseControl,\n\tButton,\n\tButtonGroup,\n\tIcon\n} from '@wordpress/components';\n\n\nfunction Edit( props ) {\n\tconst {\n\t\tsetAttributes,\n\t\tisSelected,\n\t\tattributes: {\n\t\t\tshowId,\n\t\t\ticonSize,\n\t\t\talign,\n\t\t},\n\t} = props;\n\n\t/** State for the search text for the show name. Defaults to empty string. */\n\tconst [ searchText, setSearchText ] = useState( '' );\n\n\t/** Debounced search text so that we don't trigger useEffect() for every character change. */\n\tconst [ debouncedSearchText ] = useDebounce( searchText, 300 );\n\n\t/** Indicates when the ajax search for podcasts is completed. */\n\tconst [ isSearchCompleted, setIsSearchCompleted ] = useState( false );\n\n\t/** State for search results matched by the search text. Defaults to array. */\n\tconst [ searchResults, setSearchResults ] = useState( [] );\n\n\t/** State for the icon theme. Defaults to `color`. */\n\tconst [ iconTheme, setIconTheme ] = useState( 'color' );\n\n\t/** State for platforms returned for a specific show. Defaults to array. */\n\tconst [ platforms, setPlatforms ] = useState( [] );\n\n\t/**\n\t * Hits the `/wp/v2/search` endpoint to search for\n\t * podcast show by name.\n\t */\n\tuseEffect( () => {\n\t\tconst searchPodcastShow = async () => {\n\t\t\tsetIsSearchCompleted( false );\n\n\t\t\tif ( ! searchText.length ) {\n\t\t\t\tsetSearchResults( [] );\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t/** Query object required by `/wp/v2/search` to search for a term by name. */\n\t\t\tconst queryObject = {\n\t\t\t\tsearch: searchText,\n\t\t\t\ttype: 'term',\n\t\t\t\tsubtype: 'podcasting_podcasts'\n\t\t\t};\n\n\t\t\t/** Converts an object to query-string. */\n\t\t\tconst queryString = new URLSearchParams( queryObject ).toString();\n\n\t\t\t/** Returns the results of the search. */\n\t\t\tconst searchResults = await apiFetch( {\n\t\t\t\tpath: `/wp/v2/search?${ queryString }`,\n\t\t\t} );\n\n\t\t\tif ( ! searchResults.length ) {\n\t\t\t\tsetIsSearchCompleted( true );\n\t\t\t}\n\n\t\t\tsetSearchResults( searchResults );\n\t\t\tsetIsSearchCompleted( true );\n\t\t};\n\n\t\tsearchPodcastShow();\n\t}, [ debouncedSearchText ] );\n\n\t/**\n\t * Fetches the podcasting platforms for a show whenever\n\t * showId updates.\n\t */\n\tuseEffect( () => {\n\t\tif ( ! showId ) {\n\t\t\treturn;\n\t\t}\n\n\t\t/**\n\t\t * Responsible to fetch platforms for a show by show ID.\n\t\t * @returns void\n\t\t */\n\t\tconst fetchPlatforms = async () => {\n\t\t\tconst result = await apiFetch( {\n\t\t\t\turl: `${ ajaxurl }?show_id=${ showId }&action=get_podcast_platforms`,\n\t\t\t} );\n\n\t\t\tif ( ! result.success ) {\n\t\t\t\tsetPlatforms( [] );\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst {\n\t\t\t\tdata: { platforms, theme }\n\t\t\t} = result;\n\n\t\t\tsetPlatforms( platforms );\n\t\t\tsetIconTheme( theme );\n\t\t};\n\n\t\tfetchPlatforms();\n\t}, [ showId ] );\n\n\t/**\n\t * Handler to set the attribute showId.\n\t *\n\t * @param {Int} termId The show ID.\n\t * @returns void\n\t */\n\tconst onShowSelect = ( termId ) => {\n\t\tsetAttributes( { showId: termId } );\n\t\tsetSearchResults( [] );\n\t\tsetIsSearchCompleted( false );\n\t};\n\n\t/**\n\t * Handler to set size of the icon.\n\t *\n\t * @param {Int} size The icon size in `px`\n\t */\n\tconst setIconSize = ( size ) => {\n\t\tsetAttributes( { iconSize: size } );\n\t};\n\n\t/**\n\t * Sets the HTML attributes for the root element.\n\t */\n\tconst blockProps = useBlockProps( {\n\t\tclassName: isSelected ? 'simple-podcasting__podcast-platforms simple-podcasting__podcast-platforms--selected' : 'simple-podcasting__podcast-platforms',\n\t} );\n\n\tconst platformSlugs = Object.keys( platforms );\n\n\treturn (\n\t\t<>\n\t\t\t<InspectorControls>\n\t\t\t\t<Panel header={ __( 'Customization Controls', 'simple-podacsting' ) }>\n\t\t\t\t\t<PanelBody>\n\t\t\t\t\t\t<BaseControl label={ __( 'Icon size', 'simple-podcasting' ) }/>\n\t\t\t\t\t\t<PanelRow>\n\t\t\t\t\t\t\t<RangeControl\n\t\t\t\t\t\t\t\tmin={ 16 }\n\t\t\t\t\t\t\t\tmax={ 96 }\n\t\t\t\t\t\t\t\tstep={ 16 }\n\t\t\t\t\t\t\t\tvalue={ iconSize }\n\t\t\t\t\t\t\t\tonChange={ setIconSize }\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</PanelRow>\n\t\t\t\t\t\t<BaseControl label={ __( 'Alignment', 'simple-podcasting' ) }/>\n\t\t\t\t\t\t<PanelRow>\n\t\t\t\t\t\t\t<ButtonGroup>\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\tisPressed={ align === 'left' }\n\t\t\t\t\t\t\t\t\tvariant='ternary'\n\t\t\t\t\t\t\t\t\ticon={ <Icon icon='align-left' /> }\n\t\t\t\t\t\t\t\t\tonClick={ () => setAttributes( { align: 'left' } ) }\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\tisPressed={ align === 'center' }\n\t\t\t\t\t\t\t\t\tvariant='ternary'\n\t\t\t\t\t\t\t\t\ticon={ <Icon icon='align-center' /> }\n\t\t\t\t\t\t\t\t\tonClick={ () => setAttributes( { align: 'center' } ) }\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\tisPressed={ align === 'right' }\n\t\t\t\t\t\t\t\t\tvariant='ternary'\n\t\t\t\t\t\t\t\t\ticon={ <Icon icon='align-right' /> }\n\t\t\t\t\t\t\t\t\tonClick={ () => setAttributes( { align: 'right' } ) }\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</ButtonGroup>\n\t\t\t\t\t\t</PanelRow>\n\t\t\t\t\t</PanelBody>\n\t\t\t\t</Panel>\n\t\t\t</InspectorControls>\n\t\t\t<div { ...blockProps }>\n\t\t\t\t{\n\t\t\t\t\tplatformSlugs.length ? (\n\t\t\t\t\t\t<div className={ `simple-podcasting__podcasting-platform-list simple-podcasting__podcasting-platform-list--${ align }` }>\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tplatformSlugs.map( ( platform, index ) => {\n\t\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t\t<span key={ index } className='simple-podcasting__podcasting-platform-list-item'>\n\t\t\t\t\t\t\t\t\t\t\t<a href={ platforms[ platform ] } target=\"_blank\">\n\t\t\t\t\t\t\t\t\t\t\t\t<img className={ `simple-pocasting__icon-size--${ iconSize }` } src={ `${ podcastingPlatformVars.podcastingUrl }dist/images/icons/${ platform }/${ iconTheme }-100.png` } />\n\t\t\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t} )\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<div className={ `simple-podcasting__podcasting-platform-list` }>\n\t\t\t\t\t\t\t<p>{ __( 'No platforms are set for this podcast.', 'simple-podcasting' ) }</p>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\t{\n\t\t\t\t\tisSelected || ! showId ? (\n\t\t\t\t\t\t<div className='simple-podcasting__podcasting-search-controls'>\n\t\t\t\t\t\t\t<SearchControl\n\t\t\t\t\t\t\t\tplaceholder={ __( 'Search a Podcast Show', 'simple-podcasting' ) }\n\t\t\t\t\t\t\t\tonChange={ ( searchText ) => setSearchText( searchText ) }\n\t\t\t\t\t\t\t\tvalue={ searchText }\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tsearchResults.length ?\n\t\t\t\t\t\t\t\t(\n\t\t\t\t\t\t\t\t\t<div className='simple-podcasting__podcasting-search-results'>\n\t\t\t\t\t\t\t\t\t\t<ItemGroup\n\t\t\t\t\t\t\t\t\t\t\tisSeparated\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\tsearchResults.map( ( result ) => (\n\t\t\t\t\t\t\t\t\t\t\t\t\t<Item\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tkey={ result.id }\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclassName='simple-podcasting__podcast-search-results'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tonClick={ () => onShowSelect( result.id ) }\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{ result.title }\n\t\t\t\t\t\t\t\t\t\t\t\t\t</Item>\n\t\t\t\t\t\t\t\t\t\t\t\t) )\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t</ItemGroup>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t! searchResults.length && isSearchCompleted ? (\n\t\t\t\t\t\t\t\t\t\t<div className='simple-podcasting__podcasting-search-results'>\n\t\t\t\t\t\t\t\t\t\t\t<ItemGroup\n\t\t\t\t\t\t\t\t\t\t\t\tisSeparated\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t<Item>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{ __( 'No results found.' , 'simple-podcasting') }\n\t\t\t\t\t\t\t\t\t\t\t\t</Item>\n\t\t\t\t\t\t\t\t\t\t\t</ItemGroup>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t) : null\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t) : null\n\t\t\t\t}\n\t\t\t</div>\n\t\t</>\n\t)\n}\n\nexport default Edit;"
  },
  {
    "path": "assets/js/blocks/podcast-platforms/index.js",
    "content": "/**\n * Internal block libraries\n */\nimport { __ } from '@wordpress/i18n';\nimport { registerBlockType } from '@wordpress/blocks';\n\nimport Edit from './edit';\nimport './index.scss';\n\n/**\n * Register Podcast Platforms block\n */\nexport default registerBlockType(\n\t'podcasting/podcast-platforms',\n\t{\n\t\ttitle: __( 'Podcast Platforms', 'simple-podcasting' ),\n\t\tdescription: __( 'Displays the list of platforms where the selected show is available.', 'simple-podcasting' ),\n\t\tcategory: 'common',\n\t\ticon: 'microphone',\n\t\tsupports: {\n\t\t\tmultiple: false,\n\t\t},\n\t\tattributes: {\n\t\t\tshowId: {\n\t\t\t\ttype: 'number',\n\t\t\t\tdefault: 0,\n\t\t\t},\n\t\t\ticonSize: {\n\t\t\t\ttype: 'number',\n\t\t\t\tdefault: 48,\n\t\t\t},\n\t\t\talign: {\n\t\t\t\ttype: 'string',\n\t\t\t\tdefault: 'center',\n\t\t\t}\n\t\t},\n\n\t\tedit: Edit,\n\n\t\tsave: () => null,\n\t},\n);\n"
  },
  {
    "path": "assets/js/blocks/podcast-platforms/index.scss",
    "content": ".wp-block {\n\t.simple-podcasting__podcasting-platform-list {\n\t\tmargin-bottom: 3rem;\n\t}\n\n\t.simple-podcasting__podcast-platforms {\n\t\tpadding: 2.5rem 1rem 1rem 1rem;\n\t}\n}\n\n.simple-podcasting {\n\t&__podcast-platforms {\n\t\tpadding: 1rem 0 1rem 0;\n\t}\n\n\t&__podcast-search-results {\n\t\tcursor: pointer;\n\t}\n\n\t&__select-show-popover {\n\t\tmax-width: 400px;\n\t\twidth: 100%;\n\n\t\t.components-popover__content {\n\t\t\twidth: 100%;\n\t\t\tpadding: 1rem;\n\t\t}\n\t}\n\n\t&__podcasting-platform-list {\n\t\tdisplay: flex;\n\t\tflex-flow: row wrap;\n\n\t\t&--left {\n\t\t\tjustify-content: start;\n\t\t}\n\n\t\t&--center {\n\t\t\tjustify-content: center;\n\t\t}\n\n\t\t&--right {\n\t\t\tjustify-content: end;\n\t\t}\n\n\t\ta {\n\t\t\ttext-decoration: none !important;\n\t\t}\n\n\t\timg {\n\t\t\tdisplay: block;\n\n\t\t\t@for $i from 1 through 6 {\n\t\t\t\t&.simple-pocasting__icon-size--#{$i * 16} {\n\t\t\t\t\tmax-width: #{$i * 16}px;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t&__podcasting-platform-list-item {\n\t\tdisplay: flex;\n\t\talign-self: center;\n\t\tpadding: 0.75rem;\n\n\t\t@media screen and ( max-width: 480px ) {\n\t\t\tjustify-content: center;\n\t\t\twidth: 25%;\n\t\t}\n\t}\n\n\t&__podcasting-search-controls {\n\t\tposition: relative;\n\n\t\tinput[type=\"search\"] {\n\t\t\tfont-size: 1rem;\n\t\t}\n\t}\n\n\t&__podcasting-search-results {\n\t\tposition: absolute;\n\t\tborder: 1px solid;\n\t\tbackground-color: #fff;\n\t\tmin-width: 200px;\n\t\tz-index: 10;\n\t}\n}\n"
  },
  {
    "path": "assets/js/blocks.js",
    "content": "/**\n * Internal block libraries\n */\nimport { __ } from '@wordpress/i18n';\nimport { registerBlockType, registerBlockVariation } from '@wordpress/blocks';\n\n// Split the Edit component out.\nimport Edit from './edit';\nimport transforms from './transforms';\nimport deprecated from './deprecated';\nimport '../css/podcasting-editor-screen.css';\n\n/**\n * Register example block\n */\nexport default registerBlockType(\n\t'podcasting/podcast',\n\t{\n\t\ttitle: __( 'Podcast', 'simple-podcasting' ),\n\t\tdescription: __( 'Insert a podcast episode into a post. To add it to a podcast feed, select a podcast in document settings.', 'simple-podcasting' ),\n\t\tcategory: 'common',\n\t\ticon: 'microphone',\n\t\tsupports: {\n\t\t\tmultiple: false,\n\t\t},\n\t\tattributes: {\n\t\t\tid: {\n\t\t\t\ttype: 'number',\n\t\t\t},\n\t\t\tsrc: {\n\t\t\t\ttype: 'string',\n\t\t\t\tsource: 'attribute',\n\t\t\t\tselector: 'audio',\n\t\t\t\tattribute: 'src',\n\t\t\t},\n\t\t\turl: {\n\t\t\t\ttype: 'string',\n\t\t\t\tsource: 'meta',\n\t\t\t\tmeta: 'podcast_url',\n\t\t\t},\n\t\t\tfilesize: {\n\t\t\t\ttype: 'number',\n\t\t\t\tsource: 'meta',\n\t\t\t\tmeta: 'podcast_filesize',\n\t\t\t},\n\t\t\tduration: {\n\t\t\t\ttype: 'string',\n\t\t\t\tsource: 'meta',\n\t\t\t\tmeta: 'podcast_duration',\n\t\t\t},\n\t\t\tmime: {\n\t\t\t\ttype: 'string',\n\t\t\t\tsource: 'meta',\n\t\t\t\tmeta: 'podcast_mime',\n\t\t\t},\n\t\t\tcaption: {\n\t\t\t\ttype: 'array',\n\t\t\t\tsource: 'children',\n\t\t\t\tselector: 'figcaption',\n\t\t\t},\n\t\t\tcaptioned: {\n\t\t\t\ttype: 'boolean',\n\t\t\t\tsource: 'meta',\n\t\t\t\tmeta: 'podcast_captioned',\n\t\t\t\tdefault: false,\n\t\t\t},\n\t\t\texplicit: {\n\t\t\t\ttype: 'string',\n\t\t\t\tsource: 'meta',\n\t\t\t\tmeta: 'podcast_explicit',\n\t\t\t},\n\t\t\tenclosure: {\n\t\t\t\ttype: 'string',\n\t\t\t\tsource: 'meta',\n\t\t\t\tmeta: 'enclosure',\n\t\t\t},\n\t\t\tseasonNumber: {\n\t\t\t\ttype: 'string',\n\t\t\t\tsource: 'meta',\n\t\t\t\tmeta: 'podcast_season_number',\n\t\t\t},\n\t\t\tepisodeNumber: {\n\t\t\t\ttype: 'string',\n\t\t\t\tsource: 'meta',\n\t\t\t\tmeta: 'podcast_episode_number',\n\t\t\t},\n\t\t\tepisodeType: {\n\t\t\t\ttype: 'string',\n\t\t\t\tsource: 'meta',\n\t\t\t\tmeta: 'podcast_episode_type',\n\t\t\t},\n\t\t\tdisplayDuration: {\n\t\t\t\ttype: 'boolean',\n\t\t\t\tdefault: false,\n\t\t\t},\n\t\t\tdisplayShowTitle: {\n\t\t\t\ttype: 'boolean',\n\t\t\t\tdefault: false,\n\t\t\t},\n\t\t\tdisplayEpisodeTitle: {\n\t\t\t\ttype: 'boolean',\n\t\t\t\tdefault: false,\n\t\t\t},\n\t\t\tdisplayArt: {\n\t\t\t\ttype: 'boolean',\n\t\t\t\tdefault: false,\n\t\t\t},\n\t\t\tdisplayExplicitBadge: {\n\t\t\t\ttype: 'boolean',\n\t\t\t\tdefault: false,\n\t\t\t},\n\t\t\tdisplaySeasonNumber: {\n\t\t\t\ttype: 'boolean',\n\t\t\t\tdefault: false,\n\t\t\t},\n\t\t\tdisplayEpisodeNumber: {\n\t\t\t\ttype: 'boolean',\n\t\t\t\tdefault: false,\n\t\t\t},\n\t\t\tdisplayEpisodeType: {\n\t\t\t\ttype: 'boolean',\n\t\t\t\tdefault: false,\n\t\t\t}\n\t\t},\n\t\ttransforms,\n\n\t\tedit: Edit,\n\n\t\tsave: props => {\n\t\t\tconst {\n\t\t\t\tid,\n\t\t\t\tsrc,\n\t\t\t\tcaption\n\t\t\t} = props.attributes;\n\n\t\t\treturn (\n\t\t\t\t<figure className={ id ? `podcast-${ id }` : null }>\n\t\t\t\t\t{ caption && caption.length > 0 && <figcaption className=\"wp-block-podcasting-podcast__caption\">{ caption }</figcaption> }\n\t\t\t\t\t<audio controls=\"controls\" src={ src } />\n\t\t\t\t</figure>\n\t\t\t);\n\t\t},\n\t\tdeprecated,\n\t},\n);\n\nconst VARIATION_NAME = 'podcasting/latest-episode';\n\nregisterBlockVariation('core/query', {\n\tname: VARIATION_NAME,\n\ttitle: 'Latest Podcast Episode',\n\tdescription: 'Displays the latest podcast episode.',\n\tisActive: ['simple-podcasting'],\n\ticon: 'microphone',\n\tattributes: {\n\t\tnamespace: VARIATION_NAME,\n\t\tquery: {\n\t\t\tpostType: 'post',\n\t\t\tpodcastingQuery: 'not_empty',\n\t\t},\n\t},\n\tallowedControls: [ ],\n\tscope: [ 'inserter' ],\n\tinnerBlocks: [\n\t\t[\n\t\t\t'core/post-template',\n\t\t\t{},\n\t\t\t[ [\n\t\t\t\t'core/group',\n\t\t\t\t{ className: 'podcasting-latest-episode' },\n\t\t\t\t[\n\t\t\t\t\t[ 'core/post-featured-image' ],\n\t\t\t\t\t[ 'core/group', { className: 'podcasting-latest-episode__content' }, [\n\t\t\t\t\t\t[ 'core/post-title' ], [ 'core/post-date' ], [ 'core/post-excerpt' ]\n\t\t\t\t\t] ],\n\t\t\t\t]\n\t\t\t] ],\n\t\t],\n\t\t[ 'core/query-no-results' ],\n\t],\n});\n"
  },
  {
    "path": "assets/js/create-podcast-show.js",
    "content": "import { registerPlugin } from \"@wordpress/plugins\";\nimport { store as editPostStore } from '@wordpress/edit-post';\nimport { __ } from \"@wordpress/i18n\";\nimport {\n\tButton,\n\tModal,\n\tTextControl,\n\tSelectControl,\n\tTextareaControl,\n\tBaseControl,\n\tFlex,\n\tFlexItem,\n\tCheckboxControl,\n} from \"@wordpress/components\";\nimport { MediaUpload, MediaUploadCheck } from '@wordpress/block-editor';\nimport { useState, useEffect } from \"@wordpress/element\";\nimport { useSelect, dispatch } from \"@wordpress/data\";\n\n// Once WordPress 6.6 becomes our minimum, change this back to `import { PluginDocumentSettingPanel } from '@wordpress/editor';`.\nconst PluginDocumentSettingPanel = wp.editor?.PluginDocumentSettingPanel ?? ( wp.editPost?.PluginDocumentSettingPanel ?? wp.editSite?.PluginDocumentSettingPanel );\n\n// Due to unsupported versions of React, we're importing stores from the\n// `wp` namespace instead of @wordpress NPM packages for the following.\nconst { store: editorStore } = wp.editor;\nconst { store: coreDataStore } = wp.coreData;\nconst DEFAULT_QUERY = {\n\tper_page: -1,\n\torderby: 'name',\n\torder: 'asc',\n\t_fields: 'id,name,parent',\n\tcontext: 'view',\n};\n\nconst CreatePodcastShowModal = ( { isModalOpen, closeModal } ) => {\n\tconst [ showName, setShowName ] = useState( '' );\n\tconst [ artistName, setArtistName ] = useState( '' );\n\tconst [ showCategory, setShowCategory ] = useState( '' );\n\tconst [ summary, setSummary ] = useState( '' );\n\tconst [ coverId, setCoverId ] = useState( 0 );\n\tconst [ coverUrl, setCoverUrl ] = useState( '' );\n\tconst [ ajaxInprogress, setAjaxInProgress ] = useState( false );\n\tconst [ isPodcastCreated, setIsPodcastCreated ] = useState( false );\n\n\tconst modalStyle = {\n\t\tmaxWidth: '645px',\n\t\twidth: '100%'\n\t};\n\n\tconst fieldStyle = {\n\t\tmarginBottom: '26px',\n\t};\n\n\tconst createShow = async () => {\n\t\tsetAjaxInProgress( true );\n\n\t\ttry {\n\t\t\tconst podcast = await wp.data.dispatch( coreDataStore ).saveEntityRecord(\n\t\t\t\t'taxonomy',\n\t\t\t\t'podcasting_podcasts',\n\t\t\t\t{\n\t\t\t\t\tname: showName,\n\t\t\t\t\tmeta: {\n\t\t\t\t\t\tpodcasting_summary: summary,\n\t\t\t\t\t\tpodcasting_category_1: showCategory,\n\t\t\t\t\t\tpodcasting_image: coverId,\n\t\t\t\t\t\tpodcasting_image_url: coverUrl,\n\t\t\t\t\t\tpodcasting_talent_name: artistName,\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t);\n\n\t\t\tif ( podcast ) {\n\t\t\t\tsetIsPodcastCreated( true );\n\t\t\t}\n\t\t} catch ( error ) {\n\t\t\tsetAjaxInProgress( false );\n\t\t}\n\n\t\tsetAjaxInProgress( false );\n\t};\n\n\tif ( ! isModalOpen ) {\n\t\treturn false;\n\t}\n\n\tconst categoriesOptions = Object.keys( podcastingShowPluginVars.categories ).map( key => ( { value: key, label: podcastingShowPluginVars.categories[key] } ) );\n\n\treturn (\n\t\t<Modal\n\t\t\ttitle={ isPodcastCreated ? __( 'Podcast created!', 'simple-podcasting' ) : __( 'Add New Podcast', 'simple-podcasting' ) }\n\t\t\tstyle={ modalStyle }\n\t\t\tonRequestClose={ ( event ) => {\n\t\t\t\tconst selectImageBtn = event.target.closest( '.podcasting__select-image-btn' );\n\n\t\t\t\tif ( selectImageBtn ) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tcloseModal();\n\t\t\t} }\n\t\t>\n\t\t\t{\n\t\t\t\tisPodcastCreated ? (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tvariant=\"link\"\n\t\t\t\t\t\t\ttext={ __( 'Add another Podcast', 'simple-podcasting' ) }\n\t\t\t\t\t\t\tonClick={ () => {\n\t\t\t\t\t\t\t\tsetIsPodcastCreated( false );\n\t\t\t\t\t\t\t\tsetShowName( '' );\n\t\t\t\t\t\t\t\tsetShowCategory( '' );\n\t\t\t\t\t\t\t\tsetSummary( '' );\n\t\t\t\t\t\t\t\tsetCoverId( 0 );\n\t\t\t\t\t\t\t\tsetCoverUrl( '' );\n\t\t\t\t\t\t\t} }\n\t\t\t\t\t\t/>\n\t\t\t\t\t</>\n\t\t\t\t) : (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<div className=\"podcasting__modal-field-row\" style={ fieldStyle }>\n\t\t\t\t\t\t\t<TextControl\n\t\t\t\t\t\t\t\tclassName=\"podcasting__modal-name-field\"\n\t\t\t\t\t\t\t\tlabel={ __( 'Podcast name*', 'simple-podcasting' ) }\n\t\t\t\t\t\t\t\thelp={ __( 'This is the name that listeners will see when searching or subscribing.', 'simple-podcasting' ) }\n\t\t\t\t\t\t\t\tvalue={ showName }\n\t\t\t\t\t\t\t\tonChange={ ( val ) => setShowName( val ) }\n\t\t\t\t\t\t\t\trequired\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<div className=\"podcasting__modal-field-row\" style={ fieldStyle }>\n\t\t\t\t\t\t\t<TextControl\n\t\t\t\t\t\t\t\tclassName=\"podcasting__modal-artist-field\"\n\t\t\t\t\t\t\t\tlabel={ __( 'Artist name*', 'simple-podcasting' ) }\n\t\t\t\t\t\t\t\thelp={ __( 'Who’s the artist or author of your podcast show that listeners will see?', 'simple-podcasting' ) }\n\t\t\t\t\t\t\t\tvalue={ artistName }\n\t\t\t\t\t\t\t\tonChange={ ( val ) => setArtistName( val ) }\n\t\t\t\t\t\t\t\trequired\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<div className=\"podcasting__modal-field-row\" style={ fieldStyle }>\n\t\t\t\t\t\t\t<SelectControl\n\t\t\t\t\t\t\t\tclassName=\"podcasting__modal-category-field\"\n\t\t\t\t\t\t\t\tlabel={ __( 'Category*', 'simple-podcasting' ) }\n\t\t\t\t\t\t\t\thelp={ __( 'Select the category listeners will use to discover your show when browsing  podcatchers. You can also add subcategories later.', 'simple-podcasting' ) }\n\t\t\t\t\t\t\t\toptions={ categoriesOptions }\n\t\t\t\t\t\t\t\tvalue={ showCategory }\n\t\t\t\t\t\t\t\tonChange={ ( val ) => setShowCategory( val ) }\n\t\t\t\t\t\t\t\trequired\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<div className=\"podcasting__modal-field-row\" style={ fieldStyle }>\n\t\t\t\t\t\t\t<TextareaControl\n\t\t\t\t\t\t\t\tclassName=\"podcasting__modal-summary-field\"\n\t\t\t\t\t\t\t\tlabel={ __( 'Summary*', 'simple-podcasting' ) }\n\t\t\t\t\t\t\t\thelp={ __( 'Briefly describe to your listeners what your show is about. (No HTML please.)', 'simple-podcasting' ) }\n\t\t\t\t\t\t\t\trows={ 6 }\n\t\t\t\t\t\t\t\tvalue={ summary }\n\t\t\t\t\t\t\t\tonChange={ ( val ) => setSummary( val ) }\n\t\t\t\t\t\t\t\trequired\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<div className=\"podcasting__modal-field-row\" style={ fieldStyle }>\n\t\t\t\t\t\t\t<MediaUploadCheck>\n\t\t\t\t\t\t\t\t<MediaUpload\n\t\t\t\t\t\t\t\t\tonSelect={ ( media ) => {\n\t\t\t\t\t\t\t\t\t\tsetCoverId( media.id );\n\t\t\t\t\t\t\t\t\t\tsetCoverUrl( media.url );\n\t\t\t\t\t\t\t\t\t} }\n\t\t\t\t\t\t\t\t\tallowedTypes={ [ 'image' ] }\n\t\t\t\t\t\t\t\t\tvalue={ coverId }\n\t\t\t\t\t\t\t\t\trender={ ( { open } ) => (\n\t\t\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t\t\t<BaseControl label={ __( 'Cover Image*', 'simple-podcasting' ) } />\n\t\t\t\t\t\t\t\t\t\t\t<Flex justify=\"normal\">\n\t\t\t\t\t\t\t\t\t\t\t\t<FlexItem>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"podcasting__select-image-btn\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttext={ coverId ? __( 'Replace Image', 'simple-podcasting' ) : __( 'Select Image', 'simple-podcasting' ) }\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tonClick={ open }\n\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t</FlexItem>\n\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\tcoverId ? (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<FlexItem>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"podcasting__remove-image-btn\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttext={ __( 'Remove', 'simple-podcasting' ) }\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tisDestructive\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonClick={ () => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetCoverId( 0 );\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetCoverUrl( '' );\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t} }\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</FlexItem>\n\t\t\t\t\t\t\t\t\t\t\t\t\t) : null\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t</Flex>\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\tcoverId ? (\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"podcasting-cover-preview\" style={ {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tmaxWidth: '256px',\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tmarginTop: '1rem',\n\t\t\t\t\t\t\t\t\t\t\t\t\t} }>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<img src={ coverUrl } style={ { width: '100%' } } />\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t) : null\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t<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' ) } />\n\t\t\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t\t\t) }\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</MediaUploadCheck>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<Flex justify=\"normal\" gap={ 9 }>\n\t\t\t\t\t\t\t<FlexItem>\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\tclassName=\"podcasting__create-podcast-btn\"\n\t\t\t\t\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t\t\t\t\ttext={ __( 'Create Podcast', 'simple-podcasting' ) }\n\t\t\t\t\t\t\t\t\tdisabled={ ! showName || '' === showCategory || ! artistName || ! summary || ! coverId }\n\t\t\t\t\t\t\t\t\tonClick={ createShow }\n\t\t\t\t\t\t\t\t\tisBusy={ ajaxInprogress }\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</FlexItem>\n\t\t\t\t\t\t\t<FlexItem>\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\tvariant=\"link\"\n\t\t\t\t\t\t\t\t\ttext={ __( 'Cancel', 'simple-podcasting' ) }\n\t\t\t\t\t\t\t\t\tonClick={ closeModal }\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</FlexItem>\n\t\t\t\t\t\t</Flex>\n\t\t\t\t\t</>\n\t\t\t\t)\n\t\t\t}\n\t\t</Modal>\n\t);\n};\n\nconst CreatePodcastShowPlugin = () => {\n\tconst { allPodcasts, attachedPodcasts, currentPostId } = useSelect( ( select ) => {\n\t\tconst { getEntityRecords } = select( coreDataStore );\n\t\tconst { getCurrentPostId } = select( editorStore );\n\n\t\treturn {\n\t\t\tallPodcasts: getEntityRecords( 'taxonomy', 'podcasting_podcasts', DEFAULT_QUERY ) || [],\n\t\t\tattachedPodcasts: getEntityRecords( 'taxonomy', 'podcasting_podcasts', { post: getCurrentPostId() } ) || [],\n\t\t\tcurrentPostId: getCurrentPostId(),\n\t\t}\n\t} );\n\n\t// Remove the default 'Podcast' taxonomy panel.\n\tuseEffect( () => {\n\t\tdispatch( editPostStore ).removeEditorPanel( 'taxonomy-panel-podcasting_podcasts' );\n\t}, [] );\n\n\tconst [ isModalOpen, setIsModalOpen ] = useState( false );\n\tconst openModal = () => setIsModalOpen( true );\n\tconst closeModal = () => setIsModalOpen( false );\n\tconst attachedPodcastIds = useSelect( ( select ) => {\n\t\treturn select( 'core/editor' ).getEditedPostAttribute( 'podcasting_podcasts' );\n\t} );\n\n\t/**\n\t * Attaches the podcast term to the current post if selected.\n\t *\n\t * @param {Boolean} isChecked If the podcast term checkbox is checked.\n\t * @param {Integer} podcastId The podcast term ID.\n\t */\n\tfunction attachPodcastToPost( isChecked, podcastId ) {\n\t\tlet updatedAttachedPodcastIds = [ ...attachedPodcastIds, podcastId ];\n\n\t\tif ( isChecked ) {\n\t\t\tupdatedAttachedPodcastIds = [ ...attachedPodcastIds, podcastId ];\n\t\t} else {\n\t\t\tupdatedAttachedPodcastIds = attachedPodcastIds.filter( ( currentPodcastId ) => currentPodcastId !== podcastId );\n\t\t}\n\n\t\tdispatch( coreDataStore ).editEntityRecord(\n\t\t\t'postType',\n\t\t\t'post',\n\t\t\tcurrentPostId,\n\t\t\t{\n\t\t\t\tpodcasting_podcasts: updatedAttachedPodcastIds,\n\t\t\t}\n\t\t)\n\t}\n\n\treturn (\n\t\t<>\n\t\t\t<PluginDocumentSettingPanel\n\t\t\t\ttitle={ __( 'Podcasts', 'simple-podcasting' ) }\n\t\t\t\tclassName='podcasting__podcast-list'\n\t\t\t>\n\t\t\t\t{\n\t\t\t\t\tallPodcasts.map( ( item, index ) => {\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<CheckboxControl\n\t\t\t\t\t\t\t\tclassName=\"podcasting__podcast-list-item\"\n\t\t\t\t\t\t\t\tkey={ index }\n\t\t\t\t\t\t\t\tlabel={ item.name }\n\t\t\t\t\t\t\t\tonChange={ ( isChecked ) => attachPodcastToPost( isChecked, item.id ) }\n\t\t\t\t\t\t\t\tchecked={ attachedPodcastIds.includes( item.id ) }\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t)\n\t\t\t\t\t} )\n\t\t\t\t}\n\t\t\t\t<Button\n\t\t\t\t\tvariant=\"link\"\n\t\t\t\t\ttext={ __( 'Add New Podcast', 'simple-podcasting' ) }\n\t\t\t\t\tonClick={ openModal }\n\t\t\t\t\tstyle={ { marginTop: '12px' } }\n\t\t\t\t\tclassName=\"podcasting__add-new-podcast\"\n\t\t\t\t/>\n\t\t\t</PluginDocumentSettingPanel>\n\t\t\t<CreatePodcastShowModal\n\t\t\t\tisModalOpen={ isModalOpen }\n\t\t\t\topenModal={ openModal }\n\t\t\t\tcloseModal={ closeModal }\n\t\t\t/>\n\t\t</>\n\t)\n};\n\nregisterPlugin( 'podcasting-create-podcast-show', {\n\trender: CreatePodcastShowPlugin\n} );\n"
  },
  {
    "path": "assets/js/deprecated.js",
    "content": "export default [\n\t{\n\t\tattributes: {\n\t\t\tid: {\n\t\t\t\ttype: 'number',\n\t\t\t},\n\t\t\tsrc: {\n\t\t\t\ttype: 'string',\n\t\t\t\tsource: 'attribute',\n\t\t\t\tselector: 'audio',\n\t\t\t\tattribute: 'src',\n\t\t\t},\n\t\t\turl: {\n\t\t\t\ttype: 'string',\n\t\t\t\tsource: 'meta',\n\t\t\t\tmeta: 'podcast_url',\n\t\t\t},\n\t\t\tfilesize: {\n\t\t\t\ttype: 'number',\n\t\t\t\tsource: 'meta',\n\t\t\t\tmeta: 'podcast_filesize',\n\t\t\t},\n\t\t\tduration: {\n\t\t\t\ttype: 'string',\n\t\t\t\tsource: 'meta',\n\t\t\t\tmeta: 'podcast_duration',\n\t\t\t},\n\t\t\tmime: {\n\t\t\t\ttype: 'string',\n\t\t\t\tsource: 'meta',\n\t\t\t\tmeta: 'podcast_mime',\n\t\t\t},\n\t\t\tcaption: {\n\t\t\t\ttype: 'array',\n\t\t\t\tsource: 'children',\n\t\t\t\tselector: 'figcaption',\n\t\t\t},\n\t\t\tcaptioned: {\n\t\t\t\ttype: 'boolean',\n\t\t\t\tsource: 'meta',\n\t\t\t\tmeta: 'podcast_captioned',\n\t\t\t\tdefault: false,\n\t\t\t},\n\t\t\texplicit: {\n\t\t\t\ttype: 'string',\n\t\t\t\tsource: 'meta',\n\t\t\t\tmeta: 'podcast_explicit',\n\t\t\t\tdefault: 'no',\n\t\t\t},\n\t\t\tenclosure: {\n\t\t\t\ttype: 'string',\n\t\t\t\tsource: 'meta',\n\t\t\t\tmeta: 'enclosure',\n\t\t\t},\n\t\t\tseasonNumber: {\n\t\t\t\ttype: 'string',\n\t\t\t\tsource: 'meta',\n\t\t\t\tmeta: 'podcast_season_number',\n\t\t\t},\n\t\t\tepisodeNumber: {\n\t\t\t\ttype: 'string',\n\t\t\t\tsource: 'meta',\n\t\t\t\tmeta: 'podcast_episode_number',\n\t\t\t},\n\t\t\tepisodeType: {\n\t\t\t\ttype: 'string',\n\t\t\t\tsource: 'meta',\n\t\t\t\tmeta: 'podcast_episode_type',\n\t\t\t}\n\t\t},\n\t\tsupports: {\n\t\t\tmultiple: false,\n\t\t},\n\t\tsave: props => {\n\t\t\tconst {\n\t\t\t\tid,\n\t\t\t\tsrc,\n\t\t\t\tcaption\n\t\t\t} = props.attributes;\n\n\t\t\treturn (\n\t\t\t\t<figure className={ id ? `podcast-${ id }` : null }>\n\t\t\t\t\t<audio controls=\"controls\" src={ src } />\n\t\t\t\t\t{ caption && caption.length > 0 && <figcaption>{ caption }</figcaption> }\n\t\t\t\t</figure>\n\t\t\t);\n\t\t},\n\t},\n];\n"
  },
  {
    "path": "assets/js/edit.js",
    "content": "const { __ } = wp.i18n;\nconst {\n\tBlockControls,\n\tInspectorControls,\n\tMediaPlaceholder,\n\tMediaReplaceFlow,\n\tMediaUpload,\n\tMediaUploadCheck,\n\tRichText,\n} = wp.blockEditor;\nconst {\n\tToggleControl,\n\tPanelBody,\n\tPanelRow,\n\tSelectControl,\n\tTextControl,\n\tRadioControl,\n} = wp.components;\nconst { Fragment } = wp.element;\n\nconst { apiFetch } = wp;\nconst ALLOWED_MEDIA_TYPES = ['audio'];\nconst { select } = wp.data;\n\nimport { Button } from '@wordpress/components';\nimport { useState, useEffect } from '@wordpress/element';\nimport { dispatch, useSelect, useDispatch } from '@wordpress/data';\nimport { createBlock } from '@wordpress/blocks';\n\nfunction useFeaturedImage() {\n    const featuredImageId = useSelect((select) => select('core/editor').getEditedPostAttribute('featured_media'), []);\n    const { editPost } = useDispatch('core/editor');\n\n    const featuredImageUrl = useSelect((select) => {\n        const { getMedia } = select('core');\n        const image = getMedia(featuredImageId);\n        return image?.source_url;\n    }, [featuredImageId]);\n\n    const setFeaturedImage = (imageId) => {\n        editPost({ featured_media: imageId });\n    };\n\n\tconst removeFeaturedImage = () => {\n\t\teditPost({ featured_media: 0 });\n\t};\n\n    return { featuredImageUrl, setFeaturedImage, removeFeaturedImage, featuredImageId };\n}\n\nfunction Edit( props ) {\n\tconst {\n\t\tclassName,\n\t\tsetAttributes,\n\t\tisSelected,\n\t\tattributes,\n\t\tfeaturedImageUrl,\n\t\tsetFeaturedImage,\n\t\tremoveFeaturedImage,\n\t\tfeaturedImageId\n\t} = props;\n\n\tconst {\n\t\tcaption,\n\t\texplicit,\n\t\tdisplayDuration,\n\t\tdisplayShowTitle,\n\t\tdisplayEpisodeTitle,\n\t\tdisplayArt,\n\t\tdisplayExplicitBadge,\n\t\tdisplaySeasonNumber,\n\t\tdisplayEpisodeNumber,\n\t\tdisplayEpisodeType\n\t} = attributes;\n\n\tconst duration = attributes.duration || '';\n\tconst captioned = attributes.captioned || '';\n\tconst seasonNumber = attributes.seasonNumber || '';\n\tconst episodeNumber = attributes.episodeNumber || '';\n\tconst episodeType = attributes.episodeType || '';\n\n\tconst [ src, setSrc ] = useState( props.attributes.src );\n\n\tconst postTitle = useSelect( ( select ) => select( 'core/editor' ).getEditedPostAttribute( 'title' ) );\n\n\tconst onSelectAttachment = (attachment) => {\n\t\t// Upload and Media Library return different attachment objects.\n\t\t// Therefore, we need to check the existence of some entries.\n\t\tlet mime, filesize, duration;\n\n\t\tif (attachment.mime) {\n\t\t\tmime = attachment.mime;\n\t\t} else if (attachment.mime_type) {\n\t\t\tmime = attachment.mime_type;\n\t\t}\n\n\t\tif (attachment.filesizeInBytes) {\n\t\t\tfilesize = attachment.filesizeInBytes;\n\t\t} else if (\n\t\t\tattachment.media_details &&\n\t\t\tattachment.media_details.filesize\n\t\t) {\n\t\t\tfilesize = attachment.media_details.filesize;\n\t\t}\n\n\t\tif (attachment.fileLength) {\n\t\t\tduration = attachment.fileLength;\n\t\t} else if (\n\t\t\tattachment.media_details &&\n\t\t\tattachment.media_details.length_formatted\n\t\t) {\n\t\t\tduration = attachment.media_details.length_formatted;\n\t\t}\n\n\t\tsetAttributes({\n\t\t\tid: attachment.id,\n\t\t\tsrc: attachment.url,\n\t\t\turl: attachment.url,\n\t\t\tmime,\n\t\t\tfilesize,\n\t\t\tduration,\n\t\t\tcaption: attachment.title,\n\t\t\tenclosure: attachment.url + '\\n' + filesize + '\\n' + mime,\n\t\t});\n\t\tsetSrc( attachment.url );\n\t};\n\n\tconst onSelectURL = (newSrc) => {\n\t\tif (newSrc !== src) {\n\t\t\tapiFetch({\n\t\t\t\tpath: `simple-podcasting/v1/external-url/?url=${newSrc}`,\n\t\t\t})\n\t\t\t\t.then((res) => {\n\t\t\t\t\tif (res.success) {\n\t\t\t\t\t\tconst { mime, filesize, duration } = res.data;\n\t\t\t\t\t\tsetAttributes({\n\t\t\t\t\t\t\tsrc: newSrc,\n\t\t\t\t\t\t\turl: newSrc,\n\t\t\t\t\t\t\tid: null,\n\t\t\t\t\t\t\tmime,\n\t\t\t\t\t\t\tfilesize,\n\t\t\t\t\t\t\tduration,\n\t\t\t\t\t\t\tdisplayDurationValue: duration,\n\t\t\t\t\t\t\tcaption: '',\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.catch((err) => {\n\t\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\t\tconsole.error(err);\n\t\t\t\t});\n\n\t\t\tsetSrc( newSrc );\n\t\t}\n\t};\n\n\tconst controls = (\n\t\t<BlockControls key=\"controls\">\n\t\t\t{src ? (\n\t\t\t\t<MediaReplaceFlow\n\t\t\t\t\tmediaURL={attributes.src}\n\t\t\t\t\tallowedTypes={ALLOWED_MEDIA_TYPES}\n\t\t\t\t\taccept=\"audio/*\"\n\t\t\t\t\tonSelect={onSelectAttachment}\n\t\t\t\t\tonSelectURL={onSelectURL}\n\t\t\t\t/>\n\t\t\t) : null}\n\t\t</BlockControls>\n\t);\n\n\tconst showId = useSelect( ( __select ) => {\n\t\tconst attachedPodcastIds = __select( 'core/editor' ).getEditedPostAttribute( 'podcasting_podcasts' );\n\t\treturn attachedPodcastIds ? attachedPodcastIds[0] : null;\n\t} );\n\tconst show = useSelect( ( __select ) => {\n\t\treturn __select('core').getEntityRecords('taxonomy', 'podcasting_podcasts', {\n\t\t\tinclude: [ showId ],\n\t\t});\n\t} );\n\n\tconst showName = show && show[0] ? show[0]?.name : null;\n\tconst showImage = show && show[0] ? show[0]?.meta?.podcasting_image_url : null;\n\n\tconst onUpdateImage = (image) => {\n\t\tsetFeaturedImage(image.id);\n\t};\n\n\treturn (\n\t\t<Fragment>\n\t\t\t{controls}\n\t\t\t<InspectorControls>\n\t\t\t\t<PanelBody\n\t\t\t\t\ttitle={__('Podcast Settings', 'simple-podcasting')}\n\t\t\t\t\tclassName=\"simple-podcast-settings\"\n\t\t\t\t>\n\t\t\t\t\t<PanelRow>\n\t\t\t\t\t\t<ToggleControl\n\t\t\t\t\t\t\tid=\"podcast-captioned-form-toggle\"\n\t\t\t\t\t\t\tlabel={__(\n\t\t\t\t\t\t\t\t'Closed Captioned',\n\t\t\t\t\t\t\t\t'simple-podcasting'\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\tchecked={captioned}\n\t\t\t\t\t\t\tonChange={() => setAttributes({ captioned: !captioned})}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</PanelRow>\n\t\t\t\t\t<PanelRow>\n\t\t\t\t\t\t<ToggleControl\n\t\t\t\t\t\t\tlabel={__(\n\t\t\t\t\t\t\t\t'Display Listen Time',\n\t\t\t\t\t\t\t\t'simple-podcasting'\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\tchecked={displayDuration}\n\t\t\t\t\t\t\tonChange={() => setAttributes({ displayDuration: !displayDuration})}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</PanelRow>\n\t\t\t\t\t<PanelRow>\n\t\t\t\t\t\t<ToggleControl\n\t\t\t\t\t\t\tlabel={__(\n\t\t\t\t\t\t\t\t'Display Show Title',\n\t\t\t\t\t\t\t\t'simple-podcasting'\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\tchecked={displayShowTitle}\n\t\t\t\t\t\t\tonChange={() => setAttributes({ displayShowTitle: !displayShowTitle})}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</PanelRow>\n\t\t\t\t\t<PanelRow>\n\t\t\t\t\t\t<ToggleControl\n\t\t\t\t\t\t\tlabel={__(\n\t\t\t\t\t\t\t\t'Display Episode Title',\n\t\t\t\t\t\t\t\t'simple-podcasting'\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\tchecked={displayEpisodeTitle}\n\t\t\t\t\t\t\tonChange={() => setAttributes({ displayEpisodeTitle: !displayEpisodeTitle})}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</PanelRow>\n\t\t\t\t\t<PanelRow>\n\t\t\t\t\t\t<ToggleControl\n\t\t\t\t\t\t\tlabel={__(\n\t\t\t\t\t\t\t\t'Display Show Art',\n\t\t\t\t\t\t\t\t'simple-podcasting'\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\tchecked={displayArt}\n\t\t\t\t\t\t\tonChange={() => setAttributes({ displayArt: !displayArt})}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</PanelRow>\n\t\t\t\t\t<PanelRow>\n\t\t\t\t\t\t<ToggleControl\n\t\t\t\t\t\t\tlabel={__(\n\t\t\t\t\t\t\t\t'Display Explicit Badge',\n\t\t\t\t\t\t\t\t'simple-podcasting'\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\tchecked={displayExplicitBadge}\n\t\t\t\t\t\t\tonChange={() => setAttributes({ displayExplicitBadge: !displayExplicitBadge})}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</PanelRow>\n\t\t\t\t\t<PanelRow>\n\t\t\t\t\t\t<ToggleControl\n\t\t\t\t\t\t\tlabel={__(\n\t\t\t\t\t\t\t\t'Display Season Number',\n\t\t\t\t\t\t\t\t'simple-podcasting'\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\tchecked={displaySeasonNumber}\n\t\t\t\t\t\t\tonChange={() => setAttributes({ displaySeasonNumber: !displaySeasonNumber})}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</PanelRow>\n\t\t\t\t\t<PanelRow>\n\t\t\t\t\t\t<ToggleControl\n\t\t\t\t\t\t\tlabel={__(\n\t\t\t\t\t\t\t\t'Display Episode Number',\n\t\t\t\t\t\t\t\t'simple-podcasting'\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\tchecked={displayEpisodeNumber}\n\t\t\t\t\t\t\tonChange={() => setAttributes({ displayEpisodeNumber: !displayEpisodeNumber})}\n\t\t\t\t\t\t\thelp={\n\t\t\t\t\t\t\t\t! displayEpisodeTitle\n\t\t\t\t\t\t\t\t&& __( 'The \"Display Episode Title\" setting should be enabled for this to display the episode number.', 'simple-podcasting' )\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</PanelRow>\n\t\t\t\t\t<PanelRow>\n\t\t\t\t\t\t<ToggleControl\n\t\t\t\t\t\t\tlabel={__(\n\t\t\t\t\t\t\t\t'Display Episode Type',\n\t\t\t\t\t\t\t\t'simple-podcasting'\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\tchecked={displayEpisodeType}\n\t\t\t\t\t\t\tonChange={() => setAttributes({ displayEpisodeType: !displayEpisodeType})}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</PanelRow>\n\t\t\t\t\t<PanelRow>\n\t\t\t\t\t\t<SelectControl\n\t\t\t\t\t\t\tlabel={__(\n\t\t\t\t\t\t\t\t'Explicit Content',\n\t\t\t\t\t\t\t\t'simple-podcasting'\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\tvalue={explicit}\n\t\t\t\t\t\t\toptions={[\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tvalue: 'no',\n\t\t\t\t\t\t\t\t\tlabel: __('No', 'simple-podcasting'),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tvalue: 'yes',\n\t\t\t\t\t\t\t\t\tlabel: __('Yes', 'simple-podcasting'),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tvalue: 'clean',\n\t\t\t\t\t\t\t\t\tlabel: __('Clean', 'simple-podcasting'),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t]}\n\t\t\t\t\t\t\tonChange={(explicit) =>\n\t\t\t\t\t\t\t\tsetAttributes({ explicit })\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</PanelRow>\n\t\t\t\t\t<PanelRow>\n\t\t\t\t\t\t<TextControl\n\t\t\t\t\t\t\tlabel={__(\n\t\t\t\t\t\t\t\t'Length (MM:SS)',\n\t\t\t\t\t\t\t\t'simple-podcasting'\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\tvalue={duration}\n\t\t\t\t\t\t\tonChange={(duration) =>\n\t\t\t\t\t\t\t\tsetAttributes({ duration })\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</PanelRow>\n\t\t\t\t\t<PanelRow>\n\t\t\t\t\t\t<TextControl\n\t\t\t\t\t\t\tlabel={__('Season Number', 'simple-podcasting')}\n\t\t\t\t\t\t\tvalue={seasonNumber}\n\t\t\t\t\t\t\tonChange={(seasonNumber) =>\n\t\t\t\t\t\t\t\tsetAttributes({ seasonNumber })\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</PanelRow>\n\t\t\t\t\t<PanelRow>\n\t\t\t\t\t\t<TextControl\n\t\t\t\t\t\t\tlabel={__(\n\t\t\t\t\t\t\t\t'Episode Number',\n\t\t\t\t\t\t\t\t'simple-podcasting'\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\tvalue={episodeNumber}\n\t\t\t\t\t\t\tonChange={(episodeNumber) =>\n\t\t\t\t\t\t\t\tsetAttributes({ episodeNumber })\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</PanelRow>\n\t\t\t\t\t<PanelRow>\n\t\t\t\t\t\t<RadioControl\n\t\t\t\t\t\t\tlabel={__('Episode Type', 'simple-podcasting')}\n\t\t\t\t\t\t\tselected={episodeType}\n\t\t\t\t\t\t\toptions={[\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tlabel: __('None', 'simple-podcasting'),\n\t\t\t\t\t\t\t\t\tvalue: 'none',\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tlabel: __('Full', 'simple-podcasting'),\n\t\t\t\t\t\t\t\t\tvalue: 'full',\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tlabel: __(\n\t\t\t\t\t\t\t\t\t\t'Trailer',\n\t\t\t\t\t\t\t\t\t\t'simple-podcasting'\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\tvalue: 'trailer',\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tlabel: __('Bonus', 'simple-podcasting'),\n\t\t\t\t\t\t\t\t\tvalue: 'bonus',\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t]}\n\t\t\t\t\t\t\tonChange={(episodeType) =>\n\t\t\t\t\t\t\t\tsetAttributes({ episodeType })\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</PanelRow>\n\t\t\t\t\t<PanelRow>\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\t\tonClick={() =>\n\t\t\t\t\t\t\t\tdispatch('core/block-editor').insertBlocks(\n\t\t\t\t\t\t\t\t\tcreateBlock(\n\t\t\t\t\t\t\t\t\t\t'podcasting/podcast-transcript'\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{__('Add Transcript', 'simple-podcasting')}\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</PanelRow>\n\t\t\t\t\t<h3 style={{marginTop: '20px'}}>{__('Cover Image', 'simple-podcasting')}</h3>\n\t\t\t\t\t<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>\n\t\t\t\t\t<PanelRow className=\"cover-art-container\">\n\t\t\t\t\t\t{featuredImageUrl && (\n\t\t\t\t\t\t\t<img src={featuredImageUrl} alt=\"Cover Image\" />\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t<MediaUploadCheck>\n\t\t\t\t\t\t\t<MediaUpload\n\t\t\t\t\t\t\t\tonSelect={onUpdateImage}\n\t\t\t\t\t\t\t\tallowedTypes={['image']}\n\t\t\t\t\t\t\t\trender={({ open }) => (\n\t\t\t\t\t\t\t\t\t<Button isSecondary onClick={open}>\n\t\t\t\t\t\t\t\t\t\t{featuredImageUrl ?\n\t\t\t\t\t\t\t\t\t\t__('Replace Cover Art', 'simple-podcasting')\n\t\t\t\t\t\t\t\t\t\t:\n\t\t\t\t\t\t\t\t\t\t__('Select Cover Art', 'simple-podcasting')\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\tvalue={featuredImageId}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</MediaUploadCheck>\n\t\t\t\t\t\t{featuredImageUrl && (\n\t\t\t\t\t\t\t<Button isLink isDestructive onClick={removeFeaturedImage}>{__('Delete Cover Art', 'simple-podcasting')}</Button>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</PanelRow>\n\t\t\t\t</PanelBody>\n\t\t\t</InspectorControls>\n\t\t\t<div className=\"wp-block-podcasting-podcast-outer\">\n\t\t\t\t{src ? (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<div className=\"wp-block-podcasting-podcast__container\">\n\t\t\t\t\t\t\t{displayArt && (featuredImageUrl || showImage) && (\n\t\t\t\t\t\t\t\t<div className=\"wp-block-podcasting-podcast__show-art\">\n\t\t\t\t\t\t\t\t\t<div className=\"wp-block-podcasting-podcast__image\">\n\t\t\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\t\t\tsrc={featuredImageUrl ? featuredImageUrl : showImage}\n\t\t\t\t\t\t\t\t\t\t\talt={showName}\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t\t<div className=\"wp-block-podcasting-podcast__details\">\n\n\t\t\t\t\t\t\t\t{displayEpisodeTitle && postTitle && (\n\t\t\t\t\t\t\t\t\t<h3 className=\"wp-block-podcasting-podcast__show-title\">\n\t\t\t\t\t\t\t\t\t\t{displayEpisodeNumber && episodeNumber && (\n\t\t\t\t\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t\t\t\t\t{episodeNumber}.\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t{postTitle}\n\t\t\t\t\t\t\t\t\t</h3>\n\t\t\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t\t\t<div className=\"wp-block-podcasting-podcast__show-details\">\n\t\t\t\t\t\t\t\t\t{displayShowTitle && (\n\t\t\t\t\t\t\t\t\t\t<span className=\"wp-block-podcasting-podcast__title\">\n\t\t\t\t\t\t\t\t\t\t\t{showName}\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t{displaySeasonNumber && seasonNumber && (\n\t\t\t\t\t\t\t\t\t\t<span className=\"wp-block-podcasting-podcast__season\">\n\t\t\t\t\t\t\t\t\t\t\t{__(\n\t\t\t\t\t\t\t\t\t\t\t\t'Season: ',\n\t\t\t\t\t\t\t\t\t\t\t\t'simple-podcasting'\n\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t{seasonNumber}\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t{displayEpisodeNumber && episodeNumber && (\n\t\t\t\t\t\t\t\t\t\t<span className=\"wp-block-podcasting-podcast__episode\">\n\t\t\t\t\t\t\t\t\t\t\t{__('Episode: ', 'simple-podcasting')}\n\t\t\t\t\t\t\t\t\t\t\t{episodeNumber}\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t<div className=\"wp-block-podcasting-podcast__show-details\">\n\t\t\t\t\t\t\t\t\t{displayDuration && duration && (\n\t\t\t\t\t\t\t\t\t\t<span className=\"wp-block-podcasting-podcast__duration\">\n\t\t\t\t\t\t\t\t\t\t\t{__('Listen Time: ', 'simple-podcasting')}\n\t\t\t\t\t\t\t\t\t\t\t{duration}\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t{displayEpisodeType && (episodeType !== 'none') && (\n\t\t\t\t\t\t\t\t\t\t<span className=\"wp-block-podcasting-podcast__episode-type\">\n\t\t\t\t\t\t\t\t\t\t\t{__(\n\t\t\t\t\t\t\t\t\t\t\t\t'Episode type: ',\n\t\t\t\t\t\t\t\t\t\t\t\t'simple-podcasting'\n\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t{episodeType}\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t{displayExplicitBadge && (\n\t\t\t\t\t\t\t\t\t\t<span className=\"wp-block-podcasting-podcast__explicit-badge\">\n\t\t\t\t\t\t\t\t\t\t\t{__(\n\t\t\t\t\t\t\t\t\t\t\t\t'Explicit: ',\n\t\t\t\t\t\t\t\t\t\t\t\t'simple-podcasting'\n\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t{explicit}\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<figure key=\"audio\" className={className}>\n\t\t\t\t\t\t\t{((caption && caption.length) || !!isSelected) && (\n\t\t\t\t\t\t\t\t<RichText\n\t\t\t\t\t\t\t\t\ttagName=\"figcaption\"\n\t\t\t\t\t\t\t\t\tplaceholder={__(\n\t\t\t\t\t\t\t\t\t\t'Write caption…',\n\t\t\t\t\t\t\t\t\t\t'simple-podcasting'\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\tclassName=\"wp-block-podcasting-podcast__caption\"\n\t\t\t\t\t\t\t\t\tvalue={caption}\n\t\t\t\t\t\t\t\t\tonChange={(value) =>\n\t\t\t\t\t\t\t\t\t\tsetAttributes({ caption: value })\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tisSelected={isSelected}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t<audio controls=\"controls\" src={src} />\n\t\t\t\t\t\t</figure>\n\t\t\t\t\t</>\n\t\t\t\t) : (\n\t\t\t\t\t<MediaPlaceholder\n\t\t\t\t\t\ticon=\"microphone\"\n\t\t\t\t\t\tlabels={{\n\t\t\t\t\t\t\ttitle: __('Podcast', 'simple-podcasting'),\n\t\t\t\t\t\t\tname: __(\n\t\t\t\t\t\t\t\t'a podcast episode',\n\t\t\t\t\t\t\t\t'simple-podcasting'\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tclassName={className}\n\t\t\t\t\t\tonSelect={onSelectAttachment}\n\t\t\t\t\t\tonSelectURL={onSelectURL}\n\t\t\t\t\t\taccept=\"audio/*\"\n\t\t\t\t\t\tallowedTypes={ALLOWED_MEDIA_TYPES}\n\t\t\t\t\t\tvalue={attributes}\n\t\t\t\t\t/>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</Fragment>\n\t);\n}\n\nfunction PodcastBlockWithHooks(props) {\n    const featuredImageProp = useFeaturedImage();\n\n    return <Edit {...props} {...featuredImageProp} />;\n}\n\nexport default PodcastBlockWithHooks;\n"
  },
  {
    "path": "assets/js/onboarding.js",
    "content": "import '../css/podcasting-onboarding.scss';\n\n( function( $ ) {\n\t$( function() {\n\t\tconst selectImageBtn = $( '#simple-podcasting__upload-cover-image' );\n\t\tconst coverImage = $( 'input[name=\"podcast-cover-image-id\"]' );\n\t\tconst coverImagePreview = $( '#simple-podcasting__cover-image-preview' );\n\t\tlet uploader_frame = null;\n\n\t\t/** Upload image button handler */\n\t\tselectImageBtn.on( 'click', function() {\n\t\t\tuploader_frame = wp.media( {\n\t\t\t\tmultiple: false,\n\t\t\t\tlibrary: {\n\t\t\t\t\ttype: 'image'\n\t\t\t\t}\n\t\t\t} ).on( 'select', function() {\n\t\t\t\tconst { id, url } = uploader_frame.state().get( 'selection' ).first().toJSON();\n\t\t\t\tcoverImagePreview.html( `<img src=\"${ url }\" />` )\n\t\t\t\tcoverImage.val( id );\n\t\t\t} );\n\n\t\t\tuploader_frame.open();\n\t\t} );\n\t} )\n} )( jQuery )"
  },
  {
    "path": "assets/js/podcasting-edit-post.js",
    "content": "/*global jQuery */\njQuery( document ).ready( function( $ ) {\n\t$( '#podcasting-enclosure-button' ).click( function( e ) {\n\t\te.preventDefault();\n\n\t\tvar $this = $( this ),\n\t\t\t$input = $( 'input#podcasting-enclosure-url' ),\n\t\t\tmediaUploader;\n\n\t\t// If the uploader object has already been created, reopen the dialog.\n\t\tif ( mediaUploader ) {\n\t\t\tmediaUploader.open();\n\t\t\treturn;\n\t\t}\n\n\t\t// eslint-disable-next-line camelcase\n\t\tmediaUploader = wp.media.frames.file_frame = wp.media( {\n\t\t\ttitle: $this.data( 'modalTitle' ),\n\t\t\tbutton: {\n\t\t\t\ttext: $this.data( 'modalButton' )\n\t\t\t},\n\t\t\tlibrary: {\n\t\t\t\ttype: 'audio'\n\t\t\t},\n\t\t\tmultiple: false\n\t\t});\n\n\t\tmediaUploader.off( 'select' );\n\t\tmediaUploader.on( 'select', function() {\n\t\t\tvar attachment = mediaUploader.state().get('selection').first();\n\n\t\t\t$input.val( attachment.get('url') );\n\t\t});\n\n\t\tmediaUploader.open();\n\t} );\n} );\n"
  },
  {
    "path": "assets/js/podcasting-edit-term.js",
    "content": "/*global jQuery, validateForm*/\nimport '../css/podcasting-edit-term.css';\n\njQuery( document ).ready( function( $ ) {\n\n\t// Clear Image Field.\n\tfunction clearImageField( el ) {\n\t\tvar $link   = $( el ),\n\t\t\t$wrapper  = $link.parents( '.media-wrapper' ),\n\t\t\t$button   = $wrapper.find( '.podcasting-media-button' ),\n\t\t\t$hidden   = $( document.getElementById( $button.data( 'slug' ) ) ),\n\t\t\t$existing = $wrapper.find( '.podasting-existing-image' ),\n\t\t\t$upload   = $wrapper.find( '.podcasting-upload-image' );\n\n\t\t// Update the display.\n\t\t$upload.removeClass('hidden');\n\t\t$existing.addClass('hidden');\n\t\t$hidden.val( '' );\n\t}\n\n\t// When the term add button is clicked, reset the dropdown fields.\n\t$( '#submit' ).click( function() {\n\n\t\tvar $form = $( 'form#addtag' );\n\n\t\tif ( ! validateForm( $form ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Add a brief delay to allow the form to submit.\n\t\tsetTimeout( function() {\n\t\t\t$( '.fm-select select' ).val( 'None' );\n\t\t\tclearImageField( '.podcast-media-remove' );\n\t\t\t$( '#podcasting_category_1,#podcasting_category_2,#podcasting_category_3' ).val( '' );\n\t\t\twindow.scrollTo(0,0);\n\t\t}, 500 );\n\t} );\n\n\tvar mediaUploader;\n\n\t// Handle media upload buttons.\n\t$( 'input.podcasting-media-button' ).on( 'click', function( e ) {\n\t\te.preventDefault();\n\n\t\tvar $button   = $( e.currentTarget ),\n\t\t\t$hidden   = $( document.getElementById( $button.data( 'slug' ) ) ),\n\t\t\t$wrapper  = $button.parents( '.media-wrapper' ),\n\t\t\t$image    = $wrapper.find( 'img' ),\n\t\t\t$existing = $wrapper.find( '.podasting-existing-image' ),\n\t\t\t$upload   = $wrapper.find( '.podcasting-upload-image' );\n\n\t\t// If the uploader object has already been created, reopen the dialog.\n\t\tif (mediaUploader) {\n\t\t\tmediaUploader.open();\n\t\t\treturn;\n\t\t}\n\t\t// Extend the wp.media object.\n\t\t// eslint-disable-next-line camelcase\n\t\tmediaUploader = wp.media.frames.file_frame = wp.media( {\n\t\t\ttitle: $button.data( 'choose' ),\n\t\t\tbutton: {\n\t\t\t\ttext: $button.data( 'update' )\n\t\t\t},\n\t\t\tmultiple: false\n\t\t});\n\n\t\t// When a file is selected, grab the URL and set it as the text field's value.\n\t\tmediaUploader.off( 'select' );\n\t\tmediaUploader.on( 'select', function() {\n\t\t\tvar attachment = mediaUploader.state().get('selection').first();\n\n\t\t\t// Set the hidden field value.\n\t\t\t$hidden.val( attachment.get('id') );\n\n\t\t\t// Update the display.\n\t\t\t$upload.addClass('hidden');\n\t\t\t$existing.removeClass('hidden');\n\t\t\t$image.attr( 'src', attachment.get('url') );\n\n\t\t});\n\n\t\t// Open the uploader dialog\n\t\tmediaUploader.open();\n\t});\n\n\t// Handle media remove buttons.\n\t$( '.podcast-media-remove' ).on( 'click', function( e ) {\n\t\te.preventDefault();\n\t\tclearImageField( e.currentTarget );\n\t} );\n\n\tconst iconThemeRadioEl = $( 'input[name=\"podcasting_icon_theme\"]' );\n\tconst iconWrappers = $( '.simple_podcasting__platforms-icon' );\n\n\ticonThemeRadioEl.on( 'change', function() {\n\t\tconst current = $( this );\n\t\tconst selected = current.val();\n\n\t\tif ( 'white' === selected ) {\n\t\t\ticonWrappers.addClass( 'simple_podcasting__platforms-icon--darken-bg' );\n\t\t} else {\n\t\t\ticonWrappers.removeClass( 'simple_podcasting__platforms-icon--darken-bg' );\n\t\t}\n\n\t\ticonWrappers.each( ( index, icon ) => {\n\t\t\tconst imgEl = $( icon ).find( 'img' );\n\t\t\tconst platform = imgEl.data( 'platform' );\n\t\t\timgEl.attr( 'src', `${ podcastingEditPostVars.iconUrl }/${ platform }/${ selected }-100.png` );\n\t\t});\n\t} );\n} );\n\n"
  },
  {
    "path": "assets/js/transforms.js",
    "content": "/**\n * WordPress dependencies\n */\nconst { select } = wp.data;\nconst { createBlock } = wp.blocks;\n\n/**\n * Transforms\n */\nconst transforms = {\n\tfrom: [\n\t\t{\n\t\t\ttype: 'block',\n\t\t\tblocks: [ 'core/audio' ],\n\t\t\ttransform: ( attributes ) => {\n\t\t\t\treturn createBlock( 'podcasting/podcast', {\n\t\t\t\t\tid: attributes.id,\n\t\t\t\t\tsrc: attributes.src\n\t\t\t\t} );\n\t\t\t},\n\t\t},\n\t],\n\tto: [\n\t\t{\n\t\t\ttype: 'block',\n\t\t\tblocks: [ 'core/audio' ],\n\t\t\tisMatch: ( { id } ) => {\n\t\t\t\tif ( ! id ) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tconst { getMedia } = select( 'core' );\n\t\t\t\tconst media = getMedia( id );\n\t\t\t\treturn !! media && media.mime_type.includes( 'audio' );\n\t\t\t},\n\t\t\ttransform: ( attributes ) => {\n\t\t\t\treturn createBlock( 'core/audio', {\n\t\t\t\t\tsrc: attributes.src,\n\t\t\t\t\tid: attributes.id\n\t\t\t\t} );\n\t\t\t},\n\t\t},\n\t],\n\n};\n\nexport default transforms;\n"
  },
  {
    "path": "composer.json",
    "content": "{\n  \"name\": \"10up/simple-podcasting\",\n  \"description\": \"A simple podcasting solution for WordPress. \",\n  \"homepage\": \"https://github.com/10up/simple-podcasting\",\n  \"license\": \"GPL-2.0-or-later\",\n  \"authors\": [\n    {\n      \"name\": \"10up\",\n      \"email\": \"opensource@10up.com\",\n      \"homepage\": \"https://10up.com\"\n    }\n  ],\n  \"support\": {\n    \"issues\": \"https://github.com/10up/simple-podcasting/issues\"\n  },\n  \"require\": {\n    \"php\": \">=7.3\"\n  },\n  \"require-dev\": {\n    \"10up/phpcs-composer\": \"^3.0\",\n    \"10up/wp_mock\": \"^0.4.2\",\n    \"phpunit/phpunit\": \"^9.5\",\n    \"phpcompatibility/php-compatibility\": \"dev-develop as 9.99.99\"\n  },\n  \"config\": {\n    \"allow-plugins\": {\n      \"dealerdirect/phpcodesniffer-composer-installer\": true\n    }\n  }\n}\n"
  },
  {
    "path": "includes/admin/create-podcast-component.php",
    "content": "<?php\n/**\n * Defines class to handle creation of a podcast\n * from with the Post editor.\n *\n * @package tenup_podcasting\n */\n\nnamespace tenup_podcasting\\admin;\n\n/**\n * Adds methods to create a podcast using the Gutenberg inspector component.\n */\nclass Create_Podcast_Component {\n\t/**\n\t * Constructor method.\n\t */\n\tpublic function __construct() {\n\t\tadd_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );\n\t}\n\n\t/**\n\t * Admin enqueue scripts.\n\t */\n\tpublic function admin_enqueue_scripts() {\n\t\t$screen = get_current_screen();\n\n\t\tif ( ! ( $screen && 'post' === $screen->id ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\twp_enqueue_script(\n\t\t\t'podcasting_create_podcast_show_plugin',\n\t\t\tPODCASTING_URL . 'dist/create-podcast-show.js',\n\t\t\tarray(),\n\t\t\tPODCASTING_VERSION,\n\t\t\ttrue\n\t\t);\n\n\t\twp_localize_script(\n\t\t\t'podcasting_create_podcast_show_plugin',\n\t\t\t'podcastingShowPluginVars',\n\t\t\tarray(\n\t\t\t\t'categories' => \\tenup_podcasting\\get_podcasting_categories_options(),\n\t\t\t)\n\t\t);\n\t}\n}\n\nnew Create_Podcast_Component();\n"
  },
  {
    "path": "includes/admin/onboarding.php",
    "content": "<?php\n/**\n * Registers and renders the onboarding setup wizard.\n *\n * @package tenup_podcasting\n */\n\nnamespace tenup_podcasting\\admin;\n\n/**\n * Adds methods required for handling the onboarding wizard.\n */\nclass Onboarding {\n\t/**\n\t * Indicates onboarding is in progress.\n\t *\n\t * @var string\n\t */\n\tconst STATUS_IN_PROGRESS = 'in-progress';\n\n\t/**\n\t * Indicates onboarding is complete.\n\t *\n\t * @var string\n\t */\n\tconst STATUS_COMPLETED = 'completed';\n\n\t/**\n\t * Holds the object for Create_Podcast.\n\t *\n\t * @var \\tenup_podcasting\\Create_Podcast\n\t */\n\tprotected $create_podcast;\n\n\t/**\n\t * Constructor\n\t */\n\tpublic function __construct() {\n\t\t$this->create_podcast = new \\tenup_podcasting\\Create_Podcast();\n\n\t\tadd_action( 'admin_menu', array( $this, 'register_onoarding_page' ) );\n\t\tadd_action( 'admin_init', array( $this, 'onboarding_action_handler' ) );\n\t}\n\n\t/**\n\t * Registers a hidden sub menu page for the onboarding wizard.\n\t */\n\tpublic function register_onoarding_page() {\n\t\tadd_submenu_page(\n\t\t\t'admin.php',\n\t\t\tesc_html__( 'Simple Podcasting Onboarding' ),\n\t\t\t'',\n\t\t\t'manage_options',\n\t\t\t'simple-podcasting-onboarding',\n\t\t\tarray( $this, 'render_page_contents' )\n\t\t);\n\n\t\tif ( 'no' === get_option( 'simple_podcasting_onboarding', '' ) ) {\n\t\t\tupdate_option( 'simple_podcasting_onboarding', self::STATUS_IN_PROGRESS );\n\t\t\twp_safe_redirect( admin_url( 'admin.php?page=simple-podcasting-onboarding&step=1' ) );\n\t\t\tdie();\n\t\t}\n\t}\n\n\t/**\n\t * Renders the page content for the onboarding wizard.\n\t */\n\tpublic function render_page_contents() {\n\t\t$step = filter_input( INPUT_GET, 'step', FILTER_VALIDATE_INT );\n\n\t\tif ( ! $step ) {\n\t\t\t$step = 1;\n\t\t}\n\n\t\trequire_once 'views/onboarding-header.php';\n\n\t\tswitch ( $step ) {\n\t\t\tcase 1:\n\t\t\t\trequire_once 'views/onboarding-page-one.php';\n\t\t\t\tbreak;\n\n\t\t\tcase 2:\n\t\t\t\trequire_once 'views/onboarding-page-two.php';\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t/**\n\t * Onboarding data saving handler.\n\t */\n\tpublic function onboarding_action_handler() {\n\t\tif ( ! $this->create_podcast->verify_nonce() ) {\n\t\t\treturn;\n\t\t}\n\n\t\t$this->create_podcast->sanitize_podcast_fields();\n\n\t\t$is_sanitized = $this->create_podcast->save_podcast_fields();\n\n\t\tif ( is_wp_error( $is_sanitized ) ) {\n\t\t\t$error_message = $is_sanitized->get_error_message();\n\n\t\t\tadd_action(\n\t\t\t\t'admin_notices',\n\t\t\t\tfunction () use ( $error_message ) {\n\t\t\t\t\tif ( empty( $error_message ) ) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\t?>\n\t\t\t\t\t<div class=\"notice notice-error is-dismissible\">\n\t\t\t\t\t\t<p><?php echo wp_kses_post( $error_message ); ?></p>\n\t\t\t\t\t</div>\n\t\t\t\t\t<?php\n\t\t\t\t}\n\t\t\t);\n\n\t\t\treturn;\n\t\t}\n\n\t\tif ( self::STATUS_IN_PROGRESS === get_option( 'simple_podcasting_onboarding', '' ) ) {\n\t\t\twp_safe_redirect( admin_url( 'admin.php?page=simple-podcasting-onboarding&step=2' ) );\n\t\t\tdie;\n\t\t}\n\t}\n}\n\n$onbarding_status = get_option( 'simple_podcasting_onboarding', '' );\n\nif ( Onboarding::STATUS_COMPLETED !== $onbarding_status ) {\n\tnew Onboarding();\n}\n"
  },
  {
    "path": "includes/admin/views/onboarding-header.php",
    "content": "<?php\n/**\n * Header template for onboarding: Step - 1.\n *\n * @package tenup_podcasting\n */\n\n?>\n<div id=\"simple-podcasting__onboarding-header\">\n\t<div id=\"simple-podcasting__branding\">\n\t\t<div id=\"simple-podcasting__logo\">\n\t\t\t<img src=\"https://ps.w.org/simple-podcasting/assets/icon-128x128.png\" />\n\t\t</div>\n\t\t<div id=\"simple-podcasting__plugin-name\">\n\t\t\t<?php esc_html_e( 'Simple Podcasting', 'simple-podcasting' ); ?>\n\t\t</div>\n\t</div>\n\n\t<div id=\"simple-podcasting__header-title\">\n\t\t<?php esc_html_e( 'Get Started With Podcasting', 'simple-podcasting' ); ?>\n\t</div>\n\n\t<div id=\"simple-podcasting__header-controls\">\n\t\t<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>\n\t</div>\n</div>\n"
  },
  {
    "path": "includes/admin/views/onboarding-page-one.php",
    "content": "<?php\n/**\n * Body template for onboarding: Step - 1.\n *\n * @package tenup_podcasting\n */\n\n?>\n<form method=\"POST\" action=\"\">\n\t<div class=\"simple-podcasting__onboarding-body simple-podcasting__onboarding-body--step-1\">\n\t\t<div id=\"simple-podcasting__page-title\">\n\t\t\t<?php esc_html_e( 'Create your first podcast show', 'simple-podcasting' ); ?>\n\t\t</div>\n\n\t\t<!-- Podcast name -->\n\t\t<div class=\"simple-podcasting__setting\">\n\t\t\t<label class=\"simple-podcasting__setting-label\" for=\"simple-podcasting-podcast-name\"><?php esc_html_e( 'Podcast name*', 'simple-podcasting' ); ?></label>\n\t\t\t<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>\n\t\t\t<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>\n\t\t</div>\n\n\t\t<!-- Podcast artist/author -->\n\t\t<div class=\"simple-podcasting__setting\">\n\t\t\t<label class=\"simple-podcasting__setting-label\" for=\"simple-podcasting-podcast-artist\"><?php esc_html_e( 'Podcast Artist / Author name*', 'simple-podcasting' ); ?></label>\n\t\t\t<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>\n\t\t\t<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>\n\t\t</div>\n\n\t\t<!-- Podcast description -->\n\t\t<div class=\"simple-podcasting__setting\">\n\t\t\t<label class=\"simple-podcasting__setting-label\" for=\"simple-podcasting-podcast-description\"><?php esc_html_e( 'Podcast summary*', 'simple-podcasting' ); ?></label>\n\t\t\t<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>\n\t\t\t<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>\n\t\t</div>\n\n\t\t<!-- Cover image -->\n\t\t<div class=\"simple-podcasting__setting\">\n\t\t\t<input type=\"hidden\" name=\"podcast-cover-image-id\" id=\"simple-podcasting-podcast-cover-image-id\" aria-describedby=\"simple-podcasting__cover-image-description\" value=\"\" required>\n\t\t\t<label class=\"simple-podcasting__setting-label\" for=\"simple-podcasting-podcast-cover-image-id\"><?php esc_html_e( 'Cover image*', 'simple-podcasting' ); ?></label>\n\t\t\t<div id=\"simple-podcasting__cover-image-preview\"></div>\n\t\t\t<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>\n\t\t\t<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>\n\t\t</div>\n\n\t\t<!-- Podcast category -->\n\t\t<div class=\"simple-podcasting__setting\">\n\t\t\t<label class=\"simple-podcasting__setting-label\" for=\"simple-podcasting-podcast-category\"><?php esc_html_e( 'Podcast category*', 'simple-podcasting' ); ?></label>\n\t\t\t<select name=\"podcast-category\" id=\"simple-podcasting-podcast-category\" aria-describedby=\"simple-podcasting__podcast-category-description\" required>\n\t\t\t\t<?php foreach ( \\tenup_podcasting\\get_podcasting_categories_options() as $option_value => $option_label ) : ?>\n\t\t\t\t\t<option value=\"<?php echo esc_attr( $option_value ); ?>\"><?php echo esc_html( $option_label ); ?></option>\n\t\t\t\t<?php endforeach; ?>\n\t\t\t</select>\n\t\t\t<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>\n\t\t</div>\n\n\t\t<!-- Create button -->\n\t\t<button class=\"simple-podcasting__btn simple-podcasting__btn--black\" id=\"simple-podcasting__create-podcast-button\"><?php esc_html_e( 'Create', 'simple-podcasting' ); ?></button>\n\t\t<?php wp_nonce_field( 'simple-podcasting-create-show-action', 'simple-podcasting-create-show-nonce-field' ); ?>\n\t</div>\n</form>\n"
  },
  {
    "path": "includes/admin/views/onboarding-page-two.php",
    "content": "<?php\n/**\n * Body template for onboarding: Step - 2.\n *\n * @package tenup_podcasting\n */\n\nuse tenup_podcasting\\admin\\Onboarding;\n\n?>\n<div class=\"simple-podcasting__onboarding-body simple-podcasting__onboarding-body--step-2\">\n\t<div class=\"simple-podcasting__panel simple-podcasting__panel--left\">\n\t\t<div id=\"simple-podcasting__page-title\">\n\t\t\t<?php esc_html_e( 'Well done!', 'simple-podcasting' ); ?>\n\t\t</div>\n\n\t\t<p>\n\t\t\t<?php\n\t\t\tprintf(\n\t\t\t\t/* translators: %s podcast term edit page. */\n\t\t\t\t__( 'You can always edit show details <a href=\"%s\">here</a>.', 'simple-podcasting' ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped \n\t\t\t\tesc_url( admin_url( 'edit-tags.php?taxonomy=podcasting_podcasts&podcasts=true' ) )\n\t\t\t);\n\t\t\t?>\n\t\t</p>\n\t\t<p><?php esc_html_e( 'Now let’s create your show’s first episode:', 'simple-podcasting' ); ?></p>\n\t\t<ol>\n\t\t\t<li><?php esc_html_e( 'Create a new post', 'simple-podcasting' ); ?></li>\n\t\t\t<li><?php esc_html_e( 'Assign the post to a show', 'simple-podcasting' ); ?></li>\n\t\t\t<li><?php esc_html_e( 'Insert a podcast block with an audio file into the content', 'simple-podcasting' ); ?></li>\n\t\t</ol>\n\t\t<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>\n\t\t<p><?php esc_html_e( \"Let's get started!\", 'simple-podcasting' ); ?></p>\n\n\t\t<div class=\"simple-podcasting__step-2-controls\">\n\t\t\t<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>\n\t\t\t<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>\n\t\t</div>\n\t</div>\n\n\t<div class=\"simple-podcasting__panel simple-podcasting__panel--right\">\n\t\t<div class=\"simple-podcasting__podcast-block-preview\">\n\t\t\t<img\n\t\t\t\tsrcset=\"<?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\"\n\t\t\t\tsrc=\"<?php echo esc_url( PODCASTING_URL . 'dist/assets/images/podcast-block-preview.png' ); ?>\"\n\t\t\t/>\n\t\t</div>\n\t</div>\n\t<?php update_option( 'simple_podcasting_onboarding', Onboarding::STATUS_COMPLETED ); ?>\n</div>\n"
  },
  {
    "path": "includes/block-patterns.php",
    "content": "<?php\n/**\n * Register and enqueue all things block patterns.\n *\n * @package tenup_podcasting\n */\n\nnamespace tenup_podcasting\\block_patterns;\n\n/**\n * Register block and its assets.\n */\nfunction init() {\n\t$podcast_terms = get_terms(\n\t\t[\n\t\t\t'taxonomy' => PODCASTING_TAXONOMY_NAME,\n\t\t\t'fields'   => 'ids',\n\t\t]\n\t);\n\n\tif ( empty( $podcast_terms ) ) {\n\t\treturn;\n\t}\n\n\tregister_block_pattern(\n\t\t'podcasting/podcast-grid',\n\t\tarray(\n\t\t\t'title'       => __( 'Podcast Grid', 'simple-podcasting' ),\n\t\t\t'description' => _x( 'Podcast Grid', 'This block pattern is used to display podcast in a grid structure.', 'simple-podcasting' ),\n\t\t\t'categories'  => [ 'query' ],\n\t\t\t'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}} -->\n\t\t\t\t<div class=\"wp-block-query\">\n\t\t\t\t\t<!-- wp:post-template -->\n\t\t\t\t\t\t<!-- wp:cover {\"useFeaturedImage\":true,\"dimRatio\":50,\"className\":\"alignfull\"} -->\n\t\t\t\t\t\t\t<div class=\"wp-block-cover alignfull\">\n\t\t\t\t\t\t\t\t<span aria-hidden=\"true\" class=\"wp-block-cover__background has-background-dim\"></span>\n\t\t\t\t\t\t\t\t<div class=\"wp-block-cover__inner-container\">\n\t\t\t\t\t\t\t\t\t<!-- wp:post-title {\"isLink\":true,\"style\":{\"typography\":{\"fontSize\":\"1.6rem\"},\"elements\":{\"link\":{\"color\":{\"text\":\"var:preset|color|white\"}}}},\"textColor\":\"white\"} /-->\n\t\t\t\t\t\t\t\t\t<!-- wp:post-terms {\"term\":\"' . esc_js( PODCASTING_TAXONOMY_NAME ) . '\",\"style\":{\"typography\":{\"fontSize\":\"2rem\"},\"elements\":{\"link\":{\"color\":{\"text\":\"var:preset|color|white\"}}}},\"textColor\":\"white\"} /-->\n\t\t\t\t\t\t\t\t\t<!-- 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\"} /-->\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<!-- /wp:cover -->\n\t\t\t\t\t<!-- /wp:post-template -->\n\t\t\t\t</div>\n\t\t\t<!-- /wp:query -->',\n\t\t)\n\t);\n}\nadd_action( 'init', __NAMESPACE__ . '\\init' );\n"
  },
  {
    "path": "includes/blocks/podcast/markup.php",
    "content": "<?php\n/**\n * Podcast markup\n *\n * @package tenup_podcasting\n *\n * @var array    $attributes Block attributes.\n * @var string   $content    Block content.\n * @var WP_Block $block      Block instance.\n * @var array    $context    Block context.\n */\n\n$attributes = wp_parse_args(\n\t$attributes ?? [],\n\t[\n\t\t'id'                   => null,\n\t\t'caption'              => '',\n\t\t'displayDuration'      => false,\n\t\t'displayShowTitle'     => false,\n\t\t'displayEpisodeTitle'  => false,\n\t\t'displayArt'           => false,\n\t\t'displayExplicitBadge' => false,\n\t\t'displaySeasonNumber'  => false,\n\t\t'displayEpisodeNumber' => false,\n\t\t'displayEpisodeType'   => false,\n\t]\n);\n\nif ( ! $attributes['id'] ) {\n\treturn;\n}\n\n$post_id        = get_the_id();\n$podcast_shows  = get_the_terms( $post_id, 'podcasting_podcasts' );\n$podcast_show   = $podcast_shows ? $podcast_shows[0] : '';\n$show_name      = $podcast_show ? $podcast_show->name : '';\n$src            = get_post_meta( $post_id, 'src', true );\n$duration       = get_post_meta( $post_id, 'podcast_duration', true );\n$explicit       = get_post_meta( $post_id, 'podcast_explicit', true );\n$episode_type   = get_post_meta( $post_id, 'podcast_episode_type', true );\n$episode_number = get_post_meta( $post_id, 'podcast_episode_number', true );\n$season_number  = get_post_meta( $post_id, 'podcast_season_number', true );\nif ( is_a( $podcast_show, 'WP_Term' ) ) {\n\t$term_image_id = get_term_meta( $podcast_show->term_id, 'podcasting_image', true );\n} else {\n\t$term_image_id = '';\n}\n\n?>\n<div class=\"wp-block-podcasting-podcast-outer\">\n\t<div class=\"wp-block-podcasting-podcast__container\">\n\t\t<?php if ( $attributes['displayArt'] && ( has_post_thumbnail() || ! empty( $term_image_id ) ) ) : ?>\n\t\t\t<div class=\"wp-block-podcasting-podcast__show-art\">\n\t\t\t\t<div class=\"wp-block-podcasting-podcast__image\">\n\t\t\t\t\t<?php\n\t\t\t\t\tif ( has_post_thumbnail() ) {\n\t\t\t\t\t\tthe_post_thumbnail( 'medium' );\n\t\t\t\t\t} elseif ( $podcast_show instanceof \\WP_Term ) {\n\t\t\t\t\t\techo wp_get_attachment_image( $term_image_id, 'medium' );\n\t\t\t\t\t}\n\t\t\t\t\t?>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t<?php endif; ?>\n\n\t\t<div class=\"wp-block-podcasting-podcast__details\">\n\t\t\t<?php if ( $attributes['displayEpisodeTitle'] ) : ?>\n\t\t\t\t<h3 class=\"wp-block-podcasting-podcast__show-title\">\n\t\t\t\t\t<?php if ( $attributes['displayEpisodeNumber'] && ! empty( $episode_number ) ) : ?>\n\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t<?php echo esc_html( $episode_number ); ?>.\n\t\t\t\t\t\t</span>\n\t\t\t\t\t<?php endif; ?>\n\t\t\t\t\t<?php the_title(); ?>\n\t\t\t\t</h3>\n\t\t\t<?php endif; ?>\n\t\t\t<div class=\"wp-block-podcasting-podcast__show-details\">\n\t\t\t\t<?php if ( $attributes['displayShowTitle'] && ! empty( $show_name ) ) : ?>\n\t\t\t\t\t<span class=\"wp-block-podcasting-podcast__title\">\n\t\t\t\t\t\t<?php echo esc_html( $show_name ); ?>\n\t\t\t\t\t</span>\n\t\t\t\t<?php endif; ?>\n\t\t\t\t<?php if ( $attributes['displaySeasonNumber'] && ! empty( $season_number ) ) : ?>\n\t\t\t\t\t<span class=\"wp-block-podcasting-podcast__season\">\n\t\t\t\t\t\t<?php esc_html_e( 'Season: ', 'simple-podcasting' ); ?>\n\t\t\t\t\t\t<?php echo esc_html( $season_number ); ?>\n\t\t\t\t\t</span>\n\t\t\t\t<?php endif; ?>\n\t\t\t\t<?php if ( $attributes['displayEpisodeNumber'] && ! empty( $episode_number ) ) : ?>\n\t\t\t\t\t<span class=\"wp-block-podcasting-podcast__episode\">\n\t\t\t\t\t\t<?php esc_html_e( 'Episode: ', 'simple-podcasting' ); ?>\n\t\t\t\t\t\t<?php echo esc_html( $episode_number ); ?>\n\t\t\t\t\t</span>\n\t\t\t\t<?php endif; ?>\n\t\t\t</div>\n\t\t\t<div class=\"wp-block-podcasting-podcast__show-details\">\n\t\t\t\t<?php if ( $attributes['displayDuration'] && ! empty( $duration ) ) : ?>\n\t\t\t\t\t<span class=\"wp-block-podcasting-podcast__duration\">\n\t\t\t\t\t\t<?php esc_html_e( 'Listen Time: ', 'simple-podcasting' ); ?>\n\t\t\t\t\t\t<?php echo esc_html( $duration ); ?>\n\t\t\t\t\t</span>\n\t\t\t\t<?php endif; ?>\n\t\t\t\t<?php if ( $attributes['displayEpisodeType'] && ! empty( $episode_type ) && 'none' !== $episode_type ) : ?>\n\t\t\t\t\t<span class=\"wp-block-podcasting-podcast__episode-type\">\n\t\t\t\t\t\t<?php esc_html_e( 'Episode type: ', 'simple-podcasting' ); ?>\n\t\t\t\t\t\t<?php echo esc_html( $episode_type ); ?>\n\t\t\t\t\t</span>\n\t\t\t\t<?php endif; ?>\n\t\t\t\t<?php if ( $attributes['displayExplicitBadge'] && ! empty( $explicit ) ) : ?>\n\t\t\t\t\t<span class=\"wp-block-podcasting-podcast__explicit-badge\">\n\t\t\t\t\t\t<?php esc_html_e( 'Explicit: ', 'simple-podcasting' ); ?>\n\t\t\t\t\t\t<?php echo esc_html( $explicit ); ?>\n\t\t\t\t\t</span>\n\t\t\t\t<?php endif; ?>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n\t<?php echo wp_kses_post( $content ); ?>\n</div>\n"
  },
  {
    "path": "includes/blocks/podcast-transcript/cite.js",
    "content": "import { registerBlockType, createBlock } from '@wordpress/blocks';\nimport { useBlockProps, RichText } from '@wordpress/block-editor';\n\nconst Edit = ({\n\tattributes,\n\tattributes: { text },\n\tsetAttributes,\n\tclientId,\n\tonReplace,\n}) => {\n\tconst blockProps = useBlockProps();\n\treturn (\n\t\t<RichText\n\t\t\ttagName=\"cite\"\n\t\t\tvalue={text}\n\t\t\tonChange={(content) => setAttributes({ text: content })}\n\t\t\tallowedFormats={[]}\n\t\t\twithoutInteractiveFormatting\n\t\t\tonSplit={(value, isOriginal) => {\n\t\t\t\tlet block;\n\n\t\t\t\tif (isOriginal || value) {\n\t\t\t\t\tblock = createBlock('podcasting/podcast-transcript-cite', {\n\t\t\t\t\t\t...attributes,\n\t\t\t\t\t\tcontent: value,\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tblock = createBlock('core/paragraph');\n\t\t\t\t}\n\n\t\t\t\tif (isOriginal) {\n\t\t\t\t\tblock.clientId = clientId;\n\t\t\t\t}\n\n\t\t\t\treturn block;\n\t\t\t}}\n\t\t\tonReplace={onReplace}\n\t\t\t{...blockProps}\n\t\t/>\n\t);\n};\n\nregisterBlockType('podcasting/podcast-transcript-cite', {\n\tedit: Edit,\n\tsave: ({ attributes: { text } }) => <cite>{text}</cite>,\n});\n"
  },
  {
    "path": "includes/blocks/podcast-transcript/edit.js",
    "content": "import { useBlockProps, RichText, InnerBlocks } from '@wordpress/block-editor';\nimport { __ } from '@wordpress/i18n';\nimport {\n\tRadioControl,\n\tCard,\n\tCardBody,\n\tPlaceholder,\n} from '@wordpress/components';\nimport { useSelect, withSelect } from '@wordpress/data';\nimport { useEffect } from '@wordpress/element';\nimport { useEntityProp } from '@wordpress/core-data';\nimport { serialize } from '@wordpress/blocks';\n\nconst Edit = withSelect((select, { clientId }) => {\n\treturn {\n\t\tinnerBlocks: select('core/block-editor').getBlocksByClientId(clientId),\n\t};\n})(({ attributes, setAttributes, isSelected, innerBlocks, clientId }) => {\n\tconst blockProps = useBlockProps({});\n\n\tconst postType = useSelect(\n\t\t(select) => select('core/editor').getCurrentPostType(),\n\t\t[]\n\t);\n\n\tconst [meta, setMeta] = useEntityProp('postType', postType, 'meta');\n\n\tuseEffect(() => {\n\t\tif (innerBlocks.length) {\n\t\t\tsetMeta({\n\t\t\t\t...meta,\n\t\t\t\tpodcast_transcript: serialize(innerBlocks[0].innerBlocks),\n\t\t\t});\n\t\t}\n\t}, [innerBlocks]);\n\n\tconst isInnerBlockSelected = useSelect((select) =>\n\t\tselect('core/block-editor').hasSelectedInnerBlock(clientId)\n\t);\n\n\tconsole.log(isInnerBlockSelected);\n\n\tconst { display, linkText } = attributes;\n\treturn (\n\t\t<section {...blockProps}>\n\t\t\t{(isSelected || isInnerBlockSelected) && (\n\t\t\t\t<>\n\t\t\t\t\t<Card>\n\t\t\t\t\t\t<CardBody>\n\t\t\t\t\t\t\t<RadioControl\n\t\t\t\t\t\t\t\tlabel={__(\n\t\t\t\t\t\t\t\t\t'Transcript Display',\n\t\t\t\t\t\t\t\t\t'simple-podcasting'\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\tselected={display}\n\t\t\t\t\t\t\t\toptions={[\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tlabel: __(\n\t\t\t\t\t\t\t\t\t\t\t'Display Transcript on Post',\n\t\t\t\t\t\t\t\t\t\t\t'simple-podcasting'\n\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\tvalue: 'post',\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tlabel: __(\n\t\t\t\t\t\t\t\t\t\t\t'Display Link to Transcript',\n\t\t\t\t\t\t\t\t\t\t\t'simple-podcasting'\n\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\tvalue: 'link',\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tlabel: __(\n\t\t\t\t\t\t\t\t\t\t\t'Do not display - only show link in RSS feed',\n\t\t\t\t\t\t\t\t\t\t\t'simple-podcasting'\n\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\tvalue: 'none',\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t]}\n\t\t\t\t\t\t\t\tonChange={(value) =>\n\t\t\t\t\t\t\t\t\tsetAttributes({ display: value })\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</CardBody>\n\t\t\t\t\t</Card>\n\t\t\t\t\t<br />\n\t\t\t\t</>\n\t\t\t)}\n\n\t\t\t{display === 'none' && !isSelected && (\n\t\t\t\t<Placeholder\n\t\t\t\t\ticon=\"microphone\"\n\t\t\t\t\tlabel={__('Podcast Transcript', 'simple-podcasting')}\n\t\t\t\t/>\n\t\t\t)}\n\n\t\t\t{display === 'link' && (\n\t\t\t\t<RichText\n\t\t\t\t\ttagName=\"a\"\n\t\t\t\t\tvalue={linkText}\n\t\t\t\t\tonChange={(content) => setAttributes({ linkText: content })}\n\t\t\t\t\tplaceholder={__('Transcript Link', 'simple-podcasting')}\n\t\t\t\t\tallowedFormats={[]}\n\t\t\t\t/>\n\t\t\t)}\n\n\t\t\t{(isSelected || isInnerBlockSelected || display === 'post') && (\n\t\t\t\t<>\n\t\t\t\t\t<section>\n\t\t\t\t\t\t<InnerBlocks\n\t\t\t\t\t\t\tallowedBlocks={[\n\t\t\t\t\t\t\t\t'core/paragraph',\n\t\t\t\t\t\t\t\t'podcasting/podcast-transcript-cite',\n\t\t\t\t\t\t\t\t'podcasting/podcast-transcript-time',\n\t\t\t\t\t\t\t]}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</section>\n\t\t\t\t</>\n\t\t\t)}\n\t\t</section>\n\t);\n});\n\nexport default Edit;\n"
  },
  {
    "path": "includes/blocks/podcast-transcript/formats.js",
    "content": "import { registerFormatType, toggleFormat } from '@wordpress/rich-text';\nimport { BlockControls } from '@wordpress/block-editor';\nimport { ToolbarGroup, ToolbarButton } from '@wordpress/components';\nimport { useSelect } from '@wordpress/data';\nimport { __ } from '@wordpress/i18n';\n\nconst Cite = ({ isActive, onChange, value }) => {\n\tconst selectedBlock = useSelect((select) => {\n\t\treturn select('core/block-editor').getSelectedBlock();\n\t}, []);\n\n\tif (\n\t\tselectedBlock &&\n\t\tselectedBlock.name !== 'podcasting/podcast-transcript'\n\t) {\n\t\treturn null;\n\t}\n\n\treturn (\n\t\t<BlockControls>\n\t\t\t<ToolbarGroup>\n\t\t\t\t<ToolbarButton\n\t\t\t\t\ticon=\"admin-users\"\n\t\t\t\t\ttitle={__('Speaker Citation', 'simple-podcasting')}\n\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\tonChange(\n\t\t\t\t\t\t\ttoggleFormat(value, {\n\t\t\t\t\t\t\t\ttype: 'podcasting/transcript-cite',\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t);\n\t\t\t\t\t}}\n\t\t\t\t\tisActive={isActive}\n\t\t\t\t/>\n\t\t\t</ToolbarGroup>\n\t\t</BlockControls>\n\t);\n};\n\nregisterFormatType('podcasting/transcript-cite', {\n\ttitle: __('Cite', 'simple-podcasting'),\n\ttagName: 'cite',\n\tclassName: null,\n\tedit: Cite,\n});\n\nconst Time = ({ isActive, onChange, value }) => {\n\tconst selectedBlock = useSelect((select) => {\n\t\treturn select('core/block-editor').getSelectedBlock();\n\t}, []);\n\n\tif (\n\t\tselectedBlock &&\n\t\tselectedBlock.name !== 'podcasting/podcast-transcript'\n\t) {\n\t\treturn null;\n\t}\n\n\treturn (\n\t\t<BlockControls>\n\t\t\t<ToolbarGroup>\n\t\t\t\t<ToolbarButton\n\t\t\t\t\ticon=\"clock\"\n\t\t\t\t\ttitle={__('Timestamp', 'simple-podcasting')}\n\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\tonChange(\n\t\t\t\t\t\t\ttoggleFormat(value, {\n\t\t\t\t\t\t\t\ttype: 'podcasting/transcript-time',\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t);\n\t\t\t\t\t}}\n\t\t\t\t\tisActive={isActive}\n\t\t\t\t/>\n\t\t\t</ToolbarGroup>\n\t\t</BlockControls>\n\t);\n};\n\nregisterFormatType('podcasting/transcript-time', {\n\ttitle: __('Time', 'simple-podcasting'),\n\ttagName: 'time',\n\tclassName: null,\n\tedit: Time,\n});\n"
  },
  {
    "path": "includes/blocks/podcast-transcript/index.js",
    "content": "import { registerBlockType } from '@wordpress/blocks';\nimport { InnerBlocks } from '@wordpress/block-editor';\nimport './styles.css';\n\nimport Edit from './edit';\n\nimport './cite';\nimport './time';\n\nregisterBlockType('podcasting/podcast-transcript', {\n\tedit: Edit,\n\tsave: () => <InnerBlocks.Content />,\n});\n"
  },
  {
    "path": "includes/blocks/podcast-transcript/markup.php",
    "content": "<?php\n/**\n * Podcast Transcript markup\n *\n * @package tenup_podcasting\n *\n * @var array    $attributes Block attributes.\n * @var string   $content    Block content.\n * @var WP_Block $block      Block instance.\n * @var array    $context    Block context.\n */\n\nuse function tenup_podcasting\\transcripts\\get_transcript_link_from_post;\n\nif ( 'none' !== $attributes['display'] ) : ?>\n<div <?php echo get_block_wrapper_attributes(); // phpcs:ignore ?>>\n\t<?php\n\tswitch ( $attributes['display'] ) {\n\t\tcase 'post':\n\t\t\techo wp_kses_post(\n\t\t\t\tdo_blocks(\n\t\t\t\t\tget_post_meta( get_the_ID(), 'podcast_transcript', true )\n\t\t\t\t)\n\t\t\t);\n\t\t\tbreak;\n\t\tcase 'link':\n\t\t\tprintf(\n\t\t\t\t'<p><a href=\"%s\">%s</a></p>',\n\t\t\t\tesc_url( get_transcript_link_from_post( get_post() ) ),\n\t\t\t\tesc_html( $attributes['linkText'] )\n\t\t\t);\n\t\t\tbreak;\n\t}\n\t?>\n</div>\n<?php endif; ?>\n"
  },
  {
    "path": "includes/blocks/podcast-transcript/styles.css",
    "content": ".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",
    "content": "import { registerBlockType, createBlock } from '@wordpress/blocks';\nimport { useBlockProps, RichText } from '@wordpress/block-editor';\n\nconst Edit = ({\n\tattributes,\n\tattributes: { text },\n\tsetAttributes,\n\tclientId,\n\tonReplace,\n}) => {\n\tconst blockProps = useBlockProps();\n\treturn (\n\t\t<RichText\n\t\t\ttagName=\"time\"\n\t\t\tvalue={text}\n\t\t\tonChange={(content) => setAttributes({ text: content })}\n\t\t\tallowedFormats={[]}\n\t\t\twithoutInteractiveFormatting\n\t\t\tonSplit={(value, isOriginal) => {\n\t\t\t\tlet block;\n\n\t\t\t\tif (isOriginal || value) {\n\t\t\t\t\tblock = createBlock('podcasting/podcast-transcript-time', {\n\t\t\t\t\t\t...attributes,\n\t\t\t\t\t\tcontent: value,\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tblock = createBlock('core/paragraph');\n\t\t\t\t}\n\n\t\t\t\tif (isOriginal) {\n\t\t\t\t\tblock.clientId = clientId;\n\t\t\t\t}\n\n\t\t\t\treturn block;\n\t\t\t}}\n\t\t\tonReplace={onReplace}\n\t\t\t{...blockProps}\n\t\t/>\n\t);\n};\n\nregisterBlockType('podcasting/podcast-transcript-time', {\n\tedit: Edit,\n\tsave: ({ attributes: { text } }) => <time>{text}</time>,\n});\n"
  },
  {
    "path": "includes/blocks.php",
    "content": "<?php\n/**\n * Register and enqueue all things block-related.\n *\n * @package tenup_podcasting\n */\n\nnamespace tenup_podcasting\\block;\n\n/**\n * Register block and its assets.\n */\nfunction init() {\n\t$block_asset = require PODCASTING_PATH . 'dist/blocks.asset.php';\n\twp_register_script(\n\t\t'podcasting-block-editor',\n\t\tPODCASTING_URL . 'dist/blocks.js',\n\t\t$block_asset['dependencies'],\n\t\t$block_asset['version'],\n\t\ttrue\n\t);\n\n\twp_register_style(\n\t\t'podcasting-block-editor',\n\t\tPODCASTING_URL . 'dist/blocks.css',\n\t\tarray(),\n\t\t$block_asset['version'],\n\t\t'all'\n\t);\n\n\tregister_block_type(\n\t\t'podcasting/podcast',\n\t\tarray(\n\t\t\t'editor_script'   => 'podcasting-block-editor',\n\t\t\t'editor_style'    => 'podcasting-block-editor',\n\t\t\t'render_callback' => __NAMESPACE__ . '\\render',\n\t\t)\n\t);\n}\nadd_action( 'init', __NAMESPACE__ . '\\init' );\n\n/**\n * Render the block.\n *\n * @param array    $attributes Block attributes.\n * @param string   $content Block content.\n * @param WP_Block $block Block instance.\n * @return string HTML output\n */\nfunction render( $attributes, $content, $block ) {\n\tob_start();\n\tinclude PODCASTING_PATH . 'includes/blocks/podcast/markup.php';\n\treturn ob_get_clean();\n}\n\n/**\n * Register block and its assets.\n */\nfunction init_transcript() {\n\t$podcast_transcript_block_asset = require PODCASTING_PATH . 'dist/podcasting-transcript.asset.php';\n\n\twp_register_script(\n\t\t'podcasting-transcript',\n\t\tPODCASTING_URL . 'dist/podcasting-transcript.js',\n\t\t$podcast_transcript_block_asset['dependencies'],\n\t\t$podcast_transcript_block_asset['version'],\n\t\ttrue\n\t);\n\n\twp_register_style(\n\t\t'podcasting-transcript',\n\t\tPODCASTING_URL . 'dist/podcasting-transcript.css',\n\t\tarray(),\n\t\t$podcast_transcript_block_asset['version'],\n\t\t'all'\n\t);\n\n\t$transcript_block_args = array(\n\t\t'editor_script' => 'podcasting-transcript',\n\t\t'style_handles' => array( 'podcasting-transcript' ),\n\t\t'title'         => __( 'Podcast Transcript', 'simple-podcasting' ),\n\t\t'description'   => '',\n\t\t'textdomain'    => 'simple-podcasting',\n\t\t'name'          => 'podcasting/podcast-transcript',\n\t\t'icon'          => 'format-quote',\n\t\t'api_version'   => 2,\n\t\t'category'      => 'common',\n\t\t'attributes'    => array(\n\t\t\t'transcript' => array(\n\t\t\t\t'type' => 'string',\n\t\t\t),\n\t\t\t'display'    => array(\n\t\t\t\t'type'    => 'string',\n\t\t\t\t'default' => 'post',\n\t\t\t),\n\t\t\t'linkText'   => array(\n\t\t\t\t'type'    => 'string',\n\t\t\t\t'default' => __( 'Transcript Link', 'simple-podcastin' ),\n\t\t\t),\n\t\t),\n\t\t'example'       => array(),\n\t\t'supports'      => array(\n\t\t\t'multiple' => false,\n\t\t\t'inserter' => false,\n\t\t),\n\t);\n\n\t$transcript_block_args['render_callback'] = function ( $attributes, $content, $block ) {\n\t\tob_start();\n\t\tinclude PODCASTING_PATH . 'includes/blocks/podcast-transcript/markup.php';\n\t\treturn ob_get_clean();\n\t};\n\n\tregister_block_type(\n\t\t'podcasting/podcast-transcript',\n\t\t$transcript_block_args\n\t);\n\n\t/**\n\t * Simple cite block.\n\t */\n\tregister_block_type(\n\t\t'podcasting/podcast-transcript-cite',\n\t\tarray(\n\t\t\t'editor_script' => 'podcasting-transcript',\n\t\t\t'title'         => __( 'Cite', 'simple-podcasting' ),\n\t\t\t'description'   => '',\n\t\t\t'textdomain'    => 'simple-podcasting',\n\t\t\t'name'          => 'podcasting/podcast-transcript-cite',\n\t\t\t'icon'          => 'admin-users',\n\t\t\t'api_version'   => 2,\n\t\t\t'category'      => 'text',\n\t\t\t'attributes'    => array(\n\t\t\t\t'text' => array(\n\t\t\t\t\t'type' => 'string',\n\t\t\t\t),\n\t\t\t),\n\t\t\t'supports'      => array(\n\t\t\t\t'html'     => false,\n\t\t\t\t'reusable' => false,\n\t\t\t),\n\t\t\t'parent'        => [ 'podcasting/podcast-transcript' ],\n\t\t)\n\t);\n\n\t/**\n\t * Simple time block.\n\t */\n\tregister_block_type(\n\t\t'podcasting/podcast-transcript-time',\n\t\tarray(\n\t\t\t'editor_script' => 'podcasting-transcript',\n\t\t\t'title'         => __( 'Time', 'simple-podcasting' ),\n\t\t\t'description'   => '',\n\t\t\t'textdomain'    => 'simple-podcasting',\n\t\t\t'name'          => 'podcasting/podcast-transcript-time',\n\t\t\t'icon'          => 'clock',\n\t\t\t'api_version'   => 2,\n\t\t\t'category'      => 'text',\n\t\t\t'attributes'    => array(\n\t\t\t\t'text' => array(\n\t\t\t\t\t'type' => 'string',\n\t\t\t\t),\n\t\t\t),\n\t\t\t'supports'      => array(\n\t\t\t\t'html'     => false,\n\t\t\t\t'reusable' => false,\n\t\t\t),\n\t\t\t'parent'        => [ 'podcasting/podcast-transcript' ],\n\t\t)\n\t);\n}\nadd_action( 'init', __NAMESPACE__ . '\\init_transcript' );\n\n/**\n * Registers block for Podcast Platforms.\n */\nfunction register_podcast_platforms_block() {\n\tif ( ! file_exists( PODCASTING_PATH . 'dist/podcast-platforms-block.asset.php' ) ) {\n\t\treturn;\n\t}\n\n\t$block_asset = require PODCASTING_PATH . 'dist/podcast-platforms-block.asset.php';\n\n\twp_register_script(\n\t\t'podcast-platforms-block-editor',\n\t\tPODCASTING_URL . 'dist/podcast-platforms-block.js',\n\t\t$block_asset['dependencies'],\n\t\t$block_asset['version'],\n\t\ttrue\n\t);\n\n\twp_localize_script(\n\t\t'podcast-platforms-block-editor',\n\t\t'podcastingPlatformVars',\n\t\tarray(\n\t\t\t'podcastingUrl' => PODCASTING_URL,\n\t\t)\n\t);\n\n\twp_register_style(\n\t\t'podcast-platforms-block-editor',\n\t\tPODCASTING_URL . 'dist/podcast-platforms-block.css',\n\t\tarray(),\n\t\t$block_asset['version'],\n\t\t'all'\n\t);\n\n\tregister_block_type(\n\t\t'podcasting/podcast-platforms',\n\t\tarray(\n\t\t\t'editor_script'   => 'podcast-platforms-block-editor',\n\t\t\t'editor_style'    => 'podcast-platforms-block-editor',\n\t\t\t'style'           => 'podcast-platforms-block-editor',\n\t\t\t'render_callback' => __NAMESPACE__ . '\\render_podcasting_platforms',\n\t\t)\n\t);\n}\nadd_action( 'init', __NAMESPACE__ . '\\register_podcast_platforms_block' );\n\n/**\n * Renders the block `podcasting/podcast-platforms`.\n *\n * @param array $attrs Block attributes.\n * @return string\n */\nfunction render_podcasting_platforms( $attrs ) {\n\tif ( ! ( is_array( $attrs ) && isset( $attrs['showId'] ) ) ) {\n\t\treturn '';\n\t}\n\n\t$show_id   = isset( $attrs['showId'] ) ? $attrs['showId'] : 0;\n\t$icon_size = isset( $attrs['iconSize'] ) ? $attrs['iconSize'] : 48;\n\t$align     = isset( $attrs['align'] ) ? $attrs['align'] : 'center';\n\n\tif ( 0 === $show_id ) {\n\t\treturn '';\n\t}\n\n\t$supported_platforms = \\tenup_podcasting\\get_supported_platforms();\n\t$platforms           = get_term_meta( $show_id, 'podcasting_platforms', true );\n\t$theme               = get_term_meta( $show_id, 'podcasting_icon_theme', true );\n\t$theme               = empty( $theme ) ? 'color' : $theme;\n\n\tif ( ! is_array( $platforms ) || empty( $platforms ) ) {\n\t\treturn '';\n\t}\n\n\tob_start();\n\n\t?>\n\n\t<div class=\"simple-podcasting__podcast-platforms\">\n\t\t<div class='simple-podcasting__podcasting-platform-list <?php echo esc_attr( 'simple-podcasting__podcasting-platform-list--' . $align ); ?>'>\n\t\t\t<?php foreach ( $platforms as $slug => $url ) : ?>\n\t\t\t\t<?php\n\t\t\t\tif ( empty( $url ) ) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t$podcast_title = $supported_platforms[ $slug ]['title'];\n\t\t\t\t?>\n\n\t\t\t\t<span class='simple-podcasting__podcasting-platform-list-item'>\n\t\t\t\t\t<a href=\"<?php echo esc_url( $url ); ?>\" target=\"_blank\" title=\"<?php echo esc_attr( $podcast_title ); ?>\" aria-label=\"<?php echo esc_attr( $podcast_title ); ?>\">\n\t\t\t\t\t\t<img\n\t\t\t\t\t\t\tclass=\"simple-pocasting__icon-size--<?php echo esc_attr( $icon_size ); ?>\"\n\t\t\t\t\t\t\tsrc=\"<?php printf( '%sdist/images/icons/%s/%s-100.png', esc_url( PODCASTING_URL ), esc_attr( $slug ), esc_attr( $theme ) ); ?>\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</a>\n\t\t\t\t</span>\n\t\t\t<?php endforeach; ?>\n\t\t</div>\n\t</div>\n\n\t<?php\n\n\treturn ob_get_clean();\n}\n\n/**\n * Register JS block-specific strings.\n *\n * These need to be available in PHP for .pot creation but don't need to do anything.\n *\n * @return void\n */\nfunction register_js_strings() {\n\t__( 'Insert a podcast episode into a post. To add it to a podcast feed, select a podcast in document settings.', 'simple-podcasting' );\n\t__( 'Podcast Settings', 'simple-podcasting' );\n\t__( 'Length (MM:SS)', 'simple-podcasting' );\n\t__( 'a podcast episode', 'simple-podcasting' );\n\t__( 'Season Number', 'simple-podcasting' );\n\t__( 'Episode Number', 'simple-podcasting' );\n\t__( 'Episode Type', 'simple-podcasting' );\n}\nadd_action( 'init', __NAMESPACE__ . '\\register_js_strings' );\n\n/**\n * Register and load block editor translations.\n *\n * In an ideal world, this would only load the translations absolutely necessary in a JS context.\n * Since this is a small plugin it's still okay for now.\n *\n * @return void\n */\nfunction load_translations() {\n\tif ( function_exists( 'wp_set_script_translations' ) ) {\n\t\twp_set_script_translations( 'podcasting-block-editor', 'simple-podcasting' );\n\t} elseif ( function_exists( 'gutenberg_get_jed_locale_data' ) ) {\n\t\t$data = wp_json_encode( gutenberg_get_jed_locale_data( 'simple-podcasting' ) );\n\t\twp_add_inline_script(\n\t\t\t'wp-i18n',\n\t\t\t'wp.i18n.setLocaleData( ' . $data . ', \"simple-podcasting\" );'\n\t\t);\n\t}\n}\nadd_action( 'enqueue_block_editor_assets', __NAMESPACE__ . '\\load_translations' );\n\n/**\n * Delete left over post meta after deleting podcast block.\n *\n * @param WP_Post         $post     Inserted or updated post object.\n * @param WP_REST_Request $request  Request object.\n * @param bool            $creating True when creating a post, false when updating.\n * @return void\n */\nfunction block_editor_meta_cleanup( $post, $request, $creating ) {\n\tif ( $creating ) {\n\t\treturn;\n\t}\n\n\tif ( has_block( 'podcasting/podcast', $post->ID ) ) {\n\t\treturn;\n\t}\n\n\t\\tenup_podcasting\\helpers\\delete_all_podcast_meta( $post->ID );\n}\nadd_action( 'rest_after_insert_post', __NAMESPACE__ . '\\block_editor_meta_cleanup', 10, 3 );\n\n/**\n * Returns podcast platforms meta.\n */\nfunction ajax_get_podcast_platforms() {\n\t$term_id = filter_input( INPUT_GET, 'show_id', FILTER_VALIDATE_INT );\n\n\tif ( ! $term_id ) {\n\t\twp_send_json_error( esc_html__( 'Term ID not valid', 'simple-podcasting' ) );\n\t}\n\n\t$platforms = get_term_meta( $term_id, 'podcasting_platforms', true );\n\n\tif ( ! is_array( $platforms ) ) {\n\t\twp_send_json_error( esc_html__( 'No shows found', 'simple-podcasting' ) );\n\t}\n\n\t$platforms = array_filter(\n\t\t$platforms,\n\t\tfunction ( $platform ) {\n\t\t\treturn ! empty( $platform );\n\t\t}\n\t);\n\n\t$theme = get_term_meta( $term_id, 'podcasting_icon_theme', true );\n\n\tif ( empty( $theme ) ) {\n\t\t$theme = 'color';\n\t}\n\n\t$result = array(\n\t\t'platforms' => $platforms,\n\t\t'theme'     => $theme,\n\t);\n\n\twp_send_json_success( $result );\n}\nadd_action( 'wp_ajax_get_podcast_platforms', __NAMESPACE__ . '\\ajax_get_podcast_platforms' );\n\n/**\n * Latest podcast query for front-end.\n *\n * @param Object $query query object.\n */\nfunction latest_episode_query_loop( $query ) {\n\n\t// update query to only return posts that have a podcast selected\n\treturn [\n\t\t'post_type'      => 'post',\n\t\t'posts_per_page' => 1,\n\t\t'orderby'        => 'date',\n\t\t'order'          => 'DESC',\n\t\t'tax_query'      => [\n\t\t\t[\n\t\t\t\t'taxonomy' => 'podcasting_podcasts',\n\t\t\t\t'field'    => 'term_id',\n\t\t\t\t'operator' => 'EXISTS',\n\t\t\t],\n\t\t],\n\t];\n}\n\n/**\n * Latest podcast check.\n *\n * @param String $pre_render   pre render object.\n * @param Array  $parsed_block parsed block object.\n */\nfunction latest_episode_check( $pre_render, $parsed_block ) {\n\n\tif ( isset( $parsed_block['attrs']['namespace'] ) && 'podcasting/latest-episode' === $parsed_block['attrs']['namespace'] ) {\n\t\tadd_action( 'query_loop_block_query_vars', __NAMESPACE__ . '\\latest_episode_query_loop' );\n\t}\n}\nadd_filter( 'pre_render_block', __NAMESPACE__ . '\\latest_episode_check', 10, 2 );\n\n/**\n * Latest podcast query in editor.\n *\n * @param Array           $args    query args.\n * @param WP_REST_Request $request request object.\n */\nfunction latest_episode_query_api( $args, $request ) {\n\n\t$podcasting_podcasts = $request->get_param( 'podcastingQuery' );\n\n\tif ( 'not_empty' === $podcasting_podcasts ) {\n\t\t$args = [\n\t\t\t'post_type'      => 'post',\n\t\t\t'posts_per_page' => 1,\n\t\t\t'orderby'        => 'date',\n\t\t\t'order'          => 'DESC',\n\t\t\t'tax_query'      => [\n\t\t\t\t[\n\t\t\t\t\t'taxonomy' => 'podcasting_podcasts',\n\t\t\t\t\t'field'    => 'term_id',\n\t\t\t\t\t'operator' => 'EXISTS',\n\t\t\t\t],\n\t\t\t],\n\t\t];\n\t}\n\n\treturn $args;\n}\nadd_filter( 'rest_post_query', __NAMESPACE__ . '\\latest_episode_query_api', 10, 2 );\n"
  },
  {
    "path": "includes/create-podcast.php",
    "content": "<?php\n/**\n * Defines class to handle the validation, sanitization\n * and creation of a podcast.\n *\n * @package tenup_podcasting\n */\n\nnamespace tenup_podcasting;\n\n/**\n * Provides methods to create a podcast.\n */\nclass Create_Podcast {\n\t/**\n\t * Name of the podcast.\n\t *\n\t * @var string\n\t */\n\tprotected $podcast_name = '';\n\n\t/**\n\t * Name of the podcast artist.\n\t *\n\t * @var string\n\t */\n\tprotected $podcast_talent_name = '';\n\n\t/**\n\t * Summary of the podcast.\n\t *\n\t * @var string\n\t */\n\tprotected $podcast_description = '';\n\n\t/**\n\t * Podcast's primary category.\n\t *\n\t * @var string\n\t */\n\tprotected $podcast_category = '';\n\n\t/**\n\t * ID of the podcast cover.\n\t *\n\t * @var int\n\t */\n\tprotected $podcast_cover_id = 0;\n\n\t/**\n\t * Verifies nonce needed for the creation of a podcast.\n\t *\n\t * @return boolean|WP_Error\n\t */\n\tpublic function verify_nonce() {\n\t\t$is_nonce_set = isset( $_POST['simple-podcasting-create-show-nonce-field'] );\n\n\t\tif ( ! $is_nonce_set ) {\n\t\t\treturn false;\n\t\t}\n\n\t\t$is_nonce_verified = wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['simple-podcasting-create-show-nonce-field'] ) ), 'simple-podcasting-create-show-action' );\n\n\t\tif ( ! $is_nonce_verified ) {\n\t\t\treturn new \\WP_Error(\n\t\t\t\t'simple_podcasting_nonce_verification_failed',\n\t\t\t\tesc_html__( 'Nonce verification failed.' )\n\t\t\t);\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Sanitizes the podcast fields.\n\t */\n\tpublic function sanitize_podcast_fields() {\n\t\t$this->podcast_name        = isset( $_POST['podcast-name'] ) ? sanitize_text_field( wp_unslash( $_POST['podcast-name'] ) ) : null;\n\t\t$this->podcast_talent_name = isset( $_POST['podcast-artist'] ) ? sanitize_text_field( wp_unslash( $_POST['podcast-artist'] ) ) : null;\n\t\t$this->podcast_description = isset( $_POST['podcast-description'] ) ? sanitize_text_field( wp_unslash( $_POST['podcast-description'] ) ) : null;\n\t\t$this->podcast_category    = isset( $_POST['podcast-category'] ) ? sanitize_text_field( wp_unslash( $_POST['podcast-category'] ) ) : null;\n\t\t$this->podcast_cover_id    = isset( $_POST['podcast-cover-image-id'] ) ? absint( wp_unslash( $_POST['podcast-cover-image-id'] ) ) : null;\n\t}\n\n\t/**\n\t * Create a podcast and saves its corressponding meta.\n\t *\n\t * @return boolean|WP_Error\n\t */\n\tpublic function save_podcast_fields() {\n\t\tif ( empty( $this->podcast_name ) ) {\n\t\t\treturn new \\WP_Error(\n\t\t\t\t'simple_podcasting_podcast_name_empty',\n\t\t\t\tesc_html__( 'A podcast name is required.' )\n\t\t\t);\n\t\t}\n\n\t\tif ( empty( $this->podcast_talent_name ) ) {\n\t\t\treturn new \\WP_Error(\n\t\t\t\t'simple_podcasting_podcast_artist_name_empty',\n\t\t\t\tesc_html__( 'A podcast artist name is required.' )\n\t\t\t);\n\t\t}\n\n\t\tif ( empty( $this->podcast_description ) ) {\n\t\t\treturn new \\WP_Error(\n\t\t\t\t'simple_podcasting_podcast_summary_empty',\n\t\t\t\tesc_html__( 'A podcast summary name is required.' )\n\t\t\t);\n\t\t}\n\n\t\tif ( empty( $this->podcast_category ) ) {\n\t\t\treturn new \\WP_Error(\n\t\t\t\t'simple_podcasting_podcast_category_empty',\n\t\t\t\tesc_html__( 'A podcast category is required.' )\n\t\t\t);\n\t\t}\n\n\t\tif ( empty( $this->podcast_cover_id ) ) {\n\t\t\treturn new \\WP_Error(\n\t\t\t\t'simple_podcasting_podcast_cover_image_empty',\n\t\t\t\tesc_html__( 'A podcast cover image is required.' )\n\t\t\t);\n\t\t}\n\n\t\t$result = wp_insert_term(\n\t\t\t$this->podcast_name,\n\t\t\tPODCASTING_TAXONOMY_NAME\n\t\t);\n\n\t\tif ( is_wp_error( $result ) ) {\n\t\t\treturn $result;\n\t\t}\n\n\t\t/** Add podcast talent name. */\n\t\tif ( $this->podcast_talent_name ) {\n\t\t\tupdate_term_meta( $result['term_id'], 'podcasting_talent_name', $this->podcast_talent_name );\n\t\t}\n\n\t\t/** Add podcast summary. */\n\t\tif ( $this->podcast_description ) {\n\t\t\tupdate_term_meta( $result['term_id'], 'podcasting_summary', $this->podcast_description );\n\t\t}\n\n\t\t/** Add podcast category. */\n\t\tif ( $this->podcast_category ) {\n\t\t\tupdate_term_meta( $result['term_id'], 'podcasting_category_1', $this->podcast_category );\n\t\t}\n\n\t\t/** Add podcast cover ID and URL. */\n\t\tif ( $this->podcast_cover_id ) {\n\t\t\t$image_url = wp_get_attachment_url( (int) $this->podcast_cover_id );\n\t\t\tupdate_term_meta( $result['term_id'], 'podcasting_image', $this->podcast_cover_id );\n\t\t\tupdate_term_meta( $result['term_id'], 'podcasting_image_url', $image_url );\n\t\t}\n\n\t\treturn true;\n\t}\n}\n"
  },
  {
    "path": "includes/customize-feed.php",
    "content": "<?php\n/**\n * Customize the feed for a specific podcast. Insert the podcast data stored in term meta.\n *\n * @package tenup_podcasting\n */\n\nnamespace tenup_podcasting;\n\nuse function tenup_podcasting\\transcripts\\get_transcript_link_from_post;\n\n/**\n * Add an itunes podcasting header.\n */\nfunction xmlns() {\n\techo \"\\n\\t\" . 'xmlns:itunes=\"http://www.itunes.com/dtds/podcast-1.0.dtd\"' . \"\\n\";\n}\nadd_action( 'rss2_ns', __NAMESPACE__ . '\\xmlns' );\n\n/**\n * Get the current term, verifying that is has a term_id.\n *\n * @return object WP_Term or false if not a term feed.\n */\nfunction get_the_term() {\n\t$queried_object = get_queried_object();\n\tif ( ! $queried_object || ! $queried_object->term_id ) {\n\t\treturn false;\n\t}\n\treturn $queried_object;\n}\n\n/**\n * Adjust the title for podcasting feeds.\n *\n * @param  string $output The feed title.\n *\n * @return string         The adjusted feed title.\n */\nfunction bloginfo_rss_name( $output ) {\n\t$term = get_the_term();\n\tif ( ! $term ) {\n\t\treturn $output;\n\t}\n\n\treturn apply_filters( 'simple_podcasting_feed_title', $output, $term );\n}\nadd_filter( 'wp_title_rss', __NAMESPACE__ . '\\bloginfo_rss_name' );\n\n// Don't show audio widgets in the feed.\nadd_filter( 'wp_audio_shortcode', '__return_empty_string', 999 );\n\n/**\n * Sets the podcast language and description in the feed to the values in the term edit screen.\n *\n * @param string $output    The value being displayed.\n * @param string $requested The item that was requested.\n *\n * @return mixed\n */\nfunction bloginfo_rss( $output, $requested ) {\n\t$term = get_the_term();\n\tif ( ! $term ) {\n\t\treturn $output;\n\t}\n\n\tif ( 'language' === $requested ) {\n\t\t$lang = get_term_meta( $term->term_id, 'podcasting_language', true );\n\t\tif ( $lang ) {\n\t\t\t$lang   = str_replace( '_', '-', $lang );\n\t\t\t$output = $lang;\n\t\t}\n\t}\n\tif ( 'description' === $requested ) {\n\t\t$summary = get_term_meta( $term->term_id, 'podcasting_summary', true );\n\n\t\tif ( empty( $summary ) ) {\n\t\t\t$summary = get_bloginfo( 'description' );\n\t\t}\n\n\t\tif ( ! empty( $summary ) ) {\n\t\t\t$output = '<![CDATA[' . $summary . ']]>';\n\t\t}\n\t}\n\treturn $output;\n}\nadd_filter( 'bloginfo_rss', __NAMESPACE__ . '\\bloginfo_rss', 10, 2 );\n\n/**\n * Add podcasting details to the feed header.\n */\nfunction feed_head() {\n\t$term = get_the_term();\n\tif ( ! $term ) {\n\t\treturn;\n\t}\n\t$subtitle = get_term_meta( $term->term_id, 'podcasting_subtitle', true );\n\n\tif ( empty( $subtitle ) ) {\n\t\t$subtitle = get_bloginfo( 'description' );\n\t}\n\n\tif ( ! empty( $subtitle ) ) {\n\t\techo '<itunes:subtitle>' . esc_html( wp_strip_all_tags( $subtitle ) ) . \"</itunes:subtitle>\\n\";\n\t}\n\n\t$author = get_term_meta( $term->term_id, 'podcasting_talent_name', true );\n\tif ( ! empty( $author ) ) {\n\t\techo '<itunes:author>' . esc_html( wp_strip_all_tags( $author ) ) . \"</itunes:author>\\n\";\n\t}\n\n\techo '<itunes:owner>';\n\n\tif ( ! empty( $author ) ) {\n\t\techo '<itunes:name>' . esc_html( wp_strip_all_tags( $author ) ) . \"</itunes:name>\\n\";\n\t}\n\n\t$podcasting_email = get_term_meta( $term->term_id, 'podcasting_email', true );\n\t$email            = ! empty( $podcasting_email ) ? $podcasting_email : get_bloginfo( 'admin_email' );\n\tif ( ! empty( $email ) ) {\n\t\techo '<itunes:email>' . esc_html( wp_strip_all_tags( $email ) ) . \"</itunes:email>\\n\";\n\t}\n\n\techo '</itunes:owner>';\n\n\t$copyright = get_term_meta( $term->term_id, 'podcasting_copyright', true );\n\n\tif ( ! empty( $copyright ) ) {\n\t\techo '<copyright>' . esc_html( wp_strip_all_tags( $copyright ) ) . \"</copyright>\\n\";\n\t}\n\n\t$explicit = get_term_meta( $term->term_id, 'podcasting_explicit', true );\n\n\techo '<itunes:explicit>';\n\n\tif ( empty( $explicit ) ) {\n\t\techo 'no';\n\t} else {\n\t\techo esc_html( $explicit );\n\t}\n\n\techo \"</itunes:explicit>\\n\";\n\n\t$image = get_term_meta( $term->term_id, 'podcasting_image', true );\n\n\tif ( ! empty( $image ) ) {\n\t\techo \"<itunes:image href='\" . esc_url( wp_get_attachment_url( $image ) ) . \"' />\\n\";\n\t}\n\n\t$keywords = get_term_meta( $term->term_id, 'podcasting_keywords', true );\n\n\tif ( ! empty( $keywords ) ) {\n\t\techo '<itunes:keywords>' . esc_html( $keywords ) . \"</itunes:keywords>\\n\";\n\t}\n\n\t$type_of_show = get_term_meta( $term->term_id, 'podcasting_type_of_show', true );\n\n\tif ( $type_of_show && '0' !== $type_of_show ) {\n\t\techo '<itunes:type>' . esc_html( $type_of_show ) . \"</itunes:type>\\n\";\n\t}\n\n\tgenerate_categories();\n}\nadd_action( 'rss2_head', __NAMESPACE__ . '\\feed_head' );\n\n/**\n * Output the feed for a single podcast.\n */\nfunction feed_item() {\n\tglobal $post;\n\t$term = get_the_term();\n\tif ( ! $term ) {\n\t\treturn false;\n\t}\n\n\t$feed_item = array(\n\t\t'author'      => get_option( 'podcasting_talent_name' ),\n\t\t'explicit'    => get_post_meta( $post->ID, 'podcast_explicit', true ),\n\t\t'captioned'   => get_post_meta( $post->ID, 'podcast_captioned', true ),\n\t\t'keywords'    => '',\n\t\t'image'       => '',\n\t\t'summary'     => '',\n\t\t'subtitle'    => '',\n\t\t'duration'    => get_post_meta( $post->ID, 'podcast_duration', true ),\n\t\t'season'      => get_post_meta( $post->ID, 'podcast_season_number', true ),\n\t\t'episode'     => get_post_meta( $post->ID, 'podcast_episode_number', true ),\n\t\t'episodeType' => get_post_meta( $post->ID, 'podcast_episode_type', true ),\n\t\t'transcript'  => get_post_meta( $post->ID, 'podcast_transcript', true ),\n\t);\n\n\tif ( empty( $feed_item['author'] ) ) {\n\t\t$feed_item['author'] = get_the_author();\n\t}\n\n\t// fall back to the podcast setting.\n\tif ( empty( $feed_item['explicit'] ) ) {\n\t\t$feed_item['explicit'] = get_term_meta( $term->term_id, 'podcasting_explicit', true );\n\t}\n\n\t// \"no\" explicit by default\n\tif ( empty( $feed_item['explicit'] ) ) {\n\t\t$feed_item['explicit'] = 'no';\n\t}\n\n\t// Add the featured image if available.\n\tif ( has_post_thumbnail( $post->ID ) ) {\n\t\t$feed_item['image'] = wp_get_attachment_image_src( get_post_thumbnail_id( $post->ID ), 'post-thumbnail' );\n\t\tif ( ! empty( $feed_item['image'] ) && is_array( $feed_item['image'] ) ) {\n\t\t\t$feed_item['image'] = $feed_item['image'][0];\n\t\t}\n\t}\n\n\tif ( has_excerpt() ) {\n\t\t$feed_item['summary'] = get_the_excerpt();\n\t} else {\n\t\t$feed_item['summary'] = get_term_meta( $term->term_id, 'podcasting_summary', true );\n\t}\n\t$feed_item['summary'] = apply_filters( 'the_excerpt_rss', $feed_item['summary'] );\n\n\t$feed_item['subtitle'] = wp_trim_words( $feed_item['summary'], 10, '&#8230;' );\n\n\t/**\n\t * Filter podcasting feed item data\n\t *\n\t * @since 1.3.0\n\t *\n\t * @param array $feed_item {\n\t *     Item data to filter.\n\t *\n\t *     @type string $author      Podcast author.\n\t *     @type string $explicit    Explicit content (yes|no|clean).\n\t *     @type string $captioned   Closed Captioned (\"1\"|\"0\"). Optional.\n\t *     @type string $keywords    Episode keywords. Optional.\n\t *     @type string $image       Episode image. Optional.\n\t *     @type string $summary     Episode summary.\n\t *     @type string $subtitle    Episode subtitle.\n\t *     @type string $duration    Episode duration (HH:MM). Optional.\n\t *     @type string $season      Season number Optional.\n\t *     @type string $episode     Episode number Optional.\n\t *     @type string $episodeType Episode type Optional.\n\t * }\n\t * @param int $post->ID Podcast episode post ID.\n\t * @param int $term->term_id Podcast term ID.\n\t */\n\t$feed_item = apply_filters( 'simple_podcasting_feed_item', $feed_item, $post->ID, $term->term_id );\n\n\t// Output enclosure if it's not present in the post\n\t$enclosure = get_post_meta( $post->ID, 'enclosure', true );\n\tif ( empty( $enclosure ) ) {\n\t\tdisplay_rss_enclosure( $post );\n\t}\n\n\t// Output all custom RSS tags.\n\techo '<itunes:author>' . esc_html( $feed_item['author'] ) . \"</itunes:author>\\n\";\n\techo '<itunes:explicit>' . esc_html( $feed_item['explicit'] ) . \"</itunes:explicit>\\n\";\n\tif ( $feed_item['captioned'] ) {\n\t\techo \"<itunes:isClosedCaptioned>Yes</itunes:isClosedCaptioned>\\n\";\n\t}\n\tif ( ! empty( $feed_item['image'] ) ) {\n\t\techo \"<itunes:image href='\" . esc_url( $feed_item['image'] ) . \"' />\\n\";\n\t}\n\tif ( ! empty( $feed_item['keywords'] ) ) {\n\t\techo '<itunes:keywords>' . esc_html( $feed_item['keywords'] ) . \"</itunes:keywords>\\n\";\n\t}\n\techo '<itunes:subtitle>' . esc_html( $feed_item['subtitle'] ) . \"</itunes:subtitle>\\n\";\n\tif ( ! empty( $feed_item['duration'] ) ) {\n\t\techo '<itunes:duration>' . esc_html( $feed_item['duration'] ) . \"</itunes:duration>\\n\";\n\t}\n\tif ( ! empty( $feed_item['season'] ) ) {\n\t\techo '<itunes:season>' . esc_html( $feed_item['season'] ) . \"</itunes:season>\\n\";\n\t}\n\tif ( ! empty( $feed_item['episode'] ) ) {\n\t\techo '<itunes:episode>' . esc_html( $feed_item['episode'] ) . \"</itunes:episode>\\n\";\n\t}\n\tif ( ! empty( $feed_item['episodeType'] ) && 'none' !== $feed_item['episodeType'] ) {\n\t\techo '<itunes:episodeType>' . esc_html( $feed_item['episodeType'] ) . \"</itunes:episodeType>\\n\";\n\t}\n\tif ( ! empty( $feed_item['transcript'] ) && '' !== $feed_item['transcript'] ) {\n\t\techo '<podcast:transcript>' . esc_url( get_transcript_link_from_post( $post ) ) . \"</podcast:transcript>\\n\";\n\t}\n}\nadd_action( 'rss2_item', __NAMESPACE__ . '\\feed_item' );\n\n/**\n * Displays the enclosure feed for podcasts.\n *\n * @param  WP_Post $post The post object.\n *\n * @return void\n */\nfunction display_rss_enclosure( $post ) {\n\t$podcast_url      = get_post_meta( $post->ID, 'podcast_url', true );\n\t$podcast_filesize = get_post_meta( $post->ID, 'podcast_filesize', true );\n\t$podcast_mime     = get_post_meta( $post->ID, 'podcast_mime', true );\n\n\tif ( ! empty( $podcast_url ) ) {\n\t\t$enclosure = \"<enclosure url='\" .\n\t\tesc_url( str_replace( 'https://', 'http://', $podcast_url ) ) .\n\t\t\"' length='\" .\n\t\tesc_attr( $podcast_filesize ) .\n\t\t\"' type='\" .\n\t\tesc_attr( $podcast_mime ) .\n\t\t\"' />\\n\";\n\n\t\techo wp_kses(\n\t\t\t$enclosure,\n\t\t\tarray(\n\t\t\t\t'enclosure' => array(\n\t\t\t\t\t'url'    => array(),\n\t\t\t\t\t'length' => array(),\n\t\t\t\t\t'type'   => array(),\n\t\t\t\t),\n\t\t\t)\n\t\t);\n\t}\n}\n\n/**\n * Generate the category elements from the given option (e.g. podcasting_category_1).\n */\nfunction generate_categories() {\n\t$term = get_the_term();\n\tif ( ! $term ) {\n\t\treturn false;\n\t}\n\n\t$categories[] = get_term_meta( $term->term_id, 'podcasting_category_1', true );\n\t$categories[] = get_term_meta( $term->term_id, 'podcasting_category_2', true );\n\t$categories[] = get_term_meta( $term->term_id, 'podcasting_category_3', true );\n\n\t$categories = array_filter( $categories );\n\n\t$reduced_categories = array();\n\n\tforeach ( $categories as $category ) {\n\t\t$category = explode( ':', $category );\n\n\t\tif ( ! isset( $reduced_categories[ $category[0] ] ) ) {\n\t\t\t$reduced_categories[ $category[0] ] = array();\n\t\t}\n\n\t\tif ( ! empty( $category[1] ) ) {\n\t\t\t$reduced_categories[ $category[0] ][] = $category[1];\n\t\t}\n\t}\n\n\t$categories = get_podcasting_categories();\n\n\tforeach ( $reduced_categories as $parent => $subs ) {\n\t\tif ( ! isset( $categories[ $parent ] ) ) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif ( empty( $subs ) ) {\n\t\t\techo '<itunes:category text=\"' . esc_attr( $categories[ $parent ]['name'] ) . \"\\\" />\\n\";\n\t\t} else {\n\t\t\techo '<itunes:category text=\"' . esc_attr( $categories[ $parent ]['name'] ) . \"\\\">\\n\";\n\n\t\t\tforeach ( $subs as $sub ) {\n\t\t\t\tif ( ! isset( $categories[ $parent ]['subcategories'][ $sub ] ) ) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\techo \"\\t<itunes:category text=\\\"\" . esc_attr( $categories[ $parent ]['subcategories'][ $sub ] ) . \"\\\" />\\n\";\n\t\t\t}\n\n\t\t\techo \"</itunes:category>\\n\";\n\t\t}\n\t}\n}\n\n/**\n * Ensure the excerpt is actually used for the excerpt.\n *\n * @param  string $output The excerpt.\n *\n * @return string         The filtered excerpt.\n */\nfunction empty_rss_excerpt( $output ) {\n\t$excerpt = get_the_excerpt();\n\n\tif ( empty( $excerpt ) ) {\n\t\treturn '';\n\t}\n\n\treturn $output;\n}\n// Run it super late after any other filters may have inserted something.\nadd_filter( 'the_excerpt_rss', __NAMESPACE__ . '\\empty_rss_excerpt', 1000 );\n\n/**\n * Filter the feed query.\n * - Default items listed on the feed to 250.\n *\n * @param WP_Query $query The WP_Query instance.\n * @return void\n */\nfunction pre_get_posts( $query ) {\n\t// do nothing if not the feed query.\n\tif ( ! $query->is_feed() ) {\n\t\treturn;\n\t}\n\n\t$per_page = apply_filters( 'simple_podcasting_episodes_per_page', PODCASTING_ITEMS_PER_PAGE );\n\n\t$query->set( 'posts_per_rss', $per_page );\n}\n\n// Filter the feed query.\nadd_action( 'pre_get_posts', __NAMESPACE__ . '\\pre_get_posts', 10, 1 );\n"
  },
  {
    "path": "includes/datatypes.php",
    "content": "<?php\n/**\n * Register the data types\n *\n * @package tenup_podcasting\n */\n\nnamespace tenup_podcasting;\n\n/**\n * Register the post meta to be associated with podcasts.\n */\nfunction register_meta() {\n\t\\register_meta(\n\t\t'post',\n\t\t'podcast_url',\n\t\tarray(\n\t\t\t'show_in_rest' => true,\n\t\t\t'type'         => 'string',\n\t\t\t'single'       => true,\n\t\t)\n\t);\n\n\t\\register_meta(\n\t\t'post',\n\t\t'podcast_explicit',\n\t\tarray(\n\t\t\t'show_in_rest' => true,\n\t\t\t'type'         => 'string',\n\t\t\t'single'       => true,\n\t\t\t'default'      => 'no',\n\t\t)\n\t);\n\n\t\\register_meta(\n\t\t'post',\n\t\t'podcast_captioned',\n\t\tarray(\n\t\t\t'show_in_rest' => true,\n\t\t\t'type'         => 'boolean',\n\t\t\t'single'       => true,\n\t\t)\n\t);\n\n\t\\register_meta(\n\t\t'post',\n\t\t'podcast_duration',\n\t\tarray(\n\t\t\t'show_in_rest' => true,\n\t\t\t'type'         => 'string',\n\t\t\t'single'       => true,\n\t\t)\n\t);\n\n\t\\register_meta(\n\t\t'post',\n\t\t'podcast_filesize',\n\t\tarray(\n\t\t\t'show_in_rest' => true,\n\t\t\t'type'         => 'number',\n\t\t\t'single'       => true,\n\t\t)\n\t);\n\n\t\\register_meta(\n\t\t'post',\n\t\t'podcast_mime',\n\t\tarray(\n\t\t\t'show_in_rest' => true,\n\t\t\t'type'         => 'string',\n\t\t\t'single'       => true,\n\t\t)\n\t);\n\n\t\\register_meta(\n\t\t'post',\n\t\t'enclosure',\n\t\tarray(\n\t\t\t'show_in_rest' => true,\n\t\t\t'type'         => 'string',\n\t\t\t'single'       => true,\n\t\t)\n\t);\n\n\t\\register_meta(\n\t\t'post',\n\t\t'podcast_season_number',\n\t\tarray(\n\t\t\t'show_in_rest' => true,\n\t\t\t'type'         => 'string',\n\t\t\t'single'       => true,\n\t\t)\n\t);\n\n\t\\register_meta(\n\t\t'post',\n\t\t'podcast_episode_number',\n\t\tarray(\n\t\t\t'show_in_rest' => true,\n\t\t\t'type'         => 'string',\n\t\t\t'single'       => true,\n\t\t)\n\t);\n\n\t\\register_meta(\n\t\t'post',\n\t\t'podcast_episode_type',\n\t\tarray(\n\t\t\t'show_in_rest' => true,\n\t\t\t'type'         => 'string',\n\t\t\t'single'       => true,\n\t\t\t'default'      => 'none',\n\t\t)\n\t);\n\n\t\\register_meta(\n\t\t'post',\n\t\t'podcast_transcript',\n\t\tarray(\n\t\t\t'show_in_rest'      => true,\n\t\t\t'type'              => 'string',\n\t\t\t'single'            => true,\n\t\t\t'sanitize_callback' => function ( $val ) {\n\t\t\t\treturn wp_kses_post( $val );\n\t\t\t},\n\t\t)\n\t);\n\n\t\\register_term_meta(\n\t\t'podcasting_podcasts',\n\t\t'podcasting_talent_name',\n\t\tarray(\n\t\t\t'show_in_rest'      => true,\n\t\t\t'type'              => 'string',\n\t\t\t'single'            => true,\n\t\t\t'auth_callback'     => 'podcasting_term_auth_callback',\n\t\t\t'sanitize_callback' => function ( $val ) {\n\t\t\t\treturn sanitize_text_field( wp_unslash( $val ) );\n\t\t\t},\n\t\t)\n\t);\n\n\t\\register_term_meta(\n\t\t'podcasting_podcasts',\n\t\t'podcasting_summary',\n\t\tarray(\n\t\t\t'show_in_rest'      => true,\n\t\t\t'type'              => 'string',\n\t\t\t'single'            => true,\n\t\t\t'auth_callback'     => 'podcasting_term_auth_callback',\n\t\t\t'sanitize_callback' => function ( $val ) {\n\t\t\t\treturn sanitize_text_field( wp_unslash( $val ) );\n\t\t\t},\n\t\t)\n\t);\n\n\t\\register_term_meta(\n\t\t'podcasting_podcasts',\n\t\t'podcasting_category_1',\n\t\tarray(\n\t\t\t'show_in_rest'      => true,\n\t\t\t'type'              => 'string',\n\t\t\t'single'            => true,\n\t\t\t'auth_callback'     => 'podcasting_term_auth_callback',\n\t\t\t'sanitize_callback' => function ( $val ) {\n\t\t\t\treturn sanitize_text_field( wp_unslash( $val ) );\n\t\t\t},\n\t\t)\n\t);\n\n\t\\register_term_meta(\n\t\t'podcasting_podcasts',\n\t\t'podcasting_image',\n\t\tarray(\n\t\t\t'show_in_rest'      => true,\n\t\t\t'type'              => 'number',\n\t\t\t'single'            => true,\n\t\t\t'auth_callback'     => 'podcasting_term_auth_callback',\n\t\t\t'sanitize_callback' => function ( $val ) {\n\t\t\t\treturn absint( wp_unslash( $val ) );\n\t\t\t},\n\t\t)\n\t);\n\n\t\\register_term_meta(\n\t\t'podcasting_podcasts',\n\t\t'podcasting_image_url',\n\t\tarray(\n\t\t\t'show_in_rest'      => true,\n\t\t\t'type'              => 'string',\n\t\t\t'single'            => true,\n\t\t\t'auth_callback'     => 'podcasting_term_auth_callback',\n\t\t\t'sanitize_callback' => function ( $val ) {\n\t\t\t\treturn filter_var( $val, FILTER_VALIDATE_URL );\n\t\t\t},\n\t\t)\n\t);\n}\nadd_action( 'init', __NAMESPACE__ . '\\register_meta' );\n\n/**\n * Podcasting term meta generic auth callback.\n *\n * @return boolean\n */\nfunction podcasting_term_auth_callback() {\n\tif ( current_user_can( 'manage_categories' ) ) {\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n/**\n * Add a custom podcasts taxonomy.\n */\nfunction create_podcasts_taxonomy() {\n\tregister_taxonomy(\n\t\tPODCASTING_TAXONOMY_NAME,\n\t\t'post',\n\t\tarray(\n\t\t\t'labels'            => array(\n\t\t\t\t'name'                  => __( 'Podcasts', 'simple-podcasting' ),\n\t\t\t\t'singular_name'         => __( 'Podcast', 'simple-podcasting' ),\n\t\t\t\t'search_items'          => __( 'Search Podcasts', 'simple-podcasting' ),\n\t\t\t\t'all_items'             => __( 'All Podcasts', 'simple-podcasting' ),\n\t\t\t\t'parent_item'           => __( 'Parent Podcast', 'simple-podcasting' ),\n\t\t\t\t'parent_item_colon'     => __( 'Parent Podcast:', 'simple-podcasting' ),\n\t\t\t\t'edit_item'             => __( 'Edit Podcast', 'simple-podcasting' ),\n\t\t\t\t'view_item'             => __( 'View Podcast', 'simple-podcasting' ),\n\t\t\t\t'update_item'           => __( 'Update Podcast', 'simple-podcasting' ),\n\t\t\t\t'add_new_item'          => __( 'Add New Podcast', 'simple-podcasting' ),\n\t\t\t\t'new_item_name'         => __( 'New Podcast Name', 'simple-podcasting' ),\n\t\t\t\t'add_or_remove_items'   => __( 'Add or remove podcasts', 'simple-podcasting' ),\n\t\t\t\t'not_found'             => __( 'No podcasts found', 'simple-podcasting' ),\n\t\t\t\t'no_terms'              => __( 'No podcasts', 'simple-podcasting' ),\n\t\t\t\t'items_list_navigation' => __( 'Podcasts list navigation', 'simple-podcasting' ),\n\t\t\t\t'items_list'            => __( 'Podcasts list', 'simple-podcasting' ),\n\t\t\t\t'back_to_items'         => __( '&larr; Back to Podcasts', 'simple-podcasting' ),\n\t\t\t),\n\t\t\t'hierarchical'      => true,\n\t\t\t'show_tagcloud'     => false,\n\t\t\t'public'            => true,\n\t\t\t'show_ui'           => true,\n\t\t\t'show_in_menu'      => false,\n\t\t\t'show_in_rest'      => true,\n\t\t\t'show_in_nav_menus' => false,\n\t\t\t'show_admin_column' => true,\n\t\t\t'rewrite'           => array( 'slug' => 'podcasts' ),\n\t\t)\n\t);\n}\nadd_action( 'init', __NAMESPACE__ . '\\create_podcasts_taxonomy' );\n\n/**\n * Filter the menu so podcasts are parent-less.\n *\n * @param string $file Url to the parent page.\n *\n * @return string\n */\nfunction filter_parent_file( $file ) {\n\t$screen = get_current_screen();\n\n\tif (\n\t\t( 'edit-tags' === $screen->base || 'term' === $screen->base ) &&\n\t\t'podcasting_podcasts' === $screen->taxonomy\n\t) {\n\t\treturn 'edit-tags.php?taxonomy=podcasting_podcasts&amp;podcasts=true';\n\t}\n\treturn $file;\n}\nadd_filter( 'parent_file', __NAMESPACE__ . '\\filter_parent_file' );\n\n/**\n * Add \"Podcasts\" as its own top level menu item.\n */\nfunction add_top_level_menu() {\n\tremove_submenu_page( 'edit.php', 'edit-tags.php?taxonomy=podcasting_podcasts' );\n\tadd_menu_page(\n\t\t__( 'Podcasts', 'simple-podcasting' ),\n\t\t__( 'Podcasts', 'simple-podcasting' ),\n\t\t'manage_options',\n\t\t'edit-tags.php?taxonomy=podcasting_podcasts&amp;podcasts=true',\n\t\tnull,\n\t\t'dashicons-microphone',\n\t\t13\n\t);\n}\nadd_action( 'admin_menu', __NAMESPACE__ . '\\add_top_level_menu' );\n\n/**\n * Display some help for next steps on the podcast taxonomy screen.\n */\nfunction add_podcasting_taxonomy_help_text() {\n\techo '<div class=\"notice notice-info\"><p>';\n\tesc_html_e( 'Once at least one podcast exists, you can add episodes by creating a post, assigning it to the appropriate podcast, and inserting an audio player or podcast block into the content of the post. You can then submit the feed URL to podcast directories.', 'simple-podcasting' );\n\techo '</p></div>';\n}\nadd_action( 'after-podcasting_podcasts-table', __NAMESPACE__ . '\\add_podcasting_taxonomy_help_text' );\n\n/**\n * Returns array of supported podcast platforms.\n *\n * @return array\n */\nfunction get_supported_platforms() {\n\t$platforms = array(\n\t\t'pocket-casts'    => array(\n\t\t\t'slug'  => 'pocket-casts',\n\t\t\t'title' => esc_html__( 'Pocket Casts', 'simple-podcasting' ),\n\t\t),\n\t\t'apple-podcasts'  => array(\n\t\t\t'slug'  => 'apple-podcasts',\n\t\t\t'title' => esc_html__( 'Apple Podcasts', 'simple-podcasting' ),\n\t\t),\n\t\t'google-podcasts' => array(\n\t\t\t'slug'  => 'google-podcasts',\n\t\t\t'title' => esc_html__( 'Google Podcasts', 'simple-podcasting' ),\n\t\t),\n\t\t'stitcher'        => array(\n\t\t\t'slug'  => 'stitcher',\n\t\t\t'title' => esc_html__( 'Stitcher', 'simple-podcasting' ),\n\t\t),\n\t\t'playerfm'        => array(\n\t\t\t'slug'  => 'playerfm',\n\t\t\t'title' => esc_html__( 'PlayerFM', 'simple-podcasting' ),\n\t\t),\n\t\t'overcast'        => array(\n\t\t\t'slug'  => 'overcast',\n\t\t\t'title' => esc_html__( 'Overcast', 'simple-podcasting' ),\n\t\t),\n\t\t'pandora'         => array(\n\t\t\t'slug'  => 'pandora',\n\t\t\t'title' => esc_html__( 'Pandora', 'simple-podcasting' ),\n\t\t),\n\t\t'castro'          => array(\n\t\t\t'slug'  => 'castro',\n\t\t\t'title' => esc_html__( 'Castro', 'simple-podcasting' ),\n\t\t),\n\t\t'tunein'          => array(\n\t\t\t'slug'  => 'tunein',\n\t\t\t'title' => esc_html__( 'TuneIn', 'simple-podcasting' ),\n\t\t),\n\t\t'spotify'         => array(\n\t\t\t'slug'  => 'spotify',\n\t\t\t'title' => esc_html__( 'Spotify', 'simple-podcasting' ),\n\t\t),\n\t);\n\n\treturn apply_filters( 'simple_podcasting_get_supported_platforms', $platforms );\n}\n\n/**\n * Renders the terms fields for platforms\n *\n * @param  array   $field   The field data.\n * @param  string  $value   The existing field value.\n * @param  boolean $term_id The term id, or false for the new term form.\n */\nfunction render_platform_fields( $field, $value, $term_id ) {\n\t$theme = get_term_meta( $term_id, 'podcasting_icon_theme', true );\n\n\tif ( empty( $theme ) ) {\n\t\t$theme = 'color';\n\t}\n\n\t$platforms = get_supported_platforms();\n\t?>\n\n\t<table id=\"simple_podcasting__platforms\" class=\"simple_podcasting__platforms widefat striped\">\n\t\t<thead>\n\t\t\t<th><?php esc_html_e( 'Platform', 'simple-podcasting' ); ?></th>\n\t\t\t<th><?php esc_html_e( 'Podcast URL', 'simple-podcasting' ); ?></th>\n\t\t\t<th><?php esc_html_e( 'Icon', 'simple-podcasting' ); ?></th>\n\t\t</thead>\n\n\t\t<tbody>\n\t\t\t<?php foreach ( $platforms as $slug => $platform ) : ?>\n\t\t\t\t<tr>\n\t\t\t\t\t<td class=\"simple_podcasting__platforms-title\"><?php echo esc_html( $platform['title'] ); ?></td>\n\t\t\t\t\t<td class=\"simple_podcasting__platforms-url\">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tname=\"<?php printf( '%s[%s]', esc_attr( $field['slug'] ), esc_attr( $slug ) ); ?>\"\n\t\t\t\t\t\t\tid=\"<?php printf( '%s[%s]', esc_attr( $field['slug'] ), esc_attr( $slug ) ); ?>\"\n\t\t\t\t\t\t\ttype=\"url\"\n\t\t\t\t\t\t\tvalue=\"<?php echo isset( $value[ $slug ] ) ? esc_url( $value[ $slug ] ) : ''; ?>\"\n\t\t\t\t\t\t\tclass=\"widefat\"\n\t\t\t\t\t\t>\n\t\t\t\t\t</td>\n\t\t\t\t\t<td class=\"simple_podcasting__platforms-icon <?php echo 'white' === $theme ? 'simple_podcasting__platforms-icon--darken-bg' : ''; ?>\">\n\t\t\t\t\t\t<img\n\t\t\t\t\t\t\tsrc=\"\n\t\t\t\t\t\t\t<?php\n\t\t\t\t\t\t\tprintf(\n\t\t\t\t\t\t\t\t'%s%s/%s/%s',\n\t\t\t\t\t\t\t\tesc_url( PODCASTING_URL ),\n\t\t\t\t\t\t\t\t'dist/images/icons',\n\t\t\t\t\t\t\t\tesc_attr( $slug ),\n\t\t\t\t\t\t\t\tesc_attr( $theme ) . '-100.png'\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t?>\n\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\tdata-platform=\"<?php echo esc_attr( $slug ); ?>\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</td>\n\t\t\t\t</tr>\n\t\t\t<?php endforeach; ?>\n\t\t</tbody>\n\t</table>\n\n\t<?php\n}\nadd_action( 'simple_podcasting_custom_field_platform_fields', __NAMESPACE__ . '\\render_platform_fields', 10, 3 );\n\n/**\n * Add fields to the add term screen.\n *\n * @param \\WP_Term $term The term object.\n */\nfunction add_podcasting_term_add_meta_fields( $term ) {\n\t$podcasting_meta_fields = get_meta_fields();\n\tforeach ( $podcasting_meta_fields as $field ) {\n\t\t?>\n\t\t<div class=\"form-field\">\n\t\t\t<label for=\"name\" ><?php echo esc_html( $field['title'] ); ?></label>\n\t\t\t<?php the_field( $field, '' ); ?>\n\t\t</div>\n\t\t<?php\n\t}\n}\n\n/**\n * Generate and output a single field.\n *\n * @param  array   $field   The field data.\n * @param  string  $value   The existing field value.\n * @param  boolean $term_id The term id, or false for the new term form.\n */\nfunction the_field( $field, $value = '', $term_id = false ) {\n\tswitch ( $field['type'] ) {\n\t\tcase 'language':\n\t\t\techo wp_kses(\n\t\t\t\t$field['data'],\n\t\t\t\tarray(\n\t\t\t\t\t'select'   => array(\n\t\t\t\t\t\t'name' => array(),\n\t\t\t\t\t\t'id'   => array(),\n\t\t\t\t\t),\n\t\t\t\t\t'optgroup' => array(\n\t\t\t\t\t\t'label' => array(),\n\t\t\t\t\t),\n\t\t\t\t\t'option'   => array(\n\t\t\t\t\t\t'value'          => array(),\n\t\t\t\t\t\t'lang'           => array(),\n\t\t\t\t\t\t'data-installed' => array(),\n\t\t\t\t\t\t'selected'       => array(),\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t);\n\t\t\tbreak;\n\n\t\tcase 'textfield':\n\t\t\t?>\n\t\t\t\t<input\n\t\t\t\t\tname=\"<?php echo esc_attr( $field['slug'] ); ?>\"\n\t\t\t\t\tid=\"<?php echo esc_attr( $field['slug'] ); ?>\"\n\t\t\t\t\ttype=\"text\"\n\t\t\t\t\tvalue=\"<?php echo esc_attr( $value ); ?>\"\n\t\t\t\t\tsize=\"40\"\n\t\t\t\t>\n\t\t\t\t<?php\n\t\t\tbreak;\n\n\t\tcase 'textarea':\n\t\t\t?>\n\t\t\t\t<textarea name=\"<?php echo esc_attr( $field['slug'] ); ?>\" id=\"<?php echo esc_attr( $field['slug'] ); ?>\" rows=\"5\" cols=\"40\"><?php echo esc_textarea( $value ); ?></textarea>\n\t\t\t\t<?php\n\t\t\tbreak;\n\n\t\tcase 'select':\n\t\t\t?>\n\t\t\t\t<select\n\t\t\t\t\tname=\"<?php echo esc_attr( $field['slug'] ); ?>\"\n\t\t\t\t\tid=\"<?php echo esc_attr( $field['slug'] ); ?>\"\n\t\t\t\t\tclass=\"postform\"\n\t\t\t\t>\n\t\t\t\t<?php\n\t\t\t\t$options = $field['options'];\n\t\t\t\tforeach ( $options as $key => $label ) {\n\t\t\t\t\t?>\n\t\t\t\t\t<option value=\"<?php echo esc_attr( $key ); ?>\" <?php selected( $key, $value ); ?>>\n\t\t\t\t\t\t<?php echo esc_html( $label ); ?>\n\t\t\t\t\t</option>\n\t\t\t\t\t<?php\n\t\t\t\t}\n\t\t\t\t?>\n\t\t\t\t</select>\n\t\t\t\t<?php\n\t\t\tbreak;\n\n\t\tcase 'image':\n\t\t\t$image_url = get_term_meta( $term_id, $field['slug'] . '_url', true );\n\t\t\t?>\n\t\t\t\t<div class=\"media-wrapper\">\n\t\t\t\t<?php\n\t\t\t\t$has_image = ( '' === $value );\n\t\t\t\t?>\n\t\t\t\t\t<div class=\"podasting-existing-image <?php echo ( $has_image ? 'hidden' : '' ); ?>\">\n\t\t\t\t\t\t<a href=\"#\" >\n\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\tsrc=\"<?php echo esc_url( $image_url ); ?>\"\n\t\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\t\tclass=\"podcast-image-thumbnail\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t</a>\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\ttype=\"hidden\"\n\t\t\t\t\t\t\tid=\"<?php echo esc_attr( $field['slug'] ); ?>\"\n\t\t\t\t\t\t\tname=\"<?php echo esc_attr( $field['slug'] ); ?>\"\n\t\t\t\t\t\t\tvalue=\"<?php echo esc_attr( $value ); ?>\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t<br />\n\t\t\t\t\t\t<a href=\"#\" class=\"podcast-media-remove\" data-media-id=\"<?php echo esc_attr( $value ); ?>\">\n\t\t\t\t\t\t\tremove image\n\t\t\t\t\t\t</a>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"podcasting-upload-image <?php echo ( ! $has_image ? 'hidden' : '' ); ?>\">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tclass=\"podcasting-media-button button-secondary\"\n\t\t\t\t\t\t\tid=\"image-<?php echo esc_attr( $field['slug'] ); ?>\"\n\t\t\t\t\t\t\tvalue=\"<?php esc_attr_e( 'Select Image', 'simple-podcasting' ); ?>\"\n\t\t\t\t\t\t\tdata-slug=\"<?php echo esc_attr( $field['slug'] ); ?>\"\n\t\t\t\t\t\t\tdata-choose=\"<?php esc_attr_e( 'Podcast Image', 'simple-podcasting' ); ?>\"\n\t\t\t\t\t\t\tdata-update=\"<?php esc_attr_e( 'Choose Selected Image', 'simple-podcasting' ); ?>\"\n\t\t\t\t\t\t\tdata-preview-size=\"thumbnail\"\n\t\t\t\t\t\t\tdata-mime-type=\"image\"\n\t\t\t\t\t\t>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<?php\n\t\t\tbreak;\n\n\t\tcase 'radio':\n\t\t\t$selected = empty( $value ) ? 'color' : $value;\n\n\t\t\tforeach ( $field['options'] as $option ) {\n\t\t\t\t?>\n\t\t\t\t<label>\n\t\t\t\t\t<input\n\t\t\t\t\t\ttype=\"radio\"\n\t\t\t\t\t\tname=\"podcasting_icon_theme\"\n\t\t\t\t\t\tvalue=\"<?php echo esc_attr( $option['value'] ); ?>\"\n\t\t\t\t\t\t<?php checked( $selected, $option['value'] ); ?>\n\t\t\t\t\t/>\n\t\t\t\t\t<?php echo esc_html( $option['label'] ); ?>\n\t\t\t\t</label>\n\t\t\t\t<?php\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase $field['type']:\n\t\t\tdo_action( 'simple_podcasting_custom_field_' . $field['type'], $field, $value, $term_id );\n\t\t\tbreak;\n\t}\n\n\tif ( isset( $field['description'] ) ) {\n\t\t?>\n\t\t<p class=\"description\"><?php echo esc_html( $field['description'] ); ?></p>\n\t\t\t<?php\n\t}\n}\n\n/**\n * Save podcasting fields from the term screen to term meta.\n *\n * @param int $term_id The term is being saved.\n */\nfunction save_podcasting_term_meta( $term_id ) {\n\t$tax = get_taxonomy( PODCASTING_TAXONOMY_NAME );\n\n\tif ( ! current_user_can( $tax->cap->edit_terms ) ) {\n\t\treturn;\n\t}\n\n\tif ( empty( $_POST['podcasting_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['podcasting_nonce'] ) ), 'podcasting_edit' ) ) {\n\t\treturn;\n\t}\n\n\t$podcasting_meta_fields = get_meta_fields();\n\n\tforeach ( $podcasting_meta_fields as $field ) {\n\t\t$slug = $field['slug'];\n\n\t\tif ( isset( $_POST[ $slug ] ) ) {\n\t\t\tif ( is_array( $_POST[ $slug ] ) ) {\n\t\t\t\t$sanitized_value = filter_var_array(\n\t\t\t\t\t$_POST[ $slug ], // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash\n\t\t\t\t\tarray(\n\t\t\t\t\t\t'pocket-casts'    => FILTER_SANITIZE_URL,\n\t\t\t\t\t\t'apple-podcasts'  => FILTER_SANITIZE_URL,\n\t\t\t\t\t\t'google-podcasts' => FILTER_VALIDATE_URL,\n\t\t\t\t\t\t'stitcher'        => FILTER_VALIDATE_URL,\n\t\t\t\t\t\t'playerfm'        => FILTER_VALIDATE_URL,\n\t\t\t\t\t\t'overcast'        => FILTER_VALIDATE_URL,\n\t\t\t\t\t\t'pandora'         => FILTER_VALIDATE_URL,\n\t\t\t\t\t\t'castro'          => FILTER_VALIDATE_URL,\n\t\t\t\t\t\t'tunein'          => FILTER_VALIDATE_URL,\n\t\t\t\t\t\t'spotify'         => FILTER_VALIDATE_URL,\n\t\t\t\t\t)\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\t$sanitized_value = sanitize_text_field( wp_unslash( $_POST[ $slug ] ) );\n\t\t\t}\n\n\t\t\t// If the field is an image field, store the image URL along with the slug.\n\t\t\tif ( strpos( $slug, '_image' ) ) {\n\t\t\t\t$image_url = wp_get_attachment_url( (int) $sanitized_value );\n\t\t\t\tupdate_term_meta( $term_id, $slug . '_url', $image_url );\n\t\t\t}\n\t\t\tupdate_term_meta( $term_id, $slug, $sanitized_value );\n\t\t}\n\t}\n}\nadd_action( 'edited_' . PODCASTING_TAXONOMY_NAME, __NAMESPACE__ . '\\save_podcasting_term_meta' );\nadd_action( 'created_' . PODCASTING_TAXONOMY_NAME, __NAMESPACE__ . '\\save_podcasting_term_meta' );\n\n/**\n * Add podcasting fields to the term screen.\n *\n * @param \\WP_Term $term The term object.\n */\nfunction add_podcasting_term_edit_meta_fields( $term ) {\n\t$podcasting_meta_fields = get_meta_fields();\n\t?>\n\t<table class=\"form-table\">\n\t\t<tbody><tr class=\"form-field term-name-wrap\">\n\t<?php\n\tforeach ( $podcasting_meta_fields as $field ) {\n\t\t$value = get_term_meta( $term->term_id, $field['slug'], true );\n\t\t$value = $value ? $value : '';\n\t\t?>\n\t\t<tr class=\"form-field term-name-wrap\">\n\t\t\t<th scope=\"row\">\n\t\t\t\t<label\n\t\t\t\t\tfor=\"name\"\n\t\t\t\t><?php echo esc_html( $field['title'] ); ?></label>\n\t\t\t</th>\n\t\t\t<td>\n\t\t\t\t<?php the_field( $field, $value, $term->term_id ); ?>\n\t\t\t</td>\n\t\t</tr>\n\t\t<?php\n\t}\n\t?>\n\t<tbody>\n\t</table>\n\t\t<tbody><tr class=\"form-field term-name-wrap\">\n\t<?php\n}\n\n/**\n * Add podcasting nonce to the term screen.\n *\n * @param \\WP_Term $term     The term object.\n * @param bool     $taxonomy Is this a taxonomy.\n */\nfunction add_podcasting_term_meta_nonce( $term, $taxonomy = false ) {\n\techo '<style>\n\t.term-description-wrap{\n\t\tdisplay: none;\n\t} </style>';\n\n\twp_nonce_field( 'podcasting_edit', 'podcasting_nonce' );\n\twp_enqueue_media();\n\tif ( $taxonomy ) {\n\t\t$url = get_term_feed_link( $term->term_id, PODCASTING_TAXONOMY_NAME );\n\t\tesc_html_e( 'Your Podcast Feed: ', 'simple-podcasting' );\n\t\techo '<a href=\"' . esc_url( $url ) . '\" target=\"_blank\">' . esc_url( $url ) . '</a><br />';\n\t\tesc_html_e( 'This is the URL you submit to iTunes or podcasting service.', 'simple-podcasting' );\n\t}\n}\nadd_action( PODCASTING_TAXONOMY_NAME . '_add_form_fields', __NAMESPACE__ . '\\add_podcasting_term_meta_nonce' );\nadd_action( PODCASTING_TAXONOMY_NAME . '_edit_form_fields', __NAMESPACE__ . '\\add_podcasting_term_meta_nonce', 99, 2 );\n\nadd_action( PODCASTING_TAXONOMY_NAME . '_edit_form', __NAMESPACE__ . '\\add_podcasting_term_edit_meta_fields' );\nadd_action( PODCASTING_TAXONOMY_NAME . '_add_form_fields', __NAMESPACE__ . '\\add_podcasting_term_add_meta_fields' );\n\n/**\n * Add a feed link to the podcasting term table.\n *\n * @param string $content     Blank string.\n * @param string $column_name Name of the column.\n * @param int    $term_id     Term ID.\n *\n * @return string\n */\nfunction add_podcasting_term_feed_link_column( $content, $column_name, $term_id ) {\n\n\tif ( 'feedurl' === $column_name ) {\n\t\t$url = get_term_feed_link( $term_id, PODCASTING_TAXONOMY_NAME );\n\t\techo '<a href=\"' . esc_url( $url ) . '\" target=\"_blank\">' . esc_url( $url ) . '</a>';\n\t}\n\treturn $content;\n}\nadd_filter( 'manage_' . PODCASTING_TAXONOMY_NAME . '_custom_column', __NAMESPACE__ . '\\add_podcasting_term_feed_link_column', 10, 3 );\n\n/**\n * Add a podcasting image to the podcasting term table.\n *\n * @param string $content     Blank string.\n * @param string $column_name Name of the column.\n * @param int    $term_id     Term ID.\n *\n * @return string\n */\nfunction add_podcasting_term_podcasting_image_column( $content, $column_name, $term_id ) {\n\n\tif ( 'podcasting_image' === $column_name ) {\n\t\t$image = get_term_meta( $term_id, 'podcasting_image', true );\n\t\techo wp_get_attachment_image( $image, 'thumbnail' );\n\t}\n\treturn $content;\n}\nadd_filter( 'manage_' . PODCASTING_TAXONOMY_NAME . '_custom_column', __NAMESPACE__ . '\\add_podcasting_term_podcasting_image_column', 10, 3 );\n\n\n/**\n * Add a custom column for the podcast feed link.\n *\n * @param array $columns An array of columns\n *\n * @return array\n */\nfunction add_custom_term_columns( $columns ) {\n\t$columns = array_merge(\n\t\tarray(\n\t\t\t'podcasting_image' => __( 'Podcast Cover', 'simple-podcasting' ),\n\t\t),\n\t\t$columns,\n\t\tarray(\n\t\t\t'feedurl' => __( 'Feed URL', 'simple-podcasting' ),\n\t\t)\n\t);\n\tunset( $columns['description'] );\n\tunset( $columns['author'] );\n\treturn $columns;\n}\nadd_filter( 'manage_edit-' . PODCASTING_TAXONOMY_NAME . '_columns', __NAMESPACE__ . '\\add_custom_term_columns', 99 );\n\n/**\n * Get the meta fields used for podcasts.\n */\nfunction get_meta_fields() {\n\treturn array(\n\t\tarray(\n\t\t\t'slug'  => 'podcasting_subtitle',\n\t\t\t'title' => __( 'Subtitle', 'simple-podcasting' ),\n\t\t\t'type'  => 'textfield',\n\t\t),\n\t\tarray(\n\t\t\t'slug'  => 'podcasting_talent_name',\n\t\t\t'title' => __( 'Artist / Author name (required)', 'simple-podcasting' ),\n\t\t\t'type'  => 'textfield',\n\t\t),\n\t\tarray(\n\t\t\t'slug'  => 'podcasting_email',\n\t\t\t'title' => __( 'Podcast email', 'simple-podcasting' ),\n\t\t\t'type'  => 'textfield',\n\t\t),\n\t\tarray(\n\t\t\t'slug'  => 'podcasting_summary',\n\t\t\t'title' => __( 'Summary (required)', 'simple-podcasting' ),\n\t\t\t'type'  => 'textarea',\n\t\t),\n\t\tarray(\n\t\t\t'slug'  => 'podcasting_copyright',\n\t\t\t'title' => __( 'Copyright / License information', 'simple-podcasting' ),\n\t\t\t'type'  => 'textfield',\n\t\t),\n\t\tarray(\n\t\t\t'slug'    => 'podcasting_explicit',\n\t\t\t'title'   => __( 'Mark as explicit', 'simple-podcasting' ),\n\t\t\t'type'    => 'select',\n\t\t\t'options' => array(\n\t\t\t\t'No',\n\t\t\t\t'Yes',\n\t\t\t\t'Clean',\n\t\t\t),\n\t\t),\n\t\tarray(\n\t\t\t'slug'  => 'podcasting_language',\n\t\t\t'title' => __( 'Language', 'simple-podcasting' ),\n\t\t\t'type'  => 'language',\n\t\t\t'data'  => get_podcasting_language_options(),\n\t\t),\n\t\tarray(\n\t\t\t'slug'        => 'podcasting_image',\n\t\t\t'title'       => __( 'Cover image (required)', 'simple-podcasting' ),\n\t\t\t'type'        => 'image',\n\t\t\t'description' => __( 'Minimum size: 1400px x 1400 px — maximum size: 2048px x 2048px', 'simple-podcasting' ),\n\t\t),\n\t\tarray(\n\t\t\t'slug'        => 'podcasting_keywords',\n\t\t\t'title'       => __( 'Keywords', 'simple-podcasting' ),\n\t\t\t'type'        => 'textfield',\n\t\t\t'description' => __( 'Comma-separated keywords to help people find your podcast.', 'simple-podcasting' ),\n\t\t),\n\t\tarray(\n\t\t\t'slug'    => 'podcasting_type_of_show',\n\t\t\t'title'   => __( 'Type of show', 'simple-podcasting' ),\n\t\t\t'type'    => 'select',\n\t\t\t'options' => array(\n\t\t\t\t0          => __( 'n/a', 'simple-podcasting' ),\n\t\t\t\t'episodic' => __( 'Episodic', 'simple-podcasting' ),\n\t\t\t\t'serial'   => __( 'Serial', 'simple-podcasting' ),\n\t\t\t),\n\t\t),\n\t\tarray(\n\t\t\t'slug'    => 'podcasting_category_1',\n\t\t\t'title'   => __( 'Category 1 (required)', 'simple-podcasting' ),\n\t\t\t'type'    => 'select',\n\t\t\t'options' => get_podcasting_categories_options(),\n\t\t),\n\t\tarray(\n\t\t\t'slug'    => 'podcasting_category_2',\n\t\t\t'title'   => __( 'Category 2', 'simple-podcasting' ),\n\t\t\t'type'    => 'select',\n\t\t\t'options' => get_podcasting_categories_options(),\n\t\t),\n\t\tarray(\n\t\t\t'slug'    => 'podcasting_category_3',\n\t\t\t'title'   => __( 'Category 3', 'simple-podcasting' ),\n\t\t\t'type'    => 'select',\n\t\t\t'options' => get_podcasting_categories_options(),\n\t\t),\n\t\tarray(\n\t\t\t'slug'  => 'podcasting_platforms',\n\t\t\t'title' => __( 'Podcasting Platforms', 'simple-podcasting' ),\n\t\t\t'type'  => 'platform_fields',\n\t\t),\n\t\tarray(\n\t\t\t'slug'    => 'podcasting_icon_theme',\n\t\t\t'title'   => __( 'Podcasting Platforms icon theme', 'simple-podcasting' ),\n\t\t\t'type'    => 'radio',\n\t\t\t'options' => array(\n\t\t\t\tarray(\n\t\t\t\t\t'label' => 'Color',\n\t\t\t\t\t'value' => 'color',\n\t\t\t\t),\n\t\t\t\tarray(\n\t\t\t\t\t'label' => 'Black',\n\t\t\t\t\t'value' => 'black',\n\t\t\t\t),\n\t\t\t\tarray(\n\t\t\t\t\t'label' => 'White',\n\t\t\t\t\t'value' => 'white',\n\t\t\t\t),\n\t\t\t),\n\t\t),\n\t);\n}\n\n/**\n * Get array of podcasting categories.\n *\n * Podcasting category names are not translated because they need to be provided in English.\n *\n * @return array Array of podcasting categories.\n */\nfunction get_podcasting_categories() {\n\t// phpcs:disable WordPress.Arrays.MultipleStatementAlignment.DoubleArrowNotAligned -- keep nested array readable\n\treturn array(\n\t\t'arts' => array(\n\t\t\t'name' => 'Arts',\n\t\t\t'subcategories' => array(\n\t\t\t\t'design'          => 'Design',\n\t\t\t\t'fashion-beauty'  => 'Fashion & Beauty',\n\t\t\t\t'food'            => 'Food',\n\t\t\t\t'books'           => 'Books',\n\t\t\t\t'performing-arts' => 'Performing Arts',\n\t\t\t\t'visual-arts'     => 'Visual Arts',\n\t\t\t),\n\t\t),\n\t\t'business' => array(\n\t\t\t'name' => 'Business',\n\t\t\t'subcategories' => array(\n\t\t\t\t'careers'          => 'Careers',\n\t\t\t\t'entrepreneurship' => 'Entrepreneurship',\n\t\t\t\t'investing'        => 'Investing',\n\t\t\t\t'management'       => 'Management',\n\t\t\t\t'marketing'        => 'Marketing',\n\t\t\t\t'non-profit'       => 'Non-Profit',\n\t\t\t),\n\t\t),\n\t\t'comedy' => array(\n\t\t\t'name' => 'Comedy',\n\t\t\t'subcategories' => array(\n\t\t\t\t'comedy-interviews' => 'Comedy Interviews',\n\t\t\t\t'improv'            => 'Improv',\n\t\t\t\t'stand-up'          => 'Stand-Up',\n\t\t\t),\n\t\t),\n\t\t'education' => array(\n\t\t\t'name' => 'Education',\n\t\t\t'subcategories' => array(\n\t\t\t\t'courses'           => 'Courses',\n\t\t\t\t'how-to'            => 'How-To',\n\t\t\t\t'language-learning' => 'Language Learning',\n\t\t\t\t'self-improvment'   => 'Self-Improvement',\n\t\t\t),\n\t\t),\n\t\t'fiction' => array(\n\t\t\t'name' => 'Fiction',\n\t\t\t'subcategories' => array(\n\t\t\t\t'comedy-fiction'  => 'Comedy Fiction',\n\t\t\t\t'drama'           => 'Drama',\n\t\t\t\t'science-fiction' => 'Science Fiction',\n\t\t\t),\n\t\t),\n\t\t'leisure' => array(\n\t\t\t'name' => 'Leisure',\n\t\t\t'subcategories' => array(\n\t\t\t\t'animation-manga' => 'Animation & Manga',\n\t\t\t\t'automotive'      => 'Automotive',\n\t\t\t\t'aviation'        => 'Aviation',\n\t\t\t\t'crafts'          => 'Crafts',\n\t\t\t\t'hobbies'         => 'Hobbies',\n\t\t\t\t'home-garden'     => 'Home & Garden',\n\t\t\t\t'games'           => 'Games',\n\t\t\t\t'video-games'     => 'Video Games',\n\t\t\t),\n\t\t),\n\t\t'government' => array(\n\t\t\t'name' => 'Government',\n\t\t\t'subcategories' => array(\n\t\t\t\t'local'    => 'Local',\n\t\t\t\t'national' => 'National',\n\t\t\t\t'regional' => 'Regional',\n\t\t\t),\n\t\t),\n\t\t'health-fitness' => array(\n\t\t\t'name' => 'Health & Fitness',\n\t\t\t'subcategories' => array(\n\t\t\t\t'alternative-health' => 'Alternative Health',\n\t\t\t\t'fitness'            => 'Fitness',\n\t\t\t\t'medicine'           => 'Medicine',\n\t\t\t\t'mental-health'      => 'Mental Health',\n\t\t\t\t'nutrition'          => 'Nutrition',\n\t\t\t\t'sexuality'          => 'Sexuality',\n\t\t\t),\n\t\t),\n\t\t'history' => array(\n\t\t\t'name' => 'History',\n\t\t),\n\t\t'kids-family' => array(\n\t\t\t'name' => 'Kids & Family',\n\t\t\t'subcategories' => array(\n\t\t\t\t'education-for-kids' => 'Education for Kids',\n\t\t\t\t'parenting'          => 'Parenting',\n\t\t\t\t'pets-animals'       => 'Pets & Animals',\n\t\t\t\t'stories-for-kids'   => 'Stories for Kids',\n\t\t\t),\n\t\t),\n\t\t'music' => array(\n\t\t\t'name' => 'Music',\n\t\t\t'subcategories' => array(\n\t\t\t\t'music-commentary' => 'Music Commentary',\n\t\t\t\t'music-history'    => 'Music History',\n\t\t\t\t'music-interviews' => 'Music Interviews',\n\t\t\t),\n\t\t),\n\t\t'news' => array(\n\t\t\t'name' => 'News',\n\t\t\t'subcategories' => array(\n\t\t\t\t'business-news'      => 'Business News',\n\t\t\t\t'daily-news'         => 'Daily News',\n\t\t\t\t'entertainment-news' => 'Entertainment News',\n\t\t\t\t'news-commentary'    => 'News Commentary',\n\t\t\t\t'politics'           => 'Politics',\n\t\t\t\t'sports-news'        => 'Sports News',\n\t\t\t\t'tech-news'          => 'Tech News',\n\t\t\t),\n\t\t),\n\t\t'religion-spirituality' => array(\n\t\t\t'name' => 'Religion & Spirituality',\n\t\t\t'subcategories' => array(\n\t\t\t\t'buddhism'     => 'Buddhism',\n\t\t\t\t'christianity' => 'Christianity',\n\t\t\t\t'hinduism'     => 'Hinduism',\n\t\t\t\t'islam'        => 'Islam',\n\t\t\t\t'judaism'      => 'Judaism',\n\t\t\t\t'religion'     => 'Religion',\n\t\t\t\t'spirituality' => 'Spirituality',\n\t\t\t),\n\t\t),\n\t\t'science' => array(\n\t\t\t'name' => 'Science',\n\t\t\t'subcategories' => array(\n\t\t\t\t'astronomy'        => 'Astronomy',\n\t\t\t\t'chemistry'        => 'Chemistry',\n\t\t\t\t'earth-sciences'   => 'Earth Sciences',\n\t\t\t\t'life-sciences'    => 'Life Sciences',\n\t\t\t\t'mathematics'      => 'Mathematics',\n\t\t\t\t'nature'           => 'Nature',\n\t\t\t\t'natural-sciences' => 'Natural Sciences',\n\t\t\t\t'physics'          => 'Physics',\n\t\t\t\t'social-sciences'  => 'Social Sciences',\n\t\t\t),\n\t\t),\n\t\t'society-culture' => array(\n\t\t\t'name' => 'Society & Culture',\n\t\t\t'subcategories' => array(\n\t\t\t\t'documentary'       => 'Documentary',\n\t\t\t\t'personal-journals' => 'Personal Journals',\n\t\t\t\t'philosophy'        => 'Philosophy',\n\t\t\t\t'places-travel'     => 'Places & Travel',\n\t\t\t\t'relationships'     => 'Relationships',\n\t\t\t),\n\t\t),\n\t\t'sports' => array(\n\t\t\t'name' => 'Sports',\n\t\t\t'subcategories' => array(\n\t\t\t\t'baseball'       => 'Baseball',\n\t\t\t\t'basketball'     => 'Basketball',\n\t\t\t\t'cricket'        => 'Cricket',\n\t\t\t\t'fantasy-sports' => 'Fantasy Sports',\n\t\t\t\t'football'       => 'Football',\n\t\t\t\t'golf'           => 'Golf',\n\t\t\t\t'hockey'         => 'Hockey',\n\t\t\t\t'rugby'          => 'Rugby',\n\t\t\t\t'soccer'         => 'Soccer',\n\t\t\t\t'swimming'       => 'Swimming',\n\t\t\t\t'tennis'         => 'Tennis',\n\t\t\t\t'volleyball'     => 'Volleyball',\n\t\t\t\t'wilderness'     => 'Wilderness',\n\t\t\t\t'wrestling'      => 'Wrestling',\n\t\t\t),\n\t\t),\n\t\t'technology' => array(\n\t\t\t'name' => 'Technology',\n\t\t\t'subcategories' => array(\n\t\t\t\t'education'       => 'Education',\n\t\t\t\t'gadgets'         => 'Gadgets',\n\t\t\t\t'podcasting'      => 'Podcasting',\n\t\t\t\t'software-how-to' => 'Software How-To',\n\t\t\t),\n\t\t),\n\t\t'true-crime' => array(\n\t\t\t'name' => 'True Crime',\n\t\t),\n\t\t'tv-film' => array(\n\t\t\t'name' => 'TV & Film',\n\t\t\t'subcategories' => array(\n\t\t\t\t'after-shows'     => 'After Shows',\n\t\t\t\t'film-history'    => 'Film History',\n\t\t\t\t'film-interviews' => 'Film Interviews',\n\t\t\t\t'film-reviews'    => 'Film Reviews',\n\t\t\t\t'tv-reviews'      => 'TV Reviews',\n\t\t\t),\n\t\t),\n\t);\n\t// phpcs:enable WordPress.Arrays.MultipleStatementAlignment.DoubleArrowNotAligned\n}\n\n/**\n * Transform podcasting categories into dropdown options\n */\nfunction get_podcasting_categories_options() {\n\t$to_return  = array( '' => __( 'None', 'simple-podcasting' ) );\n\t$categories = get_podcasting_categories();\n\n\tforeach ( $categories as $key => $category ) {\n\t\t$to_return[ $key ] = $category['name'];\n\n\t\tif ( ! empty( $category['subcategories'] ) ) {\n\t\t\tforeach ( $category['subcategories'] as $subkey => $subcategory ) {\n\t\t\t\t$to_return[ \"$key:$subkey\" ] = '— ' . $subcategory;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn $to_return;\n}\n\n/**\n * Return the list of available languages.\n *\n * @see wp_dropdown_languages()\n *\n * @return string\n */\nfunction get_podcasting_language_options() {\n\t$lang = '';\n\tif ( is_admin() ) {\n\t\tglobal $tag_ID; // WPCS: @codingStandardsIgnoreLine - we can't control WP global names.\n\t\t// Are we on the term edit screen?\n\t\t$term_id = $tag_ID; // WPCS: @codingStandardsIgnoreLine - we can't control WP global names.\n\t\tif ( $term_id ) {\n\t\t\t$lang = get_term_meta( $term_id, 'podcasting_language', true );\n\t\t}\n\t}\n\treturn \\wp_dropdown_languages(\n\t\tarray(\n\t\t\t'echo'     => false,\n\t\t\t'name'     => 'podcasting_language',\n\t\t\t'selected' => $lang,\n\t\t)\n\t);\n}\n"
  },
  {
    "path": "includes/helpers.php",
    "content": "<?php\n/**\n * Common helper functions\n *\n * @package tenup_podcasting\\helpers\n */\n\nnamespace tenup_podcasting\\helpers;\n\n/**\n * Retrieve the enclosure and return the meta\n *\n * @param string $url The podcast url.\n *\n * @return array\n */\nfunction get_podcast_meta_from_url( $url ) {\n\n\t// Is the required when calling this from outside of the admin.\n\tif ( ! is_admin() ) {\n\t\trequire_once ABSPATH . 'wp-admin/includes/file.php';\n\t\trequire_once ABSPATH . 'wp-admin/includes/media.php';\n\t}\n\t// Modeled after WordPress do_enclose().\n\t$podcast_meta = array();\n\t$headers      = \\wp_get_http_headers( $url );\n\tif ( $headers ) {\n\t\tif ( ! empty( $headers['location'] ) ) {\n\t\t\t$headers = \\wp_get_http_headers( $headers['location'] );\n\t\t}\n\n\t\t// Grab a temporary copy of the file to determine the audio duration.\n\t\t$temp_file = \\download_url( $url, 30 );\n\t\t$meta_data = \\wp_read_audio_metadata( $temp_file );\n\t\t$duration  = isset( $meta_data['length_formatted'] ) ? $meta_data['length_formatted'] : false;\n\n\t\t$len           = isset( $headers['content-length'] ) ? (int) $headers['content-length'] : 0;\n\t\t$type          = isset( $headers['content-type'] ) ? $headers['content-type'] : '';\n\t\t$allowed_types = array( 'video', 'audio' );\n\n\t\t// Check to see if we can figure out the mime type from the extension.\n\t\t$url_parts = \\wp_parse_url( $url );\n\t\tif ( false !== $url_parts ) {\n\t\t\t$extension = \\pathinfo( $url_parts['path'], PATHINFO_EXTENSION );\n\t\t\tif ( ! empty( $extension ) ) {\n\t\t\t\tforeach ( \\wp_get_mime_types() as $exts => $mime ) {\n\t\t\t\t\tif ( preg_match( '!^(' . $exts . ')$!i', $extension ) ) {\n\t\t\t\t\t\t$type = $mime;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif ( in_array( substr( $type, 0, strpos( $type, '/' ) ), $allowed_types, true ) ) {\n\t\t\t$podcast_meta['url']      = esc_url_raw( $url );\n\t\t\t$podcast_meta['mime']     = $type;\n\t\t\t$podcast_meta['duration'] = $duration;\n\t\t\t$podcast_meta['filesize'] = $len;\n\t\t}\n\n\t\treturn $podcast_meta;\n\t}\n}\n\n/**\n * Delete all podcast meta for a post.\n *\n * @param int $post_id Post ID.\n */\nfunction delete_all_podcast_meta( $post_id ) {\n\tif ( metadata_exists( 'post', $post_id, 'podcast_url' ) ) {\n\t\tdelete_post_meta( $post_id, 'podcast_url' );\n\t\tdelete_post_meta( $post_id, 'podcast_filesize' );\n\t\tdelete_post_meta( $post_id, 'podcast_duration' );\n\t\tdelete_post_meta( $post_id, 'podcast_mime' );\n\t\tdelete_post_meta( $post_id, 'podcast_captioned' );\n\t\tdelete_post_meta( $post_id, 'podcast_explicit' );\n\t\tdelete_post_meta( $post_id, 'enclosure' );\n\t\tdelete_post_meta( $post_id, 'podcast_season_number' );\n\t\tdelete_post_meta( $post_id, 'podcast_episode_number' );\n\t\tdelete_post_meta( $post_id, 'podcast_episode_type' );\n\t}\n}\n"
  },
  {
    "path": "includes/post-meta-box.php",
    "content": "<?php\n/**\n * Add a meta box to the post edit screen, plus handlers for saving.\n *\n * @package tenup_podcasting;\n */\n\nnamespace tenup_podcasting;\n\n/**\n * Add a Podcasting metabox to the post edit screen.\n */\nfunction add_podcasting_meta_box() {\n\tadd_meta_box(\n\t\t'podcasting',\n\t\t__( 'Podcasting', 'simple-podcasting' ),\n\t\t__NAMESPACE__ . '\\meta_box_html',\n\t\t'post',\n\t\t'advanced',\n\t\t'default',\n\t\tarray(\n\t\t\t'__back_compat_meta_box' => true,\n\t\t)\n\t);\n}\nadd_action( 'add_meta_boxes', __NAMESPACE__ . '\\add_podcasting_meta_box' );\n\n/**\n * Output the Podcasting meta box.\n *\n * @param  object WP_Post $post The current post.\n */\nfunction meta_box_html( $post ) {\n\t$podcast_url       = get_post_meta( $post->ID, 'podcast_url', true );\n\t$podcast_explicit  = get_post_meta( $post->ID, 'podcast_explicit', true );\n\t$podcast_captioned = get_post_meta( $post->ID, 'podcast_captioned', true );\n\t$season_number     = get_post_meta( $post->ID, 'podcast_season_number', true );\n\t$episode_number    = get_post_meta( $post->ID, 'podcast_episode_number', true );\n\t$episode_type      = get_post_meta( $post->ID, 'podcast_episode_type', true );\n\t$episode_cover     = has_post_thumbnail( $post->ID ) ? get_the_post_thumbnail_url( $post->ID, 'thumbnail' ) : '';\n\n\twp_nonce_field( plugin_basename( __FILE__ ), 'simple-podcasting' );\n\t?>\n\t<p>\n\t\t<label for=\"podcast_closed_captioned\">\n\t\t\t<?php esc_html_e( 'Closed Captioned', 'simple-podcasting' ); ?>\n\t\t\t<input type=\"checkbox\" id=\"podcast_closed_captioned\" name=\"podcast_closed_captioned\" <?php checked( $podcast_captioned ); ?> />\n\t\t</label>\n\t</p>\n\n\t<p>\n\t\t<label for=\"podcast_explicit_content\">\n\t\t\t<?php esc_html_e( 'Explicit Content', 'simple-podcasting' ); ?>\n\t\t\t<select id=\"podcast_explicit_content\" name=\"podcast_explicit_content\">\n\t\t\t\t<option value=\"no\"<?php selected( $podcast_explicit, 'no' ); ?>><?php esc_html_e( 'No', 'simple-podcasting' ); ?></option>\n\t\t\t\t<option value=\"yes\"<?php selected( $podcast_explicit, 'yes' ); ?>><?php esc_html_e( 'Yes', 'simple-podcasting' ); ?></option>\n\t\t\t\t<option value=\"clean\"<?php selected( $podcast_explicit, 'clean' ); ?>><?php esc_html_e( 'Clean', 'simple-podcasting' ); ?></option>\n\t\t\t</select>\n\t\t</label>\n\t</p>\n\t<p>\n\t\t<label for=\"podcast_season_number\">\n\t\t\t<?php esc_html_e( 'Season Number', 'simple-podcasting' ); ?>\n\t\t\t<input type=\"text\" id=\"podcast_season_number\" name=\"podcast_season_number\" value=\"<?php echo esc_attr( $season_number ); ?>\" />\n\t\t</label>\n\t</p>\n\t<p>\n\t\t<label for=\"podcast_episode_number\">\n\t\t\t<?php esc_html_e( 'Episode Number', 'simple-podcasting' ); ?>\n\t\t\t<input type=\"text\" id=\"podcast_episode_number\" name=\"podcast_episode_number\" value=\"<?php echo esc_attr( $episode_number ); ?>\" />\n\t\t</label>\n\t</p>\n\t<div style=\"display: flex; align-items: center;\">\n\t\t<p style=\"margin-right: 5px;\"><?php esc_html_e( 'Episode Type', 'simple-podcasting' ); ?></p>\n\t\t<p>\n\t\t\t<input type=\"radio\" id=\"none\" name=\"podcast_episode_type\" value=\"none\" <?php echo isset( $episode_type ) && 'none' === $episode_type ? 'checked' : ''; ?>>\n\t\t\t<label for=\"none\"><?php esc_html_e( 'None', 'simple-podcasting' ); ?></label><br>\n\t\t\t<input type=\"radio\" id=\"full\" name=\"podcast_episode_type\" value=\"full\" <?php echo isset( $episode_type ) && 'full' === $episode_type ? 'checked' : ''; ?>>\n\t\t\t<label for=\"full\"><?php esc_html_e( 'Full', 'simple-podcasting' ); ?></label><br>\n\t\t\t<input type=\"radio\" id=\"trailer\" name=\"podcast_episode_type\" value=\"trailer\" <?php echo isset( $episode_type ) && 'trailer' === $episode_type ? 'checked' : ''; ?>>\n\t\t\t<label for=\"trailer\"><?php esc_html_e( 'Trailer', 'simple-podcasting' ); ?></label><br>\n\t\t\t<input type=\"radio\" id=\"bonus\" name=\"podcast_episode_type\" value=\"bonus\" <?php echo isset( $episode_type ) && 'bonus' === $episode_type ? 'checked' : ''; ?>>\n\t\t\t<label for=\"bonus\"><?php esc_html_e( 'Bonus', 'simple-podcasting' ); ?></label>\n\t\t</p>\n\t</div>\n\t<p>\n\t\t<label for=\"podcasting-episode-cover\"><?php esc_html_e( 'Episode Cover', 'simple-podcasting' ); ?></label>\n\t\t<p><?php esc_html_e( '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>\n\t\t<?php if ( ! empty( $episode_cover ) ) : ?>\n\t\t\t<img src=\"<?php echo esc_url( $episode_cover ); ?>\" alt=\"<?php esc_attr_e( 'Cover Art', 'simple-podcasting' ); ?>\" />\n\t\t<?php endif; ?>\n\t</p>\n\t<p>\n\t\t<label for=\"podcasting-enclosure-url\"><?php esc_html_e( 'Enclosure', 'simple-podcasting' ); ?></label>\n\t\t<input type=\"text\" id=\"podcasting-enclosure-url\" name=\"podcast_enclosure_url\" value=\"<?php echo esc_url( $podcast_url ); ?>\" size=\"35\" />\n\t\t<input type=\"button\" id=\"podcasting-enclosure-button\" value=\"<?php esc_attr_e( 'Choose File', 'simple-podcasting' ); ?>\" class=\"button\" data-modal-title=\"<?php esc_attr_e( 'Podcast Enclosure', 'simple-podcasting' ); ?>\" data-modal-button=\"<?php esc_attr_e( 'Select this file', 'simple-podcasting' ); ?>\" />\n\t</p>\n\n\t<p class=\"howto\"><?php esc_html_e( 'Optional: Use this field if you have more than one audio/video file in your post.', 'simple-podcasting' ); ?></p>\n\n\t<?php\n}\n\n/**\n * Handle the post save event, saving any data from the meta box.\n *\n * @param  [type] $post_id [description]\n * @return [type]          [description]\n */\nfunction save_meta_box( $post_id ) {\n\tif ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {\n\t\treturn;\n\t}\n\n\tif ( ! current_user_can( 'edit_post', $post_id ) ) {\n\t\treturn;\n\t}\n\n\tif ( empty( $_POST['simple-podcasting'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['simple-podcasting'] ) ), plugin_basename( __FILE__ ) ) ) {\n\t\treturn;\n\t}\n\n\t$_post = wp_unslash( $_POST );\n\n\t$url               = false;\n\t$podcast_captioned = 0;\n\t$podcast_explicit  = 'no';\n\t$season_number     = isset( $_post['podcast_season_number'] ) ? sanitize_text_field( $_post['podcast_season_number'] ) : '';\n\t$episode_number    = isset( $_post['podcast_episode_number'] ) ? sanitize_text_field( $_post['podcast_episode_number'] ) : '';\n\t$episode_type      = isset( $_post['podcast_episode_type'] ) && in_array( $_post['podcast_episode_type'], array( 'none', 'full', 'trailer', 'bonus' ), true ) ? sanitize_text_field( $_post['podcast_episode_type'] ) : '';\n\n\tif ( isset( $_post['podcast_closed_captioned'] ) && 'on' === $_post['podcast_closed_captioned'] ) {\n\t\t$podcast_captioned = 1;\n\t}\n\n\tif ( isset( $_post['podcast_explicit_content'] ) && in_array( $_post['podcast_explicit_content'], array( 'yes', 'no', 'clean' ), true ) ) {\n\t\t$podcast_explicit = sanitize_text_field( $_post['podcast_explicit_content'] );\n\t}\n\n\tif ( isset( $_post['podcast_enclosure_url'] ) && ! empty( $_post['podcast_enclosure_url'] ) ) {\n\t\t$url = sanitize_text_field( $_post['podcast_enclosure_url'] );\n\t} else {\n\t\t// Search for an audio shortcode to determine the audio enclosure url.\n\t\t$pattern = get_shortcode_regex();\n\t\t$post    = get_post( $post_id );\n\n\t\tif (\n\t\t\tpreg_match_all( '/' . $pattern . '/s', $post->post_content, $matches )\n\t\t\t&& array_key_exists( 2, $matches )\n\t\t\t&& in_array( 'audio', $matches[2], true )\n\t\t) {\n\t\t\tpreg_match( '/.*mp3=\\\\\"(.*)\\\\\".*/', $matches[0][0], $matches2 );\n\t\t\tif ( isset( $matches2[1] ) ) {\n\t\t\t\t$url = $matches2[1];\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Retrieve the enclosure and store its metadata in post meta.\n\t *\n\t * @todo only retrieve enclosure metadata when a podcasting term id is selected and the url has changed.\n\t */\n\tif ( $url ) {\n\t\t$podcast_meta = \\tenup_podcasting\\helpers\\get_podcast_meta_from_url( $url );\n\n\t\tif ( ! empty( $podcast_meta ) ) {\n\t\t\tupdate_post_meta( $post_id, 'podcast_url', $podcast_meta['url'] );\n\t\t\tupdate_post_meta( $post_id, 'podcast_filesize', $podcast_meta['filesize'] );\n\t\t\tupdate_post_meta( $post_id, 'podcast_duration', $podcast_meta['duration'] );\n\t\t\tupdate_post_meta( $post_id, 'podcast_mime', $podcast_meta['mime'] );\n\n\t\t\t// Add enclosure meta data\n\t\t\t$enclosure = $podcast_meta['url'] . \"\\n\" . $podcast_meta['filesize'] . \"\\n\" . $podcast_meta['mime'];\n\n\t\t\tupdate_post_meta( $post_id, 'enclosure', $enclosure );\n\t\t}\n\t}\n\n\tupdate_post_meta( $post_id, 'podcast_explicit', $podcast_explicit );\n\tupdate_post_meta( $post_id, 'podcast_captioned', $podcast_captioned );\n\tupdate_post_meta( $post_id, 'podcast_season_number', $season_number );\n\tupdate_post_meta( $post_id, 'podcast_episode_number', $episode_number );\n\tupdate_post_meta( $post_id, 'podcast_episode_type', $episode_type );\n}\nadd_action( 'save_post_post', __NAMESPACE__ . '\\save_meta_box' );\n\n/**\n * Enqueue helper script for the post edit and new post screens.\n *\n * @param  string $hook_suffix The current admin page.\n */\nfunction edit_post_enqueues( $hook_suffix ) {\n\t$screens = array(\n\t\t'post.php',\n\t\t'post-new.php',\n\t);\n\n\tif ( ! in_array( $hook_suffix, $screens, true ) ) {\n\t\treturn;\n\t}\n\n\twp_enqueue_script(\n\t\t'podcasting_edit_post_screen',\n\t\tPODCASTING_URL . 'dist/podcasting-edit-post.js',\n\t\tarray( 'jquery' ),\n\t\tPODCASTING_VERSION,\n\t\ttrue\n\t);\n}\nadd_action( 'admin_enqueue_scripts', __NAMESPACE__ . '\\edit_post_enqueues' );\n"
  },
  {
    "path": "includes/rest-external-url.php",
    "content": "<?php\n/**\n * Endpoint definitions.\n *\n * @package tenup_podcasting\\endpoints\n */\n\nnamespace tenup_podcasting\\endpoints\\externalurl;\n\n/**\n * Hook into admin_init action\n *\n * @since 0.1.0\n *\n * @uses add_action()\n *\n * @return void\n */\nfunction setup() {\n\t$n = function ( $function_name ) {\n\t\treturn __NAMESPACE__ . \"\\\\$function_name\";\n\t};\n\n\tadd_action( 'rest_api_init', $n( 'define_endpoint_for_external_files_meta_check' ) );\n}\n\n/**\n * Define the endpoint being used to retrieve and fill files ize, mime, and duration for external URLs\n */\nfunction define_endpoint_for_external_files_meta_check() {\n\n\tregister_rest_route(\n\t\t'simple-podcasting/v1',\n\t\t'external-url',\n\t\tarray(\n\t\t\t'methods'             => \\WP_REST_Server::READABLE,\n\t\t\t'callback'            => __NAMESPACE__ . '\\handle_request',\n\t\t\t'permission_callback' => function () {\n\t\t\t\treturn true;\n\t\t\t},\n\t\t\t'args'                => array(\n\t\t\t\t'url' => array(\n\t\t\t\t\t'required'          => true,\n\t\t\t\t\t'sanitize_callback' => 'sanitize_text_field',\n\t\t\t\t),\n\t\t\t),\n\t\t)\n\t);\n}\n\n/**\n * Callbakc for the external-url endpoint.\n *\n * @param \\WP_REST_Request $request The API request\n *\n * @return mixed|\\WP_REST_Response\n */\nfunction handle_request( \\WP_REST_Request $request ) {\n\n\t$url          = $request['url'];\n\t$cache_key    = 'spc_external_url_' . $url;\n\t$podcast_meta = get_transient( $cache_key );\n\tif ( false === $podcast_meta ) {\n\t\tif ( filter_var( $url, FILTER_VALIDATE_URL ) ) {\n\t\t\t$podcast_meta = \\tenup_podcasting\\helpers\\get_podcast_meta_from_url( $url );\n\t\t\tif ( $podcast_meta ) {\n\t\t\t\t$response = array(\n\t\t\t\t\t'success' => true,\n\t\t\t\t\t'data'    => $podcast_meta,\n\t\t\t\t);\n\t\t\t\tset_transient( $cache_key, $podcast_meta, MONTH_IN_SECONDS ); // We add the long expiry so we don't autoload the option in a non-object-cached env.\n\t\t\t}\n\t\t} else {\n\t\t\t$response = array(\n\t\t\t\t'success' => false,\n\t\t\t\t'message' => 'Invalid URL parameter passed',\n\t\t\t);\n\t\t}\n\t} else {\n\t\t$response = array(\n\t\t\t'success' => true,\n\t\t\t'data'    => $podcast_meta,\n\t\t);\n\t}\n\treturn rest_ensure_response( $response );\n}\n"
  },
  {
    "path": "includes/transcripts.php",
    "content": "<?php\n/**\n * Adds an endpoint for viewing transcripts.\n *\n * @package tenup_podcasting\\transcripts;\n */\n\nnamespace tenup_podcasting\\transcripts;\n\nuse DOMDocument;\nuse WP_Post;\n\n/**\n * Adds transcript query var.\n * Used to indicate that the transcript template should be rendered.\n *\n * @param array $vars Array of existing query vars.\n *\n * @return array\n */\nfunction query_vars( $vars ) {\n\t$vars[] = 'podcast-transcript';\n\t$vars[] = 'podcasting-episode';\n\treturn $vars;\n}\nadd_filter( 'query_vars', __NAMESPACE__ . '\\\\query_vars', 10, 1 );\n\n/**\n * Renders transcript template.\n *\n * @param string $template Template path.\n *\n * @return string\n */\nfunction template( $template ) {\n\tif ( ! get_query_var( 'podcast-transcript' ) || ! get_query_var( 'podcasting_podcasts' ) ) {\n\t\treturn $template;\n\t}\n\treturn PODCASTING_PATH . 'templates/transcript.php';\n}\nadd_filter( 'taxonomy_template', __NAMESPACE__ . '\\\\template', 10, 1 );\n\n/**\n * Adds rewrite rule to podcasts.\n *\n * @param array $rules Array of redirect rules.\n *\n * @return array\n */\nfunction rewrite_rules( $rules ) {\n\t$rules['^podcasts/([^/]+)/([^/]+)/transcript'] = 'index.php?podcast-transcript=1&podcasting_podcasts=$matches[1]&podcasting-episode=$matches[2]';\n\treturn $rules;\n}\nadd_filter( 'podcasting_podcasts_rewrite_rules', __NAMESPACE__ . '\\\\rewrite_rules', 10, 1 );\n\n/**\n * Get the transcript link from a post object\n *\n * @param WP_Post $post Post object.\n *\n * @return string url\n */\nfunction get_transcript_link_from_post( $post = null ) {\n\t$post = get_post( $post );\n\tif ( ! $post ) {\n\t\treturn false;\n\t}\n\t$podcast = get_the_terms( $post, PODCASTING_TAXONOMY_NAME );\n\tif ( ! $podcast ) {\n\t\treturn '';\n\t}\n\treturn trailingslashit( get_term_link( $podcast[0]->term_id ) ) . $post->post_name . '/transcript/';\n}\n\n/**\n * Adds <time> element to allowed html.\n *\n * @param array[] $html Allowed HTML tags.\n * @param string  $context Context name.\n * @return array[] html\n */\nfunction allow_time_element( $html, $context ) {\n\tif ( 'post' !== $context ) {\n\t\treturn $html;\n\t}\n\n\t$html['time'] = array();\n\n\treturn $html;\n}\nadd_filter( 'wp_kses_allowed_html', __NAMESPACE__ . '\\\\allow_time_element', 10, 2 );\n"
  },
  {
    "path": "includes/upgrade.php",
    "content": "<?php\n/**\n * Upgrade routines using options.\n *\n * @package tenup_podcasting\\upgrade;\n */\n\nnamespace tenup_podcasting\\upgrade;\n\n/**\n * Flush rewrite rules on version 2.0\n *\n * @return void\n */\nfunction maybe_flush_rewrite() {\n\t$version = get_option( 'simple_podcasting_db_version' );\n\tif ( ! $version || version_compare( '2.0', $version, '>=' ) ) {\n\t\tflush_rewrite_rules();\n\t\tupdate_option( 'simple_podcasting_db_version', PODCASTING_VERSION );\n\t}\n}\n\nadd_filter( 'admin_init', __NAMESPACE__ . '\\\\maybe_flush_rewrite', 10, 1 );\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"@10up/simple-podcasting\",\n  \"version\": \"1.9.1\",\n  \"description\": \"A simple podcasting solution for WordPress. \",\n  \"homepage\": \"https://github.com/10up/simple-podcasting\",\n  \"bugs\": {\n    \"url\": \"https://github.com/10up/simple-podcasting/issues\"\n  },\n  \"license\": \"GPL-2.0-or-later\",\n  \"author\": {\n    \"name\": \"10up\",\n    \"email\": \"opensource@10up.com\",\n    \"url\": \"https://10up.com\",\n    \"role\": \"developer\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/10up/simple-podcasting\"\n  },\n  \"scripts\": {\n    \"start\": \"wp-scripts start\",\n    \"build\": \"wp-scripts build\",\n    \"deploy\": \"npm install && npm run build\",\n    \"makepot\": \"wpi18n makepot --domain-path languages --pot-file simple-podcasting.pot --type plugin --main-file simple-podcasting.php --exclude node_modules,tests,docs,vendor\",\n    \"prepare\": \"husky install\",\n    \"wp-env\": \"wp-env\",\n    \"env:start\": \"wp-env start\",\n    \"env:stop\": \"wp-env stop\",\n    \"cypress:open\": \"cypress open --browser chrome --config-file tests/cypress/config.config.js\",\n    \"cypress:run\": \"cypress run --browser chrome --config-file tests/cypress/config.config.js\"\n  },\n  \"lint-staged\": {\n    \"*.php\": [\n      \"./vendor/bin/phpcs --standard=10up-Default --extensions=php --warning-severity=8 -s --runtime-set testVersion 7.4\"\n    ],\n    \"tests/**/*.js\": [\n      \"./node_modules/.bin/wp-scripts format\"\n    ]\n  },\n  \"devDependencies\": {\n    \"@10up/cypress-wp-utils\": \"^0.6.0\",\n    \"@wordpress/env\": \"^10.37.0\",\n    \"@wordpress/plugins\": \"^6.2.0\",\n    \"@wordpress/prettier-config\": \"^2.2.0\",\n    \"@wordpress/scripts\": \"^31.2.0\",\n    \"ansi-regex\": \">=6.0.1\",\n    \"copy-webpack-plugin\": \"^11.0.0\",\n    \"cypress-file-upload\": \"^5.0.8\",\n    \"cypress-localstorage-commands\": \"^2.2.4\",\n    \"cypress-mochawesome-reporter\": \"^3.6.0\",\n    \"husky\": \"^8.0.1\",\n    \"json-schema\": \">=0.4.0\",\n    \"lint-staged\": \"^13.0.3\",\n    \"markdown-it\": \"^13.0.1\",\n    \"mochawesome-json-to-md\": \"^0.7.2\",\n    \"node-wp-i18n\": \"^1.2.6\"\n  },\n  \"prettier\": \"@wordpress/prettier-config\",\n  \"dependencies\": {\n    \"use-debounce\": \"^8.0.4\"\n  }\n}\n"
  },
  {
    "path": "phpunit.xml.dist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit\n\t\tbootstrap=\"tests/unit/bootstrap.php\"\n\t\tbackupGlobals=\"false\"\n\t\tcolors=\"true\"\n\t\tconvertErrorsToExceptions=\"true\"\n\t\tconvertNoticesToExceptions=\"true\"\n\t\tconvertWarningsToExceptions=\"true\"\n\t\tverbose=\"true\"\n>\n\t<testsuites>\n\t\t<testsuite name=\"Simple Podcasting\">\n\t\t\t<directory prefix=\"test-\" suffix=\".php\">./tests/unit</directory>\n\t\t</testsuite>\n\t</testsuites>\n    <php>\n        <ini name=\"display_errors\" value=\"On\" />\n        <ini name=\"display_startup_errors\" value=\"On\" />\n    </php>\n</phpunit>\n"
  },
  {
    "path": "readme.txt",
    "content": "=== Simple Podcasting ===\nContributors: 10up, helen, adamsilverstein, jakemgold, jeffpaul, cadic\nTags:         podcasting, podcast, apple podcasts, episode, season\nTested up to: 6.9\nStable tag:   1.9.1\nLicense:      GPLv2 or later\nLicense URI:  http://www.gnu.org/licenses/gpl-2.0.html\n\nSet up multiple podcast feeds using built-in WordPress posts. Includes a podcast block and podcast transcript block for the WordPress block editor.\n\n== Description ==\n\nSet up multiple podcast feeds using built-in WordPress posts. Includes a podcast block and podcast transcript block for the WordPress block editor (aka Gutenberg).\n\nPodcasting is a method to distribute audio and video episodes 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:\n\n= Technical Notes =\n\n* Requires PHP 7.4+.\n* RSS feeds must not be disabled.\n\n== Installation ==\n\n1. Install the plugin via the plugin installer, either by searching for it or uploading a .zip file.\n2. Activate the plugin.\n3. Head to Posts → Podcasts and add at least one podcast.\n4. Create a post and insert an audio embed (or a podcast block in Gutenberg) and select a Podcast feed to include it in.\n\n== Usage ==\n\n= Create your podcast =\n\nFrom the WordPress Admin, go to Podcasts.\nTo create a podcast, complete all of the \"Add New Podcast\" fields and click \"Add New Podcast\".\n\n * Name: this title appears in Apple Podcasts and any other podcast apps.\n * Slug: this is the URL-friendly version of the Name field.\n * Subtitle: the subtitle also appears in Apple Podcasts and any other podcast apps.\n * Artist / Author name: the artist or producer of the work.\n * Podcast email: a contact email address for your podcast.\n * Summary: Apple Podcasts displays this summary when browsing through podcasts.\n * Copyright / License information: copyright information viewable in Apple Podcasts or other podcast apps.\n * Mark as explicit: mark Yes if podcast contains adult language or adult themes.\n * Language: the main language spoken in the podcast.\n * 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.\n * Keywords: add terms to help your podcast show up in search results on Apple Podcasts and other podcast apps.\n * Categories: these allow your podcast to show up for those browsing Apple Podcasts or other podcast apps by category.\n\nRepeat for each podcast you would like to create.\n\n= Add content to your podcast =\n\n * Create a new post and assign it to one or more Podcasts using the panel labeled Podcasts.\n * 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.\n * 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.\n * 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.\n\n= Submit your podcast feed to Apple Podcasts =\n\n* Each podcast has a unique feed URL you can find on the Podcasts page. This is the URL you will submit to Apple.\n* Ensure you test feeds before submitting them, see [Apple's \"Test a Podcast page\"](https://help.apple.com/itc/podcasts_connect/#/itcac471c970) for more information.\n* 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 [Apple's \"Submit a podcast\" page](https://help.apple.com/itc/podcasts_connect/#/itcd88ea40b9) for more information.\n\n= Submit your podcast feed to Pocket Casts =\n\n* Validate your feeds at [https://www.castfeedvalidator.com/ Cast Feed Validator] before submitting them.\n* Submit the podcast feed to https://pocketcasts.com/submit/.\n\n= Control how many episodes are listed on the feed =\n\nIf you want to adjust the default number of episodes included in a podcast RSS feed, then utilize the following to do so...\n\n`<?php\n\nadd_filter( 'simple_podcasting_episodes_per_page', 'podcasting_feed_episodes_per_page' );\n\n/**\n * Filter how many items are displayed on the feed\n * Default is 250\n *\n * @param int $qty Items count.\n * @return string\n */\nfunction podcasting_feed_episodes_per_page( $qty ) {\n\treturn 300;\n}\n`\n\n= Customize RSS feed =\n\nIf you want to modify RSS feed items output, there is a filter for that:\n\n`<?php\nfunction podcasting_feed_item_filter( $feed_item = array(), $post_id = null, $term_id = null ) {\n\tif ( 42 === $post_id ) {\n\t\t$feed_item['keywords'] = 'one,two,three';\n\t}\n\treturn $feed_item;\n}\nadd_filter( 'simple_podcasting_feed_item', 'podcasting_feed_item_filter', 10, 3 );\n`\n\n== Frequently Asked Questions ==\n\n= How do I get my podcast featured on Pocket Casts? =\n\nThe 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.\n\nIf you’d like to suggest your podcast for a featured spot, reach out to `curation@pocketcasts.com`\n\nFor more information, [https://pocketcasts.com/podcast-producers/ read more].\n\n= How do I submit private and paid podcast feeds? =\n\nFollow this documentation to submit [https://support.pocketcasts.com/article/password-protected-podcasts-2/ private and paid podcast feeds]\n\n= Where do I report security bugs found in this plugin? =\n\nPlease 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.\n\n== Screenshots ==\n\n1. Podcast in block editor\n2. Podcast Platforms block in the block editor\n3. Creating a podcast\n4. Podcast feed\n5. Podcast Grid pattern\n6. Podcast Transcript block\n\n== Changelog ==\n\n= 1.9.1 - 2025-05-16 =\n* **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)).\n* **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)).\n* **Changed:** 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)).\n* **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)).\n* **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)).\n* **Security:** 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)).\n* **Security:** 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)).\n\n= 1.9.0 - 2024-11-18 =\n* **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)).\n* **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)).\n* **Changed:** 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)).\n* **Changed:** 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)).\n* **Changed:** 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)).\n* **Security:** 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)).\n* **Security:** 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)).\n* **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)).\n* **Security:** 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)).\n* **Security:** 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)).\n\n= 1.8.0 - 2024-04-03 =\n* **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)).\n* **Added:** 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)).\n* **Added:** `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)).\n* **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)).\n* **Fixed:** 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)).\n* **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)).\n* **Changed:** 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)).\n* **Changed:** 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)).\n* **Changed:** 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)).\n* **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)).\n\n= 1.7.0 - 2024-01-16 =\n* **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)).\n* **Added:** 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)).\n* **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)).\n* **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)).\n* **Security:** 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)).\n\n= 1.6.1 - 2023-11-21 =\n* **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)).\n* **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)).\n* **Changed:** 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)).\n* **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)).\n* **Security:** 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)).\n\n= 1.6.0 - 2023-08-31 =\n* **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)).\n* **Added:** 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)).\n* **Added:** 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)).\n* **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)).\n* **Changed:** 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)).\n* **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)).\n* **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)).\n\n[View historical changelog details here](https://github.com/10up/simple-podcasting/blob/develop/CHANGELOG.md).\n\n== Upgrade Notice ==\n\n= 1.9.1 =\nThis release bumps the minimum required version of WordPress from 6.5 to 6.6.\n\n= 1.9.0 =\nThis release bumps the minimum required version of WordPress from 5.7 to 6.5.\n\n= 1.3.0 =\nNote 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.\n"
  },
  {
    "path": "simple-podcasting.php",
    "content": "<?php\n/**\n * Plugin Name:       Simple Podcasting\n * Plugin URI:        https://github.com/10up/simple-podcasting\n * Description:       Easily set up multiple podcast feeds using built-in WordPress posts. Includes a podcast block for the new WordPress editor.\n * Version:           1.9.1\n * Requires at least: 6.6\n * Requires PHP:      7.4\n * Author:            10up\n * Author URI:        http://10up.com/\n * License:           GPL v2 or later\n * License URI:       https://www.gnu.org/licenses/gpl-2.0.html\n * Text Domain:       simple-podcasting\n *\n * @package tenup_podcasting\n */\n\nnamespace tenup_podcasting;\n\n/**\n * Get the minimum version of PHP required by this plugin.\n *\n * @since 1.6.0\n *\n * @return string Minimum version required.\n */\nfunction minimum_php_requirement() {\n\treturn '7.4';\n}\n\n/**\n * Whether PHP installation meets the minimum requirements\n *\n * @since 1.6.0\n *\n * @return bool True if meets minimum requirements, false otherwise.\n */\nfunction site_meets_php_requirements() {\n\treturn version_compare( phpversion(), minimum_php_requirement(), '>=' );\n}\n\n// Try to load the plugin files, ensuring our PHP version is met first.\nif ( ! site_meets_php_requirements() ) {\n\tadd_action(\n\t\t'admin_notices',\n\t\tfunction () {\n\t\t\t?>\n\t\t\t<div class=\"notice notice-error\">\n\t\t\t\t<p>\n\t\t\t\t\t<?php\n\t\t\t\t\techo wp_kses_post(\n\t\t\t\t\t\tsprintf(\n\t\t\t\t\t\t\t/* translators: %s: Minimum required PHP version */\n\t\t\t\t\t\t\t__( 'Simple Podcasting requires PHP version %s or later. Please upgrade PHP or disable the plugin.', 'simple-podcasting' ),\n\t\t\t\t\t\t\tesc_html( minimum_php_requirement() )\n\t\t\t\t\t\t)\n\t\t\t\t\t);\n\t\t\t\t\t?>\n\t\t\t\t</p>\n\t\t\t</div>\n\t\t\t<?php\n\t\t}\n\t);\n\treturn;\n}\n\ndefine( 'PODCASTING_VERSION', '1.9.1' );\ndefine( 'PODCASTING_PATH', __DIR__ . '/' );\ndefine( 'PODCASTING_URL', plugin_dir_url( __FILE__ ) );\ndefine( 'PODCASTING_TAXONOMY_NAME', 'podcasting_podcasts' );\ndefine( 'PODCASTING_ITEMS_PER_PAGE', 250 );\n\nrequire_once PODCASTING_PATH . 'includes/create-podcast.php';\nrequire_once PODCASTING_PATH . 'includes/admin/onboarding.php';\nrequire_once PODCASTING_PATH . 'includes/admin/create-podcast-component.php';\nrequire_once PODCASTING_PATH . 'includes/datatypes.php';\nrequire_once PODCASTING_PATH . 'includes/helpers.php';\nrequire_once PODCASTING_PATH . 'includes/rest-external-url.php';\nrequire_once PODCASTING_PATH . 'includes/transcripts.php';\nrequire_once PODCASTING_PATH . 'includes/upgrade.php';\n\n// Init the endpoint.\nendpoints\\externalurl\\setup();\n\n/**\n * Flush rewrite rules on plugin activation.\n *\n * `flush_rewrite_rules()` cannot just be hooked on directly\n * because the taxonomy is not yet registered at that point.\n * So we call the taxonomy registration function ourselves first.\n *\n * @return void\n */\nfunction activate_plugin() {\n\tcreate_podcasts_taxonomy();\n\t\\flush_rewrite_rules();\n\n\t$terms = get_terms(\n\t\tarray(\n\t\t\t'taxonomy'   => 'podcasting_podcasts',\n\t\t\t'hide_empty' => false,\n\t\t)\n\t);\n\n\t$has_podcast = is_array( $terms ) && ! empty( $terms );\n\n\tif ( $has_podcast ) {\n\t\tupdate_option( 'simple_podcasting_onboarding', 'completed' );\n\t}\n\n\tif ( '' === get_option( 'simple_podcasting_onboarding', '' ) ) {\n\t\tupdate_option( 'simple_podcasting_onboarding', 'no' );\n\t}\n}\nregister_activation_hook( __FILE__, __NAMESPACE__ . '\\activate_plugin' );\n\n// Block editor support.\nif ( function_exists( 'register_block_type' ) ) {\n\trequire_once PODCASTING_PATH . 'includes/blocks.php';\n}\n\nif ( function_exists( 'register_block_pattern' ) ) {\n\trequire_once PODCASTING_PATH . 'includes/block-patterns.php';\n}\n\n/**\n * Is podcasting enabled?\n *\n * If there are any podcast terms set up, podcasting is enabled.\n *\n * @return bool\n */\nfunction podcasting_is_enabled() {\n\t$podcasting_terms = get_terms(\n\t\tarray(\n\t\t\t'taxonomy'      => PODCASTING_TAXONOMY_NAME,\n\t\t\t'hide_empty'    => false,\n\t\t\t'fields'        => 'ids',\n\t\t\t'no_found_rows' => true,\n\t\t)\n\t);\n\n\treturn ! empty( $podcasting_terms );\n}\n\n/**\n * Enqueue admin scripts and styles on the term and term edit screens.\n *\n * @param  string $hook_suffix The $hook_suffix for the current admin page.\n */\nfunction podcasting_edit_term_enqueues( $hook_suffix ) {\n\t$screens = array(\n\t\t'edit-tags.php',\n\t\t'term.php',\n\t\t'admin_page_simple-podcasting-onboarding',\n\t);\n\n\tif ( in_array( $hook_suffix, $screens, true ) ) {\n\t\twp_enqueue_style(\n\t\t\t'podcasting_edit_term_screen',\n\t\t\tPODCASTING_URL . 'dist/podcasting-edit-term.css',\n\t\t\tarray(),\n\t\t\tPODCASTING_VERSION\n\t\t);\n\n\t\twp_enqueue_script(\n\t\t\t'podcasting_edit_term_screen',\n\t\t\tPODCASTING_URL . 'dist/podcasting-edit-term.js',\n\t\t\tarray( 'jquery' ),\n\t\t\tPODCASTING_VERSION,\n\t\t\ttrue\n\t\t);\n\t}\n\n\tif ( in_array( $hook_suffix, $screens, true ) ) {\n\t\twp_enqueue_media();\n\t\twp_enqueue_script(\n\t\t\t'podcasting_onboarding_screen_script',\n\t\t\tPODCASTING_URL . 'dist/podcasting-onboarding.js',\n\t\t\tarray( 'jquery' ),\n\t\t\tPODCASTING_VERSION,\n\t\t\ttrue\n\t\t);\n\n\t\twp_enqueue_script(\n\t\t\t'podcasting_edit_term_screen',\n\t\t\tPODCASTING_URL . 'dist/podcasting-edit-term.js',\n\t\t\tarray( 'jquery' ),\n\t\t\tPODCASTING_VERSION,\n\t\t\ttrue\n\t\t);\n\n\t\twp_localize_script(\n\t\t\t'podcasting_edit_term_screen',\n\t\t\t'podcastingEditPostVars',\n\t\t\tarray(\n\t\t\t\t'iconUrl' => PODCASTING_URL . 'dist/images/icons',\n\t\t\t)\n\t\t);\n\n\t\twp_enqueue_style(\n\t\t\t'podcasting_onboarding_screen_style',\n\t\t\tPODCASTING_URL . 'dist/podcasting-onboarding.css',\n\t\t\tarray(),\n\t\t\tPODCASTING_VERSION\n\t\t);\n\n\t\twp_enqueue_style( 'podcasting_onboarding_fonts', 'https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap' ); // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion\n\t}\n}\nadd_action( 'admin_enqueue_scripts', __NAMESPACE__ . '\\podcasting_edit_term_enqueues' );\n\n/**\n * Load the file containing iTunes specific feed hooks.\n *\n * @param \\WP_Query $query The query being parsed.\n *\n * @uses includes/customize-feed.php\n */\nfunction custom_feed( \\WP_Query $query ) {\n\tif ( is_admin() || ! podcasting_is_enabled() ) {\n\t\treturn;\n\t}\n\n\t// Is this a feed for a term in the podcasting taxonomy?\n\tif ( $query->is_feed() && $query->is_tax( PODCASTING_TAXONOMY_NAME ) ) {\n\t\tremove_action( 'rss2_head', 'rss2_blavatar' );\n\t\tremove_action( 'rss2_head', 'rss2_site_icon' );\n\t\tremove_filter( 'the_excerpt_rss', 'add_bug_to_feed', 100 );\n\t\tremove_action( 'rss2_head', 'rsscloud_add_rss_cloud_element' );\n\t\tadd_filter(\n\t\t\t'wp_feed_cache_transient_lifetime',\n\t\t\tfunction () {\n\t\t\t\treturn HOUR_IN_SECONDS;\n\t\t\t}\n\t\t);\n\t\trequire_once PODCASTING_PATH . 'includes/customize-feed.php';\n\t}\n}\nadd_action( 'parse_query', __NAMESPACE__ . '\\custom_feed', 10, 1 );\n\n\n/**\n * Initialize the edit screen if podcasting is enabled.\n *\n * @uses includes/post-meta-box.php\n */\nfunction setup_edit_screen() {\n\tif ( podcasting_is_enabled() ) {\n\t\trequire_once PODCASTING_PATH . 'includes/post-meta-box.php';\n\t}\n}\nadd_action( 'admin_init', __NAMESPACE__ . '\\setup_edit_screen' );\n\n/**\n * Registers block assets for Podcast block.\n */\nfunction register_podcast_block_assets() {\n\t$block_asset = [\n\t\t'version'      => PODCASTING_VERSION,\n\t\t'dependencies' => [],\n\t];\n\tif ( file_exists( PODCASTING_PATH . 'dist/podcast.asset.php' ) ) {\n\t\t$block_asset = require PODCASTING_PATH . 'dist/podcast.asset.php';\n\t}\n\n\twp_register_style(\n\t\t'podcast-block',\n\t\tPODCASTING_URL . 'dist/podcast.css',\n\t\tarray(),\n\t\t$block_asset['version'],\n\t\t'all'\n\t);\n\n\twp_enqueue_style( 'podcast-block' );\n}\nadd_action( 'wp_enqueue_scripts', __NAMESPACE__ . '\\register_podcast_block_assets' );\n\n/**\n * Registers block assets for Podcast block in admin.\n */\nfunction register_podcast_block_assets_admin() {\n\t$block_asset = [\n\t\t'version'      => PODCASTING_VERSION,\n\t\t'dependencies' => [],\n\t];\n\tif ( file_exists( PODCASTING_PATH . 'dist/podcast.asset.php' ) ) {\n\t\t$block_asset = require PODCASTING_PATH . 'dist/podcast.asset.php';\n\t}\n\n\twp_register_style(\n\t\t'podcast-block',\n\t\tPODCASTING_URL . 'dist/podcast.css',\n\t\tarray(),\n\t\t$block_asset['version'],\n\t\t'all'\n\t);\n\n\twp_enqueue_style( 'podcast-block' );\n}\nadd_action( 'admin_enqueue_scripts', __NAMESPACE__ . '\\register_podcast_block_assets_admin' );\n\n/**\n * Registers block assets for Latest Episode.\n */\nfunction register_latest_episode_assets() {\n\tif ( ! file_exists( PODCASTING_PATH . 'dist/latest-episode.asset.php' ) ) {\n\t\treturn;\n\t}\n\n\t$block_asset = require PODCASTING_PATH . 'dist/latest-episode.asset.php';\n\n\twp_register_style(\n\t\t'latest-episode-block',\n\t\tPODCASTING_URL . 'dist/latest-episode.css',\n\t\tarray(),\n\t\t$block_asset['version'],\n\t\t'all'\n\t);\n\n\twp_enqueue_style( 'latest-episode-block' );\n}\nadd_action( 'wp_enqueue_scripts', __NAMESPACE__ . '\\register_latest_episode_assets' );\n\n/**\n * Registers block assets for Latest Episode in admin.\n */\nfunction register_latest_episode_assets_admin() {\n\tif ( ! file_exists( PODCASTING_PATH . 'dist/latest-episode.asset.php' ) ) {\n\t\treturn;\n\t}\n\n\t$block_asset = require PODCASTING_PATH . 'dist/latest-episode.asset.php';\n\n\twp_register_style(\n\t\t'latest-episode-block',\n\t\tPODCASTING_URL . 'dist/latest-episode.css',\n\t\tarray(),\n\t\t$block_asset['version'],\n\t\t'all'\n\t);\n\n\twp_enqueue_style( 'latest-episode-block' );\n}\nadd_action( 'admin_enqueue_scripts', __NAMESPACE__ . '\\register_latest_episode_assets_admin' );\n"
  },
  {
    "path": "templates/transcript.php",
    "content": "<?php\n/**\n * Template for transcripts.\n * Intentionally barebones with the minimum html for use by tools.\n *\n * @package tenup_podcasting\n */\n\n?>\n<!DOCTYPE html>\n<html lang=\"<?php echo esc_attr( get_locale() ); ?>\">\n<head>\n\t<meta charset=\"UTF-8\">\n\t<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n\t<?php\n\tif ( function_exists( 'wp_robots' ) && function_exists( 'wp_robots_no_robots' ) && function_exists( 'add_filter' ) ) {\n\t\tadd_filter( 'wp_robots', 'wp_robots_no_robots' );\n\t\twp_robots();\n\t}\n\t?>\n\t<title>\n\t\t<?php\n\t\tprintf(\n\t\t\t/* translators: %s: The page title */\n\t\t\tesc_html__( 'Transcript - %s', 'simple-podcasting' ),\n\t\t\twp_strip_all_tags( get_the_title() ) // phpcs:ignore WordPress.Security.EscapeOutput\n\t\t);\n\t\t?>\n\t</title>\n</head>\n<body>\n<?php\n$podcast_slug = get_query_var( 'podcasting-episode' );\n$post_object  = get_page_by_path( $podcast_slug, OBJECT, 'post' );\nif ( $post_object instanceof WP_Post ) {\n\techo wp_kses_post(\n\t\tdo_blocks(\n\t\t\tget_post_meta( $post_object->ID, 'podcast_transcript', true )\n\t\t)\n\t);\n}\n?>\n</body>\n</html>\n"
  },
  {
    "path": "tests/bin/set-wp-config.js",
    "content": "#!/usr/bin/env node\n\nconst fs = require('fs');\n\nconst path = `${process.cwd()}/.wp-env.override.json`;\n\nlet config = fs.existsSync(path) ? require(path) : { plugins: ['.'] };\n\nconst args = {};\nprocess.argv.slice(2, process.argv.length).forEach((arg) => {\n\tif (arg.slice(0, 2) === '--') {\n\t\tconst param = arg.split('=');\n\t\tconst paramName = param[0].slice(2, param[0].length);\n\t\tconst paramValue = param.length > 1 ? param[1] : true;\n\t\targs[paramName] = paramValue;\n\t}\n});\n\nif (!args.core && !args.plugins) {\n\treturn;\n}\n\nif ('latest' === args.core) {\n\tdelete args.core;\n}\n\nif (Object.keys(args).length === 0) {\n\treturn;\n}\n\nif (args.plugins) {\n\targs.plugins = args.plugins.split(',');\n}\n\nconfig = {\n\t...config,\n\t...args,\n};\n\ntry {\n\tfs.writeFileSync(path, JSON.stringify(config));\n} catch (err) {\n\tconsole.error(err);\n}\n"
  },
  {
    "path": "tests/cypress/.eslintrc",
    "content": "{\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",
    "content": "const { defineConfig } = require('cypress');\n\nmodule.exports = defineConfig({\n\tfixturesFolder: __dirname + '/fixtures',\n\tscreenshotsFolder: __dirname + '/screenshots',\n\tvideosFolder: __dirname + '/videos',\n\tdownloadsFolder: __dirname + '/downloads',\n\tvideo: true,\n\treporter: 'mochawesome',\n\treporterOptions: {\n\t\tmochaFile: 'mochawesome-[name]',\n\t\treportDir: __dirname + '/reports',\n\t\toverwrite: false,\n\t\thtml: false,\n\t\tjson: true,\n\t},\n\tchromeWebSecurity: false,\n\tenv: {\n\t\tHAS_BLOCK_EDITOR: true,\n\t},\n\te2e: {\n\t\t// We've imported your old cypress plugins here.\n\t\t// You may want to clean this up later by importing these.\n\t\tsetupNodeEvents(on, config) {\n\t\t\treturn require(__dirname + '/plugins/index.js')(on, config);\n\t\t},\n\t\tsupportFile: __dirname + '/support/index.js',\n\t\tspecPattern: [\n\t\t\t'tests/cypress/integration/admin.test.js',\n\t\t\t'tests/cypress/integration/onboarding.test.js',\n\t\t\t'tests/cypress/integration/taxonomy.test.js',\n\t\t\t'tests/cypress/integration/block.test.js',\n\t\t\t'tests/cypress/integration/podcast-setting-panel.test.js',\n\t\t],\n\t},\n});\n"
  },
  {
    "path": "tests/cypress/fixtures/example.json",
    "content": "{\n  \"name\": \"Using fixtures to represent data\",\n  \"email\": \"hello@cypress.io\",\n  \"body\": \"Fixtures are a great way to mock data for responses to routes\"\n}\n"
  },
  {
    "path": "tests/cypress/integration/admin.test.js",
    "content": "describe('Admin can login and make sure plugin is activated', () => {\n\tbeforeEach(() => {\n\t\tcy.login();\n\t});\n\n\tit('Can activate plugin if it is deactivated', () => {\n\t\tcy.deactivatePlugin('simple-podcasting');\n\t\tcy.activatePlugin('simple-podcasting');\n\t});\n});\n"
  },
  {
    "path": "tests/cypress/integration/block.test.js",
    "content": "import 'cypress-localstorage-commands';\nconst { populatePodcast } = require('../support/functions');\n\ndescribe('Admin can publish posts with podcast block', () => {\n\tconst taxonomy = 'Remote work';\n\n\tbefore(() => {\n\t\tconst userId = '1';\n\t\tcy.setLocalStorage(\n\t\t\t`WP_DATA_USER_${userId}`,\n\t\t\tJSON.stringify({\n\t\t\t\t'core/edit-post': {\n\t\t\t\t\tpreferences: {\n\t\t\t\t\t\tfeatures: {\n\t\t\t\t\t\t\twelcomeGuide: false,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t})\n\t\t);\n\t});\n\n\tif (Cypress.env('HAS_BLOCK_EDITOR')) {\n\t\tit('Can insert the block and publish the post', () => {\n\t\t\tcy.login();\n\t\t\tcy.uploadMedia('tests/cypress/fixtures/example.jpg');\n\t\t\tcy.createTerm(taxonomy, 'podcasting_podcasts', {\n\t\t\t\tbeforeSave: () => {\n\t\t\t\t\tpopulatePodcast({\n\t\t\t\t\t\tauthor: 'Person Doe',\n\t\t\t\t\t\tsummary: 'Lorem ipsum dolor',\n\t\t\t\t\t\tcategory: 'arts:food',\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tcy.visit('/wp-admin/post-new.php');\n\t\t\tcy.closeWelcomeGuide();\n\t\t\tcy.get('h1.editor-post-title__input, #post-title-0')\n\t\t\t\t.first()\n\t\t\t\t.as('title-input');\n\t\t\tcy.get('@title-input').click().type('Test episode');\n\t\t\tcy.get(\n\t\t\t\t'.edit-post-header-toolbar__inserter-toggle, .editor-document-tools__inserter-toggle'\n\t\t\t).click();\n\t\t\tcy.get(\n\t\t\t\t'#components-search-control-0, #block-editor-inserter__search-0'\n\t\t\t)\n\t\t\t\t.first()\n\t\t\t\t.as('block-search');\n\t\t\tcy.get('@block-search').click().type('Podcast');\n\t\t\tcy.get('.editor-block-list-item-podcasting-podcast', {\n\t\t\t\ttimeout: 4000,\n\t\t\t}).click();\n\t\t\tcy.get(\n\t\t\t\t'.edit-post-header-toolbar__inserter-toggle, .editor-document-tools__inserter-toggle'\n\t\t\t).click();\n\t\t\tcy.get(\n\t\t\t\t'.wp-block-podcasting-podcast input[type=\"file\"]'\n\t\t\t).attachFile('example.mp3');\n\t\t\tcy.get('.wp-block-podcasting-podcast audio')\n\t\t\t\t.should('have.attr', 'src')\n\t\t\t\t.and('include', 'example');\n\t\t\tcy.openDocumentSettingsSidebar();\n\t\t\tcy.openDocumentSettingsPanel('Podcasts');\n\t\t\tcy.get('.components-panel__body')\n\t\t\t\t.contains('Podcasts')\n\t\t\t\t.parents('.components-panel__body')\n\t\t\t\t.find('.components-checkbox-control__label')\n\t\t\t\t.contains(taxonomy)\n\t\t\t\t.click();\n\t\t\tcy.get('.editor-post-publish-panel__toggle').click();\n\t\t\tcy.get('.editor-post-publish-button').click();\n\t\t\tcy.get('.components-snackbar', { timeout: 10000 }).should(\n\t\t\t\t'be.visible'\n\t\t\t);\n\t\t\tcy.get(\n\t\t\t\t'a.components-snackbar__action:is(.components-button, .components-external-link)'\n\t\t\t).click();\n\t\t\tcy.get('.wp-block-podcasting-podcast audio')\n\t\t\t\t.should('have.attr', 'src')\n\t\t\t\t.and('include', 'example');\n\t\t\tcy.visit('/wp-admin/edit.php');\n\t\t\tcy.get('.column-taxonomy-podcasting_podcasts').should(\n\t\t\t\t'contain.text',\n\t\t\t\ttaxonomy\n\t\t\t);\n\t\t});\n\t}\n});\n"
  },
  {
    "path": "tests/cypress/integration/onboarding.test.js",
    "content": "const {\n\trandomName,\n\tpopulatePodcast,\n\tdeleteAllTerms,\n} = require('../support/functions');\n\ndescribe('Onboarding tests', () => {\n\tbeforeEach(() => {\n\t\tcy.login();\n\t\tcy.uploadMedia('tests/cypress/fixtures/example.jpg');\n\t\tcy.activatePlugin('simple-podcasting');\n\t\tcy.visit(\n\t\t\t'/wp-admin/edit-tags.php?taxonomy=podcasting_podcasts&podcasts=true'\n\t\t);\n\t\tdeleteAllTerms();\n\t\tcy.deactivatePlugin('simple-podcasting');\n\t\tcy.visit('/wp-admin/options.php');\n\t\tcy.get('body').then(($body) => {\n\t\t\tif ($body.find('#simple_podcasting_onboarding').length !== 0) {\n\t\t\t\tcy.get('#simple_podcasting_onboarding')\n\t\t\t\t\t.click()\n\t\t\t\t\t.type('{selectAll}no');\n\t\t\t\tcy.get('.submit input[type=submit]').click();\n\t\t\t}\n\t\t});\n\t\tcy.activatePlugin('simple-podcasting');\n\t});\n\n\tit('Should indicate errors', () => {\n\t\tcy.url().should('include', 'simple-podcasting-onboarding');\n\t\tcy.url().should('include', 'step=1');\n\n\t\tcy.get('#simple-podcasting__create-podcast-button')\n\t\t\t.closest('form')\n\t\t\t.submit();\n\n\t\tcy.get('.notice-error').should(\n\t\t\t'contain',\n\t\t\t'A podcast name is required.'\n\t\t);\n\t});\n\n\tit('Should pass onboarding', () => {\n\t\tcy.url().should('include', 'simple-podcasting-onboarding');\n\t\tcy.url().should('include', 'step=1');\n\n\t\tconst podcastName = 'Onboarding ' + randomName();\n\t\tcy.get('input[name=podcast-name]').click().type(podcastName);\n\t\tpopulatePodcast({\n\t\t\tauthor: 'Person Doe',\n\t\t\tsummary: 'Lorem ipsum dolor',\n\t\t\tcategory: 'Arts',\n\t\t\tonboarding: true,\n\t\t});\n\t\tcy.get('#simple-podcasting__create-podcast-button')\n\t\t\t.closest('form')\n\t\t\t.submit();\n\n\t\tcy.url().should('include', 'step=2');\n\n\t\tcy.visit(\n\t\t\t'/wp-admin/edit-tags.php?taxonomy=podcasting_podcasts&podcasts=true'\n\t\t);\n\n\t\t// Podcast was created.\n\t\tcy.contains(podcastName);\n\n\t\t// Option was saved.\n\t\tcy.visit('/wp-admin/options.php');\n\t\tcy.get('#simple_podcasting_onboarding').should(\n\t\t\t'have.value',\n\t\t\t'completed'\n\t\t);\n\t});\n});\n"
  },
  {
    "path": "tests/cypress/integration/podcast-setting-panel.test.js",
    "content": "describe('Create podcast setting panel', () => {\n\tbefore(() => {\n\t\tcy.login();\n\t\tcy.visit(\n\t\t\t'/wp-admin/edit-tags.php?taxonomy=podcasting_podcasts&podcasts=true'\n\t\t);\n\t\tcy.get('body').then(($body) => {\n\t\t\tif (\n\t\t\t\t$body.find('[aria-label=\"Delete “Family People”\"]').length > 0\n\t\t\t) {\n\t\t\t\tcy.get('[aria-label=\"Delete “Family People”\"]').click({\n\t\t\t\t\tforce: true,\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\n\t\tcy.visit('/wp-admin/post-new.php');\n\t\tcloseWelcomeGuide();\n\t\topenEditorSidebar();\n\t});\n\n\tbeforeEach(() => {\n\t\tcy.login();\n\t});\n\n\t/**\n\t * Closes welcome guide programatically.\n\t */\n\tfunction closeWelcomeGuide() {\n\t\tcy.window().then((win) => {\n\t\t\tif (\n\t\t\t\twin.wp.data\n\t\t\t\t\t.select('core/edit-post')\n\t\t\t\t\t.isFeatureActive('welcomeGuide')\n\t\t\t) {\n\t\t\t\twin.wp.data\n\t\t\t\t\t.dispatch('core/edit-post')\n\t\t\t\t\t.toggleFeature('welcomeGuide');\n\t\t\t}\n\t\t});\n\t}\n\n\tfunction openEditorSidebar() {\n\t\tcy.get('button.components-button[aria-label=\"Settings\"]').should(\n\t\t\t'exist'\n\t\t);\n\t\tcy.get('body').then(($body) => {\n\t\t\tif (!$body.find('.edit-post-sidebar').length) {\n\t\t\t\tcy.get(\n\t\t\t\t\t'button.components-button[aria-label=\"Settings\"]'\n\t\t\t\t).click();\n\t\t\t}\n\t\t});\n\t}\n\n\tfunction openComplementaryArea() {\n\t\tcy.window().then((win) => {\n\t\t\tif (\n\t\t\t\twin.wp.data.dispatch('core/interface').enableComplementaryArea\n\t\t\t) {\n\t\t\t\twin.wp.data\n\t\t\t\t\t.dispatch('core/interface')\n\t\t\t\t\t.enableComplementaryArea('core', 'edit-post/block');\n\t\t\t}\n\t\t});\n\t}\n\n\tit('Podcast setting panel exists', () => {\n\t\tcy.visit('/wp-admin/post-new.php');\n\t\tcloseWelcomeGuide();\n\t\topenComplementaryArea();\n\t\tcy.get('.podcasting__podcast-list').should('exist');\n\t});\n\n\tit('Create podcast popup shows when clicking creating a podcast', () => {\n\t\tcy.visit('/wp-admin/post-new.php');\n\t\tcloseWelcomeGuide();\n\t\tcy.get('.podcasting__podcast-list').click();\n\t\tcy.get('.podcasting__add-new-podcast').click();\n\t\tcy.get('.components-modal__content').should('exist');\n\t});\n\n\tit('Create podcast popup has valid fields', () => {\n\t\tcy.visit('/wp-admin/post-new.php');\n\t\tcloseWelcomeGuide();\n\t\tcy.get('.podcasting__podcast-list').click();\n\t\tcy.get('.podcasting__add-new-podcast').click();\n\n\t\tcy.get('.podcasting__modal-name-field input').should(\n\t\t\t'have.attr',\n\t\t\t'required'\n\t\t);\n\t\tcy.get('.podcasting__modal-artist-field input').should(\n\t\t\t'have.attr',\n\t\t\t'required'\n\t\t);\n\t\tcy.get('.podcasting__modal-category-field select').should(\n\t\t\t'have.attr',\n\t\t\t'required'\n\t\t);\n\t\tcy.get('.podcasting__modal-summary-field textarea').should(\n\t\t\t'have.attr',\n\t\t\t'required'\n\t\t);\n\t});\n\n\tit('Create podcast using popup', () => {\n\t\tcy.visit('/wp-admin/post-new.php');\n\t\tcloseWelcomeGuide();\n\t\tcy.get('.podcasting__podcast-list').click();\n\t\tcy.get('.podcasting__add-new-podcast').click();\n\n\t\tcy.get('.podcasting__modal-name-field input').type('Family People');\n\t\tcy.get('.podcasting__modal-artist-field input').type('Alex Neason');\n\t\tcy.get('.podcasting__modal-category-field select').select(\n\t\t\t'society-culture:documentary'\n\t\t);\n\t\tcy.get('.podcasting__modal-summary-field textarea').type(\n\t\t\t'At the 1998 Olympics in Nagano, Japan, one athlete pulled a move that, as far as we know, no one else had ever attempted.'\n\t\t);\n\n\t\tcy.get('.podcasting__select-image-btn').click();\n\t\tcy.get('.supports-drag-drop:not([aria-hidden=\"true\"]) .attachment')\n\t\t\t.first()\n\t\t\t.click();\n\t\tcy.get(\n\t\t\t'.supports-drag-drop:not([aria-hidden=\"true\"]) .media-button-select'\n\t\t).click();\n\t\tcy.get('.podcasting__select-image-btn').should(\n\t\t\t'have.text',\n\t\t\t'Replace Image'\n\t\t);\n\t\tcy.get('.podcasting__remove-image-btn').should('exist');\n\t\tcy.get('.podcasting-cover-preview').should('exist');\n\t\tcy.get('.podcasting__create-podcast-btn').should(\n\t\t\t'not.have.attr',\n\t\t\t'disabled'\n\t\t);\n\n\t\tcy.get('.podcasting__remove-image-btn').click();\n\t\tcy.get('.podcasting__select-image-btn').should(\n\t\t\t'have.text',\n\t\t\t'Select Image'\n\t\t);\n\t\tcy.get('.podcasting__create-podcast-btn').should(\n\t\t\t'have.attr',\n\t\t\t'disabled'\n\t\t);\n\n\t\tcy.get('.podcasting__select-image-btn').click();\n\t\tcy.get('.supports-drag-drop:not([aria-hidden=\"true\"]) .attachment')\n\t\t\t.first()\n\t\t\t.click();\n\t\tcy.get(\n\t\t\t'.supports-drag-drop:not([aria-hidden=\"true\"]) .media-button-select'\n\t\t).click();\n\n\t\tcy.get('.podcasting__create-podcast-btn').click();\n\t\tcy.get('.components-modal__header-heading').should(\n\t\t\t'have.text',\n\t\t\t'Podcast created!'\n\t\t);\n\n\t\tcy.get('button').contains('Add another Podcast').click();\n\t\tcy.get('.components-modal__content').should('exist');\n\t\tcy.get('.podcasting__podcast-list-item label').should(\n\t\t\t'contain',\n\t\t\t'Family People'\n\t\t);\n\t});\n\n\tit('Verify created podcast', () => {\n\t\tcy.visit(\n\t\t\t'/wp-admin/edit-tags.php?taxonomy=podcasting_podcasts&podcasts=true'\n\t\t);\n\t\tcy.get('a').contains('Family People').click();\n\n\t\tcy.get('#name').should('have.value', 'Family People');\n\t\tcy.get('#podcasting_talent_name').should('have.value', 'Alex Neason');\n\t\tcy.get('#podcasting_summary').should(\n\t\t\t'have.value',\n\t\t\t'At the 1998 Olympics in Nagano, Japan, one athlete pulled a move that, as far as we know, no one else had ever attempted.'\n\t\t);\n\t\tcy.get('.podasting-existing-image img')\n\t\t\t.should('have.attr', 'src')\n\t\t\t.then((src) => {\n\t\t\t\texpect(src).to.contain('example');\n\t\t\t});\n\n\t\tcy.get('#podcasting_category_1').should(\n\t\t\t'have.value',\n\t\t\t'society-culture:documentary'\n\t\t);\n\t});\n});\n"
  },
  {
    "path": "tests/cypress/integration/taxonomy.test.js",
    "content": "const {\n\trandomName,\n\tpopulatePodcast,\n\tdeleteAllTerms,\n} = require('../support/functions');\n\ndescribe('Admin can create and update podcast taxonomy', () => {\n\tbeforeEach(() => {\n\t\tcy.login();\n\t});\n\n\tbefore(() => {\n\t\tdeleteAllTerms();\n\t});\n\n\tit('Can see taxonomy menu item', () => {\n\t\tcy.visit('/wp-admin');\n\t\tcy.get(\n\t\t\t'#toplevel_page_edit-tags-taxonomy-podcasting_podcasts-amp-podcasts-true .wp-menu-name'\n\t\t).should('have.text', 'Podcasts');\n\t});\n\n\tit('Can visit taxonomy page', () => {\n\t\tcy.visit(\n\t\t\t'/wp-admin/edit-tags.php?taxonomy=podcasting_podcasts&podcasts=true'\n\t\t);\n\t\tcy.get('h1').should('have.text', 'Podcasts');\n\t\tcy.get('.form-wrap h2').should('have.text', 'Add New Podcast');\n\t\tcy.get('.notice').should(\n\t\t\t'contain.text',\n\t\t\t'Once at least one podcast exists'\n\t\t);\n\t});\n\n\tit('Can add a new taxonomy', () => {\n\t\tcy.uploadMedia('tests/cypress/fixtures/example.jpg');\n\t\tcy.createTerm('Remote work', 'podcasting_podcasts', {\n\t\t\tbeforeSave: () => {\n\t\t\t\tpopulatePodcast({\n\t\t\t\t\tauthor: 'Person Doe',\n\t\t\t\t\tsummary: 'Lorem ipsum dolor',\n\t\t\t\t\tcategory: 'arts:food',\n\t\t\t\t});\n\t\t\t},\n\t\t});\n\t\tcy.get('.row-title').first().should('have.text', 'Remote work');\n\t});\n\n\tit('Can edit taxonomy', () => {\n\t\tcy.visit(\n\t\t\t'/wp-admin/edit-tags.php?taxonomy=podcasting_podcasts&podcasts=true'\n\t\t);\n\t\tcy.get('.row-title').contains('Remote work').click();\n\t\tcy.url().should('contain', 'http://localhost:8889/wp-admin/term.php');\n\t\tcy.get('#name').click().clear();\n\t\tcy.get('#name').type('Distributed');\n\t\tcy.get('#slug').click().clear();\n\t\tcy.get('input[type=\"submit\"]').click();\n\t\tcy.url().should('contain', 'http://localhost:8889/wp-admin/term.php');\n\t\tcy.visit(\n\t\t\t'/wp-admin/edit-tags.php?taxonomy=podcasting_podcasts&podcasts=true'\n\t\t);\n\n\t\tcy.get('.row-title').first().should('have.text', 'Distributed');\n\t});\n\n\tit('Can delete taxonomy', () => {\n\t\tcy.visit(\n\t\t\t'/wp-admin/edit-tags.php?taxonomy=podcasting_podcasts&podcasts=true'\n\t\t);\n\t\tcy.get('.row-title').contains('Distributed').click();\n\t\tcy.url().should('contain', 'http://localhost:8889/wp-admin/term.php');\n\t\tcy.on('window:confirm', () => true);\n\t\tcy.get('.delete').click();\n\t\tcy.url().should(\n\t\t\t'contain',\n\t\t\t'http://localhost:8889/wp-admin/edit-tags.php'\n\t\t);\n\t\tcy.visit(\n\t\t\t'/wp-admin/edit-tags.php?taxonomy=podcasting_podcasts&podcasts=true'\n\t\t);\n\t\tcy.get('.row-title').contains('Distributed').should('not.exist');\n\t});\n\n\tconst tests = {\n\t\t0: 'n/a',\n\t\tserial: 'Serial',\n\t\tepisodic: 'Episodic',\n\t};\n\n\tfor (const [typeOfShowKey, typeOfShowName] of Object.entries(tests)) {\n\t\tit(`Can add taxonomy with ${typeOfShowName} type of show`, () => {\n\t\t\tconst podcastName = 'Podcast ' + randomName();\n\t\t\tcy.uploadMedia('tests/cypress/fixtures/example.jpg');\n\t\t\tcy.createTerm(podcastName, 'podcasting_podcasts', {\n\t\t\t\tbeforeSave: () => {\n\t\t\t\t\tpopulatePodcast({\n\t\t\t\t\t\ttypeOfShowName,\n\t\t\t\t\t\tauthor: 'Person Doe',\n\t\t\t\t\t\tsummary: 'Lorem ipsum dolor',\n\t\t\t\t\t\tcategory: 'arts:food',\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t}).then((term) => {\n\t\t\t\tcy.visit(\n\t\t\t\t\t`/wp-admin/term.php?taxonomy=podcasting_podcasts&tag_ID=${term.term_id}`\n\t\t\t\t);\n\t\t\t\tcy.get('#podcasting_type_of_show').should(\n\t\t\t\t\t'have.value',\n\t\t\t\t\ttypeOfShowKey\n\t\t\t\t);\n\t\t\t});\n\t\t});\n\t}\n});\n"
  },
  {
    "path": "tests/cypress/plugins/index.js",
    "content": "/// <reference types=\"cypress\" />\n// ***********************************************************\n// This example plugins/index.js can be used to load plugins\n//\n// You can change the location of this file or turn off loading\n// the plugins file with the 'pluginsFile' configuration option.\n//\n// You can read more here:\n// https://on.cypress.io/plugins-guide\n// ***********************************************************\n\n// This function is called when a project is opened or re-opened (e.g. due to\n// the project's config changing)\n\nconst path = require('path');\n\n// Resolve the package directory\nconst wpEnvPackagePath = require.resolve('@wordpress/env/package.json');\nconst wpEnvLibPath = path.join(path.dirname(wpEnvPackagePath), 'lib');\n\n// Directly require the files using their resolved paths\nconst { loadConfig } = require(path.join(wpEnvLibPath, 'config', 'index.js'));\nconst getCacheDirectory = require(path.join(\n\twpEnvLibPath,\n\t'config',\n\t'get-cache-directory.js'\n));\n\n/**\n * @type {Cypress.PluginConfig}\n */\n// eslint-disable-next-line no-unused-vars\nmodule.exports = async (on, config) => {\n\tconst cacheDirectory = await getCacheDirectory();\n\tconst wpEnvConfig = await loadConfig(cacheDirectory);\n\n\tif (wpEnvConfig) {\n\t\tconst port = wpEnvConfig.env.tests.port || null;\n\n\t\tif (port) {\n\t\t\tconfig.baseUrl = wpEnvConfig.env.tests.config.WP_SITEURL;\n\t\t}\n\t}\n\n\treturn config;\n};\n"
  },
  {
    "path": "tests/cypress/support/functions.js",
    "content": "export const randomName = () => Math.random().toString(16).substring(7);\n\nexport const getFirstImage = () => {\n\tcy.get('#menu-item-browse').click();\n\tcy.get('ul.attachments li:first-of-type').click();\n\tcy.get('.media-button-select').click();\n};\n\n/**\n * Populates a podcast taxonomy.\n * @param {Object} args The podcast data to populate.\n * @param {string} args.typeOfShowName The type of show.\n * @param {string} args.author The author of the podcast.\n * @param {string} args.summary The summary of the podcast.\n * @param {string} args.category The category of the podcast.\n * @param {boolean} args.onboarding Whether this is onboarding or not.\n */\nexport const populatePodcast = (args) => {\n\tfor (const [key, value] of Object.entries(args)) {\n\t\tswitch (key) {\n\t\t\tcase 'typeOfShowName':\n\t\t\t\tcy.get('#podcasting_type_of_show').select(value);\n\t\t\t\tbreak;\n\t\t\tcase 'author':\n\t\t\t\tif (args.onboarding) {\n\t\t\t\t\tcy.get('input[name=\"podcast-artist\"]').type(value);\n\t\t\t\t} else {\n\t\t\t\t\tcy.get('#podcasting_talent_name').type(value);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'summary':\n\t\t\t\tif (args.onboarding) {\n\t\t\t\t\tcy.get('textarea[name=\"podcast-description\"]').type(value);\n\t\t\t\t} else {\n\t\t\t\t\tcy.get('#podcasting_summary').type(value);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'category':\n\t\t\t\tif (args.onboarding) {\n\t\t\t\t\tcy.get('select[name=podcast-category]').select(value);\n\t\t\t\t} else {\n\t\t\t\t\tcy.get('#podcasting_category_1').select(value);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t}\n\t// Get first image from media library.\n\tif (args.onboarding) {\n\t\tcy.get('#simple-podcasting__upload-cover-image').click();\n\t} else {\n\t\tcy.get('#image-podcasting_image').click();\n\t}\n\tgetFirstImage();\n};\n\nexport const deleteAllTerms = () => {\n\tcy.get('body').then(($body) => {\n\t\tif ($body.find('#doaction').length > 0) {\n\t\t\tcy.get('#cb-select-all-1').click({ force: true });\n\t\t\tcy.get('#bulk-action-selector-top').select('delete');\n\t\t\tcy.get('#doaction').click();\n\t\t}\n\t});\n};\n"
  },
  {
    "path": "tests/cypress/support/index.js",
    "content": "// ***********************************************************\n// This example support/index.js is processed and\n// loaded automatically before your test files.\n//\n// This is a great place to put global configuration and\n// behavior that modifies Cypress.\n//\n// You can change the location of this file or turn off\n// automatically serving support files with the\n// 'supportFile' configuration option.\n//\n// You can read more here:\n// https://on.cypress.io/configuration\n// ***********************************************************\n\nimport '@10up/cypress-wp-utils';\nimport 'cypress-file-upload';\n"
  },
  {
    "path": "tests/cypress/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"allowJs\": true,\n    \"types\": [\"cypress\"]\n  },\n  \"include\": [\"**/*.*\"]\n}\n"
  },
  {
    "path": "tests/unit/bootstrap.php",
    "content": "<?php\n/**\n * The bootstrap file for PHPUnit tests for the Simple Podcasting plugin.\n * Starts up WP_Mock and requires the files needed for testing.\n */\n\ndefine( 'TEST_PLUGIN_DIR', dirname( dirname( __DIR__ ) ) . '/' );\n\n// First we need to load the composer autoloader so we can use WP Mock\nrequire_once TEST_PLUGIN_DIR . '/vendor/autoload.php';\n\n// Now call the bootstrap method of WP Mock.\nWP_Mock::bootstrap();\n\ndefine( 'PODCASTING_VERSION', '1.2.1' );\ndefine( 'PODCASTING_PATH', TEST_PLUGIN_DIR );\ndefine( 'PODCASTING_URL', 'https://example.com/wp-content/plugins/simple-podcasting/' );\ndefine( 'PODCASTING_TAXONOMY_NAME', 'podcasting_podcasts' );\ndefine( 'PODCASTING_ITEMS_PER_PAGE', 250 );\n\nrequire TEST_PLUGIN_DIR . 'includes/blocks.php';\nrequire TEST_PLUGIN_DIR . 'includes/customize-feed.php';\nrequire TEST_PLUGIN_DIR . 'includes/datatypes.php';\nrequire TEST_PLUGIN_DIR . 'includes/helpers.php';\nrequire TEST_PLUGIN_DIR . 'includes/post-meta-box.php';\nrequire TEST_PLUGIN_DIR . 'includes/rest-external-url.php';\nrequire TEST_PLUGIN_DIR . 'includes/transcripts.php';\n"
  },
  {
    "path": "tests/unit/test-blocks.php",
    "content": "<?php\n\nuse PHPUnit\\Framework\\TestCase;\n\n/**\n * The AddressTests class tests the functions associated with an address associated with an invoice.\n */\nclass BlockTests extends TestCase {\n\t/**\n\t * Set up our mocked WP functions. Rather than setting up a database we can mock the returns of core WordPress functions.\n\t *\n\t * @return void\n\t */\n\tpublic function setUp() : void {\n\t\t\\WP_Mock::setUp();\n\t}\n\t/**\n\t * Tear down WP Mock.\n\t *\n\t * @return void\n\t */\n\tpublic function tearDown() : void {\n\t\t\\WP_Mock::tearDown();\n\t}\n\n\tpublic function test_init() {\n\t\t$block_asset = require PODCASTING_PATH . 'dist/blocks.asset.php';\n\n\t\t\\WP_Mock::userFunction(\n\t\t\t'wp_register_script',\n\t\t\tarray(\n\t\t\t\t'times' => 1,\n\t\t\t\t'args'  => array(\n\t\t\t\t\t'podcasting-block-editor',\n\t\t\t\t\tPODCASTING_URL . 'dist/blocks.js',\n\t\t\t\t\t$block_asset['dependencies'],\n\t\t\t\t\t$block_asset['version'],\n\t\t\t\t\ttrue,\n\t\t\t\t),\n\t\t\t)\n\t\t);\n\n\t\t\\WP_Mock::userFunction(\n\t\t\t'wp_register_style',\n\t\t\tarray(\n\t\t\t\t'times' => 1,\n\t\t\t\t'args'  => array(\n\t\t\t\t\t'podcasting-block-editor',\n\t\t\t\t\tPODCASTING_URL . 'dist/blocks.css',\n\t\t\t\t\tarray(),\n\t\t\t\t\t$block_asset['version'],\n\t\t\t\t\t'all',\n\t\t\t\t),\n\t\t\t)\n\t\t);\n\n\t\t\\WP_Mock::userFunction(\n\t\t\t'register_block_type',\n\t\t\tarray(\n\t\t\t\t'times' => 1,\n\t\t\t\t'args'  => array(\n\t\t\t\t\t'podcasting/podcast',\n\t\t\t\t\tarray(\n\t\t\t\t\t\t'editor_script' => 'podcasting-block-editor',\n\t\t\t\t\t\t'editor_style'  => 'podcasting-block-editor',\n\t\t\t\t\t\t'render_callback' => 'tenup_podcasting\\block\\render',\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t)\n\t\t);\n\n\t\t$result = tenup_podcasting\\block\\init();\n\n\t\t$this->assertNull( $result );\n\t}\n\n\tpublic function test_register_js_strings() {\n\t\t\\WP_Mock::userFunction( '__', array( 'times' => 7 ) );\n\n\t\t$result = tenup_podcasting\\block\\register_js_strings();\n\t\t$this->assertNull( $result );\n\t}\n\n\t/**\n\t * @dataProvider data_provider_for_test_block_editor_meta_cleanup\n\t */\n\tpublic function test_block_editor_meta_cleanup( $creating, $has_block, $metadata_exists, $expected ) {\n\t\t$post     = new stdClass();\n\t\t$post->ID = 42;\n\n\t\t\\WP_Mock::userFunction( 'has_block' )\n\t\t\t->with( 'podcasting/podcast', $post->ID )\n\t\t\t->andReturn( $has_block );\n\n\t\t\\WP_Mock::userFunction( 'metadata_exists' )\n\t\t\t->with( 'post', $post->ID, 'podcast_url' )\n\t\t\t->andReturn( $metadata_exists );\n\n\t\tif ( is_array( $expected ) ) {\n\t\t\tforeach ( $expected as $meta_key ) {\n\t\t\t\t\\WP_Mock::userFunction( 'delete_post_meta' )->once()->with( 42, $meta_key );\n\t\t\t}\n\t\t}\n\n\t\t$this->assertNull( tenup_podcasting\\block\\block_editor_meta_cleanup( $post, null, $creating ) );\n\t}\n\n\tpublic function data_provider_for_test_block_editor_meta_cleanup() {\n\t\treturn array(\n\t\t\t'Don\\'t delete meta if creating post' => array(\n\t\t\t\t'creating'        => true,\n\t\t\t\t'has_block'       => false,\n\t\t\t\t'metadata_exists' => false,\n\t\t\t\t'expected'        => null,\n\t\t\t),\n\t\t\t'Don\\'t delete meta if has block'     => array(\n\t\t\t\t'creating'        => false,\n\t\t\t\t'has_block'       => true,\n\t\t\t\t'metadata_exists' => false,\n\t\t\t\t'expected'        => null,\n\t\t\t),\n\t\t\t'Don\\'t delete meta if no metadata'   => array(\n\t\t\t\t'creating'        => false,\n\t\t\t\t'has_block'       => false,\n\t\t\t\t'metadata_exists' => false,\n\t\t\t\t'expected'        => null,\n\t\t\t),\n\t\t\t'Delete 10 metas'                      => array(\n\t\t\t\t'creating'        => false,\n\t\t\t\t'has_block'       => false,\n\t\t\t\t'metadata_exists' => true,\n\t\t\t\t'expected'        => array( 'podcast_url', 'podcast_filesize', 'podcast_duration', 'podcast_mime', 'podcast_captioned', 'podcast_explicit', 'enclosure', 'podcast_season_number', 'podcast_episode_number', 'podcast_episode_type' ),\n\t\t\t),\n\t\t);\n\t}\n}\n"
  },
  {
    "path": "tests/unit/test-customize-feed.php",
    "content": "<?php\n\nuse PHPUnit\\Framework\\TestCase;\n\n/**\n * The AddressTests class tests the functions associated with an address associated with an invoice.\n */\nclass CustomizeFeedTests extends TestCase {\n\t/**\n\t * Set up our mocked WP functions. Rather than setting up a database we can mock the returns of core WordPress functions.\n\t *\n\t * @return void\n\t */\n\tpublic function setUp() : void {\n\t\t\\WP_Mock::setUp();\n\t}\n\t/**\n\t * Tear down WP Mock.\n\t *\n\t * @return void\n\t */\n\tpublic function tearDown() : void {\n\t\t\\WP_Mock::tearDown();\n\t}\n\n\tpublic function test_get_the_term() {\n\t\t$empty_object          = new stdClass();\n\t\t$empty_object->term_id = false;\n\n\t\t$queried_object          = new stdClass();\n\t\t$queried_object->term_id = 42;\n\n\t\t\\WP_Mock::userFunction(\n\t\t\t'get_queried_object',\n\t\t\tarray(\n\t\t\t\t'times'           => 3,\n\t\t\t\t'return_in_order' => array( null, $empty_object, $queried_object ),\n\t\t\t)\n\t\t);\n\n\t\t$this->assertFalse(\n\t\t\ttenup_podcasting\\get_the_term(),\n\t\t\t'tenup_podcasting\\get_the_term() should return false if no queried object.'\n\t\t);\n\n\t\t$this->assertFalse(\n\t\t\ttenup_podcasting\\get_the_term(),\n\t\t\t'tenup_podcasting\\get_the_term() should return false if queried object is not a term.'\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\ttenup_podcasting\\get_the_term(),\n\t\t\t$queried_object,\n\t\t\t'tenup_podcasting\\get_the_term() should return queried object.'\n\t\t);\n\t}\n\n\tpublic function test_empty_rss_excerpt() {\n\t\t\\WP_Mock::userFunction(\n\t\t\t'get_the_excerpt',\n\t\t\tarray(\n\t\t\t\t'times'           => 2,\n\t\t\t\t'return_in_order' => array( '', 'excerpt' ),\n\t\t\t)\n\t\t);\n\n\t\t$this->assertEmpty(\n\t\t\ttenup_podcasting\\empty_rss_excerpt( 'Non-empty' ),\n\t\t\t'tenup_podcasting\\empty_rss_excerpt() should return empty string if the excerpt is empty.'\n\t\t);\n\n\t\t$this->assertEquals(\n\t\t\ttenup_podcasting\\empty_rss_excerpt( 'Non-empty' ),\n\t\t\t'Non-empty',\n\t\t\t'tenup_podcasting\\empty_rss_excerpt() should return original content if the excerpt is not empty.'\n\t\t);\n\t}\n\n\tpublic function test_pre_get_posts_no_feed() {\n\t\t$query_mock = \\Mockery::mock( '\\WP_Query' );\n\t\t$query_mock->shouldReceive( 'is_feed' )->andReturn( false );\n\t\t$query_mock->shouldNotReceive( 'set' );\n\n\t\t$this->assertNull(\n\t\t\ttenup_podcasting\\pre_get_posts( $query_mock ),\n\t\t\t'tenup_podcasting\\pre_get_posts() should not make updates to WP_Query if not in a feed loop.'\n\t\t);\n\t}\n\n\tpublic function test_pre_get_posts_feed() {\n\t\t$query_mock = \\Mockery::mock( '\\WP_Query' );\n\n\t\t$query_mock->shouldReceive( 'is_feed' )->andReturn( true );\n\n\t\t\\WP_Mock::onFilter( 'simple_podcasting_episodes_per_page' )\n\t\t\t->with( PODCASTING_ITEMS_PER_PAGE )\n\t\t\t->reply( 100 );\n\n\t\t$query_mock->shouldReceive( 'set' )->with( 'posts_per_rss', 100 );\n\n\t\t$this->assertNull(\n\t\t\ttenup_podcasting\\pre_get_posts( $query_mock ),\n\t\t\t'tenup_podcasting\\pre_get_posts() should update WP_Query->posts_per_rss with filtered value.'\n\t\t);\n\t}\n\n\t/**\n\t * @test - The most basic of PHPUnit tests to make sure it's working.\n\t * @dataProvider data_provider_for_test_feed_item\n\t */\n\tpublic function test_feed_item( $talent_option, $post_data, $term, $post_author, $post_meta, $term_meta, $filters, $expected ) {\n\t\tglobal $post;\n\t\t$post = $post_data;\n\n\t\t\\WP_Mock::userFunction(\n\t\t\t'get_queried_object',\n\t\t\tarray(\n\t\t\t\t'times'  => 1,\n\t\t\t\t'return' => $term,\n\t\t\t)\n\t\t);\n\n\t\t\\WP_Mock::userFunction( 'get_option' )\n\t\t\t->with( 'podcasting_talent_name' )\n\t\t\t->andReturn( $talent_option );\n\n\t\t\\WP_Mock::userFunction( 'get_post_meta' )\n\t\t\t->andReturnUsing(\n\t\t\t\tfunction( $id, $key, $_ ) use ( $post_meta ) {\n\t\t\t\t\treturn isset( $post_meta[ $key ] ) ? $post_meta[ $key ] : false;\n\t\t\t\t}\n\t\t\t);\n\n\t\t\\WP_Mock::userFunction( 'get_term_meta' )\n\t\t\t->andReturnUsing(\n\t\t\t\tfunction( $id, $key, $_ ) use ( $term_meta ) {\n\t\t\t\t\treturn isset( $term_meta[ $key ] ) ? $term_meta[ $key ] : false;\n\t\t\t\t}\n\t\t\t);\n\n\t\t\\WP_Mock::userFunction( 'get_the_author' )\n\t\t\t->andReturn( $post_author );\n\n\t\t\\WP_Mock::userFunction( 'has_post_thumbnail' )\n\t\t\t->andReturn( isset( $post->thumbnail ) );\n\n\t\t\\WP_Mock::userFunction( 'wp_get_attachment_image_src' )\n\t\t\t->andReturn( isset( $post->thumbnail ) ? $post->thumbnail : false );\n\n\t\t\\WP_Mock::passthruFunction( 'get_post_thumbnail_id' );\n\t\t\\WP_Mock::passthruFunction( 'wp_strip_all_tags' );\n\t\t\\WP_Mock::passthruFunction( 'wp_kses' );\n\n\t\t\\WP_Mock::userFunction( 'has_excerpt' )\n\t\t\t->andReturn( isset( $post->excerpt ) );\n\t\t\\WP_Mock::userFunction( 'get_the_excerpt' )\n\t\t\t->andReturn( isset( $post->excerpt ) ? $post->excerpt : '' );\n\n\t\t\\WP_Mock::userFunction( 'wp_trim_words' )\n\t\t\t->andReturnUsing(\n\t\t\t\tfunction ( $text, $num_words, $more ) {\n\t\t\t\t\treturn strlen( $text ) > 30 ? 'Short Excerpt' : $text;\n\t\t\t\t}\n\t\t\t);\n\n\t\tob_start();\n\t\t$result = tenup_podcasting\\feed_item();\n\t\t$output = ob_get_clean();\n\n\t\tif ( is_bool( $expected ) ) {\n\t\t\t$this->assertSame( $expected, $result );\n\t\t} elseif ( is_array( $expected ) ) {\n\t\t\tforeach ( $expected as $message => $regex_string ) {\n\t\t\t\t$this->assertMatchesRegularExpression( $regex_string, $output, $message );\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic function test_rss_title_can_be_filtered() {\n\t\t$queried_object = (object) array(\n\t\t\t'term_id' => 42,\n\t\t\t'name' => 'Original Podcast Name'\n\t\t);\n\t\t\\WP_Mock::userFunction( 'get_queried_object' )\n\t\t\t->andReturn( $queried_object );\n\n\t\t\\WP_Mock::userFunction( 'get_bloginfo' )\n\t\t\t->with( 'name' )\n\t\t\t->andReturn( 'Blogname' );\n\n\n\t\t\\WP_Mock::onFilter( 'simple_podcasting_feed_title' )\n\t\t\t->with( 'Podcast Title', $queried_object )\n\t\t\t->reply( 'Filtered Podcast Title' );\n\n\t\t$this->assertEquals(\n\t\t\t'Filtered Podcast Title',\n\t\t\ttenup_podcasting\\bloginfo_rss_name( 'Podcast Title' ),\n\t\t\t'tenup_podcasting\\bloginfo_rss_name() should return the filtered value.'\n\t\t);\n\n\t}\n\n\tpublic function data_provider_for_test_feed_item() {\n\t\treturn array(\n\t\t\t'Term not found'              => array(\n\t\t\t\t'talent_option' => '',\n\t\t\t\t'post_data'     => (object) array(),\n\t\t\t\t'term'          => false,\n\t\t\t\t'post_author'   => '',\n\t\t\t\t'post_meta'     => array(),\n\t\t\t\t'term_meta'     => array(),\n\t\t\t\t'filters'       => false,\n\t\t\t\t'expected'      => false,\n\t\t\t),\n\t\t\t'Mixed Test 1'                => array(\n\t\t\t\t'talent_option' => 'Talent from settings',\n\t\t\t\t'post_data'     => (object) array(\n\t\t\t\t\t'ID'        => 42,\n\t\t\t\t\t'excerpt'   => 'Post Excerpt',\n\t\t\t\t\t'thumbnail' => 'http://example.com/image.jpg',\n\t\t\t\t),\n\t\t\t\t'term'          => (object) array( 'term_id' => 2 ),\n\t\t\t\t'post_author'   => 'Post Author',\n\t\t\t\t'post_meta'     => array(\n\t\t\t\t\t'podcast_explicit'       => '',\n\t\t\t\t\t'podcast_captioned'      => '',\n\t\t\t\t\t'podcast_duration'       => '',\n\t\t\t\t\t'podcast_season_number'  => '',\n\t\t\t\t\t'podcast_episode_number' => '',\n\t\t\t\t\t'podcast_episode_type'   => '',\n\t\t\t\t),\n\t\t\t\t'term_meta'     => array(\n\t\t\t\t\t'podcasting_explicit' => '',\n\t\t\t\t\t'podcasting_summary'  => '',\n\t\t\t\t),\n\t\t\t\t'filters'       => false,\n\t\t\t\t'expected'      => array(\n\t\t\t\t\t'Talent from settings'          => '/<itunes:author>Talent from settings<\\/itunes:author>/',\n\t\t\t\t\t'No explicit if all defaults'   => '/<itunes:explicit>no<\\/itunes:explicit>/',\n\t\t\t\t\t'Doesnt contain closed caption' => '/^((?!isClosedCaptioned).)*$/s',\n\t\t\t\t\t'Episode thumbnail'             => \"/<itunes:image href='http:\\/\\/example\\.com\\/image\\.jpg' \\/>/\",\n\t\t\t\t\t'Post excerpt as subtitle'      => '/<itunes:subtitle>Post Excerpt<\\/itunes:subtitle>/',\n\t\t\t\t\t'Duration is empty'             => '/^((?!<itunes:duration>).)*$/s',\n\t\t\t\t\t'Doesnt contain season'         => '/^((?!season).)*$/s',\n\t\t\t\t\t'Doesnt contain episode'        => '/^((?!episode).)*$/s',\n\t\t\t\t\t'Doesnt contain episode type'   => '/^((?!episodeType).)*$/s',\n\t\t\t\t),\n\t\t\t),\n\t\t\t'Mixed Test 2'                => array(\n\t\t\t\t'talent_option' => '',\n\t\t\t\t'post_data'     => (object) array(\n\t\t\t\t\t'ID'      => 42,\n\t\t\t\t\t'excerpt' => 'Very Long Post Excerpt Very Long Post Excerpt Very Long Post Excerpt Very Long Post Excerpt',\n\t\t\t\t),\n\t\t\t\t'term'          => (object) array( 'term_id' => 2 ),\n\t\t\t\t'post_author'   => 'Post Author',\n\t\t\t\t'post_meta'     => array(\n\t\t\t\t\t'podcast_explicit'       => 'yes',\n\t\t\t\t\t'podcast_captioned'      => '1',\n\t\t\t\t\t'podcast_duration'       => '1:23',\n\t\t\t\t\t'podcast_season_number'  => '2',\n\t\t\t\t\t'podcast_episode_number' => '4',\n\t\t\t\t\t'podcast_episode_type'   => 'full',\n\t\t\t\t),\n\t\t\t\t'term_meta'     => array(\n\t\t\t\t\t'podcasting_explicit' => '',\n\t\t\t\t\t'podcasting_summary'  => '',\n\t\t\t\t),\n\t\t\t\t'filters'       => false,\n\t\t\t\t'expected'      => array(\n\t\t\t\t\t'Post Author'              => '/<itunes:author>Post Author<\\/itunes:author>/',\n\t\t\t\t\t'Explicit from episode'    => '/<itunes:explicit>yes<\\/itunes:explicit>/',\n\t\t\t\t\t'Contain Closed Captioned' => '/<itunes:isClosedCaptioned>Yes<\\/itunes:isClosedCaptioned>/',\n\t\t\t\t\t'Doesnt contain thumbnail' => '/^((?!<itunes:image).)*$/s',\n\t\t\t\t\t'Short Subtitle'           => '/<itunes:subtitle>Short Excerpt<\\/itunes:subtitle>/',\n\t\t\t\t\t'Duration'                 => '/<itunes:duration>1:23<\\/itunes:duration>/',\n\t\t\t\t\t'Season'                   => '/<itunes:season>2<\\/itunes:season>/',\n\t\t\t\t\t'Episode'                  => '/<itunes:episode>4<\\/itunes:episode>/',\n\t\t\t\t\t'Episode type'             => '/<itunes:episodeType>full<\\/itunes:episodeType>/',\n\t\t\t\t),\n\t\t\t),\n\t\t\t'Mixed Test 3'                => array(\n\t\t\t\t'talent_option' => '',\n\t\t\t\t'post_data'     => (object) array(\n\t\t\t\t\t'ID' => 42,\n\t\t\t\t),\n\t\t\t\t'term'          => (object) array( 'term_id' => 2 ),\n\t\t\t\t'post_author'   => 'Post Author',\n\t\t\t\t'post_meta'     => array(\n\t\t\t\t\t'podcast_explicit'  => '',\n\t\t\t\t\t'podcast_captioned' => '1',\n\t\t\t\t\t'podcast_duration'  => '1:23',\n\t\t\t\t),\n\t\t\t\t'term_meta'     => array(\n\t\t\t\t\t'podcasting_explicit' => 'clean',\n\t\t\t\t\t'podcasting_summary'  => 'Summary from plugin settings',\n\t\t\t\t),\n\t\t\t\t'filters'       => false,\n\t\t\t\t'expected'      => array(\n\t\t\t\t\t'Post Author'                   => '/<itunes:author>Post Author<\\/itunes:author>/',\n\t\t\t\t\t'Explicit from plugin settings' => '/<itunes:explicit>clean<\\/itunes:explicit>/',\n\t\t\t\t\t'Contain Closed Captioned'      => '/<itunes:isClosedCaptioned>Yes<\\/itunes:isClosedCaptioned>/',\n\t\t\t\t\t'Doesnt contain thumbnail'      => '/^((?!<itunes:image).)*$/s',\n\t\t\t\t\t'Subtitle from plugin settings' => '/<itunes:subtitle>Summary from plugin settings<\\/itunes:subtitle>/',\n\t\t\t\t\t'Duration'                      => '/<itunes:duration>1:23<\\/itunes:duration>/',\n\t\t\t\t),\n\t\t\t),\n\t\t\t'Enclosure from podcast meta' => array(\n\t\t\t\t'talent_option' => '',\n\t\t\t\t'post_data'     => (object) array(\n\t\t\t\t\t'ID' => 42,\n\t\t\t\t),\n\t\t\t\t'term'          => (object) array( 'term_id' => 2 ),\n\t\t\t\t'post_author'   => 'Post Author',\n\t\t\t\t'post_meta'     => array(\n\t\t\t\t\t'enclosure'        => false,\n\t\t\t\t\t'podcast_url'      => 'http://example.com/media.mp3',\n\t\t\t\t\t'podcast_filesize' => '42000',\n\t\t\t\t\t'podcast_mime'     => 'audio/mpeg',\n\t\t\t\t),\n\t\t\t\t'term_meta'     => array(),\n\t\t\t\t'filters'       => false,\n\t\t\t\t'expected'      => array(\n\t\t\t\t\t'Enclosure' => '/<enclosure url=\\'http:\\/\\/example.com\\/media.mp3\\' length=\\'42000\\' type=\\'audio\\/mpeg\\' \\/>/',\n\t\t\t\t),\n\t\t\t),\n\t\t);\n\t}\n}\n"
  },
  {
    "path": "tests/unit/test-datatypes.php",
    "content": "<?php\n\nuse PHPUnit\\Framework\\TestCase;\n\n/**\n * The AddressTests class tests the functions associated with an address associated with an invoice.\n */\nclass DatatypesTests extends TestCase {\n\t/**\n\t * Set up our mocked WP functions. Rather than setting up a database we can mock the returns of core WordPress functions.\n\t *\n\t * @return void\n\t */\n\tpublic function setUp() : void {\n\t\t\\WP_Mock::setUp();\n\t}\n\t/**\n\t * Tear down WP Mock.\n\t *\n\t * @return void\n\t */\n\tpublic function tearDown() : void {\n\t\t\\WP_Mock::tearDown();\n\t}\n\n\t/**\n\t * @dataProvider data_provider_for_test_filter_parent_file\n\t */\n\tpublic function test_filter_parent_file( $screen, $original_file, $expected_file )\n\t{\n\t\t\\WP_Mock::userFunction( 'get_current_screen', array(\n\t\t\t'return' => $screen\n\t\t));\n\n\t\t$this->assertSame( $expected_file, tenup_podcasting\\filter_parent_file( $original_file ) );\n\t}\n\n\tpublic function data_provider_for_test_filter_parent_file()\n\t{\n\t\treturn array(\n\t\t\t'Edit tags screen for podcasting' => array(\n\t\t\t\t'screen' => (object) array(\n\t\t\t\t\t'base' => 'edit-tags',\n\t\t\t\t\t'taxonomy' => 'podcasting_podcasts',\n\t\t\t\t),\n\t\t\t\t'original_file' => 'original.php',\n\t\t\t\t'expected_file' => 'edit-tags.php?taxonomy=podcasting_podcasts&amp;podcasts=true',\n\t\t\t),\n\t\t\t'Term screen for podcasting' => array(\n\t\t\t\t'screen' => (object) array(\n\t\t\t\t\t'base' => 'term',\n\t\t\t\t\t'taxonomy' => 'podcasting_podcasts',\n\t\t\t\t),\n\t\t\t\t'original_file' => 'original.php',\n\t\t\t\t'expected_file' => 'edit-tags.php?taxonomy=podcasting_podcasts&amp;podcasts=true',\n\t\t\t),\n\t\t\t'Other screen for podcasting' => array(\n\t\t\t\t'screen' => (object) array(\n\t\t\t\t\t'base' => 'other',\n\t\t\t\t\t'taxonomy' => 'podcasting_podcasts',\n\t\t\t\t),\n\t\t\t\t'original_file' => 'original.php',\n\t\t\t\t'expected_file' => 'original.php',\n\t\t\t),\n\t\t\t'Edit tags screen for other taxonomy' => array(\n\t\t\t\t'screen' => (object) array(\n\t\t\t\t\t'base' => 'edit-tags',\n\t\t\t\t\t'taxonomy' => 'other',\n\t\t\t\t),\n\t\t\t\t'original_file' => 'original.php',\n\t\t\t\t'expected_file' => 'original.php',\n\t\t\t),\n\t\t\t'Other screen for other taxonomy' => array(\n\t\t\t\t'screen' => (object) array(\n\t\t\t\t\t'base' => 'other',\n\t\t\t\t\t'taxonomy' => 'other',\n\t\t\t\t),\n\t\t\t\t'original_file' => 'original.php',\n\t\t\t\t'expected_file' => 'original.php',\n\t\t\t),\n\t\t);\n\t}\n}\n"
  },
  {
    "path": "tests/unit/test-helpers.php",
    "content": "<?php\n\nuse PHPUnit\\Framework\\TestCase;\n\n/**\n * The AddressTests class tests the functions associated with an address associated with an invoice.\n */\nclass HelpersTests extends TestCase {\n\t/**\n\t * Set up our mocked WP functions. Rather than setting up a database we can mock the returns of core WordPress functions.\n\t *\n\t * @return void\n\t */\n\tpublic function setUp() : void {\n\t\t\\WP_Mock::setUp();\n\t}\n\t/**\n\t * Tear down WP Mock.\n\t *\n\t * @return void\n\t */\n\tpublic function tearDown() : void {\n\t\t\\WP_Mock::tearDown();\n\t}\n\n\tpublic function test_delete_all_podcast_meta() {\n\t\t\\WP_Mock::userFunction(\n\t\t\t'metadata_exists',\n\t\t\tarray(\n\t\t\t\t'return_in_order' => array( false, true ),\n\t\t\t)\n\t\t);\n\n\t\t$this->assertNull( tenup_podcasting\\helpers\\delete_all_podcast_meta( 42 ) );\n\n\t\t$meta_keys = array( 'podcast_url', 'podcast_filesize', 'podcast_duration', 'podcast_mime', 'podcast_captioned', 'podcast_explicit', 'enclosure', 'podcast_season_number', 'podcast_episode_number', 'podcast_episode_type' );\n\t\tforeach ( $meta_keys as $meta_key ) {\n\t\t\t\\WP_Mock::userFunction( 'delete_post_meta' )->with( 42, $meta_key );\n\t\t}\n\n\t\t$this->assertNull( tenup_podcasting\\helpers\\delete_all_podcast_meta( 42 ) );\n\t}\n\n\t/**\n\t * @dataProvider data_provider_for_test_get_podcast_meta_from_url\n\t * @covers tenup_podcasting\\helpers\\get_podcast_meta_from_url\n\t */\n\tpublic function test_get_podcast_meta_from_url( $url, $redirect, $headers, $audio_metadata, $expected ) {\n\t\t\\WP_Mock::userFunction( 'is_admin' )->andReturn( true );\n\n\t\tif ( $redirect ) {\n\t\t\t\\WP_Mock::userFunction( 'wp_get_http_headers' )->with( $url )->andReturn( array( 'location' => $redirect ) );\n\t\t\t\\WP_Mock::userFunction( 'wp_get_http_headers' )->with( $redirect )->andReturn( $headers );\n\t\t} else {\n\t\t\t\\WP_Mock::userFunction( 'wp_get_http_headers' )->with( $url )->andReturn( $headers );\n\t\t}\n\n\t\t\\WP_Mock::userFunction( 'download_url' )->with( $url, 30 )->andReturn( 'downloaded_file_blob' );\n\t\t\\WP_Mock::userFunction( 'wp_read_audio_metadata' )->with( 'downloaded_file_blob' )->andReturn( $audio_metadata );\n\t\t\\WP_Mock::userFunction( 'wp_parse_url' )->andReturnUsing(\n\t\t\tfunction( $url ) {\n\t\t\t\treturn parse_url( $url );\n\t\t\t}\n\t\t);\n\t\t\\WP_Mock::userFunction( 'wp_get_mime_types' )->andReturn( array( 'mp3|mp4' => 'audio/mpeg' ) );\n\n\t\t$meta = tenup_podcasting\\helpers\\get_podcast_meta_from_url( $url );\n\t\t$this->assertSame( $expected, $meta );\n\t}\n\n\tpublic function data_provider_for_test_get_podcast_meta_from_url() {\n\t\treturn array(\n\t\t\t'Should follow redirect' => array(\n\t\t\t\t'url'            => 'https://simple-podcasting.test/correct.mp3',\n\t\t\t\t'redirect'       => 'https://simple-podcasting.test/redirected.mp3',\n\t\t\t\t'headers'        => array(\n\t\t\t\t\t'content-length' => 42000,\n\t\t\t\t\t'content-type'   => 'audio/mpeg',\n\t\t\t\t),\n\t\t\t\t'audio_metadata' => array( 'length_formatted' => '0:42' ),\n\t\t\t\t'expected' => array(\n\t\t\t\t\t'url' => 'https://simple-podcasting.test/correct.mp3',\n\t\t\t\t\t'mime' => 'audio/mpeg',\n\t\t\t\t\t'duration' => '0:42',\n\t\t\t\t\t'filesize' => 42000\n\t\t\t\t),\n\t\t\t),\n\t\t\t'Should extract metadata' => array(\n\t\t\t\t'url'            => 'https://simple-podcasting.test/correct.mp3',\n\t\t\t\t'redirect'       => false,\n\t\t\t\t'headers'        => array(\n\t\t\t\t\t'content-length' => 42000,\n\t\t\t\t\t'content-type'   => 'audio/mpeg',\n\t\t\t\t),\n\t\t\t\t'audio_metadata' => array( 'length_formatted' => '0:42' ),\n\t\t\t\t'expected' => array(\n\t\t\t\t\t'url' => 'https://simple-podcasting.test/correct.mp3',\n\t\t\t\t\t'mime' => 'audio/mpeg',\n\t\t\t\t\t'duration' => '0:42',\n\t\t\t\t\t'filesize' => 42000\n\t\t\t\t),\n\t\t\t),\n\t\t);\n\t}\n}\n"
  },
  {
    "path": "tests/unit/test-rest-external-url.php",
    "content": "<?php\n\nuse PHPUnit\\Framework\\TestCase;\n\n/**\n * The AddressTests class tests the functions associated with an address associated with an invoice.\n */\nclass RestExternalUrlTests extends TestCase {\n\t/**\n\t * Set up our mocked WP functions. Rather than setting up a database we can mock the returns of core WordPress functions.\n\t *\n\t * @return void\n\t */\n\tpublic function setUp() : void {\n\t\t\\WP_Mock::setUp();\n\t}\n\t/**\n\t * Tear down WP Mock.\n\t *\n\t * @return void\n\t */\n\tpublic function tearDown() : void {\n\t\t\\WP_Mock::tearDown();\n\t}\n\n\tpublic function test_handle_request_cached() {\n\t\t$url = 'https://example.com';\n\n\t\t$mock_request = \\Mockery::mock( '\\WP_REST_Request, ArrayAccess' );\n\n\t\t$mock_request->shouldReceive( 'offsetGet' )\n\t\t\t->with( 'url' )\n\t\t\t->andReturn( $url );\n\n\t\t$mock_request->shouldReceive( 'offsetExists' )\n\t\t\t->with( 'url' )\n\t\t\t->andReturn( true );\n\n\t\t$cache_key = 'spc_external_url_' . $url;\n\n\t\t\\WP_Mock::userFunction( 'get_transient' )\n\t\t\t->with( $cache_key )\n\t\t\t->andReturn( 'Podcast Meta' );\n\n\t\t\\WP_Mock::passthruFunction( 'rest_ensure_response' );\n\n\t\t$response = array(\n\t\t\t'success' => true,\n\t\t\t'data'    => 'Podcast Meta',\n\t\t);\n\n\t\t$this->assertSame(\n\t\t\t$response,\n\t\t\ttenup_podcasting\\endpoints\\externalurl\\handle_request( $mock_request ),\n\t\t\t'handle_request() should return cached result'\n\t\t);\n\t}\n}\n"
  },
  {
    "path": "tests/unit/test-transcript.php",
    "content": "<?php\n\nuse PHPUnit\\Framework\\TestCase;\n\n/**\n * The TranscriptTests class tests the functions associated with a transcript.\n */\nclass TranscriptTests extends TestCase {\n\t/**\n\t * Set up our mocked WP functions. Rather than setting up a database we can mock the returns of core WordPress functions.\n\t *\n\t * @return void\n\t */\n\tpublic function setUp() : void {\n\t\t\\WP_Mock::setUp();\n\t}\n\t/**\n\t * Tear down WP Mock.\n\t *\n\t * @return void\n\t */\n\tpublic function tearDown() : void {\n\t\t\\WP_Mock::tearDown();\n\t}\n\n\tpublic function test_get_transcript_from_post() {\n\t\t$post              = Mockery::mock( 'WP_Post' );\n\t\t$post->post_name   = 'latest-podcast';\n\t\t$terms             = [ new stdClass() ];\n\t\t$terms[0]->term_id = 100;\n\n\t\t\\WP_Mock::userFunction( 'get_post' )->andReturn( $post );\n\t\t\\WP_Mock::userFunction( 'get_the_terms' )->andReturn( $terms );\n\t\t\\WP_Mock::userFunction( 'get_term_link' )->andReturn( 'https://simple-podcasting.test/podcasts/test-podcast/' );\n\t\t\\WP_Mock::userFunction( 'trailingslashit' )->andReturnUsing( function( $url ) {\n\t\t\treturn rtrim( $url, '/\\\\' ) . '/';\n\t\t} );\n\n\t\t$this->assertSame(\n\t\t\ttenup_podcasting\\transcripts\\get_transcript_link_from_post( $post ),\n\t\t\t'https://simple-podcasting.test/podcasts/test-podcast/latest-podcast/transcript/'\n\t\t);\n\t}\n}\n"
  },
  {
    "path": "webpack.config.js",
    "content": "const defaultConfig = require('@wordpress/scripts/config/webpack.config');\nconst CopyPlugin = require('copy-webpack-plugin');\nconst path = require('path');\n\nmodule.exports = {\n\t...defaultConfig,\n\toutput: {\n\t\t...defaultConfig.output,\n\t\tpath: path.resolve(process.cwd(), 'dist'),\n\t},\n\tentry: {\n\t\tblocks: path.resolve(process.cwd(), 'assets/js', 'blocks.js'),\n\t\t'podcasting-edit-post': path.resolve(\n\t\t\tprocess.cwd(),\n\t\t\t'assets/js',\n\t\t\t'podcasting-edit-post.js'\n\t\t),\n\t\t'podcasting-edit-term': path.resolve(\n\t\t\tprocess.cwd(),\n\t\t\t'assets/js',\n\t\t\t'podcasting-edit-term.js'\n\t\t),\n\t\t'podcast-platforms-block': path.resolve(\n\t\t\tprocess.cwd(),\n\t\t\t'assets/js/blocks/podcast-platforms',\n\t\t\t'index.js'\n\t\t),\n\t\t'podcasting-onboarding': path.resolve(\n\t\t\tprocess.cwd(),\n\t\t\t'assets/js',\n\t\t\t'onboarding.js'\n\t\t),\n\t\t'podcasting-transcript': path.resolve(\n\t\t\tprocess.cwd(),\n\t\t\t'includes/blocks/podcast-transcript',\n\t\t\t'index.js'\n\t\t),\n\t\t'create-podcast-show': path.resolve(\n\t\t\tprocess.cwd(),\n\t\t\t'assets/js',\n\t\t\t'create-podcast-show.js'\n\t\t),\n\t\t'latest-episode': path.resolve(\n\t\t\tprocess.cwd(),\n\t\t\t'assets/js/blocks/latest-episode',\n\t\t\t'index.js'\n\t\t),\n\t\t'podcast': path.resolve(\n\t\t\tprocess.cwd(),\n\t\t\t'assets/js/blocks/podcast',\n\t\t\t'index.js'\n\t\t),\n\t},\n\tplugins: [\n\t\t...defaultConfig.plugins,\n\t\tnew CopyPlugin({\n\t\t\tpatterns: [\n\t\t\t\t{ from: 'assets/images/*', to: './' },\n\t\t\t\t{ from: 'assets/images/icons', to: './images/icons' },\n\t\t\t],\n\t\t}),\n\t],\n};\n"
  }
]