├── .wordpress-org
├── icon-128x128.png
├── icon-256x256.png
├── screenshot-1.png
├── screenshot-2.png
├── banner-772x250.png
├── banner-1544x500.png
├── wp-post-series-assets.sketch
└── icon.svg
├── .eslintignore
├── .prettierrc
├── .distignore
├── .gitignore
├── assets
├── js
│ ├── frontend.js
│ ├── hocs
│ │ └── with-post-series-terms.js
│ └── post-series-block
│ │ ├── index.js
│ │ ├── block.js
│ │ └── edit.js
└── css
│ └── post-series.scss
├── .eslintrc.js
├── .editorconfig
├── .github
└── workflows
│ └── deploy.yml
├── src
├── Registry
│ ├── FactoryType.php
│ ├── SharedType.php
│ ├── AbstractDependencyType.php
│ └── Container.php
├── Template.php
├── BlockTypes
│ └── PostSeries.php
├── Plugin.php
├── TaxonomyController.php
└── PostContent.php
├── wp-post-series.php
├── composer.json
├── templates
└── series-box.php
├── tests
└── checklist.md
├── phpcs.xml
├── languages
└── wp-post-series.pot
├── package.json
├── wp-post-series-init.php
├── webpack.config.js
├── readme.txt
└── composer.lock
/.wordpress-org/icon-128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mikejolley/wp-post-series/HEAD/.wordpress-org/icon-128x128.png
--------------------------------------------------------------------------------
/.wordpress-org/icon-256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mikejolley/wp-post-series/HEAD/.wordpress-org/icon-256x256.png
--------------------------------------------------------------------------------
/.wordpress-org/screenshot-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mikejolley/wp-post-series/HEAD/.wordpress-org/screenshot-1.png
--------------------------------------------------------------------------------
/.wordpress-org/screenshot-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mikejolley/wp-post-series/HEAD/.wordpress-org/screenshot-2.png
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | !.eslintrc.js
2 | build
3 | build-module
4 | coverage
5 | languages
6 | node_modules
7 | vendor
8 | legacy
9 |
--------------------------------------------------------------------------------
/.wordpress-org/banner-772x250.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mikejolley/wp-post-series/HEAD/.wordpress-org/banner-772x250.png
--------------------------------------------------------------------------------
/.wordpress-org/banner-1544x500.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mikejolley/wp-post-series/HEAD/.wordpress-org/banner-1544x500.png
--------------------------------------------------------------------------------
/.wordpress-org/wp-post-series-assets.sketch:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mikejolley/wp-post-series/HEAD/.wordpress-org/wp-post-series-assets.sketch
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | useTabs: true
2 | tabWidth: 4
3 | printWidth: 80
4 | singleQuote: true
5 | trailingComma: es5
6 | bracketSpacing: true
7 | parenSpacing: true
8 | jsxBracketSameLine: false
9 | semi: true
10 | arrowParens: always
11 |
--------------------------------------------------------------------------------
/.distignore:
--------------------------------------------------------------------------------
1 | /.wordpress-org
2 | /.git
3 | /.github
4 | /node_modules
5 | /tests
6 |
7 | .distignore
8 | .gitignore
9 | .gitattributes
10 | .prettierrc
11 | composer.json
12 | composer.lock
13 | package.json
14 | package-lock.json
15 | phpcs.xml
16 | webpack.config.js
17 | .editorconfig
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /nbproject/private/
2 | project.xml
3 | project.properties
4 | .DS_Store
5 | Thumbs.db
6 | .buildpath
7 | .project
8 | .settings*
9 | sftp-config.json
10 |
11 | # Ignore all log files except for .htaccess
12 | /logs/*
13 | !/logs/.htaccess
14 |
15 | # Composer/NPM
16 | /vendor
17 | /node_modules
18 |
19 | # Builds
20 | /build
21 | /deploy
22 |
--------------------------------------------------------------------------------
/assets/js/frontend.js:
--------------------------------------------------------------------------------
1 | let myLabels = document.querySelectorAll('.wp-post-series-box__label');
2 |
3 | Array.from(myLabels).forEach((label) => {
4 | label.addEventListener('keydown', (e) => {
5 | // 32 === spacebar
6 | // 13 === enter
7 | if (e.which === 32 || e.which === 13) {
8 | e.preventDefault();
9 | label.click();
10 | }
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: [
3 | 'plugin:@wordpress/eslint-plugin/recommended',
4 | 'prettier',
5 | 'plugin:jest/recommended',
6 | 'plugin:react-hooks/recommended',
7 | ],
8 | env: {
9 | 'jest/globals': true,
10 | },
11 | globals: {
12 | page: true,
13 | browser: true,
14 | context: true,
15 | jestPuppeteer: true,
16 | fetchMock: true,
17 | jQuery: 'readonly',
18 | },
19 | plugins: ['jest'],
20 | rules: {
21 | '@wordpress/dependency-group': 'off',
22 | 'valid-jsdoc': 'off',
23 | radix: 'error',
24 | yoda: ['error', 'never'],
25 | },
26 | };
27 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # This file is for unifying the coding style for different editors and IDEs
2 | # editorconfig.org
3 |
4 | # WordPress Coding Standards
5 | # https://make.wordpress.org/core/handbook/coding-standards/
6 |
7 | root = true
8 |
9 | [*]
10 | charset = utf-8
11 | end_of_line = lf
12 | indent_size = 4
13 | tab_width = 4
14 | indent_style = tab
15 | insert_final_newline = true
16 | trim_trailing_whitespace = true
17 |
18 | [*.txt]
19 | trim_trailing_whitespace = false
20 |
21 | [*.{md,json,yml}]
22 | trim_trailing_whitespace = false
23 | indent_style = space
24 | indent_size = 2
25 |
26 | [*.json]
27 | indent_style = tab
28 |
--------------------------------------------------------------------------------
/.github/workflows/deploy.yml:
--------------------------------------------------------------------------------
1 | name: Deploy to WordPress.org
2 | on:
3 | push:
4 | tags:
5 | - "*"
6 | jobs:
7 | tag:
8 | name: New tag
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions/checkout@master
12 | - name: Build
13 | run: |
14 | composer install --no-dev
15 | composer dump-autoload --no-dev
16 | npm install
17 | npm run build
18 | - name: WordPress Plugin Deploy
19 | uses: 10up/action-wordpress-plugin-deploy@stable
20 | env:
21 | SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }}
22 | SVN_USERNAME: ${{ secrets.SVN_USERNAME }}
23 | SLUG: wp-post-series
24 |
--------------------------------------------------------------------------------
/src/Registry/FactoryType.php:
--------------------------------------------------------------------------------
1 | resolve_value( $container );
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Registry/SharedType.php:
--------------------------------------------------------------------------------
1 | shared_instance ) ) {
32 | $this->shared_instance = $this->resolve_value( $container );
33 | }
34 | return $this->shared_instance;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/assets/js/hocs/with-post-series-terms.js:
--------------------------------------------------------------------------------
1 | /**
2 | * External dependencies
3 | */
4 | import apiFetch from '@wordpress/api-fetch';
5 | import { useState, useEffect } from '@wordpress/element';
6 |
7 | /**
8 | * HOC that loads post series terms from the API.
9 | *
10 | * @param {Function} OriginalComponent Component being wrapped.
11 | */
12 | const withPostSeriesTerms = ( OriginalComponent ) => {
13 | return ( props ) => {
14 | const [ termsList, setTermsList ] = useState( {} );
15 | const [ loading, setLoading ] = useState( true );
16 |
17 | useEffect( () => {
18 | apiFetch( { path: '/wp/v2/post_series?per_page=-1' } )
19 | .then( ( terms ) => {
20 | setTermsList( terms );
21 | } )
22 | .catch( async () => {
23 | setTermsList( [] );
24 | } )
25 | .finally( () => {
26 | setLoading( false );
27 | } );
28 | }, [] );
29 |
30 | return (
31 |
36 | );
37 | };
38 | };
39 |
40 | export default withPostSeriesTerms;
41 |
--------------------------------------------------------------------------------
/assets/js/post-series-block/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * External dependencies
3 | */
4 | import { __ } from '@wordpress/i18n';
5 | import { postList as icon } from '@wordpress/icons';
6 | import { registerBlockType } from '@wordpress/blocks';
7 |
8 | /**
9 | * Internal dependencies
10 | */
11 | import edit from './edit.js';
12 |
13 | registerBlockType( 'mj/wp-post-series', {
14 | title: __( 'Post Series', 'wp-post-series' ),
15 | icon,
16 | keywords: [
17 | __( 'series', 'wp-post-series' ),
18 | __( 'post', 'wp-post-series' ),
19 | ],
20 | category: 'widgets',
21 | description: __(
22 | 'Show a list of posts in the same series.',
23 | 'wp-post-series'
24 | ),
25 | supports: {
26 | html: false,
27 | multiple: true,
28 | },
29 | example: { attributes: {} },
30 | attributes: {
31 | series: {
32 | type: 'string',
33 | default: '',
34 | },
35 | showDescription: {
36 | type: 'boolean',
37 | default: true,
38 | },
39 | showPosts: {
40 | type: 'boolean',
41 | default: false,
42 | },
43 | },
44 | edit,
45 | save() {
46 | return null;
47 | },
48 | } );
49 |
--------------------------------------------------------------------------------
/wp-post-series.php:
--------------------------------------------------------------------------------
1 | slug );
9 | ?>
10 |
11 |
12 |
13 |
14 |
15 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/tests/checklist.md:
--------------------------------------------------------------------------------
1 | # Testing Checklist
2 |
3 | ## Manual testing checklist:
4 |
5 | - [x] Plugin activates without notices
6 | - [x] "Series" available in Gutenberg sidebar
7 | - [x] "Series" saves to post
8 | - [x] "Series" shows on frontend content
9 | - [x] Functional list of posts
10 | - [x] Toggle works on click
11 | - [x] Descriptions are shown correctly
12 | - [x] Future posts shown with publish date
13 | - [x] Post Series Block
14 | - [x] Can be inserted
15 | - [x] Shows current series
16 | - [x] Shows chosen series
17 | - [x] Saves/loads correctly
18 | - [x] Has preview
19 | - [x] Compatibility
20 | - [x] "Series" available in classic editor meta box
21 | - [x] "Legacy" template file doesn't cause errors
22 | - [x] Runs on WP 5.5
23 | - [x] Runs on PHP 5.6
24 | - [x] Appearance acceptable across default themes:
25 | - [x] Twenty Ten
26 | - [x] Twenty Eleven
27 | - [x] Twenty Twelve
28 | - [x] Twenty Thirteen
29 | - [x] Twenty Fourteen
30 | - [x] Twenty Fifteen
31 | - [x] Twenty Sixteen
32 | - [x] Twenty Seventeen
33 | - [x] Twenty Nineteen
34 | - [x] Twenty Twenty
35 |
36 | ## Post deployment checklist
37 |
38 | - [ ] Stable tag is up to date on wordpress.org
39 | - [ ] Tag exists on wordpress.org
40 | - [ ] Plugin contains the /build/ directory
41 | - [ ] Plugin contains the /vendor/ directory and autoloader
42 |
--------------------------------------------------------------------------------
/phpcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | PHP_CodeSniffer ruleset.
4 |
5 |
6 | */node_modules/*
7 | */vendor/*
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | tests/
24 |
25 |
26 |
27 | src/*
28 |
29 |
30 |
31 | src/*
32 |
33 |
34 |
35 | tests/
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | src/*
45 |
46 |
47 |
--------------------------------------------------------------------------------
/src/Registry/AbstractDependencyType.php:
--------------------------------------------------------------------------------
1 | callable_or_value = $callable_or_value;
34 | }
35 |
36 | /**
37 | * Resolver for the internal dependency value.
38 | *
39 | * @param Container $container The Dependency Injection Container.
40 | *
41 | * @return mixed
42 | */
43 | protected function resolve_value( Container $container ) {
44 | $callback = $this->callable_or_value;
45 | return \method_exists( $callback, '__invoke' )
46 | ? $callback( $container )
47 | : $callback;
48 | }
49 |
50 | /**
51 | * Retrieves the value stored internally for this DependencyType
52 | *
53 | * @param Container $container The Dependency Injection Container.
54 | *
55 | * @return void
56 | */
57 | abstract public function get( Container $container );
58 | }
59 |
--------------------------------------------------------------------------------
/assets/js/post-series-block/block.js:
--------------------------------------------------------------------------------
1 | /**
2 | * External dependencies
3 | */
4 | import { __ } from '@wordpress/i18n';
5 | import { Icon, postList } from '@wordpress/icons';
6 | import { ServerSideRender } from '@wordpress/editor';
7 | import { Placeholder } from '@wordpress/components';
8 |
9 | const EmptyPlaceholder = ( { message } ) => (
10 | }
12 | label={ __( 'Post Series', 'wp-post-series' ) }
13 | className="wp-block-post-series"
14 | >
15 | { message
16 | ? message
17 | : __(
18 | 'This block shows a list of posts within the selected series.',
19 | 'wp-post-series'
20 | ) }
21 |
22 | );
23 |
24 | /**
25 | * Component displaying a post series.
26 | *
27 | * @param {Object} props Incoming props.
28 | * @param {Array} [props.attributes] Block attributes.
29 | * @param {number} [props.currentPostSeriesId] Current post series ID--may not be saved yet.
30 | * @return {*} The component.
31 | */
32 | const PostSeriesBlock = ( { attributes, currentPostSeriesId } ) => {
33 | // If we are not loading terms and the post has no assigned series, show a placeholder.
34 | if ( attributes.series === '' && currentPostSeriesId === 0 ) {
35 | return (
36 |
42 | );
43 | }
44 | const ssrAttributes = attributes;
45 |
46 | if ( attributes.series === '' ) {
47 | ssrAttributes.previewId = currentPostSeriesId;
48 | }
49 |
50 | return (
51 |
56 | );
57 | };
58 |
59 | export default PostSeriesBlock;
60 |
--------------------------------------------------------------------------------
/languages/wp-post-series.pot:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 WP Post Series
2 | # This file is distributed under the same license as the WP Post Series package.
3 | msgid ""
4 | msgstr ""
5 | "Project-Id-Version: WP Post Series 1.0.2\n"
6 | "Report-Msgid-Bugs-To: http://wordpress.org/tag/wp-post-series\n"
7 | "POT-Creation-Date: 2015-11-26 16:25:03+00:00\n"
8 | "MIME-Version: 1.0\n"
9 | "Content-Type: text/plain; charset=UTF-8\n"
10 | "Content-Transfer-Encoding: 8bit\n"
11 | "PO-Revision-Date: 2015-MO-DA HO:MI+ZONE\n"
12 | "Last-Translator: FULL NAME \n"
13 | "Language-Team: LANGUAGE \n"
14 |
15 | #: templates/series-box.php:9
16 | msgid "This is post #%d of %d in the series “%s”"
17 | msgstr ""
18 |
19 | #: wp-post-series.php:57 wp-post-series.php:58
20 | msgid "Post series"
21 | msgstr ""
22 |
23 | #: wp-post-series.php:66 wp-post-series.php:160
24 | msgid "Series"
25 | msgstr ""
26 |
27 | #: wp-post-series.php:69
28 | msgid "Search %s"
29 | msgstr ""
30 |
31 | #: wp-post-series.php:70
32 | msgid "All %s"
33 | msgstr ""
34 |
35 | #: wp-post-series.php:71
36 | msgid "%s"
37 | msgstr ""
38 |
39 | #: wp-post-series.php:72
40 | msgid "%s:"
41 | msgstr ""
42 |
43 | #: wp-post-series.php:73
44 | msgid "Edit %s"
45 | msgstr ""
46 |
47 | #: wp-post-series.php:74
48 | msgid "Update %s"
49 | msgstr ""
50 |
51 | #: wp-post-series.php:75
52 | msgid "Add New %s"
53 | msgstr ""
54 |
55 | #: wp-post-series.php:76
56 | msgid "New %s Name"
57 | msgstr ""
58 |
59 | #: wp-post-series.php:180
60 | msgid "N/A"
61 | msgstr ""
62 |
63 | #: wp-post-series.php:203
64 | msgid "Show all series"
65 | msgstr ""
66 |
67 | #. Plugin Name of the plugin/theme
68 | msgid "WP Post Series"
69 | msgstr ""
70 |
71 | #. Plugin URI of the plugin/theme
72 | msgid "https://github.com/mikejolley/wp-post-series"
73 | msgstr ""
74 |
75 | #. Description of the plugin/theme
76 | msgid ""
77 | "Lets you setup a simple series of posts using taxonomies. Posts within a "
78 | "series will show an information box above the content automatically with "
79 | "links to other posts in the series and a description."
80 | msgstr ""
81 |
82 | #. Author of the plugin/theme
83 | msgid "Mike Jolley"
84 | msgstr ""
85 |
86 | #. Author URI of the plugin/theme
87 | msgid "http://mikejolley.com"
88 | msgstr ""
89 |
--------------------------------------------------------------------------------
/assets/css/post-series.scss:
--------------------------------------------------------------------------------
1 | .wp-post-series-box,
2 | .entry-content .wp-post-series-box,
3 | #content .wp-post-series-box {
4 | margin-top: 1.5em;
5 | margin-bottom: 1.5em;
6 | padding: 0;
7 | border-radius: 5px;
8 | border: 2px solid rgba( 0, 0, 0, 0.2 );
9 | box-shadow: 0 2px 3px rgba( 0, 0, 0, 0.05 );
10 |
11 | &:first-child {
12 | margin-top: 0;
13 | }
14 |
15 | .wp-post-series-box__description,
16 | .wp-post-series-box__scheduled_text {
17 | font-style: italic;
18 | }
19 | .wp-post-series-box__current {
20 | font-weight: bold;
21 | }
22 | .wp-post-series-box__name {
23 | margin: 0;
24 | font-weight: bold;
25 | }
26 | .wp-post-series-box__description {
27 | margin: 0;
28 | p {
29 | margin: 0;
30 | }
31 | p + p {
32 | margin: 1em 0 0 0;
33 | }
34 | }
35 | .wp-post-series-box__posts {
36 | ol {
37 | list-style: decimal inside;
38 | margin: 0;
39 | padding: 1em;
40 | border-top: 1px solid rgba( 0, 0, 0, 0.2 );
41 | background-color: rgba( 0, 0, 0, 0.03 );
42 |
43 | li {
44 | margin: 0;
45 | padding: 0;
46 | }
47 | }
48 | }
49 | .wp-post-series-box__toggle_checkbox {
50 | display: none;
51 | }
52 | .wp-post-series-box__label {
53 | display: block;
54 | position: relative;
55 | padding: 1em;
56 | margin: 0;
57 | cursor: default;
58 | }
59 | &.wp-post-series-box--expandable {
60 | .wp-post-series-box__label {
61 | cursor: pointer;
62 |
63 | &:focus {
64 | outline: auto;
65 | }
66 | &:before {
67 | content: ' ';
68 | position: absolute;
69 | top: 50%;
70 | right: 1em;
71 | margin-top: -3px;
72 | display: block;
73 | transform: translateY(-2px);
74 | border-top: 5px solid transparent;
75 | border-bottom: 5px solid transparent;
76 | border-left: 5px solid currentColor;
77 | transition: transform .2s ease-out;
78 | }
79 | }
80 | .wp-post-series-box__posts {
81 | max-height: 0px;
82 | overflow: hidden;
83 | transition: max-height .25s ease-in-out;
84 | }
85 | .wp-post-series-box__toggle_checkbox:checked + .wp-post-series-box__label + .wp-post-series-box__posts {
86 | max-height: 100vh;
87 | }
88 | .wp-post-series-box__toggle_checkbox:checked + .wp-post-series-box__label::before {
89 | transform: rotate(90deg) translateX(-3px);
90 | }
91 | }
92 | }
93 |
94 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "wp-post-series",
3 | "title": "WP Post Series",
4 | "homepage": "https://wordpress.org/plugins/wp-post-series",
5 | "description": "Lets you setup a simple series of posts using taxonomies. Posts within a series will show an information box above the content automatically with links to other posts in the series and a description.",
6 | "author": "Mike Jolley",
7 | "license": "GPL-3.0-or-later",
8 | "version": "2.0.0",
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/mikejolley/wp-post-series.git"
12 | },
13 | "bugs": {
14 | "url": "https://github.com/mikejolley/wp-post-series/issues"
15 | },
16 | "babel": {
17 | "presets": [
18 | "@babel/preset-env"
19 | ]
20 | },
21 | "browserslist": [
22 | "extends @wordpress/browserslist-config"
23 | ],
24 | "devDependencies": {
25 | "@babel/cli": "^7.11.6",
26 | "@babel/core": "^7.11.6",
27 | "@babel/plugin-proposal-async-generator-functions": "^7.12.1",
28 | "@babel/plugin-proposal-class-properties": "^7.12.1",
29 | "@babel/plugin-proposal-object-rest-spread": "^7.12.1",
30 | "@babel/plugin-transform-react-jsx": "^7.12.1",
31 | "@babel/plugin-transform-runtime": "^7.11.5",
32 | "@babel/preset-env": "^7.11.5",
33 | "@wordpress/babel-preset-default": "^4.19.0",
34 | "@wordpress/browserslist-config": "^2.7.0",
35 | "@wordpress/dependency-extraction-webpack-plugin": "^2.8.0",
36 | "@wordpress/eslint-plugin": "^7.3.0",
37 | "babel-loader": "^8.1.0",
38 | "babel-minify-webpack-plugin": "^0.3.1",
39 | "babel-plugin-transform-react-remove-prop-types": "^0.4.24",
40 | "browserslist": "^4.14.5",
41 | "css-loader": "^4.3.0",
42 | "eslint": "6.8.0",
43 | "eslint-config-prettier": "6.11.0",
44 | "eslint-plugin-jest": "23.19.0",
45 | "eslint-plugin-react-hooks": "4.0.0",
46 | "mini-css-extract-plugin": "^0.11.3",
47 | "postcss": "^8.1.1",
48 | "postcss-loader": "^4.0.3",
49 | "prettier": "npm:wp-prettier@1.19.1",
50 | "rimraf": "^3.0.2",
51 | "sass": "^1.26.12",
52 | "sass-loader": "^10.0.2",
53 | "webpack": "^4.44.2",
54 | "webpack-cli": "^3.3.12"
55 | },
56 | "scripts": {
57 | "watch": "rimraf build/* && webpack --mode=development --watch --config webpack.config.js",
58 | "build": "rimraf build/* && webpack --mode=production --config webpack.config.js"
59 | },
60 | "dependencies": {
61 | "@wordpress/blocks": "^6.23.0",
62 | "@wordpress/i18n": "^3.16.0",
63 | "@wordpress/icons": "^2.7.0",
64 | "classnames": "^2.2.6"
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/Template.php:
--------------------------------------------------------------------------------
1 | default_template_dir = $default_template_dir;
33 | }
34 |
35 | /**
36 | * Get and include template files.
37 | *
38 | * @param mixed $template_name Name of template to load.
39 | * @param array $args (default: array()) Args to pass to the template file.
40 | * @param string $template_path (default: '') Path to look for template file.
41 | * @param string $default_path (default: '') Default path to look for template file.
42 | */
43 | public function get_template( $template_name, $args = array(), $template_path = '', $default_path = '' ) {
44 | if ( $args && is_array( $args ) ) {
45 | // phpcs:ignore WordPress.PHP.DontExtract.extract_extract
46 | extract( $args );
47 | }
48 | include $this->locate_template( $template_name, $template_path, $default_path );
49 | }
50 |
51 | /**
52 | * Locate a template and return the path for inclusion.
53 | *
54 | * This is the load order:
55 | *
56 | * yourtheme / $template_path / $template_name
57 | * yourtheme / $template_name
58 | * $default_path / $template_name
59 | *
60 | * @param mixed $template_name Name of template to load.
61 | * @param string $template_path (default: '') Path to look for template file.
62 | * @param string $default_path (default: '') Default path to look for template file.
63 | * @return string
64 | */
65 | public function locate_template( $template_name, $template_path = '', $default_path = '' ) {
66 | if ( ! $template_path ) {
67 | $template_path = 'wp_post_series';
68 | }
69 | if ( ! $default_path ) {
70 | $default_path = $this->default_template_dir;
71 | }
72 |
73 | // Look within passed path within the theme - this is priority.
74 | $template = locate_template(
75 | array(
76 | trailingslashit( $template_path ) . $template_name,
77 | $template_name,
78 | )
79 | );
80 |
81 | // Get default template.
82 | if ( ! $template ) {
83 | $template = $default_path . $template_name;
84 | }
85 |
86 | return apply_filters( 'wp_post_series_locate_template', $template, $template_name, $template_path );
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/wp-post-series-init.php:
--------------------------------------------------------------------------------
1 |
39 |
40 |
41 | composer install && && npm install && npm run build',
46 | '' . esc_html( str_replace( ABSPATH, '', __DIR__ ) ) . ''
47 | );
48 | ?>
49 |
50 |
51 | register(
84 | \MJ\PostSeries\Plugin::class,
85 | function( \MJ\PostSeries\Registry\Container $container ) {
86 | return new \MJ\PostSeries\Plugin( $container, __FILE__ );
87 | }
88 | );
89 | $container->get( \MJ\PostSeries\Plugin::class );
90 | }
91 |
92 | return $container;
93 | }
94 |
95 | add_action( 'plugins_loaded', 'MJ\PostSeries\init', 20 );
96 |
--------------------------------------------------------------------------------
/src/BlockTypes/PostSeries.php:
--------------------------------------------------------------------------------
1 | content = $content;
47 | }
48 |
49 | /**
50 | * Gets the editor script handle.
51 | *
52 | * @return string
53 | */
54 | public function get_script_handle() {
55 | return $this->block_name . '-block';
56 | }
57 |
58 | /**
59 | * Registers the block type with WordPress.
60 | */
61 | public function register_block_type() {
62 | register_block_type(
63 | $this->namespace . '/' . $this->block_name,
64 | array(
65 | 'editor_script' => $this->get_script_handle(),
66 | 'script' => 'wp-post-series',
67 | 'style' => 'wp-post-series',
68 | 'render_callback' => array( $this, 'render' ),
69 | 'attributes' => array(
70 | 'series' => array(
71 | 'type' => 'string',
72 | ),
73 | 'showDescription' => array(
74 | 'type' => 'boolean',
75 | ),
76 | 'showPosts' => array(
77 | 'type' => 'boolean',
78 | ),
79 | 'className' => array(
80 | 'type' => 'string',
81 | ),
82 | 'previewId' => array(
83 | 'type' => 'number',
84 | ),
85 | ),
86 | 'supports' => [],
87 | )
88 | );
89 | }
90 |
91 | /**
92 | * Append frontend scripts when rendering the block.
93 | *
94 | * @param array|\WP_Block $attributes Block attributes, or an instance of a WP_Block. Defaults to an empty array.
95 | * @param string $content Block content. Default empty string.
96 | * @return string Rendered block type output.
97 | */
98 | public function render( $attributes = [], $content = '' ) {
99 | $attributes = wp_parse_args(
100 | $attributes,
101 | array(
102 | 'series' => '',
103 | 'showDescription' => true,
104 | 'showPosts' => false,
105 | 'className' => '',
106 | 'previewId' => 0,
107 | )
108 | );
109 | $post_id = get_the_ID();
110 |
111 | if ( ! empty( $attributes['previewId'] ) && empty( $attributes['series'] ) ) {
112 | $series = get_term_by( 'id', absint( $attributes['previewId'] ), 'post_series' );
113 | } else {
114 | $series_slug = ! empty( $attributes['series'] ) ? $attributes['series'] : '';
115 | $series = $series_slug ? get_term_by( 'slug', $series_slug, 'post_series' ) : \MJ\PostSeries\get_post_series( $post_id );
116 | }
117 |
118 | if ( ! $series || is_wp_error( $series ) ) {
119 | return $content;
120 | }
121 |
122 | return $this->content->render_post_series( $post_id, $series, $attributes['className'], $attributes['showDescription'], $attributes['showPosts'] );
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/src/Registry/Container.php:
--------------------------------------------------------------------------------
1 | register( MyClass::class, $container->factory( $mycallback ) );
37 | * ```
38 | *
39 | * @param Closure $instantiation_callback This will be invoked when the
40 | * dependency is required. It will
41 | * receive an instance of this
42 | * container so the callback can
43 | * retrieve dependencies from the
44 | * container.
45 | * @return FactoryType An instance of the FactoryType dependency.
46 | */
47 | public function factory( Closure $instantiation_callback ) {
48 | return new FactoryType( $instantiation_callback );
49 | }
50 |
51 | /**
52 | * Interface for registering a new dependency with the container.
53 | *
54 | * By default, the $value will be added as a shared dependency. This means
55 | * that it will be a single instance shared among any other classes having
56 | * that dependency.
57 | *
58 | * If you want a new instance every time it's required, then wrap the value
59 | * in a call to the factory method (@see Container::factory for example)
60 | *
61 | * Note: Currently if the provided id already is registered in the container,
62 | * the provided value is ignored.
63 | *
64 | * @param string $id A unique string identifier for the provided value.
65 | * Typically it's the fully qualified name for the
66 | * dependency.
67 | * @param mixed $value The value for the dependency. Typically, this is a
68 | * closure that will create the class instance needed.
69 | */
70 | public function register( $id, $value ) {
71 | if ( empty( $this->registry[ $id ] ) ) {
72 | if ( ! $value instanceof FactoryType ) {
73 | $value = new SharedType( $value );
74 | }
75 | $this->registry[ $id ] = $value;
76 | }
77 | }
78 |
79 | /**
80 | * Interface for retrieving the dependency stored in the container for the
81 | * given identifier.
82 | *
83 | * @param string $id The identifier for the dependency being retrieved.
84 | * @throws Exception If there is no dependency for the given identifier in
85 | * the container.
86 | *
87 | * @return mixed Typically a class instance.
88 | */
89 | public function get( $id ) {
90 | if ( ! isset( $this->registry[ $id ] ) ) {
91 | // this is a developer facing exception, hence it is not localized.
92 | throw new Exception(
93 | sprintf(
94 | 'Cannot construct an instance of %s because it has not been registered.',
95 | $id
96 | )
97 | );
98 | }
99 | return $this->registry[ $id ]->get( $this );
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | // Require path.
2 | const path = require('path');
3 | const DependencyExtractionWebpackPlugin = require('@wordpress/dependency-extraction-webpack-plugin');
4 | const MinifyPlugin = require('babel-minify-webpack-plugin');
5 | const MiniCssExtractPlugin = require('mini-css-extract-plugin');
6 | const frontConfig = {
7 | mode: 'production',
8 | entry: {
9 | frontend: './assets/js/frontend.js',
10 | },
11 | output: {
12 | filename: '[name].js',
13 | path: path.resolve(__dirname, 'build'),
14 | },
15 | module: {
16 | rules: [
17 | {
18 | test: /\.jsx?$/,
19 | include: [path.resolve(__dirname, 'assets/js')],
20 | use: {
21 | loader: 'babel-loader?cacheDirectory',
22 | options: {
23 | presets: [
24 | [
25 | '@babel/preset-env',
26 | {
27 | modules: false,
28 | targets: {
29 | browsers: [
30 | 'extends @wordpress/browserslist-config',
31 | ],
32 | },
33 | },
34 | ],
35 | ],
36 | plugins: [
37 | require.resolve('@babel/plugin-transform-runtime'),
38 | ].filter(Boolean),
39 | },
40 | },
41 | },
42 | ],
43 | },
44 | plugins: [
45 | new DependencyExtractionWebpackPlugin({
46 | injectPolyfill: true,
47 | }),
48 | new MinifyPlugin(),
49 | ],
50 | };
51 | const blocksConfig = {
52 | mode: 'production',
53 | devtool: false,
54 | entry: {
55 | 'wp-post-series-block': './assets/js/post-series-block/index.js',
56 | },
57 | output: {
58 | path: path.resolve(__dirname, 'build'),
59 | filename: '[name].js',
60 | library: ['mj', 'blocks', '[name]'],
61 | libraryTarget: 'this',
62 | // This fixes an issue with multiple webpack projects using chunking
63 | // overwriting each other's chunk loader function.
64 | // See https://webpack.js.org/configuration/output/#outputjsonpfunction
65 | jsonpFunction: 'webpackMjBlocksJsonp',
66 | },
67 | optimization: {
68 | splitChunks: {
69 | minSize: 0,
70 | cacheGroups: {
71 | commons: {
72 | test: /[\\/]node_modules[\\/]/,
73 | name: 'vendors',
74 | chunks: 'all',
75 | enforce: true,
76 | },
77 | },
78 | },
79 | },
80 | module: {
81 | rules: [
82 | {
83 | test: /\.jsx?$/,
84 | include: [path.resolve(__dirname, 'assets/js')],
85 | exclude: /node_modules/,
86 | use: {
87 | loader: 'babel-loader?cacheDirectory',
88 | options: {
89 | presets: ['@wordpress/babel-preset-default'],
90 | plugins: [
91 | require.resolve(
92 | 'babel-plugin-transform-react-remove-prop-types'
93 | ),
94 | require.resolve(
95 | '@babel/plugin-proposal-class-properties'
96 | ),
97 | ].filter(Boolean),
98 | },
99 | },
100 | },
101 | ],
102 | },
103 | plugins: [
104 | new DependencyExtractionWebpackPlugin({
105 | injectPolyfill: true,
106 | }),
107 | new MinifyPlugin(),
108 | ],
109 | };
110 | const styleConfig = {
111 | mode: 'production',
112 | entry: {
113 | 'post-series': './assets/css/post-series.scss',
114 | },
115 | output: {
116 | path: path.resolve(__dirname, 'build'),
117 | filename: `[name]-style.js`,
118 | },
119 | plugins: [
120 | new MiniCssExtractPlugin({
121 | filename: `[name].css`,
122 | }),
123 | ],
124 | module: {
125 | rules: [
126 | {
127 | test: /\.s[ac]ss$/i,
128 | use: [
129 | MiniCssExtractPlugin.loader,
130 | { loader: 'css-loader', options: { importLoaders: 1 } },
131 | 'postcss-loader',
132 | 'sass-loader',
133 | ],
134 | },
135 | ],
136 | },
137 | };
138 |
139 | module.exports = [frontConfig, blocksConfig, styleConfig];
140 |
--------------------------------------------------------------------------------
/assets/js/post-series-block/edit.js:
--------------------------------------------------------------------------------
1 | /**
2 | * External dependencies
3 | */
4 | import { __ } from '@wordpress/i18n';
5 | import { InspectorControls } from '@wordpress/block-editor';
6 | import {
7 | Disabled,
8 | PanelBody,
9 | ToggleControl,
10 | SelectControl,
11 | } from '@wordpress/components';
12 | import { useSelect } from '@wordpress/data';
13 | import { useMemo } from '@wordpress/element';
14 |
15 | /**
16 | * Internal dependencies
17 | */
18 | import Block from './block.js';
19 | import withPostSeriesTerms from '../hocs/with-post-series-terms';
20 |
21 | /**
22 | * Edit Component.
23 | *
24 | * @param {Object} props Incoming props.
25 | * @param {Array} [props.attributes] Block attributes.
26 | * @param {Function} [props.setAttributes] Set block attributes.
27 | * @param {Array} [props.termsList] Array of post_series terms.
28 | * @param {boolean} [props.termsLoading] True when terms are being loaded from the API.
29 | * @return {*} The component.
30 | */
31 | const Edit = ( { attributes, setAttributes, termsList, termsLoading } ) => {
32 | const { series, showDescription, showPosts } = attributes;
33 |
34 | /**
35 | * Track the post series term assigned to the post (unsaved).
36 | *
37 | * @type {Array} editingPostSeries Array of term IDs.
38 | */
39 | const editingPostSeries = useSelect( ( select ) => {
40 | const store = select( 'core/editor' );
41 | return store.getEditedPostAttribute( 'post_series' );
42 | }, [] );
43 |
44 | const currentPostSeriesId = useMemo( () => {
45 | if ( ! editingPostSeries[ 0 ] ) {
46 | return 0;
47 | }
48 | return editingPostSeries[ 0 ];
49 | }, [ editingPostSeries ] );
50 |
51 | return (
52 | <>
53 |
54 |
58 | { ! termsLoading && (
59 | {
71 | return {
72 | label: term.name,
73 | value: term.slug,
74 | };
75 | } ),
76 | ] }
77 | onChange={ ( chosenSeries ) =>
78 | setAttributes( { series: chosenSeries } )
79 | }
80 | />
81 | ) }
82 |
100 | setAttributes( {
101 | showDescription: ! showDescription,
102 | } )
103 | }
104 | />
105 |
123 | setAttributes( { showPosts: ! showPosts } )
124 | }
125 | />
126 |
127 |
128 |
129 |
133 |
134 | >
135 | );
136 | };
137 |
138 | Edit.propTypes = {};
139 |
140 | export default withPostSeriesTerms( Edit );
141 |
--------------------------------------------------------------------------------
/readme.txt:
--------------------------------------------------------------------------------
1 | === WP Post Series ===
2 | Contributors: mikejolley
3 | Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=mike.jolley@me.com¤cy_code=&amount=&return=&item_name=Buy+me+a+coffee+for+WP+Post+Series
4 | Tags: series, post series, organize, course, book
5 | Requires at least: 5.4
6 | Tested up to: 5.6
7 | Requires PHP: 5.6
8 | Stable tag: 2.0.0
9 |
10 | Publish and link together a series of posts using a new "series" taxonomy. Automatically display links to other posts in a series above your content.
11 |
12 | == Description ==
13 |
14 | WP Post Series is a _lightweight_ plugin for making a series of posts and showing information about the series on the post page.
15 |
16 | Posts in a series will automatically show a series box (prepended before the post content), or you can insert them manually using the Post Series Block.
17 |
18 | = Features =
19 |
20 | * Add post series using the familiar WordPress UI and give each one a description.
21 | * Assign post series to your posts.
22 | * Filter posts in the backend by series.
23 | * Show the series above the post content or using the Post Series Block in the editor.
24 | * Developer friendly code — Custom taxonomies & template files.
25 |
26 | = Contributing and reporting bugs =
27 |
28 | You can contribute code and localizations to this plugin via GitHub: [https://github.com/mikejolley/wp-post-series](https://github.com/mikejolley/wp-post-series)
29 |
30 | = Support =
31 |
32 | Use the WordPress.org forums for community support - I cannot offer support directly for free. If you spot a bug, you can of course log it on [Github](https://github.com/mikejolley/wp-post-series) instead where I can act upon it more efficiently.
33 |
34 | If you want help with a customisation, hire a developer!
35 |
36 | == Installation ==
37 |
38 | = Automatic installation =
39 |
40 | Automatic installation is the easiest option as WordPress handles the file transfers itself and you don't even need to leave your web browser. To do an automatic install, log in to your WordPress admin panel, navigate to the Plugins menu and click Add New.
41 |
42 | In the search field type "WP Post Series" and click Search Plugins. Once you've found the plugin you can view details about it such as the point release, rating and description. Most importantly of course, you can install it by clicking _Install Now_.
43 |
44 | = Manual installation =
45 |
46 | The manual installation method involves downloading the plugin and uploading it to your webserver via your favourite FTP application.
47 |
48 | * Download the plugin file to your computer and unzip it
49 | * Using an FTP program, or your hosting control panel, upload the unzipped plugin folder to your WordPress `wp-content/plugins/` directory.
50 | * Activate the plugin from the Plugins menu within the WordPress admin.
51 |
52 | == Screenshots ==
53 |
54 | 1. Post Series Display
55 | 2. Post Series Block Settings
56 |
57 | == Changelog ==
58 |
59 | = 2.0.0 =
60 | * Refactor - Improved template markup and default styling. If you have customized the series-box.php file, be sure to update it based on the new version to take advantage of the new functionality.
61 | * Feature - New Post Series Block for use in the new editor.
62 | * Feature - If the post does not contain the post series block, post series are still injected via the_content hook.
63 | * Refactor - Rewritten majority of plugin using more up to date standards and namespaces.
64 | * Refactor - Content toggle no longer relies om jQuery.
65 | * Fix - Made series taxonomy visible in the Gutenberg editor.
66 |
67 | = 1.1.0 =
68 | * Scheduled post handling! Scheduled posts will contribute to your series count, and the title and scheduled date will be listed along with your other series items.
69 | * Removed bundled language files.
70 | * Added POT file.
71 |
72 | = 1.0.1 =
73 | * Added CSS Class for Series.
74 | * Fix taxonomy class name.
75 | * Show description of series even if the number of posts == 1.
76 | * Fix link to repo in readme.
77 | * Added swedish translation.
78 | * Tweaked styles to work with default themes.
79 |
80 | = 1.0.0 =
81 | * First stable release.
82 |
--------------------------------------------------------------------------------
/src/Plugin.php:
--------------------------------------------------------------------------------
1 | file = $file;
43 | $this->container = $container;
44 | $this->init();
45 | }
46 |
47 | /**
48 | * Initialize class features.
49 | */
50 | private function init() {
51 | add_action( 'init', array( $this, 'load_plugin_textdomain' ) );
52 | add_action( 'init', array( $this, 'register_assets' ) );
53 | add_action( 'init', array( $this, 'register_block_types' ) );
54 | add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
55 |
56 | $this->container->register(
57 | TaxonomyController::class,
58 | function( Container $container ) {
59 | return new TaxonomyController();
60 | }
61 | );
62 | $this->container->register(
63 | Template::class,
64 | function( Container $container ) {
65 | $default_template_dir = dirname( $this->file ) . '/templates/';
66 | return new Template( $default_template_dir );
67 | }
68 | );
69 | $this->container->register(
70 | PostContent::class,
71 | function( Container $container ) {
72 | return new PostContent( $container->get( Template::class ) );
73 | }
74 | );
75 | $this->container->register(
76 | PostSeries::class,
77 | function( Container $container ) {
78 | return new PostSeries( $container->get( PostContent::class ) );
79 | }
80 | );
81 |
82 | $this->container->get( TaxonomyController::class );
83 | $this->container->get( PostContent::class );
84 | }
85 |
86 | /**
87 | * Init localizations.
88 | */
89 | public function load_plugin_textdomain() {
90 | load_plugin_textdomain( 'wp-post-series', false, dirname( plugin_basename( $this->file ) ) . '/languages/' );
91 | }
92 |
93 | /**
94 | * Register block type scripts and styles.
95 | */
96 | public function register_assets() {
97 | wp_register_style( 'wp-post-series', plugins_url( 'build/post-series.css', $this->file ), '', filemtime( dirname( __DIR__ ) . '/build/post-series.css' ) );
98 | wp_register_script( 'wp-post-series-vendors', plugins_url( 'build/vendors.js', $this->file ), [], filemtime( dirname( __DIR__ ) . '/build/vendors.js' ), false );
99 | $this->register_script_asset( 'wp-post-series', plugins_url( 'build/frontend.js', $this->file ), dirname( __DIR__ ) . '/build/frontend.asset.php', [], true );
100 |
101 | $handle = $this->container->get( PostSeries::class )->get_script_handle();
102 | $this->register_script_asset( $handle, plugins_url( 'build/' . $handle . '.js', $this->file ), dirname( __DIR__ ) . '/build/' . $handle . '.asset.php', [ 'wp-post-series-vendors' ] );
103 | }
104 |
105 | /**
106 | * Register block types.
107 | */
108 | public function register_block_types() {
109 | $this->container->get( PostSeries::class )->register_block_type();
110 | }
111 |
112 | /**
113 | * Enqueue scripts and styles.
114 | */
115 | public function enqueue_scripts() {
116 | wp_enqueue_style( 'wp-post-series' );
117 | }
118 |
119 | /**
120 | * Enqueue a script asset with correct dependencies and version.
121 | *
122 | * @param string $handle Script handle.
123 | * @param string $script Script URL.
124 | * @param string $asset_path Path to asset file.
125 | * @param array $dependencies Static list of dependencies.
126 | * @param bool $in_footer Should script be added to the footer.
127 | */
128 | protected function register_script_asset( $handle, $script, $asset_path, $dependencies = [], $in_footer = false ) {
129 | $asset = require $asset_path;
130 | $dependencies = array_merge( $dependencies, isset( $asset['dependencies'] ) ? $asset['dependencies'] : array() );
131 | $version = ! empty( $asset['version'] ) ? $asset['version'] : filemtime( $asset_path );
132 | wp_register_script( $handle, $script, $dependencies, $version, $in_footer );
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/.wordpress-org/icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/TaxonomyController.php:
--------------------------------------------------------------------------------
1 | init();
21 | }
22 |
23 | /**
24 | * Initialize class features.
25 | */
26 | private function init() {
27 | add_action( 'init', array( $this, 'register_taxonomies' ) );
28 | add_filter( 'manage_edit-post_columns', array( $this, 'add_post_series_column' ) );
29 | add_action( 'manage_post_posts_custom_column', array( $this, 'post_series_column_content' ), 2 );
30 | add_action( 'restrict_manage_posts', array( $this, 'filter_posts_by_series' ) );
31 | }
32 |
33 | /**
34 | * Register the taxonomies
35 | */
36 | public function register_taxonomies() {
37 | $plural = __( 'Post series', 'wp-post-series' );
38 | $singular = __( 'Post series', 'wp-post-series' );
39 |
40 | register_taxonomy(
41 | 'post_series',
42 | array( 'post' ),
43 | array(
44 | 'hierarchical' => false,
45 | 'label' => $plural,
46 | 'labels' => array(
47 | 'menu_name' => __( 'Series', 'wp-post-series' ),
48 | 'name' => $plural,
49 | 'singular_name' => $singular,
50 | /* Translators: %s taxonomy name */
51 | 'search_items' => sprintf( __( 'Search %s', 'wp-post-series' ), $plural ),
52 | /* Translators: %s taxonomy name */
53 | 'all_items' => sprintf( __( 'All %s', 'wp-post-series' ), $plural ),
54 | 'parent_item' => $singular,
55 | /* Translators: %s taxonomy name */
56 | 'parent_item_colon' => sprintf( __( '%s:', 'wp-post-series' ), $singular ),
57 | /* Translators: %s taxonomy name */
58 | 'edit_item' => sprintf( __( 'Edit %s', 'wp-post-series' ), $singular ),
59 | /* Translators: %s taxonomy name */
60 | 'update_item' => sprintf( __( 'Update %s', 'wp-post-series' ), $singular ),
61 | /* Translators: %s taxonomy name */
62 | 'add_new_item' => sprintf( __( 'Add New %s', 'wp-post-series' ), $singular ),
63 | /* Translators: %s taxonomy name */
64 | 'new_item_name' => sprintf( __( 'New %s Name', 'wp-post-series' ), $singular ),
65 | ),
66 | 'show_ui' => true,
67 | 'show_in_rest' => true,
68 | 'query_var' => true,
69 | 'rewrite' => apply_filters( 'wp_post_series_enable_archive', false ),
70 | 'meta_box_cb' => array( $this, 'post_series_meta_box' ),
71 | )
72 | );
73 | }
74 |
75 | /**
76 | * Classic editor meta box.
77 | *
78 | * Render the list of post series and allow admin to assign them to a post.
79 | *
80 | * @param array $post Post being edited.
81 | */
82 | public function post_series_meta_box( $post ) {
83 | $current_series = get_post_series( $post->ID );
84 | $current_series_id = $current_series->term_id;
85 | $taxonomy_data = get_taxonomy( 'post_series' );
86 | $post_series_terms = get_terms(
87 | 'post_series',
88 | array(
89 | 'hide_empty' => false,
90 | 'orderby' => 'name',
91 | )
92 | );
93 |
94 | ?>
95 |
96 |
99 |
105 |
106 | $column ) {
121 | $new_columns[ $key ] = $column;
122 |
123 | if ( 'categories' === $key ) {
124 | $new_columns['post_series'] = __( 'Series', 'wp-post-series' );
125 | }
126 | }
127 |
128 | return $new_columns;
129 | }
130 |
131 | /**
132 | * Output admin column value.
133 | *
134 | * @param string $column key for the column.
135 | */
136 | public function post_series_column_content( $column ) {
137 | global $post;
138 |
139 | if ( 'post_series' === $column ) {
140 | $current_series = get_post_series( $post->ID );
141 |
142 | if ( $current_series ) {
143 | echo '' . esc_html( $current_series->name ) . '';
144 | } else {
145 | esc_html_e( 'N/A', 'wp-post-series' );
146 | }
147 | }
148 | }
149 |
150 | /**
151 | * Filter posts by a particular series
152 | */
153 | public function filter_posts_by_series() {
154 | global $typenow, $wp_query;
155 |
156 | if ( $typenow != 'post' ) {
157 | return;
158 | }
159 |
160 | // phpcs:ignore WordPress.Security.NonceVerification.Recommended
161 | $current_series = isset( $_REQUEST['post_series'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['post_series'] ) ) : '';
162 | $post_series_terms = get_terms(
163 | 'post_series',
164 | array(
165 | 'hide_empty' => true,
166 | 'orderby' => 'name',
167 | )
168 | );
169 |
170 | if ( empty( $post_series_terms ) ) {
171 | return;
172 | }
173 | ?>
174 |
180 | template = $template;
32 | $this->init();
33 | }
34 |
35 | /**
36 | * Initialize class features.
37 | */
38 | private function init() {
39 | add_filter( 'the_content', array( $this, 'filter_the_content' ) );
40 | }
41 |
42 | /**
43 | * Filters the_content hook.
44 | *
45 | * @param string $content Post content.
46 | * @return string
47 | */
48 | public function filter_the_content( $content ) {
49 | global $post;
50 |
51 | if ( ! is_main_query() || empty( $post ) || 'post' !== $post->post_type ) {
52 | return $content;
53 | }
54 |
55 | // Disable automatic insertion if already including the series box e.g. with Gutenberg.
56 | if ( strstr( $content, 'wp-post-series-box' ) ) {
57 | return $content;
58 | }
59 |
60 | $post_id = absint( $post->ID );
61 | $series = get_post_series( $post_id );
62 |
63 | if ( ! $series ) {
64 | return $content;
65 | }
66 |
67 | $series_html = $this->render_post_series( $post_id, $series );
68 |
69 | // Append or prepend.
70 | if ( apply_filters( 'wp_post_series_append_info', false ) ) {
71 | return $content . $series_html;
72 | }
73 |
74 | return $series_html . $content;
75 | }
76 |
77 | /**
78 | * Render a series.
79 | *
80 | * @param int $post_id Current Post ID.
81 | * @param \WP_Term $series Series to show.
82 | * @param string $class_name Custom classname.
83 | * @param bool $show_description Whether or not to display the series description.
84 | * @param bool $show_posts Whether or not to display the posts by default, or toggle them.
85 | * @return string
86 | */
87 | public function render_post_series( $post_id, $series, $class_name = '', $show_description = true, $show_posts = false ) {
88 | wp_enqueue_script( 'wp-post-series' );
89 |
90 | $term_description = term_description( $series->term_id, 'post_series' );
91 | $posts_in_series = array_values(
92 | array_map(
93 | 'absint',
94 | get_posts(
95 | array(
96 | 'post_type' => 'post',
97 | 'posts_per_page' => -1,
98 | 'fields' => 'ids',
99 | 'no_found_rows' => true,
100 | 'orderby' => 'date',
101 | 'order' => 'asc',
102 | 'post_status' => array( 'publish', 'future' ),
103 | 'tax_query' => array(
104 | array(
105 | 'taxonomy' => 'post_series',
106 | 'field' => 'slug',
107 | 'terms' => $series->slug,
108 | ),
109 | ),
110 | )
111 | )
112 | )
113 | );
114 | $post_in_series = array_search( $post_id, $posts_in_series, true ) + 1;
115 | $post_series_box_class = trim( 'wp-post-series-box series-' . $series->slug . ' ' . $class_name );
116 | $has_multiple_posts = count( $posts_in_series ) > 1;
117 |
118 | if ( ! $show_posts && $has_multiple_posts ) {
119 | $post_series_box_class .= ' wp-post-series-box--expandable';
120 | }
121 |
122 | ob_start();
123 |
124 | $this->template->get_template(
125 | 'series-box.php',
126 | array(
127 | 'series' => $series,
128 | 'series_name' => $this->post_series_name( $series ),
129 | 'series_label' => $this->post_series_label( $post_id, $series, $posts_in_series ),
130 | 'description' => $term_description ? wpautop( wptexturize( $term_description ) ) : '',
131 | 'posts_in_series' => $posts_in_series,
132 | 'posts_in_series_links' => array_map( array( $this, 'post_series_post_link' ), $posts_in_series ),
133 | 'post_in_series' => $post_in_series,
134 | 'post_series_box_class' => $post_series_box_class,
135 | 'has_multiple_posts' => count( $posts_in_series ) > 1,
136 | 'show_posts' => $show_posts,
137 | 'show_description' => $show_description && $term_description,
138 | )
139 | );
140 |
141 | return ob_get_clean();
142 | }
143 |
144 | /**
145 | * Render a link to a post in a series.
146 | *
147 | * @param \WP_Term $term Series term.
148 | * @return string
149 | */
150 | protected function post_series_name( $term ) {
151 | $series_name = esc_html( $term->name );
152 |
153 | if ( apply_filters( 'wp_post_series_enable_archive', false ) ) {
154 | $series_name = '' . $series_name . '';
155 | }
156 |
157 | return $series_name;
158 | }
159 |
160 | /**
161 | * Render the label for the series; this takes the current post into consideration.
162 | *
163 | * @param int $post_id Current post ID.
164 | * @param \WP_Term $term Series term.
165 | * @param array $posts_in_series List of posts in the series.
166 | * @return string
167 | */
168 | protected function post_series_label( $post_id, $term, $posts_in_series ) {
169 | $series_name = $this->post_series_name( $term );
170 | $post_in_series = array_search( $post_id, $posts_in_series, true );
171 |
172 | if ( false === $post_in_series ) {
173 | return sprintf(
174 | /* translators: %s series name/link */
175 | __( 'Series: %s', 'wp-post-series' ),
176 | $series_name
177 | );
178 | }
179 |
180 | return sprintf(
181 | /* translators: %1$d Post index, %2$d number of posts in series, %3$s series name/link */
182 | __( 'This is post %1$d of %2$d in the series “%3$s”', 'wp-post-series' ),
183 | $post_in_series + 1,
184 | count( $posts_in_series ),
185 | $series_name
186 | );
187 | }
188 |
189 | /**
190 | * Render a link to a post in a series.
191 | *
192 | * @param int $post_id Post ID to render.
193 | * @return string
194 | */
195 | protected function post_series_post_link( $post_id ) {
196 | $is_current = get_the_ID() === $post_id;
197 | $is_published = 'publish' === get_post_status( $post_id );
198 | $prefix = '';
199 | $suffix = '';
200 |
201 | if ( $is_published && ! $is_current ) {
202 | $prefix = '';
203 | $suffix = '';
204 | } elseif ( $is_current ) {
205 | $prefix = '';
206 | $suffix = '';
207 | }
208 |
209 | $title = get_the_title( $post_id );
210 |
211 | if ( ! $is_published ) {
212 | $title .= ' ';
213 | /* translators: %s scheduled post date */
214 | $title .= sprintf( __( 'Scheduled for %s', 'wp-post-series' ), get_post_time( get_option( 'date_format' ), false, $post_id, true ) );
215 | $title .= '';
216 | }
217 |
218 | return $prefix . $title . $suffix;
219 | }
220 | }
221 |
--------------------------------------------------------------------------------
/composer.lock:
--------------------------------------------------------------------------------
1 | {
2 | "_readme": [
3 | "This file locks the dependencies of your project to a known state",
4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
5 | "This file is @generated automatically"
6 | ],
7 | "content-hash": "d6e5782dfc682363e5894a018df69648",
8 | "packages": [
9 | {
10 | "name": "composer/installers",
11 | "version": "v1.7.0",
12 | "source": {
13 | "type": "git",
14 | "url": "https://github.com/composer/installers.git",
15 | "reference": "141b272484481432cda342727a427dc1e206bfa0"
16 | },
17 | "dist": {
18 | "type": "zip",
19 | "url": "https://api.github.com/repos/composer/installers/zipball/141b272484481432cda342727a427dc1e206bfa0",
20 | "reference": "141b272484481432cda342727a427dc1e206bfa0",
21 | "shasum": ""
22 | },
23 | "require": {
24 | "composer-plugin-api": "^1.0"
25 | },
26 | "replace": {
27 | "roundcube/plugin-installer": "*",
28 | "shama/baton": "*"
29 | },
30 | "require-dev": {
31 | "composer/composer": "1.0.*@dev",
32 | "phpunit/phpunit": "^4.8.36"
33 | },
34 | "type": "composer-plugin",
35 | "extra": {
36 | "class": "Composer\\Installers\\Plugin",
37 | "branch-alias": {
38 | "dev-master": "1.0-dev"
39 | }
40 | },
41 | "autoload": {
42 | "psr-4": {
43 | "Composer\\Installers\\": "src/Composer/Installers"
44 | }
45 | },
46 | "notification-url": "https://packagist.org/downloads/",
47 | "license": [
48 | "MIT"
49 | ],
50 | "authors": [
51 | {
52 | "name": "Kyle Robinson Young",
53 | "email": "kyle@dontkry.com",
54 | "homepage": "https://github.com/shama"
55 | }
56 | ],
57 | "description": "A multi-framework Composer library installer",
58 | "homepage": "https://composer.github.io/installers/",
59 | "keywords": [
60 | "Craft",
61 | "Dolibarr",
62 | "Eliasis",
63 | "Hurad",
64 | "ImageCMS",
65 | "Kanboard",
66 | "Lan Management System",
67 | "MODX Evo",
68 | "Mautic",
69 | "Maya",
70 | "OXID",
71 | "Plentymarkets",
72 | "Porto",
73 | "RadPHP",
74 | "SMF",
75 | "Thelia",
76 | "Whmcs",
77 | "WolfCMS",
78 | "agl",
79 | "aimeos",
80 | "annotatecms",
81 | "attogram",
82 | "bitrix",
83 | "cakephp",
84 | "chef",
85 | "cockpit",
86 | "codeigniter",
87 | "concrete5",
88 | "croogo",
89 | "dokuwiki",
90 | "drupal",
91 | "eZ Platform",
92 | "elgg",
93 | "expressionengine",
94 | "fuelphp",
95 | "grav",
96 | "installer",
97 | "itop",
98 | "joomla",
99 | "known",
100 | "kohana",
101 | "laravel",
102 | "lavalite",
103 | "lithium",
104 | "magento",
105 | "majima",
106 | "mako",
107 | "mediawiki",
108 | "modulework",
109 | "modx",
110 | "moodle",
111 | "osclass",
112 | "phpbb",
113 | "piwik",
114 | "ppi",
115 | "puppet",
116 | "pxcms",
117 | "reindex",
118 | "roundcube",
119 | "shopware",
120 | "silverstripe",
121 | "sydes",
122 | "symfony",
123 | "typo3",
124 | "wordpress",
125 | "yawik",
126 | "zend",
127 | "zikula"
128 | ],
129 | "time": "2019-08-12T15:00:31+00:00"
130 | }
131 | ],
132 | "packages-dev": [
133 | {
134 | "name": "doctrine/instantiator",
135 | "version": "1.3.1",
136 | "source": {
137 | "type": "git",
138 | "url": "https://github.com/doctrine/instantiator.git",
139 | "reference": "f350df0268e904597e3bd9c4685c53e0e333feea"
140 | },
141 | "dist": {
142 | "type": "zip",
143 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/f350df0268e904597e3bd9c4685c53e0e333feea",
144 | "reference": "f350df0268e904597e3bd9c4685c53e0e333feea",
145 | "shasum": ""
146 | },
147 | "require": {
148 | "php": "^7.1 || ^8.0"
149 | },
150 | "require-dev": {
151 | "doctrine/coding-standard": "^6.0",
152 | "ext-pdo": "*",
153 | "ext-phar": "*",
154 | "phpbench/phpbench": "^0.13",
155 | "phpstan/phpstan-phpunit": "^0.11",
156 | "phpstan/phpstan-shim": "^0.11",
157 | "phpunit/phpunit": "^7.0"
158 | },
159 | "type": "library",
160 | "extra": {
161 | "branch-alias": {
162 | "dev-master": "1.2.x-dev"
163 | }
164 | },
165 | "autoload": {
166 | "psr-4": {
167 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
168 | }
169 | },
170 | "notification-url": "https://packagist.org/downloads/",
171 | "license": [
172 | "MIT"
173 | ],
174 | "authors": [
175 | {
176 | "name": "Marco Pivetta",
177 | "email": "ocramius@gmail.com",
178 | "homepage": "http://ocramius.github.com/"
179 | }
180 | ],
181 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
182 | "homepage": "https://www.doctrine-project.org/projects/instantiator.html",
183 | "keywords": [
184 | "constructor",
185 | "instantiate"
186 | ],
187 | "time": "2020-05-29T17:27:14+00:00"
188 | },
189 | {
190 | "name": "myclabs/deep-copy",
191 | "version": "1.10.1",
192 | "source": {
193 | "type": "git",
194 | "url": "https://github.com/myclabs/DeepCopy.git",
195 | "reference": "969b211f9a51aa1f6c01d1d2aef56d3bd91598e5"
196 | },
197 | "dist": {
198 | "type": "zip",
199 | "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/969b211f9a51aa1f6c01d1d2aef56d3bd91598e5",
200 | "reference": "969b211f9a51aa1f6c01d1d2aef56d3bd91598e5",
201 | "shasum": ""
202 | },
203 | "require": {
204 | "php": "^7.1 || ^8.0"
205 | },
206 | "replace": {
207 | "myclabs/deep-copy": "self.version"
208 | },
209 | "require-dev": {
210 | "doctrine/collections": "^1.0",
211 | "doctrine/common": "^2.6",
212 | "phpunit/phpunit": "^7.1"
213 | },
214 | "type": "library",
215 | "autoload": {
216 | "psr-4": {
217 | "DeepCopy\\": "src/DeepCopy/"
218 | },
219 | "files": [
220 | "src/DeepCopy/deep_copy.php"
221 | ]
222 | },
223 | "notification-url": "https://packagist.org/downloads/",
224 | "license": [
225 | "MIT"
226 | ],
227 | "description": "Create deep copies (clones) of your objects",
228 | "keywords": [
229 | "clone",
230 | "copy",
231 | "duplicate",
232 | "object",
233 | "object graph"
234 | ],
235 | "time": "2020-06-29T13:22:24+00:00"
236 | },
237 | {
238 | "name": "phar-io/manifest",
239 | "version": "1.0.1",
240 | "source": {
241 | "type": "git",
242 | "url": "https://github.com/phar-io/manifest.git",
243 | "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0"
244 | },
245 | "dist": {
246 | "type": "zip",
247 | "url": "https://api.github.com/repos/phar-io/manifest/zipball/2df402786ab5368a0169091f61a7c1e0eb6852d0",
248 | "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0",
249 | "shasum": ""
250 | },
251 | "require": {
252 | "ext-dom": "*",
253 | "ext-phar": "*",
254 | "phar-io/version": "^1.0.1",
255 | "php": "^5.6 || ^7.0"
256 | },
257 | "type": "library",
258 | "extra": {
259 | "branch-alias": {
260 | "dev-master": "1.0.x-dev"
261 | }
262 | },
263 | "autoload": {
264 | "classmap": [
265 | "src/"
266 | ]
267 | },
268 | "notification-url": "https://packagist.org/downloads/",
269 | "license": [
270 | "BSD-3-Clause"
271 | ],
272 | "authors": [
273 | {
274 | "name": "Arne Blankerts",
275 | "email": "arne@blankerts.de",
276 | "role": "Developer"
277 | },
278 | {
279 | "name": "Sebastian Heuer",
280 | "email": "sebastian@phpeople.de",
281 | "role": "Developer"
282 | },
283 | {
284 | "name": "Sebastian Bergmann",
285 | "email": "sebastian@phpunit.de",
286 | "role": "Developer"
287 | }
288 | ],
289 | "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
290 | "time": "2017-03-05T18:14:27+00:00"
291 | },
292 | {
293 | "name": "phar-io/version",
294 | "version": "1.0.1",
295 | "source": {
296 | "type": "git",
297 | "url": "https://github.com/phar-io/version.git",
298 | "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df"
299 | },
300 | "dist": {
301 | "type": "zip",
302 | "url": "https://api.github.com/repos/phar-io/version/zipball/a70c0ced4be299a63d32fa96d9281d03e94041df",
303 | "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df",
304 | "shasum": ""
305 | },
306 | "require": {
307 | "php": "^5.6 || ^7.0"
308 | },
309 | "type": "library",
310 | "autoload": {
311 | "classmap": [
312 | "src/"
313 | ]
314 | },
315 | "notification-url": "https://packagist.org/downloads/",
316 | "license": [
317 | "BSD-3-Clause"
318 | ],
319 | "authors": [
320 | {
321 | "name": "Arne Blankerts",
322 | "email": "arne@blankerts.de",
323 | "role": "Developer"
324 | },
325 | {
326 | "name": "Sebastian Heuer",
327 | "email": "sebastian@phpeople.de",
328 | "role": "Developer"
329 | },
330 | {
331 | "name": "Sebastian Bergmann",
332 | "email": "sebastian@phpunit.de",
333 | "role": "Developer"
334 | }
335 | ],
336 | "description": "Library for handling version information and constraints",
337 | "time": "2017-03-05T17:38:23+00:00"
338 | },
339 | {
340 | "name": "phpdocumentor/reflection-common",
341 | "version": "2.2.0",
342 | "source": {
343 | "type": "git",
344 | "url": "https://github.com/phpDocumentor/ReflectionCommon.git",
345 | "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b"
346 | },
347 | "dist": {
348 | "type": "zip",
349 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b",
350 | "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b",
351 | "shasum": ""
352 | },
353 | "require": {
354 | "php": "^7.2 || ^8.0"
355 | },
356 | "type": "library",
357 | "extra": {
358 | "branch-alias": {
359 | "dev-2.x": "2.x-dev"
360 | }
361 | },
362 | "autoload": {
363 | "psr-4": {
364 | "phpDocumentor\\Reflection\\": "src/"
365 | }
366 | },
367 | "notification-url": "https://packagist.org/downloads/",
368 | "license": [
369 | "MIT"
370 | ],
371 | "authors": [
372 | {
373 | "name": "Jaap van Otterdijk",
374 | "email": "opensource@ijaap.nl"
375 | }
376 | ],
377 | "description": "Common reflection classes used by phpdocumentor to reflect the code structure",
378 | "homepage": "http://www.phpdoc.org",
379 | "keywords": [
380 | "FQSEN",
381 | "phpDocumentor",
382 | "phpdoc",
383 | "reflection",
384 | "static analysis"
385 | ],
386 | "time": "2020-06-27T09:03:43+00:00"
387 | },
388 | {
389 | "name": "phpdocumentor/reflection-docblock",
390 | "version": "5.2.2",
391 | "source": {
392 | "type": "git",
393 | "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
394 | "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556"
395 | },
396 | "dist": {
397 | "type": "zip",
398 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/069a785b2141f5bcf49f3e353548dc1cce6df556",
399 | "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556",
400 | "shasum": ""
401 | },
402 | "require": {
403 | "ext-filter": "*",
404 | "php": "^7.2 || ^8.0",
405 | "phpdocumentor/reflection-common": "^2.2",
406 | "phpdocumentor/type-resolver": "^1.3",
407 | "webmozart/assert": "^1.9.1"
408 | },
409 | "require-dev": {
410 | "mockery/mockery": "~1.3.2"
411 | },
412 | "type": "library",
413 | "extra": {
414 | "branch-alias": {
415 | "dev-master": "5.x-dev"
416 | }
417 | },
418 | "autoload": {
419 | "psr-4": {
420 | "phpDocumentor\\Reflection\\": "src"
421 | }
422 | },
423 | "notification-url": "https://packagist.org/downloads/",
424 | "license": [
425 | "MIT"
426 | ],
427 | "authors": [
428 | {
429 | "name": "Mike van Riel",
430 | "email": "me@mikevanriel.com"
431 | },
432 | {
433 | "name": "Jaap van Otterdijk",
434 | "email": "account@ijaap.nl"
435 | }
436 | ],
437 | "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
438 | "time": "2020-09-03T19:13:55+00:00"
439 | },
440 | {
441 | "name": "phpdocumentor/type-resolver",
442 | "version": "1.4.0",
443 | "source": {
444 | "type": "git",
445 | "url": "https://github.com/phpDocumentor/TypeResolver.git",
446 | "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0"
447 | },
448 | "dist": {
449 | "type": "zip",
450 | "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0",
451 | "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0",
452 | "shasum": ""
453 | },
454 | "require": {
455 | "php": "^7.2 || ^8.0",
456 | "phpdocumentor/reflection-common": "^2.0"
457 | },
458 | "require-dev": {
459 | "ext-tokenizer": "*"
460 | },
461 | "type": "library",
462 | "extra": {
463 | "branch-alias": {
464 | "dev-1.x": "1.x-dev"
465 | }
466 | },
467 | "autoload": {
468 | "psr-4": {
469 | "phpDocumentor\\Reflection\\": "src"
470 | }
471 | },
472 | "notification-url": "https://packagist.org/downloads/",
473 | "license": [
474 | "MIT"
475 | ],
476 | "authors": [
477 | {
478 | "name": "Mike van Riel",
479 | "email": "me@mikevanriel.com"
480 | }
481 | ],
482 | "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
483 | "time": "2020-09-17T18:55:26+00:00"
484 | },
485 | {
486 | "name": "phpspec/prophecy",
487 | "version": "v1.10.3",
488 | "source": {
489 | "type": "git",
490 | "url": "https://github.com/phpspec/prophecy.git",
491 | "reference": "451c3cd1418cf640de218914901e51b064abb093"
492 | },
493 | "dist": {
494 | "type": "zip",
495 | "url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093",
496 | "reference": "451c3cd1418cf640de218914901e51b064abb093",
497 | "shasum": ""
498 | },
499 | "require": {
500 | "doctrine/instantiator": "^1.0.2",
501 | "php": "^5.3|^7.0",
502 | "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0",
503 | "sebastian/comparator": "^1.2.3|^2.0|^3.0|^4.0",
504 | "sebastian/recursion-context": "^1.0|^2.0|^3.0|^4.0"
505 | },
506 | "require-dev": {
507 | "phpspec/phpspec": "^2.5 || ^3.2",
508 | "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1"
509 | },
510 | "type": "library",
511 | "extra": {
512 | "branch-alias": {
513 | "dev-master": "1.10.x-dev"
514 | }
515 | },
516 | "autoload": {
517 | "psr-4": {
518 | "Prophecy\\": "src/Prophecy"
519 | }
520 | },
521 | "notification-url": "https://packagist.org/downloads/",
522 | "license": [
523 | "MIT"
524 | ],
525 | "authors": [
526 | {
527 | "name": "Konstantin Kudryashov",
528 | "email": "ever.zet@gmail.com",
529 | "homepage": "http://everzet.com"
530 | },
531 | {
532 | "name": "Marcello Duarte",
533 | "email": "marcello.duarte@gmail.com"
534 | }
535 | ],
536 | "description": "Highly opinionated mocking framework for PHP 5.3+",
537 | "homepage": "https://github.com/phpspec/prophecy",
538 | "keywords": [
539 | "Double",
540 | "Dummy",
541 | "fake",
542 | "mock",
543 | "spy",
544 | "stub"
545 | ],
546 | "time": "2020-03-05T15:02:03+00:00"
547 | },
548 | {
549 | "name": "phpunit/php-code-coverage",
550 | "version": "5.3.2",
551 | "source": {
552 | "type": "git",
553 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
554 | "reference": "c89677919c5dd6d3b3852f230a663118762218ac"
555 | },
556 | "dist": {
557 | "type": "zip",
558 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c89677919c5dd6d3b3852f230a663118762218ac",
559 | "reference": "c89677919c5dd6d3b3852f230a663118762218ac",
560 | "shasum": ""
561 | },
562 | "require": {
563 | "ext-dom": "*",
564 | "ext-xmlwriter": "*",
565 | "php": "^7.0",
566 | "phpunit/php-file-iterator": "^1.4.2",
567 | "phpunit/php-text-template": "^1.2.1",
568 | "phpunit/php-token-stream": "^2.0.1",
569 | "sebastian/code-unit-reverse-lookup": "^1.0.1",
570 | "sebastian/environment": "^3.0",
571 | "sebastian/version": "^2.0.1",
572 | "theseer/tokenizer": "^1.1"
573 | },
574 | "require-dev": {
575 | "phpunit/phpunit": "^6.0"
576 | },
577 | "suggest": {
578 | "ext-xdebug": "^2.5.5"
579 | },
580 | "type": "library",
581 | "extra": {
582 | "branch-alias": {
583 | "dev-master": "5.3.x-dev"
584 | }
585 | },
586 | "autoload": {
587 | "classmap": [
588 | "src/"
589 | ]
590 | },
591 | "notification-url": "https://packagist.org/downloads/",
592 | "license": [
593 | "BSD-3-Clause"
594 | ],
595 | "authors": [
596 | {
597 | "name": "Sebastian Bergmann",
598 | "email": "sebastian@phpunit.de",
599 | "role": "lead"
600 | }
601 | ],
602 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
603 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
604 | "keywords": [
605 | "coverage",
606 | "testing",
607 | "xunit"
608 | ],
609 | "time": "2018-04-06T15:36:58+00:00"
610 | },
611 | {
612 | "name": "phpunit/php-file-iterator",
613 | "version": "1.4.5",
614 | "source": {
615 | "type": "git",
616 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
617 | "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4"
618 | },
619 | "dist": {
620 | "type": "zip",
621 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4",
622 | "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4",
623 | "shasum": ""
624 | },
625 | "require": {
626 | "php": ">=5.3.3"
627 | },
628 | "type": "library",
629 | "extra": {
630 | "branch-alias": {
631 | "dev-master": "1.4.x-dev"
632 | }
633 | },
634 | "autoload": {
635 | "classmap": [
636 | "src/"
637 | ]
638 | },
639 | "notification-url": "https://packagist.org/downloads/",
640 | "license": [
641 | "BSD-3-Clause"
642 | ],
643 | "authors": [
644 | {
645 | "name": "Sebastian Bergmann",
646 | "email": "sb@sebastian-bergmann.de",
647 | "role": "lead"
648 | }
649 | ],
650 | "description": "FilterIterator implementation that filters files based on a list of suffixes.",
651 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
652 | "keywords": [
653 | "filesystem",
654 | "iterator"
655 | ],
656 | "time": "2017-11-27T13:52:08+00:00"
657 | },
658 | {
659 | "name": "phpunit/php-text-template",
660 | "version": "1.2.1",
661 | "source": {
662 | "type": "git",
663 | "url": "https://github.com/sebastianbergmann/php-text-template.git",
664 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686"
665 | },
666 | "dist": {
667 | "type": "zip",
668 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
669 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
670 | "shasum": ""
671 | },
672 | "require": {
673 | "php": ">=5.3.3"
674 | },
675 | "type": "library",
676 | "autoload": {
677 | "classmap": [
678 | "src/"
679 | ]
680 | },
681 | "notification-url": "https://packagist.org/downloads/",
682 | "license": [
683 | "BSD-3-Clause"
684 | ],
685 | "authors": [
686 | {
687 | "name": "Sebastian Bergmann",
688 | "email": "sebastian@phpunit.de",
689 | "role": "lead"
690 | }
691 | ],
692 | "description": "Simple template engine.",
693 | "homepage": "https://github.com/sebastianbergmann/php-text-template/",
694 | "keywords": [
695 | "template"
696 | ],
697 | "time": "2015-06-21T13:50:34+00:00"
698 | },
699 | {
700 | "name": "phpunit/php-timer",
701 | "version": "1.0.9",
702 | "source": {
703 | "type": "git",
704 | "url": "https://github.com/sebastianbergmann/php-timer.git",
705 | "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f"
706 | },
707 | "dist": {
708 | "type": "zip",
709 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f",
710 | "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f",
711 | "shasum": ""
712 | },
713 | "require": {
714 | "php": "^5.3.3 || ^7.0"
715 | },
716 | "require-dev": {
717 | "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0"
718 | },
719 | "type": "library",
720 | "extra": {
721 | "branch-alias": {
722 | "dev-master": "1.0-dev"
723 | }
724 | },
725 | "autoload": {
726 | "classmap": [
727 | "src/"
728 | ]
729 | },
730 | "notification-url": "https://packagist.org/downloads/",
731 | "license": [
732 | "BSD-3-Clause"
733 | ],
734 | "authors": [
735 | {
736 | "name": "Sebastian Bergmann",
737 | "email": "sb@sebastian-bergmann.de",
738 | "role": "lead"
739 | }
740 | ],
741 | "description": "Utility class for timing",
742 | "homepage": "https://github.com/sebastianbergmann/php-timer/",
743 | "keywords": [
744 | "timer"
745 | ],
746 | "time": "2017-02-26T11:10:40+00:00"
747 | },
748 | {
749 | "name": "phpunit/php-token-stream",
750 | "version": "2.0.2",
751 | "source": {
752 | "type": "git",
753 | "url": "https://github.com/sebastianbergmann/php-token-stream.git",
754 | "reference": "791198a2c6254db10131eecfe8c06670700904db"
755 | },
756 | "dist": {
757 | "type": "zip",
758 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/791198a2c6254db10131eecfe8c06670700904db",
759 | "reference": "791198a2c6254db10131eecfe8c06670700904db",
760 | "shasum": ""
761 | },
762 | "require": {
763 | "ext-tokenizer": "*",
764 | "php": "^7.0"
765 | },
766 | "require-dev": {
767 | "phpunit/phpunit": "^6.2.4"
768 | },
769 | "type": "library",
770 | "extra": {
771 | "branch-alias": {
772 | "dev-master": "2.0-dev"
773 | }
774 | },
775 | "autoload": {
776 | "classmap": [
777 | "src/"
778 | ]
779 | },
780 | "notification-url": "https://packagist.org/downloads/",
781 | "license": [
782 | "BSD-3-Clause"
783 | ],
784 | "authors": [
785 | {
786 | "name": "Sebastian Bergmann",
787 | "email": "sebastian@phpunit.de"
788 | }
789 | ],
790 | "description": "Wrapper around PHP's tokenizer extension.",
791 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/",
792 | "keywords": [
793 | "tokenizer"
794 | ],
795 | "abandoned": true,
796 | "time": "2017-11-27T05:48:46+00:00"
797 | },
798 | {
799 | "name": "phpunit/phpunit",
800 | "version": "6.5.14",
801 | "source": {
802 | "type": "git",
803 | "url": "https://github.com/sebastianbergmann/phpunit.git",
804 | "reference": "bac23fe7ff13dbdb461481f706f0e9fe746334b7"
805 | },
806 | "dist": {
807 | "type": "zip",
808 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/bac23fe7ff13dbdb461481f706f0e9fe746334b7",
809 | "reference": "bac23fe7ff13dbdb461481f706f0e9fe746334b7",
810 | "shasum": ""
811 | },
812 | "require": {
813 | "ext-dom": "*",
814 | "ext-json": "*",
815 | "ext-libxml": "*",
816 | "ext-mbstring": "*",
817 | "ext-xml": "*",
818 | "myclabs/deep-copy": "^1.6.1",
819 | "phar-io/manifest": "^1.0.1",
820 | "phar-io/version": "^1.0",
821 | "php": "^7.0",
822 | "phpspec/prophecy": "^1.7",
823 | "phpunit/php-code-coverage": "^5.3",
824 | "phpunit/php-file-iterator": "^1.4.3",
825 | "phpunit/php-text-template": "^1.2.1",
826 | "phpunit/php-timer": "^1.0.9",
827 | "phpunit/phpunit-mock-objects": "^5.0.9",
828 | "sebastian/comparator": "^2.1",
829 | "sebastian/diff": "^2.0",
830 | "sebastian/environment": "^3.1",
831 | "sebastian/exporter": "^3.1",
832 | "sebastian/global-state": "^2.0",
833 | "sebastian/object-enumerator": "^3.0.3",
834 | "sebastian/resource-operations": "^1.0",
835 | "sebastian/version": "^2.0.1"
836 | },
837 | "conflict": {
838 | "phpdocumentor/reflection-docblock": "3.0.2",
839 | "phpunit/dbunit": "<3.0"
840 | },
841 | "require-dev": {
842 | "ext-pdo": "*"
843 | },
844 | "suggest": {
845 | "ext-xdebug": "*",
846 | "phpunit/php-invoker": "^1.1"
847 | },
848 | "bin": [
849 | "phpunit"
850 | ],
851 | "type": "library",
852 | "extra": {
853 | "branch-alias": {
854 | "dev-master": "6.5.x-dev"
855 | }
856 | },
857 | "autoload": {
858 | "classmap": [
859 | "src/"
860 | ]
861 | },
862 | "notification-url": "https://packagist.org/downloads/",
863 | "license": [
864 | "BSD-3-Clause"
865 | ],
866 | "authors": [
867 | {
868 | "name": "Sebastian Bergmann",
869 | "email": "sebastian@phpunit.de",
870 | "role": "lead"
871 | }
872 | ],
873 | "description": "The PHP Unit Testing framework.",
874 | "homepage": "https://phpunit.de/",
875 | "keywords": [
876 | "phpunit",
877 | "testing",
878 | "xunit"
879 | ],
880 | "time": "2019-02-01T05:22:47+00:00"
881 | },
882 | {
883 | "name": "phpunit/phpunit-mock-objects",
884 | "version": "5.0.10",
885 | "source": {
886 | "type": "git",
887 | "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git",
888 | "reference": "cd1cf05c553ecfec36b170070573e540b67d3f1f"
889 | },
890 | "dist": {
891 | "type": "zip",
892 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/cd1cf05c553ecfec36b170070573e540b67d3f1f",
893 | "reference": "cd1cf05c553ecfec36b170070573e540b67d3f1f",
894 | "shasum": ""
895 | },
896 | "require": {
897 | "doctrine/instantiator": "^1.0.5",
898 | "php": "^7.0",
899 | "phpunit/php-text-template": "^1.2.1",
900 | "sebastian/exporter": "^3.1"
901 | },
902 | "conflict": {
903 | "phpunit/phpunit": "<6.0"
904 | },
905 | "require-dev": {
906 | "phpunit/phpunit": "^6.5.11"
907 | },
908 | "suggest": {
909 | "ext-soap": "*"
910 | },
911 | "type": "library",
912 | "extra": {
913 | "branch-alias": {
914 | "dev-master": "5.0.x-dev"
915 | }
916 | },
917 | "autoload": {
918 | "classmap": [
919 | "src/"
920 | ]
921 | },
922 | "notification-url": "https://packagist.org/downloads/",
923 | "license": [
924 | "BSD-3-Clause"
925 | ],
926 | "authors": [
927 | {
928 | "name": "Sebastian Bergmann",
929 | "email": "sebastian@phpunit.de",
930 | "role": "lead"
931 | }
932 | ],
933 | "description": "Mock Object library for PHPUnit",
934 | "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/",
935 | "keywords": [
936 | "mock",
937 | "xunit"
938 | ],
939 | "abandoned": true,
940 | "time": "2018-08-09T05:50:03+00:00"
941 | },
942 | {
943 | "name": "sebastian/code-unit-reverse-lookup",
944 | "version": "1.0.1",
945 | "source": {
946 | "type": "git",
947 | "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
948 | "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18"
949 | },
950 | "dist": {
951 | "type": "zip",
952 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18",
953 | "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18",
954 | "shasum": ""
955 | },
956 | "require": {
957 | "php": "^5.6 || ^7.0"
958 | },
959 | "require-dev": {
960 | "phpunit/phpunit": "^5.7 || ^6.0"
961 | },
962 | "type": "library",
963 | "extra": {
964 | "branch-alias": {
965 | "dev-master": "1.0.x-dev"
966 | }
967 | },
968 | "autoload": {
969 | "classmap": [
970 | "src/"
971 | ]
972 | },
973 | "notification-url": "https://packagist.org/downloads/",
974 | "license": [
975 | "BSD-3-Clause"
976 | ],
977 | "authors": [
978 | {
979 | "name": "Sebastian Bergmann",
980 | "email": "sebastian@phpunit.de"
981 | }
982 | ],
983 | "description": "Looks up which function or method a line of code belongs to",
984 | "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
985 | "time": "2017-03-04T06:30:41+00:00"
986 | },
987 | {
988 | "name": "sebastian/comparator",
989 | "version": "2.1.3",
990 | "source": {
991 | "type": "git",
992 | "url": "https://github.com/sebastianbergmann/comparator.git",
993 | "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9"
994 | },
995 | "dist": {
996 | "type": "zip",
997 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/34369daee48eafb2651bea869b4b15d75ccc35f9",
998 | "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9",
999 | "shasum": ""
1000 | },
1001 | "require": {
1002 | "php": "^7.0",
1003 | "sebastian/diff": "^2.0 || ^3.0",
1004 | "sebastian/exporter": "^3.1"
1005 | },
1006 | "require-dev": {
1007 | "phpunit/phpunit": "^6.4"
1008 | },
1009 | "type": "library",
1010 | "extra": {
1011 | "branch-alias": {
1012 | "dev-master": "2.1.x-dev"
1013 | }
1014 | },
1015 | "autoload": {
1016 | "classmap": [
1017 | "src/"
1018 | ]
1019 | },
1020 | "notification-url": "https://packagist.org/downloads/",
1021 | "license": [
1022 | "BSD-3-Clause"
1023 | ],
1024 | "authors": [
1025 | {
1026 | "name": "Jeff Welch",
1027 | "email": "whatthejeff@gmail.com"
1028 | },
1029 | {
1030 | "name": "Volker Dusch",
1031 | "email": "github@wallbash.com"
1032 | },
1033 | {
1034 | "name": "Bernhard Schussek",
1035 | "email": "bschussek@2bepublished.at"
1036 | },
1037 | {
1038 | "name": "Sebastian Bergmann",
1039 | "email": "sebastian@phpunit.de"
1040 | }
1041 | ],
1042 | "description": "Provides the functionality to compare PHP values for equality",
1043 | "homepage": "https://github.com/sebastianbergmann/comparator",
1044 | "keywords": [
1045 | "comparator",
1046 | "compare",
1047 | "equality"
1048 | ],
1049 | "time": "2018-02-01T13:46:46+00:00"
1050 | },
1051 | {
1052 | "name": "sebastian/diff",
1053 | "version": "2.0.1",
1054 | "source": {
1055 | "type": "git",
1056 | "url": "https://github.com/sebastianbergmann/diff.git",
1057 | "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd"
1058 | },
1059 | "dist": {
1060 | "type": "zip",
1061 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/347c1d8b49c5c3ee30c7040ea6fc446790e6bddd",
1062 | "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd",
1063 | "shasum": ""
1064 | },
1065 | "require": {
1066 | "php": "^7.0"
1067 | },
1068 | "require-dev": {
1069 | "phpunit/phpunit": "^6.2"
1070 | },
1071 | "type": "library",
1072 | "extra": {
1073 | "branch-alias": {
1074 | "dev-master": "2.0-dev"
1075 | }
1076 | },
1077 | "autoload": {
1078 | "classmap": [
1079 | "src/"
1080 | ]
1081 | },
1082 | "notification-url": "https://packagist.org/downloads/",
1083 | "license": [
1084 | "BSD-3-Clause"
1085 | ],
1086 | "authors": [
1087 | {
1088 | "name": "Kore Nordmann",
1089 | "email": "mail@kore-nordmann.de"
1090 | },
1091 | {
1092 | "name": "Sebastian Bergmann",
1093 | "email": "sebastian@phpunit.de"
1094 | }
1095 | ],
1096 | "description": "Diff implementation",
1097 | "homepage": "https://github.com/sebastianbergmann/diff",
1098 | "keywords": [
1099 | "diff"
1100 | ],
1101 | "time": "2017-08-03T08:09:46+00:00"
1102 | },
1103 | {
1104 | "name": "sebastian/environment",
1105 | "version": "3.1.0",
1106 | "source": {
1107 | "type": "git",
1108 | "url": "https://github.com/sebastianbergmann/environment.git",
1109 | "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5"
1110 | },
1111 | "dist": {
1112 | "type": "zip",
1113 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/cd0871b3975fb7fc44d11314fd1ee20925fce4f5",
1114 | "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5",
1115 | "shasum": ""
1116 | },
1117 | "require": {
1118 | "php": "^7.0"
1119 | },
1120 | "require-dev": {
1121 | "phpunit/phpunit": "^6.1"
1122 | },
1123 | "type": "library",
1124 | "extra": {
1125 | "branch-alias": {
1126 | "dev-master": "3.1.x-dev"
1127 | }
1128 | },
1129 | "autoload": {
1130 | "classmap": [
1131 | "src/"
1132 | ]
1133 | },
1134 | "notification-url": "https://packagist.org/downloads/",
1135 | "license": [
1136 | "BSD-3-Clause"
1137 | ],
1138 | "authors": [
1139 | {
1140 | "name": "Sebastian Bergmann",
1141 | "email": "sebastian@phpunit.de"
1142 | }
1143 | ],
1144 | "description": "Provides functionality to handle HHVM/PHP environments",
1145 | "homepage": "http://www.github.com/sebastianbergmann/environment",
1146 | "keywords": [
1147 | "Xdebug",
1148 | "environment",
1149 | "hhvm"
1150 | ],
1151 | "time": "2017-07-01T08:51:00+00:00"
1152 | },
1153 | {
1154 | "name": "sebastian/exporter",
1155 | "version": "3.1.2",
1156 | "source": {
1157 | "type": "git",
1158 | "url": "https://github.com/sebastianbergmann/exporter.git",
1159 | "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e"
1160 | },
1161 | "dist": {
1162 | "type": "zip",
1163 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/68609e1261d215ea5b21b7987539cbfbe156ec3e",
1164 | "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e",
1165 | "shasum": ""
1166 | },
1167 | "require": {
1168 | "php": "^7.0",
1169 | "sebastian/recursion-context": "^3.0"
1170 | },
1171 | "require-dev": {
1172 | "ext-mbstring": "*",
1173 | "phpunit/phpunit": "^6.0"
1174 | },
1175 | "type": "library",
1176 | "extra": {
1177 | "branch-alias": {
1178 | "dev-master": "3.1.x-dev"
1179 | }
1180 | },
1181 | "autoload": {
1182 | "classmap": [
1183 | "src/"
1184 | ]
1185 | },
1186 | "notification-url": "https://packagist.org/downloads/",
1187 | "license": [
1188 | "BSD-3-Clause"
1189 | ],
1190 | "authors": [
1191 | {
1192 | "name": "Sebastian Bergmann",
1193 | "email": "sebastian@phpunit.de"
1194 | },
1195 | {
1196 | "name": "Jeff Welch",
1197 | "email": "whatthejeff@gmail.com"
1198 | },
1199 | {
1200 | "name": "Volker Dusch",
1201 | "email": "github@wallbash.com"
1202 | },
1203 | {
1204 | "name": "Adam Harvey",
1205 | "email": "aharvey@php.net"
1206 | },
1207 | {
1208 | "name": "Bernhard Schussek",
1209 | "email": "bschussek@gmail.com"
1210 | }
1211 | ],
1212 | "description": "Provides the functionality to export PHP variables for visualization",
1213 | "homepage": "http://www.github.com/sebastianbergmann/exporter",
1214 | "keywords": [
1215 | "export",
1216 | "exporter"
1217 | ],
1218 | "time": "2019-09-14T09:02:43+00:00"
1219 | },
1220 | {
1221 | "name": "sebastian/global-state",
1222 | "version": "2.0.0",
1223 | "source": {
1224 | "type": "git",
1225 | "url": "https://github.com/sebastianbergmann/global-state.git",
1226 | "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4"
1227 | },
1228 | "dist": {
1229 | "type": "zip",
1230 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4",
1231 | "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4",
1232 | "shasum": ""
1233 | },
1234 | "require": {
1235 | "php": "^7.0"
1236 | },
1237 | "require-dev": {
1238 | "phpunit/phpunit": "^6.0"
1239 | },
1240 | "suggest": {
1241 | "ext-uopz": "*"
1242 | },
1243 | "type": "library",
1244 | "extra": {
1245 | "branch-alias": {
1246 | "dev-master": "2.0-dev"
1247 | }
1248 | },
1249 | "autoload": {
1250 | "classmap": [
1251 | "src/"
1252 | ]
1253 | },
1254 | "notification-url": "https://packagist.org/downloads/",
1255 | "license": [
1256 | "BSD-3-Clause"
1257 | ],
1258 | "authors": [
1259 | {
1260 | "name": "Sebastian Bergmann",
1261 | "email": "sebastian@phpunit.de"
1262 | }
1263 | ],
1264 | "description": "Snapshotting of global state",
1265 | "homepage": "http://www.github.com/sebastianbergmann/global-state",
1266 | "keywords": [
1267 | "global state"
1268 | ],
1269 | "time": "2017-04-27T15:39:26+00:00"
1270 | },
1271 | {
1272 | "name": "sebastian/object-enumerator",
1273 | "version": "3.0.3",
1274 | "source": {
1275 | "type": "git",
1276 | "url": "https://github.com/sebastianbergmann/object-enumerator.git",
1277 | "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5"
1278 | },
1279 | "dist": {
1280 | "type": "zip",
1281 | "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5",
1282 | "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5",
1283 | "shasum": ""
1284 | },
1285 | "require": {
1286 | "php": "^7.0",
1287 | "sebastian/object-reflector": "^1.1.1",
1288 | "sebastian/recursion-context": "^3.0"
1289 | },
1290 | "require-dev": {
1291 | "phpunit/phpunit": "^6.0"
1292 | },
1293 | "type": "library",
1294 | "extra": {
1295 | "branch-alias": {
1296 | "dev-master": "3.0.x-dev"
1297 | }
1298 | },
1299 | "autoload": {
1300 | "classmap": [
1301 | "src/"
1302 | ]
1303 | },
1304 | "notification-url": "https://packagist.org/downloads/",
1305 | "license": [
1306 | "BSD-3-Clause"
1307 | ],
1308 | "authors": [
1309 | {
1310 | "name": "Sebastian Bergmann",
1311 | "email": "sebastian@phpunit.de"
1312 | }
1313 | ],
1314 | "description": "Traverses array structures and object graphs to enumerate all referenced objects",
1315 | "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
1316 | "time": "2017-08-03T12:35:26+00:00"
1317 | },
1318 | {
1319 | "name": "sebastian/object-reflector",
1320 | "version": "1.1.1",
1321 | "source": {
1322 | "type": "git",
1323 | "url": "https://github.com/sebastianbergmann/object-reflector.git",
1324 | "reference": "773f97c67f28de00d397be301821b06708fca0be"
1325 | },
1326 | "dist": {
1327 | "type": "zip",
1328 | "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be",
1329 | "reference": "773f97c67f28de00d397be301821b06708fca0be",
1330 | "shasum": ""
1331 | },
1332 | "require": {
1333 | "php": "^7.0"
1334 | },
1335 | "require-dev": {
1336 | "phpunit/phpunit": "^6.0"
1337 | },
1338 | "type": "library",
1339 | "extra": {
1340 | "branch-alias": {
1341 | "dev-master": "1.1-dev"
1342 | }
1343 | },
1344 | "autoload": {
1345 | "classmap": [
1346 | "src/"
1347 | ]
1348 | },
1349 | "notification-url": "https://packagist.org/downloads/",
1350 | "license": [
1351 | "BSD-3-Clause"
1352 | ],
1353 | "authors": [
1354 | {
1355 | "name": "Sebastian Bergmann",
1356 | "email": "sebastian@phpunit.de"
1357 | }
1358 | ],
1359 | "description": "Allows reflection of object attributes, including inherited and non-public ones",
1360 | "homepage": "https://github.com/sebastianbergmann/object-reflector/",
1361 | "time": "2017-03-29T09:07:27+00:00"
1362 | },
1363 | {
1364 | "name": "sebastian/recursion-context",
1365 | "version": "3.0.0",
1366 | "source": {
1367 | "type": "git",
1368 | "url": "https://github.com/sebastianbergmann/recursion-context.git",
1369 | "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8"
1370 | },
1371 | "dist": {
1372 | "type": "zip",
1373 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8",
1374 | "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8",
1375 | "shasum": ""
1376 | },
1377 | "require": {
1378 | "php": "^7.0"
1379 | },
1380 | "require-dev": {
1381 | "phpunit/phpunit": "^6.0"
1382 | },
1383 | "type": "library",
1384 | "extra": {
1385 | "branch-alias": {
1386 | "dev-master": "3.0.x-dev"
1387 | }
1388 | },
1389 | "autoload": {
1390 | "classmap": [
1391 | "src/"
1392 | ]
1393 | },
1394 | "notification-url": "https://packagist.org/downloads/",
1395 | "license": [
1396 | "BSD-3-Clause"
1397 | ],
1398 | "authors": [
1399 | {
1400 | "name": "Jeff Welch",
1401 | "email": "whatthejeff@gmail.com"
1402 | },
1403 | {
1404 | "name": "Sebastian Bergmann",
1405 | "email": "sebastian@phpunit.de"
1406 | },
1407 | {
1408 | "name": "Adam Harvey",
1409 | "email": "aharvey@php.net"
1410 | }
1411 | ],
1412 | "description": "Provides functionality to recursively process PHP variables",
1413 | "homepage": "http://www.github.com/sebastianbergmann/recursion-context",
1414 | "time": "2017-03-03T06:23:57+00:00"
1415 | },
1416 | {
1417 | "name": "sebastian/resource-operations",
1418 | "version": "1.0.0",
1419 | "source": {
1420 | "type": "git",
1421 | "url": "https://github.com/sebastianbergmann/resource-operations.git",
1422 | "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52"
1423 | },
1424 | "dist": {
1425 | "type": "zip",
1426 | "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52",
1427 | "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52",
1428 | "shasum": ""
1429 | },
1430 | "require": {
1431 | "php": ">=5.6.0"
1432 | },
1433 | "type": "library",
1434 | "extra": {
1435 | "branch-alias": {
1436 | "dev-master": "1.0.x-dev"
1437 | }
1438 | },
1439 | "autoload": {
1440 | "classmap": [
1441 | "src/"
1442 | ]
1443 | },
1444 | "notification-url": "https://packagist.org/downloads/",
1445 | "license": [
1446 | "BSD-3-Clause"
1447 | ],
1448 | "authors": [
1449 | {
1450 | "name": "Sebastian Bergmann",
1451 | "email": "sebastian@phpunit.de"
1452 | }
1453 | ],
1454 | "description": "Provides a list of PHP built-in functions that operate on resources",
1455 | "homepage": "https://www.github.com/sebastianbergmann/resource-operations",
1456 | "time": "2015-07-28T20:34:47+00:00"
1457 | },
1458 | {
1459 | "name": "sebastian/version",
1460 | "version": "2.0.1",
1461 | "source": {
1462 | "type": "git",
1463 | "url": "https://github.com/sebastianbergmann/version.git",
1464 | "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019"
1465 | },
1466 | "dist": {
1467 | "type": "zip",
1468 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019",
1469 | "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019",
1470 | "shasum": ""
1471 | },
1472 | "require": {
1473 | "php": ">=5.6"
1474 | },
1475 | "type": "library",
1476 | "extra": {
1477 | "branch-alias": {
1478 | "dev-master": "2.0.x-dev"
1479 | }
1480 | },
1481 | "autoload": {
1482 | "classmap": [
1483 | "src/"
1484 | ]
1485 | },
1486 | "notification-url": "https://packagist.org/downloads/",
1487 | "license": [
1488 | "BSD-3-Clause"
1489 | ],
1490 | "authors": [
1491 | {
1492 | "name": "Sebastian Bergmann",
1493 | "email": "sebastian@phpunit.de",
1494 | "role": "lead"
1495 | }
1496 | ],
1497 | "description": "Library that helps with managing the version number of Git-hosted PHP projects",
1498 | "homepage": "https://github.com/sebastianbergmann/version",
1499 | "time": "2016-10-03T07:35:21+00:00"
1500 | },
1501 | {
1502 | "name": "squizlabs/php_codesniffer",
1503 | "version": "3.5.6",
1504 | "source": {
1505 | "type": "git",
1506 | "url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
1507 | "reference": "e97627871a7eab2f70e59166072a6b767d5834e0"
1508 | },
1509 | "dist": {
1510 | "type": "zip",
1511 | "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/e97627871a7eab2f70e59166072a6b767d5834e0",
1512 | "reference": "e97627871a7eab2f70e59166072a6b767d5834e0",
1513 | "shasum": ""
1514 | },
1515 | "require": {
1516 | "ext-simplexml": "*",
1517 | "ext-tokenizer": "*",
1518 | "ext-xmlwriter": "*",
1519 | "php": ">=5.4.0"
1520 | },
1521 | "require-dev": {
1522 | "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0"
1523 | },
1524 | "bin": [
1525 | "bin/phpcs",
1526 | "bin/phpcbf"
1527 | ],
1528 | "type": "library",
1529 | "extra": {
1530 | "branch-alias": {
1531 | "dev-master": "3.x-dev"
1532 | }
1533 | },
1534 | "notification-url": "https://packagist.org/downloads/",
1535 | "license": [
1536 | "BSD-3-Clause"
1537 | ],
1538 | "authors": [
1539 | {
1540 | "name": "Greg Sherwood",
1541 | "role": "lead"
1542 | }
1543 | ],
1544 | "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
1545 | "homepage": "https://github.com/squizlabs/PHP_CodeSniffer",
1546 | "keywords": [
1547 | "phpcs",
1548 | "standards"
1549 | ],
1550 | "time": "2020-08-10T04:50:15+00:00"
1551 | },
1552 | {
1553 | "name": "symfony/polyfill-ctype",
1554 | "version": "v1.18.1",
1555 | "source": {
1556 | "type": "git",
1557 | "url": "https://github.com/symfony/polyfill-ctype.git",
1558 | "reference": "1c302646f6efc070cd46856e600e5e0684d6b454"
1559 | },
1560 | "dist": {
1561 | "type": "zip",
1562 | "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/1c302646f6efc070cd46856e600e5e0684d6b454",
1563 | "reference": "1c302646f6efc070cd46856e600e5e0684d6b454",
1564 | "shasum": ""
1565 | },
1566 | "require": {
1567 | "php": ">=5.3.3"
1568 | },
1569 | "suggest": {
1570 | "ext-ctype": "For best performance"
1571 | },
1572 | "type": "library",
1573 | "extra": {
1574 | "branch-alias": {
1575 | "dev-master": "1.18-dev"
1576 | },
1577 | "thanks": {
1578 | "name": "symfony/polyfill",
1579 | "url": "https://github.com/symfony/polyfill"
1580 | }
1581 | },
1582 | "autoload": {
1583 | "psr-4": {
1584 | "Symfony\\Polyfill\\Ctype\\": ""
1585 | },
1586 | "files": [
1587 | "bootstrap.php"
1588 | ]
1589 | },
1590 | "notification-url": "https://packagist.org/downloads/",
1591 | "license": [
1592 | "MIT"
1593 | ],
1594 | "authors": [
1595 | {
1596 | "name": "Gert de Pagter",
1597 | "email": "BackEndTea@gmail.com"
1598 | },
1599 | {
1600 | "name": "Symfony Community",
1601 | "homepage": "https://symfony.com/contributors"
1602 | }
1603 | ],
1604 | "description": "Symfony polyfill for ctype functions",
1605 | "homepage": "https://symfony.com",
1606 | "keywords": [
1607 | "compatibility",
1608 | "ctype",
1609 | "polyfill",
1610 | "portable"
1611 | ],
1612 | "time": "2020-07-14T12:35:20+00:00"
1613 | },
1614 | {
1615 | "name": "theseer/tokenizer",
1616 | "version": "1.2.0",
1617 | "source": {
1618 | "type": "git",
1619 | "url": "https://github.com/theseer/tokenizer.git",
1620 | "reference": "75a63c33a8577608444246075ea0af0d052e452a"
1621 | },
1622 | "dist": {
1623 | "type": "zip",
1624 | "url": "https://api.github.com/repos/theseer/tokenizer/zipball/75a63c33a8577608444246075ea0af0d052e452a",
1625 | "reference": "75a63c33a8577608444246075ea0af0d052e452a",
1626 | "shasum": ""
1627 | },
1628 | "require": {
1629 | "ext-dom": "*",
1630 | "ext-tokenizer": "*",
1631 | "ext-xmlwriter": "*",
1632 | "php": "^7.2 || ^8.0"
1633 | },
1634 | "type": "library",
1635 | "autoload": {
1636 | "classmap": [
1637 | "src/"
1638 | ]
1639 | },
1640 | "notification-url": "https://packagist.org/downloads/",
1641 | "license": [
1642 | "BSD-3-Clause"
1643 | ],
1644 | "authors": [
1645 | {
1646 | "name": "Arne Blankerts",
1647 | "email": "arne@blankerts.de",
1648 | "role": "Developer"
1649 | }
1650 | ],
1651 | "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
1652 | "time": "2020-07-12T23:59:07+00:00"
1653 | },
1654 | {
1655 | "name": "webmozart/assert",
1656 | "version": "1.9.1",
1657 | "source": {
1658 | "type": "git",
1659 | "url": "https://github.com/webmozart/assert.git",
1660 | "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389"
1661 | },
1662 | "dist": {
1663 | "type": "zip",
1664 | "url": "https://api.github.com/repos/webmozart/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389",
1665 | "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389",
1666 | "shasum": ""
1667 | },
1668 | "require": {
1669 | "php": "^5.3.3 || ^7.0 || ^8.0",
1670 | "symfony/polyfill-ctype": "^1.8"
1671 | },
1672 | "conflict": {
1673 | "phpstan/phpstan": "<0.12.20",
1674 | "vimeo/psalm": "<3.9.1"
1675 | },
1676 | "require-dev": {
1677 | "phpunit/phpunit": "^4.8.36 || ^7.5.13"
1678 | },
1679 | "type": "library",
1680 | "autoload": {
1681 | "psr-4": {
1682 | "Webmozart\\Assert\\": "src/"
1683 | }
1684 | },
1685 | "notification-url": "https://packagist.org/downloads/",
1686 | "license": [
1687 | "MIT"
1688 | ],
1689 | "authors": [
1690 | {
1691 | "name": "Bernhard Schussek",
1692 | "email": "bschussek@gmail.com"
1693 | }
1694 | ],
1695 | "description": "Assertions to validate method input/output with nice error messages.",
1696 | "keywords": [
1697 | "assert",
1698 | "check",
1699 | "validate"
1700 | ],
1701 | "time": "2020-07-08T17:02:28+00:00"
1702 | },
1703 | {
1704 | "name": "wp-coding-standards/wpcs",
1705 | "version": "2.3.0",
1706 | "source": {
1707 | "type": "git",
1708 | "url": "https://github.com/WordPress/WordPress-Coding-Standards.git",
1709 | "reference": "7da1894633f168fe244afc6de00d141f27517b62"
1710 | },
1711 | "dist": {
1712 | "type": "zip",
1713 | "url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/7da1894633f168fe244afc6de00d141f27517b62",
1714 | "reference": "7da1894633f168fe244afc6de00d141f27517b62",
1715 | "shasum": ""
1716 | },
1717 | "require": {
1718 | "php": ">=5.4",
1719 | "squizlabs/php_codesniffer": "^3.3.1"
1720 | },
1721 | "require-dev": {
1722 | "dealerdirect/phpcodesniffer-composer-installer": "^0.5 || ^0.6",
1723 | "phpcompatibility/php-compatibility": "^9.0",
1724 | "phpcsstandards/phpcsdevtools": "^1.0",
1725 | "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0"
1726 | },
1727 | "suggest": {
1728 | "dealerdirect/phpcodesniffer-composer-installer": "^0.6 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically."
1729 | },
1730 | "type": "phpcodesniffer-standard",
1731 | "notification-url": "https://packagist.org/downloads/",
1732 | "license": [
1733 | "MIT"
1734 | ],
1735 | "authors": [
1736 | {
1737 | "name": "Contributors",
1738 | "homepage": "https://github.com/WordPress/WordPress-Coding-Standards/graphs/contributors"
1739 | }
1740 | ],
1741 | "description": "PHP_CodeSniffer rules (sniffs) to enforce WordPress coding conventions",
1742 | "keywords": [
1743 | "phpcs",
1744 | "standards",
1745 | "wordpress"
1746 | ],
1747 | "time": "2020-05-13T23:57:56+00:00"
1748 | }
1749 | ],
1750 | "aliases": [],
1751 | "minimum-stability": "dev",
1752 | "stability-flags": [],
1753 | "prefer-stable": true,
1754 | "prefer-lowest": false,
1755 | "platform": [],
1756 | "platform-dev": []
1757 | }
1758 |
--------------------------------------------------------------------------------