Added dependency plugins
This commit is contained in:
2
wordpress/wp-content/plugins/wp-graphql/.coveralls.yml
Executable file
2
wordpress/wp-content/plugins/wp-graphql/.coveralls.yml
Executable file
@@ -0,0 +1,2 @@
|
||||
service_name: travis-ci
|
||||
coverage_clover: tests/_output/coverage.xml
|
||||
3
wordpress/wp-content/plugins/wp-graphql/.dockerignore
Executable file
3
wordpress/wp-content/plugins/wp-graphql/.dockerignore
Executable file
@@ -0,0 +1,3 @@
|
||||
.git
|
||||
.idea
|
||||
|
||||
22
wordpress/wp-content/plugins/wp-graphql/.gitignore
vendored
Executable file
22
wordpress/wp-content/plugins/wp-graphql/.gitignore
vendored
Executable file
@@ -0,0 +1,22 @@
|
||||
.idea
|
||||
!vendor/
|
||||
vendor/*
|
||||
!vendor/webonyx/
|
||||
vendor/webonyx/*
|
||||
!vendor/webonyx/graphql-php/
|
||||
vendor/webonyx/graphql-php/*
|
||||
!vendor/webonyx/graphql-php/src/
|
||||
!vendor/ivome/
|
||||
vendor/ivome/*
|
||||
!vendor/ivome/graphql-relay-php/
|
||||
vendor/ivome/graphql-relay-php/*
|
||||
!vendor/ivome/graphql-relay-php/src/
|
||||
!vendor/autoload.php
|
||||
!vendor/composer
|
||||
vendor/composer/installed.json
|
||||
vendor/composer/*/
|
||||
!/tests
|
||||
/tests/*.suite.yml
|
||||
build/
|
||||
coverage/*
|
||||
schema.graphql
|
||||
101
wordpress/wp-content/plugins/wp-graphql/.travis.yml
Executable file
101
wordpress/wp-content/plugins/wp-graphql/.travis.yml
Executable file
@@ -0,0 +1,101 @@
|
||||
sudo: required
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
notifications:
|
||||
email:
|
||||
on_success: never
|
||||
on_failure: never
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- develop
|
||||
|
||||
cache:
|
||||
apt: true
|
||||
|
||||
# Set the global environment variables
|
||||
env:
|
||||
global:
|
||||
- COVERAGE: false
|
||||
matrix:
|
||||
- WP_MULTISITE=1 PHP_VERSION=5.6 WP_VERSION=4.7.1
|
||||
- WP_MULTISITE=1 PHP_VERSION=5.6 WP_VERSION=4.9.4
|
||||
- WP_MULTISITE=1 PHP_VERSION=7.0 WP_VERSION=4.7.1
|
||||
- WP_MULTISITE=1 PHP_VERSION=7.0 WP_VERSION=4.9.4
|
||||
- WP_MULTISITE=1 PHP_VERSION=7.1 WP_VERSION=4.7.1
|
||||
- WP_MULTISITE=1 PHP_VERSION=7.1 WP_VERSION=4.9.4
|
||||
- PHP_VERSION=5.6 WP_VERSION=4.7.1
|
||||
- PHP_VERSION=5.6 WP_VERSION=4.9.4
|
||||
- PHP_VERSION=7.0 WP_VERSION=4.7.1
|
||||
- PHP_VERSION=7.0 WP_VERSION=4.9.4
|
||||
- PHP_VERSION=7.1 WP_VERSION=4.7.1
|
||||
- PHP_VERSION=7.1 WP_VERSION=4.9.4 APIGEN_DOCS=true COVERAGE=true LINT_SCHEMA=true
|
||||
|
||||
install:
|
||||
- docker run --rm -v $(pwd):/app composer install --ignore-platform-reqs
|
||||
- docker-compose build
|
||||
|
||||
before_script:
|
||||
- docker-compose up -d
|
||||
- docker-compose run --rm tests ./bin/install-wp-tests.sh ignored root testing mysql_test $WP_VERSION true
|
||||
|
||||
script:
|
||||
- docker-compose run --rm tests ./vendor/bin/codecept run acceptance --env docker
|
||||
- docker-compose run --rm tests ./vendor/bin/codecept run functional --env docker
|
||||
- |
|
||||
if [ $COVERAGE == true ]; then
|
||||
docker-compose run --rm tests phpdbg -qrr ./vendor/bin/codecept run wpunit --env docker --coverage --coverage-xml
|
||||
else
|
||||
docker-compose run --rm tests ./vendor/bin/codecept run wpunit --env docker
|
||||
fi
|
||||
|
||||
after_success:
|
||||
# Install coveralls.phar
|
||||
# Upload coverage to coveralls
|
||||
- |
|
||||
if [ $COVERAGE == true ]; then
|
||||
wget -c -nc --retry-connrefused --tries=0 https://github.com/satooshi/php-coveralls/releases/download/v1.0.1/coveralls.phar
|
||||
chmod +x coveralls.phar
|
||||
php coveralls.phar --version
|
||||
travis_retry php coveralls.phar -v
|
||||
fi
|
||||
# Install GraphQL Schema Linter
|
||||
# Move to the WordPress Install
|
||||
# Generate the Static Schema
|
||||
# Lint the Schema
|
||||
- |
|
||||
if [ $LINT_SCHEMA == true ]; then
|
||||
npm install -g graphql-schema-linter
|
||||
cd $WP_CORE_DIR
|
||||
wp graphql generate-static-schema
|
||||
cd $WP_CORE_DIR/wp-content/plugins/wp-graphql
|
||||
graphql-schema-linter ./schema.graphql
|
||||
fi
|
||||
# Generate the APIGen Docs
|
||||
# - |
|
||||
# if [ $APIGEN_DOCS == true ]; then
|
||||
# cd $TRAVIS_BUILD_DIR
|
||||
# vendor/bin/apigen generate --source="src" --destination $TRAVIS_BUILD_DIR/apigen
|
||||
# fi
|
||||
|
||||
deploy:
|
||||
# Deploy the Generated API docs to the wp-graphql-api-docs repo
|
||||
- provider: pages
|
||||
# This is set in the Travis CI dashboard
|
||||
github_token: $APIGEN_GITHUB_TOKEN
|
||||
local_dir: $TRAVIS_BUILD_DIR/apigen
|
||||
repo: wp-graphql/wp-graphql-api-docs
|
||||
target_branch: master
|
||||
# Make sure this is true to ensure the files that were built in the previous step are not cleaned up before deploy
|
||||
skip_cleanup: true
|
||||
on:
|
||||
# Probably switch this to master?
|
||||
all_branches: develop
|
||||
# Only deploy when APIGEN_DOCS are generated
|
||||
condition: $APIGEN_DOCS == true
|
||||
|
||||
after_script:
|
||||
- docker-compose down
|
||||
46
wordpress/wp-content/plugins/wp-graphql/CODE_OF_CONDUCT.md
Executable file
46
wordpress/wp-content/plugins/wp-graphql/CODE_OF_CONDUCT.md
Executable file
@@ -0,0 +1,46 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at info@wpgraphql.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
||||
35
wordpress/wp-content/plugins/wp-graphql/CONTRIBUTING.md
Executable file
35
wordpress/wp-content/plugins/wp-graphql/CONTRIBUTING.md
Executable file
@@ -0,0 +1,35 @@
|
||||
## Contribute to WPGraphQL
|
||||
|
||||
WPGraphQL welcomes community contributions, bug reports and other constructive feedback.
|
||||
|
||||
When contributing please ensure you follow the guidelines below so that we can keep on top of things.
|
||||
|
||||
## Getting Started
|
||||
|
||||
* __Do not report potential security vulnerabilities here. Email them privately to our security team at
|
||||
[info@wpgraphql.com](mailto:info@wpgraphql.com)__
|
||||
* Before submitting a ticket, please be sure to replicate the behavior with no other plugins active and on a base theme like Twenty Seventeen.
|
||||
* Submit a ticket for your issue, assuming one does not already exist.
|
||||
* Raise it on our [Issue Tracker](https://github.com/wp-graphql/wp-graphql/issues)
|
||||
* Clearly describe the issue including steps to reproduce the bug.
|
||||
* Make sure you fill in the earliest version that you know has the issue as well as the version of WordPress you're using.
|
||||
|
||||
## Making Changes
|
||||
|
||||
* Fork the repository on GitHub
|
||||
* Make the changes to your forked repository
|
||||
* Ensure you stick to the [WordPress Coding Standards](https://codex.wordpress.org/WordPress_Coding_Standards)
|
||||
* When committing, reference your issue (if present) and include a note about the fix
|
||||
* If possible, and if applicable, please also add/update unit tests for your changes
|
||||
* Push the changes to your fork and submit a pull request to the 'develop' branch of this repository
|
||||
|
||||
## Code Documentation
|
||||
|
||||
* We strive for full doc coverage and follow the standards set by phpDoc
|
||||
* Please make sure that every function is documented so that when we update our API Documentation things don't go awry!
|
||||
* If you're adding/editing a function in a class, make sure to add `@access {private|public|protected}`
|
||||
* Finally, please use tabs and not spaces.
|
||||
|
||||
At this point you're waiting on us to merge your pull request. We'll review all pull requests, and make suggestions and changes if necessary.
|
||||
|
||||
> **NOTE:** This CONTRIBUTING.md file was forked from [Easy Digital Downloads](https://github.com/easydigitaldownloads/easy-digital-downloads/blob/master/CONTRIBUTING.md)
|
||||
16
wordpress/wp-content/plugins/wp-graphql/ISSUE_TEMPLATE.md
Executable file
16
wordpress/wp-content/plugins/wp-graphql/ISSUE_TEMPLATE.md
Executable file
@@ -0,0 +1,16 @@
|
||||
Remember, an issue is not the place to ask general questions. You can use [Slack](https://wp-graphql.slack.com) for that ([join here](https://wpgql-slack.herokuapp.com/)).
|
||||
|
||||
Before you open an issue, please check if a similar issue already exists or has been closed before.
|
||||
|
||||
### When reporting a _bug_, please be sure to include the following:
|
||||
- [ ] A descriptive title
|
||||
- [ ] An *isolated* way to reproduce the behavior (example: GitHub repository with code isolated to the issue that anyone can clone to observe the problem)
|
||||
- [ ] What version of `WPGraphQL` you're using, and the platform(s) you're running it on
|
||||
- [ ] What other plugins you're using
|
||||
- [ ] The behavior you expect to see, and the actual behavior
|
||||
|
||||
### When you open an _issue_ for a feature request, please add as much detail as possible:
|
||||
- [ ] A descriptive title
|
||||
- [ ] A description of the problem you're trying to solve, including *why* you think this is a problem
|
||||
- [ ] An overview of the suggested solution
|
||||
- [ ] If the feature changes current behavior, reasons why your solution is better
|
||||
674
wordpress/wp-content/plugins/wp-graphql/LICENSE
Executable file
674
wordpress/wp-content/plugins/wp-graphql/LICENSE
Executable file
@@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
{one line to give the program's name and a brief idea of what it does.}
|
||||
Copyright (C) {year} {name of author}
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
{project} Copyright (C) {year} {fullname}
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
33
wordpress/wp-content/plugins/wp-graphql/PULL_REQUEST_TEMPLATE.md
Executable file
33
wordpress/wp-content/plugins/wp-graphql/PULL_REQUEST_TEMPLATE.md
Executable file
@@ -0,0 +1,33 @@
|
||||
### Your checklist for this pull request
|
||||
Thanks for sending a pull request! Please make sure you click the link above to view the contribution guidelines, then fill out the blanks below.
|
||||
|
||||
🚨Please review the [guidelines for contributing](../CONTRIBUTING.md) to this repository.
|
||||
|
||||
- [ ] Make sure you are making a pull request against the **develop branch** (left side). Also you should start *your branch* off *our develop*.
|
||||
- [ ] Make sure you are requesting to pull request from a **topic/feature/bugfix branch** (right side). Don't pull request from your master!
|
||||
|
||||
What does this implement/fix? Explain your changes.
|
||||
---------------------------------------------------
|
||||
…
|
||||
|
||||
|
||||
Does this close any currently open issues?
|
||||
------------------------------------------
|
||||
…
|
||||
|
||||
|
||||
Any relevant logs, error output, GraphiQL screenshots, etc?
|
||||
-------------------------------------
|
||||
(If it’s long, please paste to https://ghostbin.com/ and insert the link here.)
|
||||
|
||||
|
||||
Any other comments?
|
||||
-------------------
|
||||
…
|
||||
|
||||
|
||||
Where has this been tested?
|
||||
---------------------------
|
||||
**Operating System:** …
|
||||
|
||||
**WordPress Version:** …
|
||||
229
wordpress/wp-content/plugins/wp-graphql/README.md
Executable file
229
wordpress/wp-content/plugins/wp-graphql/README.md
Executable file
@@ -0,0 +1,229 @@
|
||||

|
||||
|
||||
# WPGraphQL
|
||||
|
||||
<a href="https://www.wpgraphql.com" target="_blank">Website</a> • <a href="https://wpgraphql.com/docs/getting-started/about/" target="_blank">Docs</a> • <a href="https://wp-graphql.github.io/wp-graphql-api-docs/" target="_blank">ApiGen Code Docs</a> • <a href="https://wpgql-slack.herokuapp.com/" target="_blank">Slack</a>
|
||||
|
||||
GraphQL API for WordPress.
|
||||
|
||||
[](https://travis-ci.org/wp-graphql/wp-graphql)
|
||||
[](https://coveralls.io/github/wp-graphql/wp-graphql?branch=master)
|
||||
|
||||
------
|
||||
|
||||
## Quick Install
|
||||
Download and install like any WordPress plugin.
|
||||
[Details on Install and Activation](https://wpgraphql.com/docs/getting-started/install-and-activate/)
|
||||
|
||||
## Documentation
|
||||
|
||||
Documentation is being moved [here](https://wpgraphql.com/docs/getting-started/about), but some can still be found [on the Wiki](https://github.com/wp-graphql/wp-graphql/wiki) on this repository.
|
||||
|
||||
- Requires PHP 5.5+
|
||||
- Requires WordPress 4.7+
|
||||
|
||||
## Overview
|
||||
This plugin brings the power of GraphQL to WordPress.
|
||||
|
||||
<a href="https://graphql.org" target="_blank">GraphQL</a> is a query language spec that was open sourced by Facebook® in
|
||||
2015, and has been used in production by Facebook® since 2012.
|
||||
|
||||
GraphQL has some similarities to REST in that it exposes an HTTP endpoint where requests can be sent and a JSON response
|
||||
is returned. However, where REST has a different endpoint per resource, GraphQL has just a single endpoint and the
|
||||
data returned isn't implicit, but rather explicit and matches the shape of the request.
|
||||
|
||||
A REST API is implicit, meaning that the data coming back from an endpoint is implied. An endpoint such as `/posts/`
|
||||
implies that the data I will retrieve is data related to Post objects, but beyond that it's hard to know exactly what
|
||||
will be returned. It might be more data than I need or might not be the data I need at all.
|
||||
|
||||
GraphQL is explicit, meaning that you ask for the data you want and you get the data back in the same shape that it was
|
||||
asked for.
|
||||
|
||||
Additionally, where REST requires multiple HTTP requests for related data, GraphQL allows related data to be queried and
|
||||
retrieved in a single request, and again, in the same shape of the request without any worry of over or under-fetching
|
||||
data.
|
||||
|
||||
GraphQL also provides rich introspection, allowing for queries to be run to find out details about the Schema, which is
|
||||
how powerful dev tools, such as _GraphiQl_ have been able to be created.
|
||||
|
||||
## GraphiQL API Explorer
|
||||
_GraphiQL_ is a fantastic GraphQL API Explorer / IDE. There are various versions of _GraphiQL_
|
||||
that you can find, including a <a href="https://chrome.google.com/webstore/detail/chromeiql/fkkiamalmpiidkljmicmjfbieiclmeij?hl=en">Chrome Extension</a> but
|
||||
my recommendation is the _GraphiQL_ desktop app below:
|
||||
|
||||
- <a href="https://github.com/skevy/graphiql-app">Download the GraphiQL Desktop App</a>
|
||||
- Once the app is downloaded and installed, open the App.
|
||||
- Set the `GraphQL Endpoint` to `http://yoursite.com/graphql`. In order for the /graphql endpoint to work, you must have [pretty permalinks](https://codex.wordpress.org/Using_Permalinks/) enabled.
|
||||
- You should now be able to browse the GraphQL Schema via the "Docs" explorer
|
||||
at the top right.
|
||||
- On the left side, you can execute GraphQL Queries
|
||||
|
||||
<img src="https://github.com/wp-graphql/wp-graphql/blob/master/img/graphql-docs.gif?raw=true" alt="GraphiQL API Explorer">
|
||||
|
||||
## POSSIBLE BREAKING CHANGES
|
||||
Please note that as the plugin continues to take shape, there might be breaking changes at any point. Once the plugin reaches a stable 1.0.0 release, breaking changes should be minimized and communicated appropriately if they are required.
|
||||
|
||||
## Unit Testing and Code Coverage
|
||||
|
||||
Before anything is merged into the WPGraphQL code base it must pass all tests and have 100% code coverage.
|
||||
Travis-CI and Coveralls will check this when you create a pull request to the WPGraphQL repo.
|
||||
However, before that happens, you should ensure all of these requirements are met locally.
|
||||
The following will help you set up both testing and code coverage in your local environment.
|
||||
|
||||
### Prerequisites
|
||||
To run unit tests and code coverage during development you'll need the following:
|
||||
|
||||
* [Composer](https://getcomposer.org/doc/00-intro.md)
|
||||
* [php-coveralls](https://github.com/php-coveralls/php-coveralls)
|
||||
* `composer global require php-coveralls/php-coveralls`
|
||||
* [Xdebug](https://xdebug.org/docs/install)
|
||||
|
||||
### Test Database
|
||||
In order for tests to run, you need MySQL setup locally. The test suite will need 2 databases for testing.
|
||||
One named `wpgraphql_serve` and the other you can name yourself.
|
||||
You can keep these databases around if you like and the test suite will use the existing databases, or you can delete them when you're done testing and the test suite will
|
||||
re-install them as needed the next time you run the script to install the tests.
|
||||
|
||||
*NOTE*: You'll want the test database to be a true test database, not a database with valuable, existing information.
|
||||
The tests will create new data and clear out data, and you don't want to cause issues with a database you're actually using for projects.
|
||||
|
||||
### Installing the Test Suite
|
||||
To install the test suite/test databases, from the root of the plugin directory, in the command line run:
|
||||
|
||||
`bin/install-wp-tests.sh <db-name> <db-user> <db-pass> [db-host] [wp-version]`
|
||||
|
||||
For example:
|
||||
|
||||
`bin/install-wp-tests.sh wpgraphql_test root password 127.0.0.1 latest`
|
||||
|
||||
DEBUGGING: If you have run this command before in another branch you may already have a local copy of WordPress downloaded in your `/private/tmp` directory.
|
||||
If this is the case, please remove it and then run the install script again. Without removing this you may receive an error when running phpunit.
|
||||
|
||||
#### Local Environment Configuration for Codeception Tests
|
||||
|
||||
You may have different local environment configuration than what Travis CI has to run the tests, such as database username/password.
|
||||
|
||||
In the `/tests` directory you will find `*.suite.dist.yml` config files for each of the codeception test suites.
|
||||
|
||||
You can copy those files and remove the `.dist` from the filename, and that file will be loaded locally _before_ the `.dist` file.
|
||||
|
||||
For example, if you wanted to update the `dbName` or `dbPassword` for your local tests, you could copy `wpunit.suite.dist.yml` to `wpunit.suite.yml` and update the `dbName` or `dbPassword` value to reflect your local database and password.
|
||||
|
||||
This file is .gitignored, so it will remain in your local environment but will not be added to the repo when you submit pull requests.
|
||||
|
||||
### Running the Tests
|
||||
The tests are built on top of the Codeception testing framework.
|
||||
|
||||
To run the tests, after you've installed the test suite, as described above, you need to also install the `wp-browser`.
|
||||
|
||||
*@todo*: Make this easier than running all these steps, but for now this is what we've got to do.
|
||||
Perhaps someone who's more of a Composer expert could lend some advise?:
|
||||
|
||||
|
||||
- `rm -rf composer.lock vendor` to remove all composer dependencies and the composer lock file
|
||||
- `composer require lucatume/wp-browser --dev` to install the Codeception WordPress deps
|
||||
- `vendor/bin/codecept run` to run all the codeception tests
|
||||
- You can specify which tests to run like:
|
||||
- `vendor/bin/codecept run wpunit`
|
||||
- `vendor/bin/codecept run functional`
|
||||
- `vendor/bin/codecept run unit`
|
||||
- `vendor/bin/codecept run acceptance`
|
||||
|
||||
|
||||
### Testing in Docker
|
||||
|
||||
A `docker-compose` file in the root of this repo provides all of the testing prerequisites, allowing you to run
|
||||
tests in isolation without installing anything locally (besides Docker).
|
||||
|
||||
Install dependencies using Composer. Note the `--ignore-platform-reqs` which skips the checks for PHP extensions
|
||||
inside the barebones Docker container.
|
||||
|
||||
```
|
||||
docker run --rm -v $(pwd):/app composer install --ignore-platform-reqs
|
||||
```
|
||||
|
||||
Now you're ready to start the Docker containers: a PHP container to hold the code under test, a PHP/Apache
|
||||
container to serve WordPress, and a MariaDB container.
|
||||
|
||||
```
|
||||
docker-compose build
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
Your stack is starting! If this is your first time running that command, it will take a few minutes to download the
|
||||
referenced Docker images. Note that it takes about 15-30 seconds after this process completes for MariaDB to be
|
||||
ready to accept connections, so you'll need to wait that long before running the next command.
|
||||
|
||||
Now we install the WordPress testing suite inside the container, link the local codebase inside it, and activate it
|
||||
as a plugin. Note that we are skipping database creation (handled by our `docker-compose` file). The WordPress
|
||||
version you pass here should match what is specified in `docker-compose.yml`.
|
||||
|
||||
```
|
||||
docker-compose run --rm tests ./bin/install-wp-tests.sh ignored root testing mysql_test 4.9.4 true
|
||||
```
|
||||
|
||||
You now have a stable testing environment. The WPGraphQL codebase is mapped inside your containers and any changes
|
||||
you make will be reflected almost immediately. We use Codeception Environments (`--env`) to point tests to our
|
||||
database container instead of `127.0.0.1`. Run your tests and repeat as necessary, e.g.:
|
||||
|
||||
```
|
||||
docker-compose run --rm tests ./vendor/bin/codecept run acceptance --env docker
|
||||
docker-compose run --rm tests ./vendor/bin/codecept run functional --env docker
|
||||
docker-compose run --rm tests ./vendor/bin/codecept run wpunit --env docker
|
||||
```
|
||||
|
||||
Code coverage for `wpunit` tests can be generated using `phpdbg`:
|
||||
|
||||
```
|
||||
docker-compose run --rm tests phpdbg -qrr ./vendor/bin/codecept run wpunit --env docker --coverage --coverage-xml
|
||||
```
|
||||
|
||||
If you need to test against a different WordPress version, you will need to destroy your environemnt, update
|
||||
`docker-compose.yml` and `bin/Dockerfile` to point to the desired version, then recreate your environment.
|
||||
|
||||
|
||||
|
||||
### Generating Code Coverage
|
||||
You can generate code coverage for tests by passing `--coverage`, `--coverage-xml` or `--coverage-html` with the tests.
|
||||
|
||||
- `--coverage` will print coverage info to the screen
|
||||
- `--coverage-xml` will save an XML file that can be used by services like Coveralls or CodeCov
|
||||
- `--coverage-html` will save the coverage report in an HTML file that you can browse.
|
||||
|
||||
The coverage details will be output to `/tests/_output`
|
||||
|
||||
### Running Individual Files
|
||||
As you'll note, running all of the tests in the entire test suite can be time consuming. If you would like to run only one test file instead of all of them, simply pass the test file you're trying to test, like so:
|
||||
|
||||
`vendor/bin/codecept run wpunit AvatarObjectQueriesTest`
|
||||
|
||||
To capture coverage for a single file, you can run the test like so:
|
||||
|
||||
`vendor/bin/codecept run wpunit AvatarObjectQueriesTest --coverage`
|
||||
|
||||
And you can output the coverage locally to HTML like so:
|
||||
|
||||
`vendor/bin/codecept run wpunit AvatarObjectQueriesTest --coverage --coverage-html`
|
||||
|
||||
## Shout Outs
|
||||
This plugin brings the power of GraphQL (http://graphql.org/) to WordPress.
|
||||
|
||||
This plugin is based on the hard work of Jason Bahl, Ryan Kanner, Hughie Devore and Peter Pak of Digital First Media (https://github.com/dfmedia),
|
||||
and Edwin Cromley of BE-Webdesign (https://github.com/BE-Webdesign).
|
||||
|
||||
The plugin is built on top of the graphql-php library by Webonyx (https://github.com/webonyx/graphql-php) and makes use
|
||||
of the graphql-relay-php library by Ivome (https://github.com/ivome/graphql-relay-php/)
|
||||
|
||||
Special thanks to Digital First Media (http://digitalfirstmedia.com) for allocating development resources to push the
|
||||
project forward.
|
||||
|
||||
Some of the concepts and code are based on the WordPress Rest API. Much love to the folks (https://github.com/orgs/WP-API/people)
|
||||
that put their blood, sweat and tears into the WP-API project, as it's been huge in moving WordPress forward as a
|
||||
platform and helped inspire and direct the development of WPGraphQL.
|
||||
|
||||
Much love to Facebook® for open sourcing the GraphQL spec (https://facebook.github.io/graphql/), the amazing GraphiQL
|
||||
dev tools (https://github.com/graphql/graphiql), and maintaining the JavaScript GraphQL reference
|
||||
implementation (https://github.com/graphql/graphql-js)
|
||||
|
||||
Much love to Apollo (Meteor Development Group) for their work on driving GraphQL forward and providing a lot of insight
|
||||
into how to design GraphQL schemas, etc. Check them out: http://www.apollodata.com/
|
||||
38
wordpress/wp-content/plugins/wp-graphql/access-functions.php
Executable file
38
wordpress/wp-content/plugins/wp-graphql/access-functions.php
Executable file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/**
|
||||
* This file contains access functions for various class methods
|
||||
*
|
||||
* @since 0.0.2
|
||||
*/
|
||||
|
||||
/**
|
||||
* Formats the name of a field so that it plays nice with GraphiQL
|
||||
*
|
||||
* @param string $field_name Name of the field
|
||||
*
|
||||
* @access public
|
||||
* @return string Name of the field
|
||||
* @since 0.0.2
|
||||
*/
|
||||
function graphql_format_field_name( $field_name ) {
|
||||
$field_name = preg_replace( '/[^A-Za-z0-9]/i', ' ', $field_name );
|
||||
$field_name = preg_replace( '/[^A-Za-z0-9]/i', '', ucwords( $field_name ) );
|
||||
$field_name = lcfirst( $field_name );
|
||||
|
||||
return $field_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a simple way to run a GraphQL query with out posting a request to the endpoint.
|
||||
*
|
||||
* @param string $request The GraphQL query to run
|
||||
* @param string $operation_name The name of the operation
|
||||
* @param string $variables Variables to be passed to your GraphQL request
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
* @since 0.0.2
|
||||
*/
|
||||
function do_graphql_request( $request, $operation_name = '', $variables = '' ) {
|
||||
return \WPGraphQL::do_graphql_request( $request, $operation_name, $variables );
|
||||
}
|
||||
6
wordpress/wp-content/plugins/wp-graphql/apigen.yml
Executable file
6
wordpress/wp-content/plugins/wp-graphql/apigen.yml
Executable file
@@ -0,0 +1,6 @@
|
||||
parameters:
|
||||
visibilityLevels: [public, protected] # array
|
||||
annotationGroups: [todo, deprecated] # array
|
||||
title: "WPGraphQL" # string
|
||||
baseUrl: "https://www.wpgraphql.com/" # string
|
||||
overwrite: false # bool
|
||||
15
wordpress/wp-content/plugins/wp-graphql/bin/Dockerfile
Executable file
15
wordpress/wp-content/plugins/wp-graphql/bin/Dockerfile
Executable file
@@ -0,0 +1,15 @@
|
||||
ARG PHP_VERSION
|
||||
|
||||
FROM wordpress:cli-php${PHP_VERSION}
|
||||
|
||||
USER root
|
||||
|
||||
RUN apk --no-cache --update add bash subversion sudo
|
||||
|
||||
RUN docker-php-ext-install pdo_mysql
|
||||
|
||||
RUN echo 'date.timezone = "UTC"' > /usr/local/etc/php/conf.d/timezone.ini
|
||||
|
||||
ENV WP_CLI_ARGS="--allow-root"
|
||||
|
||||
WORKDIR /project
|
||||
181
wordpress/wp-content/plugins/wp-graphql/bin/install-wp-tests.sh
Executable file
181
wordpress/wp-content/plugins/wp-graphql/bin/install-wp-tests.sh
Executable file
@@ -0,0 +1,181 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
if [ $# -lt 3 ]; then
|
||||
echo "usage: $0 <db-name> <db-user> <db-pass> [db-host] [wp-version] [skip-database-creation]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DB_NAME=$1
|
||||
DB_USER=$2
|
||||
DB_PASS=$3
|
||||
DB_HOST=${4-localhost}
|
||||
WP_VERSION=${5-latest}
|
||||
SKIP_DB_CREATE=${6-false}
|
||||
|
||||
PLUGIN_DIR=$(pwd)
|
||||
WP_TESTS_DIR=${WP_TESTS_DIR-/tmp/wordpress-tests-lib}
|
||||
WP_CORE_DIR=${WP_CORE_DIR-/tmp/wordpress/}
|
||||
DB_SERVE_NAME=${DB_SERVE_NAME-wpgraphql_serve}
|
||||
|
||||
download() {
|
||||
if [ `which curl` ]; then
|
||||
curl -s "$1" > "$2";
|
||||
elif [ `which wget` ]; then
|
||||
wget -nv -O "$2" "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ $WP_VERSION =~ [0-9]+\.[0-9]+(\.[0-9]+)? ]]; then
|
||||
WP_TESTS_TAG="tags/$WP_VERSION"
|
||||
elif [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then
|
||||
WP_TESTS_TAG="trunk"
|
||||
else
|
||||
# http serves a single offer, whereas https serves multiple. we only want one
|
||||
download http://api.wordpress.org/core/version-check/1.7/ /tmp/wp-latest.json
|
||||
grep '[0-9]+\.[0-9]+(\.[0-9]+)?' /tmp/wp-latest.json
|
||||
LATEST_VERSION=$(grep -o '"version":"[^"]*' /tmp/wp-latest.json | sed 's/"version":"//')
|
||||
if [[ -z "$LATEST_VERSION" ]]; then
|
||||
echo "Latest WordPress version could not be found"
|
||||
exit 1
|
||||
fi
|
||||
WP_TESTS_TAG="tags/$LATEST_VERSION"
|
||||
fi
|
||||
|
||||
set -ex
|
||||
|
||||
install_wp() {
|
||||
|
||||
if [ -d $WP_CORE_DIR ]; then
|
||||
return;
|
||||
fi
|
||||
|
||||
mkdir -p $WP_CORE_DIR
|
||||
|
||||
if [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then
|
||||
mkdir -p /tmp/wordpress-nightly
|
||||
download https://wordpress.org/nightly-builds/wordpress-latest.zip /tmp/wordpress-nightly/wordpress-nightly.zip
|
||||
unzip -q /tmp/wordpress-nightly/wordpress-nightly.zip -d /tmp/wordpress-nightly/
|
||||
mv /tmp/wordpress-nightly/wordpress/* $WP_CORE_DIR
|
||||
else
|
||||
if [ $WP_VERSION == 'latest' ]; then
|
||||
local ARCHIVE_NAME='latest'
|
||||
else
|
||||
local ARCHIVE_NAME="wordpress-$WP_VERSION"
|
||||
fi
|
||||
download https://wordpress.org/${ARCHIVE_NAME}.tar.gz /tmp/wordpress.tar.gz
|
||||
tar --strip-components=1 -zxmf /tmp/wordpress.tar.gz -C $WP_CORE_DIR
|
||||
fi
|
||||
|
||||
download https://raw.github.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php
|
||||
}
|
||||
|
||||
install_test_suite() {
|
||||
# portable in-place argument for both GNU sed and Mac OSX sed
|
||||
if [[ $(uname -s) == 'Darwin' ]]; then
|
||||
local ioption='-i .bak'
|
||||
else
|
||||
local ioption='-i'
|
||||
fi
|
||||
|
||||
# set up testing suite if it doesn't yet exist
|
||||
if [ ! -d $WP_TESTS_DIR ]; then
|
||||
# set up testing suite
|
||||
mkdir -p $WP_TESTS_DIR
|
||||
svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/includes/ $WP_TESTS_DIR/includes
|
||||
svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/data/ $WP_TESTS_DIR/data
|
||||
fi
|
||||
|
||||
if [ ! -f wp-tests-config.php ]; then
|
||||
download https://develop.svn.wordpress.org/${WP_TESTS_TAG}/wp-tests-config-sample.php "$WP_TESTS_DIR"/wp-tests-config.php
|
||||
# remove all forward slashes in the end
|
||||
WP_CORE_DIR=$(echo $WP_CORE_DIR | sed "s:/\+$::")
|
||||
sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR/':" "$WP_TESTS_DIR"/wp-tests-config.php
|
||||
sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php
|
||||
sed $ioption "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php
|
||||
sed $ioption "s/yourpasswordhere/$DB_PASS/" "$WP_TESTS_DIR"/wp-tests-config.php
|
||||
sed $ioption "s|localhost|${DB_HOST}|" "$WP_TESTS_DIR"/wp-tests-config.php
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
install_db() {
|
||||
|
||||
if [ ${SKIP_DB_CREATE} = "true" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# parse DB_HOST for port or socket references
|
||||
local PARTS=(${DB_HOST//\:/ })
|
||||
local DB_HOSTNAME=${PARTS[0]};
|
||||
local DB_SOCK_OR_PORT=${PARTS[1]};
|
||||
local EXTRA=""
|
||||
|
||||
if ! [ -z $DB_HOSTNAME ] ; then
|
||||
if [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\{1,\}$') ]; then
|
||||
EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp"
|
||||
elif ! [ -z $DB_SOCK_OR_PORT ] ; then
|
||||
EXTRA=" --socket=$DB_SOCK_OR_PORT"
|
||||
elif ! [ -z $DB_HOSTNAME ] ; then
|
||||
EXTRA=" --host=$DB_HOSTNAME --protocol=tcp"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
RESULT=`mysql -u $DB_USER --password="$DB_PASS" --skip-column-names -e "SHOW DATABASES LIKE '$DB_NAME'"`
|
||||
if [ "$RESULT" != $DB_NAME ]; then
|
||||
mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA
|
||||
fi
|
||||
|
||||
RESULT_2=`mysql -u $DB_USER --password="$DB_PASS" --skip-column-names -e "SHOW DATABASES LIKE '$DB_SERVE_NAME'"`
|
||||
if [ "$RESULT_2" != $DB_SERVE_NAME ]; then
|
||||
mysqladmin create $DB_SERVE_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
# MySQL may not be ready when container starts; wait at most 30 seconds.
|
||||
wait_for_database_connection() {
|
||||
set +ex
|
||||
DB_TRIES=1
|
||||
while [ $DB_TRIES -lt 15 ]; do
|
||||
if curl --fail --show-error --silent "$DB_HOST:3306" > /dev/null 2>&1; then break; fi
|
||||
echo "Waiting for database to be ready...."
|
||||
sleep 2
|
||||
DB_TRIES=$((DB_TRIES + 1))
|
||||
done
|
||||
set -ex
|
||||
}
|
||||
|
||||
configure_wordpress() {
|
||||
|
||||
cd $WP_CORE_DIR
|
||||
wp $WP_CLI_ARGS config create --dbname="$DB_SERVE_NAME" --dbuser=root --dbpass="$DB_PASS" --dbhost="$DB_HOST" --skip-check --force=true
|
||||
wp $WP_CLI_ARGS core install --url=wpgraphql.test --title="WPGraphQL Tests" --admin_user=admin --admin_password=password --admin_email=admin@wpgraphql.test --skip-email
|
||||
wp $WP_CLI_ARGS rewrite structure '/%year%/%monthnum%/%postname%/'
|
||||
}
|
||||
|
||||
activate_plugin() {
|
||||
|
||||
# Add this repo as a plugin to the repo
|
||||
if [ ! -d $WP_CORE_DIR/wp-content/plugins/wp-graphql ]; then
|
||||
ln -s $PLUGIN_DIR $WP_CORE_DIR/wp-content/plugins/wp-graphql
|
||||
fi
|
||||
|
||||
cd $WP_CORE_DIR
|
||||
|
||||
# activate the plugin
|
||||
wp $WP_CLI_ARGS plugin activate wp-graphql
|
||||
|
||||
# Flush the permalinks
|
||||
wp $WP_CLI_ARGS rewrite flush
|
||||
|
||||
# Export the db for codeception to use
|
||||
wp $WP_CLI_ARGS db export $PLUGIN_DIR/tests/_data/dump.sql
|
||||
}
|
||||
|
||||
install_wp
|
||||
install_test_suite
|
||||
wait_for_database_connection
|
||||
install_db
|
||||
configure_wordpress
|
||||
activate_plugin
|
||||
4
wordpress/wp-content/plugins/wp-graphql/cli/README.md
Executable file
4
wordpress/wp-content/plugins/wp-graphql/cli/README.md
Executable file
@@ -0,0 +1,4 @@
|
||||
# CLI Scripts
|
||||
|
||||
These are handy scripts for interacting with WPGraphQL via the command line.
|
||||
|
||||
57
wordpress/wp-content/plugins/wp-graphql/cli/wp-cli.php
Executable file
57
wordpress/wp-content/plugins/wp-graphql/cli/wp-cli.php
Executable file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class WPGraphQL_CLI_Command extends WP_CLI_Command {
|
||||
|
||||
/**
|
||||
* Generate a static schema.
|
||||
*
|
||||
* Defaults to creating a schema.graphql file in the IDL format at the root
|
||||
* of the plugin.
|
||||
*
|
||||
* @todo: Provide alternative formats (AST? INTROSPECTION JSON?) and options for output location/file-type?
|
||||
* @todo: Add Unit Tests
|
||||
*
|
||||
* ## EXAMPLE
|
||||
*
|
||||
* $ wp graphql generate
|
||||
*
|
||||
* @alias generate
|
||||
* @subcommand generate-static-schema
|
||||
*/
|
||||
public function generate_static_schema( $args, $assoc_args ) {
|
||||
|
||||
/**
|
||||
* Set the file path for where to save the static schema
|
||||
*/
|
||||
$file_path = WPGRAPHQL_PLUGIN_DIR . 'schema.graphql';
|
||||
|
||||
/**
|
||||
* Generate the Schema
|
||||
*/
|
||||
WP_CLI::line( 'Getting the Schema...' );
|
||||
$schema = WPGraphQL::get_schema();
|
||||
|
||||
/**
|
||||
* Format the Schema
|
||||
*/
|
||||
WP_CLI::line( 'Formatting the Schema...' );
|
||||
$printed = \GraphQL\Utils\SchemaPrinter::doPrint( $schema );
|
||||
|
||||
/**
|
||||
* Save the Schema to the file
|
||||
*/
|
||||
WP_CLI::line( 'Saving the Schema...' );
|
||||
file_put_contents( $file_path, $printed );
|
||||
|
||||
/**
|
||||
* All done!
|
||||
*/
|
||||
WP_CLI::success( sprintf( 'All done. Schema output to %s.', $file_path ) );
|
||||
}
|
||||
}
|
||||
|
||||
WP_CLI::add_command( 'graphql', 'WPGraphQL_CLI_Command' );
|
||||
28
wordpress/wp-content/plugins/wp-graphql/codeception.yml
Executable file
28
wordpress/wp-content/plugins/wp-graphql/codeception.yml
Executable file
@@ -0,0 +1,28 @@
|
||||
paths:
|
||||
tests: tests
|
||||
output: tests/_output
|
||||
data: tests/_data
|
||||
support: tests/_support
|
||||
envs: tests/_envs
|
||||
actor_suffix: Tester
|
||||
coverage:
|
||||
enabled: true
|
||||
whitelist:
|
||||
include:
|
||||
- wp-graphql.php
|
||||
- access-functions.php
|
||||
- src/*.php
|
||||
|
||||
extensions:
|
||||
enabled:
|
||||
- Codeception\Extension\RunFailed
|
||||
commands:
|
||||
- Codeception\Command\GenerateWPUnit
|
||||
- Codeception\Command\GenerateWPRestApi
|
||||
- Codeception\Command\GenerateWPRestController
|
||||
- Codeception\Command\GenerateWPRestPostTypeController
|
||||
- Codeception\Command\GenerateWPAjax
|
||||
- Codeception\Command\GenerateWPCanonical
|
||||
- Codeception\Command\GenerateWPXMLRPC
|
||||
- Codeception\Command\DbSnapshot
|
||||
- tad\Codeception\Command\SearchReplace
|
||||
44
wordpress/wp-content/plugins/wp-graphql/composer.json
Executable file
44
wordpress/wp-content/plugins/wp-graphql/composer.json
Executable file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"name": "wp-graphql/wp-graphql",
|
||||
"description": "GraphQL API for WordPress",
|
||||
"type": "wordpress-plugin",
|
||||
"license": "GPL-3.0+",
|
||||
"version": "0.0.28",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jason Bahl",
|
||||
"email": "jasonbahl@mac.com"
|
||||
},
|
||||
{
|
||||
"name": "Edwin Cromley"
|
||||
},
|
||||
{
|
||||
"name": "Ryan Kanner"
|
||||
},
|
||||
{
|
||||
"name": "Hughie Devore"
|
||||
},
|
||||
{
|
||||
"name": "Chris Zarate"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"webonyx/graphql-php": "^0.11.2",
|
||||
"ivome/graphql-relay-php": "^0.3.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"apigen/apigen": "^4.1",
|
||||
"lucatume/wp-browser": "~1.21.20"
|
||||
},
|
||||
"config": {
|
||||
"optimize-autoloader": true
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"WPGraphQL\\": "src/"
|
||||
},
|
||||
"classmap": [
|
||||
"src/"
|
||||
]
|
||||
}
|
||||
}
|
||||
7379
wordpress/wp-content/plugins/wp-graphql/composer.lock
generated
Executable file
7379
wordpress/wp-content/plugins/wp-graphql/composer.lock
generated
Executable file
File diff suppressed because it is too large
Load Diff
45
wordpress/wp-content/plugins/wp-graphql/docker-compose.yml
Executable file
45
wordpress/wp-content/plugins/wp-graphql/docker-compose.yml
Executable file
@@ -0,0 +1,45 @@
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
tests:
|
||||
build:
|
||||
context: "./bin"
|
||||
args:
|
||||
PHP_VERSION: "${PHP_VERSION:-7.2}"
|
||||
depends_on:
|
||||
- wpgraphql_test
|
||||
environment:
|
||||
DB_SERVE_NAME: "wpgraphql_test"
|
||||
networks:
|
||||
- "testing"
|
||||
volumes:
|
||||
- "test-suite:/tmp"
|
||||
- "./:/project"
|
||||
wpgraphql_test:
|
||||
image: "wordpress:${WP_VERSION:-4.9.4}-php${PHP_VERSION:-7.2}-apache"
|
||||
depends_on:
|
||||
- mysql_test
|
||||
environment:
|
||||
WORDPRESS_DB_HOST: "mysql_test"
|
||||
WORDPRESS_DB_NAME: "wpgraphql_test"
|
||||
WORDPRESS_DB_PASSWORD: "testing"
|
||||
WORDPRESS_DB_USER: "root"
|
||||
networks:
|
||||
testing:
|
||||
aliases:
|
||||
- "wpgraphql.test"
|
||||
volumes:
|
||||
- "./:/var/www/html/wp-content/plugins/wp-graphql"
|
||||
mysql_test:
|
||||
image: "mariadb:10.2"
|
||||
environment:
|
||||
MYSQL_DATABASE: "wpgraphql_test"
|
||||
MYSQL_ROOT_PASSWORD: "testing"
|
||||
networks:
|
||||
- "testing"
|
||||
|
||||
networks:
|
||||
testing: {}
|
||||
|
||||
volumes:
|
||||
test-suite: {}
|
||||
2
wordpress/wp-content/plugins/wp-graphql/docs/README.md
Executable file
2
wordpress/wp-content/plugins/wp-graphql/docs/README.md
Executable file
@@ -0,0 +1,2 @@
|
||||
# WPGraphQL Docs
|
||||
The docs for WPGraphQL have been moved to a new repo: https://github.com/wp-graphql/wpgraphql.com
|
||||
BIN
wordpress/wp-content/plugins/wp-graphql/img/anatomy-of-request.png
Executable file
BIN
wordpress/wp-content/plugins/wp-graphql/img/anatomy-of-request.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 221 KiB |
BIN
wordpress/wp-content/plugins/wp-graphql/img/graphql-docs.gif
Executable file
BIN
wordpress/wp-content/plugins/wp-graphql/img/graphql-docs.gif
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 398 KiB |
BIN
wordpress/wp-content/plugins/wp-graphql/img/wp-example-web.png
Executable file
BIN
wordpress/wp-content/plugins/wp-graphql/img/wp-example-web.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 MiB |
10
wordpress/wp-content/plugins/wp-graphql/phpcs.ruleset.xml
Executable file
10
wordpress/wp-content/plugins/wp-graphql/phpcs.ruleset.xml
Executable file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0"?>
|
||||
<ruleset name="WordPress Coding Standards for Plugins">
|
||||
<description>Generally-applicable sniffs for WordPress plugins</description>
|
||||
|
||||
<rule ref="WordPress-Core" />
|
||||
<rule ref="WordPress-Docs" />
|
||||
|
||||
<exclude-pattern>*/node_modules/*</exclude-pattern>
|
||||
<exclude-pattern>*/vendor/*</exclude-pattern>
|
||||
</ruleset>
|
||||
35
wordpress/wp-content/plugins/wp-graphql/phpunit.xml.dist
Executable file
35
wordpress/wp-content/plugins/wp-graphql/phpunit.xml.dist
Executable file
@@ -0,0 +1,35 @@
|
||||
<phpunit
|
||||
bootstrap="tests/bootstrap.php"
|
||||
backupGlobals="false"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
>
|
||||
<groups>
|
||||
<exclude>
|
||||
<group>ajax</group>
|
||||
</exclude>
|
||||
<exclude>
|
||||
<group>external-http</group>
|
||||
</exclude>
|
||||
<exclude>
|
||||
<group>ms-files</group>
|
||||
</exclude>
|
||||
</groups>
|
||||
<testsuites>
|
||||
<testsuite name="WPGraphQL Test Suite">
|
||||
<directory prefix="test-" suffix=".php">./tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<logging>
|
||||
<log type="coverage-clover" target="build/logs/clover.xml"/>
|
||||
</logging>
|
||||
<filter>
|
||||
<whitelist processUncoveredFilesFromWhitelist="true">
|
||||
<file>./wp-graphql.php</file>
|
||||
<file>./access-functions.php</file>
|
||||
<directory suffix=".php">./src/</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
||||
13
wordpress/wp-content/plugins/wp-graphql/readme.txt
Executable file
13
wordpress/wp-content/plugins/wp-graphql/readme.txt
Executable file
@@ -0,0 +1,13 @@
|
||||
=== WPGraphQL ===
|
||||
Contributors: jasonbahl, ryankanner, chopinbach, hughdevore
|
||||
Tags: GraphQL, API, HTTP, Remote, Query Language
|
||||
Requires at least: 4.7.0
|
||||
Tested up to: 4.8.1
|
||||
License: GPL-3
|
||||
License URI: https://www.gnu.org/licenses/gpl-3.0.html
|
||||
|
||||
=== Description ===
|
||||
|
||||
Adds a GraphQL API to WordPress which can be used inside WordPress or from external sources via a "/graphql" HTTP endpoint.
|
||||
|
||||
The plugin is maintained on Github: https://github.com/wp-graphql/wp-graphql
|
||||
53
wordpress/wp-content/plugins/wp-graphql/src/AppContext.php
Executable file
53
wordpress/wp-content/plugins/wp-graphql/src/AppContext.php
Executable file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
namespace WPGraphQL;
|
||||
|
||||
/**
|
||||
* Class AppContext
|
||||
* Creates an object that contains all of the context for the GraphQL query
|
||||
* This class gets instantiated and populated in the main WPGraphQL class
|
||||
*
|
||||
* @package WPGraphQL
|
||||
*/
|
||||
class AppContext {
|
||||
|
||||
/**
|
||||
* Stores the url string for the current site
|
||||
*
|
||||
* @var string $root_url
|
||||
* @access public
|
||||
*/
|
||||
public $root_url;
|
||||
|
||||
/**
|
||||
* Stores the WP_User object of the current user
|
||||
*
|
||||
* @var \WP_User $viewer
|
||||
* @access public
|
||||
*/
|
||||
public $viewer;
|
||||
|
||||
/**
|
||||
* Stores everything from the $_REQUEST global
|
||||
*
|
||||
* @var \mixed $request
|
||||
* @access public
|
||||
*/
|
||||
public $request;
|
||||
|
||||
/**
|
||||
* Stores additional $config properties
|
||||
* @var \mixed $config
|
||||
* @access public
|
||||
*/
|
||||
public $config;
|
||||
|
||||
/**
|
||||
* AppContext constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
|
||||
$this->config = apply_filters( 'graphql_app_context_config', $this->config );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
203
wordpress/wp-content/plugins/wp-graphql/src/Data/Config.php
Executable file
203
wordpress/wp-content/plugins/wp-graphql/src/Data/Config.php
Executable file
@@ -0,0 +1,203 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL\Data;
|
||||
|
||||
/**
|
||||
* Class Config
|
||||
*
|
||||
* This class contains configurations for various data-related things, such as query filters for cursor pagination.
|
||||
*
|
||||
* @package WPGraphQL\Data
|
||||
*/
|
||||
class Config {
|
||||
|
||||
/**
|
||||
* Config constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
|
||||
/**
|
||||
* Filter the term_clauses in the WP_Term_Query to allow for cursor pagination support where a Term ID
|
||||
* can be used as a point of comparison when slicing the results to return.
|
||||
*/
|
||||
add_filter( 'comments_clauses', [ $this, 'graphql_wp_comments_query_cursor_pagination_support' ], 10, 2 );
|
||||
|
||||
|
||||
/**
|
||||
* Filter the WP_Query to support cursor based pagination where a post ID can be used
|
||||
* as a point of comparison when slicing the results to return.
|
||||
*/
|
||||
add_filter( 'posts_where', [ $this, 'graphql_wp_query_cursor_pagination_support' ], 10, 2 );
|
||||
|
||||
/**
|
||||
* Filter the term_clauses in the WP_Term_Query to allow for cursor pagination support where a Term ID
|
||||
* can be used as a point of comparison when slicing the results to return.
|
||||
*/
|
||||
add_filter( 'terms_clauses', [ $this, 'graphql_wp_term_query_cursor_pagination_support' ], 10, 3 );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This filters the WPQuery 'where' $args, enforcing the query to return results before or
|
||||
* after the referenced cursor
|
||||
*
|
||||
* @param string $where The WHERE clause of the query.
|
||||
* @param \WP_Query $query The WP_Query instance (passed by reference).
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function graphql_wp_query_cursor_pagination_support( $where, \WP_Query $query ) {
|
||||
|
||||
/**
|
||||
* Access the global $wpdb object
|
||||
*/
|
||||
global $wpdb;
|
||||
|
||||
/**
|
||||
* If there's a graphql_cursor_offset in the query, we should check to see if
|
||||
* it should be applied to the query
|
||||
*/
|
||||
if ( defined( 'GRAPHQL_REQUEST' ) && GRAPHQL_REQUEST ) {
|
||||
|
||||
$cursor_offset = ! empty( $query->query_vars['graphql_cursor_offset'] ) ? $query->query_vars['graphql_cursor_offset'] : 0;
|
||||
|
||||
/**
|
||||
* Ensure the cursor_offset is a positive integer
|
||||
*/
|
||||
if ( is_integer( $cursor_offset ) && 0 < $cursor_offset ) {
|
||||
|
||||
$compare = ! empty( $query->get( 'graphql_cursor_compare' ) ) ? $query->get( 'graphql_cursor_compare' ) : '>';
|
||||
$compare = in_array( $compare, [ '>', '<' ], true ) ? $compare : '>';
|
||||
$compare_opposite = ( '<' === $compare ) ? '>' : '<';
|
||||
|
||||
// Get the $cursor_post
|
||||
$cursor_post = get_post( $cursor_offset );
|
||||
|
||||
/**
|
||||
* If the $cursor_post exists (hasn't been deleted), modify the query to compare based on the ID and post_date values
|
||||
* But if the $cursor_post no longer exists, we're forced to just compare with the ID
|
||||
*
|
||||
*/
|
||||
if ( ! empty( $cursor_post ) && ! empty( $cursor_post->post_date ) ) {
|
||||
$orderby = $query->get( 'orderby' );
|
||||
if ( ! empty( $orderby ) && is_array( $orderby ) ) {
|
||||
foreach ( $orderby as $by => $order ) {
|
||||
$order_compare = ( 'ASC' === $order ) ? '>' : '<';
|
||||
$value = $cursor_post->{$by};
|
||||
if ( ! empty( $by ) && ! empty( $value ) ) {
|
||||
$where .= $wpdb->prepare( " AND {$wpdb->posts}.{$by} {$order_compare} %s", $value );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$where .= $wpdb->prepare( " AND {$wpdb->posts}.post_date {$compare}= %s AND {$wpdb->posts}.ID != %d", esc_sql( $cursor_post->post_date ), absint( $cursor_offset ) );
|
||||
}
|
||||
} else {
|
||||
$where .= $wpdb->prepare( " AND {$wpdb->posts}.ID {$compare} %d", $cursor_offset );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return $where;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This filters the term_clauses in the WP_Term_Query to support cursor based pagination, where we can
|
||||
* move forward or backward from a particular record, instead of typical offset pagination which can be
|
||||
* much more expensive and less accurate.
|
||||
*
|
||||
* @param array $pieces Terms query SQL clauses.
|
||||
* @param array $taxonomies An array of taxonomies.
|
||||
* @param array $args An array of terms query arguments.
|
||||
*
|
||||
* @return array $pieces
|
||||
*/
|
||||
public function graphql_wp_term_query_cursor_pagination_support( array $pieces, $taxonomies, $args ) {
|
||||
|
||||
/**
|
||||
* Access the global $wpdb object
|
||||
*/
|
||||
global $wpdb;
|
||||
|
||||
if ( defined( 'GRAPHQL_REQUEST' ) && GRAPHQL_REQUEST && ! empty( $args['graphql_cursor_offset'] ) ) {
|
||||
|
||||
$cursor_offset = $args['graphql_cursor_offset'];
|
||||
|
||||
/**
|
||||
* Ensure the cursor_offset is a positive integer
|
||||
*/
|
||||
if ( is_integer( $cursor_offset ) && 0 < $cursor_offset ) {
|
||||
|
||||
$compare = ! empty( $args['graphql_cursor_compare'] ) ? $args['graphql_cursor_compare'] : '>';
|
||||
$compare = in_array( $compare, [ '>', '<' ], true ) ? $compare : '>';
|
||||
|
||||
$order_by = ! empty( $args['orderby'] ) ? $args['orderby'] : 'comment_date';
|
||||
$order = ! empty( $args['order'] ) ? $args['order'] : 'DESC';
|
||||
$order_compare = ( 'ASC' === $order ) ? '>' : '<';
|
||||
|
||||
// Get the $cursor_post
|
||||
$cursor_term = get_term( $cursor_offset );
|
||||
|
||||
if ( ! empty( $cursor_term ) && ! empty( $cursor_term->name ) ) {
|
||||
$pieces['where'] .= $wpdb->prepare( " AND t.{$order_by} {$order_compare} %s", $cursor_term->{$order_by} );
|
||||
} else {
|
||||
$pieces['where'] .= $wpdb->prepare( ' AND t.term_id %1$s %2$d', $compare, $cursor_offset );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $pieces;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This returns a modified version of the $pieces of the comment query clauses if the request
|
||||
* is a GRAPHQL_REQUEST and the query has a graphql_cursor_offset defined
|
||||
*
|
||||
* @param array $pieces A compacted array of comment query clauses.
|
||||
* @param \WP_Comment_Query $query Current instance of WP_Comment_Query, passed by reference.
|
||||
*
|
||||
* @return array $pieces
|
||||
*/
|
||||
public function graphql_wp_comments_query_cursor_pagination_support( array $pieces, \WP_Comment_Query $query ) {
|
||||
|
||||
/**
|
||||
* Access the global $wpdb object
|
||||
*/
|
||||
global $wpdb;
|
||||
|
||||
if (
|
||||
defined( 'GRAPHQL_REQUEST' ) && GRAPHQL_REQUEST &&
|
||||
( is_array( $query->query_vars ) && array_key_exists( 'graphql_cursor_offset', $query->query_vars ) )
|
||||
) {
|
||||
|
||||
$cursor_offset = $query->query_vars['graphql_cursor_offset'];
|
||||
|
||||
/**
|
||||
* Ensure the cursor_offset is a positive integer
|
||||
*/
|
||||
if ( is_integer( $cursor_offset ) && 0 < $cursor_offset ) {
|
||||
|
||||
$compare = ! empty( $query->get( 'graphql_cursor_compare' ) ) ? $query->get( 'graphql_cursor_compare' ) : '>';
|
||||
$compare = in_array( $compare, [ '>', '<' ], true ) ? $compare : '>';
|
||||
|
||||
$order_by = ! empty( $query->query_vars['order_by'] ) ? $query->query_vars['order_by'] : 'comment_date';
|
||||
$order = ! empty( $query->query_vars['order'] ) ? $query->query_vars['order'] : 'DESC';
|
||||
$order_compare = ( 'ASC' === $order ) ? '>' : '<';
|
||||
|
||||
// Get the $cursor_post
|
||||
$cursor_comment = get_comment( $cursor_offset );
|
||||
if ( ! empty( $cursor_comment ) ) {
|
||||
$pieces['where'] .= $wpdb->prepare( " AND {$order_by} {$order_compare} %s", $cursor_comment->{$order_by} );
|
||||
} else {
|
||||
$pieces['where'] .= $wpdb->prepare( ' AND comment_ID %1$s %2$d', $compare, $cursor_offset );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $pieces;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
319
wordpress/wp-content/plugins/wp-graphql/src/Data/ConnectionResolver.php
Executable file
319
wordpress/wp-content/plugins/wp-graphql/src/Data/ConnectionResolver.php
Executable file
@@ -0,0 +1,319 @@
|
||||
<?php
|
||||
namespace WPGraphQL\Data;
|
||||
|
||||
use GraphQL\Error\UserError;
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQLRelay\Connection\ArrayConnection;
|
||||
use WPGraphQL\AppContext;
|
||||
|
||||
/**
|
||||
* Class Connections
|
||||
*
|
||||
* This class is meant to be extended by ConnectionResolvers
|
||||
*
|
||||
* @package WPGraphQL\Data
|
||||
*/
|
||||
abstract class ConnectionResolver implements ConnectionResolverInterface {
|
||||
|
||||
/**
|
||||
* Runs the query for comments
|
||||
*
|
||||
* @param mixed $source Data returned from the query
|
||||
* @param array $args Args for the query
|
||||
* @param AppContext $context AppContext object for the query
|
||||
* @param ResolveInfo $info ResolveInfo object
|
||||
*
|
||||
* @return array
|
||||
* @since 0.5.0
|
||||
* @throws \Exception
|
||||
* @access public
|
||||
*/
|
||||
public static function resolve( $source, $args, AppContext $context, ResolveInfo $info ) {
|
||||
|
||||
$query_args = static::get_query_args( $source, $args, $context, $info );
|
||||
$query = static::get_query( $query_args );
|
||||
$array_slice = self::get_array_slice( $query, $args );
|
||||
$connection = static::get_connection( $query, $array_slice, $source, $args, $context, $info );
|
||||
/**
|
||||
* Filter the connection, and provide heaps of info to make it easy to filter very specific cases
|
||||
*
|
||||
* @param array $connection The connection to return
|
||||
* @param array $array_slice The Array to create the connection from
|
||||
* @param mixed $query The query that was run to retrieve the items
|
||||
* @param array $query_args The args that were used by the query
|
||||
* @param mixed $source The source being passed down the GraphQL tree
|
||||
* @param array $args The args that were input for the specific GraphQL query
|
||||
* @param AppContext object $context The AppContext that gets passed down the GraphQL tree
|
||||
* @param ResolveInfo object $info The ResolveInfo object that gets passed down the GraphQL tree
|
||||
*
|
||||
*/
|
||||
$connection = apply_filters(
|
||||
'graphql_connection_resolver_resolve',
|
||||
$connection,
|
||||
$array_slice,
|
||||
$query,
|
||||
$query_args,
|
||||
$source,
|
||||
$args,
|
||||
$context,
|
||||
$info
|
||||
);
|
||||
|
||||
return $connection;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Take an array return a connection
|
||||
*
|
||||
* @param mixed $query The query being performed
|
||||
* @param array $array The array of connection items
|
||||
* @param mixed $source The source being passed down the resolve tree
|
||||
* @param array $args The args for the field being resolved
|
||||
* @param AppContext $context The context being passed down the Resolve tree
|
||||
* @param ResolveInfo $info The ResolveInfo for the field being resolved
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_connection( $query, array $array, $source, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
|
||||
$meta = self::get_array_meta( $query, $args );
|
||||
$connection = ArrayConnection::connectionFromArraySlice( $array, $args, $meta );
|
||||
$connection['nodes'] = $array;
|
||||
|
||||
return $connection;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This returns a slice of the query results based on the posts retrieved and the $args passed to the query
|
||||
*
|
||||
* @param mixed $query The query that was made to fetch the items
|
||||
* @param array $args array of arguments input in the field as part of the GraphQL query
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_array_slice( $query, array $args ) {
|
||||
|
||||
$info = self::get_query_info( $query );
|
||||
$items = $info['items'];
|
||||
$array_slice = [];
|
||||
if ( ! empty( $items ) && is_array( $items ) ) {
|
||||
foreach ( $items as $item ) {
|
||||
if ( true === is_object( $item ) ) {
|
||||
switch ( true ) {
|
||||
case $item instanceof \WP_Comment:
|
||||
$array_slice[ $item->comment_ID ] = $item;
|
||||
break;
|
||||
case $item instanceof \WP_Term:
|
||||
$array_slice[ $item->term_id ] = $item;
|
||||
break;
|
||||
case $item instanceof \WP_Post:
|
||||
$array_slice[ $item->ID ] = $item;
|
||||
break;
|
||||
// the \WP_User_Query doesn't have proper filters to allow for true cursor based pagination
|
||||
case $item instanceof \WP_User:
|
||||
$array_slice[] = $item;
|
||||
break;
|
||||
default:
|
||||
$array_slice = $items;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $array_slice;
|
||||
}
|
||||
|
||||
/**
|
||||
* This checks the $args to determine the amount requested, and if
|
||||
*
|
||||
* @param array $args array of arguments input in the field as part of the GraphQL query
|
||||
*
|
||||
* @return int|null
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function get_amount_requested( array $args ) {
|
||||
|
||||
/**
|
||||
* Set the default amount
|
||||
*/
|
||||
$amount_requested = 10;
|
||||
|
||||
/**
|
||||
* If both first & last are used in the input args, throw an exception as that won't
|
||||
* work properly
|
||||
*/
|
||||
if ( ! empty( $args['first'] ) && ! empty( $args['last'] ) ) {
|
||||
throw new UserError( esc_html__( 'first and last cannot be used together. For forward pagination, use first & after. For backward pagination, use last & before.', 'wp-graphql' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* If first is set, and is a positive integer, use it for the $amount_requested
|
||||
* but if it's set to anything that isn't a positive integer, throw an exception
|
||||
*/
|
||||
if ( ! empty( $args['first'] ) && is_int( $args['first'] ) ) {
|
||||
if ( 0 > $args['first'] ) {
|
||||
throw new UserError( esc_html__( 'first must be a positive integer.', 'wp-graphql' ) );
|
||||
} else {
|
||||
$amount_requested = $args['first'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If last is set, and is a positive integer, use it for the $amount_requested
|
||||
* but if it's set to anything that isn't a positive integer, throw an exception
|
||||
*/
|
||||
if ( ! empty( $args['last'] ) && is_int( $args['last'] ) ) {
|
||||
if ( 0 > $args['last'] ) {
|
||||
throw new UserError( esc_html__( 'last must be a positive integer.', 'wp-graphql' ) );
|
||||
} else {
|
||||
$amount_requested = $args['last'];
|
||||
}
|
||||
}
|
||||
|
||||
return max( 0, $amount_requested );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* get_query_amount
|
||||
*
|
||||
* Returns the max between what was requested and what is defined as the $max_query_amount to ensure that
|
||||
* queries don't exceed unwanted limits when querying data.
|
||||
*
|
||||
* @param $source
|
||||
* @param $args
|
||||
* @param $context
|
||||
* @param $info
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get_query_amount( $source, $args, $context, $info ) {
|
||||
|
||||
/**
|
||||
* Filter the maximum number of posts per page that should be quried. The default is 100 to prevent queries from
|
||||
* being exceedingly resource intensive, however individual systems can override this for their specific needs.
|
||||
*
|
||||
* This filter is intentionally applied AFTER the query_args filter, as
|
||||
*
|
||||
* @param array $query_args array of query_args being passed to the
|
||||
* @param mixed $source source passed down from the resolve tree
|
||||
* @param array $args array of arguments input in the field as part of the GraphQL query
|
||||
* @param AppContext $context Object containing app context that gets passed down the resolve tree
|
||||
* @param ResolveInfo $info Info about fields passed down the resolve tree
|
||||
*
|
||||
* @since 0.0.6
|
||||
*/
|
||||
$max_query_amount = apply_filters( 'graphql_connection_max_query_amount', 100, $source, $args, $context, $info );
|
||||
|
||||
return min( $max_query_amount, absint( self::get_amount_requested( $args ) ) );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This returns a meta array to be used in preparing the connection edges
|
||||
*
|
||||
* @param mixed $query The query that was made to fetch the items
|
||||
* @param array $args array of arguments input in the field as part of the GraphQL query
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_array_meta( $query, $args ) {
|
||||
|
||||
$info = self::get_query_info( $query );
|
||||
$meta = [
|
||||
'sliceStart' => max( 0, absint( self::get_offset( $args ) ) ),
|
||||
'arrayLength' => absint( max( 0, $info['total_items'], count( $info['items'] ) ) ),
|
||||
];
|
||||
|
||||
return $meta;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This returns the offset to be used in the $query_args based on the $args passed to the GraphQL query.
|
||||
*
|
||||
* @param $args
|
||||
*
|
||||
* @return int|mixed
|
||||
*/
|
||||
public static function get_offset( $args ) {
|
||||
|
||||
/**
|
||||
* Defaults
|
||||
*/
|
||||
$offset = 0;
|
||||
|
||||
/**
|
||||
* Get the $after offset
|
||||
*/
|
||||
if ( ! empty( $args['after'] ) ) {
|
||||
$offset = ArrayConnection::cursorToOffset( $args['after'] );
|
||||
} elseif ( ! empty( $args['before'] ) ) {
|
||||
$offset = ArrayConnection::cursorToOffset( $args['before'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the higher of the two values
|
||||
*/
|
||||
return max( 0, $offset );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* WordPress has different queries that return date in different shapes. This normalizes the return
|
||||
* for re-use.
|
||||
*
|
||||
* @param mixed $query The query that was made to fetch the items
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public static function get_query_info( $query ) {
|
||||
|
||||
/**
|
||||
* Set the default values to return
|
||||
*/
|
||||
$query_info = [
|
||||
'total_items' => 0,
|
||||
'items' => [],
|
||||
];
|
||||
|
||||
if ( true === is_object( $query ) ) {
|
||||
switch ( true ) {
|
||||
case $query instanceof \WP_Query:
|
||||
$found_posts = $query->posts;
|
||||
$query_info['total_items'] = ! empty( $found_posts ) ? count( $found_posts ) : null;
|
||||
$query_info['items'] = $found_posts;
|
||||
break;
|
||||
case $query instanceof \WP_Comment_Query:
|
||||
$found_comments = $query->get_comments();
|
||||
$query_info['total_items'] = ! empty( $found_comments ) ? count( $found_comments ) : null;
|
||||
$query_info['items'] = ! empty( $found_comments ) ? $found_comments : [];
|
||||
break;
|
||||
case $query instanceof \WP_Term_Query:
|
||||
$query_info['total_items'] = ! empty( $query->query_vars['taxonomy'] ) ? wp_count_terms( $query->query_vars['taxonomy'][0], $query->query_vars ) : 0;
|
||||
$query_info['items'] = $query->get_terms();
|
||||
break;
|
||||
case $query instanceof \WP_User_Query:
|
||||
$query_info['total_items'] = ! empty( $query->get_total() ) ? $query->get_total() : count( $query->get_results() );
|
||||
$query_info['items'] = $query->get_results();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the items count after a query has been made
|
||||
*
|
||||
* @param int $items_count The number of items matching the query
|
||||
* @param mixed $query The query that was made to fetch the items
|
||||
*/
|
||||
$query_info = apply_filters( 'graphql_connection_query_info', $query_info, $query );
|
||||
|
||||
/**
|
||||
* Return the $count
|
||||
*/
|
||||
return $query_info;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
namespace WPGraphQL\Data;
|
||||
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use WPGraphQL\AppContext;
|
||||
|
||||
/**
|
||||
* Class Connections
|
||||
*
|
||||
* This class provides some helper methods to make creating connections easier.
|
||||
*
|
||||
* @package WPGraphQL\Data
|
||||
*/
|
||||
interface ConnectionResolverInterface {
|
||||
|
||||
public static function get_query( $query_args );
|
||||
|
||||
/**
|
||||
* Placeholder class that should be implemented by the extending class.
|
||||
*
|
||||
* This prepares the $query_args for use in the connection query. This is where default $args are set, where dynamic
|
||||
* $args from the $source get set, and where mapping the input $args to the actual $query_args occurs.
|
||||
*
|
||||
* @param $source
|
||||
* @param array $args
|
||||
* @param AppContext $context
|
||||
* @param ResolveInfo $info
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_query_args( $source, array $args, AppContext $context, ResolveInfo $info );
|
||||
|
||||
/**
|
||||
* Placeholder class that should be implemented by the extending class.
|
||||
*
|
||||
* This validates, sanitizes and maps the input $args and maps them to the appropriate WP Query class
|
||||
* (WP_Query, WP_Comment_Query, etc) ensuring the args being processed by the query are "safe"
|
||||
* to process.
|
||||
*
|
||||
* @param $args
|
||||
* @param $source
|
||||
* @param $all_args
|
||||
* @param $context
|
||||
* @param $info
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function sanitize_input_fields( array $args, $source, array $all_args, AppContext $context, ResolveInfo $info );
|
||||
|
||||
}
|
||||
716
wordpress/wp-content/plugins/wp-graphql/src/Data/DataSource.php
Executable file
716
wordpress/wp-content/plugins/wp-graphql/src/Data/DataSource.php
Executable file
@@ -0,0 +1,716 @@
|
||||
<?php
|
||||
namespace WPGraphQL\Data;
|
||||
|
||||
use GraphQL\Deferred;
|
||||
use GraphQL\Error\UserError;
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQLRelay\Relay;
|
||||
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\Type\TermObject\Connection\TermObjectConnectionResolver;
|
||||
use WPGraphQL\Type\Comment\Connection\CommentConnectionResolver;
|
||||
use WPGraphQL\Type\Plugin\Connection\PluginConnectionResolver;
|
||||
use WPGraphQL\Type\PostObject\Connection\PostObjectConnectionResolver;
|
||||
use WPGraphQL\Type\Theme\Connection\ThemeConnectionResolver;
|
||||
use WPGraphQL\Type\User\Connection\UserConnectionResolver;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class DataSource
|
||||
*
|
||||
* This class serves as a factory for all the resolvers for queries and mutations. This layer of
|
||||
* abstraction over the actual resolve functions allows easier, granular control over versioning as
|
||||
* we can change big things behind the scenes if/when needed, and we just need to ensure the
|
||||
* call to the DataSource method returns the expected data later on. This should make it easy
|
||||
* down the road to version resolvers if/when changes to the WordPress API are rolled out.
|
||||
*
|
||||
* @package WPGraphQL\Data
|
||||
* @since 0.0.4
|
||||
*/
|
||||
class DataSource {
|
||||
|
||||
/**
|
||||
* Stores an array of node definitions
|
||||
*
|
||||
* @var array $node_definition
|
||||
* @since 0.0.4
|
||||
* @access protected
|
||||
*/
|
||||
protected static $node_definition;
|
||||
|
||||
/**
|
||||
* Retrieves a WP_Comment object for the id that gets passed
|
||||
*
|
||||
* @param int $id ID of the comment we want to get the object for
|
||||
*
|
||||
* @return \WP_Comment object
|
||||
* @throws UserError
|
||||
* @since 0.0.5
|
||||
* @access public
|
||||
*/
|
||||
public static function resolve_comment( $id ) {
|
||||
|
||||
$comment = \WP_Comment::get_instance( $id );
|
||||
if ( empty( $comment ) ) {
|
||||
throw new UserError( sprintf( __( 'No comment was found with ID %d', 'wp-graphql' ), absint( $id ) ) );
|
||||
}
|
||||
|
||||
return $comment;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a WP_Comment object for the ID that gets passed
|
||||
*
|
||||
* @param string $author_email The ID of the comment the comment author is associated with.
|
||||
*
|
||||
* @return array
|
||||
* @throws
|
||||
*/
|
||||
public static function resolve_comment_author( $author_email ) {
|
||||
global $wpdb;
|
||||
$comment_author = $wpdb->get_row( $wpdb->prepare( "SELECT comment_author_email, comment_author, comment_author_url, comment_author_email from $wpdb->comments WHERE comment_author_email = %s LIMIT 1", esc_sql( $author_email ) ) );
|
||||
$comment_author = ! empty( $comment_author ) ? ( array ) $comment_author : [];
|
||||
$comment_author['is_comment_author'] = true;
|
||||
return $comment_author;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for the CommentsConnectionResolver class
|
||||
*
|
||||
* @param WP_Post object $source
|
||||
* @param array $args Query args to pass to the connection resolver
|
||||
* @param AppContext $context The context of the query to pass along
|
||||
* @param ResolveInfo $info The ResolveInfo object
|
||||
*
|
||||
* @return mixed
|
||||
* @since 0.0.5
|
||||
*/
|
||||
public static function resolve_comments_connection( $source, array $args, $context, ResolveInfo $info ) {
|
||||
$resolver = new CommentConnectionResolver();
|
||||
|
||||
return $resolver->resolve( $source, $args, $context, $info );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of data about the plugin you are requesting
|
||||
*
|
||||
* @param string $name Name of the plugin you want info for
|
||||
*
|
||||
* @return null|array
|
||||
* @throws \Exception
|
||||
* @since 0.0.5
|
||||
* @access public
|
||||
*/
|
||||
public static function resolve_plugin( $name ) {
|
||||
|
||||
// Puts input into a url friendly slug format.
|
||||
$slug = sanitize_title( $name );
|
||||
$plugin = null;
|
||||
|
||||
// The file may have not been loaded yet.
|
||||
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
||||
|
||||
/**
|
||||
* NOTE: This is missing must use and drop in plugins.
|
||||
*/
|
||||
$plugins = apply_filters( 'all_plugins', get_plugins() );
|
||||
|
||||
/**
|
||||
* Loop through the plugins and find the matching one
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
foreach ( $plugins as $path => $plugin_data ) {
|
||||
if ( sanitize_title( $plugin_data['Name'] ) === $slug ) {
|
||||
$plugin = $plugin_data;
|
||||
$plugin['path'] = $path;
|
||||
// Exit early when plugin is found.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the plugin, or throw an exception
|
||||
*/
|
||||
if ( ! empty( $plugin ) ) {
|
||||
return $plugin;
|
||||
} else {
|
||||
throw new UserError( sprintf( __( 'No plugin was found with the name %s', 'wp-graphql' ), $name ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for PluginsConnectionResolver::resolve
|
||||
*
|
||||
* @param \WP_Post $source WP_Post object
|
||||
* @param array $args Array of arguments to pass to reolve method
|
||||
* @param AppContext $context AppContext object passed down
|
||||
* @param ResolveInfo $info The ResolveInfo object
|
||||
*
|
||||
* @return array
|
||||
* @since 0.0.5
|
||||
* @access public
|
||||
*/
|
||||
public static function resolve_plugins_connection( $source, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return PluginConnectionResolver::resolve( $source, $args, $context, $info );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the post object for the ID and post type passed
|
||||
*
|
||||
* @param int $id ID of the post you are trying to retrieve
|
||||
* @param string $post_type Post type the post is attached to
|
||||
*
|
||||
* @throws UserError
|
||||
* @since 0.0.5
|
||||
* @return \WP_Post
|
||||
* @access public
|
||||
*/
|
||||
public static function resolve_post_object( $id, $post_type ) {
|
||||
|
||||
$post_object = \WP_Post::get_instance( $id );
|
||||
if ( empty( $post_object ) ) {
|
||||
throw new UserError( sprintf( __( 'No %1$s was found with the ID: %2$s', 'wp-graphql' ), $id, $post_type ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the resolving post to the global $post. That way any filters that
|
||||
* might be applied when resolving fields can rely on global post and
|
||||
* post data being set up.
|
||||
*/
|
||||
$GLOBALS['post'] = $post_object;
|
||||
|
||||
return $post_object;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for PostObjectsConnectionResolver
|
||||
*
|
||||
* @param string $post_type Post type of the post we are trying to resolve
|
||||
* @param $source
|
||||
* @param array $args Arguments to pass to the resolve method
|
||||
* @param AppContext $context AppContext object to pass down
|
||||
* @param ResolveInfo $info The ResolveInfo object
|
||||
*
|
||||
* @return mixed
|
||||
* @since 0.0.5
|
||||
* @access public
|
||||
*/
|
||||
public static function resolve_post_objects_connection( $source, array $args, AppContext $context, ResolveInfo $info, $post_type ) {
|
||||
$resolver = new PostObjectConnectionResolver( $post_type );
|
||||
|
||||
return $resolver->resolve( $source, $args, $context, $info );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the post type object from the post type name
|
||||
*
|
||||
* @param string $post_type Name of the post type you want to retrieve the object for
|
||||
*
|
||||
* @return \WP_Post_Type object
|
||||
* @throws UserError
|
||||
* @since 0.0.5
|
||||
* @access public
|
||||
*/
|
||||
public static function resolve_post_type( $post_type ) {
|
||||
|
||||
/**
|
||||
* Get the allowed_post_types
|
||||
*/
|
||||
$allowed_post_types = \WPGraphQL::get_allowed_post_types();
|
||||
|
||||
/**
|
||||
* If the $post_type is one of the allowed_post_types
|
||||
*/
|
||||
if ( in_array( $post_type, $allowed_post_types, true ) ) {
|
||||
return get_post_type_object( $post_type );
|
||||
} else {
|
||||
throw new UserError( sprintf( __( 'No post_type was found with the name %s', 'wp-graphql' ), $post_type ) );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the taxonomy object for the name of the taxonomy passed
|
||||
*
|
||||
* @param string $taxonomy Name of the taxonomy you want to retrieve the taxonomy object for
|
||||
*
|
||||
* @return \WP_Taxonomy object
|
||||
* @throws UserError
|
||||
* @since 0.0.5
|
||||
* @access public
|
||||
*/
|
||||
public static function resolve_taxonomy( $taxonomy ) {
|
||||
|
||||
/**
|
||||
* Get the allowed_taxonomies
|
||||
*/
|
||||
$allowed_taxonomies = \WPGraphQL::get_allowed_taxonomies();
|
||||
|
||||
/**
|
||||
* If the $post_type is one of the allowed_post_types
|
||||
*/
|
||||
if ( in_array( $taxonomy, $allowed_taxonomies, true ) ) {
|
||||
return get_taxonomy( $taxonomy );
|
||||
} else {
|
||||
throw new UserError( sprintf( __( 'No taxonomy was found with the name %s', 'wp-graphql' ), $taxonomy ) );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the term object for a term
|
||||
*
|
||||
* @param int $id ID of the term you are trying to retrieve the object for
|
||||
* @param string $taxonomy Name of the taxonomy the term is in
|
||||
*
|
||||
* @return mixed
|
||||
* @throws UserError
|
||||
* @since 0.0.5
|
||||
* @access public
|
||||
*/
|
||||
public static function resolve_term_object( $id, $taxonomy ) {
|
||||
|
||||
$term_object = \WP_Term::get_instance( $id, $taxonomy );
|
||||
if ( empty( $term_object ) ) {
|
||||
throw new UserError( sprintf( __( 'No %1$s was found with the ID: %2$s', 'wp-graphql' ), $taxonomy, $id ) );
|
||||
}
|
||||
|
||||
return $term_object;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for TermObjectConnectionResolver::resolve
|
||||
*
|
||||
* @param $source
|
||||
* @param array $args Array of args to be passed to the resolve method
|
||||
* @param AppContext $context The AppContext object to be passed down
|
||||
* @param ResolveInfo $info The ResolveInfo object
|
||||
* @param \WP_Taxonomy $taxonomy The WP_Taxonomy object of the taxonomy the term is connected to
|
||||
*
|
||||
* @return array
|
||||
* @since 0.0.5
|
||||
* @access public
|
||||
*/
|
||||
public static function resolve_term_objects_connection( $source, array $args, $context, ResolveInfo $info, $taxonomy ) {
|
||||
$resolver = new TermObjectConnectionResolver( $taxonomy );
|
||||
|
||||
return $resolver->resolve( $source, $args, $context, $info );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the theme object for the theme you are looking for
|
||||
*
|
||||
* @param string $stylesheet Directory name for the theme.
|
||||
*
|
||||
* @return \WP_Theme object
|
||||
* @throws UserError
|
||||
* @since 0.0.5
|
||||
* @access public
|
||||
*/
|
||||
public static function resolve_theme( $stylesheet ) {
|
||||
$theme = wp_get_theme( $stylesheet );
|
||||
if ( $theme->exists() ) {
|
||||
return $theme;
|
||||
} else {
|
||||
throw new UserError( sprintf( __( 'No theme was found with the stylesheet: %s', 'wp-graphql' ), $stylesheet ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for the ThemesConnectionResolver::resolve method
|
||||
*
|
||||
* @param $source
|
||||
* @param array $args Passes an array of arguments to the resolve method
|
||||
* @param AppContext $context The AppContext object to be passed down
|
||||
* @param ResolveInfo $info The ResolveInfo object
|
||||
*
|
||||
* @return array
|
||||
* @since 0.0.5
|
||||
* @access public
|
||||
*/
|
||||
public static function resolve_themes_connection( $source, array $args, $context, ResolveInfo $info ) {
|
||||
return ThemeConnectionResolver::resolve( $source, $args, $context, $info );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the user object for the user ID specified
|
||||
*
|
||||
* @param int $id ID of the user you want the object for
|
||||
*
|
||||
* @return Deferred
|
||||
* @since 0.0.5
|
||||
* @access public
|
||||
*/
|
||||
public static function resolve_user( $id ) {
|
||||
|
||||
Loader::addOne( 'user', $id );
|
||||
$loader = function() use ( $id ) {
|
||||
Loader::loadBuffered( 'user' );
|
||||
return Loader::loadOne( 'user', $id );
|
||||
};
|
||||
return new Deferred( $loader );
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for the UsersConnectionResolver::resolve method
|
||||
*
|
||||
* @param $source
|
||||
* @param array $args Array of args to be passed down to the resolve method
|
||||
* @param AppContext $context The AppContext object to be passed down
|
||||
* @param ResolveInfo $info The ResolveInfo object
|
||||
*
|
||||
* @return array
|
||||
* @since 0.0.5
|
||||
* @access public
|
||||
*/
|
||||
public static function resolve_users_connection( $source, array $args, $context, ResolveInfo $info ) {
|
||||
return UserConnectionResolver::resolve( $source, $args, $context, $info );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all of the allowed settings by group and return the
|
||||
* settings group that matches the group param
|
||||
*
|
||||
* @access public
|
||||
* @param string $group
|
||||
*
|
||||
* @return array $settings_groups[ $group ]
|
||||
*/
|
||||
public static function get_setting_group_fields( $group ) {
|
||||
|
||||
/**
|
||||
* Get all of the settings, sorted by group
|
||||
*/
|
||||
$settings_groups = self::get_allowed_settings_by_group();
|
||||
|
||||
return ! empty( $settings_groups[ $group ] ) ? $settings_groups[ $group ] : [];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all of the allowed settings by group
|
||||
*
|
||||
* @access public
|
||||
* @return array $allowed_settings_by_group
|
||||
*/
|
||||
public static function get_allowed_settings_by_group() {
|
||||
|
||||
/**
|
||||
* Get all registered settings
|
||||
*/
|
||||
$registered_settings = get_registered_settings();
|
||||
|
||||
/**
|
||||
* Loop through the $registered_settings array and build an array of
|
||||
* settings for each group ( general, reading, discussion, writing, reading, etc. )
|
||||
* if the setting is allowed in REST or GraphQL
|
||||
*/
|
||||
foreach ( $registered_settings as $key => $setting ) {
|
||||
if ( ! isset( $setting['show_in_graphql'] ) ) {
|
||||
if ( isset( $setting['show_in_rest'] ) && false !== $setting['show_in_rest'] ) {
|
||||
$setting['key'] = $key;
|
||||
$allowed_settings_by_group[ $setting['group'] ][ $key ] = $setting;
|
||||
}
|
||||
} else if ( true === $setting['show_in_graphql'] ) {
|
||||
$setting['key'] = $key;
|
||||
$allowed_settings_by_group[ $setting['group'] ][ $key ] = $setting;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the setting groups that are allowed
|
||||
*/
|
||||
$allowed_settings_by_group = ! empty( $allowed_settings_by_group ) && is_array( $allowed_settings_by_group ) ? $allowed_settings_by_group : [];
|
||||
|
||||
/**
|
||||
* Filter the $allowed_settings_by_group to allow enabling or disabling groups in the GraphQL Schema.
|
||||
*
|
||||
* @param array $allowed_settings_by_group
|
||||
*/
|
||||
$allowed_settings_by_group = apply_filters( 'graphql_allowed_settings_by_group', $allowed_settings_by_group );
|
||||
|
||||
return $allowed_settings_by_group;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all of the $allowed_settings
|
||||
*
|
||||
* @access public
|
||||
* @return array $allowed_settings
|
||||
*/
|
||||
public static function get_allowed_settings() {
|
||||
|
||||
/**
|
||||
* Get all registered settings
|
||||
*/
|
||||
$registered_settings = get_registered_settings();
|
||||
|
||||
/**
|
||||
* Loop through the $registered_settings and if the setting is allowed in REST or GraphQL
|
||||
* add it to the $allowed_settings array
|
||||
*/
|
||||
foreach ( $registered_settings as $key => $setting ) {
|
||||
if ( ! isset( $setting['show_in_graphql'] ) ) {
|
||||
if ( isset( $setting['show_in_rest'] ) && false !== $setting['show_in_rest'] ) {
|
||||
$setting['key'] = $key;
|
||||
$allowed_settings[ $key ] = $setting;
|
||||
}
|
||||
} else if ( true === $setting['show_in_graphql'] ) {
|
||||
$setting['key'] = $key;
|
||||
$allowed_settings[ $key ] = $setting;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify that we have the allowed settings
|
||||
*/
|
||||
$allowed_settings = ! empty( $allowed_settings ) && is_array( $allowed_settings ) ? $allowed_settings : [];
|
||||
|
||||
/**
|
||||
* Filter the $allowed_settings to allow some to be enabled or disabled from showing in
|
||||
* the GraphQL Schema.
|
||||
*
|
||||
* @param array $allowed_settings
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
$allowed_settings = apply_filters( 'graphql_allowed_setting_groups', $allowed_settings );
|
||||
|
||||
return $allowed_settings;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* We get the node interface and field from the relay library.
|
||||
*
|
||||
* The first method is the way we resolve an ID to its object. The second is the way we resolve
|
||||
* an object that implements node to its type.
|
||||
*
|
||||
* @return array
|
||||
* @throws UserError
|
||||
* @access public
|
||||
*/
|
||||
public static function get_node_definition() {
|
||||
|
||||
if ( null === self::$node_definition ) {
|
||||
|
||||
$node_definition = Relay::nodeDefinitions(
|
||||
|
||||
// The ID fetcher definition
|
||||
function( $global_id ) {
|
||||
|
||||
if ( empty( $global_id ) ) {
|
||||
throw new UserError( __( 'An ID needs to be provided to resolve a node.', 'wp-graphql' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the encoded ID into an array we can work with
|
||||
*
|
||||
* @since 0.0.4
|
||||
*/
|
||||
$id_components = Relay::fromGlobalId( $global_id );
|
||||
|
||||
/**
|
||||
* If the $id_components is a proper array with a type and id
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
if ( is_array( $id_components ) && ! empty( $id_components['id'] ) && ! empty( $id_components['type'] ) ) {
|
||||
|
||||
/**
|
||||
* Get the allowed_post_types and allowed_taxonomies
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
$allowed_post_types = \WPGraphQL::get_allowed_post_types();
|
||||
$allowed_taxonomies = \WPGraphQL::get_allowed_taxonomies();
|
||||
|
||||
switch ( $id_components['type'] ) {
|
||||
case in_array( $id_components['type'], $allowed_post_types, true ):
|
||||
$node = self::resolve_post_object( $id_components['id'], $id_components['type'] );
|
||||
break;
|
||||
case in_array( $id_components['type'], $allowed_taxonomies, true ):
|
||||
$node = self::resolve_term_object( $id_components['id'], $id_components['type'] );
|
||||
break;
|
||||
case 'comment':
|
||||
$node = self::resolve_comment( $id_components['id'] );
|
||||
break;
|
||||
case 'commentAuthor':
|
||||
$node = self::resolve_comment_author( $id_components['id'] );
|
||||
break;
|
||||
case 'plugin':
|
||||
$node = self::resolve_plugin( $id_components['id'] );
|
||||
break;
|
||||
case 'postType':
|
||||
$node = self::resolve_post_type( $id_components['id'] );
|
||||
break;
|
||||
case 'taxonomy':
|
||||
$node = self::resolve_taxonomy( $id_components['id'] );
|
||||
break;
|
||||
case 'theme':
|
||||
$node = self::resolve_theme( $id_components['id'] );
|
||||
break;
|
||||
case 'user':
|
||||
$node = self::resolve_user( $id_components['id'] );
|
||||
break;
|
||||
default:
|
||||
/**
|
||||
* Add a filter to allow externally registered node types to resolve based on
|
||||
* the id_components
|
||||
*
|
||||
* @param int $id The id of the node, from the global ID
|
||||
* @param string $type The type of node to resolve, from the global ID
|
||||
*
|
||||
* @since 0.0.6
|
||||
*/
|
||||
$node = apply_filters( 'graphql_resolve_node', null, $id_components['id'], $id_components['type'] );
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* If the $node is not properly resolved, throw an exception
|
||||
*
|
||||
* @since 0.0.6
|
||||
*/
|
||||
if ( null === $node ) {
|
||||
throw new UserError( sprintf( __( 'No node could be found with global ID: %s', 'wp-graphql' ), $global_id ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the resolved $node
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
return $node;
|
||||
|
||||
} else {
|
||||
throw new UserError( sprintf( __( 'The global ID isn\'t recognized ID: %s', 'wp-graphql' ), $global_id ) );
|
||||
}
|
||||
},
|
||||
|
||||
// Type resolver
|
||||
function( $node ) {
|
||||
|
||||
if ( true === is_object( $node ) ) {
|
||||
|
||||
switch ( true ) {
|
||||
case $node instanceof \WP_Post:
|
||||
$type = Types::post_object( $node->post_type );
|
||||
break;
|
||||
case $node instanceof \WP_Term:
|
||||
$type = Types::term_object( $node->taxonomy );
|
||||
break;
|
||||
case $node instanceof \WP_Comment:
|
||||
$type = Types::comment();
|
||||
break;
|
||||
case $node instanceof \WP_Post_Type:
|
||||
$type = Types::post_type();
|
||||
break;
|
||||
case $node instanceof \WP_Taxonomy:
|
||||
$type = Types::taxonomy();
|
||||
break;
|
||||
case $node instanceof \WP_Theme:
|
||||
$type = Types::theme();
|
||||
break;
|
||||
case $node instanceof \WP_User:
|
||||
$type = Types::user();
|
||||
break;
|
||||
default:
|
||||
$type = null;
|
||||
}
|
||||
|
||||
// Some nodes might return an array instead of an object
|
||||
} elseif ( is_array( $node ) ) {
|
||||
|
||||
switch ( $node ) {
|
||||
case array_key_exists( 'PluginURI', $node ):
|
||||
$type = Types::plugin();
|
||||
break;
|
||||
case array_key_exists( 'is_comment_author', $node ):
|
||||
$type = Types::comment_author();
|
||||
break;
|
||||
default:
|
||||
$type = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a filter to allow externally registered node types to return the proper type
|
||||
* based on the node_object that's returned
|
||||
*
|
||||
* @param mixed|object|array $type The type definition the node should resolve to.
|
||||
* @param mixed|object|array $node The $node that is being resolved
|
||||
*
|
||||
* @since 0.0.6
|
||||
*/
|
||||
$type = apply_filters( 'graphql_resolve_node_type', $type, $node );
|
||||
|
||||
/**
|
||||
* If the $type is not properly resolved, throw an exception
|
||||
*
|
||||
* @since 0.0.6
|
||||
*/
|
||||
if ( null === $type ) {
|
||||
throw new UserError( __( 'No type was found matching the node', 'wp-graphql' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the resolved $type for the $node
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
return $type;
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
self::$node_definition = $node_definition;
|
||||
|
||||
}
|
||||
|
||||
return self::$node_definition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cached version of get_page_by_path so that we're not making unnecessary SQL all the time
|
||||
*
|
||||
* This is a modified version of the cached function from WordPress.com VIP MU Plugins here.
|
||||
*
|
||||
* @param string $uri
|
||||
* @param string $output Optional. Output type; OBJECT*, ARRAY_N, or ARRAY_A.
|
||||
* @param string $post_type Optional. Post type; default is 'post'.
|
||||
* @return WP_Post|null WP_Post on success or null on failure
|
||||
* @see https://github.com/Automattic/vip-go-mu-plugins/blob/52549ae9a392fc1343b7ac9dba4ebcdca46e7d55/vip-helpers/vip-caching.php#L186
|
||||
* @link http://vip.wordpress.com/documentation/uncached-functions/ Uncached Functions
|
||||
*/
|
||||
public static function get_post_object_by_uri( $uri, $output = OBJECT, $post_type = 'post' ) {
|
||||
|
||||
if ( is_array( $post_type ) ) {
|
||||
$cache_key = sanitize_key( $uri ) . '_' . md5( serialize( $post_type ) );
|
||||
} else {
|
||||
$cache_key = $post_type . '_' . sanitize_key( $uri );
|
||||
}
|
||||
$post_id = wp_cache_get( $cache_key, 'get_post_object_by_path' );
|
||||
|
||||
if ( false === $post_id ) {
|
||||
$post = get_page_by_path( $uri, $output, $post_type );
|
||||
$post_id = $post ? $post->ID : 0;
|
||||
if ( 0 === $post_id ) {
|
||||
wp_cache_set( $cache_key, $post_id, 'get_post_object_by_path', ( 1 * HOUR_IN_SECONDS + mt_rand( 0, HOUR_IN_SECONDS ) ) ); // We only store the ID to keep our footprint small
|
||||
} else {
|
||||
wp_cache_set( $cache_key, $post_id, 'get_post_object_by_path', 0 ); // We only store the ID to keep our footprint small
|
||||
}
|
||||
}
|
||||
if ( $post_id ) {
|
||||
return get_post( $post_id, $output );
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
168
wordpress/wp-content/plugins/wp-graphql/src/Data/Loader.php
Executable file
168
wordpress/wp-content/plugins/wp-graphql/src/Data/Loader.php
Executable file
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL\Data;
|
||||
|
||||
use GraphQL\Error\UserError;
|
||||
|
||||
/**
|
||||
* Class Loader
|
||||
*
|
||||
* This class sets up general patterns for loading data in an optimized way. Used in conjunction with GraphQL Deferred
|
||||
* resolvers
|
||||
*
|
||||
* @package WPGraphQL\Data
|
||||
*/
|
||||
class Loader {
|
||||
|
||||
/**
|
||||
* Holds the queue of items to be loaded
|
||||
*
|
||||
* @var array $buffer
|
||||
* @access protected
|
||||
*/
|
||||
protected static $buffer = [];
|
||||
|
||||
/**
|
||||
* Holds the collection of items that have already been loaded
|
||||
*
|
||||
* @var array $loaded
|
||||
* @access protected
|
||||
*/
|
||||
protected static $loaded = [];
|
||||
|
||||
/**
|
||||
* Add an item to the buffer
|
||||
*
|
||||
* @param string $type The type of object to add
|
||||
* @param integer $id The ID of the item to be loaded
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public static function addOne( $type, $id ) {
|
||||
|
||||
if ( empty( self::$buffer[ $type ] ) ) {
|
||||
self::$buffer[ $type ] = [];
|
||||
}
|
||||
|
||||
if ( ! in_array( $id, self::$buffer[ $type ], true ) ) {
|
||||
array_push( self::$buffer[ $type ], absint( $id ) );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Add many items to the buffer
|
||||
*
|
||||
* @param string $type The type of objects to add
|
||||
* @param array $ids Array of IDs to be added to the buffer
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public static function addMany( $type, array $ids ) {
|
||||
if ( ! empty( $ids ) && is_array( $ids ) ) {
|
||||
foreach ( $ids as $id ) {
|
||||
self::addOne( $type, $id );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load an individual item from the loaded items
|
||||
*
|
||||
* @param string $type The type of object to load
|
||||
* @param int $id The ID of the item to load
|
||||
*
|
||||
* @return mixed
|
||||
* @access public
|
||||
*/
|
||||
public static function loadOne( $type, $id ) {
|
||||
$loaded = ! empty( self::$loaded[ $type ][ $id ] ) ? self::$loaded[ $type ][ $id ] : null;
|
||||
|
||||
if ( ! empty( $loaded ) ) {
|
||||
return $loaded;
|
||||
} else {
|
||||
throw new UserError( sprintf( __( 'No %1$s was found with the provided ID', 'wp-graphql' ), $type, $id ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load many items from the already loaded items
|
||||
*
|
||||
* @param string $type The type of objects to load
|
||||
* @param array $ids Array of items to load
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public static function loadMany( $type, array $ids ) {
|
||||
$load = [];
|
||||
if ( ! empty( $ids ) && is_array( $ids ) ) {
|
||||
foreach ( $ids as $id ) {
|
||||
$load[ $type ][] = self::$loaded[ $id ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Should be implemented by extending loader
|
||||
*
|
||||
* @param string $type The type of objects to load
|
||||
*
|
||||
* @return array
|
||||
* @access public
|
||||
*/
|
||||
public static function loadBuffered( $type ) {
|
||||
|
||||
switch ( $type ) {
|
||||
case 'user':
|
||||
return self::load_users();
|
||||
break;
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads users from the buffer
|
||||
*
|
||||
* @return mixed
|
||||
* @access protected
|
||||
*/
|
||||
protected static function load_users() {
|
||||
|
||||
$type = 'user';
|
||||
|
||||
if ( ! empty( self::$buffer[ $type ] ) ) {
|
||||
$query = new \WP_User_Query( [
|
||||
'include' => self::$buffer[ $type ],
|
||||
'orderby' => 'include',
|
||||
'count_total' => false,
|
||||
'fields' => 'all_with_meta'
|
||||
] );
|
||||
if ( ! empty( $query->get_results() ) && is_array( $query->get_results() ) ) {
|
||||
foreach ( $query->get_results() as $user ) {
|
||||
if ( $user instanceof \WP_User ) {
|
||||
self::$loaded[ $type ][ $user->ID ] = $user;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self::reset_buffer( $type );
|
||||
|
||||
return ! empty( self::$loaded[ $type ] ) ? self::$loaded[ $type ] : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the buffer for a given type
|
||||
*
|
||||
* @param string $type The buffer type to reset
|
||||
*
|
||||
* @access protected
|
||||
*/
|
||||
protected static function reset_buffer( $type ) {
|
||||
self::$buffer[ $type ] = [];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
4
wordpress/wp-content/plugins/wp-graphql/src/Data/README.md
Executable file
4
wordpress/wp-content/plugins/wp-graphql/src/Data/README.md
Executable file
@@ -0,0 +1,4 @@
|
||||
#Data
|
||||
|
||||
Methods for reading and writing data should live here. The "DataSource" class serves as a factory for methods
|
||||
that handle the fetching/writing of data.
|
||||
590
wordpress/wp-content/plugins/wp-graphql/src/Router.php
Executable file
590
wordpress/wp-content/plugins/wp-graphql/src/Router.php
Executable file
@@ -0,0 +1,590 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL;
|
||||
|
||||
use GraphQL\Error\FormattedError;
|
||||
use GraphQL\Error\UserError;
|
||||
|
||||
/**
|
||||
* Class Router
|
||||
* This sets up the /graphql endpoint
|
||||
*
|
||||
* @package WPGraphQL
|
||||
* @since 0.0.1
|
||||
*/
|
||||
class Router {
|
||||
|
||||
/**
|
||||
* Sets the route to use as the endpoint
|
||||
*
|
||||
* @var string $route
|
||||
* @access public
|
||||
*/
|
||||
public static $route = 'graphql';
|
||||
|
||||
/**
|
||||
* Set the default status code to 200.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public static $http_status_code = 200;
|
||||
|
||||
/**
|
||||
* Router constructor.
|
||||
*
|
||||
* @since 0.0.1
|
||||
* @access public
|
||||
*/
|
||||
public function __construct() {
|
||||
|
||||
/**
|
||||
* Pass the route through a filter in case the endpoint /graphql should need to be changed
|
||||
*
|
||||
* @since 0.0.1
|
||||
* @return string
|
||||
*/
|
||||
self::$route = apply_filters( 'graphql_endpoint', 'graphql' );
|
||||
|
||||
/**
|
||||
* Create the rewrite rule for the route
|
||||
*
|
||||
* @since 0.0.1
|
||||
*/
|
||||
add_action( 'init', [ $this, 'add_rewrite_rule' ], 10 );
|
||||
|
||||
/**
|
||||
* Add the query var for the route
|
||||
*
|
||||
* @since 0.0.1
|
||||
*/
|
||||
add_filter( 'query_vars', [ $this, 'add_query_var' ], 1, 1 );
|
||||
|
||||
/**
|
||||
* Redirects the route to the graphql processor
|
||||
*
|
||||
* @since 0.0.1
|
||||
*/
|
||||
add_action( 'parse_request', [ $this, 'resolve_http_request' ], 10 );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds rewrite rule for the route endpoint
|
||||
*
|
||||
* @uses add_rewrite_rule()
|
||||
* @since 0.0.1
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public static function add_rewrite_rule() {
|
||||
|
||||
add_rewrite_rule(
|
||||
self::$route . '/?$',
|
||||
'index.php?' . self::$route . '=true',
|
||||
'top'
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the query_var for the route
|
||||
*
|
||||
* @param array $query_vars The array of whitelisted query variables
|
||||
*
|
||||
* @access public
|
||||
* @since 0.0.1
|
||||
* @return array
|
||||
*/
|
||||
public static function add_query_var( $query_vars ) {
|
||||
|
||||
$query_vars[] = self::$route;
|
||||
|
||||
return $query_vars;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This resolves the http request and ensures that WordPress can respond with the appropriate
|
||||
* JSON response instead of responding with a template from the standard WordPress Template
|
||||
* Loading process
|
||||
*
|
||||
* @since 0.0.1
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public static function resolve_http_request() {
|
||||
|
||||
/**
|
||||
* Access the $wp_query object
|
||||
*/
|
||||
global $wp_query;
|
||||
|
||||
/**
|
||||
* Ensure we're on the registered route for graphql route
|
||||
*/
|
||||
if ( empty( $GLOBALS['wp']->query_vars ) || ! is_array( $GLOBALS['wp']->query_vars ) || ! array_key_exists( self::$route, $GLOBALS['wp']->query_vars ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set is_home to false
|
||||
*/
|
||||
$wp_query->is_home = false;
|
||||
|
||||
/**
|
||||
* Whether it's a GraphQL HTTP Request
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
if ( ! defined( 'GRAPHQL_HTTP_REQUEST' ) ) {
|
||||
define( 'GRAPHQL_HTTP_REQUEST', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the GraphQL query Request
|
||||
*/
|
||||
self::process_http_request();
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an HTTP header.
|
||||
*
|
||||
* @since 0.0.5
|
||||
* @access public
|
||||
*
|
||||
* @param string $key Header key.
|
||||
* @param string $value Header value.
|
||||
*/
|
||||
public static function send_header( $key, $value ) {
|
||||
|
||||
/**
|
||||
* Sanitize as per RFC2616 (Section 4.2):
|
||||
*
|
||||
* Any LWS that occurs between field-content MAY be replaced with a
|
||||
* single SP before interpreting the field value or forwarding the
|
||||
* message downstream.
|
||||
*/
|
||||
$value = preg_replace( '/\s+/', ' ', $value );
|
||||
header( apply_filters( 'graphql_send_header', sprintf( '%s: %s', $key, $value ), $key, $value ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an HTTP status code.
|
||||
*
|
||||
* @since 0.0.5
|
||||
* @access protected
|
||||
*
|
||||
* @param int $code HTTP status.
|
||||
*/
|
||||
protected static function set_status( $code ) {
|
||||
status_header( $code );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of headers to send with the HTTP response
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function get_response_headers() {
|
||||
|
||||
/**
|
||||
* Filtered list of access control headers.
|
||||
*
|
||||
* @param array $access_control_headers Array of headers to allow.
|
||||
*/
|
||||
$access_control_allow_headers = apply_filters( 'graphql_access_control_allow_headers', [
|
||||
'Authorization',
|
||||
'Content-Type'
|
||||
] );
|
||||
|
||||
$headers = [
|
||||
'Access-Control-Allow-Origin' => '*',
|
||||
'Access-Control-Allow-Headers' => implode( ', ', $access_control_allow_headers ),
|
||||
'Content-Type' => 'application/json ; charset=' . get_option( 'blog_charset' ),
|
||||
'X-Robots-Tag' => 'noindex',
|
||||
'X-Content-Type-Options' => 'nosniff',
|
||||
'X-hacker' => __( 'If you\'re reading this, you should visit github.com/wp-graphql and contribute!', 'wp-graphql' ),
|
||||
];
|
||||
|
||||
/**
|
||||
* Send nocache headers on authenticated requests.
|
||||
*
|
||||
* @since 0.0.5
|
||||
*
|
||||
* @param bool $rest_send_nocache_headers Whether to send no-cache headers.
|
||||
*/
|
||||
$send_no_cache_headers = apply_filters( 'graphql_send_nocache_headers', is_user_logged_in() );
|
||||
if ( $send_no_cache_headers ) {
|
||||
foreach ( wp_get_nocache_headers() as $no_cache_header_key => $no_cache_header_value ) {
|
||||
$headers[ $no_cache_header_key ] = $no_cache_header_value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the $headers to send
|
||||
*/
|
||||
return apply_filters( 'graphql_response_headers_to_send', $headers );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the response headers
|
||||
*
|
||||
* @param int $http_status The status code to send as a header
|
||||
*
|
||||
* @since 0.0.1
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public static function set_headers( $http_status ) {
|
||||
|
||||
if ( false === headers_sent() ) {
|
||||
|
||||
/**
|
||||
* Set the HTTP response status
|
||||
*/
|
||||
self::set_status( $http_status );
|
||||
|
||||
/**
|
||||
* Get the response headers
|
||||
*/
|
||||
$headers = self::get_response_headers();
|
||||
|
||||
/**
|
||||
* If there are headers, set them for the response
|
||||
*/
|
||||
if ( ! empty( $headers ) && is_array( $headers ) ) {
|
||||
|
||||
foreach ( $headers as $key => $value ) {
|
||||
self::send_header( $key, $value );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire an action when the headers are set
|
||||
*
|
||||
* @param array $headers The headers sent in the response
|
||||
*/
|
||||
do_action( 'graphql_response_set_headers', $headers );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the raw request entity (body).
|
||||
*
|
||||
* @since 0.0.5
|
||||
* @access public
|
||||
* @global string $HTTP_RAW_POST_DATA Raw post data.
|
||||
* @return string Raw request data.
|
||||
*/
|
||||
public static function get_raw_data() {
|
||||
|
||||
global $HTTP_RAW_POST_DATA;
|
||||
|
||||
/*
|
||||
* A bug in PHP < 5.2.2 makes $HTTP_RAW_POST_DATA not set by default,
|
||||
* but we can do it ourself.
|
||||
*/
|
||||
if ( ! isset( $HTTP_RAW_POST_DATA ) ) {
|
||||
$HTTP_RAW_POST_DATA = file_get_contents( 'php://input' );
|
||||
}
|
||||
|
||||
return $HTTP_RAW_POST_DATA;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This processes the graphql requests that come into the /graphql endpoint via an HTTP request
|
||||
*
|
||||
* @since 0.0.1
|
||||
* @access public
|
||||
* @return mixed
|
||||
*/
|
||||
public static function process_http_request() {
|
||||
|
||||
/**
|
||||
* This action can be hooked to to enable various debug tools,
|
||||
* such as enableValidation from the GraphQL Config.
|
||||
*
|
||||
* @since 0.0.4
|
||||
*/
|
||||
do_action( 'graphql_process_http_request' );
|
||||
|
||||
/**
|
||||
* Start the $response array to return for the response content
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
$response = [];
|
||||
$graphql_results = [];
|
||||
$request = '';
|
||||
$operation_name = '';
|
||||
$variables = [];
|
||||
$user = null;
|
||||
|
||||
try {
|
||||
|
||||
/**
|
||||
* Store the global post so it can be reset after GraphQL execution
|
||||
*
|
||||
* This allows for a GraphQL query to be used in the middle of post content, such as in a Shortcode
|
||||
* without disrupting the flow of the post as the global POST before and after GraphQL execution will be
|
||||
* the same.
|
||||
*/
|
||||
$global_post = ! empty( $GLOBALS['post'] ) ? $GLOBALS['post'] : null;
|
||||
|
||||
/**
|
||||
* Respond to pre-flight requests.
|
||||
*
|
||||
* @see: https://apollographql.slack.com/archives/C10HTKHPC/p1507649812000123
|
||||
* @see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Preflighted_requests
|
||||
*/
|
||||
if ( 'OPTIONS' === $_SERVER['REQUEST_METHOD'] ) {
|
||||
|
||||
self::$http_status_code = 200;
|
||||
self::set_headers( self::$http_status_code );
|
||||
exit;
|
||||
|
||||
} else if ( isset( $_SERVER['REQUEST_METHOD'] ) && $_SERVER['REQUEST_METHOD'] === 'GET' ) {
|
||||
|
||||
$data = [
|
||||
'query' => isset( $_GET['query'] ) ? wp_kses_stripslashes( sanitize_text_field( $_GET['query'] ) ) : '',
|
||||
'operationName' => isset( $_GET['operationName'] ) ? wp_kses_stripslashes( sanitize_text_field( $_GET['operationName'] ) ) : '',
|
||||
'variables' => isset( $_GET['variables'] ) ? $_GET['variables'] : '',
|
||||
];
|
||||
|
||||
/**
|
||||
* Allow the data to be filtered
|
||||
*
|
||||
* @param array $data An array containing the pieces of the data of the GraphQL request
|
||||
*/
|
||||
$data = apply_filters( 'graphql_request_data', $data );
|
||||
|
||||
/**
|
||||
* If the variables are already formatted as an array use them.
|
||||
*
|
||||
* Example:
|
||||
* ?query=query getPosts($first:Int){posts(first:$first){edges{node{id}}}}&variables[first]=1
|
||||
*/
|
||||
if ( is_array( $data['variables'] ) ) {
|
||||
$sanitized_variables = [];
|
||||
foreach ( $data['variables'] as $key => $value ) {
|
||||
$sanitized_variables[ $key ] = sanitize_text_field( $value );
|
||||
}
|
||||
$decoded_variables = $sanitized_variables;
|
||||
|
||||
/**
|
||||
* If the variables are not an array, let's attempt to decode them and convert them to an array for
|
||||
* use in the executor.
|
||||
*/
|
||||
} else {
|
||||
$decoded_variables = json_decode( wp_kses_stripslashes( $data['variables'] ), true );
|
||||
}
|
||||
|
||||
$data['variables'] = ! empty( $decoded_variables ) && is_array( $decoded_variables ) ? $decoded_variables : null;
|
||||
|
||||
|
||||
/**
|
||||
* Allow the data to be filtered
|
||||
*
|
||||
* @param array $data An array containing the pieces of the data of the GraphQL request
|
||||
*/
|
||||
$data = apply_filters( 'graphql_request_data', $data );
|
||||
|
||||
/**
|
||||
* Get the pieces of the request from the data
|
||||
*/
|
||||
$request = isset( $data['query'] ) ? $data['query'] : '';
|
||||
$operation_name = isset( $data['operationName'] ) ? $data['operationName'] : '';
|
||||
$variables = isset( $data['variables'] ) ? $data['variables'] : [];
|
||||
|
||||
|
||||
if ( false === headers_sent() ) {
|
||||
self::prepare_headers( $response, $graphql_results, $request, $operation_name, $variables, $user );
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the GraphQL request
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
$graphql_results = do_graphql_request( $request, $operation_name, $variables );
|
||||
|
||||
/**
|
||||
* Ensure the $graphql_request is returned as a proper, populated array,
|
||||
* otherwise add an error to the result
|
||||
*/
|
||||
if ( ! empty( $graphql_results ) && is_array( $graphql_results ) ) {
|
||||
$response = $graphql_results;
|
||||
} else {
|
||||
$response['errors'] = __( 'The GraphQL request returned an invalid response', 'wp-graphql' );
|
||||
}
|
||||
|
||||
self::after_execute( $response, $operation_name, $request, $variables, $graphql_results );
|
||||
|
||||
} else {
|
||||
|
||||
/**
|
||||
* If headers haven't been sent already, let's set the headers and return the JSON response
|
||||
*/
|
||||
if ( false === headers_sent() ) {
|
||||
|
||||
self::prepare_headers( $response, $graphql_results, $request, $operation_name, $variables, $user );
|
||||
|
||||
/**
|
||||
* Send the JSON response
|
||||
*/
|
||||
$server = \WPGraphQL::server();
|
||||
$response = $server->executeRequest();
|
||||
|
||||
self::after_execute( $response, $operation_name, $request, $variables, $graphql_results );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} catch ( \Exception $error ) {
|
||||
|
||||
/**
|
||||
* If there are errors, set the status to 500
|
||||
* and format the captured errors to be output properly
|
||||
*
|
||||
* @since 0.0.4
|
||||
*/
|
||||
self::$http_status_code = 500;
|
||||
$response['errors'] = [ FormattedError::createFromException( $error ) ];
|
||||
} // End try().
|
||||
|
||||
/**
|
||||
* Send the response
|
||||
*/
|
||||
wp_send_json( $response );
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prepare headers for response
|
||||
*
|
||||
* @param array $response The response of the GraphQL Request
|
||||
* @param array $graphql_results The results of the GraphQL execution
|
||||
* @param string $request The GraphQL Request
|
||||
* @param string $operation_name The operation name of the GraphQL Request
|
||||
* @param array $variables The variables applied to the GraphQL Request
|
||||
* @param \WP_User $user The current user object
|
||||
*/
|
||||
protected static function prepare_headers( $response, $graphql_results, $request, $operation_name, $variables, $user ) {
|
||||
|
||||
/**
|
||||
* Filter the $status_code before setting the headers
|
||||
*
|
||||
* @param int $status_code The status code to apply to the headers
|
||||
* @param array $response The response of the GraphQL Request
|
||||
* @param array $graphql_results The results of the GraphQL execution
|
||||
* @param string $request The GraphQL Request
|
||||
* @param string $operation_name The operation name of the GraphQL Request
|
||||
* @param array $variables The variables applied to the GraphQL Request
|
||||
* @param \WP_User $user The current user object
|
||||
*/
|
||||
$status_code = apply_filters( 'graphql_response_status_code', self::$http_status_code, $response, $graphql_results, $request, $operation_name, $variables, $user );
|
||||
|
||||
/**
|
||||
* Set the response headers
|
||||
*/
|
||||
self::set_headers( $status_code );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply filters and do actions after GraphQL Execution
|
||||
*
|
||||
* @param array $result The result of your GraphQL request
|
||||
* @param string $operation_name The name of the operation
|
||||
* @param string $request The request that GraphQL executed
|
||||
* @param array|null $variables Variables to passed to your GraphQL query
|
||||
*/
|
||||
protected static function after_execute( $result, $operation_name, $request, $variables ) {
|
||||
|
||||
/**
|
||||
* Run an action. This is a good place for debug tools to hook in to log things, etc.
|
||||
*
|
||||
* @since 0.0.4
|
||||
*
|
||||
* @param array $result The result of your GraphQL request
|
||||
* @param Schema object $schema The schema object for the root request
|
||||
* @param string $operation_name The name of the operation
|
||||
* @param string $request The request that GraphQL executed
|
||||
* @param array|null $variables Variables to passed to your GraphQL query
|
||||
*/
|
||||
do_action( 'graphql_execute', $result, \WPGraphQL::get_schema(), $operation_name, $request, $variables );
|
||||
|
||||
/**
|
||||
* Filter the $result of the GraphQL execution. This allows for the response to be filtered before
|
||||
* it's returned, allowing granular control over the response at the latest point.
|
||||
*
|
||||
* POSSIBLE USAGE EXAMPLES:
|
||||
* This could be used to ensure that certain fields never make it to the response if they match
|
||||
* certain criteria, etc. For example, this filter could be used to check if a current user is
|
||||
* allowed to see certain things, and if they are not, the $result could be filtered to remove
|
||||
* the data they should not be allowed to see.
|
||||
*
|
||||
* Or, perhaps some systems want the result to always include some additional piece of data in
|
||||
* every response, regardless of the request that was sent to it, this could allow for that
|
||||
* to be hooked in and included in the $result
|
||||
*
|
||||
* @since 0.0.5
|
||||
*
|
||||
* @param array $result The result of your GraphQL query
|
||||
* @param Schema object $schema The schema object for the root query
|
||||
* @param string $operation_name The name of the operation
|
||||
* @param string $request The request that GraphQL executed
|
||||
* @param array|null $variables Variables to passed to your GraphQL request
|
||||
*/
|
||||
$filtered_result = apply_filters( 'graphql_request_results', $result, \WPGraphQL::get_schema(), $operation_name, $request, $variables );
|
||||
|
||||
/**
|
||||
* Run an action after the result has been filtered, as the response is being returned.
|
||||
* This is a good place for debug tools to hook in to log things, etc.
|
||||
*
|
||||
* @param array $filtered_result The filtered_result of the GraphQL request
|
||||
* @param array $result The result of your GraphQL request
|
||||
* @param WPSchema $schema The schema object for the root request
|
||||
* @param string $operation_name The name of the operation
|
||||
* @param string $request The request that GraphQL executed
|
||||
* @param array|null $variables Variables to passed to your GraphQL query
|
||||
*/
|
||||
do_action( 'graphql_return_response', $filtered_result, $result, \WPGraphQL::get_schema(), $operation_name, $request, $variables );
|
||||
|
||||
/**
|
||||
* Reset the global post after execution
|
||||
*
|
||||
* This allows for a GraphQL query to be used in the middle of post content, such as in a Shortcode
|
||||
* without disrupting the flow of the post as the global POST before and after GraphQL execution will be
|
||||
* the same.
|
||||
*/
|
||||
if ( ! empty( $global_post ) ) {
|
||||
$GLOBALS['post'] = $global_post;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run an action after the HTTP Response is ready to be sent back. This might be a good place for tools
|
||||
* to hook in to track metrics, such as how long the process took from `graphql_process_http_request`
|
||||
* to here, etc.
|
||||
*
|
||||
* @param array $result The result of the GraphQL Query
|
||||
* @param array $filtered_result
|
||||
* @param string $operation_name The name of the operation
|
||||
* @param string $request The request that GraphQL executed
|
||||
* @param array $variables Variables to passed to your GraphQL query
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
do_action( 'graphql_process_http_request_response', $filtered_result, $result, $operation_name, $request, $variables );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
136
wordpress/wp-content/plugins/wp-graphql/src/Type/Avatar/AvatarType.php
Executable file
136
wordpress/wp-content/plugins/wp-graphql/src/Type/Avatar/AvatarType.php
Executable file
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL\Type\Avatar;
|
||||
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\Type\WPObjectType;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class AvatarType
|
||||
*
|
||||
* @package WPGraphQL\Type
|
||||
* @since 0.0.5
|
||||
*/
|
||||
class AvatarType extends WPObjectType {
|
||||
|
||||
/**
|
||||
* Holds the type name
|
||||
*
|
||||
* @var string $type_name
|
||||
*/
|
||||
private static $type_name;
|
||||
|
||||
/**
|
||||
* This holds the field definitions
|
||||
*
|
||||
* @var array $fields
|
||||
* @since 0.0.5
|
||||
*/
|
||||
private static $fields;
|
||||
|
||||
/**
|
||||
* WPObjectType constructor.
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
public function __construct() {
|
||||
|
||||
/**
|
||||
* Set the type_name
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
self::$type_name = 'Avatar';
|
||||
|
||||
$config = [
|
||||
'name' => self::$type_name,
|
||||
'fields' => self::fields(),
|
||||
'description' => __( 'Avatars are profile images for users. WordPress by default uses the Gravatar service to host and fetch avatars from.', 'wp-graphql' ),
|
||||
];
|
||||
|
||||
parent::__construct( $config );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* fields
|
||||
*
|
||||
* This defines the fields for the AvatarType. The fields are passed through a filter so the shape of the schema
|
||||
* can be modified
|
||||
*
|
||||
* @return array|\GraphQL\Type\Definition\FieldDefinition[]
|
||||
* @since 0.0.5
|
||||
*/
|
||||
private static function fields() {
|
||||
|
||||
if ( null === self::$fields ) {
|
||||
self::$fields = function() {
|
||||
$fields = [
|
||||
'size' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( 'The size of the avatar in pixels. A value of 96 will match a 96px x 96px gravatar image.', 'wp-graphql' ),
|
||||
],
|
||||
'height' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( 'Height of the avatar image.', 'wp-graphql' ),
|
||||
],
|
||||
'width' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( 'Width of the avatar image.', 'wp-graphql' ),
|
||||
],
|
||||
'default' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( "URL for the default image or a default type. Accepts '404' (return a 404 instead of a default image), 'retro' (8bit), 'monsterid' (monster), 'wavatar' (cartoon face), 'indenticon' (the 'quilt'), 'mystery', 'mm', or 'mysteryman' (The Oyster Man), 'blank' (transparent GIF), or 'gravatar_default' (the Gravatar logo).", 'wp-graphql' ),
|
||||
],
|
||||
'forceDefault' => [
|
||||
'type' => Types::boolean(),
|
||||
'description' => __( 'Whether to always show the default image, never the Gravatar.', 'wp-graphql' ),
|
||||
'resolve' => function( $avatar, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ( ! empty( $avatar['force_default'] ) && true === $avatar['force_default'] ) ? true : false;
|
||||
},
|
||||
],
|
||||
'rating' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( "What rating to display avatars up to. Accepts 'G', 'PG', 'R', 'X', and are judged in that order.", 'wp-graphql' ),
|
||||
],
|
||||
'scheme' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Type of url scheme to use. Typically HTTP vs. HTTPS.', 'wp-graphql' ),
|
||||
],
|
||||
'extraAttr' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'HTML attributes to insert in the IMG element. Is not sanitized.', 'wp-graphql' ),
|
||||
'resolve' => function( $avatar, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $avatar['extra_attr'] ) ? $avatar['extra_attr'] : null;
|
||||
},
|
||||
],
|
||||
'foundAvatar' => [
|
||||
'type' => Types::boolean(),
|
||||
'description' => __( 'Whether the avatar was successfully found.', 'wp-graphql' ),
|
||||
'resolve' => function( $avatar, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $avatar['found_avatar'] && true === $avatar['found_avatar'] ) ? true : false;
|
||||
},
|
||||
],
|
||||
'url' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'URL for the gravatar image source.', 'wp-graphql' ),
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* This prepares the fields by sorting them and applying a filter for adjusting the schema.
|
||||
* Because these fields are implemented via a closure the prepare_fields needs to be applied
|
||||
* to the fields directly instead of being applied to all objects extending
|
||||
* the WPObjectType class.
|
||||
*/
|
||||
return self::prepare_fields( $fields, self::$type_name );
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
return self::$fields;
|
||||
|
||||
}
|
||||
}
|
||||
33
wordpress/wp-content/plugins/wp-graphql/src/Type/Comment/CommentQuery.php
Executable file
33
wordpress/wp-content/plugins/wp-graphql/src/Type/Comment/CommentQuery.php
Executable file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
namespace WPGraphQL\Type\Comment;
|
||||
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQLRelay\Relay;
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\Data\DataSource;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
class CommentQuery {
|
||||
|
||||
/**
|
||||
* root_query
|
||||
* @return array
|
||||
*/
|
||||
public static function root_query() {
|
||||
|
||||
return [
|
||||
'type' => Types::comment(),
|
||||
'description' => __( 'Returns a Comment', 'wp-graphql' ),
|
||||
'args' => [
|
||||
'id' => Types::non_null( Types::id() ),
|
||||
],
|
||||
'resolve' => function( $source, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
$id_components = Relay::fromGlobalId( $args['id'] );
|
||||
|
||||
return DataSource::resolve_comment( $id_components['id'] );
|
||||
},
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
186
wordpress/wp-content/plugins/wp-graphql/src/Type/Comment/CommentType.php
Executable file
186
wordpress/wp-content/plugins/wp-graphql/src/Type/Comment/CommentType.php
Executable file
@@ -0,0 +1,186 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL\Type\Comment;
|
||||
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQLRelay\Relay;
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\Data\DataSource;
|
||||
use WPGraphQL\Type\Comment\Connection\CommentConnectionDefinition;
|
||||
use WPGraphQL\Type\WPObjectType;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
class CommentType extends WPObjectType {
|
||||
|
||||
/**
|
||||
* Holds the type name
|
||||
*
|
||||
* @var string $type_name
|
||||
*/
|
||||
private static $type_name;
|
||||
|
||||
/**
|
||||
* Holds the $fields definition for the CommentType
|
||||
*
|
||||
* @var $fields
|
||||
*/
|
||||
private static $fields;
|
||||
|
||||
/**
|
||||
* CommentType constructor.
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
public function __construct() {
|
||||
|
||||
/**
|
||||
* Set the type_name
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
self::$type_name = 'Comment';
|
||||
|
||||
$config = [
|
||||
'name' => self::$type_name,
|
||||
'description' => __( 'A Comment object', 'wp-graphql' ),
|
||||
'fields' => self::fields(),
|
||||
'interfaces' => [ self::node_interface() ],
|
||||
];
|
||||
|
||||
parent::__construct( $config );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This defines the fields that make up the CommentType
|
||||
*
|
||||
* @return mixed
|
||||
* @since 0.0.5
|
||||
*/
|
||||
private static function fields() {
|
||||
|
||||
if ( null === self::$fields ) {
|
||||
self::$fields = function() {
|
||||
$fields = [
|
||||
'id' => [
|
||||
'type' => Types::non_null( Types::id() ),
|
||||
'description' => __( 'The globally unique identifier for the user', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Comment $comment, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $comment->comment_ID ) ? Relay::toGlobalId( 'comment', $comment->comment_ID ) : null;
|
||||
},
|
||||
],
|
||||
'commentId' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( 'ID for the comment, unique among comments.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Comment $comment, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $comment->comment_ID ) ? $comment->comment_ID : 0;
|
||||
},
|
||||
],
|
||||
'commentedOn' => [
|
||||
'type' => Types::post_object_union(),
|
||||
'description' => __( 'The object the comment was added to', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Comment $comment, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $comment->comment_post_ID ) ? get_post( $comment->comment_post_ID ) : null;
|
||||
},
|
||||
],
|
||||
'author' => [
|
||||
'type' => Types::comment_author_union(),
|
||||
'description' => __( 'The author of the comment', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Comment $comment, $args, AppContext $context, ResolveInfo $info ) {
|
||||
/**
|
||||
* If the comment has a user associated, use it to populate the author, otherwise return
|
||||
* the $comment and the Union will use that to hydrate the CommentAuthor Type
|
||||
*/
|
||||
if ( ! empty( $comment->user_id ) ) {
|
||||
return DataSource::resolve_user( absint( $comment->user_id ) );
|
||||
} else {
|
||||
return DataSource::resolve_comment_author( $comment->comment_author_email );
|
||||
}
|
||||
},
|
||||
],
|
||||
'authorIp' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'IP address for the author. This field is equivalent to WP_Comment->comment_author_IP and the value matching the `comment_author_IP` column in SQL.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Comment $comment, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $comment->comment_author_IP ) ? $comment->comment_author_IP : '';
|
||||
},
|
||||
],
|
||||
'date' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Date the comment was posted in local time. This field is equivalent to WP_Comment->date and the value matching the `date` column in SQL.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Comment $comment, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $comment->comment_date ) ? $comment->comment_date : '';
|
||||
},
|
||||
],
|
||||
'dateGmt' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Date the comment was posted in GMT. This field is equivalent to WP_Comment->date_gmt and the value matching the `date_gmt` column in SQL.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Comment $comment, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $comment->comment_date_gmt ) ? $comment->comment_date_gmt : '';
|
||||
},
|
||||
],
|
||||
'content' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Content of the comment. This field is equivalent to WP_Comment->comment_content and the value matching the `comment_content` column in SQL.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Comment $comment, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $comment->comment_content ) ? $comment->comment_content : '';
|
||||
},
|
||||
],
|
||||
'karma' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( 'Karma value for the comment. This field is equivalent to WP_Comment->comment_karma and the value matching the `comment_karma` column in SQL.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Comment $comment, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $comment->comment_karma ) ? $comment->comment_karma : 0;
|
||||
},
|
||||
],
|
||||
'approved' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The approval status of the comment. This field is equivalent to WP_Comment->comment_approved and the value matching the `comment_approved` column in SQL.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Comment $comment, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $comment->comment_approved ) ? $comment->comment_approved : '';
|
||||
},
|
||||
],
|
||||
'agent' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'User agent used to post the comment. This field is equivalent to WP_Comment->comment_agent and the value matching the `comment_agent` column in SQL.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Comment $comment, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $comment->comment_agent ) ? $comment->comment_agent : '';
|
||||
},
|
||||
],
|
||||
'type' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Type of comment. This field is equivalent to WP_Comment->comment_type and the value matching the `comment_type` column in SQL.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Comment $comment, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $comment->comment_type ) ? $comment->comment_type : '';
|
||||
},
|
||||
],
|
||||
'parent' => [
|
||||
'type' => Types::comment(),
|
||||
'description' => __( 'Parent comment of current comment. This field is equivalent to the WP_Comment instance matching the WP_Comment->comment_parent ID.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Comment $comment, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return get_comment( $comment->comment_parent );
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Add a comments_connection to display the child comments
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
$fields['children'] = CommentConnectionDefinition::connection( 'Children' );
|
||||
|
||||
/**
|
||||
* This prepares the fields by sorting them and applying a filter for adjusting the schema.
|
||||
* Because these fields are implemented via a closure the prepare_fields needs to be applied
|
||||
* to the fields directly instead of being applied to all objects extending
|
||||
* the WPObjectType class.
|
||||
*/
|
||||
return self::prepare_fields( $fields, self::$type_name );
|
||||
};
|
||||
}
|
||||
|
||||
return self::$fields;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
<?php
|
||||
namespace WPGraphQL\Type\Comment\Connection;
|
||||
|
||||
use GraphQL\Type\Definition\EnumType;
|
||||
use WPGraphQL\Type\WPEnumType;
|
||||
use WPGraphQL\Type\WPInputObjectType;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class CommentConnectionArgs
|
||||
*
|
||||
* This sets up the Query Args for comments connections, which uses WP_Comment_Query, so this defines the allowed
|
||||
* input fields that will be passed to the WP_Comment_Query
|
||||
*
|
||||
* @package WPGraphQL\Type
|
||||
* @since 0.0.5
|
||||
*/
|
||||
class CommentConnectionArgs extends WPInputObjectType {
|
||||
|
||||
/**
|
||||
* This holds the field definitions
|
||||
* @var array $fields
|
||||
* @since 0.0.5
|
||||
*/
|
||||
public static $fields = [];
|
||||
|
||||
/**
|
||||
* Holds the orderby Enum definition
|
||||
* @var EnumType
|
||||
* @since 0.0.5
|
||||
*/
|
||||
private static $comments_orderby_enum;
|
||||
|
||||
/**
|
||||
* Holds the order Enum definition
|
||||
* @var EnumType
|
||||
*/
|
||||
private static $comments_order;
|
||||
|
||||
/**
|
||||
* CommentConnectionArgs constructor.
|
||||
* @param array $config
|
||||
* @param string $connection
|
||||
* @since 0.0.5
|
||||
*/
|
||||
public function __construct( $config = [], $connection ) {
|
||||
$config['name'] = ucfirst( $connection ) . 'CommentArgs';
|
||||
$config['fields'] = self::fields( $connection );
|
||||
parent::__construct( $config );
|
||||
}
|
||||
|
||||
/**
|
||||
* fields
|
||||
*
|
||||
* This defines the fields that make up the CommentConnectionArgs
|
||||
*
|
||||
* @return array
|
||||
* @since 0.0.5
|
||||
*/
|
||||
private static function fields( $connection ) {
|
||||
|
||||
if ( empty( self::$fields[ $connection ] ) ) {
|
||||
$fields = [
|
||||
'authorEmail' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Comment author email address.', 'wp-graphql' ),
|
||||
],
|
||||
'authorUrl' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Comment author URL.', 'wp-graphql' ),
|
||||
],
|
||||
'authorIn' => [
|
||||
'type' => Types::list_of( Types::int() ),
|
||||
'description' => __( 'Array of author IDs to include comments for.', 'wp-graphql' ),
|
||||
],
|
||||
'authorNotIn' => [
|
||||
'type' => Types::list_of( Types::int() ),
|
||||
'description' => __( 'Array of author IDs to exclude comments for.', 'wp-graphql' ),
|
||||
],
|
||||
'commentIn' => [
|
||||
'type' => Types::list_of( Types::int() ),
|
||||
'description' => __( 'Array of comment IDs to include.', 'wp-graphql' ),
|
||||
],
|
||||
'commentNotIn' => [
|
||||
'type' => Types::list_of( Types::int() ),
|
||||
'description' => __( 'Array of IDs of users whose unapproved comments will be returned by the
|
||||
query regardless of status.', 'wp-graphql' ),
|
||||
],
|
||||
'includeUnapproved' => [
|
||||
'type' => Types::list_of( Types::int() ),
|
||||
'description' => __( 'Array of author IDs to include comments for.', 'wp-graphql' ),
|
||||
],
|
||||
'karma' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( 'Karma score to retrieve matching comments for.', 'wp-graphql' ),
|
||||
],
|
||||
'orderby' => [
|
||||
'type' => self::comments_orderby_enum(),
|
||||
'description' => __( 'Field to order the comments by.', 'wp-graphql' ),
|
||||
],
|
||||
'order' => [
|
||||
'type' => self::comment_order(),
|
||||
],
|
||||
'parent' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( 'Parent ID of comment to retrieve children of.', 'wp-graphql' ),
|
||||
],
|
||||
'parentIn' => [
|
||||
'type' => Types::list_of( Types::int() ),
|
||||
'description' => __( 'Array of parent IDs of comments to retrieve children for.', 'wp-graphql' ),
|
||||
],
|
||||
'parentNotIn' => [
|
||||
'type' => Types::list_of( Types::int() ),
|
||||
'description' => __( 'Array of parent IDs of comments *not* to retrieve children
|
||||
for.', 'wp-graphql' ),
|
||||
],
|
||||
'contentAuthorIn' => [
|
||||
'type' => Types::list_of( Types::int() ),
|
||||
'description' => __( 'Array of author IDs to retrieve comments for.', 'wp-graphql' ),
|
||||
],
|
||||
'contentAuthorNotIn' => [
|
||||
'type' => Types::list_of( Types::int() ),
|
||||
'description' => __( 'Array of author IDs *not* to retrieve comments for.', 'wp-graphql' ),
|
||||
],
|
||||
'contentId' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( 'Limit results to those affiliated with a given content object
|
||||
ID.', 'wp-graphql' ),
|
||||
],
|
||||
'contentIdIn' => [
|
||||
'type' => Types::list_of( Types::int() ),
|
||||
'description' => __( 'Array of content object IDs to include affiliated comments
|
||||
for.', 'wp-graphql' ),
|
||||
],
|
||||
'contentIdNotIn' => [
|
||||
'type' => Types::list_of( Types::int() ),
|
||||
'description' => __( 'Array of content object IDs to exclude affiliated comments
|
||||
for.', 'wp-graphql' ),
|
||||
],
|
||||
'contentAuthor' => [
|
||||
'type' => Types::list_of( Types::int() ),
|
||||
'description' => __( 'Content object author ID to limit results by.', 'wp-graphql' ),
|
||||
],
|
||||
'contentStatus' => [
|
||||
'type' => Types::list_of( Types::post_status_enum() ),
|
||||
'description' => __( 'Array of content object statuses to retrieve affiliated comments for.
|
||||
Pass \'any\' to match any value.', 'wp-graphql' ),
|
||||
],
|
||||
'contentType' => [
|
||||
'type' => Types::list_of( Types::post_type_enum() ),
|
||||
'description' => __( 'Content object type or array of types to retrieve affiliated comments for. Pass \'any\' to match any value.', 'wp-graphql' ),
|
||||
],
|
||||
'contentName' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Content object name to retrieve affiliated comments for.', 'wp-graphql' ),
|
||||
],
|
||||
'contentParent' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( 'Content Object parent ID to retrieve affiliated comments for.', 'wp-graphql' ),
|
||||
],
|
||||
'search' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Search term(s) to retrieve matching comments for.', 'wp-graphql' ),
|
||||
],
|
||||
'status' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Comment status to limit results by.', 'wp-graphql' ),
|
||||
],
|
||||
'commentType' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Include comments of a given type.', 'wp-graphql' ),
|
||||
],
|
||||
'commentTypeIn' => [
|
||||
'type' => Types::list_of( Types::string() ),
|
||||
'description' => __( 'Include comments from a given array of comment types.', 'wp-graphql' ),
|
||||
],
|
||||
'commentTypeNotIn' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Exclude comments from a given array of comment types.', 'wp-graphql' ),
|
||||
],
|
||||
'userId' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( 'Include comments for a specific user ID.', 'wp-graphql' ),
|
||||
],
|
||||
];
|
||||
|
||||
self::$fields[ $connection ] = self::prepare_fields( $fields, ucfirst( $connection ) . 'CommentArgs' );
|
||||
}
|
||||
return ! empty( self::$fields[ $connection ] ) ? self::$fields[ $connection ] : null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* comments_orderby_enum
|
||||
*
|
||||
* Defines the orderby Enum values for ordering a comments query
|
||||
*
|
||||
* @return EnumType
|
||||
* @since 0.0.5
|
||||
*/
|
||||
private static function comments_orderby_enum() {
|
||||
|
||||
if ( null === self::$comments_orderby_enum ) {
|
||||
self::$comments_orderby_enum = new WPEnumType( [
|
||||
'name' => 'CommentsOrderby',
|
||||
'values' => [
|
||||
'COMMENT_AGENT' => [
|
||||
'value' => 'comment_agent',
|
||||
],
|
||||
'COMMENT_APPROVED' => [
|
||||
'value' => 'comment_approved',
|
||||
],
|
||||
'COMMENT_AUTHOR' => [
|
||||
'value' => 'comment_author',
|
||||
],
|
||||
'COMMENT_AUTHOR_EMAIL' => [
|
||||
'value' => 'comment_author_email',
|
||||
],
|
||||
'COMMENT_AUTHOR_IP' => [
|
||||
'value' => 'comment_author_IP',
|
||||
],
|
||||
'COMMENT_AUTHOR_URL' => [
|
||||
'value' => 'comment_author_url',
|
||||
],
|
||||
'COMMENT_CONTENT' => [
|
||||
'value' => 'comment_content',
|
||||
],
|
||||
'COMMENT_DATE' => [
|
||||
'value' => 'comment_date',
|
||||
],
|
||||
'COMMENT_DATE_GMT' => [
|
||||
'value' => 'comment_date_gmt',
|
||||
],
|
||||
'COMMENT_ID' => [
|
||||
'value' => 'comment_ID',
|
||||
],
|
||||
'COMMENT_KARMA' => [
|
||||
'value' => 'comment_karma',
|
||||
],
|
||||
'COMMENT_PARENT' => [
|
||||
'value' => 'comment_parent',
|
||||
],
|
||||
'COMMENT_POST_ID' => [
|
||||
'value' => 'comment_post_ID',
|
||||
],
|
||||
'COMMENT_TYPE' => [
|
||||
'value' => 'comment_type',
|
||||
],
|
||||
'USER_ID' => [
|
||||
'value' => 'user_id',
|
||||
],
|
||||
'COMMENT_IN' => [
|
||||
'value' => 'comment__in',
|
||||
],
|
||||
],
|
||||
] );
|
||||
}
|
||||
return self::$comments_orderby_enum;
|
||||
}
|
||||
|
||||
private static function comment_order() {
|
||||
|
||||
if ( null === self::$comments_order ) {
|
||||
self::$comments_order = new WPEnumType( [
|
||||
'name' => 'CommentsOrder',
|
||||
'values' => [
|
||||
'ASC' => [
|
||||
'value' => 'ASC',
|
||||
],
|
||||
'DESC' => [
|
||||
'value' => 'DESC',
|
||||
],
|
||||
],
|
||||
'defaultValue' => 'DESC',
|
||||
] );
|
||||
}
|
||||
|
||||
return self::$comments_order;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL\Type\Comment\Connection;
|
||||
|
||||
use GraphQL\Type\Definition\InputObjectType;
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQLRelay\Relay;
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\Data\DataSource;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class CommentConnectionDefinition
|
||||
*
|
||||
* @package WPGraphQL\Type\Comment\Connection
|
||||
* @since 0.0.5
|
||||
*/
|
||||
class CommentConnectionDefinition {
|
||||
|
||||
/**
|
||||
* @var array connection
|
||||
* @since 0.0.5
|
||||
*/
|
||||
private static $connection = [];
|
||||
|
||||
/**
|
||||
* Holds the input $args for the Connection
|
||||
*
|
||||
* @var $args InputObjectType
|
||||
*/
|
||||
private static $args = [];
|
||||
|
||||
/**
|
||||
* connection
|
||||
* This sets up a connection of comments
|
||||
*
|
||||
* @param string $from_type
|
||||
*
|
||||
* @return mixed
|
||||
* @since 0.0.5
|
||||
*/
|
||||
public static function connection( $from_type = 'Root' ) {
|
||||
|
||||
if ( empty( self::$connection[ $from_type ] ) ) {
|
||||
$connection = Relay::connectionDefinitions( [
|
||||
'nodeType' => Types::comment(),
|
||||
'name' => ucfirst( $from_type ) . 'Comments',
|
||||
'connectionFields' => function() {
|
||||
return [
|
||||
'nodes' => [
|
||||
'type' => Types::list_of( Types::comment() ),
|
||||
'description' => __( 'The nodes of the connection, without the edges', 'wp-graphql' ),
|
||||
'resolve' => function( $source, $args, $context, $info ) {
|
||||
return ! empty( $source['nodes'] ) ? $source['nodes'] : [];
|
||||
},
|
||||
],
|
||||
];
|
||||
},
|
||||
] );
|
||||
|
||||
/**
|
||||
* Add the "where" args to the commentConnection
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
$args[ $from_type ] = [
|
||||
'where' => [
|
||||
'name' => 'where',
|
||||
'type' => self::args( ucfirst( $from_type ) . 'Comments' ),
|
||||
],
|
||||
];
|
||||
|
||||
self::$connection[ $from_type ] = [
|
||||
'type' => $connection['connectionType'],
|
||||
'description' => __( 'A collection of comment objects', 'wp-graphql' ),
|
||||
'args' => array_merge( Relay::connectionArgs(), $args[ $from_type ] ),
|
||||
'resolve' => function( $source, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return DataSource::resolve_comments_connection( $source, $args, $context, $info );
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return ! empty( self::$connection[ $from_type ] ) ? self::$connection[ $from_type ] : null;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the $args to use for the connection
|
||||
*
|
||||
* @param string $connection
|
||||
*
|
||||
* @return mixed
|
||||
* @since 0.0.5
|
||||
*/
|
||||
private static function args( $connection ) {
|
||||
|
||||
if ( empty( self::$args[ $connection ] ) ) {
|
||||
self::$args[ $connection ] = new CommentConnectionArgs( [], $connection );
|
||||
}
|
||||
|
||||
return self::$args[ $connection ];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
<?php
|
||||
namespace WPGraphQL\Type\Comment\Connection;
|
||||
|
||||
use GraphQL\Error\UserError;
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQLRelay\Connection\ArrayConnection;
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\Data\ConnectionResolver;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class CommentConnectionResolver - Connects the comments to other objects
|
||||
*
|
||||
* @package WPGraphQL\Data\Resolvers
|
||||
*/
|
||||
class CommentConnectionResolver extends ConnectionResolver {
|
||||
|
||||
/**
|
||||
* This prepares the $query_args for use in the connection query. This is where default $args are set, where dynamic
|
||||
* $args from the $source get set, and where mapping the input $args to the actual $query_args occurs.
|
||||
*
|
||||
* @param $source
|
||||
* @param array $args
|
||||
* @param AppContext $context
|
||||
* @param ResolveInfo $info
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get_query_args( $source, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
|
||||
/**
|
||||
* Prepare for later use
|
||||
*/
|
||||
$last = ! empty( $args['last'] ) ? $args['last'] : null;
|
||||
$first = ! empty( $args['first'] ) ? $args['first'] : null;
|
||||
|
||||
/**
|
||||
* Don't calculate the total rows, it's not needed and can be expensive
|
||||
*/
|
||||
$query_args['no_found_rows'] = true;
|
||||
|
||||
/**
|
||||
* Set the default comment_status for Comment Queries to be "approved"
|
||||
*/
|
||||
$query_args['comment_status'] = 'approved';
|
||||
|
||||
/**
|
||||
* Set the default comment_parent for Comment Queries to be "0" to only get top level comments
|
||||
*/
|
||||
$query_args['parent'] = 0;
|
||||
|
||||
/**
|
||||
* Set the number, ensuring it doesn't exceed the amount set as the $max_query_amount
|
||||
*
|
||||
* @since 0.0.6
|
||||
*/
|
||||
$query_args['number'] = min( max( absint( $first ), absint( $last ), 10 ), self::get_query_amount( $source, $args, $context, $info ) ) + 1;
|
||||
|
||||
/**
|
||||
* Set the default order
|
||||
*/
|
||||
$query_args['orderby'] = 'comment_date';
|
||||
|
||||
/**
|
||||
* Take any of the $args that were part of the GraphQL query and map their
|
||||
* GraphQL names to the WP_Term_Query names to be used in the WP_Term_Query
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
$input_fields = [];
|
||||
if ( ! empty( $args['where'] ) ) {
|
||||
$input_fields = self::sanitize_input_fields( $args['where'], $source, $args, $context, $info );
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the default $query_args with the $args that were entered
|
||||
* in the query.
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
if ( ! empty( $input_fields ) ) {
|
||||
$query_args = array_merge( $query_args, $input_fields );
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw an exception if the query is attempted to be queried by
|
||||
*/
|
||||
if ( 'comment__in' === $query_args['orderby'] && empty( $query_args['comment__in'] ) ) {
|
||||
throw new UserError( __( 'In order to sort by comment__in, an array of IDs must be passed as the commentIn argument', 'wp-graphql' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* If there's no orderby params in the inputArgs, set order based on the first/last argument
|
||||
*/
|
||||
if ( empty( $query_args['order'] ) ) {
|
||||
$query_args['order'] = ! empty( $last ) ? 'ASC' : 'DESC';
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the graphql_cursor_offset
|
||||
*/
|
||||
$query_args['graphql_cursor_offset'] = self::get_offset( $args );
|
||||
$query_args['graphql_cursor_compare'] = ( ! empty( $last ) ) ? '>' : '<';
|
||||
|
||||
/**
|
||||
* Pass the graphql $args to the WP_Query
|
||||
*/
|
||||
$query_args['graphql_args'] = $args;
|
||||
|
||||
/**
|
||||
* Handle setting dynamic $query_args based on the source (higher level query)
|
||||
*/
|
||||
if ( true === is_object( $source ) ) {
|
||||
switch ( true ) {
|
||||
case $source instanceof \WP_Post:
|
||||
$query_args['post_id'] = absint( $source->ID );
|
||||
break;
|
||||
case $source instanceof \WP_User:
|
||||
$query_args['user_id'] = absint( $source->ID );
|
||||
break;
|
||||
case $source instanceof \WP_Comment:
|
||||
$query_args['parent'] = absint( $source->comment_ID );
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the query_args that should be applied to the query. This filter is applied AFTER the input args from
|
||||
* the GraphQL Query have been applied and has the potential to override the GraphQL Query Input Args.
|
||||
*
|
||||
* @param array $query_args array of query_args being passed to the
|
||||
* @param mixed $source source passed down from the resolve tree
|
||||
* @param array $args array of arguments input in the field as part of the GraphQL query
|
||||
* @param AppContext $context object passed down zthe resolve tree
|
||||
* @param ResolveInfo $info info about fields passed down the resolve tree
|
||||
*
|
||||
* @since 0.0.6
|
||||
*/
|
||||
$query_args = apply_filters( 'graphql_comment_connection_query_args', $query_args, $source, $args, $context, $info );
|
||||
|
||||
return $query_args;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param $query_args
|
||||
*
|
||||
* @return \WP_Comment_Query
|
||||
*/
|
||||
public static function get_query( $query_args ) {
|
||||
$query = new \WP_Comment_Query;
|
||||
$query->query( $query_args );
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $query The query that was processed to retrieve connection data
|
||||
* @param array $items The array of connected items
|
||||
* @param mixed $source The source being passed down the resolve tree
|
||||
* @param array $args The Input args for the field
|
||||
* @param AppContext $context The AppContext passed down the resolve tree
|
||||
* @param ResolveInfo $info The ResolveInfo passed down the resolve tree
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_connection( $query, array $items, $source, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
|
||||
/**
|
||||
* Get the $posts from the query
|
||||
*/
|
||||
$items = ! empty( $items ) && is_array( $items ) ? $items : [];
|
||||
|
||||
/**
|
||||
* Set whether there is or is not another page
|
||||
*/
|
||||
$has_previous_page = ( ! empty( $args['last'] ) && count( $items ) > self::get_amount_requested( $args ) ) ? true : false;
|
||||
$has_next_page = ( ! empty( $args['first'] ) && count( $items ) > self::get_amount_requested( $args ) ) ? true : false;
|
||||
|
||||
/**
|
||||
* Slice the array to the amount of items that were requested
|
||||
*/
|
||||
$items = array_slice( $items, 0, self::get_amount_requested( $args ) );
|
||||
|
||||
/**
|
||||
* Get the edges from the $items
|
||||
*/
|
||||
$edges = self::get_edges( $items, $source, $args, $context, $info );
|
||||
|
||||
/**
|
||||
* Find the first_edge and last_edge
|
||||
*/
|
||||
$first_edge = $edges ? $edges[0] : null;
|
||||
$last_edge = $edges ? $edges[ count( $edges ) - 1 ] : null;
|
||||
$connection = [
|
||||
'edges' => $edges,
|
||||
'pageInfo' => [
|
||||
'hasPreviousPage' => $has_previous_page,
|
||||
'hasNextPage' => $has_next_page,
|
||||
'startCursor' => ! empty( $first_edge['cursor'] ) ? $first_edge['cursor'] : null,
|
||||
'endCursor' => ! empty( $last_edge['cursor'] ) ? $last_edge['cursor'] : null,
|
||||
],
|
||||
'nodes' => $items,
|
||||
];
|
||||
|
||||
return $connection;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an array of items and returns the edges
|
||||
*
|
||||
* @param $items
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_edges( $items, $source, $args, $context, $info ) {
|
||||
$edges = [];
|
||||
/**
|
||||
* If we're doing backward pagination we want to reverse the array before
|
||||
* returning it to the edges
|
||||
*/
|
||||
if ( ! empty( $args['last'] ) ) {
|
||||
$items = array_reverse( $items );
|
||||
}
|
||||
if ( ! empty( $items ) && is_array( $items ) ) {
|
||||
foreach ( $items as $item ) {
|
||||
$edges[] = [
|
||||
'cursor' => ArrayConnection::offsetToCursor( $item->comment_ID ),
|
||||
'node' => $item,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $edges;
|
||||
}
|
||||
|
||||
/**
|
||||
* This sets up the "allowed" args, and translates the GraphQL-friendly keys to
|
||||
* WP_Comment_Query friendly keys.
|
||||
*
|
||||
* There's probably a cleaner/more dynamic way to approach this, but this was quick. I'd be
|
||||
* down to explore more dynamic ways to map this, but for now this gets the job done.
|
||||
*
|
||||
* @param array $args The array of query arguments
|
||||
* @param mixed $source The query results
|
||||
* @param array $all_args Array of all of the original arguments (not just the "where"
|
||||
* args)
|
||||
* @param AppContext $context The AppContext object
|
||||
* @param ResolveInfo $info The ResolveInfo object for the query
|
||||
*
|
||||
* @since 0.0.5
|
||||
* @access private
|
||||
* @return array
|
||||
*/
|
||||
public static function sanitize_input_fields( array $args, $source, array $all_args, AppContext $context, ResolveInfo $info ) {
|
||||
|
||||
$arg_mapping = [
|
||||
'authorEmail' => 'author_email',
|
||||
'authorIn' => 'author__in',
|
||||
'authorNotIn' => 'author__not_in',
|
||||
'authorUrl' => 'author_url',
|
||||
'commentIn' => 'comment__in',
|
||||
'commentNotIn' => 'comment__not_in',
|
||||
'contentAuthor' => 'post_author',
|
||||
'contentAuthorIn' => 'post_author__in',
|
||||
'contentAuthorNotIn' => 'post_author__not_in',
|
||||
'contentId' => 'post_id',
|
||||
'contentIdIn' => 'post__in',
|
||||
'contentIdNotIn' => 'post__not_in',
|
||||
'contentName' => 'post_name',
|
||||
'contentParent' => 'post_parent',
|
||||
'contentStatus' => 'post_status',
|
||||
'contentType' => 'post_type',
|
||||
'includeUnapproved' => 'includeUnapproved',
|
||||
'parentIn' => 'parent__in',
|
||||
'parentNotIn' => 'parent__not_in',
|
||||
'userId' => 'user_id',
|
||||
];
|
||||
|
||||
/**
|
||||
* Map and sanitize the input args to the WP_Comment_Query compatible args
|
||||
*/
|
||||
$query_args = Types::map_input( $args, $arg_mapping );
|
||||
|
||||
/**
|
||||
* Filter the input fields
|
||||
*
|
||||
* This allows plugins/themes to hook in and alter what $args should be allowed to be passed
|
||||
* from a GraphQL Query to the get_terms query
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
$query_args = apply_filters( 'graphql_map_input_fields_to_wp_comment_query', $query_args, $args, $source, $all_args, $context, $info );
|
||||
|
||||
return ! empty( $query_args ) && is_array( $query_args ) ? $query_args : [];
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL\Type\CommentAuthor;
|
||||
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQLRelay\Relay;
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\Type\WPObjectType;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
class CommentAuthorType extends WPObjectType {
|
||||
|
||||
/**
|
||||
* Holds the type name
|
||||
*
|
||||
* @var string $type_name
|
||||
*/
|
||||
private static $type_name;
|
||||
|
||||
/**
|
||||
* Holds the $fields definition for the CommentAuthorType
|
||||
*
|
||||
* @var $fields
|
||||
*/
|
||||
private static $fields;
|
||||
|
||||
/**
|
||||
* CommentAuthorType constructor.
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
public function __construct() {
|
||||
|
||||
/**
|
||||
* Set the type_name
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
self::$type_name = 'CommentAuthor';
|
||||
|
||||
$config = [
|
||||
'name' => self::$type_name,
|
||||
'description' => __( 'A Comment Author object', 'wp-graphql' ),
|
||||
'fields' => self::fields(),
|
||||
'interfaces' => [ self::node_interface() ],
|
||||
];
|
||||
|
||||
parent::__construct( $config );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This defines the fields that make up the CommentAuthorType
|
||||
*
|
||||
* @return mixed
|
||||
* @since 0.0.5
|
||||
*/
|
||||
private static function fields() {
|
||||
|
||||
if ( null === self::$fields ) {
|
||||
self::$fields = function() {
|
||||
$fields = [
|
||||
'id' => [
|
||||
'type' => Types::non_null( Types::id() ),
|
||||
'description' => __( 'The globally unique identifier for the Comment Author user', 'wp-graphql' ),
|
||||
'resolve' => function( array $comment_author, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $comment_author['comment_author_email'] ) ? Relay::toGlobalId( 'commentAuthor', $comment_author['comment_author_email'] ) : null;
|
||||
},
|
||||
],
|
||||
'name' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The name for the comment author.', 'wp-graphql' ),
|
||||
'resolve' => function( array $comment_author, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $comment_author['comment_author'] ) ? $comment_author['comment_author'] : '';
|
||||
},
|
||||
],
|
||||
'email' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The email for the comment author', 'wp-graphql' ),
|
||||
'resolve' => function( array $comment_author, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $comment_author['comment_author_email'] ) ? $comment_author['comment_author_email'] : '';
|
||||
},
|
||||
],
|
||||
'url' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The url the comment author.', 'wp-graphql' ),
|
||||
'resolve' => function( array $comment_author, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $comment_author['comment_author_url'] ) ? $comment_author['comment_author_url'] : '';
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* This prepares the fields by sorting them and applying a filter for adjusting the schema.
|
||||
* Because these fields are implemented via a closure the prepare_fields needs to be applied
|
||||
* to the fields directly instead of being applied to all objects extending
|
||||
* the WPObjectType class.
|
||||
*/
|
||||
return self::prepare_fields( $fields, self::$type_name );
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
return self::$fields;
|
||||
}
|
||||
|
||||
}
|
||||
82
wordpress/wp-content/plugins/wp-graphql/src/Type/EditLock/EditLockType.php
Executable file
82
wordpress/wp-content/plugins/wp-graphql/src/Type/EditLock/EditLockType.php
Executable file
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
namespace WPGraphQL\Type\EditLock;
|
||||
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\Data\DataSource;
|
||||
use WPGraphQL\Type\WPObjectType;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
class EditLockType extends WPObjectType {
|
||||
|
||||
/**
|
||||
* Holds the $fields definition for the PostObjectType
|
||||
*
|
||||
* @var $fields
|
||||
*/
|
||||
private static $type_name;
|
||||
|
||||
/**
|
||||
* Holds the post_type_object
|
||||
*
|
||||
* @var object $post_type_object
|
||||
*/
|
||||
private static $fields;
|
||||
|
||||
/**
|
||||
* EditLockType constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
|
||||
self::$type_name = 'EditLock';
|
||||
|
||||
$config = [
|
||||
'name' => self::$type_name,
|
||||
'description' => __( 'Info on whether the object is locked by another user editing it', 'wp-graphql' ),
|
||||
'fields' => function() {
|
||||
return self::fields();
|
||||
},
|
||||
];
|
||||
|
||||
parent::__construct( $config );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the fields for the EditLock type
|
||||
* @return mixed|null
|
||||
*/
|
||||
protected static function fields() {
|
||||
|
||||
if ( null === self::$fields ) {
|
||||
|
||||
$fields = [
|
||||
'editTime' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The time when the object was last edited', 'wp-graphql' ),
|
||||
'resolve' => function( $edit_lock, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
$time = ( is_array( $edit_lock ) && ! empty( $edit_lock[0] ) ) ? $edit_lock[0] : null;
|
||||
|
||||
return ! empty( $time ) ? date( 'Y-m-d H:i:s', $time ) : null;
|
||||
},
|
||||
],
|
||||
'user' => [
|
||||
'type' => Types::user(),
|
||||
'description' => __( 'The user that most recently edited the object', 'wp-graphql' ),
|
||||
'resolve' => function( $edit_lock, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
$user_id = ( is_array( $edit_lock ) && ! empty( $edit_lock[1] ) ) ? $edit_lock[1] : null;
|
||||
|
||||
return ! empty( $user_id ) ? DataSource::resolve_user( $user_id ) : null;
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
self::$fields = self::prepare_fields( $fields, self::$type_name );
|
||||
|
||||
}
|
||||
|
||||
return ! empty( self::$fields ) ? self::$fields : null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL\Type\Enum;
|
||||
|
||||
use WPGraphQL\Type\WPEnumType;
|
||||
|
||||
/**
|
||||
* Class MediaItemStatusEnumType
|
||||
*
|
||||
* This defines an EnumType with allowed post stati for attachments in WordPress.
|
||||
* Attachments do not have the same status capabilities as other post types, see here
|
||||
* for reference: https://github.com/WordPress/WordPress/blob/master/wp-includes/post.php#L3072
|
||||
*
|
||||
* @package WPGraphQL\Type\Enum
|
||||
*/
|
||||
class MediaItemStatusEnumType extends WPEnumType {
|
||||
|
||||
/**
|
||||
* This holds the enum values array
|
||||
*
|
||||
* @var array $values
|
||||
*/
|
||||
private static $values;
|
||||
|
||||
public function __construct() {
|
||||
$config = [
|
||||
'name' => 'MediaItemStatus',
|
||||
'description' => __( 'The status of the media item object.', 'wp-graphql' ),
|
||||
'values' => self::values(),
|
||||
];
|
||||
parent::__construct( $config );
|
||||
}
|
||||
|
||||
/**
|
||||
* values
|
||||
* Creates a list of post_stati that can be used to query by.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private static function values() {
|
||||
|
||||
/**
|
||||
* Set the default, if no values are built dynamically
|
||||
*
|
||||
*/
|
||||
self::$values = [
|
||||
'INHERIT' => [
|
||||
'value' => 'inherit',
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the dynamic list of post_stati
|
||||
*/
|
||||
$post_stati = [
|
||||
'inherit',
|
||||
'private',
|
||||
'trash',
|
||||
'auto-draft',
|
||||
];
|
||||
|
||||
/**
|
||||
* If there are $post_stati, create the $values based on them
|
||||
*/
|
||||
if ( ! empty( $post_stati ) && is_array( $post_stati ) ) {
|
||||
/**
|
||||
* Reset the array
|
||||
*/
|
||||
self::$values = [];
|
||||
/**
|
||||
* Loop through the post_stati
|
||||
*/
|
||||
foreach ( $post_stati as $status ) {
|
||||
|
||||
$formatted_status = strtoupper( preg_replace( '/[^A-Za-z0-9]/i', '_', $status ) );
|
||||
|
||||
self::$values[ $formatted_status ] = [
|
||||
'name' => $formatted_status,
|
||||
'description' => sprintf( __( 'Objects with the %1$s status', 'wp-graphql' ), $status ),
|
||||
'value' => $status,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the $values
|
||||
*/
|
||||
return self::$values;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
81
wordpress/wp-content/plugins/wp-graphql/src/Type/Enum/MimeTypeEnumType.php
Executable file
81
wordpress/wp-content/plugins/wp-graphql/src/Type/Enum/MimeTypeEnumType.php
Executable file
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL\Type\Enum;
|
||||
|
||||
use WPGraphQL\Type\WPEnumType;
|
||||
|
||||
/**
|
||||
* Class MimeTypeEnumType
|
||||
*
|
||||
* This defines an EnumType with allowed mime types that are registered to WordPress.
|
||||
*
|
||||
* @package WPGraphQL\Type\Enum
|
||||
* @since 0.0.5
|
||||
*/
|
||||
class MimeTypeEnumType extends WPEnumType {
|
||||
|
||||
/**
|
||||
* This holds the enum values array
|
||||
*
|
||||
* @var array $values
|
||||
*/
|
||||
private static $values;
|
||||
|
||||
/**
|
||||
* MimeTypeEnumType constructor.
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
public function __construct() {
|
||||
|
||||
$config = [
|
||||
'name' => 'MimeType',
|
||||
'description' => __( 'The MimeType of the object', 'wp-graphql' ),
|
||||
'values' => self::values(),
|
||||
];
|
||||
|
||||
parent::__construct( $config );
|
||||
}
|
||||
|
||||
/**
|
||||
* values
|
||||
* Returns the values to be used in the Enum
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
private static function values() {
|
||||
|
||||
if ( null === self::$values ) {
|
||||
|
||||
/**
|
||||
* Establish a default MimeType value to ensure we don't
|
||||
* return null values
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
self::$values = [
|
||||
'IMAGE_JPEG' => [
|
||||
'value' => 'image/jpeg',
|
||||
],
|
||||
];
|
||||
|
||||
$allowed_mime_types = get_allowed_mime_types();
|
||||
|
||||
if ( ! empty( $allowed_mime_types ) ) {
|
||||
self::$values = [];
|
||||
foreach ( $allowed_mime_types as $mime_type ) {
|
||||
|
||||
$formatted_mime_type = strtoupper( preg_replace( '/[^A-Za-z0-9]/i', '_', $mime_type ) );
|
||||
|
||||
self::$values[ $formatted_mime_type ] = [
|
||||
'value' => $mime_type,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return self::$values;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
namespace WPGraphQL\Type\Enum;
|
||||
|
||||
use WPGraphQL\Type\WPEnumType;
|
||||
|
||||
/**
|
||||
* Class PostObjectFieldFormatEnumType
|
||||
*
|
||||
* This defines an EnumType with allowed formats of post field data.
|
||||
*
|
||||
* @package WPGraphQL\Type\Enum
|
||||
* @since 0.0.18
|
||||
*/
|
||||
class PostObjectFieldFormatEnumType extends WPEnumType {
|
||||
|
||||
/**
|
||||
* This holds the enum values array.
|
||||
*
|
||||
* @var array $values
|
||||
*/
|
||||
private static $values;
|
||||
|
||||
public function __construct() {
|
||||
$config = [
|
||||
'name' => 'PostObjectFieldFormat',
|
||||
'description' => __( 'The format of post field data.', 'wp-graphql' ),
|
||||
'values' => self::values(),
|
||||
];
|
||||
parent::__construct( $config );
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a list of formats of post field data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private static function values() {
|
||||
|
||||
if ( null === self::$values ) {
|
||||
|
||||
/**
|
||||
* Post object field formats.
|
||||
*
|
||||
* @since 0.0.18
|
||||
*/
|
||||
self::$values = [
|
||||
'RAW' => [
|
||||
'name' => 'RAW',
|
||||
'description' => __( 'Provide the field value directly from database', 'wp-graphql' ),
|
||||
'value' => 'raw',
|
||||
],
|
||||
'RENDERED' => [
|
||||
'name' => 'RENDERED',
|
||||
'description' => __( 'Apply the default WordPress rendering', 'wp-graphql' ),
|
||||
'value' => 'rendered',
|
||||
],
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the $values
|
||||
*/
|
||||
return self::$values;
|
||||
}
|
||||
}
|
||||
85
wordpress/wp-content/plugins/wp-graphql/src/Type/Enum/PostStatusEnumType.php
Executable file
85
wordpress/wp-content/plugins/wp-graphql/src/Type/Enum/PostStatusEnumType.php
Executable file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL\Type\Enum;
|
||||
|
||||
use WPGraphQL\Type\WPEnumType;
|
||||
|
||||
/**
|
||||
* Class PostStatusEnumType
|
||||
*
|
||||
* This defines an EnumType with allowed post stati that are registered to WordPress.
|
||||
*
|
||||
* @package WPGraphQL\Type\Enum
|
||||
* @since 0.0.5
|
||||
*/
|
||||
class PostStatusEnumType extends WPEnumType {
|
||||
|
||||
/**
|
||||
* This holds the enum values array
|
||||
*
|
||||
* @var array $values
|
||||
*/
|
||||
private static $values;
|
||||
|
||||
public function __construct() {
|
||||
$config = [
|
||||
'name' => 'PostStatusEnum',
|
||||
'description' => __( 'The status of the object.', 'wp-graphql' ),
|
||||
'values' => self::values(),
|
||||
];
|
||||
parent::__construct( $config );
|
||||
}
|
||||
|
||||
/**
|
||||
* values
|
||||
* Creates a list of post_stati that can be used to query by.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private static function values() {
|
||||
|
||||
/**
|
||||
* Set the default, if no values are built dynamically
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
self::$values = [
|
||||
'name' => 'PUBLISH',
|
||||
'value' => 'publish',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the dynamic list of post_stati
|
||||
*/
|
||||
$post_stati = get_post_stati();
|
||||
|
||||
/**
|
||||
* If there are $post_stati, create the $values based on them
|
||||
*/
|
||||
if ( ! empty( $post_stati ) && is_array( $post_stati ) ) {
|
||||
/**
|
||||
* Reset the array
|
||||
*/
|
||||
self::$values = [];
|
||||
/**
|
||||
* Loop through the post_stati
|
||||
*/
|
||||
foreach ( $post_stati as $status ) {
|
||||
|
||||
$formatted_status = strtoupper( preg_replace( '/[^A-Za-z0-9]/i', '_', $status ) );
|
||||
|
||||
self::$values[ $formatted_status ] = [
|
||||
'description' => sprintf( __( 'Objects with the %1$s status', 'wp-graphql' ), $status ),
|
||||
'value' => $status,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the $values
|
||||
*/
|
||||
return self::$values;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
74
wordpress/wp-content/plugins/wp-graphql/src/Type/Enum/PostTypeEnumType.php
Executable file
74
wordpress/wp-content/plugins/wp-graphql/src/Type/Enum/PostTypeEnumType.php
Executable file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL\Type\Enum;
|
||||
|
||||
use GraphQL\Type\Definition\EnumType;
|
||||
|
||||
/**
|
||||
* Class PostTypeEnumType
|
||||
*
|
||||
* @package WPGraphQL\Type\Enum
|
||||
*/
|
||||
class PostTypeEnumType extends EnumType {
|
||||
|
||||
/**
|
||||
* Holds the values to be used for the Enum
|
||||
* @var array $values
|
||||
*/
|
||||
private static $values;
|
||||
|
||||
/**
|
||||
* PostTypeEnumType constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
|
||||
$config = [
|
||||
'name' => 'PostTypeEnum',
|
||||
'description' => __( 'Allowed Post Types', 'wp-graphql' ),
|
||||
'values' => self::values(),
|
||||
];
|
||||
|
||||
parent::__construct( $config );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This returns an array of values to be used by the Enum
|
||||
* @return array|null
|
||||
*/
|
||||
private static function values() {
|
||||
|
||||
if ( null === self::$values ) {
|
||||
|
||||
/**
|
||||
* Set an empty array
|
||||
*/
|
||||
self::$values = [];
|
||||
|
||||
/**
|
||||
* Get the allowed taxonomies
|
||||
*/
|
||||
$allowed_post_types = \WPGraphQL::get_allowed_post_types();
|
||||
|
||||
/**
|
||||
* Loop through the taxonomies and create an array
|
||||
* of values for use in the enum type.
|
||||
*/
|
||||
foreach ( $allowed_post_types as $post_type ) {
|
||||
|
||||
$formatted_post_type = strtoupper( get_post_type_object( $post_type )->graphql_single_name );
|
||||
|
||||
self::$values[ $formatted_post_type ] = [
|
||||
'value' => $post_type,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the $values
|
||||
*/
|
||||
return ! empty( self::$values ) ? self::$values : null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
66
wordpress/wp-content/plugins/wp-graphql/src/Type/Enum/RelationEnumType.php
Executable file
66
wordpress/wp-content/plugins/wp-graphql/src/Type/Enum/RelationEnumType.php
Executable file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL\Type\Enum;
|
||||
|
||||
use WPGraphQL\Type\WPEnumType;
|
||||
|
||||
/**
|
||||
* Class RelationEnumType
|
||||
*
|
||||
* This defines an EnumType with allowed relations for use in various query args.
|
||||
*
|
||||
* @package WPGraphQL\Type\Enum
|
||||
* @since 0.0.5
|
||||
*/
|
||||
class RelationEnumType extends WPEnumType {
|
||||
|
||||
/**
|
||||
* This holds the enum values array
|
||||
*
|
||||
* @var array $values
|
||||
*/
|
||||
private static $values;
|
||||
|
||||
/**
|
||||
* RelationEnumType constructor.
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
public function __construct() {
|
||||
$config = [
|
||||
'name' => 'RelationEnum',
|
||||
'description' => __( 'The logical relation between each item in the array when there are more than one.', 'wp-graphql' ),
|
||||
'values' => self::values(),
|
||||
];
|
||||
parent::__construct( $config );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* values
|
||||
* Returns the values to be used in the Enum
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
private static function values() {
|
||||
|
||||
if ( null === self::$values ) {
|
||||
|
||||
self::$values = [
|
||||
'AND' => [
|
||||
'name' => 'AND',
|
||||
'value' => 'AND',
|
||||
],
|
||||
'OR' => [
|
||||
'name' => 'OR',
|
||||
'value' => 'OR',
|
||||
],
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
return self::$values;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
71
wordpress/wp-content/plugins/wp-graphql/src/Type/Enum/TaxonomyEnumType.php
Executable file
71
wordpress/wp-content/plugins/wp-graphql/src/Type/Enum/TaxonomyEnumType.php
Executable file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL\Type\Enum;
|
||||
|
||||
use WPGraphQL\Type\WPEnumType;
|
||||
|
||||
class TaxonomyEnumType extends WPEnumType {
|
||||
|
||||
/**
|
||||
* This holds the enum values array
|
||||
*
|
||||
* @var array $values
|
||||
*/
|
||||
private static $values;
|
||||
|
||||
/**
|
||||
* TaxonomyEnumType constructor.
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
public function __construct() {
|
||||
$config = [
|
||||
'name' => 'TaxonomyEnum',
|
||||
'description' => __( 'Allowed taxonomies', 'wp-graphql' ),
|
||||
'values' => self::values(),
|
||||
];
|
||||
parent::__construct( $config );
|
||||
}
|
||||
|
||||
/**
|
||||
* values
|
||||
* Returns the values to be used in the Enum
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
private static function values() {
|
||||
|
||||
if ( null === self::$values ) {
|
||||
|
||||
/**
|
||||
* Set an empty array
|
||||
*/
|
||||
self::$values = [];
|
||||
|
||||
/**
|
||||
* Get the allowed taxonomies
|
||||
*/
|
||||
$allowed_taxonomies = \WPGraphQL::get_allowed_taxonomies();
|
||||
|
||||
/**
|
||||
* Loop through the taxonomies and create an array
|
||||
* of values for use in the enum type.
|
||||
*/
|
||||
foreach ( $allowed_taxonomies as $taxonomy ) {
|
||||
|
||||
$formatted_taxonomy = strtoupper( get_taxonomy( $taxonomy )->graphql_single_name );
|
||||
|
||||
self::$values[ $formatted_taxonomy ] = [
|
||||
'value' => $taxonomy,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the $values
|
||||
*/
|
||||
return self::$values;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
299
wordpress/wp-content/plugins/wp-graphql/src/Type/MediaItem/MediaItemType.php
Executable file
299
wordpress/wp-content/plugins/wp-graphql/src/Type/MediaItem/MediaItemType.php
Executable file
@@ -0,0 +1,299 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL\Type\MediaItem;
|
||||
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use WPGraphQL\Type\WPObjectType;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class MediaItemType
|
||||
*
|
||||
* This class isn't a full definition of a new Type, instead it's used to customize
|
||||
* the shape of the mediaItemType (via filter), which is instantiated as a PostObjectType.
|
||||
*
|
||||
* @see : wp-graphql.php - add_filter( 'graphql_mediaItem_fields', [ '\WPGraphQL\Type\MediaItem\MediaItemType',
|
||||
* 'fields' ], 10, 1 );
|
||||
*
|
||||
* @package WPGraphQL\Type\MediaItem
|
||||
*/
|
||||
class MediaItemType {
|
||||
|
||||
/**
|
||||
* Holds the object definition for media details
|
||||
*
|
||||
* @var object $media_details
|
||||
*/
|
||||
private static $media_details;
|
||||
|
||||
/**
|
||||
* Holds the object definition for media item meta
|
||||
*
|
||||
* @var object $media_item_meta
|
||||
*/
|
||||
private static $media_item_meta;
|
||||
|
||||
/**
|
||||
* Holds the object definition for media sizes
|
||||
*
|
||||
* @var object $media_sizes
|
||||
*/
|
||||
private static $media_sizes;
|
||||
|
||||
/**
|
||||
* This customizes the fields for the mediaItem type ( attachment post_type) as the shape of the mediaItem Schema
|
||||
* is different than a standard post
|
||||
*
|
||||
* @see: wp-graphql.php - add_filter( 'graphql_mediaItem_fields' );add_filter( 'graphql_mediaItem_fields', [
|
||||
* '\WPGraphQL\Type\MediaItem\MediaItemType', 'fields' ], 10, 1 );
|
||||
*
|
||||
* @param array $fields
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function fields( $fields ) {
|
||||
|
||||
/**
|
||||
* Deprecate fields for the mediaItem type.
|
||||
* These fields can still be queried, but are just not preferred for the mediaItem type
|
||||
*
|
||||
* @since 0.0.6
|
||||
*/
|
||||
$fields['excerpt']['isDeprecated'] = true;
|
||||
$fields['excerpt']['deprecationReason'] = __( 'Use the caption field instead of excerpt', 'wp-graphql' );
|
||||
$fields['content']['isDeprecated'] = true;
|
||||
$fields['content']['deprecationReason'] = __( 'Use the description field instead of content', 'wp-graphql' );
|
||||
|
||||
/**
|
||||
* Add new fields to the mediaItem type
|
||||
*
|
||||
* @since 0.0.6
|
||||
*/
|
||||
$new_fields = [
|
||||
'caption' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The caption for the resource', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post $post, $args, $context, ResolveInfo $info ) {
|
||||
$caption = apply_filters( 'the_excerpt', $post->post_excerpt );
|
||||
|
||||
return ! empty( $caption ) ? $caption : null;
|
||||
},
|
||||
],
|
||||
'altText' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Alternative text to display when resource is not displayed', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post $post, $args, $context, ResolveInfo $info ) {
|
||||
return get_post_meta( $post->ID, '_wp_attachment_image_alt', true );
|
||||
},
|
||||
],
|
||||
'description' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Description of the image (stored as post_content)', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post $post, $args, $context, ResolveInfo $info ) {
|
||||
return apply_filters( 'the_content', $post->post_content );
|
||||
},
|
||||
],
|
||||
'mediaType' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Type of resource', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post $post, $args, $context, ResolveInfo $info ) {
|
||||
return wp_attachment_is_image( $post->ID ) ? 'image' : 'file';
|
||||
},
|
||||
],
|
||||
'sourceUrl' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Url of the mediaItem', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post $post, $args, $context, ResolveInfo $info ) {
|
||||
return wp_get_attachment_url( $post->ID );
|
||||
},
|
||||
],
|
||||
'mimeType' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The mime type of the mediaItem', 'wp-graphql' ),
|
||||
'resolve' =>function( \WP_Post $post, $args, $context, ResolveInfo $info ) {
|
||||
return ! empty( $post->post_mime_type ) ? $post->post_mime_type : null;
|
||||
},
|
||||
],
|
||||
'mediaDetails' => [
|
||||
'type' => self::media_details(),
|
||||
'description' => __( 'Details about the mediaItem', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post $post, $args, $context, ResolveInfo $info ) {
|
||||
return wp_get_attachment_metadata( $post->ID );
|
||||
},
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
return array_merge( $fields, $new_fields );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This defines the media details object type that can be queried on mediaItems
|
||||
*
|
||||
* @return null|WPObjectType
|
||||
* @since 0.0.6
|
||||
*/
|
||||
private static function media_details() {
|
||||
|
||||
if ( null === self::$media_details ) {
|
||||
self::$media_details = new WPObjectType( [
|
||||
'name' => 'MediaDetails',
|
||||
'fields' => function() {
|
||||
$fields = [
|
||||
'width' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( 'The width of the mediaItem', 'wp-graphql' ),
|
||||
],
|
||||
'height' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( 'The height of the mediaItem', 'wp-graphql' ),
|
||||
],
|
||||
'file' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The height of the mediaItem', 'wp-graphql' ),
|
||||
],
|
||||
'sizes' => [
|
||||
'type' => Types::list_of( self::media_sizes() ),
|
||||
'description' => __( 'The available sizes of the mediaItem', 'wp-graphql' ),
|
||||
'resolve' => function( $media_details, $args, $context, ResolveInfo $info ) {
|
||||
if ( ! empty( $media_details['sizes'] ) ) {
|
||||
foreach ( $media_details['sizes'] as $size_name => $size ) {
|
||||
$size['name'] = $size_name;
|
||||
$sizes[] = $size;
|
||||
}
|
||||
}
|
||||
return ! empty( $sizes ) ? $sizes : null;
|
||||
},
|
||||
],
|
||||
'meta' => [
|
||||
'type' => self::media_item_meta(),
|
||||
'resolve' => function( $media_details, $args, $context, ResolveInfo $info ) {
|
||||
return ! empty( $media_details['image_meta'] ) ? $media_details['image_meta'] : null;
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
return WPObjectType::prepare_fields( $fields, 'MediaDetails' );
|
||||
},
|
||||
] );
|
||||
} // End if().
|
||||
|
||||
return ! empty( self::$media_details ) ? self::$media_details : null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This defines the media item meta object type that can be queried on mediaItems
|
||||
*
|
||||
* @return null|WPObjectType
|
||||
* @since 0.0.6
|
||||
*/
|
||||
private static function media_item_meta() {
|
||||
if ( null === self::$media_item_meta ) {
|
||||
self::$media_item_meta = new WPObjectType( [
|
||||
'name' => 'MediaItemMeta',
|
||||
'fields' => [
|
||||
'aperture' => [
|
||||
'type' => Types::float(),
|
||||
],
|
||||
'credit' => [
|
||||
'type' => Types::string(),
|
||||
],
|
||||
'camera' => [
|
||||
'type' => Types::string(),
|
||||
],
|
||||
'caption' => [
|
||||
'type' => Types::string(),
|
||||
],
|
||||
'createdTimestamp' => [
|
||||
'type' => Types::int(),
|
||||
'resolve' => function( $meta, $args, $context, ResolveInfo $info ) {
|
||||
return ! empty( $meta['created_timestamp'] ) ? $meta['created_timestamp'] : null;
|
||||
},
|
||||
],
|
||||
'copyright' => [
|
||||
'type' => Types::string(),
|
||||
],
|
||||
'focalLength' => [
|
||||
'type' => Types::int(),
|
||||
'resolve' => function( $meta, $args, $context, ResolveInfo $info ) {
|
||||
return ! empty( $meta['focal_length'] ) ? $meta['focal_length'] : null;
|
||||
},
|
||||
],
|
||||
'iso' => [
|
||||
'type' => Types::int(),
|
||||
],
|
||||
'shutterSpeed' => [
|
||||
'type' => Types::float(),
|
||||
'resolve' => function( $meta, $args, $context, ResolveInfo $info ) {
|
||||
return ! empty( $meta['shutter_speed'] ) ? $meta['shutter_speed'] : null;
|
||||
},
|
||||
],
|
||||
'title' => [
|
||||
'type' => Types::string(),
|
||||
],
|
||||
'orientation' => [
|
||||
'type' => Types::string(),
|
||||
],
|
||||
'keywords' => [
|
||||
'type' => Types::list_of( Types::string() ),
|
||||
],
|
||||
],
|
||||
] );
|
||||
} // End if().
|
||||
|
||||
return ! empty( self::$media_item_meta ) ? self::$media_item_meta : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This defines the sizes object type that can be queried on mediaItems within the mediaDetails
|
||||
*
|
||||
* @return null|WPObjectType
|
||||
* @since 0.0.6
|
||||
*/
|
||||
private static function media_sizes() {
|
||||
|
||||
if ( null === self::$media_sizes ) {
|
||||
self::$media_sizes = new WPObjectType( [
|
||||
'name' => 'MediaSizes',
|
||||
'fields' => [
|
||||
'name' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The referenced size name', 'wp-graphql' ),
|
||||
],
|
||||
'file' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The file of the for the referenced size', 'wp-graphql' ),
|
||||
],
|
||||
'width' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The width of the for the referenced size', 'wp-graphql' ),
|
||||
],
|
||||
'height' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The height of the for the referenced size', 'wp-graphql' ),
|
||||
],
|
||||
'mimeType' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The mime type of the resource', 'wp-graphql' ),
|
||||
'resolve' => function( $image, $args, $context, ResolveInfo $info ) {
|
||||
return ! empty( $image['mime-type'] ) ? $image['mime-type'] : null;
|
||||
},
|
||||
],
|
||||
'sourceUrl' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The url of the for the referenced size', 'wp-graphql' ),
|
||||
'resolve' => function( $image, $args, $context, ResolveInfo $info ) {
|
||||
return ! empty( $image['file'] ) ? $image['file'] : null;
|
||||
},
|
||||
],
|
||||
],
|
||||
] );
|
||||
} // End if().
|
||||
|
||||
return ! empty( self::$media_sizes ) ? self::$media_sizes : null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL\Type\MediaItem\Mutation;
|
||||
|
||||
use GraphQL\Error\UserError;
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQLRelay\Relay;
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class MediaItemCreate
|
||||
*
|
||||
* @package WPGraphQL\Type\MediaItem\Mutation
|
||||
*/
|
||||
class MediaItemCreate {
|
||||
|
||||
/**
|
||||
* Holds the mutation field definition
|
||||
*
|
||||
* @var array mutation
|
||||
*/
|
||||
private static $mutation = [];
|
||||
|
||||
/**
|
||||
* Defines the create mutation for MediaItems
|
||||
*
|
||||
* @var \WP_Post_Type $post_type_object
|
||||
*
|
||||
* @return array|mixed
|
||||
*/
|
||||
public static function mutate( \WP_Post_Type $post_type_object ) {
|
||||
|
||||
/**
|
||||
* Set the name of the mutation being performed
|
||||
*/
|
||||
$mutation_name = 'CreateMediaItem';
|
||||
|
||||
self::$mutation['mediaItem'] = Relay::mutationWithClientMutationId( [
|
||||
'name' => esc_html( $mutation_name ),
|
||||
'description' => __( 'Create mediaItems', 'wp-graphql' ),
|
||||
'inputFields' => self::input_fields( $post_type_object ),
|
||||
'outputFields' => [
|
||||
'mediaItem' => [
|
||||
'type' => Types::post_object( $post_type_object->name ),
|
||||
'resolve' => function( $payload ) {
|
||||
return get_post( $payload['id'] );
|
||||
},
|
||||
],
|
||||
],
|
||||
'mutateAndGetPayload' => function( $input, AppContext $context, ResolveInfo $info ) use ( $post_type_object, $mutation_name ) {
|
||||
|
||||
/**
|
||||
* Stop now if a user isn't allowed to upload a mediaItem
|
||||
*/
|
||||
if ( ! current_user_can( 'upload_files' ) ) {
|
||||
throw new UserError( __( 'Sorry, you are not allowed to upload mediaItems', 'wp-graphql' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the file name, whether it's a local file or from a URL.
|
||||
* Then set the url for the uploaded file
|
||||
*/
|
||||
$file_name = basename( $input['filePath'] );
|
||||
$uploaded_file_url = $input['filePath'];
|
||||
|
||||
/**
|
||||
* Require the file.php file from wp-admin. This file includes the
|
||||
* download_url and wp_handle_sideload methods
|
||||
*/
|
||||
require_once( ABSPATH . 'wp-admin/includes/file.php' );
|
||||
|
||||
/**
|
||||
* If the mediaItem file is from a local server, use wp_upload_bits before saving it to the uploads folder
|
||||
*/
|
||||
if ( 'file' === parse_url( $input['filePath'], PHP_URL_SCHEME ) ) {
|
||||
$uploaded_file = wp_upload_bits( $file_name, null, file_get_contents( $input['filePath'] ) );
|
||||
$uploaded_file_url = ( empty ( $uploaded_file['error'] ) ? $uploaded_file['url'] : null );
|
||||
}
|
||||
|
||||
/**
|
||||
* URL data for the mediaItem, timeout value is the default, see:
|
||||
* https://developer.wordpress.org/reference/functions/download_url/
|
||||
*/
|
||||
$timeout_seconds = 300;
|
||||
$temp_file = download_url( $uploaded_file_url, $timeout_seconds );
|
||||
|
||||
/**
|
||||
* Handle the error from download_url if it occurs
|
||||
*/
|
||||
if ( is_wp_error( $temp_file ) ) {
|
||||
throw new UserError( __( 'Sorry, the URL for this file is invalid, it must be a valid URL', 'wp-graphql' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the file data for side loading
|
||||
*/
|
||||
$file_data = [
|
||||
'name' => $file_name,
|
||||
'type' => ! empty ( $input['fileType'] ) ? $input['fileType'] : wp_check_filetype( $temp_file ),
|
||||
'tmp_name' => $temp_file,
|
||||
'error' => 0,
|
||||
'size' => filesize( $temp_file ),
|
||||
];
|
||||
|
||||
/**
|
||||
* Tells WordPress to not look for the POST form fields that would normally be present as
|
||||
* we downloaded the file from a remote server, so there will be no form fields
|
||||
* The default is true
|
||||
*/
|
||||
$overrides = [
|
||||
'test_form' => false,
|
||||
];
|
||||
|
||||
/**
|
||||
* Insert the mediaItem and retrieve it's data
|
||||
*/
|
||||
$file = wp_handle_sideload( $file_data, $overrides );
|
||||
|
||||
/**
|
||||
* Handle the error from wp_handle_sideload if it occurs
|
||||
*/
|
||||
if ( ! empty( $file['error'] ) ) {
|
||||
throw new UserError( __( 'Sorry, the URL for this file is invalid, it must be a path to the mediaItem file', 'wp-graphql' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert the mediaItem object and get the ID
|
||||
*/
|
||||
$media_item_args = MediaItemMutation::prepare_media_item( $input, $post_type_object, $mutation_name, $file );
|
||||
|
||||
/**
|
||||
* Get the post parent and if it's not set, set it to false
|
||||
*/
|
||||
$attachment_parent_id = ( ! empty( $media_item_args['post_parent'] ) ? $media_item_args['post_parent'] : false );
|
||||
|
||||
/**
|
||||
* Stop now if a user isn't allowed to edit the parent post
|
||||
*/
|
||||
$parent = get_post( $attachment_parent_id );
|
||||
|
||||
if ( null !== get_post( $attachment_parent_id ) ) {
|
||||
$post_parent_type = get_post_type_object( $parent->post_type );
|
||||
if ( ! current_user_can( $post_parent_type->cap->edit_post, $attachment_parent_id ) ) {
|
||||
throw new UserError( __( 'Sorry, you are not allowed to upload mediaItems to this post', 'wp-graphql' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the mediaItem being created is being assigned to another user that's not the current user, make sure
|
||||
* the current user has permission to edit others mediaItems
|
||||
*/
|
||||
if ( ! empty( $input['authorId'] ) && get_current_user_id() !== $input['authorId'] && ! current_user_can( $post_type_object->cap->edit_others_posts ) ) {
|
||||
throw new UserError( __( 'Sorry, you are not allowed to create mediaItems as this user', 'wp-graphql' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert the mediaItem
|
||||
*
|
||||
* Required Argument defaults are set in the main MediaItemMutation.php if they aren't set
|
||||
* by the user during input, they are:
|
||||
* post_title (pulled from file if not entered)
|
||||
* post_content (empty string if not entered)
|
||||
* post_status (inherit if not entered)
|
||||
* post_mime_type (pulled from the file if not entered in the mutation)
|
||||
*/
|
||||
$attachment_id = wp_insert_attachment( $media_item_args, $file['file'], $attachment_parent_id );
|
||||
|
||||
/**
|
||||
* Check if the wp_generate_attachment_metadata method exists and include it if not
|
||||
*/
|
||||
require_once( ABSPATH . 'wp-admin/includes/image.php' );
|
||||
|
||||
/**
|
||||
* Generate and update the mediaItem's metadata.
|
||||
* If we make it this far the file and attachment
|
||||
* have been validated and we will not receive any errors
|
||||
*/
|
||||
$attachment_data = wp_generate_attachment_metadata( $attachment_id, $file['file'] );
|
||||
$attachment_data_update = wp_update_attachment_metadata( $attachment_id, $attachment_data );
|
||||
|
||||
/**
|
||||
* Update alt text postmeta for mediaItem
|
||||
*/
|
||||
MediaItemMutation::update_additional_media_item_data( $attachment_id, $input, $post_type_object, $mutation_name, $context, $info );
|
||||
|
||||
return [
|
||||
'id' => $attachment_id,
|
||||
];
|
||||
|
||||
},
|
||||
|
||||
] );
|
||||
|
||||
return ! empty( self::$mutation['mediaItem'] ) ? self::$mutation['mediaItem'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the filePath as a nonNull field for create mutations as its required
|
||||
* to create a media item
|
||||
*
|
||||
* @param \WP_Post_Type $post_type_object
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private static function input_fields( $post_type_object ) {
|
||||
|
||||
/**
|
||||
* Creating mutations requires a filePath to be passed
|
||||
*/
|
||||
return array_merge(
|
||||
[
|
||||
'filePath' => [
|
||||
'type' => Types::non_null( Types::string() ),
|
||||
'description' => __( 'The URL or file path to the mediaItem', 'wp-graphql' ),
|
||||
],
|
||||
],
|
||||
MediaItemMutation::input_fields( $post_type_object )
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL\Type\MediaItem\Mutation;
|
||||
|
||||
use GraphQL\Error\UserError;
|
||||
use GraphQLRelay\Relay;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class MediaItemDelete
|
||||
*
|
||||
* @package WPGraphQL\Type\mediaItem\Mutation
|
||||
*/
|
||||
class MediaItemDelete {
|
||||
|
||||
/**
|
||||
* Holds the mutation field definition
|
||||
*
|
||||
* @var array $mutation
|
||||
*/
|
||||
private static $mutation = [];
|
||||
|
||||
/**
|
||||
* Defines the delete mutation for MediaItems
|
||||
*
|
||||
* @param \WP_Post_Type $post_type_object
|
||||
*
|
||||
* @return array|mixed
|
||||
*/
|
||||
public static function mutate( \WP_Post_Type $post_type_object ) {
|
||||
|
||||
/**
|
||||
* Set the name of the media item mutation being performed
|
||||
*/
|
||||
$mutation_name = 'DeleteMediaItem';
|
||||
|
||||
self::$mutation['mediaItem'] = Relay::mutationWithClientMutationId( [
|
||||
'name' => esc_html( $mutation_name ),
|
||||
'description' => __( 'Delete mediaItem objects. By default mediaItem objects will be moved to the trash unless the forceDelete is used', 'wp-graphql' ),
|
||||
'inputFields' => [
|
||||
'id' => [
|
||||
'type' => Types::non_null( Types::id() ),
|
||||
'description' => __( 'The ID of the mediaItem to delete', 'wp-graphql' ),
|
||||
],
|
||||
'forceDelete' => [
|
||||
'type' => Types::boolean(),
|
||||
'description' => __( 'Whether the mediaItem should be force deleted instead of being moved to the trash', 'wp-graphql' ),
|
||||
],
|
||||
],
|
||||
'outputFields' => [
|
||||
'deletedId' => [
|
||||
'type' => Types::id(),
|
||||
'description' => __( 'The ID of the deleted mediaItem', 'wp-graphql' ),
|
||||
'resolve' => function( $payload ) use ( $post_type_object ) {
|
||||
$deleted = (object) $payload['mediaItemObject'];
|
||||
return ! empty( $deleted->ID ) ? Relay::toGlobalId( $post_type_object->name, absint( $deleted->ID ) ) : null;
|
||||
},
|
||||
],
|
||||
'mediaItem' => [
|
||||
'type' => Types::post_object( $post_type_object->name ),
|
||||
'description' => __( 'The mediaItem before it was deleted', 'wp-graphql' ),
|
||||
'resolve' => function( $payload ) {
|
||||
$deleted = (object) $payload['mediaItemObject'];
|
||||
return ! empty( $deleted ) ? $deleted : null;
|
||||
},
|
||||
],
|
||||
],
|
||||
'mutateAndGetPayload' => function( $input ) use ( $post_type_object, $mutation_name ) {
|
||||
|
||||
/**
|
||||
* Get the ID from the global ID
|
||||
*/
|
||||
$id_parts = Relay::fromGlobalId( $input['id'] );
|
||||
$existing_media_item = get_post( absint( $id_parts['id'] ) );
|
||||
|
||||
/**
|
||||
* If there's no existing mediaItem, throw an exception
|
||||
*/
|
||||
if ( empty( $existing_media_item ) ) {
|
||||
throw new UserError( __( 'No mediaItem could be found to delete', 'wp-graphql' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop now if a user isn't allowed to delete a mediaItem
|
||||
*/
|
||||
if ( ! current_user_can( $post_type_object->cap->delete_post, absint( $id_parts['id'] ) ) ) {
|
||||
throw new UserError( __( 'Sorry, you are not allowed to delete mediaItems', 'wp-graphql' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we should force delete or not
|
||||
*/
|
||||
$force_delete = ( ! empty( $input['forceDelete'] ) && true === $input['forceDelete'] ) ? true : false;
|
||||
|
||||
/**
|
||||
* Get the mediaItem object before deleting it
|
||||
*/
|
||||
$media_item_before_delete = get_post( absint( $id_parts['id'] ) );
|
||||
|
||||
/**
|
||||
* If the mediaItem isn't of the attachment post type, throw an error
|
||||
*/
|
||||
if ( 'attachment' !== $media_item_before_delete->post_type ) {
|
||||
throw new UserError( sprintf( __( 'Sorry, the item you are trying to delete is a %1%s, not a mediaItem', 'wp-graphql' ), $media_item_before_delete->post_type ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* If the mediaItem is already in the trash, and the forceDelete input was not passed,
|
||||
* don't remove from the trash
|
||||
*/
|
||||
if ( 'trash' === $media_item_before_delete->post_status ) {
|
||||
if ( true !== $force_delete ) {
|
||||
// Translators: the first placeholder is the post_type of the object being deleted and the second placeholder is the unique ID of that object
|
||||
throw new UserError( sprintf( __( 'The mediaItem with id %1$s is already in the trash. To remove from the trash, use the forceDelete input', 'wp-graphql' ), $input['id'] ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the mediaItem. This will not throw false thanks to
|
||||
* all of the above validation
|
||||
*/
|
||||
$deleted = wp_delete_attachment( $id_parts['id'], $force_delete );
|
||||
|
||||
/**
|
||||
* If the post was moved to the trash, spoof the object's status before returning it
|
||||
*/
|
||||
$media_item_before_delete->post_status = ( false !== $deleted && true !== $force_delete ) ? 'trash' : $media_item_before_delete->post_status;
|
||||
|
||||
/**
|
||||
* Return the deletedId and the mediaItem before it was deleted
|
||||
*/
|
||||
return [
|
||||
'mediaItemObject' => $media_item_before_delete,
|
||||
];
|
||||
|
||||
},
|
||||
] );
|
||||
|
||||
return ! empty( self::$mutation['mediaItem'] ) ? self::$mutation['mediaItem'] : null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL\Type\MediaItem\Mutation;
|
||||
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQLRelay\Relay;
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class MediaItemMutation
|
||||
*
|
||||
* @package WPGraphQL\Type\MediaItem
|
||||
*/
|
||||
class MediaItemMutation {
|
||||
|
||||
/**
|
||||
* Holds the input fields configuration
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $input_fields = [];
|
||||
|
||||
/**
|
||||
* @param $post_type_object
|
||||
*
|
||||
* @return mixed|array|null $input_fields
|
||||
*/
|
||||
public static function input_fields( $post_type_object ) {
|
||||
|
||||
if ( ! empty( $post_type_object->graphql_single_name ) && empty( self::$input_fields[ $post_type_object->graphql_single_name ] ) ) {
|
||||
|
||||
$input_fields = [
|
||||
'altText' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Alternative text to display when mediaItem is not displayed', 'wp-graphql' ),
|
||||
],
|
||||
'authorId' => [
|
||||
'type' => Types::id(),
|
||||
'description' => __( 'The userId to assign as the author of the mediaItem', 'wp-graphql' ),
|
||||
],
|
||||
'caption' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The caption for the mediaItem', 'wp-graphql' ),
|
||||
],
|
||||
'commentStatus' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The comment status for the mediaItem', 'wp-graphql' ),
|
||||
],
|
||||
'date' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The date of the mediaItem', 'wp-graphql' ),
|
||||
],
|
||||
'dateGmt' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The date (in GMT zone) of the mediaItem', 'wp-graphql' ),
|
||||
],
|
||||
'description' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Description of the mediaItem', 'wp-graphql' ),
|
||||
],
|
||||
'filePath' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The file name of the mediaItem', 'wp-graphql' ),
|
||||
],
|
||||
'fileType' => [
|
||||
'type' => Types::mime_type_enum(),
|
||||
'description' => __( 'The file type of the mediaItem', 'wp-graphql' ),
|
||||
],
|
||||
'slug' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The slug of the mediaItem', 'wp-graphql' ),
|
||||
],
|
||||
'status' => [
|
||||
'type' => Types::media_item_status_enum(),
|
||||
'description' => __( 'The status of the mediaItem', 'wp-graphql' ),
|
||||
],
|
||||
'title' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The title of the mediaItem', 'wp-graphql' ),
|
||||
],
|
||||
'pingStatus' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The ping status for the mediaItem', 'wp-graphql' ),
|
||||
],
|
||||
'parentId' => [
|
||||
'type' => Types::id(),
|
||||
'description' => __( 'The WordPress post ID or the graphQL postId of the parent object', 'wp-graphql' ),
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Filters the mutation input fields for the mediaItem
|
||||
*
|
||||
* @param array $input_fields The array of input fields
|
||||
* @param \WP_Post_Type $post_type_object The post_type object for the mediaItem
|
||||
*/
|
||||
self::$input_fields[ $post_type_object->graphql_single_name ] = apply_filters( 'graphql_media_item_mutation_input_fields', $input_fields, $post_type_object );
|
||||
|
||||
} // End if().
|
||||
|
||||
return ! empty( self::$input_fields[ $post_type_object->graphql_single_name ] ) ? self::$input_fields[ $post_type_object->graphql_single_name ] : null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This prepares the media item for insertion
|
||||
*
|
||||
* @param array $input The input for the mutation from the GraphQL request
|
||||
* @param \WP_Post_Type $post_type_object The post_type_object for the mediaItem (attachment)
|
||||
* @param string $mutation_name The name of the mutation being performed (create, update, etc.)
|
||||
* @param mixed $file The mediaItem (attachment) file
|
||||
*
|
||||
* @return array $media_item_args
|
||||
*/
|
||||
public static function prepare_media_item( $input, $post_type_object, $mutation_name, $file ) {
|
||||
|
||||
/**
|
||||
* Set the post_type (attachment) for the insert
|
||||
*/
|
||||
$insert_post_args['post_type'] = $post_type_object->name;
|
||||
|
||||
/**
|
||||
* Prepare the data for inserting the mediaItem
|
||||
* NOTE: These are organized in the same order as: http://v2.wp-api.org/reference/media/#schema-meta
|
||||
*/
|
||||
if ( ! empty( $input['date'] ) && false !== strtotime( $input['date'] ) ) {
|
||||
$insert_post_args['post_date'] = date( 'Y-m-d H:i:s', strtotime( $input['date'] ) );
|
||||
}
|
||||
|
||||
if ( ! empty( $input['dateGmt'] ) && false !== strtotime( $input['dateGmt'] ) ) {
|
||||
$insert_post_args['post_date_gmt'] = date( 'Y-m-d H:i:s', strtotime( $input['dateGmt'] ) );
|
||||
}
|
||||
|
||||
if ( ! empty( $input['slug'] ) ) {
|
||||
$insert_post_args['post_name'] = $input['slug'];
|
||||
}
|
||||
|
||||
if ( ! empty( $input['status'] ) ) {
|
||||
$insert_post_args['post_status'] = $input['status'];
|
||||
} else {
|
||||
$insert_post_args['post_status'] = 'inherit';
|
||||
}
|
||||
|
||||
if ( ! empty( $input['title'] ) ) {
|
||||
$insert_post_args['post_title'] = $input['title'];
|
||||
} elseif ( ! empty( $file['file'] ) ) {
|
||||
$insert_post_args['post_title'] = basename( $file['file'] );
|
||||
}
|
||||
|
||||
$author_id_parts = ! empty( $input['authorId'] ) ? Relay::fromGlobalId( $input['authorId'] ) : null;
|
||||
if ( is_array( $author_id_parts ) && ! empty( $author_id_parts['id'] ) ) {
|
||||
$insert_post_args['post_author'] = absint( $author_id_parts['id'] );
|
||||
}
|
||||
|
||||
if ( ! empty( $input['commentStatus'] ) ) {
|
||||
$insert_post_args['comment_status'] = $input['commentStatus'];
|
||||
}
|
||||
|
||||
if ( ! empty( $input['pingStatus'] ) ) {
|
||||
$insert_post_args['ping_status'] = $input['pingStatus'];
|
||||
}
|
||||
|
||||
if ( ! empty( $input['caption'] ) ) {
|
||||
$insert_post_args['post_excerpt'] = $input['caption'];
|
||||
}
|
||||
|
||||
if ( ! empty( $input['description'] ) ) {
|
||||
$insert_post_args['post_content'] = $input['description'];
|
||||
} else {
|
||||
$insert_post_args['post_content'] = '';
|
||||
}
|
||||
|
||||
if ( ! empty( $file['type'] ) ) {
|
||||
$insert_post_args['post_mime_type'] = $file['type'];
|
||||
} elseif ( ! empty( $input['fileType'] ) ) {
|
||||
$insert_post_args['post_mime_type'] = $input['fileType'];
|
||||
}
|
||||
|
||||
if ( ! empty( $input['parentId'] ) ) {
|
||||
|
||||
$parent_id_parts = ( ! empty( $input['parentId'] ) ? Relay::fromGlobalId( $input['parentId'] ) : null );
|
||||
|
||||
if ( is_array( $parent_id_parts ) && absint( $parent_id_parts['id'] ) ) {
|
||||
$insert_post_args['post_parent'] = absint( $parent_id_parts['id'] );
|
||||
} else {
|
||||
$insert_post_args['post_parent'] = absint( $input['parentId'] );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the $insert_post_args
|
||||
*
|
||||
* @param array $insert_post_args The array of $input_post_args that will be passed to wp_insert_attachment
|
||||
* @param array $input The data that was entered as input for the mutation
|
||||
* @param \WP_Post_Type $post_type_object The post_type_object that the mutation is affecting
|
||||
* @param string $mutation_type The type of mutation being performed (create, update, delete)
|
||||
*/
|
||||
$insert_post_args = apply_filters( 'graphql_media_item_insert_post_args', $insert_post_args, $input, $post_type_object, $mutation_name );
|
||||
|
||||
return $insert_post_args;
|
||||
}
|
||||
|
||||
/**
|
||||
* This updates additional data related to a mediaItem, such as postmeta.
|
||||
*
|
||||
* @param int $media_item_id The ID of the media item being mutated
|
||||
* @param array $input The input on the mutation
|
||||
* @param \WP_Post_Type $post_type_object The Post Type Object for the item being mutated
|
||||
* @param string $mutation_name The name of the mutation
|
||||
* @param AppContext $context The AppContext that is passed down the resolve tree
|
||||
* @param ResolveInfo $info The ResolveInfo that is passed down the resolve tree
|
||||
*/
|
||||
public static function update_additional_media_item_data( $media_item_id, $input, $post_type_object, $mutation_name, AppContext $context, ResolveInfo $info ) {
|
||||
|
||||
/**
|
||||
* Update alt text postmeta for the mediaItem
|
||||
*/
|
||||
if ( ! empty( $input['altText'] ) ) {
|
||||
update_post_meta( $media_item_id, '_wp_attachment_image_alt', $input['altText'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Run an action after the additional data has been updated. This is a great spot to hook into to
|
||||
* update additional data related to mediaItems, such as updating additional postmeta,
|
||||
* or sending emails to Kevin. . .whatever you need to do with the mediaItem.
|
||||
*
|
||||
* @param int $media_item_id The ID of the mediaItem being mutated
|
||||
* @param array $input The input for the mutation
|
||||
* @param \WP_Post_Type $post_type_object The Post Type Object for the type of post being mutated
|
||||
* @param string $mutation_name The name of the mutation (ex: create, update, delete)
|
||||
* @param AppContext $context The AppContext that is passed down the resolve tree
|
||||
* @param ResolveInfo $info The ResolveInfo that is passed down the resolve tree
|
||||
*/
|
||||
do_action( 'graphql_media_item_mutation_update_additional_data', $media_item_id, $input, $post_type_object, $mutation_name, $context, $info );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL\Type\MediaItem\Mutation;
|
||||
|
||||
use GraphQL\Error\UserError;
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQLRelay\Relay;
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class MediaItemUpdate
|
||||
*
|
||||
* @package WPGraphQL\Type\PostObject\Mutation
|
||||
*/
|
||||
class MediaItemUpdate {
|
||||
|
||||
/**
|
||||
* Holds the mutation field definition
|
||||
*
|
||||
* @var array $mutation
|
||||
*/
|
||||
private static $mutation = [];
|
||||
|
||||
/**
|
||||
* Defines the update mutation for MediaItems
|
||||
*
|
||||
* @param \WP_Post_Type $post_type_object
|
||||
*
|
||||
* @return array|mixed
|
||||
*/
|
||||
public static function mutate( \WP_Post_Type $post_type_object ) {
|
||||
|
||||
/**
|
||||
* Set the name of the mutation being performed
|
||||
*/
|
||||
$mutation_name = 'UpdateMediaItem';
|
||||
|
||||
self::$mutation['mediaItem'] = Relay::mutationWithClientMutationId([
|
||||
'name' => esc_html( $mutation_name ),
|
||||
'description' => __( 'Updates mediaItem objects', 'wp-graphql' ),
|
||||
'inputFields' => self::input_fields( $post_type_object ),
|
||||
'outputFields' => [
|
||||
'mediaItem' => [
|
||||
'type' => Types::post_object( $post_type_object->name ),
|
||||
'resolve' => function( $payload ) {
|
||||
return get_post( $payload['postObjectId'] );
|
||||
},
|
||||
],
|
||||
],
|
||||
'mutateAndGetPayload' => function( $input, AppContext $context, ResolveInfo $info ) use ( $post_type_object, $mutation_name ) {
|
||||
|
||||
$id_parts = ! empty( $input['id'] ) ? Relay::fromGlobalId( $input['id'] ) : null;
|
||||
$existing_media_item = get_post( absint( $id_parts['id'] ) );
|
||||
|
||||
/**
|
||||
* If there's no existing mediaItem, throw an exception
|
||||
*/
|
||||
if ( null === $existing_media_item ) {
|
||||
throw new UserError( __( 'No mediaItem with that ID could be found to update', 'wp-graphql' ) );
|
||||
} else {
|
||||
$author_id = $existing_media_item->post_author;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop now if the post isn't a mediaItem
|
||||
*/
|
||||
if ( $post_type_object->name !== $existing_media_item->post_type ) {
|
||||
// translators: The placeholder is the ID of the mediaItem being edited
|
||||
throw new UserError( sprintf( __( 'The id %1$d is not of the type mediaItem', 'wp-graphql' ), $id_parts['id'] ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop now if a user isn't allowed to edit mediaItems
|
||||
*/
|
||||
if ( ! current_user_can( $post_type_object->cap->edit_posts ) ) {
|
||||
throw new UserError( __( 'Sorry, you are not allowed to update mediaItems', 'wp-graphql' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* If the mutation is setting the author to be someone other than the user making the request
|
||||
* make sure they have permission to edit others posts
|
||||
**/
|
||||
if ( ! empty( $input['authorId'] ) ) {
|
||||
$author_id_parts = Relay::fromGlobalId( $input['authorId'] );
|
||||
$author_id = $author_id_parts['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if the existing_media_item author matches the current user,
|
||||
* if not they need to be able to edit others posts to proceed
|
||||
*/
|
||||
if ( get_current_user_id() !== $author_id && ! current_user_can( $post_type_object->cap->edit_others_posts ) ) {
|
||||
throw new UserError( __( 'Sorry, you are not allowed to update mediaItems as this user.', 'wp-graphql' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* insert the post object and get the ID
|
||||
*/
|
||||
$post_args = MediaItemMutation::prepare_media_item( $input, $post_type_object, $mutation_name, false );
|
||||
$post_args['ID'] = absint( $id_parts['id'] );
|
||||
$post_args['post_author'] = $author_id;
|
||||
|
||||
/**
|
||||
* Insert the post and retrieve the ID
|
||||
*
|
||||
* This will not fail as long as we have an ID in $post_args
|
||||
* Thanks to the validation above we will always have the ID
|
||||
*/
|
||||
$post_id = wp_update_post( wp_slash( (array) $post_args ), true );
|
||||
|
||||
/**
|
||||
* This updates additional data not part of the posts table (postmeta, terms, other relations, etc)
|
||||
*
|
||||
* The input for the postObjectMutation will be passed, along with the $new_post_id for the
|
||||
* postObject that was updated so that relations can be set, meta can be updated, etc.
|
||||
*/
|
||||
MediaItemMutation::update_additional_media_item_data( $post_id, $input, $post_type_object, $mutation_name, $context, $info );
|
||||
|
||||
/**
|
||||
* Return the payload
|
||||
*/
|
||||
return [
|
||||
'postObjectId' => $post_id,
|
||||
];
|
||||
|
||||
},
|
||||
]);
|
||||
|
||||
return ! empty( self::$mutation[ $post_type_object->graphql_single_name ] ) ? self::$mutation[ $post_type_object->graphql_single_name ] : null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the id as a nonNull field for update mutations
|
||||
*
|
||||
* @param \WP_Post_Type $post_type_object
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private static function input_fields( $post_type_object ) {
|
||||
|
||||
/**
|
||||
* Update mutations require an ID to be passed
|
||||
*/
|
||||
return array_merge(
|
||||
[
|
||||
'id' => [
|
||||
'type' => Types::non_null( Types::id() ),
|
||||
// translators: the placeholder is the name of the type of post object being updated
|
||||
'description' => sprintf( __( 'The ID of the %1$s object', 'wp-graphql' ), $post_type_object->graphql_single_name ),
|
||||
],
|
||||
],
|
||||
MediaItemMutation::input_fields( $post_type_object )
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
namespace WPGraphQL\Type\Plugin\Connection;
|
||||
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQLRelay\Relay;
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\Data\DataSource;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class CommentConnectionDefinition
|
||||
* @package WPGraphQL\Type\Comment\Connection
|
||||
* @since 0.0.5
|
||||
*/
|
||||
class PluginConnectionDefinition {
|
||||
|
||||
/**
|
||||
* @var array connection
|
||||
* @since 0.0.5
|
||||
*/
|
||||
private static $connection = [];
|
||||
|
||||
/**
|
||||
* connection
|
||||
* This sets up a connection of plugins
|
||||
* @return mixed
|
||||
* @since 0.0.5
|
||||
*/
|
||||
public static function connection( $from_type = 'Root' ) {
|
||||
|
||||
if ( empty( self::$connection[ $from_type ] ) ) {
|
||||
|
||||
/**
|
||||
* Setup the connectionDefinition
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
$connection = Relay::connectionDefinitions( [
|
||||
'nodeType' => Types::plugin(),
|
||||
'name' => 'Plugins',
|
||||
'connectionFields' => function() {
|
||||
return [
|
||||
'nodes' => [
|
||||
'type' => Types::list_of( Types::plugin() ),
|
||||
'description' => __( 'The nodes of the connection, without the edges', 'wp-graphql' ),
|
||||
'resolve' => function( $source, $args, $context, $info ) {
|
||||
return ! empty( $source['nodes'] ) ? $source['nodes'] : [];
|
||||
},
|
||||
],
|
||||
];
|
||||
},
|
||||
] );
|
||||
|
||||
/**
|
||||
* Add the connection to the post_objects_connection object
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
self::$connection[ $from_type ] = [
|
||||
'type' => $connection['connectionType'],
|
||||
'description' => __( 'A collection of plugins', 'wp-graphql' ),
|
||||
'args' => Relay::connectionArgs(),
|
||||
'resolve' => function( $source, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return DataSource::resolve_plugins_connection( $source, $args, $context, $info );
|
||||
},
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
return ! empty( self::$connection[ $from_type ] ) ? self::$connection[ $from_type ] : null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
namespace WPGraphQL\Type\Plugin\Connection;
|
||||
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQLRelay\Relay;
|
||||
use WPGraphQL\AppContext;
|
||||
|
||||
/**
|
||||
* Class PluginConnectionResolver - Connects plugins to other objects
|
||||
*
|
||||
* @package WPGraphQL\Data\Resolvers
|
||||
* @since 0.0.5
|
||||
*/
|
||||
class PluginConnectionResolver {
|
||||
|
||||
/**
|
||||
* Creates the connection for plugins
|
||||
*
|
||||
* @param mixed $source The query results
|
||||
* @param array $args The query arguments
|
||||
* @param AppContext $context The AppContext object
|
||||
* @param ResolveInfo $info The ResolveInfo object
|
||||
*
|
||||
* @since 0.5.0
|
||||
* @return array
|
||||
* @access public
|
||||
*/
|
||||
public static function resolve( $source, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
|
||||
// File has not loaded.
|
||||
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
||||
// This is missing must use and drop in plugins.
|
||||
$plugins = apply_filters( 'all_plugins', get_plugins() );
|
||||
$plugins_array = [];
|
||||
if ( ! empty( $plugins ) && is_array( $plugins ) ) {
|
||||
foreach ( $plugins as $plugin ) {
|
||||
$plugins_array[] = $plugin;
|
||||
}
|
||||
}
|
||||
|
||||
$connection = Relay::connectionFromArray( $plugins_array, $args );
|
||||
|
||||
$nodes = [];
|
||||
if ( ! empty( $connection['edges'] ) && is_array( $connection['edges'] ) ) {
|
||||
foreach ( $connection['edges'] as $edge ) {
|
||||
$nodes[] = ! empty( $edge['node'] ) ? $edge['node'] : null;
|
||||
}
|
||||
}
|
||||
$connection['nodes'] = ! empty( $nodes ) ? $nodes : null;
|
||||
|
||||
return ! empty( $plugins_array ) ? $connection : null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
52
wordpress/wp-content/plugins/wp-graphql/src/Type/Plugin/PluginQuery.php
Executable file
52
wordpress/wp-content/plugins/wp-graphql/src/Type/Plugin/PluginQuery.php
Executable file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
namespace WPGraphQL\Type\Plugin;
|
||||
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQLRelay\Relay;
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\Data\DataSource;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class PluginQuery
|
||||
* @package WPGraphQL\Type\Plugin
|
||||
* @Since 0.0.5
|
||||
*/
|
||||
class PluginQuery {
|
||||
|
||||
/**
|
||||
* Holds the root_query field definition
|
||||
* @var array $root_query
|
||||
* @since 0.0.5
|
||||
*/
|
||||
private static $root_query;
|
||||
|
||||
/**
|
||||
* Method that returns the root query field definition for the plugin type
|
||||
*
|
||||
* @return array
|
||||
* @since 0.0.5
|
||||
*/
|
||||
public static function root_query() {
|
||||
|
||||
if ( null === self::$root_query ) {
|
||||
|
||||
self::$root_query = [
|
||||
'type' => Types::plugin(),
|
||||
'description' => __( 'A WordPress plugin', 'wp-graphql' ),
|
||||
'args' => [
|
||||
'id' => Types::non_null( Types::id() ),
|
||||
],
|
||||
'resolve' => function( $source, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
$id_components = Relay::fromGlobalId( $args['id'] );
|
||||
|
||||
return DataSource::resolve_plugin( $id_components['id'] );
|
||||
},
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
return self::$root_query;
|
||||
}
|
||||
|
||||
}
|
||||
130
wordpress/wp-content/plugins/wp-graphql/src/Type/Plugin/PluginType.php
Executable file
130
wordpress/wp-content/plugins/wp-graphql/src/Type/Plugin/PluginType.php
Executable file
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
namespace WPGraphQL\Type\Plugin;
|
||||
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQLRelay\Relay;
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\Type\WPObjectType;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class PluginType
|
||||
*
|
||||
* This sets up the PluginType schema.
|
||||
*
|
||||
* @package WPGraphQL\Type
|
||||
* @since 0.0.5
|
||||
*/
|
||||
class PluginType extends WPObjectType {
|
||||
|
||||
/**
|
||||
* Holds the type name
|
||||
* @var string $type_name
|
||||
*/
|
||||
private static $type_name;
|
||||
|
||||
/**
|
||||
* Holds the $fields definition for the PluginType
|
||||
* @var $fields
|
||||
*/
|
||||
private static $fields;
|
||||
|
||||
/**
|
||||
* PluginType constructor.
|
||||
* @since 0.0.5
|
||||
*/
|
||||
public function __construct() {
|
||||
|
||||
/**
|
||||
* Set the type_name
|
||||
* @since 0.0.5
|
||||
*/
|
||||
self::$type_name = 'Plugin';
|
||||
|
||||
$config = [
|
||||
'name' => self::$type_name,
|
||||
'description' => __( 'An plugin object', 'wp-graphql' ),
|
||||
'fields' => self::fields(),
|
||||
'interfaces' => [ self::node_interface() ],
|
||||
];
|
||||
|
||||
parent::__construct( $config );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This defines the fields for the PluginType. The fields are passed through a filter so the shape of the schema
|
||||
* can be modified, for example to add entry points to Types that are unique to certain plugins.
|
||||
*
|
||||
* @return mixed
|
||||
* @since 0.0.5
|
||||
*/
|
||||
private static function fields() {
|
||||
|
||||
if ( null === self::$fields ) {
|
||||
self::$fields = function() {
|
||||
$fields = [
|
||||
'id' => [
|
||||
'type' => Types::non_null( Types::id() ),
|
||||
'resolve' => function( array $plugin, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ( ! empty( $plugin ) && ! empty( $plugin['Name'] ) ) ? Relay::toGlobalId( 'plugin', $plugin['Name'] ) : null;
|
||||
},
|
||||
],
|
||||
'name' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Display name of the plugin.', 'wp-graphql' ),
|
||||
'resolve' => function( array $plugin, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $plugin['Name'] ) ? $plugin['Name'] : '';
|
||||
},
|
||||
],
|
||||
'pluginUri' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'URI for the plugin website. This is useful for directing users for support requests etc.', 'wp-graphql' ),
|
||||
'resolve' => function( array $plugin, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $plugin['PluginURI'] ) ? $plugin['PluginURI'] : '';
|
||||
},
|
||||
],
|
||||
'description' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Description of the plugin.', 'wp-graphql' ),
|
||||
'resolve' => function( array $plugin, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $plugin['Description'] ) ? $plugin['Description'] : '';
|
||||
},
|
||||
],
|
||||
'author' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Name of the plugin author(s), may also be a company name.', 'wp-graphql' ),
|
||||
'resolve' => function( array $plugin, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $plugin['Author'] ) ? $plugin['Author'] : '';
|
||||
},
|
||||
],
|
||||
'authorUri' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'URI for the related author(s)/company website.', 'wp-graphql' ),
|
||||
'resolve' => function( array $plugin, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $plugin['AuthorURI'] ) ? $plugin['AuthorURI'] : '';
|
||||
},
|
||||
],
|
||||
'version' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Current version of the plugin.', 'wp-graphql' ),
|
||||
'resolve' => function( array $plugin, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $plugin['Version'] ) ? $plugin['Version'] : '';
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Pass the fields through a filter to allow for hooking in and adjusting the shape
|
||||
* of the type's schema
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
return self::prepare_fields( $fields, self::$type_name );
|
||||
|
||||
};
|
||||
}
|
||||
return self::$fields;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,368 @@
|
||||
<?php
|
||||
namespace WPGraphQL\Type\PostObject\Connection;
|
||||
|
||||
use GraphQL\Type\Definition\EnumType;
|
||||
use GraphQL\Type\Definition\InputObjectType;
|
||||
use WPGraphQL\Type\WPEnumType;
|
||||
use WPGraphQL\Type\WPInputObjectType;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class PostObjectConnectionArgs
|
||||
*
|
||||
* This sets up the Query Args for post_object connections, which uses WP_Query, so this defines the allowed
|
||||
* input fields that will be passed to the WP_Query
|
||||
*
|
||||
* @package WPGraphQL\Type
|
||||
* @since 0.0.5
|
||||
*/
|
||||
class PostObjectConnectionArgs extends WPInputObjectType {
|
||||
|
||||
/**
|
||||
* Stores the date query object
|
||||
*
|
||||
* @var PostObjectConnectionArgsDateQuery obj $date_query
|
||||
* @since 0.5.0
|
||||
* @access private
|
||||
*/
|
||||
private static $date_query;
|
||||
|
||||
/**
|
||||
* This holds the field definitions
|
||||
*
|
||||
* @var array $fields
|
||||
* @since 0.0.5
|
||||
*/
|
||||
public static $fields = [];
|
||||
|
||||
/**
|
||||
* This holds the orderby_field input object type
|
||||
*
|
||||
* @var array $orderby_field
|
||||
*/
|
||||
private static $orderby_field;
|
||||
|
||||
/**
|
||||
* This holds the orderby EnumType definition
|
||||
*
|
||||
* @var EnumType
|
||||
*/
|
||||
private static $orderby_enum;
|
||||
|
||||
/**
|
||||
* PostObjectConnectionArgs constructor.
|
||||
* @param array $config Array of Config data for the Input Type
|
||||
* @param string $connection The name of the connection the args belong to
|
||||
* @since 0.0.5
|
||||
*/
|
||||
public function __construct( $config = [], $connection ) {
|
||||
$config['name'] = ucfirst( $connection ) . 'QueryArgs';
|
||||
$config['queryClass'] = 'WP_Query';
|
||||
$config['fields'] = self::fields( $connection );
|
||||
parent::__construct( $config );
|
||||
}
|
||||
|
||||
/**
|
||||
* fields
|
||||
*
|
||||
* This defines the fields that make up the PostObjectConnectionArgs
|
||||
*
|
||||
* @param string $connection The name of the connection the fields belong to
|
||||
* @return array
|
||||
* @since 0.0.5
|
||||
*/
|
||||
private static function fields( $connection ) {
|
||||
|
||||
if ( empty( self::$fields[ $connection ] ) ) {
|
||||
$fields = [
|
||||
|
||||
/**
|
||||
* Author $args
|
||||
*
|
||||
* @see : https://codex.wordpress.org/Class_Reference/WP_Query#Author_Parameters
|
||||
* @since 0.0.5
|
||||
*/
|
||||
'author' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( 'The user that\'s connected as the author of the object. Use the
|
||||
userId for the author object.', 'wp-graphql' ),
|
||||
],
|
||||
'authorName' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Find objects connected to the author by the author\'s nicename', 'wp-graphql' ),
|
||||
],
|
||||
'authorIn' => [
|
||||
'type' => Types::list_of( Types::id() ),
|
||||
'description' => __( 'Find objects connected to author(s) in the array of author\'s userIds', 'wp-graphql' ),
|
||||
],
|
||||
'authorNotIn' => [
|
||||
'type' => Types::list_of( Types::int() ),
|
||||
'description' => __( 'Find objects NOT connected to author(s) in the array of author\'s
|
||||
userIds', 'wp-graphql' ),
|
||||
],
|
||||
|
||||
/**
|
||||
* Category $args
|
||||
*
|
||||
* @see : https://codex.wordpress.org/Class_Reference/WP_Query#Category_Parameters
|
||||
* @since 0.0.5
|
||||
*/
|
||||
'categoryId' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( 'Category ID', 'wp-graphql' ),
|
||||
],
|
||||
'categoryName' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Use Category Slug', 'wp-graphql' ),
|
||||
],
|
||||
'categoryIn' => [
|
||||
'type' => Types::list_of( Types::int() ),
|
||||
'description' => __( 'Array of category IDs, used to display objects from one
|
||||
category OR another', 'wp-graphql' ),
|
||||
],
|
||||
|
||||
/**
|
||||
* Tag $args
|
||||
*
|
||||
* @see : https://codex.wordpress.org/Class_Reference/WP_Query#Tag_Parameters
|
||||
* @since 0.0.5
|
||||
*/
|
||||
'tag' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Tag Slug', 'wp-graphql' ),
|
||||
],
|
||||
'tagId' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Use Tag ID', 'wp-graphql' ),
|
||||
],
|
||||
'tagIn' => [
|
||||
'type' => Types::list_of( Types::int() ),
|
||||
'description' => __( 'Array of tag IDs, used to display objects from one tag OR
|
||||
another', 'wp-graphql' ),
|
||||
],
|
||||
'tagSlugAnd' => [
|
||||
'type' => Types::list_of( Types::string() ),
|
||||
'description' => __( 'Array of tag slugs, used to display objects from one tag OR
|
||||
another', 'wp-graphql' ),
|
||||
],
|
||||
'tagSlugIn' => [
|
||||
'type' => Types::list_of( Types::string() ),
|
||||
'description' => __( 'Array of tag slugs, used to exclude objects in specified
|
||||
tags', 'wp-graphql' ),
|
||||
],
|
||||
|
||||
/**
|
||||
* Search Parameter
|
||||
*
|
||||
* @see : https://codex.wordpress.org/Class_Reference/WP_Query#Search_Parameter
|
||||
* @since 0.0.5
|
||||
*/
|
||||
'search' => [
|
||||
'name' => 'search',
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Show Posts based on a keyword search', 'wp-graphql' ),
|
||||
],
|
||||
|
||||
/**
|
||||
* Post & Page Parameters
|
||||
*
|
||||
* @see : https://codex.wordpress.org/Class_Reference/WP_Query#Post_.26_Page_Parameters
|
||||
* @since 0.0.5
|
||||
*/
|
||||
'id' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( 'Specific ID of the object', 'wp-graphql' ),
|
||||
],
|
||||
'name' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Slug / post_name of the object', 'wp-graphql' ),
|
||||
],
|
||||
'title' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Title of the object', 'wp-graphql' ),
|
||||
],
|
||||
'parent' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Use ID to return only children. Use 0 to return only top-level
|
||||
items', 'wp-graphql' ),
|
||||
],
|
||||
'parentIn' => [
|
||||
'type' => Types::list_of( Types::int() ),
|
||||
'description' => __( 'Specify objects whose parent is in an array', 'wp-graphql' ),
|
||||
],
|
||||
'parentNotIn' => [
|
||||
'type' => Types::list_of( Types::int() ),
|
||||
'description' => __( 'Specify posts whose parent is not in an array', 'wp-graphql' ),
|
||||
],
|
||||
'in' => [
|
||||
'type' => Types::list_of( Types::int() ),
|
||||
'description' => __( 'Array of IDs for the objects to retrieve', 'wp-graphql' ),
|
||||
],
|
||||
'notIn' => [
|
||||
'type' => Types::list_of( Types::int() ),
|
||||
'description' => __( 'Specify IDs NOT to retrieve. If this is used in the same query as "in",
|
||||
it will be ignored', 'wp-graphql' ),
|
||||
],
|
||||
'nameIn' => [
|
||||
'type' => Types::list_of( Types::string() ),
|
||||
'description' => __( 'Specify objects to retrieve. Use slugs', 'wp-graphql' ),
|
||||
],
|
||||
|
||||
/**
|
||||
* Password parameters
|
||||
*
|
||||
* @see : https://codex.wordpress.org/Class_Reference/WP_Query#Password_Parameters
|
||||
* @since 0.0.2
|
||||
*/
|
||||
'hasPassword' => [
|
||||
'type' => Types::boolean(),
|
||||
'description' => __( 'True for objects with passwords; False for objects without passwords;
|
||||
null for all objects with or without passwords', 'wp-graphql' ),
|
||||
],
|
||||
'password' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Show posts with a specific password.', 'wp-graphql' ),
|
||||
],
|
||||
|
||||
/**
|
||||
* post_type
|
||||
* NOTE: post_type is intentionally not supported as it's the post_type is the entity entry
|
||||
* point for the queries
|
||||
*
|
||||
* @see : https://codex.wordpress.org/Class_Reference/WP_Query#Type_Parameters
|
||||
* @since 0.0.2
|
||||
*/
|
||||
|
||||
/**
|
||||
* Status parameters
|
||||
*
|
||||
* @see : https://codex.wordpress.org/Class_Reference/WP_Query#Status_Parameters
|
||||
* @since 0.0.2
|
||||
*/
|
||||
'status' => [
|
||||
'type' => Types::post_status_enum(),
|
||||
],
|
||||
|
||||
/**
|
||||
* List of post status parameters
|
||||
*/
|
||||
'stati' => [
|
||||
'type' => Types::list_of( Types::post_status_enum() ),
|
||||
],
|
||||
|
||||
/**
|
||||
* Order & Orderby parameters
|
||||
*
|
||||
* @see : https://codex.wordpress.org/Class_Reference/WP_Query#Order_.26_Orderby_Parameters
|
||||
* @since 0.0.2
|
||||
*/
|
||||
'orderby' => [
|
||||
'type' => Types::list_of( self::orderby_field() ),
|
||||
'description' => __( 'What paramater to use to order the objects by.', 'wp-graphql' ),
|
||||
],
|
||||
'dateQuery' => self::date_query(),
|
||||
'mimeType' => [
|
||||
'type' => Types::mime_type_enum(),
|
||||
'description' => __( 'Get objects with a specific mimeType property', 'wp-graphql' ),
|
||||
],
|
||||
];
|
||||
|
||||
self::$fields[ $connection ] = self::prepare_fields( $fields, ucfirst( $connection ) . 'QueryArgs' );
|
||||
}
|
||||
return ! empty( self::$fields[ $connection ] ) ? self::$fields[ $connection ] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This returns the definition for the PostObjectConnectionArgsDateQuery
|
||||
*
|
||||
* @return PostObjectConnectionArgsDateQuery object
|
||||
* @since 0.0.5
|
||||
* @access public
|
||||
*/
|
||||
public static function date_query() {
|
||||
return self::$date_query ? : ( self::$date_query = new PostObjectConnectionArgsDateQuery() );
|
||||
}
|
||||
|
||||
/**
|
||||
* This returns the orderby field which accepts a field (enum) and an order (enum, ASC/DESC)
|
||||
*
|
||||
* @return InputObjectType object
|
||||
* @access private
|
||||
*/
|
||||
private static function orderby_field() {
|
||||
if ( null === self::$orderby_field ) {
|
||||
|
||||
self::$orderby_field = new WPInputObjectType( [
|
||||
'name' => 'OrderByOptions',
|
||||
'fields' => self::prepare_fields( [
|
||||
'field' => Types::non_null( self::orderby_enum() ),
|
||||
'order' => new WPEnumType( [
|
||||
'name' => 'Order',
|
||||
'values' => [
|
||||
'ASC' => [ 'value' => 'ASC' ],
|
||||
'DESC' => [ 'value' => 'DESC' ],
|
||||
],
|
||||
] ),
|
||||
], 'OrderByOptions' ),
|
||||
] );
|
||||
}
|
||||
|
||||
return ! empty( self::$orderby_field ) ? self::$orderby_field : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* orderby_enum
|
||||
* This returns the orderby enum type for the PostObjectQueryArgs
|
||||
*
|
||||
* @return EnumType
|
||||
* @since 0.0.5
|
||||
*/
|
||||
private static function orderby_enum() {
|
||||
|
||||
if ( null === self::$orderby_enum ) {
|
||||
self::$orderby_enum = new WPEnumType( [
|
||||
'name' => 'OrderBy',
|
||||
'values' => [
|
||||
'AUTHOR' => [
|
||||
'value' => 'post_author',
|
||||
'description' => __( 'Order by author', 'wp-graphql' ),
|
||||
],
|
||||
'TITLE' => [
|
||||
'value' => 'post_title',
|
||||
'description' => __( 'Order by title', 'wp-graphql' ),
|
||||
],
|
||||
'SLUG' => [
|
||||
'value' => 'post_name',
|
||||
'description' => __( 'Order by slug', 'wp-graphql' ),
|
||||
],
|
||||
'MODIFIED' => [
|
||||
'value' => 'post_modified',
|
||||
'description' => __( 'Order by last modified date', 'wp-graphql' ),
|
||||
],
|
||||
'DATE' => [
|
||||
'value' => 'post_date',
|
||||
'description' => __( 'Order by publish date', 'wp-graphql' ),
|
||||
],
|
||||
'PARENT' => [
|
||||
'value' => 'post_parent',
|
||||
'description' => __( 'Order by parent ID', 'wp-graphql' ),
|
||||
],
|
||||
'IN' => [
|
||||
'value' => 'post__in',
|
||||
'description' => __( 'Preserve the ID order given in the IN array', 'wp-graphql' ),
|
||||
],
|
||||
'NAME_IN' => [
|
||||
'value' => 'post_name__in',
|
||||
'description' => __( 'Preserve slug order given in the NAME_IN array', 'wp-graphql' ),
|
||||
],
|
||||
'MENU_ORDER' => [
|
||||
'value' => 'menu_order',
|
||||
'description' => __( 'Order by the menu order value', 'wp-graphql' ),
|
||||
],
|
||||
],
|
||||
] );
|
||||
}
|
||||
return self::$orderby_enum;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
<?php
|
||||
namespace WPGraphQL\Type\PostObject\Connection;
|
||||
|
||||
use GraphQL\Type\Definition\EnumType;
|
||||
use WPGraphQL\Type\WPEnumType;
|
||||
use WPGraphQL\Type\WPInputObjectType;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class PostObjectConnectionArgsDateQuery
|
||||
*
|
||||
* This defines the input fields for date queries
|
||||
*
|
||||
* @package WPGraphQL\Type
|
||||
* @since 0.0.5
|
||||
*/
|
||||
class PostObjectConnectionArgsDateQuery extends WPInputObjectType {
|
||||
|
||||
/**
|
||||
* This holds the field definitions
|
||||
* @var array $fields
|
||||
* @since 0.0.5
|
||||
*/
|
||||
public static $fields;
|
||||
|
||||
/**
|
||||
* Holds the date_after input object definition
|
||||
* @var WPInputObjectType
|
||||
* @since 0.0.5
|
||||
*/
|
||||
private static $date_after;
|
||||
|
||||
/**
|
||||
* Holds the date_before input object definition
|
||||
* @var WPInputObjectType
|
||||
* @since 0.0.5
|
||||
*/
|
||||
private static $date_before;
|
||||
|
||||
/**
|
||||
* Holds the column_enum EnumType definition
|
||||
* @var EnumType
|
||||
* @since 0.0.5
|
||||
*/
|
||||
private static $column_enum;
|
||||
|
||||
/**
|
||||
* DateQueryType constructor.
|
||||
* @since 0.0.5
|
||||
*/
|
||||
public function __construct( $config = [] ) {
|
||||
$config['name'] = 'DateQuery';
|
||||
$config['fields'] = self::fields();
|
||||
parent::__construct( $config );
|
||||
}
|
||||
|
||||
/**
|
||||
* fields
|
||||
*
|
||||
* This defines the fields that make up the DateQueryType
|
||||
*
|
||||
* @return array|null
|
||||
* @since 0.0.5
|
||||
*/
|
||||
private static function fields() {
|
||||
|
||||
if ( null === self::$fields ) {
|
||||
self::$fields = [
|
||||
'year' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( '4 digit year (e.g. 2017)', 'wp-graphql' ),
|
||||
],
|
||||
'month' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( 'Month number (from 1 to 12)', 'wp-graphql' ),
|
||||
],
|
||||
'week' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( 'Week of the year (from 0 to 53)', 'wp-graphql' ),
|
||||
],
|
||||
'day' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( 'Day of the month (from 1 to 31)', 'wp-graphql' ),
|
||||
],
|
||||
'hour' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( 'Hour (from 0 to 23)', 'wp-graphql' ),
|
||||
],
|
||||
'minute' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( 'Minute (from 0 to 59)', 'wp-graphql' ),
|
||||
],
|
||||
'second' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( 'Second (0 to 59)', 'wp-graphql' ),
|
||||
],
|
||||
'after' => [
|
||||
'type' => self::date_after(),
|
||||
],
|
||||
'before' => [
|
||||
'type' => self::date_before(),
|
||||
],
|
||||
'inclusive' => [
|
||||
'type' => Types::boolean(),
|
||||
'description' => __( 'For after/before, whether exact value should be matched or not', 'wp-graphql' ),
|
||||
],
|
||||
'compare' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'For after/before, whether exact value should be matched or not', 'wp-graphql' ),
|
||||
],
|
||||
'column' => [
|
||||
'type' => self::column_enum(),
|
||||
'description' => __( 'Column to query against', 'wp-graphql' ),
|
||||
],
|
||||
'relation' => [
|
||||
'type' => Types::relation_enum(),
|
||||
'description' => __( 'OR or AND, how the sub-arrays should be compared', 'wp-graphql' ),
|
||||
],
|
||||
];
|
||||
}
|
||||
return self::prepare_fields( self::$fields, 'DateQuery' );
|
||||
}
|
||||
|
||||
/**
|
||||
* column_enum
|
||||
* Creates an Enum type with the columns that can be queried against for the DateQuery
|
||||
* @return EnumType|null
|
||||
* @since 0.0.5
|
||||
*/
|
||||
private static function column_enum() {
|
||||
|
||||
if ( null === self::$column_enum ) {
|
||||
self::$column_enum = new WPEnumType( [
|
||||
'name' => 'DateColumn',
|
||||
'values' => [
|
||||
'DATE' => [
|
||||
'value' => 'post_date',
|
||||
],
|
||||
'MODIFIED' => [
|
||||
'value' => 'post_modified',
|
||||
],
|
||||
],
|
||||
] );
|
||||
}
|
||||
return self::$column_enum;
|
||||
}
|
||||
|
||||
/**
|
||||
* date_after
|
||||
* Creates the date_after input field that allows "after" paramaters
|
||||
* to be entered
|
||||
* @return WPInputObjectType|null
|
||||
* @since 0.0.5
|
||||
*/
|
||||
private static function date_after() {
|
||||
if ( null === self::$date_after ) {
|
||||
self::$date_after = new WPInputObjectType( [
|
||||
'name' => 'DateAfter',
|
||||
'fields' => self::prepare_fields( [
|
||||
'year' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( '4 digit year (e.g. 2017)', 'wp-graphql' ),
|
||||
],
|
||||
'month' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( 'Month number (from 1 to 12)', 'wp-graphql' ),
|
||||
],
|
||||
'day' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( 'Day of the month (from 1 to 31)', 'wp-graphql' ),
|
||||
],
|
||||
], 'DateAfter' ),
|
||||
] );
|
||||
}
|
||||
return self::$date_after;
|
||||
}
|
||||
|
||||
/**
|
||||
* date_before
|
||||
* Creates the date_before input field that allows "before" paramaters
|
||||
* to be entered
|
||||
* @return WPInputObjectType|null
|
||||
* @since 0.0.5
|
||||
*/
|
||||
private static function date_before() {
|
||||
|
||||
if ( null === self::$date_before ) {
|
||||
self::$date_before = new WPInputObjectType( [
|
||||
'name' => 'DateBefore',
|
||||
'fields' => self::prepare_fields( [
|
||||
'year' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( '4 digit year (e.g. 2017)', 'wp-graphql' ),
|
||||
],
|
||||
'month' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( 'Month number (from 1 to 12)', 'wp-graphql' ),
|
||||
],
|
||||
'day' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( 'Day of the month (from 1 to 31)', 'wp-graphql' ),
|
||||
],
|
||||
], 'DateBefore' ),
|
||||
] );
|
||||
}
|
||||
return self::$date_before;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL\Type\PostObject\Connection;
|
||||
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQLRelay\Relay;
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\Data\DataSource;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class PostObjectConnectionDefinition
|
||||
*
|
||||
* @package WPGraphQL\Type\Comment\Connection
|
||||
* @since 0.0.5
|
||||
*/
|
||||
class PostObjectConnectionDefinition {
|
||||
|
||||
/**
|
||||
* Stores some date for the Relay connection for post objects
|
||||
*
|
||||
* @var array $connection
|
||||
* @since 0.0.5
|
||||
* @access private
|
||||
*/
|
||||
private static $connection = [];
|
||||
|
||||
/**
|
||||
* Stores the added_args for the connection (in addition to the standard Relay args)
|
||||
*
|
||||
* @var array $added_args
|
||||
*/
|
||||
protected static $added_args = [];
|
||||
|
||||
/**
|
||||
* Method that sets up the relay connection for post objects
|
||||
*
|
||||
* @param object $post_type_object The post type object for the connection is registered for
|
||||
* @param string $from_type The name of the type the connection is coming from
|
||||
*
|
||||
* @return mixed
|
||||
* @since 0.0.5
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function connection( $post_type_object, $from_type = 'Root' ) {
|
||||
|
||||
if ( empty( self::$connection[ $from_type ][ $post_type_object->name ] ) ) {
|
||||
/**
|
||||
* Setup the connectionDefinition
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
$connection = Relay::connectionDefinitions( [
|
||||
'nodeType' => Types::post_object( $post_type_object->name ),
|
||||
'name' => ucfirst( $from_type ) . ucfirst( $post_type_object->graphql_plural_name ),
|
||||
'connectionFields' => function() use ( $post_type_object ) {
|
||||
|
||||
return [
|
||||
'postTypeInfo' => [
|
||||
'type' => Types::post_type(),
|
||||
'description' => __( 'Information about the type of content being queried', 'wp-graphql' ),
|
||||
'resolve' => function( $source, array $args, AppContext $context, ResolveInfo $info ) use ( $post_type_object ) {
|
||||
return $post_type_object;
|
||||
},
|
||||
],
|
||||
'nodes' => [
|
||||
'type' => Types::list_of( Types::post_object( $post_type_object->name ) ),
|
||||
'description' => __( 'The nodes of the connection, without the edges', 'wp-graphql' ),
|
||||
'resolve' => function( $source, $args, $context, $info ) {
|
||||
return ! empty( $source['nodes'] ) ? $source['nodes'] : [];
|
||||
},
|
||||
],
|
||||
];
|
||||
},
|
||||
] );
|
||||
|
||||
/**
|
||||
* Add the connection to the post_objects_connection object
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
$connection_name = ucfirst( $from_type ) . ucfirst( $post_type_object->graphql_plural_name );
|
||||
self::$connection[ $from_type ][ $post_type_object->name ] = [
|
||||
'type' => $connection['connectionType'],
|
||||
// Translators: the placeholder is the name of the post_type
|
||||
'description' => sprintf( __( 'A collection of %s objects', 'wp-graphql' ), $post_type_object->graphql_plural_name ),
|
||||
'args' => array_merge( Relay::connectionArgs(), self::added_args( $connection_name ) ),
|
||||
'resolve' => function( $source, array $args, AppContext $context, ResolveInfo $info ) use ( $post_type_object ) {
|
||||
return DataSource::resolve_post_objects_connection( $source, $args, $context, $info, $post_type_object->name );
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the connection from the post_objects_connection object
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
return self::$connection[ $from_type ][ $post_type_object->name ];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the $args that should be added to the connection args
|
||||
*
|
||||
* @param string $connection The name of the connection the args belong to
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function added_args( $connection ) {
|
||||
|
||||
if ( empty( self::$added_args[ $connection ] ) ) {
|
||||
self::$added_args[ $connection ] = [
|
||||
'where' => [
|
||||
'name' => 'where',
|
||||
'description' => __( '', 'wp-graphql' ),
|
||||
'type' => Types::post_object_query_args( $connection ),
|
||||
],
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
return ! empty( self::$added_args[ $connection ] ) ? self::$added_args[ $connection ] : null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,377 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL\Type\PostObject\Connection;
|
||||
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQLRelay\Connection\ArrayConnection;
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\Data\ConnectionResolver;
|
||||
use WPGraphQL\Data\DataSource;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class PostObjectConnection - connects posts to other types
|
||||
*
|
||||
* @package WPGraphQL\Data\Resolvers
|
||||
* @since 0.0.5
|
||||
*/
|
||||
class PostObjectConnectionResolver extends ConnectionResolver {
|
||||
|
||||
/**
|
||||
* Stores the name of the $post_type being resolved
|
||||
*
|
||||
* @var $post_type
|
||||
*/
|
||||
public static $post_type;
|
||||
|
||||
/**
|
||||
* Holds the maximum number of items that can be queried per request
|
||||
*
|
||||
* @var int $max_query_amount
|
||||
*/
|
||||
public static $max_query_amount = 100;
|
||||
|
||||
/**
|
||||
* PostObjectConnectionResolver constructor.
|
||||
*
|
||||
* @param $post_type
|
||||
*/
|
||||
public function __construct( $post_type ) {
|
||||
self::$post_type = $post_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* This returns the $query_args that should be used when querying for posts in the postObjectConnectionResolver.
|
||||
* This checks what input $args are part of the query, combines them with various filters, etc and returns an
|
||||
* array of $query_args to be used in the \WP_Query call
|
||||
*
|
||||
* @param mixed $source The query source being passed down to the resolver
|
||||
* @param array $args The arguments that were provided to the query
|
||||
* @param AppContext $context Object containing app context that gets passed down the resolve tree
|
||||
* @param ResolveInfo $info Info about fields passed down the resolve tree
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_query_args( $source, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
|
||||
/**
|
||||
* Prepare for later use
|
||||
*/
|
||||
$last = ! empty( $args['last'] ) ? $args['last'] : null;
|
||||
$first = ! empty( $args['first'] ) ? $args['first'] : null;
|
||||
|
||||
/**
|
||||
* Set the post_type for the query based on the type of post being queried
|
||||
*/
|
||||
$query_args['post_type'] = ! empty( self::$post_type ) ? self::$post_type : 'post';
|
||||
|
||||
/**
|
||||
* Don't calculate the total rows, it's not needed and can be expensive
|
||||
*/
|
||||
$query_args['no_found_rows'] = true;
|
||||
|
||||
/**
|
||||
* Set the post_status to "publish" by default
|
||||
*/
|
||||
$query_args['post_status'] = 'publish';
|
||||
|
||||
/**
|
||||
* Set posts_per_page the highest value of $first and $last, with a (filterable) max of 100
|
||||
*/
|
||||
$query_args['posts_per_page'] = min( max( absint( $first ), absint( $last ), 10 ), self::get_query_amount( $source, $args, $context, $info ) ) + 1;
|
||||
|
||||
/**
|
||||
* Set the default to only query posts with no post_parent set
|
||||
*/
|
||||
$query_args['post_parent'] = 0;
|
||||
|
||||
/**
|
||||
* Set the graphql_cursor_offset which is used by Config::graphql_wp_query_cursor_pagination_support
|
||||
* to filter the WP_Query to support cursor pagination
|
||||
*/
|
||||
$query_args['graphql_cursor_offset'] = self::get_offset( $args );
|
||||
$query_args['graphql_cursor_compare'] = ( ! empty( $last ) ) ? '>' : '<';
|
||||
|
||||
/**
|
||||
* Pass the graphql $args to the WP_Query
|
||||
*/
|
||||
$query_args['graphql_args'] = $args;
|
||||
|
||||
/**
|
||||
* Collect the input_fields and sanitize them to prepare them for sending to the WP_Query
|
||||
*/
|
||||
$input_fields = [];
|
||||
if ( ! empty( $args['where'] ) ) {
|
||||
$input_fields = self::sanitize_input_fields( $args['where'], $source, $args, $context, $info );
|
||||
}
|
||||
|
||||
/**
|
||||
* If the post_type is "attachment" set the default "post_status" $query_arg to "inherit"
|
||||
*/
|
||||
if ( 'attachment' === self::$post_type ) {
|
||||
$query_args['post_status'] = 'inherit';
|
||||
|
||||
/**
|
||||
* Unset the "post_parent" for attachments, as we don't really care if they
|
||||
* have a post_parent set by default
|
||||
*/
|
||||
unset( $query_args['post_parent'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine where we're at in the Graph and adjust the query context appropriately.
|
||||
*
|
||||
* For example, if we're querying for posts as a field of termObject query, this will automatically
|
||||
* set the query to pull posts that belong to that term.
|
||||
*/
|
||||
if ( true === is_object( $source ) ) {
|
||||
switch ( true ) {
|
||||
case $source instanceof \WP_Post:
|
||||
$query_args['post_parent'] = $source->ID;
|
||||
break;
|
||||
case $source instanceof \WP_Post_Type:
|
||||
$query_args['post_type'] = $source->name;
|
||||
break;
|
||||
case $source instanceof \WP_Term:
|
||||
$query_args['tax_query'] = [
|
||||
[
|
||||
'taxonomy' => $source->taxonomy,
|
||||
'terms' => [ $source->term_id ],
|
||||
'field' => 'term_id',
|
||||
],
|
||||
];
|
||||
break;
|
||||
case $source instanceof \WP_User:
|
||||
$query_args['author'] = $source->ID;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the input_fields with the default query_args
|
||||
*/
|
||||
if ( ! empty( $input_fields ) ) {
|
||||
$query_args = array_merge( $query_args, $input_fields );
|
||||
}
|
||||
|
||||
/**
|
||||
* Map the orderby inputArgs to the WP_Query
|
||||
*/
|
||||
if ( ! empty( $args['where']['orderby'] ) && is_array( $args['where']['orderby'] ) ) {
|
||||
$query_args['orderby'] = [];
|
||||
foreach ( $args['where']['orderby'] as $orderby_input ) {
|
||||
/**
|
||||
* These orderby options should not include the order parameter.
|
||||
*/
|
||||
if ( in_array( $orderby_input['field'], [ 'post__in', 'post_name__in', 'post_parent__in' ], true ) ) {
|
||||
$query_args['orderby'] = esc_sql( $orderby_input['field'] );
|
||||
} else if ( ! empty( $orderby_input['field'] ) ) {
|
||||
$query_args['orderby'] = [
|
||||
esc_sql( $orderby_input['field'] ) => esc_sql( $orderby_input['order'] ),
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If there's no orderby params in the inputArgs, set order based on the first/last argument
|
||||
*/
|
||||
if ( empty( $query_args['orderby'] ) ) {
|
||||
$query_args['order'] = ! empty( $last ) ? 'ASC' : 'DESC';
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the $query args to allow folks to customize queries programmatically
|
||||
*
|
||||
* @param array $query_args The args that will be passed to the WP_Query
|
||||
* @param mixed $source The source that's passed down the GraphQL queries
|
||||
* @param array $args The inputArgs on the field
|
||||
* @param AppContext $context The AppContext passed down the GraphQL tree
|
||||
* @param ResolveInfo $info The ResolveInfo passed down the GraphQL tree
|
||||
*/
|
||||
$query_args = apply_filters( 'graphql_post_object_connection_query_args', $query_args, $source, $args, $context, $info );
|
||||
return $query_args;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This runs the query and returns the response
|
||||
*
|
||||
* @param $query_args
|
||||
*
|
||||
* @return \WP_Query
|
||||
*/
|
||||
public static function get_query( $query_args ) {
|
||||
$query = new \WP_Query( $query_args );
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* This takes an array of items, the $args and the $query and returns the connection including
|
||||
* the edges and page info
|
||||
*
|
||||
* @param mixed $query The Query that was processed to get the connection data
|
||||
* @param array $items The array of items being connected
|
||||
* @param array $args The $args that were passed to the query
|
||||
* @param mixed $source The source being passed down the resolve tree
|
||||
* @param AppContext $context The AppContext being passed down the resolve tree
|
||||
* @param ResolveInfo $info the ResolveInfo passed down the resolve tree
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_connection( $query, array $items, $source, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
|
||||
/**
|
||||
* Get the $posts from the query
|
||||
*/
|
||||
$items = ! empty( $items ) && is_array( $items ) ? $items : [];
|
||||
|
||||
$info = self::get_query_info( $query );
|
||||
|
||||
/**
|
||||
* Set whether there is or is not another page
|
||||
*/
|
||||
$has_previous_page = ( ! empty( $args['last'] ) && ( $info['total_items'] >= self::get_query_amount( $source, $args, $context, $info ) ) ) ? true : false;
|
||||
$has_next_page = ( ! empty( $args['first'] ) && ( $info['total_items'] >= self::get_query_amount( $source, $args, $context, $info ) ) ) ? true : false;
|
||||
|
||||
/**
|
||||
* Slice the array to the amount of items that were requested
|
||||
*/
|
||||
$items = array_slice( $items, 0, self::get_query_amount( $source, $args, $query, $info ) );
|
||||
|
||||
/**
|
||||
* Get the edges from the $items
|
||||
*/
|
||||
$edges = self::get_edges( $items, $source, $args, $context, $info );
|
||||
|
||||
/**
|
||||
* Find the first_edge and last_edge
|
||||
*/
|
||||
$first_edge = $edges ? $edges[0] : null;
|
||||
$last_edge = $edges ? $edges[ count( $edges ) - 1 ] : null;
|
||||
$edges_to_return = $edges;
|
||||
|
||||
/**
|
||||
* Create the connection to return
|
||||
*/
|
||||
$connection = [
|
||||
'edges' => $edges_to_return,
|
||||
'pageInfo' => [
|
||||
'hasPreviousPage' => $has_previous_page,
|
||||
'hasNextPage' => $has_next_page,
|
||||
'startCursor' => ! empty( $first_edge['cursor'] ) ? $first_edge['cursor'] : null,
|
||||
'endCursor' => ! empty( $last_edge['cursor'] ) ? $last_edge['cursor'] : null,
|
||||
],
|
||||
'nodes' => $items,
|
||||
];
|
||||
|
||||
return $connection;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an array of items and returns the edges
|
||||
*
|
||||
* @param $items
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_edges( $items, $source, $args, $context, $info ) {
|
||||
$edges = [];
|
||||
|
||||
/**
|
||||
* If we're doing backward pagination we want to reverse the array before
|
||||
* returning it to the edges
|
||||
*/
|
||||
if ( ! empty( $args['last'] ) ) {
|
||||
$items = array_reverse( $items );
|
||||
}
|
||||
|
||||
if ( ! empty( $items ) && is_array( $items ) ) {
|
||||
foreach ( $items as $item ) {
|
||||
$edges[] = [
|
||||
'cursor' => ArrayConnection::offsetToCursor( $item->ID ),
|
||||
'node' => DataSource::resolve_post_object( $item->ID, $item->post_type ),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $edges;
|
||||
}
|
||||
|
||||
/**
|
||||
* This sets up the "allowed" args, and translates the GraphQL-friendly keys to WP_Query
|
||||
* friendly keys. There's probably a cleaner/more dynamic way to approach this, but
|
||||
* this was quick. I'd be down to explore more dynamic ways to map this, but for
|
||||
* now this gets the job done.
|
||||
*
|
||||
* @param array $args Query "where" args
|
||||
* @param mixed $source The query results for a query calling this
|
||||
* @param array $all_args All of the arguments for the query (not just the "where" args)
|
||||
* @param AppContext $context The AppContext object
|
||||
* @param ResolveInfo $info The ResolveInfo object
|
||||
*
|
||||
* @since 0.0.5
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public static function sanitize_input_fields( array $args, $source, array $all_args, AppContext $context, ResolveInfo $info ) {
|
||||
|
||||
$arg_mapping = [
|
||||
'authorName' => 'author_name',
|
||||
'authorIn' => 'author__in',
|
||||
'authorNotIn' => 'author__not_in',
|
||||
'categoryId' => 'cat',
|
||||
'categoryName' => 'category_name',
|
||||
'categoryIn' => 'category__in',
|
||||
'tagId' => 'tag_id',
|
||||
'tagIds' => 'tag__and',
|
||||
'tagSlugAnd' => 'tag_slug__and',
|
||||
'tagSlugIn' => 'tag_slug__in',
|
||||
'search' => 's',
|
||||
'id' => 'p',
|
||||
'parent' => 'post_parent',
|
||||
'parentIn' => 'post_parent__in',
|
||||
'parentNotIn' => 'post_parent__not_in',
|
||||
'in' => 'post__in',
|
||||
'notIn' => 'post__not_in',
|
||||
'nameIn' => 'post_name__in',
|
||||
'hasPassword' => 'has_password',
|
||||
'password' => 'post_password',
|
||||
'status' => 'post_status',
|
||||
'stati' => 'post_status',
|
||||
'dateQuery' => 'date_query',
|
||||
];
|
||||
|
||||
/**
|
||||
* Map and sanitize the input args to the WP_Query compatible args
|
||||
*/
|
||||
$query_args = Types::map_input( $args, $arg_mapping );
|
||||
|
||||
/**
|
||||
* Filter the input fields
|
||||
* This allows plugins/themes to hook in and alter what $args should be allowed to be passed
|
||||
* from a GraphQL Query to the WP_Query
|
||||
*
|
||||
* @param array $query_args The mapped query arguments
|
||||
* @param array $args Query "where" args
|
||||
* @param string $post_type The post type for the query
|
||||
* @param mixed $source The query results for a query calling this
|
||||
* @param array $all_args All of the arguments for the query (not just the "where" args)
|
||||
* @param AppContext $context The AppContext object
|
||||
* @param ResolveInfo $info The ResolveInfo object
|
||||
*
|
||||
* @since 0.0.5
|
||||
* @return array
|
||||
*/
|
||||
$query_args = apply_filters( 'graphql_map_input_fields_to_wp_query', $query_args, $args, $source, $all_args, $context, $info );
|
||||
|
||||
/**
|
||||
* Return the Query Args
|
||||
*/
|
||||
return ! empty( $query_args ) && is_array( $query_args ) ? $query_args : [];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL\Type\PostObject\Mutation;
|
||||
|
||||
use GraphQL\Error\UserError;
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQLRelay\Relay;
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\Type\WPInputObjectType;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class PostObjectCreate
|
||||
*
|
||||
* @package WPGraphQL\Type\PostObject\Mutation
|
||||
*/
|
||||
class PostObjectCreate {
|
||||
|
||||
/**
|
||||
* Holds the mutation field definition
|
||||
*
|
||||
* @var array $mutation
|
||||
*/
|
||||
private static $mutation = [];
|
||||
|
||||
/**
|
||||
* Defines the create mutation for PostTypeObjects
|
||||
*
|
||||
* @param \WP_Post_Type $post_type_object
|
||||
*
|
||||
* @return array|mixed
|
||||
*/
|
||||
public static function mutate( \WP_Post_Type $post_type_object ) {
|
||||
|
||||
if (
|
||||
! empty( $post_type_object->graphql_single_name ) &&
|
||||
empty( self::$mutation[ $post_type_object->graphql_single_name ] )
|
||||
) {
|
||||
|
||||
/**
|
||||
* Set the name of the mutation being performed
|
||||
*/
|
||||
$mutation_name = 'Create' . ucwords( $post_type_object->graphql_single_name );
|
||||
|
||||
self::$mutation[ $post_type_object->graphql_single_name ] = Relay::mutationWithClientMutationId( [
|
||||
'name' => $mutation_name,
|
||||
// translators: The placeholder is the name of the object type
|
||||
'description' => sprintf( __( 'Create %1$s objects', 'wp-graphql' ), $post_type_object->graphql_single_name ),
|
||||
'inputFields' => WPInputObjectType::prepare_fields( PostObjectMutation::input_fields( $post_type_object ), $mutation_name ),
|
||||
'outputFields' => [
|
||||
$post_type_object->graphql_single_name => [
|
||||
'type' => Types::post_object( $post_type_object->name ),
|
||||
'resolve' => function( $payload ) {
|
||||
return get_post( $payload['id'] );
|
||||
},
|
||||
],
|
||||
],
|
||||
'mutateAndGetPayload' => function( $input, AppContext $context, ResolveInfo $info ) use ( $post_type_object, $mutation_name ) {
|
||||
|
||||
/**
|
||||
* Throw an exception if there's no input
|
||||
*/
|
||||
if ( ( empty( $post_type_object->name ) ) || ( empty( $input ) || ! is_array( $input ) ) ) {
|
||||
throw new UserError( __( 'Mutation not processed. There was no input for the mutation or the post_type_object was invalid', 'wp-graphql' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop now if a user isn't allowed to create a post
|
||||
*/
|
||||
if ( ! current_user_can( $post_type_object->cap->create_posts ) ) {
|
||||
// translators: the $post_type_object->graphql_plural_name placeholder is the name of the object being mutated
|
||||
throw new UserError( sprintf( __( 'Sorry, you are not allowed to create %1$s', 'wp-graphql' ), $post_type_object->graphql_plural_name ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* If the post being created is being assigned to another user that's not the current user, make sure
|
||||
* the current user has permission to edit others posts for this post_type
|
||||
*/
|
||||
if ( ! empty( $input['authorId'] ) && get_current_user_id() !== $input['authorId'] && ! current_user_can( $post_type_object->cap->edit_others_posts ) ) {
|
||||
// translators: the $post_type_object->graphql_plural_name placeholder is the name of the object being mutated
|
||||
throw new UserError( sprintf( __( 'Sorry, you are not allowed to create %1$s as this user', 'wp-graphql' ), $post_type_object->graphql_plural_name ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo: When we support assigning terms and setting posts as "sticky" we need to check permissions
|
||||
* @see :https://github.com/WordPress/WordPress/blob/e357195ce303017d517aff944644a7a1232926f7/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php#L504-L506
|
||||
* @see : https://github.com/WordPress/WordPress/blob/e357195ce303017d517aff944644a7a1232926f7/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php#L496-L498
|
||||
*/
|
||||
|
||||
/**
|
||||
* insert the post object and get the ID
|
||||
*/
|
||||
$post_args = PostObjectMutation::prepare_post_object( $input, $post_type_object, $mutation_name );
|
||||
|
||||
/**
|
||||
* Filter the default post status to use when the post is initially created. Pass through a filter to
|
||||
* allow other plugins to override the default (for example, Edit Flow, which provides control over
|
||||
* customizing stati or various E-commerce plugins that make heavy use of custom stati)
|
||||
*
|
||||
* @param string $default_status The default status to be used when the post is initially inserted
|
||||
* @param \WP_Post_Type $post_type_object The Post Type that is being inserted
|
||||
* @param string $mutation_name The name of the mutation currently in progress
|
||||
*/
|
||||
$default_post_status = apply_filters( 'graphql_post_object_create_default_post_status', 'draft', $post_type_object, $mutation_name );
|
||||
|
||||
/**
|
||||
* We want to cache the "post_status" and set the status later. We will set the initial status
|
||||
* of the inserted post as the default status for the site, allow side effects to process with the
|
||||
* inserted post (set term object connections, set meta input, sideload images if necessary, etc)
|
||||
* Then will follow up with setting the status as what it was declared to be later
|
||||
*/
|
||||
$intended_post_status = ! empty( $post_args['post_status'] ) ? $post_args['post_status'] : $default_post_status;
|
||||
|
||||
/**
|
||||
* Set the post_status as the default for the initial insert. The intended $post_status will be set after
|
||||
* side effects are complete.
|
||||
*/
|
||||
$post_args['post_status'] = $default_post_status;
|
||||
|
||||
/**
|
||||
* Insert the post and retrieve the ID
|
||||
*/
|
||||
$post_id = wp_insert_post( wp_slash( (array) $post_args ), true );
|
||||
|
||||
/**
|
||||
* Throw an exception if the post failed to create
|
||||
*/
|
||||
if ( is_wp_error( $post_id ) ) {
|
||||
$error_message = $post_id->get_error_message();
|
||||
if ( ! empty( $error_message ) ) {
|
||||
throw new UserError( esc_html( $error_message ) );
|
||||
} else {
|
||||
throw new UserError( __( 'The object failed to create but no error was provided', 'wp-graphql' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the $post_id is empty, we should throw an exception
|
||||
*/
|
||||
if ( empty( $post_id ) ) {
|
||||
throw new UserError( __( 'The object failed to create', 'wp-graphql' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* This updates additional data not part of the posts table (postmeta, terms, other relations, etc)
|
||||
*
|
||||
* The input for the postObjectMutation will be passed, along with the $new_post_id for the
|
||||
* postObject that was created so that relations can be set, meta can be updated, etc.
|
||||
*/
|
||||
PostObjectMutation::update_additional_post_object_data( $post_id, $input, $post_type_object, $mutation_name, $context, $info, $default_post_status, $intended_post_status );
|
||||
|
||||
/**
|
||||
* Determine whether the intended status should be set or not.
|
||||
*
|
||||
* By filtering to false, the $intended_post_status will not be set at the completion of the mutation.
|
||||
*
|
||||
* This allows for side-effect actions to set the status later. For example, if a post
|
||||
* was being created via a GraphQL Mutation, the post had additional required assets, such as images
|
||||
* that needed to be sideloaded or some other semi-time-consuming side effect, those actions could
|
||||
* be deferred (cron or whatever), and when those actions complete they could come back and set
|
||||
* the $intended_status.
|
||||
*
|
||||
* @param boolean $should_set_intended_status Whether to set the intended post_status or not. Default true.
|
||||
* @param \WP_Post_Type $post_type_object The Post Type Object for the post being mutated
|
||||
* @param string $mutation_name The name of the mutation currently in progress
|
||||
* @param AppContext $context The AppContext passed down to all resolvers
|
||||
* @param ResolveInfo $info The ResolveInfo passed down to all resolvers
|
||||
* @param string $intended_post_status The intended post_status the post should have according to the mutation input
|
||||
* @param string $default_post_status The default status posts should use if an intended status wasn't set
|
||||
*/
|
||||
$should_set_intended_status = apply_filters( 'graphql_post_object_create_should_set_intended_post_status', true, $post_type_object, $mutation_name, $context, $info, $intended_post_status, $default_post_status );
|
||||
|
||||
/**
|
||||
* If the intended post status and the default post status are not the same,
|
||||
* update the post with the intended status now that side effects are complete.
|
||||
*/
|
||||
if ( $intended_post_status !== $default_post_status && true === $should_set_intended_status ) {
|
||||
|
||||
/**
|
||||
* If the post was deleted by a side effect action before getting here,
|
||||
* don't proceed.
|
||||
*/
|
||||
if ( ! $new_post = get_post( $post_id ) ) {
|
||||
throw new UserError( sprintf( __( 'The status of the post could not be set', 'wp-graphql' ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* If the $intended_post_status is different than the current status of the post
|
||||
* proceed and update the status.
|
||||
*/
|
||||
if ( $intended_post_status !== $new_post->post_status ) {
|
||||
$update_args = [
|
||||
'ID' => $post_id,
|
||||
'post_status' => $intended_post_status,
|
||||
// Prevent the post_date from being reset if the date was included in the create post $args
|
||||
// see: https://core.trac.wordpress.org/browser/tags/4.9/src/wp-includes/post.php#L3637
|
||||
'edit_date' => ! empty( $post_args['post_date'] ) ? $post_args['post_date'] : false,
|
||||
];
|
||||
|
||||
wp_update_post( $update_args );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the post object
|
||||
*/
|
||||
return [
|
||||
'id' => $post_id,
|
||||
];
|
||||
},
|
||||
|
||||
] );
|
||||
|
||||
}
|
||||
|
||||
return ! empty( self::$mutation[ $post_type_object->graphql_single_name ] ) ? self::$mutation[ $post_type_object->graphql_single_name ] : null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL\Type\PostObject\Mutation;
|
||||
|
||||
use GraphQL\Error\UserError;
|
||||
use GraphQLRelay\Relay;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class PostObjectDelete
|
||||
*
|
||||
* @package WPGraphQL\Type\PostObject\Mutation
|
||||
*/
|
||||
class PostObjectDelete {
|
||||
|
||||
/**
|
||||
* Holds the mutation field definition
|
||||
*
|
||||
* @var array $mutation
|
||||
*/
|
||||
private static $mutation = [];
|
||||
|
||||
/**
|
||||
* Defines the delete mutation for PostTypeObjects
|
||||
*
|
||||
* @param \WP_Post_Type $post_type_object
|
||||
*
|
||||
* @return array|mixed
|
||||
*/
|
||||
public static function mutate( \WP_Post_Type $post_type_object ) {
|
||||
|
||||
if (
|
||||
! empty( $post_type_object->graphql_single_name ) &&
|
||||
empty( self::$mutation[ $post_type_object->graphql_single_name ] )
|
||||
) {
|
||||
|
||||
/**
|
||||
* Set the name of the mutation being performed
|
||||
*/
|
||||
$mutation_name = 'Delete' . ucwords( $post_type_object->graphql_single_name );
|
||||
|
||||
self::$mutation[ $post_type_object->graphql_single_name ] = Relay::mutationWithClientMutationId( [
|
||||
'name' => esc_html( $mutation_name ),
|
||||
// translators: The placeholder is the name of the object type
|
||||
'description' => sprintf( __( 'Delete %1$s objects. By default %1$s objects will be moved to the trash unless the forceDelete is used', 'wp-graphql' ), $post_type_object->graphql_single_name ),
|
||||
'inputFields' => [
|
||||
'id' => [
|
||||
'type' => Types::non_null( Types::id() ),
|
||||
// translators: The placeholder is the name of the post's post_type being deleted
|
||||
'description' => sprintf( __( 'The ID of the %1$s to delete', 'wp-graphql' ), $post_type_object->graphql_single_name ),
|
||||
],
|
||||
'forceDelete' => [
|
||||
'type' => Types::boolean(),
|
||||
'description' => __( 'Whether the object should be force deleted instead of being moved to the trash', 'wp-graphql' ),
|
||||
],
|
||||
],
|
||||
'outputFields' => [
|
||||
'deletedId' => [
|
||||
'type' => Types::id(),
|
||||
'description' => __( 'The ID of the deleted object', 'wp-graphql' ),
|
||||
'resolve' => function( $payload ) use ( $post_type_object ) {
|
||||
$deleted = (object) $payload['postObject'];
|
||||
|
||||
return ! empty( $deleted->ID ) ? Relay::toGlobalId( $post_type_object->name, absint( $deleted->ID ) ) : null;
|
||||
},
|
||||
],
|
||||
$post_type_object->graphql_single_name => [
|
||||
'type' => Types::post_object( $post_type_object->name ),
|
||||
'description' => __( 'The object before it was deleted', 'wp-graphql' ),
|
||||
'resolve' => function( $payload ) {
|
||||
$deleted = (object) $payload['postObject'];
|
||||
|
||||
return ! empty( $deleted ) ? $deleted : null;
|
||||
},
|
||||
],
|
||||
],
|
||||
'mutateAndGetPayload' => function( $input ) use ( $post_type_object, $mutation_name ) {
|
||||
|
||||
/**
|
||||
* Get the ID from the global ID
|
||||
*/
|
||||
$id_parts = Relay::fromGlobalId( $input['id'] );
|
||||
|
||||
/**
|
||||
* Stop now if a user isn't allowed to delete a post
|
||||
*/
|
||||
if ( ! current_user_can( $post_type_object->cap->delete_post, absint( $id_parts['id'] ) ) ) {
|
||||
// translators: the $post_type_object->graphql_plural_name placeholder is the name of the object being mutated
|
||||
throw new UserError( sprintf( __( 'Sorry, you are not allowed to delete %1$s', 'wp-graphql' ), $post_type_object->graphql_plural_name ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we should force delete or not
|
||||
*/
|
||||
$force_delete = ( ! empty( $input['forceDelete'] ) && true === $input['forceDelete'] ) ? true : false;
|
||||
|
||||
/**
|
||||
* Get the post object before deleting it
|
||||
*/
|
||||
$post_before_delete = get_post( absint( $id_parts['id'] ) );
|
||||
|
||||
/**
|
||||
* If the post is already in the trash, and the forceDelete input was not passed,
|
||||
* don't remove from the trash
|
||||
*/
|
||||
if ( 'trash' === $post_before_delete->post_status ) {
|
||||
if ( true !== $force_delete ) {
|
||||
// Translators: the first placeholder is the post_type of the object being deleted and the second placeholder is the unique ID of that object
|
||||
throw new UserError( sprintf( __( 'The %1$s with id %2$s is already in the trash. To remove from the trash, use the forceDelete input', 'wp-graphql' ), $post_type_object->graphql_single_name, $input['id'] ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the post
|
||||
*/
|
||||
$deleted = wp_delete_post( $id_parts['id'], $force_delete );
|
||||
|
||||
/**
|
||||
* If the post was moved to the trash, spoof the object's status before returning it
|
||||
*/
|
||||
$post_before_delete->post_status = ( false !== $deleted && true !== $force_delete ) ? 'trash' : $post_before_delete->post_status;
|
||||
|
||||
/**
|
||||
* Return the deletedId and the object before it was deleted
|
||||
*/
|
||||
return [
|
||||
'postObject' => $post_before_delete,
|
||||
];
|
||||
|
||||
},
|
||||
] );
|
||||
|
||||
}
|
||||
|
||||
return ! empty( self::$mutation[ $post_type_object->graphql_single_name ] ) ? self::$mutation[ $post_type_object->graphql_single_name ] : null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,640 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL\Type\PostObject\Mutation;
|
||||
|
||||
use GraphQL\Error\UserError;
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQLRelay\Relay;
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\Type\WPInputObjectType;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class PostObjectMutation
|
||||
*
|
||||
* @package WPGraphQL\Type\PostObject
|
||||
*/
|
||||
class PostObjectMutation {
|
||||
|
||||
/**
|
||||
* Holds the input_fields configuration
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $input_fields = [];
|
||||
|
||||
/**
|
||||
* @param $post_type_object
|
||||
*
|
||||
* @return mixed|array|null $input_fields
|
||||
*/
|
||||
public static function input_fields( $post_type_object ) {
|
||||
|
||||
if ( ! empty( $post_type_object->graphql_single_name ) && empty( self::$input_fields[ $post_type_object->graphql_single_name ] ) ) {
|
||||
|
||||
$input_fields = [
|
||||
'authorId' => [
|
||||
'type' => Types::id(),
|
||||
'description' => __( 'The userId to assign as the author of the post', 'wp-graphql' ),
|
||||
],
|
||||
'commentCount' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( 'The number of comments. Even though WPGraphQL denotes this field as an integer, in WordPress this field should be saved as a numeric string for compatability.', 'wp-graphql' ),
|
||||
],
|
||||
'commentStatus' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The comment status for the object', 'wp-graphql' ),
|
||||
],
|
||||
'content' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The content of the object', 'wp-graphql' ),
|
||||
],
|
||||
'date' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The date of the object. Preferable to enter as year/month/day (e.g. 01/31/2017) as it will rearrange date as fit if it is not specified. Incomplete dates may have unintended results for example, "2017" as the input will use current date with timestamp 20:17 ', 'wp-graphql' ),
|
||||
],
|
||||
'excerpt' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The excerpt of the object', 'wp-graphql' ),
|
||||
],
|
||||
'menuOrder' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( 'A field used for ordering posts. This is typically used with nav menu items or for special ordering of hierarchical content types.', 'wp-graphql' ),
|
||||
],
|
||||
'mimeType' => [
|
||||
'type' => Types::mime_type_enum(),
|
||||
'description' => __( 'If the post is an attachment or a media file, this field will carry the corresponding MIME type. This field is equivalent to the value of WP_Post->post_mime_type and the post_mime_type column in the `post_objects` database table.', 'wp-graphql' ),
|
||||
],
|
||||
'parentId' => [
|
||||
'type' => Types::id(),
|
||||
'description' => __( 'The ID of the parent object', 'wp-graphql' ),
|
||||
],
|
||||
'password' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The password used to protect the content of the object', 'wp-graphql' ),
|
||||
],
|
||||
'pinged' => [
|
||||
'type' => Types::list_of( Types::string() ),
|
||||
'description' => __( 'URLs that have been pinged.', 'wp-graphql' ),
|
||||
],
|
||||
'pingStatus' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The ping status for the object', 'wp-graphql' ),
|
||||
],
|
||||
'slug' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The slug of the object', 'wp-graphql' ),
|
||||
],
|
||||
'status' => [
|
||||
'type' => Types::post_status_enum(),
|
||||
'description' => __( 'The status of the object', 'wp-graphql' ),
|
||||
],
|
||||
'title' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The title of the post', 'wp-graphql' ),
|
||||
],
|
||||
'toPing' => [
|
||||
'type' => Types::list_of( Types::string() ),
|
||||
'description' => __( 'URLs queued to be pinged.', 'wp-graphql' ),
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Add inputs for connected taxonomies
|
||||
*/
|
||||
$allowed_taxonomies = \WPGraphQL::$allowed_taxonomies;
|
||||
if ( ! empty( $allowed_taxonomies ) && is_array( $allowed_taxonomies ) ) {
|
||||
foreach ( $allowed_taxonomies as $taxonomy ) {
|
||||
// If the taxonomy is in the array of taxonomies registered to the post_type
|
||||
if ( in_array( $taxonomy, get_object_taxonomies( $post_type_object->name ), true ) ) {
|
||||
$tax_object = get_taxonomy( $taxonomy );
|
||||
|
||||
$node_input = new WPInputObjectType( [
|
||||
'name' => $post_type_object->graphql_single_name . ucfirst( $tax_object->graphql_plural_name ) . 'Nodes',
|
||||
'description' => sprintf( __( 'List of %1$s to connect the %2$s to. If an ID is set, it will be used to create the connection. If not, it will look for a slug. If neither are valid existing terms, and the site is configured to allow terms to be created during post mutations, a term will be created using the Name if it exists in the input, then fallback to the slug if it exists.', 'wp-graphql' ), $tax_object->graphql_plural_name, $post_type_object->graphql_single_name ),
|
||||
'fields' => [
|
||||
'id' => [
|
||||
'type' => Types::id(),
|
||||
'description' => sprintf( __( 'The ID of the %1$s. If present, this will be used to connect to the %2$s. If no existing %1$s exists with this ID, no connection will be made.', 'wp-graphql' ), $tax_object->graphql_single_name, $post_type_object->graphql_single_name ),
|
||||
],
|
||||
'slug' => [
|
||||
'type' => Types::string(),
|
||||
'description' => sprintf( __( 'The slug of the %1$s. If no ID is present, this field will be used to make a connection. If no existing term exists with this slug, this field will be used as a fallback to the Name field when creating a new term to connect to, if term creation is enabled as a nested mutation.', 'wp-graphql' ), $tax_object->graphql_single_name ),
|
||||
],
|
||||
'description' => [
|
||||
'type' => Types::string(),
|
||||
'description' => sprintf( __( 'The description of the %1$s. This field is used to set a description of the %1$s if a new one is created during the mutation.', 'wp-graphql' ), $tax_object->graphql_single_name ),
|
||||
],
|
||||
'name' => [
|
||||
'type' => Types::string(),
|
||||
'description' => sprintf( __( 'The name of the %1$s. This field is used to create a new term, if term creation is enabled in nested mutations, and if one does not already exist with the provided slug or ID or if a slug or ID is not provided. If no name is included and a term is created, the creation will fallback to the slug field.', 'wp-graphql' ), $tax_object->graphql_single_name ),
|
||||
],
|
||||
],
|
||||
] );
|
||||
|
||||
$input_fields[ $tax_object->graphql_plural_name ] = [
|
||||
'description' => sprintf( __( 'Set connections between the %1$s and %2$s', 'wp-graphql' ), $post_type_object->graphql_single_name, $tax_object->graphql_plural_name ),
|
||||
'type' => new WPInputObjectType( [
|
||||
'name' => ucfirst( $post_type_object->graphql_single_name ) . ucfirst( $tax_object->graphql_plural_name ),
|
||||
'description' => sprintf( __( 'Set relationships between the %1$s to %2$s', 'wp-graphql' ), $post_type_object->graphql_single_name, $tax_object->graphql_plural_name ),
|
||||
'fields' => [
|
||||
'append' => [
|
||||
'type' => Types::boolean(),
|
||||
'description' => sprintf( __( 'If true, this will append the %1$s to existing related %2$s. If false, this will replace existing relationships. Default true.', 'wp-graphql' ), $tax_object->graphql_single_name, $tax_object->graphql_plural_name ),
|
||||
],
|
||||
'nodes' => [
|
||||
'type' => Types::list_of( $node_input ),
|
||||
],
|
||||
],
|
||||
] ),
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the mutation input fields for the object type
|
||||
*
|
||||
* @param array $input_fields The array of input fields
|
||||
* @param \WP_Post_Type $post_type_object The post_type object for the type of Post being mutated
|
||||
*/
|
||||
self::$input_fields[ $post_type_object->graphql_single_name ] = apply_filters( 'graphql_post_object_mutation_input_fields', $input_fields, $post_type_object );
|
||||
|
||||
|
||||
} // End if().
|
||||
|
||||
return ! empty( self::$input_fields[ $post_type_object->graphql_single_name ] ) ? self::$input_fields[ $post_type_object->graphql_single_name ] : null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This handles inserting the post object
|
||||
*
|
||||
* @param array $input The input for the mutation
|
||||
* @param \WP_Post_Type $post_type_object The post_type_object for the type of post being mutated
|
||||
* @param string $mutation_name The name of the mutation being performed
|
||||
*
|
||||
* @return array $insert_post_args
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function prepare_post_object( $input, $post_type_object, $mutation_name ) {
|
||||
|
||||
/**
|
||||
* Set the post_type for the insert
|
||||
*/
|
||||
$insert_post_args['post_type'] = $post_type_object->name;
|
||||
|
||||
/**
|
||||
* Prepare the data for inserting the post
|
||||
* NOTE: These are organized in the same order as: https://developer.wordpress.org/reference/functions/wp_insert_post/
|
||||
*/
|
||||
$author_id_parts = ! empty( $input['authorId'] ) ? Relay::fromGlobalId( $input['authorId'] ) : null;
|
||||
if ( is_array( $author_id_parts ) && ! empty( $author_id_parts['id'] ) && is_int( $author_id_parts['id'] ) ) {
|
||||
$insert_post_args['post_author'] = absint( $author_id_parts['id'] );
|
||||
}
|
||||
|
||||
if ( ! empty( $input['date'] ) && false !== strtotime( $input['date'] ) ) {
|
||||
$insert_post_args['post_date'] = date( 'Y-m-d H:i:s', strtotime( $input['date'] ) );
|
||||
}
|
||||
|
||||
if ( ! empty( $input['content'] ) ) {
|
||||
$insert_post_args['post_content'] = $input['content'];
|
||||
}
|
||||
|
||||
if ( ! empty( $input['title'] ) ) {
|
||||
$insert_post_args['post_title'] = $input['title'];
|
||||
}
|
||||
|
||||
if ( ! empty( $input['excerpt'] ) ) {
|
||||
$insert_post_args['post_excerpt'] = $input['excerpt'];
|
||||
}
|
||||
|
||||
if ( ! empty( $input['status'] ) ) {
|
||||
$insert_post_args['post_status'] = $input['status'];
|
||||
}
|
||||
|
||||
if ( ! empty( $input['commentStatus'] ) ) {
|
||||
$insert_post_args['comment_status'] = $input['commentStatus'];
|
||||
}
|
||||
|
||||
if ( ! empty( $input['pingStatus'] ) ) {
|
||||
$insert_post_args['ping_status'] = $input['pingStatus'];
|
||||
}
|
||||
|
||||
if ( ! empty( $input['password'] ) ) {
|
||||
$insert_post_args['post_password'] = $input['password'];
|
||||
}
|
||||
|
||||
if ( ! empty( $input['slug'] ) ) {
|
||||
$insert_post_args['post_name'] = $input['slug'];
|
||||
}
|
||||
|
||||
if ( ! empty( $input['toPing'] ) ) {
|
||||
$insert_post_args['to_ping'] = $input['toPing'];
|
||||
}
|
||||
|
||||
if ( ! empty( $input['pinged'] ) ) {
|
||||
$insert_post_args['pinged'] = $input['pinged'];
|
||||
}
|
||||
|
||||
$parent_id_parts = ! empty( $input['parentId'] ) ? Relay::fromGlobalId( $input['parentId'] ) : null;
|
||||
if ( is_array( $parent_id_parts ) && ! empty( $parent_id_parts['id'] ) && is_int( $parent_id_parts['id'] ) ) {
|
||||
$insert_post_args['post_parent'] = absint( $parent_id_parts['id'] );
|
||||
}
|
||||
|
||||
if ( ! empty( $input['menuOrder'] ) ) {
|
||||
$insert_post_args['menu_order'] = $input['menuOrder'];
|
||||
}
|
||||
|
||||
if ( ! empty( $input['mimeType'] ) ) {
|
||||
$insert_post_args['post_mime_type'] = $input['mimeType'];
|
||||
}
|
||||
|
||||
if ( ! empty( $input['commentCount'] ) ) {
|
||||
$insert_post_args['comment_count'] = $input['commentCount'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the $insert_post_args
|
||||
*
|
||||
* @param array $insert_post_args The array of $input_post_args that will be passed to wp_insert_post
|
||||
* @param array $input The data that was entered as input for the mutation
|
||||
* @param \WP_Post_Type $post_type_object The post_type_object that the mutation is affecting
|
||||
* @param string $mutation_type The type of mutation being performed (create, edit, etc)
|
||||
*/
|
||||
$insert_post_args = apply_filters( 'graphql_post_object_insert_post_args', $insert_post_args, $input, $post_type_object, $mutation_name );
|
||||
|
||||
/**
|
||||
* Return the $args
|
||||
*/
|
||||
return $insert_post_args;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This updates additional data related to a post object, such as postmeta, term relationships, etc.
|
||||
*
|
||||
* @param int $post_id $post_id The ID of the postObject being mutated
|
||||
* @param array $input The input for the mutation
|
||||
* @param \WP_Post_Type $post_type_object The Post Type Object for the type of post being mutated
|
||||
* @param string $mutation_name The name of the mutation (ex: create, update, delete)
|
||||
* @param AppContext $context The AppContext passed down to all resolvers
|
||||
* @param ResolveInfo $info The ResolveInfo passed down to all resolvers
|
||||
* @param string $intended_post_status The intended post_status the post should have according to the
|
||||
* mutation input
|
||||
* @param string $default_post_status The default status posts should use if an intended status wasn't set
|
||||
*/
|
||||
public static function update_additional_post_object_data( $post_id, $input, $post_type_object, $mutation_name, AppContext $context, ResolveInfo $info, $default_post_status = null, $intended_post_status = null ) {
|
||||
|
||||
/**
|
||||
* Sets the post lock
|
||||
*
|
||||
* @param int $post_id The ID of the postObject being mutated
|
||||
* @param array $input The input for the mutation
|
||||
* @param \WP_Post_Type $post_type_object The Post Type Object for the type of post being mutated
|
||||
* @param string $mutation_name The name of the mutation (ex: create, update, delete)
|
||||
* @param AppContext $context The AppContext passed down to all resolvers
|
||||
* @param ResolveInfo $info The ResolveInfo passed down to all resolvers
|
||||
* @param string $intended_post_status The intended post_status the post should have according to the mutation input
|
||||
* @param string $default_post_status The default status posts should use if an intended status wasn't set
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
if ( true === apply_filters( 'graphql_post_object_mutation_set_edit_lock', true, $post_id, $input, $post_type_object, $mutation_name, $context, $info, $default_post_status, $intended_post_status ) ) {
|
||||
/**
|
||||
* Set the post_lock for the $new_post_id
|
||||
*/
|
||||
self::set_edit_lock( $post_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the _edit_last field
|
||||
*/
|
||||
update_post_meta( $post_id, '_edit_last', get_current_user_id() );
|
||||
|
||||
/**
|
||||
* Update the postmeta fields
|
||||
*/
|
||||
if ( ! empty( $input['desiredSlug'] ) ) {
|
||||
update_post_meta( $post_id, '_wp_desired_post_slug', $input['desiredSlug'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the object terms
|
||||
*
|
||||
* @param int $post_id The ID of the postObject being mutated
|
||||
* @param array $input The input for the mutation
|
||||
* @param \WP_Post_Type $post_type_object The Post Type Object for the type of post being mutated
|
||||
* @param string $mutation_name The name of the mutation (ex: create, update, delete)
|
||||
*/
|
||||
self::set_object_terms( $post_id, $input, $post_type_object, $mutation_name );
|
||||
|
||||
/**
|
||||
* Run an action after the additional data has been updated. This is a great spot to hook into to
|
||||
* update additional data related to postObjects, such as setting relationships, updating additional postmeta,
|
||||
* or sending emails to Kevin. . .whatever you need to do with the postObject.
|
||||
*
|
||||
* @param int $post_id The ID of the postObject being mutated
|
||||
* @param array $input The input for the mutation
|
||||
* @param \WP_Post_Type $post_type_object The Post Type Object for the type of post being mutated
|
||||
* @param string $mutation_name The name of the mutation (ex: create, update, delete)
|
||||
* @param AppContext $context The AppContext passed down to all resolvers
|
||||
* @param ResolveInfo $info The ResolveInfo passed down to all resolvers
|
||||
* @param string $intended_post_status The intended post_status the post should have according to the mutation input
|
||||
* @param string $default_post_status The default status posts should use if an intended status wasn't set
|
||||
*/
|
||||
do_action( 'graphql_post_object_mutation_update_additional_data', $post_id, $input, $post_type_object, $mutation_name, $context, $info, $default_post_status, $intended_post_status );
|
||||
|
||||
/**
|
||||
* Sets the post lock
|
||||
*
|
||||
* @param int $post_id The ID of the postObject being mutated
|
||||
* @param array $input The input for the mutation
|
||||
* @param \WP_Post_Type $post_type_object The Post Type Object for the type of post being mutated
|
||||
* @param string $mutation_name The name of the mutation (ex: create, update, delete)
|
||||
* @param AppContext $context The AppContext passed down to all resolvers
|
||||
* @param ResolveInfo $info The ResolveInfo passed down to all resolvers
|
||||
* @param string $intended_post_status The intended post_status the post should have according to the mutation input
|
||||
* @param string $default_post_status The default status posts should use if an intended status wasn't set
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
if ( true === apply_filters( 'graphql_post_object_mutation_set_edit_lock', true, $post_id, $input, $post_type_object, $mutation_name, $context, $info, $default_post_status, $intended_post_status ) ) {
|
||||
/**
|
||||
* Set the post_lock for the $new_post_id
|
||||
*/
|
||||
self::remove_edit_lock( $post_id );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a $post_id and $input from the mutation, check to see if any term associations are being made, and
|
||||
* properly set the relationships
|
||||
*
|
||||
* @param int $post_id The ID of the postObject being mutated
|
||||
* @param array $input The input for the mutation
|
||||
* @param \WP_Post_Type $post_type_object The Post Type Object for the type of post being mutated
|
||||
* @param string $mutation_name The name of the mutation (ex: create, update, delete)
|
||||
*/
|
||||
protected static function set_object_terms( $post_id, $input, $post_type_object, $mutation_name ) {
|
||||
|
||||
/**
|
||||
* Fire an action before setting object terms during a GraphQL Post Object Mutation.
|
||||
*
|
||||
* One example use for this hook would be to create terms from the input that may not exist yet, so that they can be set as a relation below.
|
||||
*
|
||||
* @param int $post_id The ID of the postObject being mutated
|
||||
* @param array $input The input for the mutation
|
||||
* @param \WP_Post_Type $post_type_object The Post Type Object for the type of post being mutated
|
||||
* @param string $mutation_name The name of the mutation (ex: create, update, delete)
|
||||
*/
|
||||
do_action( 'graphql_post_object_mutation_set_object_terms', $post_id, $input, $post_type_object, $mutation_name );
|
||||
|
||||
/**
|
||||
* Get the allowed taxonomies and iterate through them to find the term inputs to use for setting relationships
|
||||
*/
|
||||
$allowed_taxonomies = \WPGraphQL::$allowed_taxonomies;
|
||||
|
||||
if ( ! empty( $allowed_taxonomies ) && is_array( $allowed_taxonomies ) ) {
|
||||
|
||||
foreach ( $allowed_taxonomies as $taxonomy ) {
|
||||
|
||||
/**
|
||||
* If the taxonomy is in the array of taxonomies registered to the post_type
|
||||
*/
|
||||
if ( in_array( $taxonomy, get_object_taxonomies( $post_type_object->name ), true ) ) {
|
||||
|
||||
/**
|
||||
* Get the tax object
|
||||
*/
|
||||
$tax_object = get_taxonomy( $taxonomy );
|
||||
|
||||
/**
|
||||
* If there is input for the taxonomy, process it
|
||||
*/
|
||||
if ( ! empty( $tax_object->graphql_plural_name ) && ! empty( $input[ $tax_object->graphql_plural_name ] ) ) {
|
||||
|
||||
$term_input = $input[ $tax_object->graphql_plural_name ];
|
||||
|
||||
/**
|
||||
* Default append to true, but allow input to set it to false.
|
||||
*/
|
||||
$append = isset( $term_input['append'] ) && false === $term_input['append'] ? false : true;
|
||||
|
||||
/**
|
||||
* Start an array of terms to connect
|
||||
*/
|
||||
$terms_to_connect = [];
|
||||
|
||||
/**
|
||||
* Filter whether to allow terms to be created during a post mutation.
|
||||
*
|
||||
* If a post mutation includes term input for a term that does not already exist,
|
||||
* this will allow terms to be created in order to connect the term to the post object,
|
||||
* but if filtered to false, this will prevent the term that doesn't already exist
|
||||
* from being created during the mutation of the post.
|
||||
*
|
||||
* @param bool $allow_term_creation Whether new terms should be created during the post object mutation
|
||||
* @param \WP_Taxonomy $tax_object The Taxonomy object for the term being added to the Post Object
|
||||
*/
|
||||
$allow_term_creation = apply_filters( 'graphql_post_object_mutations_allow_term_creation', true, $tax_object );
|
||||
|
||||
/**
|
||||
* If there are nodes in the term_input
|
||||
*/
|
||||
if ( ! empty( $term_input['nodes'] ) && is_array( $term_input['nodes'] ) ) {
|
||||
|
||||
foreach ( $term_input['nodes'] as $node ) {
|
||||
|
||||
$term_exists = false;
|
||||
|
||||
/**
|
||||
* Handle the input for ID first.
|
||||
*/
|
||||
if ( ! empty( $node['id'] ) ) {
|
||||
|
||||
if ( ! absint( $node['id'] ) ) {
|
||||
|
||||
$id_parts = Relay::fromGlobalId( $node['id'] );
|
||||
|
||||
if ( $id_parts['type'] !== $tax_object->name ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! empty( $id_parts['id'] ) ) {
|
||||
$term_exists = get_term_by( 'id', absint( $id_parts['id'] ), $tax_object->name );
|
||||
if ( $term_exists ) {
|
||||
$terms_to_connect[] = $term_exists->term_id;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$term_exists = get_term_by( 'id', absint( $node['id'] ), $tax_object->name );
|
||||
if ( $term_exists ) {
|
||||
$terms_to_connect[] = $term_exists->term_id;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Next, handle the input for slug if there wasn't an ID input
|
||||
*/
|
||||
} elseif ( ! empty( $node['slug'] ) ) {
|
||||
$sanitized_slug = sanitize_text_field( $node['slug'] );
|
||||
$term_exists = get_term_by( 'slug', $sanitized_slug, $tax_object->name );
|
||||
if ( $term_exists ) {
|
||||
$terms_to_connect[] = $term_exists->term_id;
|
||||
}
|
||||
/**
|
||||
* If the input for the term isn't an existing term, check to make sure
|
||||
* we're allowed to create new terms during a Post Object mutation
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* If no term exists so far, and terms are set to be allowed to be created
|
||||
* during a post object mutation, create the term to connect based on the
|
||||
* input
|
||||
*/
|
||||
if ( ! $term_exists && true === $allow_term_creation ) {
|
||||
|
||||
/**
|
||||
* If the current user cannot edit terms, don't create terms to connect
|
||||
*/
|
||||
if ( ! current_user_can( $tax_object->cap->edit_terms ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$created_term = self::create_term_to_connect( $node, $tax_object->name );
|
||||
|
||||
if ( ! empty( $created_term ) ) {
|
||||
$terms_to_connect[] = $created_term;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If there are terms to connect, set the connection
|
||||
*/
|
||||
if ( ! empty( $terms_to_connect ) && is_array( $terms_to_connect ) ) {
|
||||
|
||||
/**
|
||||
* If the current user cannot edit terms, don't create terms to connect
|
||||
*/
|
||||
if ( ! current_user_can( $tax_object->cap->assign_terms ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_set_object_terms( $post_id, $terms_to_connect, $tax_object->name, $append );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an array of Term properties (slug, name, description, etc), create the term and return a term_id
|
||||
*
|
||||
* @param array $node The node input for the term
|
||||
* @param string $taxonomy The taxonomy the term input is for
|
||||
*
|
||||
* @return int $term_id The ID of the created term. 0 if no term was created.
|
||||
*/
|
||||
protected static function create_term_to_connect( $node, $taxonomy ) {
|
||||
|
||||
$created_term = [];
|
||||
$term_to_create = [];
|
||||
$term_args = [];
|
||||
|
||||
if ( ! empty( $node['name'] ) ) {
|
||||
$term_to_create['name'] = sanitize_text_field( $node['name'] );
|
||||
} elseif ( ! empty( $node['slug'] ) ) {
|
||||
$term_to_create['name'] = sanitize_text_field( $node['slug'] );
|
||||
}
|
||||
|
||||
if ( ! empty( $node['slug'] ) ) {
|
||||
$term_args['slug'] = sanitize_text_field( $node['slug'] );
|
||||
}
|
||||
|
||||
if ( ! empty( $node['description'] ) ) {
|
||||
$term_args['description'] = sanitize_text_field( $node['description'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo: consider supporting "parent" input in $term_args
|
||||
*/
|
||||
|
||||
if ( ! empty( $term_to_create['name'] ) ) {
|
||||
$created_term = wp_insert_term( $term_to_create['name'], $taxonomy, $term_args );
|
||||
}
|
||||
|
||||
if ( is_wp_error( $created_term ) && isset( $created_term->error_data['term_exists'] ) ) {
|
||||
return $created_term->error_data['term_exists'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the created term, or 0
|
||||
*/
|
||||
return ! empty( $created_term['term_id'] ) ? $created_term['term_id'] : 0;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a copy of the wp_set_post_lock function that exists in WordPress core, but is not
|
||||
* accessible because that part of WordPress is never loaded for WPGraphQL executions
|
||||
*
|
||||
* Mark the post as currently being edited by the current user
|
||||
*
|
||||
* @param int $post_id ID of the post being edited.
|
||||
*
|
||||
* @return array|false Array of the lock time and user ID. False if the post does not exist, or
|
||||
* there is no current user.
|
||||
*/
|
||||
public static function set_edit_lock( $post_id ) {
|
||||
|
||||
$post = get_post( $post_id );
|
||||
$user_id = get_current_user_id();
|
||||
|
||||
if ( empty( $post ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( 0 === $user_id ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$now = time();
|
||||
$lock = "$now:$user_id";
|
||||
update_post_meta( $post->ID, '_edit_lock', $lock );
|
||||
|
||||
return [ $now, $user_id ];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the edit lock for a post
|
||||
*
|
||||
* @param int $post_id ID of the post to delete the lock for
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function remove_edit_lock( $post_id ) {
|
||||
|
||||
$post = get_post( $post_id );
|
||||
|
||||
if ( ! is_a( $post, 'WP_Post' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return delete_post_meta( $post->ID, '_edit_lock' );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL\Type\PostObject\Mutation;
|
||||
|
||||
use GraphQL\Error\UserError;
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQLRelay\Relay;
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\Type\WPInputObjectType;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class PostObjectUpdate
|
||||
*
|
||||
* @package WPGraphQL\Type\PostObject\Mutation
|
||||
*/
|
||||
class PostObjectUpdate {
|
||||
|
||||
/**
|
||||
* Holds the mutation field definition
|
||||
*
|
||||
* @var array $mutation
|
||||
*/
|
||||
private static $mutation = [];
|
||||
|
||||
/**
|
||||
* Defines the Update mutation for PostTypeObjects
|
||||
*
|
||||
* @param \WP_Post_Type $post_type_object
|
||||
*
|
||||
* @return array|mixed
|
||||
*/
|
||||
public static function mutate( \WP_Post_Type $post_type_object ) {
|
||||
|
||||
if (
|
||||
! empty( $post_type_object->graphql_single_name ) &&
|
||||
empty( self::$mutation[ $post_type_object->graphql_single_name ] )
|
||||
) {
|
||||
|
||||
/**
|
||||
* Set the name of the mutation being performed
|
||||
*/
|
||||
$mutation_name = 'Update' . ucwords( $post_type_object->graphql_single_name );
|
||||
|
||||
self::$mutation[ $post_type_object->graphql_single_name ] = Relay::mutationWithClientMutationId( [
|
||||
'name' => $mutation_name,
|
||||
// translators: The placeholder is the name of the post type being updated
|
||||
'description' => sprintf( __( 'Updates %1$s objects', 'wp-graphql' ), $post_type_object->graphql_single_name ),
|
||||
'inputFields' => WPInputObjectType::prepare_fields( self::input_fields( $post_type_object ), $mutation_name ),
|
||||
'outputFields' => [
|
||||
$post_type_object->graphql_single_name => [
|
||||
'type' => Types::post_object( $post_type_object->name ),
|
||||
'resolve' => function( $payload ) {
|
||||
return get_post( $payload['postObjectId'] );
|
||||
},
|
||||
],
|
||||
],
|
||||
'mutateAndGetPayload' => function( $input, AppContext $context, ResolveInfo $info ) use ( $post_type_object, $mutation_name ) {
|
||||
|
||||
$id_parts = ! empty( $input['id'] ) ? Relay::fromGlobalId( $input['id'] ) : null;
|
||||
$existing_post = get_post( absint( $id_parts['id'] ) );
|
||||
|
||||
/**
|
||||
* If there's no existing post, throw an exception
|
||||
*/
|
||||
if ( empty( $id_parts['id'] ) || false === $existing_post || $id_parts['type'] !== $post_type_object->name ) {
|
||||
// translators: the placeholder is the name of the type of post being updated
|
||||
throw new UserError( sprintf( __( 'No %1$s could be found to update', 'wp-graphql' ), $post_type_object->graphql_single_name ) );
|
||||
}
|
||||
|
||||
if ( $post_type_object->name !== $existing_post->post_type ) {
|
||||
// translators: The first placeholder is an ID and the second placeholder is the name of the post type being edited
|
||||
throw new UserError( sprintf( __( 'The id %1$d is not of the type "%2$s"', 'wp-graphql' ), $id_parts['id'], $post_type_object->name ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop now if a user isn't allowed to edit posts
|
||||
*/
|
||||
if ( ! current_user_can( $post_type_object->cap->edit_posts ) ) {
|
||||
// translators: the $post_type_object->graphql_single_name placeholder is the name of the object being mutated
|
||||
throw new UserError( sprintf( __( 'Sorry, you are not allowed to update a %1$s', 'wp-graphql' ), $post_type_object->graphql_single_name ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* If the mutation is setting the author to be someone other than the user making the request
|
||||
* make sure they have permission to edit others posts
|
||||
*/
|
||||
$author_id_parts = ! empty( $input['authorId'] ) ? Relay::fromGlobalId( $input['authorId'] ) : null;
|
||||
if ( ! empty( $author_id_parts['id'] ) && get_current_user_id() !== $author_id_parts['id'] && ! current_user_can( $post_type_object->cap->edit_others_posts ) ) {
|
||||
// translators: the $post_type_object->graphql_single_name placeholder is the name of the object being mutated
|
||||
throw new UserError( sprintf( __( 'Sorry, you are not allowed to update %1$s as this user.', 'wp-graphql' ), $post_type_object->graphql_plural_name ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo: when we add support for making posts sticky, we should check permissions to make sure users can make posts sticky
|
||||
* @see : https://github.com/WordPress/WordPress/blob/e357195ce303017d517aff944644a7a1232926f7/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php#L640-L642
|
||||
*/
|
||||
|
||||
/**
|
||||
* @todo: when we add support for assigning terms to posts, we should check permissions to make sure they can assign terms
|
||||
* @see : https://github.com/WordPress/WordPress/blob/e357195ce303017d517aff944644a7a1232926f7/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php#L644-L646
|
||||
*/
|
||||
|
||||
/**
|
||||
* insert the post object and get the ID
|
||||
*/
|
||||
$post_args = PostObjectMutation::prepare_post_object( $input, $post_type_object, $mutation_name );
|
||||
$post_args['ID'] = absint( $id_parts['id'] );
|
||||
|
||||
/**
|
||||
* Insert the post and retrieve the ID
|
||||
*/
|
||||
$post_id = wp_update_post( wp_slash( (array) $post_args ), true );
|
||||
|
||||
/**
|
||||
* Throw an exception if the post failed to update
|
||||
*/
|
||||
if ( is_wp_error( $post_id ) ) {
|
||||
throw new UserError( __( 'The object failed to update but no error was provided', 'wp-graphql' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires after a single term is created or updated via a GraphQL mutation
|
||||
*
|
||||
* The dynamic portion of the hook name, `$taxonomy->name` refers to the taxonomy of the term being mutated
|
||||
*
|
||||
* @param int $post_id Inserted post ID
|
||||
* @param array $args The args used to insert the term
|
||||
* @param string $mutation_name The name of the mutation being performed
|
||||
*/
|
||||
do_action( "graphql_insert_{$post_type_object->name}", $post_id, $post_args, $mutation_name );
|
||||
|
||||
/**
|
||||
* This updates additional data not part of the posts table (postmeta, terms, other relations, etc)
|
||||
*
|
||||
* The input for the postObjectMutation will be passed, along with the $new_post_id for the
|
||||
* postObject that was updated so that relations can be set, meta can be updated, etc.
|
||||
*/
|
||||
PostObjectMutation::update_additional_post_object_data( $post_id, $input, $post_type_object, $mutation_name, $context, $info );
|
||||
|
||||
/**
|
||||
* Return the payload
|
||||
*/
|
||||
return [
|
||||
'postObjectId' => $post_id,
|
||||
];
|
||||
|
||||
},
|
||||
] );
|
||||
|
||||
}
|
||||
|
||||
return ! empty( self::$mutation[ $post_type_object->graphql_single_name ] ) ? self::$mutation[ $post_type_object->graphql_single_name ] : null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the id as a nonNull field for update mutations
|
||||
*
|
||||
* @param \WP_Post_Type $post_type_object
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private static function input_fields( $post_type_object ) {
|
||||
|
||||
/**
|
||||
* Update mutations require an ID to be passed
|
||||
*/
|
||||
return array_merge(
|
||||
[
|
||||
'id' => [
|
||||
'type' => Types::non_null( Types::id() ),
|
||||
// translators: the placeholder is the name of the type of post object being updated
|
||||
'description' => sprintf( __( 'The ID of the %1$s object', 'wp-graphql' ), $post_type_object->graphql_single_name ),
|
||||
],
|
||||
],
|
||||
PostObjectMutation::input_fields( $post_type_object )
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
159
wordpress/wp-content/plugins/wp-graphql/src/Type/PostObject/PostObjectQuery.php
Executable file
159
wordpress/wp-content/plugins/wp-graphql/src/Type/PostObject/PostObjectQuery.php
Executable file
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
namespace WPGraphQL\Type\PostObject;
|
||||
|
||||
use GraphQL\Error\UserError;
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQLRelay\Relay;
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\Data\DataSource;
|
||||
use WPGraphQL\Type\WPInputObjectType;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class PostObjectQuery
|
||||
* @package WPGraphQL\Type\PostObject
|
||||
* @Since 0.0.5
|
||||
*/
|
||||
class PostObjectQuery {
|
||||
|
||||
/**
|
||||
* Holds the root_query field definition
|
||||
* @var array $root_query
|
||||
* @since 0.0.5
|
||||
*/
|
||||
private static $root_query = [];
|
||||
|
||||
/**
|
||||
* Holds the definition of the $post_object_by field
|
||||
* @var array $post_object_by
|
||||
*/
|
||||
private static $post_object_by = [];
|
||||
|
||||
/**
|
||||
* Holds the definition for the args that can be input on the $post_object_by field
|
||||
* @var array $post_object_by_args
|
||||
*/
|
||||
private static $post_object_by_args = [];
|
||||
|
||||
/**
|
||||
* Method that returns the root query field definition for the post object type
|
||||
*
|
||||
* @param object $post_type_object
|
||||
* @return array
|
||||
* @since 0.0.5
|
||||
*/
|
||||
public static function root_query( $post_type_object ) {
|
||||
|
||||
if ( ! empty( $post_type_object->name ) && empty( self::$root_query[ $post_type_object->name ] ) ) {
|
||||
self::$root_query[ $post_type_object->name ] = [
|
||||
'type' => Types::post_object( $post_type_object->name ),
|
||||
'description' => sprintf( __( 'A % object', 'wp-graphql' ), $post_type_object->graphql_single_name ),
|
||||
'args' => [
|
||||
'id' => Types::non_null( Types::id() ),
|
||||
],
|
||||
'resolve' => function( $source, array $args, AppContext $context, ResolveInfo $info ) use ( $post_type_object ) {
|
||||
$id_components = Relay::fromGlobalId( $args['id'] );
|
||||
|
||||
return DataSource::resolve_post_object( $id_components['id'], $post_type_object->name );
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return ! empty( self::$root_query[ $post_type_object->name ] ) ? self::$root_query[ $post_type_object->name ] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that returns the "post_object_by" field definition to get a post object by id, postId or slug.
|
||||
*
|
||||
* @param \WP_Post_Type $post_type_object
|
||||
* @return array
|
||||
*/
|
||||
public static function post_object_by( \WP_Post_Type $post_type_object ) {
|
||||
|
||||
if ( ! empty( $post_type_object->name ) && empty( self::$post_object_by[ $post_type_object->name ] ) ) {
|
||||
self::$post_object_by[ $post_type_object->name ] = [
|
||||
'type' => Types::post_object( $post_type_object->name ),
|
||||
'description' => sprintf( __( 'A %s object', 'wp-graphql' ), $post_type_object->graphql_single_name ),
|
||||
'args' => self::post_object_by_args( $post_type_object ),
|
||||
'resolve' => function( $source, array $args, AppContext $context, ResolveInfo $info ) use ( $post_type_object ) {
|
||||
|
||||
$post_object = null;
|
||||
|
||||
if ( ! empty( $args['id'] ) ) {
|
||||
$id_components = Relay::fromGlobalId( $args['id'] );
|
||||
if ( empty( $id_components['id'] ) || empty( $id_components['type'] ) ) {
|
||||
throw new UserError( __( 'The "id" is invalid', 'wp-graphql' ) );
|
||||
}
|
||||
$post_object = DataSource::resolve_post_object( absint( $id_components['id'] ), $post_type_object->name );
|
||||
} elseif ( ! empty( $args[ $post_type_object->graphql_single_name . 'Id' ] ) ) {
|
||||
$id = $args[ $post_type_object->graphql_single_name . 'Id' ];
|
||||
$post_object = DataSource::resolve_post_object( $id, $post_type_object->name );
|
||||
} elseif ( ! empty( $args['uri'] ) ) {
|
||||
$uri = esc_html( $args['uri'] );
|
||||
$post_object = DataSource::get_post_object_by_uri( $uri, 'OBJECT', $post_type_object->name );
|
||||
} elseif ( ! empty( $args['slug'] ) ) {
|
||||
$slug = esc_html( $args['slug'] );
|
||||
$post_object = DataSource::get_post_object_by_uri( $slug, 'OBJECT', $post_type_object->name );
|
||||
}
|
||||
|
||||
if ( empty( $post_object ) || is_wp_error( $post_object ) ) {
|
||||
throw new UserError( __( 'No resource could be found', 'wp-graphql' ) );
|
||||
}
|
||||
|
||||
if ( ! $post_object instanceof \WP_Post ) {
|
||||
throw new UserError( __( 'The queried resource is not valid', 'wp-graphql' ) );
|
||||
}
|
||||
|
||||
if ( $post_type_object->name !== $post_object->post_type ) {
|
||||
throw new UserError( __( 'The queried resource is not the correct type', 'wp-graphql' ) );
|
||||
}
|
||||
|
||||
return $post_object;
|
||||
|
||||
},
|
||||
];
|
||||
}
|
||||
return ! empty( self::$post_object_by[ $post_type_object->name ] ) ? self::$post_object_by[ $post_type_object->name ] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the args to be used by the $postObject.By field
|
||||
* @param \WP_Post_Type $post_type_object
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function post_object_by_args( \WP_Post_Type $post_type_object ) {
|
||||
|
||||
if ( empty( self::$post_object_by_args[ ucfirst( $post_type_object->name ) . 'ByArgs' ] ) ) {
|
||||
|
||||
$args = [
|
||||
'id' => [
|
||||
'type' => Types::string(),
|
||||
'description' => sprintf( __( 'Get the object by it\'s global ID', 'wp-graphql' ), $post_type_object->graphql_single_name ),
|
||||
],
|
||||
$post_type_object->graphql_single_name . 'Id' => [
|
||||
'type' => Types::int(),
|
||||
'description' => sprintf( __( 'Get the %s by it\'s database ID', 'wp-graphql' ), $post_type_object->graphql_single_name ),
|
||||
],
|
||||
'uri' => [
|
||||
'type' => Types::string(),
|
||||
'description' => sprintf( __( 'Get the %s by it\'s uri', 'wp-graphql' ), $post_type_object->graphql_single_name ),
|
||||
]
|
||||
];
|
||||
|
||||
if ( false === $post_type_object->hierarchical ) {
|
||||
$args['slug'] = [
|
||||
'type' => Types::string(),
|
||||
'description' => sprintf( __( 'Get the %s by it\'s slug (only available for non-hierarchical types)', 'wp-graphql' ), $post_type_object->graphql_single_name ),
|
||||
];
|
||||
}
|
||||
|
||||
self::$post_object_by_args[ $post_type_object->name . 'ByArgs' ] = WPInputObjectType::prepare_fields( $args, ucfirst( $post_type_object->name . 'ByArgs' ) );
|
||||
|
||||
}
|
||||
|
||||
return self::$post_object_by_args[ $post_type_object->name . 'ByArgs' ];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
576
wordpress/wp-content/plugins/wp-graphql/src/Type/PostObject/PostObjectType.php
Executable file
576
wordpress/wp-content/plugins/wp-graphql/src/Type/PostObject/PostObjectType.php
Executable file
@@ -0,0 +1,576 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL\Type\PostObject;
|
||||
|
||||
use GraphQL\Deferred;
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQLRelay\Relay;
|
||||
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\Data\DataSource;
|
||||
use WPGraphQL\Data\Loader;
|
||||
use WPGraphQL\Type\Comment\Connection\CommentConnectionDefinition;
|
||||
use WPGraphQL\Type\PostObject\Connection\PostObjectConnectionDefinition;
|
||||
use WPGraphQL\Type\TermObject\Connection\TermObjectConnectionDefinition;
|
||||
use WPGraphQL\Type\WPObjectType;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class PostObjectType
|
||||
*
|
||||
* This sets up the base PostObjectType. Custom Post Types that are set to "show_in_graphql" automatically
|
||||
* use the PostObjectType and inherit the fields that are defined here. The fields get passed through a
|
||||
* filter unique to each type, so each post_type can modify it's type schema via field filters.
|
||||
*
|
||||
* NOTE: In some cases the shape of a Custom Post Type's schema is so drastically different from the standard
|
||||
* PostObjectType shape it might make more sense for the custom post type to register a different type
|
||||
* altogether instead of utilizing the PostObjectType.
|
||||
*
|
||||
* @package WPGraphQL\Type
|
||||
* @since 0.0.5
|
||||
*/
|
||||
class PostObjectType extends WPObjectType {
|
||||
|
||||
/**
|
||||
* Holds the $fields definition for the PostObjectType
|
||||
*
|
||||
* @var $fields
|
||||
*/
|
||||
private static $fields = [];
|
||||
|
||||
/**
|
||||
* Holds the post_type_object
|
||||
*
|
||||
* @var object $post_type_object
|
||||
*/
|
||||
private static $post_type_object;
|
||||
|
||||
/**
|
||||
* PostObjectType constructor.
|
||||
*
|
||||
* @param string $post_type The post_type name
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
public function __construct( $post_type ) {
|
||||
|
||||
/**
|
||||
* Get the post_type_object from the post_type and store it
|
||||
* for later use
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
self::$post_type_object = get_post_type_object( $post_type );
|
||||
|
||||
$config = [
|
||||
'name' => ucfirst( self::$post_type_object->graphql_single_name ),
|
||||
// translators: the placeholder is the post_type of the object
|
||||
'description' => sprintf( __( 'The %s object type', 'wp-graphql' ), self::$post_type_object->graphql_single_name ),
|
||||
'fields' => self::fields( self::$post_type_object ),
|
||||
'interfaces' => [ self::node_interface() ],
|
||||
];
|
||||
|
||||
parent::__construct( $config );
|
||||
}
|
||||
|
||||
/**
|
||||
* fields
|
||||
* This defines the fields for PostObjectTypes
|
||||
*
|
||||
* @param $post_type_object
|
||||
*
|
||||
* @return \GraphQL\Type\Definition\FieldDefinition|mixed|null
|
||||
* @since 0.0.5
|
||||
*/
|
||||
private static function fields( $post_type_object ) {
|
||||
|
||||
/**
|
||||
* Get the $single_name out of the post_type_object
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
$single_name = self::$post_type_object->graphql_single_name;
|
||||
|
||||
|
||||
/**
|
||||
* If the $fields haven't already been defined for this type,
|
||||
* define the fields
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
if ( empty( self::$fields[ $single_name ] ) ) {
|
||||
|
||||
/**
|
||||
* Get the taxonomies that are allowed in WPGraphQL
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
$allowed_taxonomies = \WPGraphQL::$allowed_taxonomies;
|
||||
|
||||
/**
|
||||
* Define the fields for the post_type
|
||||
*
|
||||
* @return mixed
|
||||
* @since 0.0.5
|
||||
*/
|
||||
self::$fields[ $single_name ] = function() use ( $single_name, $post_type_object, $allowed_taxonomies ) {
|
||||
$fields = [
|
||||
'id' => [
|
||||
'type' => Types::non_null( Types::id() ),
|
||||
'description' => __( 'The globally unique ID for the object', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post $post, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ( ! empty( $post->post_type ) && ! empty( $post->ID ) ) ? Relay::toGlobalId( $post->post_type, $post->ID ) : null;
|
||||
},
|
||||
],
|
||||
$single_name . 'Id' => [
|
||||
'type' => Types::non_null( Types::int() ),
|
||||
'description' => __( 'The id field matches the WP_Post->ID field.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post $post, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return absint( $post->ID );
|
||||
},
|
||||
],
|
||||
'ancestors' => [
|
||||
'type' => Types::list_of( Types::post_object_union() ),
|
||||
'description' => esc_html__( 'Ancestors of the object', 'wp-graphql' ),
|
||||
'args' => [
|
||||
'types' => [
|
||||
'type' => Types::list_of( Types::post_type_enum() ),
|
||||
'description' => __( 'The types of ancestors to check for. Defaults to the same type as the current object', 'wp-graphql' ),
|
||||
],
|
||||
],
|
||||
'resolve' => function( \WP_Post $post, $args, AppContext $context, ResolveInfo $info ) {
|
||||
$ancestors = [];
|
||||
$types = ! empty( $args['types'] ) ? $args['types'] : [ $post->post_type ];
|
||||
$ancestor_ids = get_ancestors( $post->ID, $post->post_type );
|
||||
if ( ! empty( $ancestor_ids ) ) {
|
||||
foreach ( $ancestor_ids as $ancestor_id ) {
|
||||
$ancestor_obj = get_post( $ancestor_id );
|
||||
if ( in_array( $ancestor_obj->post_type, $types, true ) ) {
|
||||
$ancestors[] = $ancestor_obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ! empty( $ancestors ) ? $ancestors : null;
|
||||
},
|
||||
],
|
||||
'author' => [
|
||||
'type' => Types::user(),
|
||||
'description' => __( "The author field will return a queryable User type matching the post's author.", 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post $post, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return DataSource::resolve_user( $post->post_author );
|
||||
},
|
||||
],
|
||||
'date' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Post publishing date.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post $post, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $post->post_date ) ? $post->post_date : null;
|
||||
},
|
||||
],
|
||||
'dateGmt' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The publishing date set in GMT.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post $post, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $post->post_date_gmt ) ? Types::prepare_date_response( $post->post_date_gmt ) : null;
|
||||
},
|
||||
],
|
||||
'content' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The content of the post.', 'wp-graphql' ),
|
||||
'args' => [
|
||||
'format' => self::post_object_format_arg(),
|
||||
],
|
||||
'resolve' => function( \WP_Post $post, $args, AppContext $context, ResolveInfo $info ) {
|
||||
|
||||
$content = ! empty( $post->post_content ) ? $post->post_content : null;
|
||||
|
||||
// If the raw format is requested, don't apply any filters.
|
||||
if ( isset( $args['format'] ) && 'raw' === $args['format'] ) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
return apply_filters( 'the_content', $content );
|
||||
},
|
||||
],
|
||||
'title' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The title of the post. This is currently just the raw title. An amendment to support rendered title needs to be made.', 'wp-graphql' ),
|
||||
'args' => [
|
||||
'format' => self::post_object_format_arg(),
|
||||
],
|
||||
'resolve' => function( \WP_Post $post, $args, AppContext $context, ResolveInfo $info ) {
|
||||
|
||||
$title = ! empty( $post->post_title ) ? $post->post_title : null;
|
||||
|
||||
// If the raw format is requested, don't apply any filters.
|
||||
if ( isset( $args['format'] ) && 'raw' === $args['format'] ) {
|
||||
return $title;
|
||||
}
|
||||
|
||||
return apply_filters( 'the_title', $title );
|
||||
},
|
||||
],
|
||||
'excerpt' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The excerpt of the post. This is currently just the raw excerpt. An amendment to support rendered excerpts needs to be made.', 'wp-graphql' ),
|
||||
'args' => [
|
||||
'format' => self::post_object_format_arg(),
|
||||
],
|
||||
'resolve' => function( \WP_Post $post, $args, AppContext $context, ResolveInfo $info ) {
|
||||
|
||||
$excerpt = ! empty( $post->post_excerpt ) ? $post->post_excerpt : null;
|
||||
|
||||
// If the raw format is requested, don't apply any filters.
|
||||
if ( isset( $args['format'] ) && 'raw' === $args['format'] ) {
|
||||
return $excerpt;
|
||||
}
|
||||
|
||||
$excerpt = apply_filters( 'get_the_excerpt', $excerpt, $post );
|
||||
|
||||
return apply_filters( 'the_excerpt', $excerpt );
|
||||
},
|
||||
],
|
||||
'status' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The current status of the object', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post $post, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $post->post_status ) ? $post->post_status : null;
|
||||
},
|
||||
],
|
||||
'commentStatus' => array(
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Whether the comments are open or closed for this particular post.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post $post, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $post->comment_status ) ? $post->comment_status : null;
|
||||
},
|
||||
),
|
||||
'pingStatus' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Whether the pings are open or closed for this particular post.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post $post, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $post->ping_status ) ? $post->ping_status : null;
|
||||
},
|
||||
],
|
||||
'slug' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The uri slug for the post. This is equivalent to the WP_Post->post_name field and the post_name column in the database for the `post_objects` table.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post $post, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $post->post_name ) ? $post->post_name : null;
|
||||
},
|
||||
],
|
||||
'toPing' => [
|
||||
'type' => Types::list_of( Types::string() ),
|
||||
'description' => __( 'URLs queued to be pinged.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post $post, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $post->to_ping ) ? implode( ',', $post->to_ping ) : null;
|
||||
},
|
||||
],
|
||||
'pinged' => [
|
||||
'type' => Types::list_of( Types::string() ),
|
||||
'description' => __( 'URLs that have been pinged.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post $post, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $post->pinged ) ? implode( ',', $post->pinged ) : null;
|
||||
},
|
||||
],
|
||||
'modified' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The local modified time for a post. If a post was recently updated the modified field will change to match the corresponding time.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post $post, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $post->post_modified ) ? $post->post_modified : null;
|
||||
},
|
||||
],
|
||||
'modifiedGmt' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The GMT modified time for a post. If a post was recently updated the modified field will change to match the corresponding time in GMT.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post $post, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $post->post_modified_gmt ) ? Types::prepare_date_response( $post->post_modified_gmt ) : null;
|
||||
},
|
||||
],
|
||||
'parent' => [
|
||||
'type' => Types::post_object_union(),
|
||||
'description' => __( 'The parent of the object. The parent object can be of various types', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post $post, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $post->post_parent ) ? get_post( $post->post_parent ) : null;
|
||||
},
|
||||
],
|
||||
'editLast' => [
|
||||
'type' => Types::user(),
|
||||
'description' => __( 'The user that most recently edited the object', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post $post, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
$edit_last = get_post_meta( $post->ID, '_edit_last', true );
|
||||
|
||||
return ! empty( $edit_last ) ? DataSource::resolve_user( absint( $edit_last ) ) : null;
|
||||
},
|
||||
],
|
||||
'editLock' => [
|
||||
'type' => Types::edit_lock(),
|
||||
'description' => __( 'If a user has edited the object within the past 15 seconds, this will return the user and the time they last edited. Null if the edit lock doesn\'t exist or is greater than 15 seconds', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post $post, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
$edit_lock = get_post_meta( $post->ID, '_edit_lock', true );
|
||||
$edit_lock_parts = explode( ':', $edit_lock );
|
||||
|
||||
return ! empty( $edit_lock_parts ) ? $edit_lock_parts : null;
|
||||
},
|
||||
],
|
||||
'enclosure' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The RSS enclosure for the object', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post $post, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
$enclosure = get_post_meta( $post->ID, 'enclosure', true );
|
||||
|
||||
return ! empty( $enclosure ) ? $enclosure : null;
|
||||
},
|
||||
],
|
||||
'guid' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The global unique identifier for this post. This currently matches the value stored in WP_Post->guid and the guid column in the `post_objects` database table.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post $post, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $post->guid ) ? $post->guid : null;
|
||||
},
|
||||
],
|
||||
'menuOrder' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( 'A field used for ordering posts. This is typically used with nav menu items or for special ordering of hierarchical content types.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post $post, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $post->menu_order ) ? absint( $post->menu_order ) : null;
|
||||
},
|
||||
],
|
||||
'desiredSlug' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The desired slug of the post', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post $post, $args, AppContext $context, ResolveInfo $info ) {
|
||||
$desired_slug = get_post_meta( $post->ID, '_wp_desired_post_slug', true );
|
||||
|
||||
return ! empty( $desired_slug ) ? $desired_slug : null;
|
||||
},
|
||||
],
|
||||
'link' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The permalink of the post', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post $post, $args, AppContext $context, ResolveInfo $info ) {
|
||||
$link = get_permalink( $post->ID );
|
||||
|
||||
return ! empty( $link ) ? $link : null;
|
||||
},
|
||||
],
|
||||
'uri' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'URI path for the resource', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post $post, $args, AppContext $context, ResolveInfo $info ) {
|
||||
$uri = get_page_uri( $post->ID );
|
||||
|
||||
return ! empty( $uri ) ? $uri : null;
|
||||
},
|
||||
],
|
||||
'terms' => [
|
||||
'type' => Types::list_of( Types::term_object_union() ),
|
||||
'args' => [
|
||||
'taxonomies' => [
|
||||
'type' => Types::list_of( Types::taxonomy_enum() ),
|
||||
'description' => __( 'Select which taxonomies to limit the results to', 'wp-graphql' ),
|
||||
],
|
||||
],
|
||||
// Translators: placeholder is the name of the post_type
|
||||
'description' => sprintf( __( 'Terms connected to the %1$s', 'wp-graphql' ), $single_name ),
|
||||
'resolve' => function( \WP_Post $post, $args, AppContext $context, ResolveInfo $info ) use ( $allowed_taxonomies ) {
|
||||
|
||||
/**
|
||||
* If the $arg for taxonomies is populated, use it as the $allowed_taxonomies
|
||||
* otherwise use the default $allowed_taxonomies passed down
|
||||
*/
|
||||
$taxonomies = [];
|
||||
if ( ! empty( $args['taxonomies'] ) && is_array( $args['taxonomies'] ) ) {
|
||||
$taxonomies = $args['taxonomies'];
|
||||
} else {
|
||||
$connected_taxonomies = get_object_taxonomies( $post, 'names' );
|
||||
foreach( $connected_taxonomies as $taxonomy ) {
|
||||
if ( in_array( $taxonomy, \WPGraphQL::$allowed_taxonomies ) ) {
|
||||
$taxonomies[] = $taxonomy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$tax_terms = [];
|
||||
if ( ! empty( $taxonomies ) ) {
|
||||
|
||||
$term_query = new \WP_Term_Query( [
|
||||
'taxonomy' => $taxonomies,
|
||||
'object_ids' => $post->ID,
|
||||
] );
|
||||
|
||||
$tax_terms = $term_query->get_terms();
|
||||
|
||||
}
|
||||
|
||||
return ! empty( $tax_terms ) && is_array( $tax_terms ) ? $tax_terms : null;
|
||||
},
|
||||
],
|
||||
'termNames' => [
|
||||
'type' => Types::list_of( Types::string() ),
|
||||
'args' => [
|
||||
'taxonomies' => [
|
||||
'type' => Types::list_of( Types::taxonomy_enum() ),
|
||||
'description' => __( 'Select which taxonomies to limit the results to', 'wp-graphql' ),
|
||||
],
|
||||
],
|
||||
// Translators: placeholder is the name of the post_type
|
||||
'description' => sprintf( __( 'Terms connected to the %1$s', 'wp-graphql' ), $single_name ),
|
||||
'resolve' => function( \WP_Post $post, $args, AppContext $context, ResolveInfo $info ) use ( $allowed_taxonomies ) {
|
||||
|
||||
/**
|
||||
* If the $arg for taxonomies is populated, use it as the $allowed_taxonomies
|
||||
* otherwise use the default $allowed_taxonomies passed down
|
||||
*/
|
||||
$taxonomies = [];
|
||||
if ( ! empty( $args['taxonomies'] ) && is_array( $args['taxonomies'] ) ) {
|
||||
$taxonomies = $args['taxonomies'];
|
||||
} else {
|
||||
$connected_taxonomies = get_object_taxonomies( $post, 'names' );
|
||||
foreach( $connected_taxonomies as $taxonomy ) {
|
||||
if ( in_array( $taxonomy, \WPGraphQL::$allowed_taxonomies ) ) {
|
||||
$taxonomies[] = $taxonomy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$tax_terms = [];
|
||||
if ( ! empty( $taxonomies ) ) {
|
||||
|
||||
$term_query = new \WP_Term_Query( [
|
||||
'taxonomy' => $taxonomies,
|
||||
'object_ids' => [ $post->ID ],
|
||||
] );
|
||||
|
||||
$tax_terms = $term_query->get_terms();
|
||||
|
||||
}
|
||||
$term_names = ! empty( $tax_terms ) && is_array( $tax_terms ) ? wp_list_pluck( $tax_terms, 'name' ) : [];
|
||||
|
||||
return ! empty( $term_names ) ? $term_names : null;
|
||||
},
|
||||
],
|
||||
'termSlugs' => [
|
||||
'type' => Types::list_of( Types::string() ),
|
||||
'args' => [
|
||||
'taxonomies' => [
|
||||
'type' => Types::list_of( Types::taxonomy_enum() ),
|
||||
'description' => __( 'Select which taxonomies to limit the results to', 'wp-graphql' ),
|
||||
],
|
||||
],
|
||||
// Translators: placeholder is the name of the post_type
|
||||
'description' => sprintf( __( 'Terms connected to the %1$s', 'wp-graphql' ), $single_name ),
|
||||
'resolve' => function( \WP_Post $post, $args, AppContext $context, ResolveInfo $info ) use ( $allowed_taxonomies ) {
|
||||
|
||||
/**
|
||||
* If the $arg for taxonomies is populated, use it as the $allowed_taxonomies
|
||||
* otherwise use the default $allowed_taxonomies passed down
|
||||
*/
|
||||
$taxonomies = [];
|
||||
if ( ! empty( $args['taxonomies'] ) && is_array( $args['taxonomies'] ) ) {
|
||||
$taxonomies = $args['taxonomies'];
|
||||
} else {
|
||||
$connected_taxonomies = get_object_taxonomies( $post, 'names' );
|
||||
foreach( $connected_taxonomies as $taxonomy ) {
|
||||
if ( in_array( $taxonomy, \WPGraphQL::$allowed_taxonomies ) ) {
|
||||
$taxonomies[] = $taxonomy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$tax_terms = [];
|
||||
if ( ! empty( $taxonomies ) ) {
|
||||
|
||||
$term_query = new \WP_Term_Query( [
|
||||
'taxonomy' => $taxonomies,
|
||||
'object_ids' => [ $post->ID ],
|
||||
] );
|
||||
|
||||
$tax_terms = $term_query->get_terms();
|
||||
|
||||
}
|
||||
$term_slugs = ! empty( $tax_terms ) && is_array( $tax_terms ) ? wp_list_pluck( $tax_terms, 'slug' ) : [];
|
||||
|
||||
return ! empty( $term_slugs ) ? $term_slugs : null;
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Add comment fields to the schema if the post_type supports "comments"
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
if ( post_type_supports( $post_type_object->name, 'comments' ) ) {
|
||||
$fields['comments'] = CommentConnectionDefinition::connection( $post_type_object->graphql_single_name );
|
||||
$fields['commentCount'] = [
|
||||
'type' => Types::int(),
|
||||
'description' => __( 'The number of comments. Even though WPGraphQL denotes this field as an integer, in WordPress this field should be saved as a numeric string for compatability.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post $post, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $post->comment_count ) ? absint( $post->comment_count ) : null;
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* If the post_type is Hierarchical, there should be a children field
|
||||
*/
|
||||
if ( true === $post_type_object->hierarchical ) {
|
||||
$fields[ 'child' . ucfirst( $post_type_object->graphql_plural_name ) ] = PostObjectConnectionDefinition::connection( $post_type_object, 'Children' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add term connections based on the allowed taxonomies that are also
|
||||
* registered to the post_type
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
if ( ! empty( $allowed_taxonomies ) && is_array( $allowed_taxonomies ) ) {
|
||||
foreach ( $allowed_taxonomies as $taxonomy ) {
|
||||
// If the taxonomy is in the array of taxonomies registered to the post_type
|
||||
if ( in_array( $taxonomy, get_object_taxonomies( $post_type_object->name ), true ) ) {
|
||||
$tax_object = get_taxonomy( $taxonomy );
|
||||
$fields[ $tax_object->graphql_plural_name ] = TermObjectConnectionDefinition::connection( $tax_object, $post_type_object->graphql_single_name );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( post_type_supports( $post_type_object->name, 'thumbnail' ) ) {
|
||||
$fields['featuredImage'] = [
|
||||
'type' => Types::post_object( 'attachment' ),
|
||||
'description' => __( 'The featured image for the object', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post $post, $args, AppContext $context, ResolveInfo $info ) {
|
||||
$thumbnail_id = get_post_thumbnail_id( $post->ID );
|
||||
|
||||
return ! empty( $thumbnail_id ) ? get_post( absint( $thumbnail_id ) ) : null;
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* This prepares the fields by sorting them and applying a filter for adjusting the schema.
|
||||
* Because these fields are implemented via a closure the prepare_fields needs to be applied
|
||||
* to the fields directly instead of being applied to all objects extending
|
||||
* the WPObjectType class.
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
return self::prepare_fields( $fields, $single_name );
|
||||
};
|
||||
}
|
||||
|
||||
return ! empty( self::$fields[ $single_name ] ) ? self::$fields[ $single_name ] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the args to be used by post object fields.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function post_object_format_arg() {
|
||||
return [
|
||||
'type' => Types::post_object_field_format_enum(),
|
||||
'description' => __( 'Format of the field output', 'wp-graphql' ),
|
||||
];
|
||||
}
|
||||
}
|
||||
497
wordpress/wp-content/plugins/wp-graphql/src/Type/PostType/PostTypeType.php
Executable file
497
wordpress/wp-content/plugins/wp-graphql/src/Type/PostType/PostTypeType.php
Executable file
@@ -0,0 +1,497 @@
|
||||
<?php
|
||||
namespace WPGraphQL\Type\PostType;
|
||||
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQLRelay\Relay;
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\Type\WPObjectType;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class PostTypeType
|
||||
* @package WPGraphQL\Type
|
||||
* @since 0.0.5
|
||||
*/
|
||||
class PostTypeType extends WPObjectType {
|
||||
|
||||
/**
|
||||
* Holds the type name
|
||||
* @var string $type_name
|
||||
*/
|
||||
private static $type_name;
|
||||
|
||||
/**
|
||||
* This holds the field definitions
|
||||
* @var array $fields
|
||||
* @since 0.0.5
|
||||
*/
|
||||
private static $fields;
|
||||
|
||||
/**
|
||||
* Holds the object definition for labels details
|
||||
*
|
||||
* @var object $labels_details
|
||||
*/
|
||||
private static $labels_details;
|
||||
|
||||
/**
|
||||
* PostTypeType constructor.
|
||||
* @since 0.0.5
|
||||
*/
|
||||
public function __construct() {
|
||||
|
||||
/**
|
||||
* Set the type_name
|
||||
* @since 0.0.5
|
||||
*/
|
||||
self::$type_name = 'PostType';
|
||||
|
||||
$config = [
|
||||
'name' => self::$type_name,
|
||||
'description' => __( 'An Post Type object', 'wp-graphql' ),
|
||||
'fields' => self::fields(),
|
||||
'interfaces' => [ self::node_interface() ],
|
||||
];
|
||||
|
||||
parent::__construct( $config );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* fields
|
||||
*
|
||||
* This defines the fields that make up the PostTypeType
|
||||
*
|
||||
* @return array
|
||||
* @since 0.0.5
|
||||
*/
|
||||
private static function fields() {
|
||||
|
||||
if ( null === self::$fields ) {
|
||||
/**
|
||||
* Get the taxonomies that are allowed in WPGraphQL
|
||||
*
|
||||
* @since 0.0.6
|
||||
*/
|
||||
$allowed_taxonomies = \WPGraphQL::$allowed_taxonomies;
|
||||
|
||||
self::$fields = function() use ( $allowed_taxonomies ) {
|
||||
$fields = [
|
||||
'id' => [
|
||||
'type' => Types::non_null( Types::id() ),
|
||||
'resolve' => function( \WP_Post_Type $post_type, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ( ! empty( $post_type->name ) && ! empty( $post_type->name ) ) ? Relay::toGlobalId( 'postType', $post_type->name ) : null;
|
||||
},
|
||||
],
|
||||
'name' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The internal name of the post type. This should not be used for display purposes.', 'wp-graphql' ),
|
||||
],
|
||||
'label' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Display name of the content type.', 'wp-graphql' ),
|
||||
],
|
||||
'labels' => [
|
||||
'type' => self::labels_details(),
|
||||
'description' => __( 'Details about the post type labels.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post_Type $post_type, $args, $context, ResolveInfo $info ) {
|
||||
return get_post_type_labels( $post_type );
|
||||
},
|
||||
],
|
||||
'description' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Description of the content type.', 'wp-graphql' ),
|
||||
],
|
||||
'public' => [
|
||||
'type' => Types::boolean(),
|
||||
'description' => __( 'Whether a post type is intended for use publicly either via the admin interface or by front-end users. While the default settings of exclude_from_search, publicly_queryable, show_ui, and show_in_nav_menus are inherited from public, each does not rely on this relationship and controls a very specific intention.', 'wp-graphql' ),
|
||||
],
|
||||
'hierarchical' => [
|
||||
'type' => Types::boolean(),
|
||||
'description' => __( 'Whether the post type is hierarchical, for example pages.', 'wp-graphql' ),
|
||||
],
|
||||
'excludeFromSearch' => [
|
||||
'type' => Types::boolean(),
|
||||
'description' => __( 'Whether to exclude posts with this post type from front end search results.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post_Type $post_type, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ( true === $post_type->exclude_from_search ) ? true : false;
|
||||
},
|
||||
],
|
||||
'publiclyQueryable' => [
|
||||
'type' => Types::boolean(),
|
||||
'description' => __( 'Whether queries can be performed on the front end for the post type as part of parse_request().', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post_Type $post_type, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ( true === $post_type->publicly_queryable ) ? true : false;
|
||||
},
|
||||
],
|
||||
'showUi' => [
|
||||
'type' => Types::boolean(),
|
||||
'description' => __( 'Whether to generate and allow a UI for managing this post type in the admin.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post_Type $post_type, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ( true === $post_type->show_ui ) ? true : false;
|
||||
},
|
||||
],
|
||||
'showInMenu' => [
|
||||
'type' => Types::boolean(),
|
||||
'description' => __( 'Where to show the post type in the admin menu. To work, $show_ui must be true. If true, the post type is shown in its own top level menu. If false, no menu is shown. If a string of an existing top level menu (eg. "tools.php" or "edit.php?post_type=page"), the post type will be placed as a sub-menu of that.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post_Type $post_type, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ( true === $post_type->show_in_menu ) ? true : false;
|
||||
},
|
||||
],
|
||||
'showInNavMenus' => [
|
||||
'type' => Types::boolean(),
|
||||
'description' => __( 'Makes this post type available for selection in navigation menus.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post_Type $post_type, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ( true === $post_type->show_in_nav_menus ) ? true : false;
|
||||
},
|
||||
],
|
||||
'showInAdminBar' => [
|
||||
'type' => Types::boolean(),
|
||||
'description' => __( 'Makes this post type available via the admin bar.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post_Type $post_type, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return empty( true === $post_type->show_in_admin_bar ) ? true : false;
|
||||
},
|
||||
],
|
||||
'menuPosition' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( 'The position of this post type in the menu. Only applies if show_in_menu is true.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post_Type $post_type, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $post_type->menu_position ) ? $post_type->menu_position : null;
|
||||
},
|
||||
],
|
||||
'menuIcon' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The name of the icon file to display as a menu icon.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post_Type $post_type, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $post_type->menu_icon ) ? $post_type->menu_icon : null;
|
||||
},
|
||||
],
|
||||
'hasArchive' => [
|
||||
'type' => Types::boolean(),
|
||||
'description' => __( 'Whether this content type should have archives. Content archives are generated by type and by date.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post_Type $post_type, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ( true === $post_type->has_archive ) ? true : false;
|
||||
},
|
||||
],
|
||||
'canExport' => [
|
||||
'type' => Types::boolean(),
|
||||
'description' => __( 'Whether this content type should can be exported.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post_Type $post_type, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ( true === $post_type->can_export ) ? true : false;
|
||||
},
|
||||
],
|
||||
'deleteWithUser' => [
|
||||
'type' => Types::boolean(),
|
||||
'description' => __( 'Whether delete this type of content when the author of it is deleted from the system.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post_Type $post_type, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ( true === $post_type->delete_with_user ) ? true : false;
|
||||
},
|
||||
],
|
||||
'showInRest' => [
|
||||
'type' => Types::boolean(),
|
||||
'description' => __( 'Whether to add the post type route in the REST API `wp/v2` namespace.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post_Type $post_type, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ( true === $post_type->show_in_rest ) ? true : false;
|
||||
},
|
||||
],
|
||||
'restBase' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Name of content type to diplay in REST API `wp/v2` namespace.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post_Type $post_type, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $post_type->rest_base ) ? $post_type->rest_base : null;
|
||||
},
|
||||
],
|
||||
'restControllerClass' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The REST Controller class assigned to handling this content type.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post_Type $post_type, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $post_type->rest_controller_class ) ? $post_type->rest_controller_class : null;
|
||||
},
|
||||
],
|
||||
'showInGraphql' => [
|
||||
'type' => Types::boolean(),
|
||||
'description' => __( 'Whether to add the post type to the GraphQL Schema.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post_Type $post_type, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ( true === $post_type->show_in_graphql ) ? true : false;
|
||||
},
|
||||
],
|
||||
'graphqlSingleName' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The singular name of the post type within the GraphQL Schema.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post_Type $post_type, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $post_type->graphql_single_name ) ? $post_type->graphql_single_name : null;
|
||||
},
|
||||
],
|
||||
'graphqlPluralName' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The plural name of the post type within the GraphQL Schema.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post_Type $post_type, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $post_type->graphql_plural_name ) ? $post_type->graphql_plural_name : null;
|
||||
},
|
||||
],
|
||||
'connectedTaxonomyNames' => [
|
||||
'type' => Types::list_of( Types::string() ),
|
||||
'args' => [
|
||||
'taxonomies' => [
|
||||
'type' => Types::list_of( Types::taxonomy_enum() ),
|
||||
'description' => __( 'Select which taxonomies to limit the results to', 'wp-graphql' ),
|
||||
],
|
||||
],
|
||||
'description' => __( 'A list of Taxonomies associated with the post type', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post_Type $post_type_object, array $args, $context, ResolveInfo $info ) use ( $allowed_taxonomies ) {
|
||||
|
||||
$object_taxonomies = get_object_taxonomies( $post_type_object->name );
|
||||
|
||||
$taxonomy_names = [];
|
||||
|
||||
/**
|
||||
* If the $arg for taxonomies is populated, use it as the $allowed_taxonomies
|
||||
* otherwise use the default $allowed_taxonomies passed down
|
||||
*/
|
||||
$allowed_taxonomies = ! empty( $args['taxonomies'] ) && is_array( $args['taxonomies'] ) ? $args['taxonomies'] : $allowed_taxonomies;
|
||||
|
||||
if ( ! empty( $object_taxonomies ) && is_array( $object_taxonomies ) ) {
|
||||
foreach ( $object_taxonomies as $taxonomy ) {
|
||||
if ( in_array( $taxonomy, $allowed_taxonomies, true ) ) {
|
||||
$taxonomy_names[] = $taxonomy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ! empty( $taxonomy_names ) ? $taxonomy_names : null;
|
||||
},
|
||||
],
|
||||
'connectedTaxonomies' => [
|
||||
'type' => Types::list_of( Types::taxonomy() ),
|
||||
'args' => [
|
||||
'taxonomies' => [
|
||||
'type' => Types::list_of( Types::taxonomy_enum() ),
|
||||
'description' => __( 'Select which taxonomies to limit the results to', 'wp-graphql' ),
|
||||
],
|
||||
],
|
||||
'description' => __( 'List of Taxonomies connected to the Post Type', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Post_Type $post_type_object, array $args, AppContext $context, ResolveInfo $info ) use ( $allowed_taxonomies ) {
|
||||
|
||||
$tax_objects = [];
|
||||
|
||||
/**
|
||||
* If the $arg for taxonomies is populated, use it as the $allowed_taxonomies
|
||||
* otherwise use the default $allowed_taxonomies passed down
|
||||
*/
|
||||
$allowed_taxonomies = ! empty( $args['taxonomies'] ) && is_array( $args['taxonomies'] ) ? $args['taxonomies'] : $allowed_taxonomies;
|
||||
|
||||
if ( ! empty( $allowed_taxonomies ) && is_array( $allowed_taxonomies ) ) {
|
||||
foreach ( $allowed_taxonomies as $taxonomy ) {
|
||||
if ( in_array( $taxonomy, get_object_taxonomies( $post_type_object->name ), true ) ) {
|
||||
$tax_object = get_taxonomy( $taxonomy );
|
||||
$tax_objects[ $tax_object->graphql_single_name ] = $tax_object;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ! empty( $tax_objects ) ? $tax_objects : null;
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* Pass the fields through a filter to allow for hooking in and adjusting the shape
|
||||
* of the type's schema
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
return self::prepare_fields( $fields, self::$type_name );
|
||||
|
||||
};
|
||||
}
|
||||
return self::$fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* This defines the labels details object type that can be queried on mediaItems
|
||||
*
|
||||
* @return null|WPObjectType
|
||||
* @since 0.0.6
|
||||
*/
|
||||
private static function labels_details() {
|
||||
if ( null === self::$labels_details ) {
|
||||
self::$labels_details = new WPObjectType( [
|
||||
'name' => 'LabelsDetails',
|
||||
'fields' => function() {
|
||||
$fields = [
|
||||
'name' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'General name for the post type, usually plural.', 'wp-graphql' ),
|
||||
],
|
||||
'singularName' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Name for one object of this post type.', 'wp-graphql' ),
|
||||
'resolve' => function( $labels ) {
|
||||
return ! empty( $labels->singular_name ) ? $labels->singular_name : null;
|
||||
},
|
||||
],
|
||||
'addNew' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Default is ‘Add New’ for both hierarchical and non-hierarchical types.', 'wp-graphql' ),
|
||||
'resolve' => function( $labels ) {
|
||||
return ! empty( $labels->add_new ) ? $labels->add_new : null;
|
||||
},
|
||||
],
|
||||
'addNewItem' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Label for adding a new singular item.', 'wp-graphql' ),
|
||||
'resolve' => function( $labels ) {
|
||||
return ! empty( $labels->add_new_item ) ? $labels->add_new_item : null;
|
||||
},
|
||||
],
|
||||
'editItem' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Label for editing a singular item.', 'wp-graphql' ),
|
||||
'resolve' => function( $labels ) {
|
||||
return ! empty( $labels->edit_item ) ? $labels->edit_item : null;
|
||||
},
|
||||
],
|
||||
'newItem' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Label for the new item page title.', 'wp-graphql' ),
|
||||
'resolve' => function( $labels ) {
|
||||
return ! empty( $labels->new_item ) ? $labels->new_item : null;
|
||||
},
|
||||
],
|
||||
'viewItem' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Label for viewing a singular item.', 'wp-graphql' ),
|
||||
'resolve' => function( $labels ) {
|
||||
return ! empty( $labels->view_item ) ? $labels->view_item : null;
|
||||
},
|
||||
],
|
||||
'viewItems' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Label for viewing post type archives.', 'wp-graphql' ),
|
||||
'resolve' => function( $labels ) {
|
||||
return ! empty( $labels->view_items ) ? $labels->view_items : null;
|
||||
},
|
||||
],
|
||||
'searchItems' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Label for searching plural items.', 'wp-graphql' ),
|
||||
'resolve' => function( $labels ) {
|
||||
return ! empty( $labels->search_items ) ? $labels->search_items : null;
|
||||
},
|
||||
],
|
||||
'notFound' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Label used when no items are found.', 'wp-graphql' ),
|
||||
'resolve' => function( $labels ) {
|
||||
return ! empty( $labels->not_found ) ? $labels->not_found : null;
|
||||
},
|
||||
],
|
||||
'notFoundInTrash' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Label used when no items are in the trash.', 'wp-graphql' ),
|
||||
'resolve' => function( $labels ) {
|
||||
return ! empty( $labels->not_found_in_trash ) ? $labels->not_found_in_trash : null;
|
||||
},
|
||||
],
|
||||
'parentItemColon' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Label used to prefix parents of hierarchical items.', 'wp-graphql' ),
|
||||
'resolve' => function( $labels ) {
|
||||
return ! empty( $labels->parent_item_colon ) ? $labels->parent_item_colon : null;
|
||||
},
|
||||
],
|
||||
'allItems' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Label to signify all items in a submenu link.', 'wp-graphql' ),
|
||||
'resolve' => function( $labels ) {
|
||||
return ! empty( $labels->all_items ) ? $labels->all_items : null;
|
||||
},
|
||||
],
|
||||
'archives' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Label for archives in nav menus', 'wp-graphql' ),
|
||||
],
|
||||
'attributes' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Label for the attributes meta box.', 'wp-graphql' ),
|
||||
],
|
||||
'insertIntoItem' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Label for the media frame button.', 'wp-graphql' ),
|
||||
'resolve' => function( $labels ) {
|
||||
return ! empty( $labels->insert_into_item ) ? $labels->insert_into_item : null;
|
||||
},
|
||||
],
|
||||
'uploadedToThisItem' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Label for the media frame filter.', 'wp-graphql' ),
|
||||
'resolve' => function( $labels ) {
|
||||
return ! empty( $labels->uploaded_to_this_item ) ? $labels->uploaded_to_this_item : null;
|
||||
},
|
||||
],
|
||||
'featuredImage' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Label for the Featured Image meta box title.', 'wp-graphql' ),
|
||||
'resolve' => function( $labels ) {
|
||||
return ! empty( $labels->featured_image ) ? $labels->featured_image : null;
|
||||
},
|
||||
],
|
||||
'setFeaturedImage' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Label for setting the featured image.', 'wp-graphql' ),
|
||||
'resolve' => function( $labels ) {
|
||||
return ! empty( $labels->set_featured_image ) ? $labels->set_featured_image : null;
|
||||
},
|
||||
],
|
||||
'removeFeaturedImage' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Label for removing the featured image.', 'wp-graphql' ),
|
||||
'resolve' => function( $labels ) {
|
||||
return ! empty( $labels->remove_featured_image ) ? $labels->remove_featured_image : null;
|
||||
},
|
||||
],
|
||||
'useFeaturedImage' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Label in the media frame for using a featured image.', 'wp-graphql' ),
|
||||
'resolve' => function( $labels ) {
|
||||
return ! empty( $labels->use_featured_item ) ? $labels->use_featured_item : null;
|
||||
},
|
||||
],
|
||||
'menuName' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Label for the menu name.', 'wp-graphql' ),
|
||||
'resolve' => function( $labels ) {
|
||||
return ! empty( $labels->menu_name ) ? $labels->menu_name : null;
|
||||
},
|
||||
],
|
||||
'filterItemsList' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Label for the table views hidden heading.', 'wp-graphql' ),
|
||||
'resolve' => function( $labels ) {
|
||||
return ! empty( $labels->filter_items_list ) ? $labels->filter_items_list : null;
|
||||
},
|
||||
],
|
||||
'itemsListNavigation' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Label for the table pagination hidden heading.', 'wp-graphql' ),
|
||||
'resolve' => function( $labels ) {
|
||||
return ! empty( $labels->items_list_navigation ) ? $labels->items_list_navigation : null;
|
||||
},
|
||||
],
|
||||
'itemsList' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Label for the table hidden heading.', 'wp-graphql' ),
|
||||
'resolve' => function( $labels ) {
|
||||
return ! empty( $labels->items_list ) ? $labels->items_list : null;
|
||||
},
|
||||
],
|
||||
];
|
||||
return self::prepare_fields( $fields, 'LabelsDetails' );
|
||||
},
|
||||
] );
|
||||
} // End if().
|
||||
return ! empty( self::$labels_details ) ? self::$labels_details : null;
|
||||
}
|
||||
|
||||
}
|
||||
148
wordpress/wp-content/plugins/wp-graphql/src/Type/RootMutationType.php
Executable file
148
wordpress/wp-content/plugins/wp-graphql/src/Type/RootMutationType.php
Executable file
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL\Type;
|
||||
|
||||
use WPGraphQL\Type\MediaItem\Mutation\MediaItemCreate;
|
||||
use WPGraphQL\Type\MediaItem\Mutation\MediaItemUpdate;
|
||||
use WPGraphQL\Type\MediaItem\Mutation\MediaItemDelete;
|
||||
use WPGraphQL\Type\PostObject\Mutation\PostObjectCreate;
|
||||
use WPGraphQL\Type\PostObject\Mutation\PostObjectDelete;
|
||||
use WPGraphQL\Type\PostObject\Mutation\PostObjectUpdate;
|
||||
use WPGraphQL\Type\PostObject\Mutation\TermObjectDelete;
|
||||
use WPGraphQL\Type\Settings\Mutation\SettingsUpdate;
|
||||
use WPGraphQL\Type\TermObject\Mutation\TermObjectCreate;
|
||||
use WPGraphQL\Type\TermObject\Mutation\TermObjectUpdate;
|
||||
use WPGraphQL\Type\User\Mutation\UserCreate;
|
||||
use WPGraphQL\Type\User\Mutation\UserDelete;
|
||||
use WPGraphQL\Type\User\Mutation\UserUpdate;
|
||||
|
||||
/**
|
||||
* Class RootMutationType
|
||||
* The RootMutationType is the primary entry point for Mutations in the GraphQL Schema
|
||||
*
|
||||
* @package WPGraphQL\Type
|
||||
* @since 0.0.8
|
||||
*/
|
||||
class RootMutationType extends WPObjectType {
|
||||
|
||||
/**
|
||||
* Holds the $fields definition for the PluginType
|
||||
*
|
||||
* @var $fields
|
||||
*/
|
||||
private static $fields;
|
||||
|
||||
/**
|
||||
* Holds the type name
|
||||
*
|
||||
* @var string $type_name
|
||||
*/
|
||||
private static $type_name;
|
||||
|
||||
/**
|
||||
* RootMutationType constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
|
||||
self::$type_name = 'rootMutation';
|
||||
|
||||
/**
|
||||
* Configure the rootMutation
|
||||
*/
|
||||
$config = [
|
||||
'name' => self::$type_name,
|
||||
'description' => __( 'The root mutation', 'wp-graphql' ),
|
||||
'fields' => self::fields(),
|
||||
];
|
||||
|
||||
/**
|
||||
* Pass the config to the parent construct
|
||||
*/
|
||||
parent::__construct( $config );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This defines the fields for the RootMutationType. The fields are passed through a filter so the shape of the
|
||||
* schema can be modified, for example to add entry points to Types that are unique to certain plugins.
|
||||
*
|
||||
* @return array|\GraphQL\Type\Definition\FieldDefinition[]
|
||||
*/
|
||||
private static function fields() {
|
||||
|
||||
if ( null === self::$fields ) {
|
||||
|
||||
$fields = [];
|
||||
$allowed_post_types = \WPGraphQL::$allowed_post_types;
|
||||
$allowed_taxonomies = \WPGraphQL::$allowed_taxonomies;
|
||||
|
||||
if ( ! empty( $allowed_post_types ) && is_array( $allowed_post_types ) ) {
|
||||
foreach ( $allowed_post_types as $post_type ) {
|
||||
/**
|
||||
* Get the post_type object to pass down to the schema
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
$post_type_object = get_post_type_object( $post_type );
|
||||
|
||||
if ( 'mediaItem' === $post_type_object->graphql_single_name ) {
|
||||
$fields[ 'create' . ucwords( $post_type_object->graphql_single_name ) ] = MediaItemCreate::mutate( $post_type_object );
|
||||
$fields[ 'update' . ucwords( $post_type_object->graphql_single_name ) ] = MediaItemUpdate::mutate( $post_type_object );
|
||||
$fields[ 'delete' . ucwords( $post_type_object->graphql_single_name ) ] = MediaItemDelete::mutate( $post_type_object );
|
||||
|
||||
} else {
|
||||
/**
|
||||
* Root mutation for single posts (of the specified post_type)
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
$fields[ 'create' . ucwords( $post_type_object->graphql_single_name ) ] = PostObjectCreate::mutate( $post_type_object );
|
||||
$fields[ 'update' . ucwords( $post_type_object->graphql_single_name ) ] = PostObjectUpdate::mutate( $post_type_object );
|
||||
$fields[ 'delete' . ucwords( $post_type_object->graphql_single_name ) ] = PostObjectDelete::mutate( $post_type_object );
|
||||
}
|
||||
|
||||
} // End foreach().
|
||||
} // End if().
|
||||
|
||||
/**
|
||||
* Root mutation field for updating settings
|
||||
*/
|
||||
$fields[ 'updateSettings' ] = SettingsUpdate::mutate();
|
||||
|
||||
if ( ! empty( $allowed_taxonomies ) && is_array( $allowed_taxonomies ) ) {
|
||||
foreach ( $allowed_taxonomies as $taxonomy ) {
|
||||
|
||||
/**
|
||||
* Get the taxonomy object to pass down to the schema
|
||||
*/
|
||||
$taxonomy_object = get_taxonomy( $taxonomy );
|
||||
|
||||
/**
|
||||
* Root mutation for single term objects (of the specified taxonomy)
|
||||
*/
|
||||
$fields[ 'create' . ucwords( $taxonomy_object->graphql_single_name ) ] = TermObjectCreate::mutate( $taxonomy_object );
|
||||
$fields[ 'update' . ucwords( $taxonomy_object->graphql_single_name ) ] = TermObjectUpdate::mutate( $taxonomy_object );
|
||||
$fields[ 'delete' . ucwords( $taxonomy_object->graphql_single_name ) ] = TermObjectDelete::mutate( $taxonomy_object );
|
||||
}
|
||||
} // End if().
|
||||
|
||||
/**
|
||||
* User Mutations
|
||||
*/
|
||||
$fields[ 'createUser' ] = UserCreate::mutate();
|
||||
$fields[ 'updateUser' ] = UserUpdate::mutate();
|
||||
$fields[ 'deleteUser' ] = UserDelete::mutate();
|
||||
|
||||
self::$fields = $fields;
|
||||
|
||||
} // End if().
|
||||
|
||||
/**
|
||||
* Pass the fields through a filter to allow for hooking in and adjusting the shape
|
||||
* of the type's schema
|
||||
*/
|
||||
return self::prepare_fields( self::$fields, self::$type_name );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
260
wordpress/wp-content/plugins/wp-graphql/src/Type/RootQueryType.php
Executable file
260
wordpress/wp-content/plugins/wp-graphql/src/Type/RootQueryType.php
Executable file
@@ -0,0 +1,260 @@
|
||||
<?php
|
||||
namespace WPGraphQL\Type;
|
||||
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQLRelay\Relay;
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\Data\DataSource;
|
||||
use WPGraphQL\Type\Comment\CommentQuery;
|
||||
use WPGraphQL\Type\Comment\Connection\CommentConnectionDefinition;
|
||||
use WPGraphQL\Type\Setting\SettingQuery;
|
||||
use WPGraphQL\Type\Settings\SettingsQuery;
|
||||
use WPGraphQL\Type\Plugin\Connection\PluginConnectionDefinition;
|
||||
use WPGraphQL\Type\Plugin\PluginQuery;
|
||||
use WPGraphQL\Type\PostObject\PostObjectQuery;
|
||||
use WPGraphQL\Type\PostObject\Connection\PostObjectConnectionDefinition;
|
||||
use WPGraphQL\Type\TermObject\Connection\TermObjectConnectionDefinition;
|
||||
use WPGraphQL\Type\TermObject\TermObjectQuery;
|
||||
use WPGraphQL\Type\Theme\Connection\ThemeConnectionDefinition;
|
||||
use WPGraphQL\Type\User\Connection\UserConnectionDefinition;
|
||||
use WPGraphQL\Type\User\UserQuery;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class RootQueryType
|
||||
* The RootQueryType is the primary entry for Queries in the GraphQL Schema.
|
||||
* @package WPGraphQL\Type
|
||||
* @since 0.0.4
|
||||
*/
|
||||
class RootQueryType extends WPObjectType {
|
||||
|
||||
/**
|
||||
* RootQueryType constructor.
|
||||
* @since 0.0.5
|
||||
*/
|
||||
public function __construct() {
|
||||
|
||||
/**
|
||||
* Run an action when the RootQuery is being generated
|
||||
* @since 0.0.5
|
||||
*/
|
||||
do_action( 'graphql_root_query' );
|
||||
|
||||
/**
|
||||
* Configure the RootQuery
|
||||
* @since 0.0.5
|
||||
*/
|
||||
$config = [
|
||||
'name' => 'rootQuery',
|
||||
'fields' => self::fields(),
|
||||
];
|
||||
|
||||
/**
|
||||
* Pass the config to the parent construct
|
||||
* @since 0.0.5
|
||||
*/
|
||||
parent::__construct( $config );
|
||||
|
||||
}
|
||||
|
||||
public static function fields() {
|
||||
|
||||
/**
|
||||
* Setup data
|
||||
* @since 0.0.5
|
||||
*/
|
||||
$allowed_post_types = \WPGraphQL::$allowed_post_types;
|
||||
$allowed_taxonomies = \WPGraphQL::$allowed_taxonomies;
|
||||
$allowed_setting_types = DataSource::get_allowed_settings_by_group();
|
||||
$node_definition = DataSource::get_node_definition();
|
||||
|
||||
/**
|
||||
* Creates the node root query field which can be used
|
||||
* to query any node from the system using the globally unique
|
||||
* ID
|
||||
* @since 0.0.5
|
||||
*/
|
||||
$fields['node'] = $node_definition['nodeField'];
|
||||
|
||||
/**
|
||||
* Creates the comment root query field
|
||||
* @since 0.0.5
|
||||
*/
|
||||
$fields['comment'] = CommentQuery::root_query();
|
||||
$fields['comments'] = CommentConnectionDefinition::connection();
|
||||
|
||||
/**
|
||||
* Creates the plugin root query field
|
||||
* @since 0.0.5
|
||||
*/
|
||||
$fields['plugin'] = PluginQuery::root_query();
|
||||
$fields['plugins'] = PluginConnectionDefinition::connection();
|
||||
|
||||
/**
|
||||
* Create the root query fields for any setting type in
|
||||
* the $allowed_setting_types array.
|
||||
*/
|
||||
if ( ! empty( $allowed_setting_types ) && is_array( $allowed_setting_types ) ) {
|
||||
foreach ( $allowed_setting_types as $group => $setting_type ) {
|
||||
$setting_type = str_replace('_', '', strtolower( $group ) );
|
||||
$fields[ $setting_type . 'Settings' ] = SettingQuery::root_query( $group, $setting_type );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the all settings root query field
|
||||
*/
|
||||
$fields['allSettings'] = SettingsQuery::root_query();
|
||||
|
||||
/**
|
||||
* Creates the theme root query field
|
||||
* @since 0.0.5
|
||||
*/
|
||||
$fields['theme'] = self::theme();
|
||||
|
||||
/**
|
||||
* Creates the theme root query field to query a collection
|
||||
* of themes
|
||||
* @since 0.0.5
|
||||
*/
|
||||
$fields['themes'] = ThemeConnectionDefinition::connection();
|
||||
|
||||
/**
|
||||
* Creates the user root query field
|
||||
* @since 0.0.5
|
||||
*/
|
||||
$fields['user'] = UserQuery::root_query();
|
||||
|
||||
/**
|
||||
* Creates the users root query field to query a collection
|
||||
* of users
|
||||
* @since 0.0.5
|
||||
*/
|
||||
$fields['users'] = UserConnectionDefinition::connection();
|
||||
|
||||
/**
|
||||
* Creates the viewer root query field
|
||||
* @since 0.0.5
|
||||
*/
|
||||
$fields['viewer'] = self::viewer();
|
||||
|
||||
/**
|
||||
* Creates the root fields for post objects (of any post_type)
|
||||
* This registers root fields (single and plural) for any post_type that has been registered as an
|
||||
* allowed post_type.
|
||||
* @see \WPGraphQL::$allowed_post_types
|
||||
* @since 0.0.5
|
||||
*/
|
||||
if ( ! empty( $allowed_post_types ) && is_array( $allowed_post_types ) ) {
|
||||
foreach ( $allowed_post_types as $post_type ) {
|
||||
/**
|
||||
* Get the post_type object to pass down to the schema
|
||||
* @since 0.0.5
|
||||
*/
|
||||
$post_type_object = get_post_type_object( $post_type );
|
||||
|
||||
/**
|
||||
* Root query for single posts (of the specified post_type)
|
||||
* @since 0.0.5
|
||||
*/
|
||||
$fields[ $post_type_object->graphql_single_name ] = PostObjectQuery::root_query( $post_type_object );
|
||||
$fields[ $post_type_object->graphql_single_name . 'By' ] = PostObjectQuery::post_object_by( $post_type_object );
|
||||
|
||||
/**
|
||||
* Root query for collections of posts (of the specified post_type)
|
||||
* @since 0.0.5
|
||||
*/
|
||||
$fields[ $post_type_object->graphql_plural_name ] = PostObjectConnectionDefinition::connection( $post_type_object );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the root fields for terms of each taxonomy
|
||||
* This registers root fields (single and plural) for terms of any taxonomy that has been registered as an
|
||||
* allowed taxonomy.
|
||||
* @see \WPGraphQL::$allowed_taxonomies
|
||||
* @since 0.0.5
|
||||
*/
|
||||
if ( ! empty( $allowed_taxonomies ) && is_array( $allowed_taxonomies ) ) {
|
||||
foreach ( $allowed_taxonomies as $taxonomy ) {
|
||||
|
||||
/**
|
||||
* Get the taxonomy object
|
||||
* @since 0.0.5
|
||||
*/
|
||||
$taxonomy_object = get_taxonomy( $taxonomy );
|
||||
|
||||
/**
|
||||
* Root query for single terms (of the specified taxonomy)
|
||||
* @since 0.0.5
|
||||
*/
|
||||
$fields[ $taxonomy_object->graphql_single_name ] = TermObjectQuery::root_query( $taxonomy_object );
|
||||
|
||||
/**
|
||||
* Root query for collections of terms (of the specified taxonomy)
|
||||
* @since 0.0.5
|
||||
*/
|
||||
$fields[ $taxonomy_object->graphql_plural_name ] = TermObjectConnectionDefinition::connection( $taxonomy_object );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass the root queries through a filter.
|
||||
* This allows fields to be added or removed.
|
||||
* NOTE: Use this filter with care. Before removing existing fields seriously consider deprecating the field, as
|
||||
* that will allow the field to still be used and not break systems that rely on it, but just not be present
|
||||
* in Schema documentation, etc.
|
||||
* If the behavior of a field needs to be changed, depending on the change, it might be better to consider adding
|
||||
* a new field with the new behavior instead of overriding an existing field. This will allow existing fields
|
||||
* to behave as expected, but will allow introduction of new fields with different behavior at any point.
|
||||
* @since 0.0.5
|
||||
*/
|
||||
$fields = apply_filters( 'graphql_root_queries', $fields );
|
||||
|
||||
/**
|
||||
* Sort the fields alphabetically by keys
|
||||
* (this makes the schema documentation much nicer to browse)
|
||||
*/
|
||||
ksort( $fields );
|
||||
|
||||
return $fields;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* theme
|
||||
* This sets up the theme entry point for the root query
|
||||
* @return array
|
||||
* @since 0.0.5
|
||||
*/
|
||||
public static function theme() {
|
||||
return [
|
||||
'type' => Types::theme(),
|
||||
'description' => __( 'A Theme object', 'wp-graphql' ),
|
||||
'args' => [
|
||||
'id' => Types::non_null( Types::id() ),
|
||||
],
|
||||
'resolve' => function( $source, array $args, $context, ResolveInfo $info ) {
|
||||
$id_components = Relay::fromGlobalId( $args['id'] );
|
||||
|
||||
return DataSource::resolve_theme( $id_components['id'] );
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* viewer
|
||||
* This sets up the viewer entry point for the root query
|
||||
* @return array
|
||||
* @since 0.0.5
|
||||
*/
|
||||
public static function viewer() {
|
||||
return [
|
||||
'type' => Types::user(),
|
||||
'description' => __( 'Returns the current user', 'wp-graphql' ),
|
||||
'resolve' => function( $source, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ( false !== $context->viewer->ID ) ? DataSource::resolve_user( $context->viewer->ID ) : null;
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
49
wordpress/wp-content/plugins/wp-graphql/src/Type/Setting/SettingQuery.php
Executable file
49
wordpress/wp-content/plugins/wp-graphql/src/Type/Setting/SettingQuery.php
Executable file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
namespace WPGraphQL\Type\Setting;
|
||||
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class SettingQuery
|
||||
*
|
||||
* @package WPGraphQL\Type\Setting
|
||||
*/
|
||||
class SettingQuery {
|
||||
|
||||
/**
|
||||
* Holds the root_query field definition
|
||||
*
|
||||
* @var array $root_query
|
||||
* @access private
|
||||
*/
|
||||
private static $root_query;
|
||||
|
||||
/**
|
||||
* Method that returns the root query field definition
|
||||
* for the requested setting type
|
||||
*
|
||||
* @access public
|
||||
* @param string $group
|
||||
* @param array $setting_type
|
||||
*
|
||||
* @return array $root_query
|
||||
*/
|
||||
public static function root_query( $group, $setting_type ) {
|
||||
|
||||
if ( null === self::$root_query ) {
|
||||
self::$root_query = [];
|
||||
}
|
||||
|
||||
if ( ! empty( $setting_type ) && empty( self::$root_query[ $group ] ) ) {
|
||||
self::$root_query = [
|
||||
'type' => Types::setting( $group ),
|
||||
'resolve' => function () use ( $setting_type ) {
|
||||
return $setting_type;
|
||||
},
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
return self::$root_query;
|
||||
}
|
||||
}
|
||||
156
wordpress/wp-content/plugins/wp-graphql/src/Type/Setting/SettingType.php
Executable file
156
wordpress/wp-content/plugins/wp-graphql/src/Type/Setting/SettingType.php
Executable file
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL\Type\Setting;
|
||||
|
||||
use GraphQL\Error\UserError;
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\Data\DataSource;
|
||||
use WPGraphQL\Type\WPObjectType;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* class SettingType
|
||||
*
|
||||
* This sets up the base setting type for setting queries
|
||||
*
|
||||
* @package WPGraphQL\Type\Setting
|
||||
*/
|
||||
class SettingType extends WPObjectType {
|
||||
|
||||
/**
|
||||
* Holds the $fields definition
|
||||
*
|
||||
* @var array $fields
|
||||
* @access private
|
||||
*/
|
||||
private static $fields;
|
||||
|
||||
/**
|
||||
* Holds the $setting_group definition
|
||||
*
|
||||
* @var string $setting_group
|
||||
* @access private
|
||||
*/
|
||||
private static $setting_group;
|
||||
|
||||
/**
|
||||
* SettingType constructor.
|
||||
*
|
||||
* @param string $setting_group The setting group name
|
||||
* @access public
|
||||
*/
|
||||
public function __construct( $setting_group ) {
|
||||
|
||||
/**
|
||||
* Set the setting_type so we can use it in $fields
|
||||
*/
|
||||
self::$setting_group = $setting_group;
|
||||
|
||||
/**
|
||||
* Retrieve all of the settings that are categorized under the $setting_type
|
||||
* and set them as the $setting_fields for later use in building fields
|
||||
*/
|
||||
$setting_fields = DataSource::get_setting_group_fields( $setting_group );
|
||||
|
||||
$config = [
|
||||
'name' => ucfirst( $setting_group ) . 'Settings',
|
||||
'description' => sprintf( __( 'The %s setting type', 'wp-graphql' ), $setting_group ),
|
||||
'fields' => self::fields( $setting_fields ),
|
||||
];
|
||||
|
||||
parent::__construct( $config );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This defines the fields (various settings) for a given setting group
|
||||
*
|
||||
* @param $setting_fields
|
||||
*
|
||||
* @access private
|
||||
* @return \GraphQL\Type\Definition\FieldDefinition|mixed|null
|
||||
*/
|
||||
private static function fields( $setting_fields ) {
|
||||
|
||||
/**
|
||||
* Set $fields to an empty array so that we aren't storing values
|
||||
* from another setting_type
|
||||
*/
|
||||
$fields = [];
|
||||
|
||||
if ( ! empty( $setting_fields ) && is_array( $setting_fields ) ) {
|
||||
|
||||
foreach ( $setting_fields as $key => $setting_field ) {
|
||||
|
||||
/**
|
||||
* Determine if the individual setting already has a
|
||||
* REST API name, if not use the option name.
|
||||
* Then, sanitize the field name to be camelcase
|
||||
*/
|
||||
if ( ! empty( $setting_field['show_in_rest']['name'] ) ) {
|
||||
$field_key = $setting_field['show_in_rest']['name'];
|
||||
} else {
|
||||
$field_key = $key;
|
||||
}
|
||||
$field_key = lcfirst( str_replace( '_', '', ucwords( $field_key, '_' ) ) );
|
||||
|
||||
if ( ! empty( $key ) && ! empty( $field_key ) ) {
|
||||
|
||||
/**
|
||||
* Dynamically build the individual setting and it's fields
|
||||
* then add it to the fields array
|
||||
*/
|
||||
$fields[ $field_key ] = [
|
||||
'type' => Types::get_type( $setting_field['type'] ),
|
||||
'description' => $setting_field['description'],
|
||||
'resolve' => function( $root, $args, AppContext $context, ResolveInfo $info ) use ( $setting_field, $field_key, $key ) {
|
||||
|
||||
/**
|
||||
* Check to see if the user querying the email field has the 'manage_options' capability
|
||||
* All other options should be public by default
|
||||
*/
|
||||
if ( 'admin_email' === $setting_field['key'] ) {
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
throw new UserError( __( 'Sorry, you do not have permission to view this setting.', 'wp-graphql' ) );
|
||||
}
|
||||
}
|
||||
|
||||
$option = ! empty( $setting_field['key'] ) ? get_option( $setting_field['key'] ) : null;
|
||||
|
||||
switch ( $setting_field['type'] ) {
|
||||
case 'integer':
|
||||
$option = absint( $option );
|
||||
break;
|
||||
case 'string':
|
||||
$option = (string) $option;
|
||||
break;
|
||||
case 'boolean':
|
||||
$option = (boolean) $option;
|
||||
break;
|
||||
case 'number':
|
||||
$option = (float) $option;
|
||||
break;
|
||||
}
|
||||
|
||||
return ! empty( $option ) ? $option : '';
|
||||
},
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass the fields through a filter to allow for hooking in and adjusting the shape
|
||||
* of the type's schema
|
||||
*/
|
||||
self::$fields = self::prepare_fields( $fields, self::$setting_group );
|
||||
|
||||
}
|
||||
|
||||
return ! empty( self::$fields ) ? self::$fields : null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL\Type\Settings\Mutation;
|
||||
|
||||
use WPGraphQL\Types;
|
||||
use WPGraphQL\Data\DataSource;
|
||||
|
||||
/**
|
||||
* Class SettingsMutation
|
||||
*
|
||||
* @package WPGraphQL\Type\Settings
|
||||
*/
|
||||
class SettingsMutation {
|
||||
|
||||
/**
|
||||
* Holds the input fields configuration
|
||||
*/
|
||||
private static $input_fields;
|
||||
|
||||
/**
|
||||
* The input fields for the settings mutation
|
||||
*
|
||||
* @return mixed|array|null $input_fields
|
||||
*/
|
||||
public static function input_fields() {
|
||||
|
||||
/**
|
||||
* Retrieve all of the allowed settings
|
||||
*/
|
||||
$allowed_settings = DataSource::get_allowed_settings();
|
||||
|
||||
$input_fields = [];
|
||||
|
||||
if ( ! empty( $allowed_settings ) && empty( self::$input_fields ) ) {
|
||||
|
||||
/**
|
||||
* Loop through the $allowed_settings and build fields
|
||||
* for the individual settings
|
||||
*/
|
||||
foreach ( $allowed_settings as $key => $setting ) {
|
||||
|
||||
/**
|
||||
* Determine if the individual setting already has a
|
||||
* REST API name, if not use the option name.
|
||||
* Sanitize the field name to be camelcase
|
||||
*/
|
||||
if ( ! empty( $setting['show_in_rest']['name'] ) ) {
|
||||
$individual_setting_key = lcfirst( $setting['group'] . 'Settings' . str_replace( '_', '', ucwords( $setting['show_in_rest']['name'], '_' ) ) );
|
||||
} else {
|
||||
$individual_setting_key = lcfirst( $setting['group'] . 'Settings' . str_replace( '_', '', ucwords( $key, '_' ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically build the individual setting,
|
||||
* then add it to the $input_fields
|
||||
*/
|
||||
$input_fields[ $individual_setting_key ] = [
|
||||
'type' => Types::get_type( $setting['type'] ),
|
||||
'description' => $setting['description'],
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
self::$input_fields = apply_filters( 'graphql_setting_mutation_input_fields', $input_fields );
|
||||
|
||||
}
|
||||
|
||||
return ( ! empty( self::$input_fields ) ? self::$input_fields : null );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL\Type\Settings\Mutation;
|
||||
|
||||
use GraphQL\Error\UserError;
|
||||
use GraphQL\Language\AST\Type;
|
||||
use GraphQLRelay\Relay;
|
||||
use WPGraphQL\Types;
|
||||
use WPGraphQL\Data\DataSource;
|
||||
use WPGraphQL\Type\Setting\SettingQuery;
|
||||
use WPGraphQL\Type\Settings\SettingsQuery;
|
||||
|
||||
/**
|
||||
* Class SettingsUpdate
|
||||
*
|
||||
* @package WPGraphQL\Type\Settings\Mutation
|
||||
*/
|
||||
class SettingsUpdate {
|
||||
|
||||
/**
|
||||
* Stores the UpdateSettings mutation field definition
|
||||
*
|
||||
* @var array $mutation
|
||||
*/
|
||||
private static $mutation;
|
||||
|
||||
/**
|
||||
* Define the UpdateSettings mutation
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return array|mixed
|
||||
*/
|
||||
public static function mutate() {
|
||||
|
||||
if ( empty( self::$mutation ) ) {
|
||||
|
||||
/**
|
||||
* Set the name of the mutation being performed
|
||||
*/
|
||||
$mutation_name = 'UpdateSettings';
|
||||
|
||||
self::$mutation = Relay::mutationWithClientMutationId( [
|
||||
'name' => $mutation_name,
|
||||
'description' => __( 'Update any of the various settings.', 'wp-graphql' ),
|
||||
'inputFields' => SettingsMutation::input_fields(),
|
||||
'outputFields' => self::output_fields(),
|
||||
'mutateAndGetPayload' => function ( $input ) {
|
||||
/**
|
||||
* Check that the user can manage setting options
|
||||
*/
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
throw new UserError( __( 'Sorry, you are not allowed to edit settings as this user.', 'wp-graphql' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* The $updatable_settings_options will store all of the allowed
|
||||
* settings in a WP ready format
|
||||
*/
|
||||
$updatable_settings_options = [];
|
||||
|
||||
$allowed_settings = DataSource::get_allowed_settings();
|
||||
|
||||
/**
|
||||
* Loop through the $allowed_settings and build the insert options array
|
||||
*/
|
||||
foreach ( $allowed_settings as $key => $setting ) {
|
||||
|
||||
/**
|
||||
* Determine if the individual setting already has a
|
||||
* REST API name, if not use the option name.
|
||||
* Sanitize the field name to be camelcase
|
||||
*/
|
||||
if ( ! empty( $setting['show_in_rest']['name'] ) ) {
|
||||
$individual_setting_key = lcfirst( $setting['group'] . 'Settings' . str_replace( '_', '', ucwords( $setting['show_in_rest']['name'], '_' ) ) );
|
||||
} else {
|
||||
$individual_setting_key = lcfirst( $setting['group'] . 'Settings' . str_replace( '_', '', ucwords( $key, '_' ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically build the individual setting,
|
||||
* then add it to $updatable_settings_options
|
||||
*/
|
||||
$updatable_settings_options[ $individual_setting_key ] = [
|
||||
'option' => $key,
|
||||
'group' => $setting['group'],
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
foreach ( $input as $key => $value ) {
|
||||
/**
|
||||
* Throw an error if the input field is the site url,
|
||||
* as we do not want users changing it and breaking all
|
||||
* the things
|
||||
*/
|
||||
if ( 'generalSettingsUrl' === $key ) {
|
||||
throw new UserError( __( 'Sorry, that is not allowed, speak with your site administrator to change the site URL.', 'wp-graphql' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see that the input field exists in settings, if so grab the option
|
||||
* name and update the option
|
||||
*/
|
||||
if ( array_key_exists( $key, $updatable_settings_options ) ) {
|
||||
update_option( $updatable_settings_options[ $key ]['option'], $value );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
] );
|
||||
|
||||
}
|
||||
|
||||
return ! empty( self::$mutation ) ? self::$mutation : null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the output of the UpdateSettings mutation.
|
||||
* This will build a combination of the setting and settings queries
|
||||
* so that the user can query the returned data by setting group or field
|
||||
*
|
||||
* @access protected
|
||||
*
|
||||
* @return array $output_fields
|
||||
*/
|
||||
protected static function output_fields() {
|
||||
|
||||
$output_fields = [];
|
||||
|
||||
/**
|
||||
* Get the allowed setting groups and their fields
|
||||
*/
|
||||
$allowed_setting_groups = DataSource::get_allowed_settings_by_group();
|
||||
if ( ! empty( $allowed_setting_groups ) && is_array( $allowed_setting_groups ) ) {
|
||||
foreach ( $allowed_setting_groups as $group => $setting_type ) {
|
||||
$setting_type = str_replace('_', '', strtolower( $group ) );
|
||||
$output_fields[ $setting_type . 'Settings' ] = SettingQuery::root_query( $group, $setting_type );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all of the settings, regardless of group
|
||||
*/
|
||||
$output_fields['allSettings'] = SettingsQuery::root_query();
|
||||
|
||||
return $output_fields;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
44
wordpress/wp-content/plugins/wp-graphql/src/Type/Settings/SettingsQuery.php
Executable file
44
wordpress/wp-content/plugins/wp-graphql/src/Type/Settings/SettingsQuery.php
Executable file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
namespace WPGraphQL\Type\Settings;
|
||||
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class SettingsQuery
|
||||
*
|
||||
* @package WPGraphQL\Type\Settings
|
||||
*/
|
||||
class SettingsQuery {
|
||||
|
||||
/**
|
||||
* Holds the root_query field definition
|
||||
*
|
||||
* @var array $root_query
|
||||
* @access private
|
||||
*/
|
||||
private static $root_query;
|
||||
|
||||
/**
|
||||
* Method that returns the root query field definition
|
||||
* for all settings
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return array $root_query
|
||||
*/
|
||||
public static function root_query() {
|
||||
|
||||
if ( null === self::$root_query ) {
|
||||
self::$root_query = [];
|
||||
}
|
||||
|
||||
self::$root_query = [
|
||||
'type' => Types::settings(),
|
||||
'resolve' => function () {
|
||||
return true;
|
||||
},
|
||||
];
|
||||
|
||||
return self::$root_query;
|
||||
}
|
||||
}
|
||||
156
wordpress/wp-content/plugins/wp-graphql/src/Type/Settings/SettingsType.php
Executable file
156
wordpress/wp-content/plugins/wp-graphql/src/Type/Settings/SettingsType.php
Executable file
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL\Type\Settings;
|
||||
|
||||
use GraphQL\Error\UserError;
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\Data\DataSource;
|
||||
use WPGraphQL\Type\WPObjectType;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class SettingsType
|
||||
*
|
||||
* This sets up the base settings Type for settings queries and mutations
|
||||
*
|
||||
* @package WPGraphQL\Type\Settings
|
||||
*/
|
||||
class SettingsType extends WPObjectType {
|
||||
|
||||
/**
|
||||
* Holds the type name
|
||||
*
|
||||
* @var string $type_name
|
||||
*/
|
||||
private static $type_name;
|
||||
|
||||
/**
|
||||
* Holds the $fields definition for the SettingsType
|
||||
*
|
||||
* @var array $fields
|
||||
* @access private
|
||||
*/
|
||||
private static $fields;
|
||||
|
||||
/**
|
||||
* SettingsType constructor.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function __construct() {
|
||||
|
||||
/**
|
||||
* Set the type_name
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
self::$type_name = 'Settings';
|
||||
|
||||
/**
|
||||
* Retrieve all of the allowed settings
|
||||
*/
|
||||
$settings_array = DataSource::get_allowed_settings();
|
||||
|
||||
$config = [
|
||||
'name' => self::$type_name,
|
||||
'fields' => self::fields( $settings_array ),
|
||||
'description' => __( 'All of the registered settings', 'wp-graphql' ),
|
||||
];
|
||||
|
||||
parent::__construct( $config );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This defines the fields for the settings type
|
||||
*
|
||||
* @param $settings_array
|
||||
*
|
||||
* @access private
|
||||
* @return \GraphQL\Type\Definition\FieldDefinition|mixed|null
|
||||
*/
|
||||
private static function fields( $settings_array ) {
|
||||
|
||||
/**
|
||||
* Define $fields
|
||||
*/
|
||||
$fields = [];
|
||||
|
||||
if ( ! empty( $settings_array ) && is_array( $settings_array ) ) {
|
||||
|
||||
/**
|
||||
* Loop through the $settings_array and build the setting with
|
||||
* proper fields
|
||||
*/
|
||||
foreach ( $settings_array as $key => $setting_field ) {
|
||||
|
||||
/**
|
||||
* Determine if the individual setting already has a
|
||||
* REST API name, if not use the option name.
|
||||
* Then, sanitize the field name to be camelcase
|
||||
*/
|
||||
if ( ! empty( $setting_field['show_in_rest']['name'] ) ) {
|
||||
$field_key = $setting_field['show_in_rest']['name'];
|
||||
} else {
|
||||
$field_key = $key;
|
||||
}
|
||||
$field_key = lcfirst( $setting_field['group'] . 'Settings' . str_replace( '_', '', ucwords( $field_key, '_' ) ) );
|
||||
|
||||
if ( ! empty( $key ) && ! empty( $field_key ) ) {
|
||||
|
||||
/**
|
||||
* Dynamically build the individual setting and it's fields
|
||||
* then add it to $fields
|
||||
*/
|
||||
$fields[ $field_key ] = [
|
||||
'type' => Types::get_type( $setting_field['type'] ),
|
||||
'description' => $setting_field['description'],
|
||||
|
||||
'resolve' => function( $root, $args, AppContext $context, ResolveInfo $info ) use ( $setting_field, $field_key, $key ) {
|
||||
/**
|
||||
* Check to see if the user querying the email field has the 'manage_options' capability
|
||||
* All other options should be public by default
|
||||
*/
|
||||
if ( 'admin_email' === $key && ! current_user_can( 'manage_options' ) ) {
|
||||
throw new UserError( __( 'Sorry, you do not have permission to view this setting.', 'wp-graphql' ) );
|
||||
}
|
||||
|
||||
$option = ! empty( $key ) ? get_option( $key ) : null;
|
||||
|
||||
switch ( $setting_field['type'] ) {
|
||||
case 'integer':
|
||||
$option = absint( $option );
|
||||
break;
|
||||
case 'string':
|
||||
$option = (string) $option;
|
||||
break;
|
||||
case 'boolean':
|
||||
$option = (boolean) $option;
|
||||
break;
|
||||
case 'number':
|
||||
$option = (float) $option;
|
||||
break;
|
||||
}
|
||||
|
||||
return $option;
|
||||
},
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass the fields through a filter to allow for hooking in and adjusting the shape
|
||||
* of the type's schema
|
||||
*/
|
||||
self::$fields = self::prepare_fields( $fields, self::$type_name );
|
||||
|
||||
}
|
||||
|
||||
return ! empty( self::$fields ) ? self::$fields : null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
263
wordpress/wp-content/plugins/wp-graphql/src/Type/Taxonomy/TaxonomyType.php
Executable file
263
wordpress/wp-content/plugins/wp-graphql/src/Type/Taxonomy/TaxonomyType.php
Executable file
@@ -0,0 +1,263 @@
|
||||
<?php
|
||||
namespace WPGraphQL\Type\Taxonomy;
|
||||
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQLRelay\Relay;
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\Type\WPObjectType;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class TaxonomyType
|
||||
* @package WPGraphQL\Type
|
||||
* @since 0.0.5
|
||||
*/
|
||||
class TaxonomyType extends WPObjectType {
|
||||
|
||||
/**
|
||||
* Holds the type name
|
||||
* @var string $type_name
|
||||
*/
|
||||
private static $type_name;
|
||||
|
||||
/**
|
||||
* This holds the field definitions
|
||||
* @var array $fields
|
||||
* @since 0.0.5
|
||||
*/
|
||||
private static $fields;
|
||||
|
||||
/**
|
||||
* TaxonomyType constructor.
|
||||
* @since 0.0.5
|
||||
*/
|
||||
public function __construct() {
|
||||
|
||||
/**
|
||||
* Set the type_name
|
||||
* @since 0.0.5
|
||||
*/
|
||||
self::$type_name = 'Taxonomy';
|
||||
|
||||
$config = [
|
||||
'name' => self::$type_name,
|
||||
'description' => __( 'A taxonomy object', 'wp-graphql' ),
|
||||
'fields' => self::fields(),
|
||||
'interfaces' => [ self::node_interface() ],
|
||||
];
|
||||
|
||||
parent::__construct( $config );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* fields
|
||||
*
|
||||
* This defines the fields for the TaxonomyType. The fields are passed through a filter so the shape of the schema
|
||||
* can be modified
|
||||
*
|
||||
* @return array|\GraphQL\Type\Definition\FieldDefinition[]
|
||||
* @since 0.0.5
|
||||
*/
|
||||
private function fields() {
|
||||
|
||||
if ( null === self::$fields ) {
|
||||
|
||||
/**
|
||||
* Get the post_types that are allowed in WPGraphQL
|
||||
*
|
||||
* @since 0.0.6
|
||||
*/
|
||||
$allowed_post_types = \WPGraphQL::$allowed_post_types;
|
||||
|
||||
self::$fields = function() use ( $allowed_post_types ) {
|
||||
$fields = [
|
||||
'id' => [
|
||||
'type' => Types::non_null( Types::id() ),
|
||||
'resolve' => function( $taxonomy, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ( ! empty( $info->parentType ) && ! empty( $taxonomy->name ) ) ? Relay::toGlobalId( 'taxonomy', $taxonomy->name ) : null;
|
||||
},
|
||||
],
|
||||
'name' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The display name of the taxonomy. This field is equivalent to WP_Taxonomy->label', 'wp-graphql' ),
|
||||
],
|
||||
'label' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Name of the taxonomy shown in the menu. Usually plural.', 'wp-graphql' ),
|
||||
],
|
||||
//@todo: add "labels" field
|
||||
'description' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Description of the taxonomy. This field is equivalent to WP_Taxonomy->description', 'wp-graphql' ),
|
||||
],
|
||||
'public' => [
|
||||
'type' => Types::boolean(),
|
||||
'description' => __( 'Whether the taxonomy is publicly queryable', 'wp-graphql' ),
|
||||
],
|
||||
'hierarchical' => [
|
||||
'type' => Types::boolean(),
|
||||
'description' => __( 'Whether the taxonomy is hierarchical', 'wp-graphql' ),
|
||||
],
|
||||
'showUi' => [
|
||||
'type' => Types::boolean(),
|
||||
'description' => __( 'Whether to generate and allow a UI for managing terms in this taxonomy in the admin', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Taxonomy $taxonomy, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ( true === $taxonomy->show_ui ) ? true : false;
|
||||
},
|
||||
],
|
||||
'showInMenu' => [
|
||||
'type' => Types::boolean(),
|
||||
'description' => __( 'Whether to show the taxonomy in the admin menu', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Taxonomy $taxonomy, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ( true === $taxonomy->show_in_menu ) ? true : false;
|
||||
},
|
||||
],
|
||||
'showInNavMenus' => [
|
||||
'type' => Types::boolean(),
|
||||
'description' => __( 'Whether the taxonomy is available for selection in navigation menus.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Taxonomy $taxonomy, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ( true === $taxonomy->show_in_nav_menus ) ? true : false;
|
||||
},
|
||||
],
|
||||
'showCloud' => [
|
||||
'type' => Types::boolean(),
|
||||
'description' => __( 'Whether to show the taxonomy as part of a tag cloud widget. This field is equivalent to WP_Taxonomy->show_tagcloud', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Taxonomy $taxonomy, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ( true === $taxonomy->show_tagcloud ) ? true : false;
|
||||
},
|
||||
],
|
||||
'showInQuickEdit' => [
|
||||
'type' => Types::boolean(),
|
||||
'description' => __( 'Whether to show the taxonomy in the quick/bulk edit panel.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Taxonomy $taxonomy, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ( true === $taxonomy->show_in_quick_edit ) ? true : false;
|
||||
},
|
||||
],
|
||||
'showInAdminColumn' => [
|
||||
'type' => Types::boolean(),
|
||||
'description' => __( 'Whether to display a column for the taxonomy on its post type listing screens.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Taxonomy $taxonomy, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ( true === $taxonomy->show_admin_column ) ? true : false;
|
||||
},
|
||||
],
|
||||
'showInRest' => [
|
||||
'type' => Types::boolean(),
|
||||
'description' => __( 'Whether to add the post type route in the REST API `wp/v2` namespace.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Taxonomy $taxonomy, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ( true === $taxonomy->show_in_rest ) ? true : false;
|
||||
},
|
||||
],
|
||||
'restBase' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Name of content type to diplay in REST API `wp/v2` namespace.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Taxonomy $taxonomy, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : null;
|
||||
},
|
||||
],
|
||||
'restControllerClass' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The REST Controller class assigned to handling this content type.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Taxonomy $taxonomy, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $taxonomy->rest_controller_class ) ? $taxonomy->rest_controller_class : null;
|
||||
},
|
||||
],
|
||||
'showInGraphql' => [
|
||||
'type' => Types::boolean(),
|
||||
'description' => __( 'Whether to add the post type to the GraphQL Schema.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Taxonomy $taxonomy, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ( true === $taxonomy->show_in_graphql ) ? true : false;
|
||||
},
|
||||
],
|
||||
'graphqlSingleName' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The singular name of the post type within the GraphQL Schema.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Taxonomy $taxonomy, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $taxonomy->graphql_single_name ) ? $taxonomy->graphql_single_name : null;
|
||||
},
|
||||
],
|
||||
'graphqlPluralName' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The plural name of the post type within the GraphQL Schema.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Taxonomy $taxonomy, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $taxonomy->graphql_plural_name ) ? $taxonomy->graphql_plural_name : null;
|
||||
},
|
||||
],
|
||||
'connectedPostTypeNames' => [
|
||||
'type' => Types::list_of( Types::string() ),
|
||||
'args' => [
|
||||
'types' => [
|
||||
'type' => Types::list_of( Types::post_type_enum() ),
|
||||
'description' => __( 'Select which post types to limit the results to', 'wp-graphql' ),
|
||||
],
|
||||
],
|
||||
'description' => __( 'A list of Post Types associated with the taxonomy', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Taxonomy $taxonomy, array $args, AppContext $context, ResolveInfo $info ) use ( $allowed_post_types ) {
|
||||
$post_type_names = [];
|
||||
|
||||
/**
|
||||
* If the types $arg is populated, use that to filter the $allowed_post_types,
|
||||
* otherwise use the default $allowed_post_types passed down
|
||||
*/
|
||||
$allowed_post_types = ! empty( $args['types'] ) && is_array( $args['types'] ) ? $args['types'] : $allowed_post_types;
|
||||
|
||||
$connected_post_types = $taxonomy->object_type;
|
||||
if ( ! empty( $connected_post_types ) && is_array( $connected_post_types ) ) {
|
||||
foreach ( $connected_post_types as $post_type ) {
|
||||
if ( in_array( $post_type, $allowed_post_types, true ) ) {
|
||||
$post_type_names[] = $post_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ! empty( $post_type_names ) ? $post_type_names : null;
|
||||
},
|
||||
],
|
||||
'connectedPostTypes' => [
|
||||
'type' => Types::list_of( Types::post_type() ),
|
||||
'args' => [
|
||||
'types' => [
|
||||
'type' => Types::list_of( Types::post_type_enum() ),
|
||||
'description' => __( 'Select which post types to limit the results to', 'wp-graphql' ),
|
||||
],
|
||||
],
|
||||
'description' => __( 'List of Post Types connected to the Taxonomy', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Taxonomy $taxonomy, array $args, AppContext $context, ResolveInfo $info ) use ( $allowed_post_types ) {
|
||||
|
||||
$post_type_objects = [];
|
||||
|
||||
/**
|
||||
* If the types $arg is populated, use that to filter the $allowed_post_types,
|
||||
* otherwise use the default $allowed_post_types passed down
|
||||
*/
|
||||
$allowed_post_types = ! empty( $args['types'] ) && is_array( $args['types'] ) ? $args['types'] : $allowed_post_types;
|
||||
|
||||
$connected_post_types = ! empty( $taxonomy->object_type ) ? $taxonomy->object_type : [];
|
||||
if ( ! empty( $allowed_post_types ) && is_array( $allowed_post_types ) ) {
|
||||
foreach ( $allowed_post_types as $post_type ) {
|
||||
if ( in_array( $post_type, $connected_post_types, true ) ) {
|
||||
$post_type_object = get_post_type_object( $post_type );
|
||||
$post_type_objects[ $post_type_object->graphql_single_name ] = $post_type_object;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ! empty( $post_type_objects ) ? $post_type_objects : null;
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* This prepares the fields by sorting them and applying a filter for adjusting the schema.
|
||||
* Because these fields are implemented via a closure the prepare_fields needs to be applied
|
||||
* to the fields directly instead of being applied to all objects extending
|
||||
* the WPObjectType class.
|
||||
*/
|
||||
return self::prepare_fields( $fields, self::$type_name );
|
||||
|
||||
};
|
||||
}
|
||||
return self::$fields;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
<?php
|
||||
namespace WPGraphQL\Type\TermObject\Connection;
|
||||
|
||||
use WPGraphQL\Type\WPEnumType;
|
||||
use WPGraphQL\Type\WPInputObjectType;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class TermObjectConnectionArgs
|
||||
*
|
||||
* This sets up the Query Args for term object connections, which uses get_terms, so this defines the allowed
|
||||
* input fields that will be passed to get_terms
|
||||
*
|
||||
* @package WPGraphQL\Type
|
||||
* @since 0.0.5
|
||||
*/
|
||||
class TermObjectConnectionArgs extends WPInputObjectType {
|
||||
|
||||
/**
|
||||
* This holds the field definitions
|
||||
* @var array $fields
|
||||
* @since 0.0.5
|
||||
*/
|
||||
public static $fields = [];
|
||||
|
||||
/**
|
||||
* Holds the orderby enum definition
|
||||
* @var $orderby_enum
|
||||
*/
|
||||
protected static $orderby_enum;
|
||||
|
||||
/**
|
||||
* TermObjectConnectionArgs constructor.
|
||||
*
|
||||
* @param array $config Array of config details for the Input Object Type
|
||||
* @param string $connection The name of the connection the args belong to
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
public function __construct( $config = [], $connection ) {
|
||||
$config['name'] = ucfirst( $connection ) . 'TermArgs';
|
||||
$config['queryClass'] = 'WP_Term_Query';
|
||||
$config['fields'] = self::fields( $connection );
|
||||
parent::__construct( $config );
|
||||
}
|
||||
|
||||
/**
|
||||
* fields
|
||||
*
|
||||
* This defines the fields that make up the TermObjectConnectionArgs
|
||||
*
|
||||
* @param string $connection The name of the connection that the fields args belong to
|
||||
* @return array
|
||||
* @since 0.0.5
|
||||
*/
|
||||
private static function fields( $connection ) {
|
||||
|
||||
if ( empty( self::$fields[ $connection ] ) ) {
|
||||
self::$fields[ $connection ] = [
|
||||
'objectIds' => [
|
||||
'type' => Types::list_of( Types::int() ),
|
||||
'description' => __( 'Array of object IDs. Results will be limited to terms associated with these objects.', 'wp-graphql' ),
|
||||
],
|
||||
'orderby' => [
|
||||
'type' => self::orderby_enum(),
|
||||
'description' => __( 'Field(s) to order terms by. Defaults to \'name\'.', 'wp-graphql' ),
|
||||
],
|
||||
'hideEmpty' => [
|
||||
'type' => Types::boolean(),
|
||||
'description' => __( 'Whether to hide terms not assigned to any posts. Accepts true or false. Default true', 'wp-graphql' ),
|
||||
],
|
||||
'include' => [
|
||||
'type' => Types::list_of( Types::int() ),
|
||||
'description' => __( 'Array of term ids to include. Default empty array.', 'wp-graphql' ),
|
||||
],
|
||||
'exclude' => [
|
||||
'type' => Types::list_of( Types::int() ),
|
||||
'description' => __( 'Array of term ids to exclude. If $include is non-empty, $exclude is ignored. Default empty array.', 'wp-graphql' ),
|
||||
],
|
||||
'excludeTree' => [
|
||||
'type' => Types::list_of( Types::int() ),
|
||||
'description' => __( 'Array of term ids to exclude along with all of their descendant terms. If $include is non-empty, $exclude_tree is ignored. Default empty array.', 'wp-graphql' ),
|
||||
],
|
||||
'name' => [
|
||||
'type' => Types::list_of( Types::string() ),
|
||||
'description' => __( 'Array of names to return term(s) for. Default empty.', 'wp-graphql' ),
|
||||
],
|
||||
'slug' => [
|
||||
'type' => Types::list_of( Types::string() ),
|
||||
'description' => __( 'Array of slugs to return term(s) for. Default empty.', 'wp-graphql' ),
|
||||
],
|
||||
'termTaxonomId' => [
|
||||
'type' => Types::list_of( Types::int() ),
|
||||
'description' => __( 'Array of term taxonomy IDs, to match when querying terms.', 'wp-graphql' ),
|
||||
],
|
||||
'hierarchical' => [
|
||||
'type' => Types::boolean(),
|
||||
'description' => __( 'Whether to include terms that have non-empty descendants (even if $hide_empty is set to true). Default true.', 'wp-graphql' ),
|
||||
],
|
||||
'search' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Search criteria to match terms. Will be SQL-formatted with wildcards before and after. Default empty.', 'wp-graphql' ),
|
||||
],
|
||||
'nameLike' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Retrieve terms with criteria by which a term is LIKE `$name__like`. Default empty.', 'wp-graphql' ),
|
||||
],
|
||||
'descriptionLike' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Retrieve terms where the description is LIKE `$description__like`. Default empty.', 'wp-graphql' ),
|
||||
],
|
||||
'padCounts' => [
|
||||
'type' => Types::boolean(),
|
||||
'description' => __( 'Whether to pad the quantity of a term\'s children in the quantity of each term\'s "count" object variable. Default false.', 'wp-graphql' ),
|
||||
],
|
||||
'childOf' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( 'Term ID to retrieve child terms of. If multiple taxonomies are passed, $child_of is ignored. Default 0.', 'wp-graphql' ),
|
||||
],
|
||||
'parent' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( 'Parent term ID to retrieve direct-child terms of. Default empty.', 'wp-graphql' ),
|
||||
],
|
||||
'childless' => [
|
||||
'type' => Types::boolean(),
|
||||
'description' => __( 'True to limit results to terms that have no children. This parameter has no effect on non-hierarchical taxonomies. Default false.', 'wp-graphql' ),
|
||||
],
|
||||
'cacheDomain' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Unique cache key to be produced when this query is stored in an object cache. Default is \'core\'.', 'wp-graphql' ),
|
||||
],
|
||||
'updateTermMetaCache' => [
|
||||
'type' => Types::boolean(),
|
||||
'description' => __( 'Whether to prime meta caches for matched terms. Default true.', 'wp-graphql' ),
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Add these fields to non-root connections
|
||||
*
|
||||
* @todo: possibly consider only adding these args to certain connections, like non-Root connections?
|
||||
*/
|
||||
self::$fields[ $connection ]['shouldOnlyIncludeConnectedItems'] = [
|
||||
'type' => Types::boolean(),
|
||||
'description' => __( 'Default false. If true, only the items connected to the source item will be returned. If false, all items will be returned regardless of connection to the source', 'wp-graphql' ),
|
||||
];
|
||||
|
||||
self::$fields[ $connection ]['shouldOutputInFlatList'] = [
|
||||
'type' => Types::boolean(),
|
||||
'description' => __( 'Default false. If true, the connection will be output in a flat list instead of the hierarchical list. So child terms will be output in the same level as the parent terms', 'wp-graphql' ),
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
return ! empty( self::$fields[ $connection ] ) ? self::prepare_fields( self::$fields[ $connection ], ucfirst( $connection ) . 'TermArgs' ) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the definition of the TermsOrderby enum
|
||||
* @return null|WPEnumType
|
||||
*/
|
||||
protected static function orderby_enum() {
|
||||
|
||||
if ( null === self::$orderby_enum ) {
|
||||
|
||||
self::$orderby_enum = new WPEnumType( [
|
||||
'name' => 'TermsOrderby',
|
||||
'values' => [
|
||||
'NAME' => [
|
||||
'value' => 'name',
|
||||
],
|
||||
'SLUG' => [
|
||||
'value' => 'slug',
|
||||
],
|
||||
'TERM_GROUP' => [
|
||||
'value' => 'term_group',
|
||||
],
|
||||
'TERM_ID' => [
|
||||
'value' => 'term_id',
|
||||
],
|
||||
'TERM_ORDER' => [
|
||||
'value' => 'term_order',
|
||||
],
|
||||
'DESCRIPTION' => [
|
||||
'value' => 'description',
|
||||
],
|
||||
'COUNT' => [
|
||||
'value' => 'count',
|
||||
],
|
||||
],
|
||||
] );
|
||||
|
||||
}
|
||||
|
||||
return ! empty( self::$orderby_enum ) ? self::$orderby_enum : null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
namespace WPGraphQL\Type\TermObject\Connection;
|
||||
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQLRelay\Relay;
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\Data\DataSource;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class TermObjectConnectionDefinition
|
||||
* @package WPGraphQL\Type\Comment\Connection
|
||||
* @since 0.0.5
|
||||
*/
|
||||
class TermObjectConnectionDefinition {
|
||||
|
||||
/**
|
||||
* Stores some date for the Relay connection for term objects
|
||||
*
|
||||
* @var array $connection
|
||||
* @since 0.0.5
|
||||
* @access private
|
||||
*/
|
||||
private static $connection = [];
|
||||
|
||||
/**
|
||||
* Method that sets up the relay connection for term objects
|
||||
*
|
||||
* @param object $taxonomy_object
|
||||
* @return mixed
|
||||
* @since 0.0.5
|
||||
*/
|
||||
public static function connection( $taxonomy_object, $from_type = 'Root' ) {
|
||||
|
||||
if ( empty( self::$connection[ $from_type ][ $taxonomy_object->name ] ) ) {
|
||||
/**
|
||||
* Setup the connectionDefinition
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
$connection = Relay::connectionDefinitions( [
|
||||
'nodeType' => Types::term_object( $taxonomy_object->name ),
|
||||
'name' => ucfirst( $from_type ) . ucfirst( $taxonomy_object->graphql_plural_name ),
|
||||
'connectionFields' => function() use ( $taxonomy_object ) {
|
||||
return [
|
||||
'taxonomyInfo' => [
|
||||
'type' => Types::taxonomy(),
|
||||
'description' => __( 'Information about the type of content being queried', 'wp-graphql' ),
|
||||
'resolve' => function( $source, array $args, AppContext $context, ResolveInfo $info ) use ( $taxonomy_object ) {
|
||||
return $taxonomy_object;
|
||||
},
|
||||
],
|
||||
'nodes' => [
|
||||
'type' => Types::list_of( Types::term_object( $taxonomy_object->name ) ),
|
||||
'description' => __( 'The nodes of the connection, without the edges', 'wp-graphql' ),
|
||||
'resolve' => function( $source, $args, $context, $info ) {
|
||||
return ! empty( $source['nodes'] ) ? $source['nodes'] : [];
|
||||
},
|
||||
],
|
||||
];
|
||||
},
|
||||
] );
|
||||
|
||||
/**
|
||||
* Add the "where" args to the termObjectConnections
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
$args[ $from_type ] = [
|
||||
'where' => [
|
||||
'name' => 'where',
|
||||
'type' => Types::term_object_query_args( ucfirst( $from_type ) . ucfirst( $taxonomy_object->graphql_plural_name ) ),
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Add the connection to the post_objects_connection object
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
self::$connection[ $from_type ][ $taxonomy_object->name ] = [
|
||||
'type' => $connection['connectionType'],
|
||||
'description' => sprintf( __( 'A collection of %s objects', 'wp-graphql' ), $taxonomy_object->graphql_plural_name ),
|
||||
'args' => array_merge( Relay::connectionArgs(), $args[ $from_type ] ),
|
||||
'resolve' => function( $source, array $args, AppContext $context, ResolveInfo $info ) use ( $taxonomy_object ) {
|
||||
return DataSource::resolve_term_objects_connection( $source, $args, $context, $info, $taxonomy_object->name );
|
||||
},
|
||||
];
|
||||
}
|
||||
return self::$connection[ $from_type ][ $taxonomy_object->name ];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,336 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL\Type\TermObject\Connection;
|
||||
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQLRelay\Connection\ArrayConnection;
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\Data\ConnectionResolver;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class TermObjectConnectionResolver
|
||||
*
|
||||
* @package WPGraphQL\Data\Resolvers
|
||||
* @since 0.0.5
|
||||
*/
|
||||
class TermObjectConnectionResolver extends ConnectionResolver {
|
||||
|
||||
/**
|
||||
* Stores the name of the taxonomy for the connection being resolved
|
||||
*
|
||||
* @var string $taxonomy
|
||||
*/
|
||||
public static $taxonomy;
|
||||
|
||||
/**
|
||||
* TermObjectConnectionResolver constructor.
|
||||
*
|
||||
* @param $taxonomy
|
||||
*/
|
||||
public function __construct( $taxonomy ) {
|
||||
self::$taxonomy = $taxonomy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of query_args to use in the WP_Term_Query to fetch the necessary terms for the connection
|
||||
*
|
||||
* @param $source
|
||||
* @param array $args
|
||||
* @param AppContext $context
|
||||
* @param ResolveInfo $info
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_query_args( $source, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
|
||||
/**
|
||||
* Set the taxonomy for the $args
|
||||
*/
|
||||
$query_args['taxonomy'] = ! empty( self::$taxonomy ) ? self::$taxonomy : 'category';
|
||||
|
||||
/**
|
||||
* Prepare for later use
|
||||
*/
|
||||
$last = ! empty( $args['last'] ) ? $args['last'] : null;
|
||||
$first = ! empty( $args['first'] ) ? $args['first'] : null;
|
||||
|
||||
/**
|
||||
* Set the default parent for TermObject Queries to be "0" to only get top level terms, unless
|
||||
* includeChildren is set
|
||||
*/
|
||||
$query_args['parent'] = 0;
|
||||
|
||||
/**
|
||||
* Set hide_empty as false by default
|
||||
*/
|
||||
$query_args['hide_empty'] = false;
|
||||
|
||||
/**
|
||||
* Set the number, ensuring it doesn't exceed the amount set as the $max_query_amount
|
||||
*/
|
||||
$query_args['number'] = min( max( absint( $first ), absint( $last ), 10 ), self::get_query_amount( $source, $args, $context, $info ) ) + 1;
|
||||
|
||||
/**
|
||||
* Orderby Name by default
|
||||
*/
|
||||
$query_args['orderby'] = 'name';
|
||||
|
||||
/**
|
||||
* Take any of the $args that were part of the GraphQL query and map their
|
||||
* GraphQL names to the WP_Term_Query names to be used in the WP_Term_Query
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
$input_fields = [];
|
||||
if ( ! empty( $args['where'] ) ) {
|
||||
$input_fields = self::sanitize_input_fields( $args['where'], $source, $args, $context, $info );
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the default $query_args with the $args that were entered
|
||||
* in the query.
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
if ( ! empty( $input_fields ) ) {
|
||||
$query_args = array_merge( $query_args, $input_fields );
|
||||
}
|
||||
|
||||
/**
|
||||
* If there's no orderby params in the inputArgs, set order based on the first/last argument
|
||||
*/
|
||||
if ( empty( $query_args['order'] ) ) {
|
||||
$query_args['order'] = ! empty( $last ) ? 'DESC' : 'ASC';
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the graphql_cursor_offset
|
||||
*/
|
||||
$query_args['graphql_cursor_offset'] = self::get_offset( $args );
|
||||
$query_args['graphql_cursor_compare'] = ( ! empty( $last ) ) ? '>' : '<';
|
||||
|
||||
/**
|
||||
* Pass the graphql $args to the WP_Query
|
||||
*/
|
||||
$query_args['graphql_args'] = $args;
|
||||
|
||||
/**
|
||||
* If the source of the Query is a Post object, adjust the query args to only query terms
|
||||
* connected to the post object
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
global $post;
|
||||
if ( true === is_object( $source ) ) {
|
||||
switch ( true ) {
|
||||
case $source instanceof \WP_Post:
|
||||
$post = $source;
|
||||
$post->shouldOnlyIncludeConnectedItems = isset( $input_fields['shouldOnlyIncludeConnectedItems'] ) ? $input_fields['shouldOnlyIncludeConnectedItems'] : true;
|
||||
$query_args['object_ids'] = $source->ID;
|
||||
break;
|
||||
case $source instanceof \WP_Term:
|
||||
$query_args['object_ids'] = $GLOBALS['post']->ID;
|
||||
$query_args['parent'] = ! empty( $source->term_id ) ? $source->term_id : 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* IF the connection is set to NOT ONLY include connected items (default behavior), unset the $object_ids arg
|
||||
*/
|
||||
if ( isset( $post->shouldOnlyIncludeConnectedItems ) && false === $post->shouldOnlyIncludeConnectedItems ) {
|
||||
unset( $query_args['object_ids'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* If the connection is set to output in a flat list, unset the parent
|
||||
*/
|
||||
if ( isset( $input_fields['shouldOutputInFlatList'] ) && true === $input_fields['shouldOutputInFlatList'] ){
|
||||
unset( $query_args['parent'] );
|
||||
$connected = wp_get_object_terms( $source->ID, self::$taxonomy, ['fields' => 'ids'] );
|
||||
$query_args['include'] = ! empty( $connected ) ? $connected : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the query_args that should be applied to the query. This filter is applied AFTER the input args from
|
||||
* the GraphQL Query have been applied and has the potential to override the GraphQL Query Input Args.
|
||||
*
|
||||
* @param array $query_args array of query_args being passed to the
|
||||
* @param mixed $source source passed down from the resolve tree
|
||||
* @param array $args array of arguments input in the field as part of the GraphQL query
|
||||
* @param AppContext $context object passed down the resolve tree
|
||||
* @param ResolveInfo $info info about fields passed down the resolve tree
|
||||
*
|
||||
* @since 0.0.6
|
||||
*/
|
||||
$query_args = apply_filters( 'graphql_term_object_connection_query_args', $query_args, $source, $args, $context, $info );
|
||||
|
||||
return $query_args;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This runs the query and returns the response
|
||||
*
|
||||
* @param $query_args
|
||||
*
|
||||
* @return \WP_Term_Query
|
||||
*/
|
||||
public static function get_query( $query_args ) {
|
||||
$query = new \WP_Term_Query( $query_args );
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* This gets the connection to return
|
||||
*
|
||||
* @param array|mixed $query The query that was processed to get data
|
||||
* @param array $items The array slice that was returned
|
||||
* @param mixed $source The source being passed down the resolve tress
|
||||
* @param array $args The input args for the resolving field
|
||||
* @param AppContext $context The context being passed down the resolve tree
|
||||
* @param ResolveInfo $info The ResolveInfo passed down the resolve tree
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_connection( $query, array $items, $source, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
|
||||
/**
|
||||
* Get the $posts from the query
|
||||
*/
|
||||
$items = ! empty( $items ) && is_array( $items ) ? $items : [];
|
||||
|
||||
/**
|
||||
* Set whether there is or is not another page
|
||||
*/
|
||||
$has_previous_page = ( ! empty( $args['last'] ) && count( $items ) > self::get_amount_requested( $args ) ) ? true : false;
|
||||
$has_next_page = ( ! empty( $args['first'] ) && count( $items ) > self::get_amount_requested( $args ) ) ? true : false;
|
||||
|
||||
/**
|
||||
* Slice the array to the amount of items that were requested
|
||||
*/
|
||||
$items = array_slice( $items, 0, self::get_amount_requested( $args ) );
|
||||
|
||||
/**
|
||||
* Get the edges from the $items
|
||||
*/
|
||||
$edges = self::get_edges( $items, $source, $args, $context, $info );
|
||||
|
||||
/**
|
||||
* Find the first_edge and last_edge
|
||||
*/
|
||||
$first_edge = $edges ? $edges[0] : null;
|
||||
$last_edge = $edges ? $edges[ count( $edges ) - 1 ] : null;
|
||||
|
||||
/**
|
||||
* Create the connection to return
|
||||
*/
|
||||
$connection = [
|
||||
'edges' => $edges,
|
||||
'debug' => [
|
||||
'queryRequest' => ! empty( $query->request ) ? $query->request : null,
|
||||
],
|
||||
'pageInfo' => [
|
||||
'hasPreviousPage' => $has_previous_page,
|
||||
'hasNextPage' => $has_next_page,
|
||||
'startCursor' => ! empty( $first_edge['cursor'] ) ? $first_edge['cursor'] : null,
|
||||
'endCursor' => ! empty( $last_edge['cursor'] ) ? $last_edge['cursor'] : null,
|
||||
],
|
||||
'nodes' => $items,
|
||||
];
|
||||
|
||||
return $connection;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an array of items and returns the edges
|
||||
*
|
||||
* @param $items
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_edges( $items, $source, $args, $context, $info ) {
|
||||
$edges = [];
|
||||
|
||||
/**
|
||||
* If we're doing backward pagination we want to reverse the array before
|
||||
* returning it to the edges
|
||||
*/
|
||||
if ( ! empty( $args['last'] ) ) {
|
||||
$items = array_reverse( $items );
|
||||
}
|
||||
|
||||
if ( ! empty( $items ) && is_array( $items ) ) {
|
||||
foreach ( $items as $item ) {
|
||||
$edges[] = [
|
||||
'cursor' => ArrayConnection::offsetToCursor( $item->term_id ),
|
||||
'node' => $item,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $edges;
|
||||
}
|
||||
|
||||
/**
|
||||
* This maps the GraphQL "friendly" args to get_terms $args.
|
||||
* There's probably a cleaner/more dynamic way to approach this, but this was quick. I'd be down
|
||||
* to explore more dynamic ways to map this, but for now this gets the job done.
|
||||
*
|
||||
* @param array $args Array of query "where" args
|
||||
* @param mixed $source The query results
|
||||
* @param array $all_args All of the query arguments (not just the "where" args)
|
||||
* @param AppContext $context The AppContext object
|
||||
* @param ResolveInfo $info The ResolveInfo object
|
||||
*
|
||||
* @since 0.0.5
|
||||
* @return array
|
||||
* @access public
|
||||
*/
|
||||
public static function sanitize_input_fields( array $args, $source, array $all_args, AppContext $context, ResolveInfo $info ) {
|
||||
|
||||
$arg_mapping = [
|
||||
'objectIds' => 'object_ids',
|
||||
'hideEmpty' => 'hide_empty',
|
||||
'excludeTree' => 'exclude_tree',
|
||||
'termTaxonomId' => 'term_taxonomy_id',
|
||||
'nameLike' => 'name__like',
|
||||
'descriptionLike' => 'description__like',
|
||||
'padCounts' => 'pad_counts',
|
||||
'childOf' => 'child_of',
|
||||
'cacheDomain' => 'cache_domain',
|
||||
'updateTermMetaCache' => 'update_term_meta_cache',
|
||||
];
|
||||
|
||||
/**
|
||||
* Map and sanitize the input args to the WP_Term_Query compatible args
|
||||
*/
|
||||
$query_args = Types::map_input( $args, $arg_mapping );
|
||||
|
||||
/**
|
||||
* Filter the input fields
|
||||
* This allows plugins/themes to hook in and alter what $args should be allowed to be passed
|
||||
* from a GraphQL Query to the get_terms query
|
||||
*
|
||||
* @param array $query_args Array of mapped query args
|
||||
* @param array $args Array of query "where" args
|
||||
* @param string $taxonomy The name of the taxonomy
|
||||
* @param mixed $source The query results
|
||||
* @param array $all_args All of the query arguments (not just the "where" args)
|
||||
* @param AppContext $context The AppContext object
|
||||
* @param ResolveInfo $info The ResolveInfo object
|
||||
*
|
||||
* @since 0.0.5
|
||||
* @return array
|
||||
*/
|
||||
$query_args = apply_filters( 'graphql_map_input_fields_to_get_terms', $query_args, $args, self::$taxonomy, $source, $all_args, $context, $info );
|
||||
|
||||
return ! empty( $query_args ) && is_array( $query_args ) ? $query_args : [];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL\Type\TermObject\Mutation;
|
||||
|
||||
use GraphQL\Error\UserError;
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQLRelay\Relay;
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
class TermObjectCreate {
|
||||
|
||||
/**
|
||||
* Holds the mutation field definition
|
||||
*
|
||||
* @var array $mutation
|
||||
*/
|
||||
private static $mutation = [];
|
||||
|
||||
/**
|
||||
* Defines the create mutation for TermObjects
|
||||
*
|
||||
* @param \WP_Taxonomy $taxonomy
|
||||
*
|
||||
* @return array|mixed
|
||||
*/
|
||||
public static function mutate( \WP_Taxonomy $taxonomy ) {
|
||||
|
||||
if (
|
||||
! empty( $taxonomy->graphql_single_name ) &&
|
||||
empty( self::$mutation[ $taxonomy->graphql_single_name ] )
|
||||
) {
|
||||
|
||||
/**
|
||||
* Set the name of the mutation being performed
|
||||
*/
|
||||
$mutation_name = 'Create' . ucwords( $taxonomy->graphql_single_name );
|
||||
|
||||
self::$mutation[ $taxonomy->graphql_single_name ] = Relay::mutationWithClientMutationId( [
|
||||
'name' => esc_html( $mutation_name ),
|
||||
// translators: The placeholder is the name of the object type
|
||||
'description' => sprintf( __( 'Create %1$s objects', 'wp-graphql' ), $taxonomy->name ),
|
||||
'inputFields' => self::input_fields( $taxonomy ),
|
||||
'outputFields' => [
|
||||
$taxonomy->graphql_single_name => [
|
||||
'type' => Types::term_object( $taxonomy->name ),
|
||||
// translators: Placeholder is the name of the taxonomy
|
||||
'description' => sprintf( __( 'The created %s', 'wp-graphql' ), $taxonomy->name ),
|
||||
'resolve' => function( $payload ) use ( $taxonomy ) {
|
||||
return get_term( $payload['id'], $taxonomy->name );
|
||||
},
|
||||
],
|
||||
],
|
||||
'mutateAndGetPayload' => function( $input, AppContext $context, ResolveInfo $info ) use ( $taxonomy, $mutation_name ) {
|
||||
|
||||
/**
|
||||
* Ensure the user can edit_terms
|
||||
*/
|
||||
if ( ! current_user_can( $taxonomy->cap->edit_terms ) ) {
|
||||
// translators: the $taxonomy->graphql_plural_name placeholder is the name of the object being mutated
|
||||
throw new UserError( sprintf( __( 'Sorry, you are not allowed to create %1$s', 'wp-graphql' ), $taxonomy->graphql_plural_name ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the object for insertion
|
||||
*/
|
||||
$args = TermObjectMutation::prepare_object( $input, $taxonomy, $mutation_name );
|
||||
|
||||
/**
|
||||
* Ensure a name was provided
|
||||
*/
|
||||
if ( empty( $args['name'] ) ) {
|
||||
// Translators: The placeholder is the name of the taxonomy of the term being mutated
|
||||
throw new UserError( sprintf( __( 'A name is required to create a %1$s' ), $taxonomy->name ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert the term
|
||||
*/
|
||||
$term = wp_insert_term( wp_slash( $args['name'] ), $taxonomy->name, wp_slash( (array) $args ) );
|
||||
|
||||
/**
|
||||
* If it was an error, return the message as an exception
|
||||
*/
|
||||
if ( is_wp_error( $term ) ) {
|
||||
$error_message = $term->get_error_message();
|
||||
if ( ! empty( $error_message ) ) {
|
||||
throw new UserError( esc_html( $error_message ) );
|
||||
} else {
|
||||
throw new UserError( __( 'The object failed to update but no error was provided', 'wp-graphql' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the response to creating the term didn't respond with a term_id, throw an exception
|
||||
*/
|
||||
if ( empty( $term['term_id'] ) ) {
|
||||
throw new UserError( __( 'The object failed to create', 'wp-graphql' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires after a single term is created or updated via a GraphQL mutation
|
||||
*
|
||||
* The dynamic portion of the hook name, `$taxonomy->name` refers to the taxonomy of the term being mutated
|
||||
*
|
||||
* @param int $term_id Inserted term object
|
||||
* @param array $args The args used to insert the term
|
||||
* @param string $mutation_name The name of the mutation being performed
|
||||
* @param AppContext $context The AppContext passed down the resolve tree
|
||||
* @param ResolveInfo $info The ResolveInfo passed down the resolve tree
|
||||
*/
|
||||
do_action( "graphql_insert_{$taxonomy->name}", $term['term_id'], $args, $mutation_name, $context, $info );
|
||||
|
||||
return [
|
||||
'id' => $term['term_id'],
|
||||
];
|
||||
|
||||
},
|
||||
] );
|
||||
|
||||
}
|
||||
|
||||
return ! empty( self::$mutation[ $taxonomy->graphql_single_name ] ) ? self::$mutation[ $taxonomy->graphql_single_name ] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the name as a nonNull field for create mutations
|
||||
*
|
||||
* @param \WP_Taxonomy $taxonomy
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private static function input_fields( $taxonomy ) {
|
||||
|
||||
/**
|
||||
* Add name as a non_null field for term creation
|
||||
*/
|
||||
return array_merge(
|
||||
[
|
||||
'name' => [
|
||||
'type' => Types::non_null( Types::string() ),
|
||||
// Translators: The placeholder is the name of the taxonomy for the object being mutated
|
||||
'description' => sprintf( __( 'The name of the %1$s object to mutate', 'wp-graphql' ), $taxonomy->name ),
|
||||
],
|
||||
],
|
||||
TermObjectMutation::input_fields( $taxonomy )
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL\Type\PostObject\Mutation;
|
||||
|
||||
use GraphQL\Error\UserError;
|
||||
use GraphQLRelay\Relay;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
class TermObjectDelete {
|
||||
|
||||
/**
|
||||
* Holds the mutation field definition
|
||||
*
|
||||
* @var array $mutation
|
||||
*/
|
||||
private static $mutation = [];
|
||||
|
||||
/**
|
||||
* Defines the update mutation for TermObjects
|
||||
*
|
||||
* @param \WP_Taxonomy $taxonomy
|
||||
*
|
||||
* @return array|mixed
|
||||
*/
|
||||
public static function mutate( \WP_Taxonomy $taxonomy ) {
|
||||
|
||||
if (
|
||||
! empty( $taxonomy->graphql_single_name ) &&
|
||||
empty( self::$mutation[ $taxonomy->graphql_single_name ] )
|
||||
) {
|
||||
|
||||
/**
|
||||
* Set the name of the mutation being performed
|
||||
*/
|
||||
$mutation_name = 'Delete' . ucwords( $taxonomy->graphql_single_name );
|
||||
|
||||
self::$mutation[ $taxonomy->graphql_single_name ] = Relay::mutationWithClientMutationId( [
|
||||
'name' => esc_html( $mutation_name ),
|
||||
// Translators: The placeholder is the taxonomy name of the term being deleted
|
||||
'description' => sprintf( esc_html__( 'Delete %1$s objects', 'wp-graphql' ), $taxonomy->graphql_single_name ),
|
||||
'inputFields' => [
|
||||
'id' => [
|
||||
'type' => Types::non_null( Types::id() ),
|
||||
// translators: The placeholder is the name of the taxonomy for the term being deleted
|
||||
'description' => sprintf( __( 'The ID of the %1$s to delete', 'wp-graphql' ), $taxonomy->graphql_single_name ),
|
||||
],
|
||||
],
|
||||
'outputFields' => [
|
||||
'deletedId' => [
|
||||
'type' => Types::id(),
|
||||
'description' => __( 'The ID of the deleted object', 'wp-graphql' ),
|
||||
'resolve' => function( $payload ) use ( $taxonomy ) {
|
||||
$deleted = (object) $payload['termObject'];
|
||||
|
||||
return ! empty( $deleted->term_id ) ? Relay::toGlobalId( $taxonomy->name, $deleted->term_id ) : null;
|
||||
},
|
||||
],
|
||||
$taxonomy->graphql_single_name => [
|
||||
'type' => Types::term_object( $taxonomy->name ),
|
||||
'description' => __( 'The object before it was deleted', 'wp-graphql' ),
|
||||
'resolve' => function( $payload ) use ( $taxonomy ) {
|
||||
$deleted = (object) $payload['termObject'];
|
||||
|
||||
return ! empty( $deleted ) ? $deleted : null;
|
||||
},
|
||||
],
|
||||
],
|
||||
'mutateAndGetPayload' => function( $input ) use ( $taxonomy, $mutation_name ) {
|
||||
|
||||
$id_parts = Relay::fromGlobalId( $input['id'] );
|
||||
|
||||
if ( ! empty( $id_parts['id'] ) && absint( $id_parts['id'] ) ) {
|
||||
$term_id = absint( $id_parts['id'] );
|
||||
} else {
|
||||
// Translators: The placeholder is the name of the taxonomy for the term being deleted
|
||||
throw new UserError( sprintf( __( 'The ID for the %1$s was not valid', 'wp-graphql' ), $taxonomy->graphql_single_name ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the type for the Global ID matches the type being mutated
|
||||
*/
|
||||
if ( empty( $id_parts['type'] ) || $taxonomy->name !== $id_parts['type'] ) {
|
||||
// Translators: The placeholder is the name of the taxonomy for the term being edited
|
||||
throw new UserError( sprintf( __( 'The ID passed is not for a %1$s object', 'wp-graphql' ), $taxonomy->graphql_single_name ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the term before deleting it
|
||||
*/
|
||||
$term_object = get_term( $term_id, $taxonomy->name );
|
||||
|
||||
/**
|
||||
* Ensure the user can delete terms of this taxonomy
|
||||
*/
|
||||
if ( ! current_user_can( 'delete_term', $term_object->term_id ) ) {
|
||||
// Translators: The placeholder is the name of the taxonomy for the term being deleted
|
||||
throw new UserError( sprintf( __( 'You do not have permission to delete %1$s', 'wp-graphql' ), $taxonomy->graphql_plural_name ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the term and get the response
|
||||
*/
|
||||
$deleted = wp_delete_term( $term_id, $taxonomy->name );
|
||||
|
||||
/**
|
||||
* If there was an error deleting the term, get the error message and return it
|
||||
*/
|
||||
if ( is_wp_error( $deleted ) ) {
|
||||
$error_message = $deleted->get_error_message();
|
||||
if ( ! empty( $error_message ) ) {
|
||||
throw new UserError( esc_html( $error_message ) );
|
||||
} else {
|
||||
// Translators: The placeholder is the name of the taxonomy for the term being deleted
|
||||
throw new UserError( sprintf( __( 'The %1$s failed to delete but no error was provided', 'wp-graphql' ), $taxonomy->name ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the term object that was retrieved prior to deletion
|
||||
*/
|
||||
return [
|
||||
'termObject' => $term_object,
|
||||
];
|
||||
|
||||
},
|
||||
] );
|
||||
|
||||
}
|
||||
|
||||
return ! empty( self::$mutation[ $taxonomy->graphql_single_name ] ) ? self::$mutation[ $taxonomy->graphql_single_name ] : null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL\Type\TermObject\Mutation;
|
||||
|
||||
use GraphQL\Error\UserError;
|
||||
use GraphQLRelay\Relay;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
class TermObjectMutation {
|
||||
|
||||
/**
|
||||
* Holds the input_fields configuration
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $input_fields;
|
||||
|
||||
/**
|
||||
* @param \WP_Taxonomy $taxonomy
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public static function input_fields( \WP_Taxonomy $taxonomy ) {
|
||||
|
||||
if ( ! empty( $taxonomy->name ) && empty( self::$input_fields[ $taxonomy->name ] ) ) {
|
||||
|
||||
$input_fields = [
|
||||
'name' => [
|
||||
'type' => Types::string(),
|
||||
// Translators: The placeholder is the name of the taxonomy for the object being mutated
|
||||
'description' => sprintf( __( 'The name of the %1$s object to mutate', 'wp-graphql' ), $taxonomy->name ),
|
||||
],
|
||||
'aliasOf' => [
|
||||
'type' => Types::string(),
|
||||
// Translators: The placeholder is the name of the taxonomy for the object being mutated
|
||||
'description' => sprintf( __( 'The slug that the %1$s will be an alias of', 'wp-graphql' ), $taxonomy->name ),
|
||||
],
|
||||
'description' => [
|
||||
'type' => Types::string(),
|
||||
// Translators: The placeholder is the name of the taxonomy for the object being mutated
|
||||
'description' => sprintf( __( 'The description of the %1$s object', 'wp-graphql' ), $taxonomy->name ),
|
||||
],
|
||||
'slug' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'If this argument exists then the slug will be checked to see if it is not an existing valid term. If that check succeeds (it is not a valid term), then it is added and the term id is given. If it fails, then a check is made to whether the taxonomy is hierarchical and the parent argument is not empty. If the second check succeeds, the term will be inserted and the term id will be given. If the slug argument is empty, then it will be calculated from the term name.' ),
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Add a parentId field to hierarchical taxonomies to allow parents to be set
|
||||
*/
|
||||
if ( true === $taxonomy->hierarchical ) {
|
||||
$input_fields['parentId'] = [
|
||||
'type' => Types::id(),
|
||||
// Translators: The placeholder is the name of the taxonomy for the object being mutated
|
||||
'description' => sprintf( __( 'The ID of the %1$s that should be set as the parent', 'wp-graphql' ), $taxonomy->name ),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the mutation input fields for the object type
|
||||
*
|
||||
* @param array $input_fields The array of input fields
|
||||
* @param \WP_Taxonomy The taxonomy of the Term object being mutated
|
||||
*/
|
||||
self::$input_fields[ $taxonomy->name ] = apply_filters( 'graphql_term_object_mutation_input_fields', $input_fields, $taxonomy );
|
||||
|
||||
} // End if().
|
||||
|
||||
return ! empty( self::$input_fields[ $taxonomy->name ] ) ? self::$input_fields[ $taxonomy->name ] : null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This prepares the object to be mutated – ensures data is safe to be saved,
|
||||
* and mapped from input args to WordPress $args
|
||||
*
|
||||
* @param array $input The input from the GraphQL Request
|
||||
* @param \WP_Taxonomy $taxonomy The Taxonomy object for the type of term being mutated
|
||||
* @param string $mutation_name The name of the mutation (create, update, etc)
|
||||
*
|
||||
* @throws \Exception
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function prepare_object( $input, \WP_Taxonomy $taxonomy, $mutation_name ) {
|
||||
|
||||
/**
|
||||
* Set the taxonomy for insert
|
||||
*/
|
||||
$insert_args['taxonomy'] = $taxonomy->name;
|
||||
|
||||
/**
|
||||
* Prepare the data for inserting the term
|
||||
*/
|
||||
if ( ! empty( $input['aliasOf'] ) ) {
|
||||
$insert_args['alias_of'] = $input['aliasOf'];
|
||||
}
|
||||
|
||||
if ( ! empty( $input['name'] ) ) {
|
||||
$insert_args['name'] = esc_sql( $input['name'] );
|
||||
}
|
||||
|
||||
if ( ! empty( $input['description'] ) ) {
|
||||
$insert_args['description'] = esc_sql( $input['description'] );
|
||||
}
|
||||
|
||||
if ( ! empty( $input['slug'] ) ) {
|
||||
$insert_args['slug'] = esc_sql( $input['slug'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* If the parentId argument was entered, we need to validate that it's actually a legit term that can
|
||||
* be set as a parent
|
||||
*/
|
||||
if ( ! empty( $input['parentId'] ) ) {
|
||||
|
||||
/**
|
||||
* Convert parent ID to WordPress ID
|
||||
*/
|
||||
$parent_id_parts = ! empty( $input['parentId'] ) ? Relay::fromGlobalId( $input['parentId'] ) : null;
|
||||
|
||||
/**
|
||||
* Ensure that the ID passed in is a valid GlobalID
|
||||
*/
|
||||
if ( is_array( $parent_id_parts ) && ! empty( $parent_id_parts['id'] ) ) {
|
||||
|
||||
/**
|
||||
* Get the Term ID from the global ID
|
||||
*/
|
||||
$parent_id = $parent_id_parts['id'];
|
||||
|
||||
/**
|
||||
* Ensure there's actually a parent term to be associated with
|
||||
*/
|
||||
$parent_term = get_term( absint( $parent_id ), $taxonomy->name );
|
||||
|
||||
if ( ! $parent_term || is_wp_error( $parent_term ) ) {
|
||||
throw new UserError( __( 'The parent does not exist', 'wp-graphql' ) );
|
||||
}
|
||||
|
||||
// Otherwise set the parent as the parent term's ID
|
||||
$insert_args['parent'] = $parent_term->term_id;
|
||||
|
||||
} else {
|
||||
throw new UserError( __( 'The parent ID is not a valid ID', 'wp-graphql' ) );
|
||||
} // End if().
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the $insert_args
|
||||
*
|
||||
* @param array $insert_args The array of input args that will be passed to the functions that insert terms
|
||||
* @param array $input The data that was entered as input for the mutation
|
||||
* @param \WP_Taxonomy $taxonomy The taxonomy object of the term being mutated
|
||||
* @param string $mutation_name The name of the mutation being performed (create, edit, etc)
|
||||
*/
|
||||
$insert_args = apply_filters( 'graphql_term_object_insert_term_args', $insert_args, $input, $taxonomy, $mutation_name );
|
||||
|
||||
/**
|
||||
* Return the $args
|
||||
*/
|
||||
return $insert_args;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL\Type\TermObject\Mutation;
|
||||
|
||||
use GraphQL\Error\UserError;
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQLRelay\Relay;
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
class TermObjectUpdate {
|
||||
|
||||
/**
|
||||
* Holds the mutation field definition
|
||||
*
|
||||
* @var array $mutation
|
||||
*/
|
||||
private static $mutation = [];
|
||||
|
||||
/**
|
||||
* Defines the update mutation for TermObjects
|
||||
*
|
||||
* @param \WP_Taxonomy $taxonomy
|
||||
*
|
||||
* @return array|mixed
|
||||
*/
|
||||
public static function mutate( \WP_Taxonomy $taxonomy ) {
|
||||
|
||||
if (
|
||||
! empty( $taxonomy->graphql_single_name )
|
||||
&& empty( self::$mutation[ $taxonomy->graphql_single_name ] )
|
||||
) {
|
||||
|
||||
$mutation_name = 'Update' . ucwords( $taxonomy->graphql_single_name );
|
||||
|
||||
self::$mutation[ $taxonomy->graphql_single_name ] = Relay::mutationWithClientMutationId( [
|
||||
'name' => esc_html( $mutation_name ),
|
||||
// translators: The placeholder is the name of the post type being updated
|
||||
'description' => sprintf( esc_html__( 'Updates %1$s objects', 'wp-graphql' ), $taxonomy->graphql_single_name ),
|
||||
'inputFields' => self::input_fields( $taxonomy ),
|
||||
'outputFields' => [
|
||||
$taxonomy->graphql_single_name => [
|
||||
'type' => Types::term_object( $taxonomy->name ),
|
||||
'resolve' => function( $payload ) use ( $taxonomy ) {
|
||||
return get_term( $payload['term_id'], $taxonomy->name );
|
||||
},
|
||||
],
|
||||
],
|
||||
'mutateAndGetPayload' => function( $input, AppContext $context, ResolveInfo $info ) use ( $taxonomy, $mutation_name ) {
|
||||
|
||||
/**
|
||||
* Get the ID parts
|
||||
*/
|
||||
$id_parts = ! empty( $input['id'] ) ? Relay::fromGlobalId( $input['id'] ) : null;
|
||||
|
||||
/**
|
||||
* Ensure the type for the Global ID matches the type being mutated
|
||||
*/
|
||||
if ( empty( $id_parts['type'] ) || $taxonomy->name !== $id_parts['type'] ) {
|
||||
// Translators: The placeholder is the name of the taxonomy for the term being edited
|
||||
throw new UserError( sprintf( __( 'The ID passed is not for a %1$s object', 'wp-graphql' ), $taxonomy->graphql_single_name ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the existing term
|
||||
*/
|
||||
$existing_term = get_term( absint( $id_parts['id'] ), $taxonomy->name );
|
||||
|
||||
/**
|
||||
* If there was an error getting the existing term, return the error message
|
||||
*/
|
||||
if ( is_wp_error( $existing_term ) ) {
|
||||
$error_message = $existing_term->get_error_message();
|
||||
if ( ! empty( $error_message ) ) {
|
||||
throw new UserError( esc_html( $error_message ) );
|
||||
} else {
|
||||
// Translators: The placeholder is the name of the taxonomy for the term being deleted
|
||||
throw new UserError( sprintf( __( 'The %1$s failed to update', 'wp-graphql' ), $taxonomy->name ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the user has permission to edit terms
|
||||
*/
|
||||
if ( ! current_user_can( 'edit_term', $existing_term->term_id ) ) {
|
||||
// Translators: The placeholder is the name of the taxonomy for the term being deleted
|
||||
throw new UserError( sprintf( __( 'You do not have permission to update %1$s', 'wp-graphql' ), $taxonomy->graphql_plural_name ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the $args for mutation
|
||||
*/
|
||||
$args = TermObjectMutation::prepare_object( $input, $taxonomy, $mutation_name );
|
||||
|
||||
if ( ! empty( $args ) ) {
|
||||
|
||||
/**
|
||||
* Update the term
|
||||
*/
|
||||
$update = wp_update_term( $existing_term->term_id, $taxonomy->name, wp_slash( (array) $args ) );
|
||||
|
||||
/**
|
||||
* Respond with any errors
|
||||
*/
|
||||
if ( is_wp_error( $update ) ) {
|
||||
// Translators: the placeholder is the name of the taxonomy
|
||||
throw new UserError( sprintf( __( 'The %1$s failed to update', 'wp-graphql' ), $taxonomy->name ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires an action when a term is updated via a GraphQL Mutation
|
||||
*
|
||||
* @param int $term_id The ID of the term object that was mutated
|
||||
* @param array $args The args used to update the term
|
||||
* @param string $mutation_name The name of the mutation being performed (create, update, delete, etc)
|
||||
* @param AppContext $context The AppContext passed down the resolve tree
|
||||
* @param ResolveInfo $info The ResolveInfo passed down the resolve tree
|
||||
*/
|
||||
do_action( "graphql_update_{$taxonomy->name}", $existing_term->term_id, $args, $mutation_name, $context, $info );
|
||||
|
||||
/**
|
||||
* Return the payload
|
||||
*/
|
||||
return [
|
||||
'term_id' => $existing_term->term_id,
|
||||
];
|
||||
|
||||
},
|
||||
] );
|
||||
|
||||
}
|
||||
|
||||
return ! empty( self::$mutation[ $taxonomy->graphql_single_name ] ) ? self::$mutation[ $taxonomy->graphql_single_name ] : null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the id as an optional field for update mutations
|
||||
*
|
||||
* @param \WP_Taxonomy $taxonomy
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private static function input_fields( $taxonomy ) {
|
||||
|
||||
/**
|
||||
* Add name as a non_null field for term creation
|
||||
*/
|
||||
return array_merge(
|
||||
[
|
||||
'name' => [
|
||||
'type' => Types::string(),
|
||||
// Translators: The placeholder is the name of the taxonomy for the object being mutated
|
||||
'description' => sprintf( __( 'The name of the %1$s object to mutate', 'wp-graphql' ), $taxonomy->name ),
|
||||
],
|
||||
'id' => [
|
||||
'type' => Types::non_null( Types::id() ),
|
||||
// Translators: The placeholder is the taxonomy of the term being updated
|
||||
'description' => sprintf( __( 'The ID of the %1$s object to update', 'wp-graphql' ), $taxonomy->graphql_single_name ),
|
||||
],
|
||||
],
|
||||
TermObjectMutation::input_fields( $taxonomy )
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
namespace WPGraphQL\Type\TermObject;
|
||||
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQLRelay\Relay;
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\Data\DataSource;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class TermObjectQuery
|
||||
* @package WPGraphQL\Type\TermObject
|
||||
* @Since 0.0.5
|
||||
*/
|
||||
class TermObjectQuery {
|
||||
|
||||
/**
|
||||
* Holds the root_query field definition
|
||||
* @var array $root_query
|
||||
* @since 0.0.5
|
||||
*/
|
||||
private static $root_query = [];
|
||||
|
||||
/**
|
||||
* Method that returns the root query field definition for the post object type
|
||||
*
|
||||
* @param object $taxonomy_object
|
||||
* @return array
|
||||
* @since 0.0.5
|
||||
*/
|
||||
public static function root_query( $taxonomy_object ) {
|
||||
|
||||
if ( ! empty( $taxonomy_object->name ) && empty( self::$root_query[ $taxonomy_object->name ] ) ) {
|
||||
self::$root_query[ $taxonomy_object->name ] = [
|
||||
'type' => Types::term_object( $taxonomy_object->name ),
|
||||
'description' => sprintf( __( 'A % object', 'wp-graphql' ), $taxonomy_object->graphql_single_name ),
|
||||
'args' => [
|
||||
'id' => Types::non_null( Types::id() ),
|
||||
],
|
||||
'resolve' => function( $source, array $args, AppContext $context, ResolveInfo $info ) use ( $taxonomy_object ) {
|
||||
$id_components = Relay::fromGlobalId( $args['id'] );
|
||||
|
||||
return DataSource::resolve_term_object( $id_components['id'], $taxonomy_object->name );
|
||||
},
|
||||
];
|
||||
|
||||
return self::$root_query[ $taxonomy_object->name ];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
249
wordpress/wp-content/plugins/wp-graphql/src/Type/TermObject/TermObjectType.php
Executable file
249
wordpress/wp-content/plugins/wp-graphql/src/Type/TermObject/TermObjectType.php
Executable file
@@ -0,0 +1,249 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL\Type\TermObject;
|
||||
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQLRelay\Relay;
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\Data\DataSource;
|
||||
use WPGraphQL\Type\PostObject\Connection\PostObjectConnectionDefinition;
|
||||
use WPGraphQL\Type\TermObject\Connection\TermObjectConnectionDefinition;
|
||||
use WPGraphQL\Type\TermObject\Connection\TermObjectConnectionResolver;
|
||||
use WPGraphQL\Type\WPObjectType;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class TermObjectType
|
||||
*
|
||||
* This sets up the base TermObjectType. Custom taxonomies that are set to "show_in_graphql" will automatically
|
||||
* use the TermObjectType and inherit the fields that are defined here. The fields get passed through a
|
||||
* filter unique to each taxonomy, so each taxonomy can modify it's term schema via field filters.
|
||||
*
|
||||
* NOTE: In some cases, (probably rare, I would guess) the "shape" of a custom taxonomy term might not make sense to
|
||||
* inherit the fields defined here, so it might make sense for a Taxonomy to register it's own custom defined type
|
||||
* for it's terms instead of utilizing the TermObjectType.
|
||||
*
|
||||
* @package WPGraphQL\Type
|
||||
* @since 0.0.5
|
||||
*/
|
||||
class TermObjectType extends WPObjectType {
|
||||
|
||||
/**
|
||||
* Holds the $fields definition for the TermObjectType
|
||||
*
|
||||
* @var $fields
|
||||
*/
|
||||
private static $fields = [];
|
||||
|
||||
/**
|
||||
* Holds the $taxonomy_object
|
||||
*
|
||||
* @var $taxonomy_object
|
||||
*/
|
||||
private static $taxonomy_object;
|
||||
|
||||
/**
|
||||
* TermObjectType constructor.
|
||||
*
|
||||
* @param string $taxonomy The taxonomy name
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
public function __construct( $taxonomy ) {
|
||||
|
||||
/**
|
||||
* Get the taxonomy object and store it
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
self::$taxonomy_object = get_taxonomy( $taxonomy );
|
||||
|
||||
$config = [
|
||||
'name' => ucfirst( self::$taxonomy_object->graphql_single_name ),
|
||||
'description' => sprintf( __( 'The % object type', 'wp-graphql' ), self::$taxonomy_object->graphql_single_name ),
|
||||
'fields' => self::fields( self::$taxonomy_object ),
|
||||
'interfaces' => [ self::node_interface() ],
|
||||
];
|
||||
parent::__construct( $config );
|
||||
}
|
||||
|
||||
/**
|
||||
* fields
|
||||
*
|
||||
* This defines the fields for TermObjectType
|
||||
*
|
||||
* @param \WP_Taxonomy $taxonomy_object
|
||||
* @return \GraphQL\Type\Definition\FieldDefinition|mixed|null
|
||||
* @since 0.0.5
|
||||
*/
|
||||
private static function fields( $taxonomy_object ) {
|
||||
|
||||
/**
|
||||
* Get the $single_name out of the taxonomy_object
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
$single_name = self::$taxonomy_object->graphql_single_name;
|
||||
|
||||
/**
|
||||
* If the $fields haven't already been defined for this type,
|
||||
* define the fields
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
if ( empty( self::$fields[ $single_name ] ) ) {
|
||||
|
||||
/**
|
||||
* Get the post_types and taxonomies that are allowed
|
||||
* in WPGraphQL
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
$allowed_post_types = \WPGraphQL::$allowed_post_types;
|
||||
|
||||
/**
|
||||
* Define the fields for the terms of the specified taxonomy
|
||||
*
|
||||
* @return mixed
|
||||
* @since 0.0.5
|
||||
*/
|
||||
self::$fields[ $single_name ] = function() use ( $single_name, $taxonomy_object, $allowed_post_types ) {
|
||||
$fields = [
|
||||
'id' => [
|
||||
'type' => Types::non_null( Types::id() ),
|
||||
# Placeholder is the name of the taxonomy
|
||||
'description' => __( 'The global ID for the ' . $taxonomy_object->name, 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Term $term, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ( ! empty( $term->taxonomy ) && ! empty( $term->term_id ) ) ? Relay::toGlobalId( $term->taxonomy, $term->term_id ) : null;
|
||||
},
|
||||
],
|
||||
$single_name . 'Id' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( 'The id field matches the WP_Post->ID field.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Term $term, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $term->term_id ) ? absint( $term->term_id ) : null;
|
||||
},
|
||||
],
|
||||
'count' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( 'The number of objects connected to the object', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Term $term, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $term->count ) ? absint( $term->count ) : null;
|
||||
},
|
||||
],
|
||||
'description' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The description of the object', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Term $term, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $term->description ) ? $term->description : null;
|
||||
},
|
||||
],
|
||||
'name' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The human friendly name of the object.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Term $term, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $term->name ) ? $term->name : null;
|
||||
},
|
||||
],
|
||||
'slug' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'An alphanumeric identifier for the object unique to its type.', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Term $term, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $term->slug ) ? $term->slug : null;
|
||||
},
|
||||
],
|
||||
'termGroupId' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( 'The ID of the term group that this term object belongs to', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Term $term, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $term->term_group ) ? absint( $term->term_group ) : null;
|
||||
},
|
||||
],
|
||||
'termTaxonomyId' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( 'The taxonomy ID that the object is associated with', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Term $term, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $term->term_taxonomy_id ) ? absint( $term->term_taxonomy_id ) : null;
|
||||
},
|
||||
],
|
||||
'taxonomy' => [
|
||||
'type' => Types::taxonomy(),
|
||||
'description' => __( 'The name of the taxonomy this term belongs to', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Term $term, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
$taxonomy = get_taxonomy( $term->taxonomy );
|
||||
|
||||
return ! empty( $term->taxonomy ) && false !== $taxonomy ? $taxonomy : null;
|
||||
},
|
||||
],
|
||||
'link' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The link to the term', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Term $term, $args, AppContext $context, ResolveInfo $info ) {
|
||||
$link = get_term_link( $term->term_id );
|
||||
|
||||
return ( ! is_wp_error( $link ) ) ? $link : null;
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* For hierarchical taxonomies, provide parent and ancestor fields
|
||||
*/
|
||||
if ( true === $taxonomy_object->hierarchical ) {
|
||||
$fields['parent'] = [
|
||||
'type' => Types::term_object( $taxonomy_object->name ),
|
||||
'description' => __( 'The parent object', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Term $term, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return ! empty( $term->parent ) ? DataSource::resolve_term_object( $term->parent, $term->taxonomy ) : null;
|
||||
},
|
||||
];
|
||||
|
||||
$fields['ancestors'] = [
|
||||
'type' => Types::list_of( Types::term_object( $taxonomy_object->name ) ),
|
||||
'description' => esc_html__( 'The ancestors of the object', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Term $term, $args, AppContext $context, ResolveInfo $info ) {
|
||||
$ancestors = [];
|
||||
$ancestor_ids = get_ancestors( $term->term_id, $term->taxonomy );
|
||||
if ( ! empty( $ancestor_ids ) ) {
|
||||
foreach ( $ancestor_ids as $ancestor_id ) {
|
||||
$ancestors[] = get_term( $ancestor_id );
|
||||
}
|
||||
}
|
||||
|
||||
return ! empty( $ancestors ) ? $ancestors : null;
|
||||
},
|
||||
];
|
||||
|
||||
$fields['children'] = TermObjectConnectionDefinition::connection( $taxonomy_object, 'Children' );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Add connections for post_types that are registered to the taxonomy
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
if ( ! empty( $allowed_post_types ) && is_array( $allowed_post_types ) ) {
|
||||
foreach ( $allowed_post_types as $post_type ) {
|
||||
if ( in_array( $post_type, $taxonomy_object->object_type, true ) ) {
|
||||
$post_type_object = get_post_type_object( $post_type );
|
||||
$fields[ $post_type_object->graphql_plural_name ] = PostObjectConnectionDefinition::connection( $post_type_object, $taxonomy_object->graphql_single_name );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This prepares the fields by sorting them and applying a filter for adjusting the schema.
|
||||
* Because these fields are implemented via a closure the prepare_fields needs to be applied
|
||||
* to the fields directly instead of being applied to all objects extending
|
||||
* the WPObjectType class.
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
return self::prepare_fields( $fields, $single_name );
|
||||
|
||||
};
|
||||
}
|
||||
return ! empty( self::$fields[ $single_name ] ) ? self::$fields[ $single_name ] : null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
namespace WPGraphQL\Type\Theme\Connection;
|
||||
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQLRelay\Relay;
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\Data\DataSource;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class ThemeConnectionDefinition
|
||||
* @package WPGraphQL\Type\Comment\Connection
|
||||
* @since 0.0.5
|
||||
*/
|
||||
class ThemeConnectionDefinition {
|
||||
|
||||
/**
|
||||
* Stores some date for the Relay connection for term objects
|
||||
*
|
||||
* @var array $connection
|
||||
* @since 0.0.5
|
||||
* @access private
|
||||
*/
|
||||
private static $connection;
|
||||
|
||||
/**
|
||||
* Method that sets up the relay connection for term objects
|
||||
* @param string $from_type The name of the Type the connection is coming from
|
||||
* @return mixed
|
||||
* @since 0.0.5
|
||||
*/
|
||||
public static function connection( $from_type = 'Root' ) {
|
||||
|
||||
if ( null === self::$connection ) {
|
||||
self::$connection = [];
|
||||
}
|
||||
|
||||
if ( empty( self::$connection[ $from_type ] ) ) {
|
||||
/**
|
||||
* Setup the connectionDefinition
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
$connection = Relay::connectionDefinitions( [
|
||||
'nodeType' => Types::theme(),
|
||||
'name' => ucfirst( $from_type ) . 'Themes',
|
||||
'connectionFields' => function() {
|
||||
return [
|
||||
'nodes' => [
|
||||
'type' => Types::list_of( Types::theme() ),
|
||||
'description' => __( 'The nodes of the connection, without the edges', 'wp-graphql' ),
|
||||
'resolve' => function( $source, $args, $context, $info ) {
|
||||
return ! empty( $source['nodes'] ) ? $source['nodes'] : [];
|
||||
},
|
||||
],
|
||||
];
|
||||
},
|
||||
] );
|
||||
|
||||
/**
|
||||
* Add the connection to the themes_connection object
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
self::$connection[ $from_type ] = [
|
||||
'type' => $connection['connectionType'],
|
||||
'description' => __( 'A collection of theme objects', 'wp-graphql' ),
|
||||
'args' => Relay::connectionArgs(),
|
||||
'resolve' => function( $source, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return DataSource::resolve_themes_connection( $source, $args, $context, $info );
|
||||
},
|
||||
];
|
||||
}
|
||||
return ! empty( self::$connection[ $from_type ] ) ? self::$connection[ $from_type ] : null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
namespace WPGraphQL\Type\Theme\Connection;
|
||||
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQLRelay\Relay;
|
||||
use WPGraphQL\AppContext;
|
||||
|
||||
/**
|
||||
* Class ThemeConnectionResolver
|
||||
*
|
||||
* @package WPGraphQL\Data\Resolvers
|
||||
* @since 0.5.0
|
||||
*/
|
||||
class ThemeConnectionResolver {
|
||||
|
||||
/**
|
||||
* Creates the connection for themes
|
||||
*
|
||||
* @param mixed $source The query results of the query calling this relation
|
||||
* @param array $args Query arguments
|
||||
* @param AppContext $context The AppContext object
|
||||
* @param ResolveInfo $info The ResolveInfo object
|
||||
*
|
||||
* @since 0.5.0
|
||||
* @return array
|
||||
* @access public
|
||||
*/
|
||||
public static function resolve( $source, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
$themes_array = [];
|
||||
$themes = wp_get_themes();
|
||||
if ( is_array( $themes ) && ! empty( $themes ) ) {
|
||||
foreach ( $themes as $theme ) {
|
||||
$themes_array[] = $theme;
|
||||
}
|
||||
}
|
||||
|
||||
$connection = Relay::connectionFromArray( $themes_array, $args );
|
||||
|
||||
$nodes = [];
|
||||
if ( ! empty( $connection['edges'] ) && is_array( $connection['edges'] ) ) {
|
||||
foreach ( $connection['edges'] as $edge ) {
|
||||
$nodes[] = ! empty( $edge['node'] ) ? $edge['node'] : null;
|
||||
}
|
||||
}
|
||||
|
||||
$connection['nodes'] = ! empty( $nodes ) ? $nodes : null;
|
||||
|
||||
return ! empty( $themes_array ) ? $connection : null;
|
||||
}
|
||||
|
||||
}
|
||||
151
wordpress/wp-content/plugins/wp-graphql/src/Type/Theme/ThemeType.php
Executable file
151
wordpress/wp-content/plugins/wp-graphql/src/Type/Theme/ThemeType.php
Executable file
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
namespace WPGraphQL\Type\Theme;
|
||||
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQLRelay\Relay;
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\Type\WPObjectType;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class ThemeType
|
||||
* @package WPGraphQL\Type
|
||||
* @since 0.0.5
|
||||
*/
|
||||
class ThemeType extends WPObjectType {
|
||||
|
||||
/**
|
||||
* Holds the type name
|
||||
* @var string $type_name
|
||||
*/
|
||||
private static $type_name;
|
||||
|
||||
/**
|
||||
* This holds the field definitions
|
||||
* @var array $fields
|
||||
* @since 0.0.5
|
||||
*/
|
||||
private static $fields;
|
||||
|
||||
/**
|
||||
* ThemeType constructor.
|
||||
* @since 0.0.5
|
||||
*/
|
||||
public function __construct() {
|
||||
|
||||
/**
|
||||
* Set the type_name
|
||||
* @since 0.0.5
|
||||
*/
|
||||
self::$type_name = 'Theme';
|
||||
|
||||
$config = [
|
||||
'name' => self::$type_name,
|
||||
'description' => __( 'A theme object', 'wp-graphql' ),
|
||||
'fields' => self::fields(),
|
||||
'interfaces' => [ self::node_interface() ],
|
||||
];
|
||||
|
||||
parent::__construct( $config );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* fields
|
||||
*
|
||||
* This defines the fields for the ThemeType. The fields are passed through a filter so the shape of the schema
|
||||
* can be modified
|
||||
*
|
||||
* @return array|\GraphQL\Type\Definition\FieldDefinition[]
|
||||
* @since 0.0.5
|
||||
*/
|
||||
private static function fields() {
|
||||
|
||||
if ( null === self::$fields ) {
|
||||
self::$fields = function() {
|
||||
$fields = [
|
||||
'id' => [
|
||||
'type' => Types::non_null( Types::id() ),
|
||||
'resolve' => function( \WP_Theme $theme, $args, AppContext $context, ResolveInfo $info ) {
|
||||
$stylesheet = $theme->get_stylesheet();
|
||||
|
||||
return ( ! empty( $info->parentType ) && ! empty( $stylesheet ) ) ? Relay::toGlobalId( 'theme', $stylesheet ) : null;
|
||||
},
|
||||
],
|
||||
'slug' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The theme slug is used to internally match themes. Theme slugs can have subdirectories like: my-theme/sub-theme. This field is equivalent to WP_Theme->get_stylesheet().', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Theme $theme, $args, AppContext $context, ResolveInfo $info ) {
|
||||
$stylesheet = $theme->get_stylesheet();
|
||||
|
||||
return ! empty( $stylesheet ) ? $stylesheet : null;
|
||||
},
|
||||
],
|
||||
'name' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Display name of the theme. This field is equivalent to WP_Theme->get( "Name" ).', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Theme $theme, $args, AppContext $context, ResolveInfo $info ) {
|
||||
$name = $theme->get( 'Name' );
|
||||
|
||||
return ! empty( $name ) ? $name : null;
|
||||
},
|
||||
],
|
||||
'screenshot' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The URL of the screenshot for the theme. The screenshot is intended to give an overview of what the theme looks like. This field is equivalent to WP_Theme->get_screenshot().', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Theme $theme, $args, AppContext $context, ResolveInfo $info ) {
|
||||
$screenshot = $theme->get_screenshot();
|
||||
|
||||
return ! empty( $screenshot ) ? $screenshot : null;
|
||||
},
|
||||
],
|
||||
'themeUri' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'A URI if the theme has a website associated with it. The Theme URI is handy for directing users to a theme site for support etc. This field is equivalent to WP_Theme->get( "ThemeURI" ).', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Theme $theme, $args, AppContext $context, ResolveInfo $info ) {
|
||||
$theme_uri = $theme->get( 'ThemeURI' );
|
||||
|
||||
return ! empty( $theme_uri ) ? $theme_uri : null;
|
||||
},
|
||||
],
|
||||
'description' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The description of the theme. This field is equivalent to WP_Theme->get( "Description" ).', 'wp-graphql' ),
|
||||
],
|
||||
'author' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Name of the theme author(s), could also be a company name. This field is equivalent to WP_Theme->get( "Author" ).', 'wp-graphql' ),
|
||||
],
|
||||
'authorUri' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'URI for the author/company website. This field is equivalent to WP_Theme->get( "AuthorURI" ).', 'wp-graphql' ),
|
||||
'resolve' => function( \WP_Theme $theme, $args, AppContext $context, ResolveInfo $info ) {
|
||||
$author_uri = $theme->get( 'AuthorURI' );
|
||||
|
||||
return ! empty( $author_uri ) ? $author_uri : null;
|
||||
},
|
||||
],
|
||||
'tags' => [
|
||||
'type' => Types::list_of( Types::string() ),
|
||||
'description' => __( 'URI for the author/company website. This field is equivalent to WP_Theme->get( "Tags" ).', 'wp-graphql' ),
|
||||
],
|
||||
'version' => [
|
||||
'type' => Types::float(),
|
||||
'description' => __( 'The current version of the theme. This field is equivalent to WP_Theme->get( "Version" ).', 'wp-graphql' ),
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* This prepares the fields by sorting them and applying a filter for adjusting the schema.
|
||||
* Because these fields are implemented via a closure the prepare_fields needs to be applied
|
||||
* to the fields directly instead of being applied to all objects extending
|
||||
* the WPObjectType class.
|
||||
*/
|
||||
return self::prepare_fields( $fields, self::$type_name );
|
||||
|
||||
};
|
||||
}
|
||||
return self::$fields;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL\Type\Union;
|
||||
|
||||
use GraphQL\Type\Definition\UnionType;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class CommentAuthorUnionType
|
||||
*
|
||||
* In some situations, the type of term cannot be known until query time. The commentAuthorUnion allows for a
|
||||
* comment author to be queried and resolved to a number of types. Currently will return a user or commentAuthor.
|
||||
*
|
||||
* @package WPGraphQL\Type\Union
|
||||
*/
|
||||
class CommentAuthorUnionType extends UnionType {
|
||||
|
||||
/**
|
||||
* This holds an array of the possible types that can be resolved by this union
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $possible_types;
|
||||
|
||||
/**
|
||||
* CommentAuthorUnionType constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
|
||||
self::getPossibleTypes();
|
||||
|
||||
$config = [
|
||||
'name' => 'CommentAuthorUnion',
|
||||
'types' => self::$possible_types,
|
||||
'resolveType' => function( $source ) {
|
||||
if ( $source instanceof \WP_User ) {
|
||||
$type = Types::user();
|
||||
} else {
|
||||
$type = Types::comment_author();
|
||||
}
|
||||
return $type;
|
||||
},
|
||||
];
|
||||
|
||||
parent::__construct( $config );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This defines the possible types that can be resolved by this union
|
||||
*
|
||||
* @return array|null An array of possible types that can be resolved by the union
|
||||
*/
|
||||
public function getPossibleTypes() {
|
||||
|
||||
if ( null === self::$possible_types ) {
|
||||
self::$possible_types = [];
|
||||
}
|
||||
|
||||
self::$possible_types = [
|
||||
'user' => Types::user(),
|
||||
'commentAuthor' => Types::comment_author(),
|
||||
];
|
||||
|
||||
/**
|
||||
* Filter the possible_types as it's possible some systems might set things like "parent_id" to a different
|
||||
* object than a post_type, and might want to be able to hook in and add a non postObject type to the possible
|
||||
* resolveTypes.
|
||||
*
|
||||
* @param array $possible_types An array of possible types that can be resolved for the union
|
||||
*
|
||||
* @since 0.0.6
|
||||
*/
|
||||
self::$possible_types = apply_filters( 'graphql_comment_author_union_possible_types', self::$possible_types );
|
||||
|
||||
return ! empty( self::$possible_types ) ? self::$possible_types : null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
namespace WPGraphQL\Type\Union;
|
||||
|
||||
use GraphQL\Type\Definition\UnionType;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class PostObjectUnionType
|
||||
*
|
||||
* In WordPress, relations can be set to a post object by ID, but in many cases there's no strict control over what
|
||||
* post_type the related post can be, so in many cases it's unknown what "post_type" the related post is until the post
|
||||
* has been queried and returned. Some examples of such relations are:
|
||||
*
|
||||
* - Posts can have a parent_id set, and the parent_id can be of any post_type, so it's unknown what type of postObject
|
||||
* will be returned until the query has been run.
|
||||
*
|
||||
* - Attachments (mediaItems) can be uploaded to any post_type as well, so their "uploadedTo" property can return any
|
||||
* type of post object.
|
||||
*
|
||||
* @package WPGraphQL\Type\Union
|
||||
* @since 0.0.6
|
||||
*/
|
||||
class PostObjectUnionType extends UnionType {
|
||||
|
||||
/**
|
||||
* This holds an array of the possible types that can be resolved by this union
|
||||
* @var array
|
||||
* @since 0.0.6
|
||||
*/
|
||||
private static $possible_types;
|
||||
|
||||
/**
|
||||
* PostObjectUnionType constructor.
|
||||
* @since 0.0.6
|
||||
*/
|
||||
public function __construct() {
|
||||
|
||||
self::getPossibleTypes();
|
||||
|
||||
$config = [
|
||||
'name' => 'PostObjectUnion',
|
||||
'types' => self::$possible_types,
|
||||
'resolveType' => function( $value ) {
|
||||
return ! empty( $value->post_type ) ? Types::post_object( $value->post_type ) : null;
|
||||
},
|
||||
];
|
||||
|
||||
parent::__construct( $config );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This defines the possible types that can be resolved by this union
|
||||
*
|
||||
* @return array An array of possible types that can be resolved by the union
|
||||
* @since 0.0.5
|
||||
*/
|
||||
public function getPossibleTypes() {
|
||||
|
||||
if ( null === self::$possible_types ) {
|
||||
self::$possible_types = [];
|
||||
}
|
||||
|
||||
$allowed_post_types = \WPGraphQL::$allowed_post_types;
|
||||
if ( ! empty( $allowed_post_types ) && is_array( $allowed_post_types ) ) {
|
||||
foreach ( $allowed_post_types as $allowed_post_type ) {
|
||||
if ( empty( self::$possible_types[ $allowed_post_type ] ) ) {
|
||||
self::$possible_types[ $allowed_post_type ] = Types::post_object( $allowed_post_type );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the possible_types as it's possible some systems might set things like "parent_id" to a different
|
||||
* object than a post_type, and might want to be able to hook in and add a non postObject type to the possible
|
||||
* resolveTypes.
|
||||
*
|
||||
* @param array $possible_types An array of possible types that can be resolved for the union
|
||||
* @since 0.0.6
|
||||
*/
|
||||
self::$possible_types = apply_filters( 'graphql_post_object_union_possible_types', self::$possible_types );
|
||||
|
||||
return ! empty( self::$possible_types ) ? self::$possible_types : null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
namespace WPGraphQL\Type\Union;
|
||||
|
||||
use GraphQL\Type\Definition\UnionType;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class TermObjectUnionType
|
||||
*
|
||||
* In some situations, the type of term cannot be known until query time. The termObjectUnion allows for connections to
|
||||
* be queried and resolved to a number of types.
|
||||
*
|
||||
* @package WPGraphQL\Type\Union
|
||||
*/
|
||||
class TermObjectUnionType extends UnionType {
|
||||
|
||||
/**
|
||||
* This holds an array of the possible types that can be resolved by this union
|
||||
* @var array
|
||||
*/
|
||||
private static $possible_types;
|
||||
|
||||
/**
|
||||
* TermObjectUnionType constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
|
||||
self::getPossibleTypes();
|
||||
|
||||
$config = [
|
||||
'name' => 'TermObjectUnion',
|
||||
'types' => self::$possible_types,
|
||||
'resolveType' => function( $value ) {
|
||||
return ! empty( $value->taxonomy ) ? Types::term_object( $value->taxonomy ) : null;
|
||||
},
|
||||
];
|
||||
|
||||
parent::__construct( $config );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This defines the possible types that can be resolved by this union
|
||||
*
|
||||
* @return array|null An array of possible types that can be resolved by the union
|
||||
*/
|
||||
public function getPossibleTypes() {
|
||||
|
||||
if ( null === self::$possible_types ) {
|
||||
self::$possible_types = [];
|
||||
}
|
||||
|
||||
$allowed_taxonomies = \WPGraphQL::$allowed_taxonomies;
|
||||
if ( ! empty( $allowed_taxonomies ) && is_array( $allowed_taxonomies ) ) {
|
||||
foreach ( $allowed_taxonomies as $allowed_taxonomy ) {
|
||||
if ( empty( self::$possible_types[ $allowed_taxonomy ] ) ) {
|
||||
self::$possible_types[ $allowed_taxonomy ] = Types::term_object( $allowed_taxonomy );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the possible_types as it's possible some systems might set things like "parent_id" to a different
|
||||
* object than a post_type, and might want to be able to hook in and add a non postObject type to the possible
|
||||
* resolveTypes.
|
||||
*
|
||||
* @param array $possible_types An array of possible types that can be resolved for the union
|
||||
* @since 0.0.6
|
||||
*/
|
||||
self::$possible_types = apply_filters( 'graphql_term_object_union_possible_types', self::$possible_types );
|
||||
|
||||
return ! empty( self::$possible_types ) ? self::$possible_types : null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
<?php
|
||||
namespace WPGraphQL\Type\User\Connection;
|
||||
|
||||
use GraphQL\Type\Definition\EnumType;
|
||||
use WPGraphQL\Type\WPEnumType;
|
||||
use WPGraphQL\Type\WPInputObjectType;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class UserConnectionArgs
|
||||
*
|
||||
* This sets up the Query Args for user connections, which uses WP_User_Query, so this defines the allowed
|
||||
* input fields that will be passed to the WP_User_Query
|
||||
*
|
||||
* @package WPGraphQL\Type
|
||||
* @since 0.0.5
|
||||
*/
|
||||
class UserConnectionArgs extends WPInputObjectType {
|
||||
|
||||
/**
|
||||
* This holds the field definitions
|
||||
* @var array $fields
|
||||
* @since 0.0.5
|
||||
*/
|
||||
public static $fields;
|
||||
|
||||
/**
|
||||
* This holds the $roles_enum definition
|
||||
* @var EnumType
|
||||
* @since 0.0.5
|
||||
*/
|
||||
private static $roles_enum;
|
||||
|
||||
/**
|
||||
* This holds the SearchColumnsEnumType
|
||||
* @var EnumType
|
||||
* @since 0.0.5
|
||||
*/
|
||||
private static $search_columns_enum;
|
||||
|
||||
/**
|
||||
* UserConnectionArgs constructor.
|
||||
* @param array $config Array of config for the Input Type
|
||||
* @param string $connection The name of the connection the args belong to
|
||||
* @since 0.0.5
|
||||
*/
|
||||
public function __construct( $config = [], $connection = '' ) {
|
||||
$config['name'] = ucfirst( $connection ) . 'UserArgs';
|
||||
$config['queryClass'] = 'WP_User_Query';
|
||||
$config['fields'] = self::fields( $connection );
|
||||
parent::__construct( $config );
|
||||
}
|
||||
|
||||
/**
|
||||
* fields
|
||||
*
|
||||
* This defines the fields that make up the UserConnectionArgs
|
||||
*
|
||||
* @param string $connection The name of the connection the Args belong to
|
||||
* @return array
|
||||
* @since 0.0.5
|
||||
*/
|
||||
private static function fields( $connection ) {
|
||||
|
||||
if ( null === self::$fields ) {
|
||||
self::$fields = [];
|
||||
}
|
||||
|
||||
if ( empty( self::$fields ) ) {
|
||||
|
||||
$fields = [
|
||||
'role' => [
|
||||
'type' => self::roles_enum(),
|
||||
'description' => __( 'An array of role names that users must match to be included in results. Note that this is an inclusive list: users must match *each* role.', 'wp-graphql' ),
|
||||
],
|
||||
'roleIn' => [
|
||||
'type' => Types::list_of( self::roles_enum() ),
|
||||
'description' => __( 'An array of role names. Matched users must have at least one of these roles.', 'wp-graphql' ),
|
||||
],
|
||||
'roleNotIn' => [
|
||||
'type' => Types::list_of( self::roles_enum() ),
|
||||
'description' => __( 'An array of role names to exclude. Users matching one or more of these roles will not be included in results.', 'wp-graphql' ),
|
||||
],
|
||||
'include' => [
|
||||
'type' => Types::list_of( Types::int() ),
|
||||
'description' => __( 'Array of comment IDs to include.', 'wp-graphql' ),
|
||||
],
|
||||
'exclude' => [
|
||||
'type' => Types::list_of( Types::int() ),
|
||||
'description' => __( 'Array of IDs of users whose unapproved comments will be returned by the query regardless of status.', 'wp-graphql' ),
|
||||
],
|
||||
'search' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'Search keyword. Searches for possible string matches on columns. When `searchColumns` is left empty, it tries to determine which column to search in based on search string.', 'wp-graphql' ),
|
||||
],
|
||||
'searchColumns' => [
|
||||
'type' => Types::list_of( self::search_columns_enum() ),
|
||||
'description' => __( 'Array of column names to be searched. Accepts \'ID\', \'login\', \'nicename\', \'email\', \'url\'.', 'wp-graphql' ),
|
||||
],
|
||||
'hasPublishedPosts' => [
|
||||
'type' => Types::list_of( Types::post_type_enum() ),
|
||||
'description' => __( 'Pass an array of post types to filter results to users who have published posts in those post types.', 'wp-graphql' ),
|
||||
],
|
||||
'nicename' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The user nicename.', 'wp-graphql' ),
|
||||
],
|
||||
'nicenameIn' => [
|
||||
'type' => Types::list_of( Types::string() ),
|
||||
'description' => __( 'An array of nicenames to include. Users matching one of these nicenames will be included in results.', 'wp-graphql' ),
|
||||
],
|
||||
'nicenameNotIn' => [
|
||||
'type' => Types::list_of( Types::string() ),
|
||||
'description' => __( 'An array of nicenames to exclude. Users matching one of these nicenames will not be included in results.', 'wp-graphql' ),
|
||||
],
|
||||
'login' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The user login.', 'wp-graphql' ),
|
||||
],
|
||||
'loginIn' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( 'An array of logins to include. Users matching one of these logins will be included in results.', 'wp-graphql' ),
|
||||
],
|
||||
'loginNotIn' => [
|
||||
'type' => Types::int(),
|
||||
'description' => __( 'An array of logins to exclude. Users matching one of these logins will not be included in results.', 'wp-graphql' ),
|
||||
],
|
||||
];
|
||||
self::$fields[ $connection ] = self::prepare_fields( $fields, ucfirst( $connection ) . 'UserArgs' );
|
||||
}
|
||||
|
||||
return ! empty( self::$fields[ $connection ] ) ? self::$fields[ $connection ]: null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* search_columns_enum
|
||||
*
|
||||
* Returns the searchColumnsEnum type defintion
|
||||
*
|
||||
* @return EnumType
|
||||
* @since 0.0.5
|
||||
*/
|
||||
private static function search_columns_enum() {
|
||||
|
||||
if ( null === self::$search_columns_enum ) {
|
||||
self::$search_columns_enum = new WPEnumType( [
|
||||
'name' => 'SearchColumnsEnum',
|
||||
'values' => [
|
||||
'ID' => [
|
||||
'value' => 'ID',
|
||||
],
|
||||
'LOGIN' => [
|
||||
'value' => 'login',
|
||||
],
|
||||
'NICENAME' => [
|
||||
'value' => 'nicename',
|
||||
],
|
||||
'EMAIL' => [
|
||||
'value' => 'email',
|
||||
],
|
||||
'URL' => [
|
||||
'value' => 'url',
|
||||
],
|
||||
],
|
||||
] );
|
||||
}
|
||||
return self::$search_columns_enum;
|
||||
}
|
||||
|
||||
/**
|
||||
* roles_enum
|
||||
*
|
||||
* Returns the userRoleEnum type definition
|
||||
*
|
||||
* @return EnumType
|
||||
* @since 0.0.5
|
||||
*/
|
||||
private static function roles_enum() {
|
||||
|
||||
if ( null === self::$roles_enum ) {
|
||||
global $wp_roles;
|
||||
$all_roles = $wp_roles->roles;
|
||||
$editable_roles = apply_filters( 'editable_roles', $all_roles );
|
||||
$roles = [];
|
||||
|
||||
if ( ! empty( $editable_roles ) && is_array( $editable_roles ) ) {
|
||||
foreach ( $editable_roles as $key => $role ) {
|
||||
|
||||
$formatted_role = self::format_enum_name( $role['name'] );
|
||||
|
||||
$roles[ $formatted_role ] = [
|
||||
'value' => $key,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $roles ) ) {
|
||||
self::$roles_enum = new WPEnumType( [
|
||||
'name' => 'UserRoleEnum',
|
||||
'values' => $roles,
|
||||
] );
|
||||
}
|
||||
}
|
||||
|
||||
return self::$roles_enum;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
namespace WPGraphQL\Type\User\Connection;
|
||||
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQLRelay\Relay;
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\Data\DataSource;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class UserConnectionDefinition
|
||||
* @package WPGraphQL\Type\Comment\Connection
|
||||
* @since 0.0.5
|
||||
*/
|
||||
class UserConnectionDefinition {
|
||||
|
||||
/**
|
||||
* Stores some date for the Relay connection for term objects
|
||||
*
|
||||
* @var array $connection
|
||||
* @since 0.0.5
|
||||
* @access private
|
||||
*/
|
||||
private static $connection = [];
|
||||
|
||||
/**
|
||||
* Method that sets up the relay connection for term objects
|
||||
*
|
||||
* @param string $from_type The name of the type the connection is coming from
|
||||
* @return mixed
|
||||
* @since 0.0.5
|
||||
*/
|
||||
public static function connection( $from_type = 'Root' ) {
|
||||
|
||||
if ( empty( self::$connection[ $from_type ] ) ) {
|
||||
$connection = Relay::connectionDefinitions( [
|
||||
'nodeType' => Types::user(),
|
||||
'name' => ucfirst( $from_type ) . 'Users',
|
||||
'connectionFields' => function() {
|
||||
return [
|
||||
'nodes' => [
|
||||
'type' => Types::list_of( Types::user() ),
|
||||
'description' => __( 'The nodes of the connection, without the edges', 'wp-graphql' ),
|
||||
'resolve' => function( $source, $args, $context, $info ) {
|
||||
return ! empty( $source['nodes'] ) ? $source['nodes'] : [];
|
||||
},
|
||||
],
|
||||
];
|
||||
},
|
||||
] );
|
||||
|
||||
/**
|
||||
* Add the "where" args to the commentConnection
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
$args = [
|
||||
'where' => [
|
||||
'name' => 'where',
|
||||
'type' => Types::user_connection_query_args( ucfirst( $from_type ) . 'Users' ),
|
||||
],
|
||||
];
|
||||
|
||||
self::$connection[ $from_type ] = [
|
||||
'type' => $connection['connectionType'],
|
||||
'description' => __( 'A collection of user objects', 'wp-graphql' ),
|
||||
'args' => array_merge( Relay::connectionArgs(), $args ),
|
||||
'resolve' => function( $source, $args, AppContext $context, ResolveInfo $info ) {
|
||||
return DataSource::resolve_users_connection( $source, $args, $context, $info );
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return ! empty( self::$connection[ $from_type ] ) ? self::$connection[ $from_type ] : null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
namespace WPGraphQL\Type\User\Connection;
|
||||
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\Data\ConnectionResolver;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class UserConnectionResolver
|
||||
*
|
||||
* @package WPGraphQL\Data\Resolvers
|
||||
* @since 0.5.0
|
||||
*/
|
||||
class UserConnectionResolver extends ConnectionResolver {
|
||||
|
||||
/**
|
||||
* This runs the query and returns the repsonse
|
||||
*
|
||||
* @param $query_args
|
||||
*
|
||||
* @return \WP_User_Query
|
||||
*/
|
||||
public static function get_query( $query_args ) {
|
||||
$query = new \WP_User_Query( $query_args );
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* This returns the $query_args that should be used when querying for posts in the postObjectConnectionResolver.
|
||||
* This checks what input $args are part of the query, combines them with various filters, etc and returns an
|
||||
* array of $query_args to be used in the \WP_Query call
|
||||
*
|
||||
* @param mixed $source The query source being passed down to the resolver
|
||||
* @param array $args The arguments that were provided to the query
|
||||
* @param AppContext $context Object containing app context that gets passed down the resolve tree
|
||||
* @param ResolveInfo $info Info about fields passed down the resolve tree
|
||||
*
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function get_query_args( $source, array $args, AppContext $context, ResolveInfo $info ) {
|
||||
|
||||
/**
|
||||
* Set the $query_args based on various defaults and primary input $args
|
||||
*/
|
||||
$query_args['count_total'] = false;
|
||||
$query_args['offset'] = self::get_offset( $args );
|
||||
$query_args['order'] = ! empty( $args['last'] ) ? 'ASC' : 'DESC';
|
||||
|
||||
/**
|
||||
* If "pageInfo" is in the fieldSelection, we need to calculate the pagination details, so
|
||||
* we need to run the query with count_total set to true.
|
||||
*/
|
||||
$field_selection = $info->getFieldSelection( 2 );
|
||||
if ( ! empty( $field_selection['pageInfo'] ) ) {
|
||||
$query_args['count_total'] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the number, ensuring it doesn't exceed the amount set as the $max_query_amount
|
||||
*/
|
||||
$query_args['number'] = self::get_query_amount( $source, $args, $context, $info );
|
||||
|
||||
/**
|
||||
* Take any of the input $args (under the "where" input) that were part of the GraphQL query and map and
|
||||
* sanitize their GraphQL input to apply to the WP_Query
|
||||
*/
|
||||
$input_fields = [];
|
||||
if ( ! empty( $args['where'] ) ) {
|
||||
$input_fields = self::sanitize_input_fields( $args['where'], $source, $args, $context, $info );
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the default $query_args with the $args that were entered in the query.
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
if ( ! empty( $input_fields ) ) {
|
||||
$query_args = array_merge( $query_args, $input_fields );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the query_args that should be applied to the query. This filter is applied AFTER the input args from
|
||||
* the GraphQL Query have been applied and has the potential to override the GraphQL Query Input Args.
|
||||
*
|
||||
* @param array $query_args array of query_args being passed to the
|
||||
* @param mixed $source source passed down from the resolve tree
|
||||
* @param array $args array of arguments input in the field as part of the GraphQL query
|
||||
* @param AppContext $context object passed down zthe resolve tree
|
||||
* @param ResolveInfo $info info about fields passed down the resolve tree
|
||||
*
|
||||
* @since 0.0.6
|
||||
*/
|
||||
$query_args = apply_filters( 'graphql_user_connection_query_args', $query_args, $source, $args, $context, $info );
|
||||
|
||||
return $query_args;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This sets up the "allowed" args, and translates the GraphQL-friendly keys to WP_User_Query
|
||||
* friendly keys.
|
||||
*
|
||||
* There's probably a cleaner/more dynamic way to approach this, but this was quick. I'd be
|
||||
* down to explore more dynamic ways to map this, but for now this gets the job done.
|
||||
*
|
||||
* @param array $args The query "where" args
|
||||
* @param mixed $source The query results of the query calling this relation
|
||||
* @param array $all_args Array of all the query args (not just the "where" args)
|
||||
* @param AppContext $context The AppContext object
|
||||
* @param ResolveInfo $info The ResolveInfo object
|
||||
*
|
||||
* @since 0.0.5
|
||||
* @return array
|
||||
* @access private
|
||||
*/
|
||||
public static function sanitize_input_fields( array $args, $source, array $all_args, AppContext $context, ResolveInfo $info ) {
|
||||
|
||||
$arg_mapping = [
|
||||
'roleIn' => 'role__in',
|
||||
'roleNotIn' => 'role__not_in',
|
||||
'searchColumns' => 'search_columns',
|
||||
'hasPublishedPosts' => 'has_published_posts',
|
||||
'nicenameIn' => 'nicename__in',
|
||||
'nicenameNotIn' => 'nicename__not_in',
|
||||
'loginIn' => 'login__in',
|
||||
'loginNotIn' => 'login__not_in',
|
||||
];
|
||||
|
||||
/**
|
||||
* Map and sanitize the input args to the WP_User_Query compatible args
|
||||
*/
|
||||
$query_args = Types::map_input( $args, $arg_mapping );
|
||||
|
||||
/**
|
||||
* Filter the input fields
|
||||
*
|
||||
* This allows plugins/themes to hook in and alter what $args should be allowed to be passed
|
||||
* from a GraphQL Query to the get_terms query
|
||||
*
|
||||
* @param array $query_args The mapped query args
|
||||
* @param array $args The query "where" args
|
||||
* @param mixed $source The query results of the query calling this relation
|
||||
* @param array $all_args Array of all the query args (not just the "where" args)
|
||||
* @param AppContext $context The AppContext object
|
||||
* @param ResolveInfo $info The ResolveInfo object
|
||||
*
|
||||
* @since 0.0.5
|
||||
* @return array
|
||||
*/
|
||||
$query_args = apply_filters( 'graphql_map_input_fields_to_wp_comment_query', $query_args, $args, $source, $all_args, $context, $info );
|
||||
|
||||
return ! empty( $query_args ) && is_array( $query_args ) ? $query_args : [];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
127
wordpress/wp-content/plugins/wp-graphql/src/Type/User/Mutation/UserCreate.php
Executable file
127
wordpress/wp-content/plugins/wp-graphql/src/Type/User/Mutation/UserCreate.php
Executable file
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL\Type\User\Mutation;
|
||||
|
||||
use GraphQL\Error\UserError;
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQLRelay\Relay;
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class UserCreate
|
||||
*
|
||||
* @package WPGraphQL\Type\User\Mutation
|
||||
*/
|
||||
class UserCreate {
|
||||
|
||||
/**
|
||||
* Stores the user create mutation
|
||||
*
|
||||
* @var array $mutation
|
||||
* @access private
|
||||
*/
|
||||
private static $mutation;
|
||||
|
||||
/**
|
||||
* Process the user creat mutation
|
||||
*
|
||||
* @return array|null
|
||||
* @access public
|
||||
*/
|
||||
public static function mutate() {
|
||||
|
||||
if ( empty( self::$mutation ) ) {
|
||||
|
||||
self::$mutation = Relay::mutationWithClientMutationId( [
|
||||
'name' => 'CreateUser',
|
||||
'description' => __( 'Create new user object', 'wp-graphql' ),
|
||||
'inputFields' => self::input_fields(),
|
||||
'outputFields' => [
|
||||
'user' => [
|
||||
'type' => Types::user(),
|
||||
'resolve' => function( $payload ) {
|
||||
return get_user_by( 'ID', $payload['id'] );
|
||||
}
|
||||
]
|
||||
],
|
||||
'mutateAndGetPayload' => function( $input, AppContext $context, ResolveInfo $info ) {
|
||||
|
||||
if ( ! current_user_can( 'create_users' ) ) {
|
||||
throw new UserError( __( 'Sorry, you are not allowed to create a new user.', 'wp-graphql' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Map all of the args from GQL to WP friendly
|
||||
*/
|
||||
$user_args = UserMutation::prepare_user_object( $input, 'userCreate' );
|
||||
|
||||
/**
|
||||
* Create the new user
|
||||
*/
|
||||
$user_id = wp_insert_user( $user_args );
|
||||
|
||||
/**
|
||||
* Throw an exception if the post failed to create
|
||||
*/
|
||||
if ( is_wp_error( $user_id ) ) {
|
||||
$error_message = $user_id->get_error_message();
|
||||
if ( ! empty( $error_message ) ) {
|
||||
throw new UserError( esc_html( $error_message ) );
|
||||
} else {
|
||||
throw new UserError( __( 'The object failed to create but no error was provided', 'wp-graphql' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the $post_id is empty, we should throw an exception
|
||||
*/
|
||||
if ( empty( $user_id ) ) {
|
||||
throw new UserError( __( 'The object failed to create', 'wp-graphql' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update additional user data
|
||||
*/
|
||||
UserMutation::update_additional_user_object_data( $user_id, $input, 'create', $context, $info );
|
||||
|
||||
/**
|
||||
* Return the new user ID
|
||||
*/
|
||||
return [
|
||||
'id' => $user_id,
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
] );
|
||||
}
|
||||
|
||||
return ( ! empty( self::$mutation ) ) ? self::$mutation : null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the email as a nonNull field for update mutations
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private static function input_fields() {
|
||||
|
||||
/**
|
||||
* Update mutations require an ID to be passed
|
||||
*/
|
||||
return array_merge(
|
||||
UserMutation::input_fields(),
|
||||
[
|
||||
'username' => [
|
||||
'type' => Types::non_null( Types::string() ),
|
||||
// translators: the placeholder is the name of the type of post object being updated
|
||||
'description' => __( 'A string that contains the user\'s username for logging in.', 'wp-graphql' ),
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
125
wordpress/wp-content/plugins/wp-graphql/src/Type/User/Mutation/UserDelete.php
Executable file
125
wordpress/wp-content/plugins/wp-graphql/src/Type/User/Mutation/UserDelete.php
Executable file
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL\Type\User\Mutation;
|
||||
|
||||
use GraphQL\Error\UserError;
|
||||
use GraphQLRelay\Relay;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class UserDelete
|
||||
*
|
||||
* @package WPGraphQL\Type\User\Mutation
|
||||
*/
|
||||
class UserDelete {
|
||||
|
||||
/**
|
||||
* Stores the user delete mutation
|
||||
*
|
||||
* @var array $mutation
|
||||
* @access private
|
||||
*/
|
||||
private static $mutation;
|
||||
|
||||
/**
|
||||
* Processes the user delete mutation
|
||||
*
|
||||
* @return array|null
|
||||
* @access public
|
||||
*/
|
||||
public static function mutate() {
|
||||
|
||||
if ( empty( self::$mutation ) ) {
|
||||
|
||||
self::$mutation = Relay::mutationWithClientMutationId( [
|
||||
'name' => 'DeleteUser',
|
||||
'description' => __( 'Delete a user object', 'wp-graphql' ),
|
||||
'inputFields' => [
|
||||
'id' => [
|
||||
'type' => Types::non_null( Types::id() ),
|
||||
'description' => __( 'The ID of the user you want to delete', 'wp-graphql' ),
|
||||
],
|
||||
'reassignId' => [
|
||||
'type' => Types::id(),
|
||||
'description' => __( 'Reassign posts and links to new User ID.', 'wp-graphql' ),
|
||||
]
|
||||
],
|
||||
'outputFields' => [
|
||||
'deletedId' => [
|
||||
'type' => Types::id(),
|
||||
'description' => __( 'The ID of the user that you just deleted', 'wp-graphql' ),
|
||||
'resolve' => function( $payload ) {
|
||||
$deleted = (object) $payload['userObject'];
|
||||
return ( ! empty( $deleted->ID ) ) ? Relay::toGlobalId( 'user', $deleted->ID ) : null;
|
||||
}
|
||||
],
|
||||
'user' => [
|
||||
'type' => Types::user(),
|
||||
'description' => __( 'The user object for the user you are trying to delete', 'wp-graphql' ),
|
||||
'resolve' => function( $payload ) {
|
||||
$deleted = (object) $payload['userObject'];
|
||||
return ( ! empty( $deleted ) ) ? $deleted : null;
|
||||
}
|
||||
]
|
||||
],
|
||||
'mutateAndGetPayload' => function( $input ) {
|
||||
|
||||
/**
|
||||
* Get the ID from the global ID
|
||||
*/
|
||||
$id_parts = Relay::fromGlobalId( $input['id'] );
|
||||
|
||||
if ( ! current_user_can( 'delete_users' ) ) {
|
||||
throw new UserError( __( 'Sorry, you are not allowed to delete users.', 'wp-graphql' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the user object before it's deleted
|
||||
*/
|
||||
$user_before_delete = get_user_by( 'id', absint( $id_parts['id'] ) );
|
||||
|
||||
/**
|
||||
* Throw an error if the user we are trying to delete doesn't exist
|
||||
*/
|
||||
if ( false === $user_before_delete ) {
|
||||
throw new UserError( __( 'Could not find an existing user to delete', 'wp-graphql' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the DB id for the user to reassign posts to from the relay ID.
|
||||
*/
|
||||
$reassign_id_parts = ( ! empty( $input['reassignId'] ) ) ? Relay::fromGlobalId( $input['reassignId'] ) : null;
|
||||
$reassign_id = ( ! empty( $reassign_id_parts ) ) ? absint( $reassign_id_parts['id'] ) : null;
|
||||
|
||||
/**
|
||||
* If the wp_delete_user doesn't exist yet, load the file in which it is
|
||||
* registered so it is available in this context. I think we need to
|
||||
* load this manually here because WordPress only uses this
|
||||
* function on the user edit screen normally.
|
||||
*/
|
||||
if ( ! function_exists( 'wp_delete_user' ) ) {
|
||||
require_once( ABSPATH . 'wp-admin/includes/user.php' );
|
||||
}
|
||||
|
||||
if ( is_multisite() ) {
|
||||
$deleted_user = wpmu_delete_user( absint( $id_parts['id'] ) );
|
||||
} else {
|
||||
$deleted_user = wp_delete_user( absint( $id_parts['id'] ), $reassign_id );
|
||||
}
|
||||
|
||||
if ( true !== $deleted_user ) {
|
||||
throw new UserError( __( 'Could not delete the user.', 'wp-grapgql' ) );
|
||||
}
|
||||
|
||||
return [
|
||||
'userObject' => $user_before_delete,
|
||||
];
|
||||
}
|
||||
] );
|
||||
}
|
||||
|
||||
return ( ! empty( self::$mutation ) ) ? self::$mutation : null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
291
wordpress/wp-content/plugins/wp-graphql/src/Type/User/Mutation/UserMutation.php
Executable file
291
wordpress/wp-content/plugins/wp-graphql/src/Type/User/Mutation/UserMutation.php
Executable file
@@ -0,0 +1,291 @@
|
||||
<?php
|
||||
|
||||
namespace WPGraphQL\Type\User\Mutation;
|
||||
|
||||
use GraphQL\Error\UserError;
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\Types;
|
||||
|
||||
/**
|
||||
* Class UserMutation
|
||||
*
|
||||
* @package WPGraphQL\Type\User\Mutation
|
||||
*/
|
||||
class UserMutation {
|
||||
|
||||
/**
|
||||
* Stores the input fields static definition
|
||||
*
|
||||
* @var array $input_fields
|
||||
* @access private
|
||||
*/
|
||||
private static $input_fields = [];
|
||||
|
||||
/**
|
||||
* Defines the accepted input arguments
|
||||
*
|
||||
* @return array|null
|
||||
* @access public
|
||||
*/
|
||||
public static function input_fields() {
|
||||
|
||||
if ( empty( self::$input_fields ) ) {
|
||||
|
||||
$input_fields = [
|
||||
'password' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'A string that contains the plain text password for the user.', 'wp-graphql' ),
|
||||
],
|
||||
'nicename' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'A string that contains a URL-friendly name for the user. The default is the user\'s username.', 'wp-graphql' ),
|
||||
],
|
||||
'websiteUrl' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'A string containing the user\'s URL for the user\'s web site.', 'wp-grapql' ),
|
||||
],
|
||||
'email' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'A string containing the user\'s email address.', 'wp-graphql' ),
|
||||
],
|
||||
'displayName' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'A string that will be shown on the site. Defaults to user\'s username. It is likely that you will want to change this, for both appearance and security through obscurity (that is if you dont use and delete the default admin user).', 'wp-graphql' ),
|
||||
],
|
||||
'nickname' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The user\'s nickname, defaults to the user\'s username.', 'wp-graphql' ),
|
||||
],
|
||||
'firstName' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( ' The user\'s first name.', 'wp-graphql' ),
|
||||
],
|
||||
'lastName' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The user\'s last name.', 'wp-graphql' ),
|
||||
],
|
||||
'description' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'A string containing content about the user.', 'wp-graphql' ),
|
||||
],
|
||||
'richEditing' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'A string for whether to enable the rich editor or not. False if not empty.', 'wp-graphql' ),
|
||||
],
|
||||
'registered' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'The date the user registered. Format is Y-m-d H:i:s.', 'wp-graphql' ),
|
||||
],
|
||||
'roles' => [
|
||||
'type' => Types::list_of( Types::string() ),
|
||||
'description' => __( 'An array of roles to be assigned to the user.', 'wp-graphql' ),
|
||||
],
|
||||
'jabber' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'User\'s Jabber account.', 'wp-graphql' ),
|
||||
],
|
||||
'aim' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'User\'s AOL IM account.', 'wp-graphql' ),
|
||||
],
|
||||
'yim' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'User\'s Yahoo IM account.', 'wp-graphql' ),
|
||||
],
|
||||
'locale' => [
|
||||
'type' => Types::string(),
|
||||
'description' => __( 'User\'s locale.', 'wp-graphql' ),
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Filters all of the fields available for input
|
||||
*
|
||||
* @var array $input_fields
|
||||
*/
|
||||
self::$input_fields = apply_filters( 'graphql_user_mutation_input_fields', $input_fields );
|
||||
|
||||
}
|
||||
|
||||
return ( ! empty( self::$input_fields ) ) ? self::$input_fields : null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps the GraphQL input to a format that the WordPress functions can use
|
||||
*
|
||||
* @param array $input Data coming from the GraphQL mutation query input
|
||||
* @param string $mutation_name Name of the mutation being performed
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public static function prepare_user_object( $input, $mutation_name ) {
|
||||
|
||||
$insert_user_args = [];
|
||||
|
||||
if ( ! empty( $input['password'] ) ) {
|
||||
$insert_user_args['user_pass'] = $input['password'];
|
||||
} else {
|
||||
$insert_user_args['user_pass'] = null;
|
||||
}
|
||||
|
||||
if ( ! empty( $input['username'] ) ) {
|
||||
$insert_user_args['user_login'] = $input['username'];
|
||||
}
|
||||
|
||||
if ( ! empty( $input['nicename'] ) ) {
|
||||
$insert_user_args['user_nicename'] = $input['nicename'];
|
||||
}
|
||||
|
||||
if ( ! empty( $input['websiteUrl'] ) ) {
|
||||
$insert_user_args['user_url'] = esc_url( $input['websiteUrl'] );
|
||||
}
|
||||
|
||||
if ( ! empty( $input['email'] ) ) {
|
||||
if ( false === is_email( apply_filters( 'pre_user_email', $input['email'] ) ) ) {
|
||||
throw new UserError( __( 'The email address you are trying to use is invalid', 'graphql' ) );
|
||||
}
|
||||
$insert_user_args['user_email'] = $input['email'];
|
||||
}
|
||||
|
||||
if ( ! empty( $input['displayName'] ) ) {
|
||||
$insert_user_args['display_name'] = $input['displayName'];
|
||||
}
|
||||
|
||||
if ( ! empty( $input['nickname'] ) ) {
|
||||
$insert_user_args['nickname'] = $input['nickname'];
|
||||
}
|
||||
|
||||
if ( ! empty( $input['firstName'] ) ) {
|
||||
$insert_user_args['first_name'] = $input['firstName'];
|
||||
}
|
||||
|
||||
if ( ! empty( $input['lastName'] ) ) {
|
||||
$insert_user_args['last_name'] = $input['lastName'];
|
||||
}
|
||||
|
||||
if ( ! empty( $input['description'] ) ) {
|
||||
$insert_user_args['description'] = $input['description'];
|
||||
}
|
||||
|
||||
if ( ! empty( $input['richEditing'] ) ) {
|
||||
$insert_user_args['rich_editing'] = $input['richEditing'];
|
||||
}
|
||||
|
||||
if ( ! empty( $input['registered'] ) ) {
|
||||
$insert_user_args['user_registered'] = $input['registered'];
|
||||
}
|
||||
|
||||
if ( ! empty( $input['roles'] ) ) {
|
||||
/**
|
||||
* Pluck the first role out of the array since the insert and update functions only
|
||||
* allow one role to be set at a time. We will add all of the roles passed to the
|
||||
* mutation later on after the initial object has been created or updated.
|
||||
*/
|
||||
$insert_user_args['role'] = $input['roles'][0];
|
||||
}
|
||||
|
||||
if ( ! empty( $input['locale'] ) ) {
|
||||
$insert_user_args['locale'] = $input['locale'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the mappings for input to arguments
|
||||
*
|
||||
* @var array $insert_user_args The arguments to ultimately be passed to the WordPress function
|
||||
* @var array $input Input data from the GraphQL mutation
|
||||
* @var string $mutation_name What user mutation is being performed for context
|
||||
*/
|
||||
$insert_user_args = apply_filters( 'graphql_user_insert_post_args', $insert_user_args, $input, $mutation_name );
|
||||
|
||||
return $insert_user_args;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This updates additional data related to the user object after the initial mutation has happened
|
||||
*
|
||||
* @param int $user_id The ID of the user being mutated
|
||||
* @param array $input The input data from the GraphQL query
|
||||
* @param string $mutation_name Name of the mutation currently being run
|
||||
* @param AppContext $context The AppContext passed down the resolve tree
|
||||
* @param ResolveInfo $info The ResolveInfo passed down the Resolve Tree
|
||||
*/
|
||||
public static function update_additional_user_object_data( $user_id, $input, $mutation_name, AppContext $context, ResolveInfo $info ) {
|
||||
|
||||
$roles = ! empty( $input['roles'] ) ? $input['roles'] : [];
|
||||
self::add_user_roles( $user_id, $roles );
|
||||
|
||||
/**
|
||||
* Run an action after the additional data has been updated. This is a great spot to hook into to
|
||||
* update additional data related to users, such as setting relationships, updating additional usermeta,
|
||||
* or sending emails to Kevin... whatever you need to do with the userObject.
|
||||
*
|
||||
* @param int $user_id The ID of the user being mutated
|
||||
* @param array $input The input for the mutation
|
||||
* @param string $mutation_name The name of the mutation (ex: create, update, delete)
|
||||
* @param AppContext $context The AppContext passed down the resolve tree
|
||||
* @param ResolveInfo $info The ResolveInfo passed down the Resolve Tree
|
||||
*/
|
||||
do_action( 'graphql_user_object_mutation_update_additional_data', $user_id, $input, $mutation_name, $context, $info );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to add user roles to a user object
|
||||
*
|
||||
* @param int $user_id The ID of the user
|
||||
* @param array $roles List of roles that need to get added to the user
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
private static function add_user_roles( $user_id, $roles ) {
|
||||
|
||||
if ( empty( $roles ) || ! is_array( $roles ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$user = get_user_by( 'ID', $user_id );
|
||||
|
||||
if ( false !== $user ) {
|
||||
foreach ( $roles as $role ) {
|
||||
self::verify_user_role( $role );
|
||||
$user->add_role( $role );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to check if the user role is valid, and if the current user has permission to add, or remove it from a
|
||||
* user.
|
||||
*
|
||||
* @param string $role Name of the role trying to get added to a user object
|
||||
*
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
* @access private
|
||||
*/
|
||||
private static function verify_user_role( $role ) {
|
||||
|
||||
/**
|
||||
* The function for this is only loaded on admin pages. See note: https://codex.wordpress.org/Function_Reference/get_editable_roles#Notes
|
||||
*/
|
||||
if ( ! function_exists( 'get_editable_roles' ) ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/admin.php';
|
||||
}
|
||||
|
||||
$editable_roles = get_editable_roles();
|
||||
|
||||
if ( empty( $editable_roles[ $role ] ) ) {
|
||||
// Translators: %s is the name of the role that can't be added to the user.
|
||||
throw new UserError( sprintf( __( 'Sorry, you are not allowed to give this the following role: %s.', 'wp-graphql' ), $role ) );
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user